1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 23:21:08 +03:00

basically working attach to remote tabs/windows

The Domain::spawn interface needs to be adjusted to allow indicating
which window we want to spawn into, or whether the spawn should
create a new window, but aside from that, we can now attach to
a mux server and instantiate tabs and windows!
This commit is contained in:
Wez Furlong 2019-06-11 06:48:19 -07:00
parent 9b6800f589
commit 7456c06028
11 changed files with 141 additions and 39 deletions

View File

@ -4,6 +4,7 @@ use crate::frontend::glium::window::GliumTerminalWindow;
use crate::frontend::guicommon::window::TerminalWindow;
use crate::frontend::{front_end, FrontEnd};
use crate::mux::tab::Tab;
use crate::mux::window::WindowId as MuxWindowId;
use crate::mux::{Mux, SessionTerminated};
use failure::{bail, Error};
use glium;
@ -145,10 +146,12 @@ impl FrontEnd for GlutinFrontEnd {
config: &Arc<Config>,
fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<(), Error> {
) -> Result<MuxWindowId, Error> {
let window = GliumTerminalWindow::new(&self.event_loop, fontconfig, config, tab)?;
self.event_loop.add_window(window)
let id = window.get_mux_window_id();
self.event_loop.add_window(window)?;
Ok(id)
}
}

View File

@ -1,6 +1,7 @@
use crate::config::Config;
use crate::font::FontConfiguration;
use crate::mux::tab::Tab;
use crate::mux::window::WindowId;
use crate::mux::Mux;
use downcast_rs::{impl_downcast, Downcast};
use failure::{format_err, Error};
@ -113,7 +114,7 @@ pub trait FrontEnd: Downcast {
config: &Arc<Config>,
fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<(), Error>;
) -> Result<WindowId, Error>;
fn gui_executor(&self) -> Box<dyn Executor>;
}

View File

@ -3,6 +3,7 @@ use crate::config::Config;
use crate::font::FontConfiguration;
use crate::frontend::FrontEnd;
use crate::mux::tab::Tab;
use crate::mux::window::WindowId;
use crate::mux::Mux;
use crate::server::listener::spawn_listener;
use failure::{bail, Error};
@ -79,9 +80,8 @@ impl FrontEnd for MuxServerFrontEnd {
&self,
_config: &Arc<Config>,
_fontconfig: &Rc<FontConfiguration>,
_tab: &Rc<dyn Tab>,
) -> Result<(), Error> {
// The tab was already added to the mux, so we are a NOP
Ok(())
tab: &Rc<dyn Tab>,
) -> Result<WindowId, Error> {
Mux::get().unwrap().add_new_window_with_tab(tab)
}
}

View File

@ -5,6 +5,7 @@ use crate::frontend::xwindows::xwin::X11TerminalWindow;
use crate::frontend::xwindows::Connection;
use crate::frontend::FrontEnd;
use crate::mux::tab::Tab;
use crate::mux::window::WindowId as MuxWindowId;
use crate::mux::Mux;
use failure::{bail, Error};
use log::debug;
@ -83,10 +84,12 @@ impl FrontEnd for X11FrontEnd {
config: &Arc<Config>,
fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<(), Error> {
) -> Result<MuxWindowId, Error> {
let window = X11TerminalWindow::new(&self.event_loop, fontconfig, config, tab)?;
self.event_loop.add_window(window)
let id = window.get_mux_window_id();
self.event_loop.add_window(window)?;
Ok(id)
}
}

View File

@ -14,7 +14,7 @@ mod frontend;
mod mux;
mod opengl;
mod server;
use crate::frontend::{FrontEnd, FrontEndSelection};
use crate::frontend::FrontEndSelection;
use crate::mux::domain::{Domain, LocalDomain};
use crate::mux::Mux;
use portable_pty::cmdbuilder::CommandBuilder;
@ -145,7 +145,11 @@ fn run_terminal_gui(config: Arc<config::Config>, opts: &StartCommand) -> Result<
let front_end = opts.front_end.unwrap_or(config.front_end);
let gui = front_end.try_new(&mux)?;
spawn_window(&mux, &*gui, cmd, &fontconfig)?;
domain.attach()?;
let tab = mux.default_domain().spawn(PtySize::default(), cmd)?;
gui.spawn_new_window(mux.config(), &fontconfig, &tab)?;
gui.run_forever()
}
@ -190,33 +194,19 @@ fn main() -> Result<(), Error> {
let mut client = Client::new(&config)?;
info!("ping: {:?}", client.ping()?);
let tabs = client.list_tabs()?;
for (tab_id, title) in tabs.tabs.iter() {
info!("tab {}: {}", tab_id, title);
let _data = client.get_coarse_tab_renderable_data(GetCoarseTabRenderableData {
tab_id: *tab_id,
dirty_all: true,
})?;
// info!("coarse: {:?}", data);
for entry in tabs.tabs.iter() {
info!("tab {} {}: {}", entry.window_id, entry.tab_id, entry.title);
}
error!(
"spawn: {:?}",
client.spawn(Spawn {
domain_id: 0,
size: PtySize::default(),
command: None
command: None,
window_id: None,
})
);
Ok(())
}
}
}
fn spawn_window(
mux: &Rc<Mux>,
gui: &dyn FrontEnd,
cmd: Option<CommandBuilder>,
fontconfig: &Rc<FontConfiguration>,
) -> Result<(), Error> {
let tab = mux.default_domain().spawn(PtySize::default(), cmd)?;
gui.spawn_new_window(mux.config(), &fontconfig, &tab)
}

View File

@ -10,7 +10,7 @@ use crate::frontend::guicommon::localtab::LocalTab;
use crate::mux::tab::Tab;
use crate::mux::Mux;
use downcast_rs::{impl_downcast, Downcast};
use failure::Error;
use failure::{Error, Fallible};
use log::info;
use portable_pty::cmdbuilder::CommandBuilder;
use portable_pty::{PtySize, PtySystem};
@ -31,6 +31,9 @@ pub trait Domain: Downcast {
/// Returns the domain id, which is useful for obtaining
/// a handle on the domain later.
fn domain_id(&self) -> DomainId;
/// Re-attach to any tabs that might be pre-existing in this domain
fn attach(&self) -> Fallible<()>;
}
impl_downcast!(Domain);
@ -80,4 +83,8 @@ impl Domain for LocalDomain {
fn domain_id(&self) -> DomainId {
self.id
}
fn attach(&self) -> Fallible<()> {
Ok(())
}
}

View File

@ -171,6 +171,18 @@ impl Mux {
pub fn remove_tab(&self, tab_id: TabId) {
debug!("removing tab {}", tab_id);
self.tabs.borrow_mut().remove(&tab_id);
let mut windows = self.windows.borrow_mut();
let mut dead_windows = vec![];
for (window_id, win) in windows.iter_mut() {
if win.remove_by_id(tab_id) && win.is_empty() {
dead_windows.push(*window_id);
}
}
for window_id in dead_windows {
debug!("removing window {}", window_id);
windows.remove(&window_id);
}
}
pub fn get_window(&self, window_id: WindowId) -> Option<Ref<Window>> {
@ -208,6 +220,7 @@ impl Mux {
self.tabs.borrow().is_empty()
}
#[allow(dead_code)]
pub fn iter_tabs(&self) -> Vec<Rc<dyn Tab>> {
self.tabs
.borrow()
@ -215,6 +228,10 @@ impl Mux {
.map(|(_, v)| Rc::clone(v))
.collect()
}
pub fn iter_windows(&self) -> Vec<WindowId> {
self.windows.borrow().keys().cloned().collect()
}
}
#[derive(Debug, Fail)]

View File

@ -48,13 +48,16 @@ impl Window {
None
}
pub fn remove_by_id(&mut self, id: TabId) {
pub fn remove_by_id(&mut self, id: TabId) -> bool {
if let Some(idx) = self.idx_by_id(id) {
self.tabs.remove(idx);
let len = self.tabs.len();
if len > 0 && self.active == idx && idx >= len {
self.set_active(len - 1);
}
true
} else {
false
}
}

View File

@ -12,12 +12,12 @@
use crate::mux::domain::DomainId;
use crate::mux::tab::TabId;
use crate::mux::window::WindowId;
use failure::{bail, Error};
use leb128;
use log::debug;
use portable_pty::{CommandBuilder, PtySize};
use serde_derive::*;
use std::collections::HashMap;
use std::sync::Arc;
use term::{CursorPosition, Line};
use termwiz::hyperlink::Hyperlink;
@ -242,9 +242,16 @@ pub struct Pong {}
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct ListTabs {}
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct WindowAndTabEntry {
pub window_id: WindowId,
pub tab_id: TabId,
pub title: String,
}
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct ListTabsResponse {
pub tabs: HashMap<TabId, String>,
pub tabs: Vec<WindowAndTabEntry>,
}
/// This is a transitional request to get some basic
@ -277,6 +284,8 @@ pub struct GetCoarseTabRenderableDataResponse {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct Spawn {
pub domain_id: DomainId,
/// If None, create a new window for this new tab
pub window_id: Option<WindowId>,
pub command: Option<CommandBuilder>,
pub size: PtySize,
}
@ -284,6 +293,7 @@ pub struct Spawn {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SpawnResponse {
pub tab_id: TabId,
pub window_id: WindowId,
}
#[derive(Deserialize, Serialize, PartialEq, Debug)]

View File

@ -1,3 +1,5 @@
use crate::font::{FontConfiguration, FontSystemSelection};
use crate::frontend::front_end;
use crate::mux::domain::{alloc_domain_id, Domain, DomainId};
use crate::mux::tab::Tab;
use crate::mux::Mux;
@ -6,6 +8,7 @@ use crate::server::codec::Spawn;
use crate::server::tab::ClientTab;
use failure::Fallible;
use portable_pty::{CommandBuilder, PtySize};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
@ -52,6 +55,7 @@ impl Domain for ClientDomain {
client
.spawn(Spawn {
domain_id: self.inner.remote_domain_id,
window_id: None,
size,
command,
})?
@ -61,4 +65,38 @@ impl Domain for ClientDomain {
Mux::get().unwrap().add_tab(&tab)?;
Ok(tab)
}
fn attach(&self) -> Fallible<()> {
let mux = Mux::get().unwrap();
let mut client = self.inner.client.lock().unwrap();
let tabs = client.list_tabs()?;
let mut windows = HashMap::new();
for entry in tabs.tabs.iter() {
log::error!("attaching to remote tab {} {}", entry.tab_id, entry.title);
let tab: Rc<dyn Tab> = Rc::new(ClientTab::new(&self.inner, entry.tab_id));
mux.add_tab(&tab)?;
windows
.entry(entry.window_id)
.and_modify(|local_window_id| {
let mut window = mux
.get_window_mut(*local_window_id)
.expect("no such window!?");
window.push(&tab);
})
.or_insert_with(|| {
let fonts = Rc::new(FontConfiguration::new(
Arc::clone(mux.config()),
FontSystemSelection::get_default(),
));
front_end()
.unwrap()
.spawn_new_window(mux.config(), &fonts, &tab)
.unwrap()
});
}
Ok(())
}
}

View File

@ -7,7 +7,6 @@ use failure::{bail, err_msg, format_err, Error, Fallible};
use libc::{mode_t, umask};
use log::{debug, error, warn};
use promise::{Executor, Future};
use std::collections::HashMap;
use std::fs::{remove_file, DirBuilder};
#[cfg(unix)]
use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
@ -95,11 +94,30 @@ impl ClientSession {
Pdu::Ping(Ping {}) => Pdu::Pong(Pong {}),
Pdu::ListTabs(ListTabs {}) => {
let result = Future::with_executor(self.executor.clone_executor(), move || {
let mut tabs = HashMap::new();
let mux = Mux::get().unwrap();
for tab in mux.iter_tabs() {
tabs.insert(tab.tab_id(), tab.get_title());
}
let tabs = mux
.iter_windows()
.into_iter()
.flat_map(|window_id| {
let window = mux.get_window(window_id).unwrap();
let tabs: Vec<WindowAndTabEntry> = window
.iter()
.map(|tab| WindowAndTabEntry {
window_id,
tab_id: tab.tab_id(),
title: tab.get_title(),
})
.collect();
log::error!(
"ListTabs: window {} has {} tabs {:?}",
window_id,
tabs.len(),
tabs
);
tabs.into_iter()
})
.collect();
Ok(ListTabsResponse { tabs })
})
.wait()?;
@ -227,8 +245,20 @@ impl ClientSession {
let domain = mux.get_domain(spawn.domain_id).ok_or_else(|| {
format_err!("domain {} not found on this server", spawn.domain_id)
})?;
let tab = domain.spawn(spawn.size, spawn.command)?;
let window_id = if let Some(window_id) = spawn.window_id {
mux.get_window_mut(window_id)
.ok_or_else(|| {
format_err!("window_id {} not found on this server", window_id)
})?
.push(&tab);
window_id
} else {
mux.add_new_window_with_tab(&tab)?
};
Ok(SpawnResponse {
tab_id: domain.spawn(spawn.size, spawn.command)?.tab_id(),
tab_id: tab.tab_id(),
window_id,
})
})
.wait()?;