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

improve window<->tab management in spawn and mux

spawn now requires that the window_id be passed in; this makes it
a bit easier to spawn a remote tab into an existing window.
As part of this, beef up how we manage the window/tab association.
This commit is contained in:
Wez Furlong 2019-06-11 08:23:21 -07:00
parent 7456c06028
commit a7306f520a
12 changed files with 168 additions and 98 deletions

View File

@ -6,7 +6,7 @@ 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 failure::{bail, Error, Fallible};
use glium;
use glium::glutin::EventsLoopProxy;
use glium::glutin::WindowId;
@ -146,12 +146,11 @@ impl FrontEnd for GlutinFrontEnd {
config: &Arc<Config>,
fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<MuxWindowId, Error> {
let window = GliumTerminalWindow::new(&self.event_loop, fontconfig, config, tab)?;
let id = window.get_mux_window_id();
self.event_loop.add_window(window)?;
Ok(id)
window_id: MuxWindowId,
) -> Fallible<()> {
let window =
GliumTerminalWindow::new(&self.event_loop, fontconfig, config, tab, window_id)?;
self.event_loop.add_window(window)
}
}

View File

@ -195,6 +195,7 @@ impl GliumTerminalWindow {
fonts: &Rc<FontConfiguration>,
config: &Arc<Config>,
tab: &Rc<dyn Tab>,
mux_window_id: WindowId,
) -> Result<GliumTerminalWindow, Error> {
let (physical_rows, physical_cols) = tab.renderer().physical_dimensions();
@ -242,9 +243,6 @@ impl GliumTerminalWindow {
let height = height as u16;
let renderer = Renderer::new(&host.display, width, height, fonts)?;
let mux = Mux::get().unwrap();
let mux_window_id = mux.add_new_window_with_tab(tab)?;
Ok(GliumTerminalWindow {
host,
config: Arc::clone(config),

View File

@ -209,9 +209,12 @@ impl<H: HostHelper> HostImpl<H> {
Arc::clone(mux.config()),
FontSystemSelection::get_default(),
));
let tab = mux.default_domain().spawn(PtySize::default(), None)?;
let window_id = mux.new_empty_window();
let tab = mux
.default_domain()
.spawn(PtySize::default(), None, window_id)?;
let front_end = front_end().expect("to be called on gui thread");
front_end.spawn_new_window(mux.config(), &fonts, &tab)?;
front_end.spawn_new_window(mux.config(), &fonts, &tab, window_id)?;
Ok(())
});
}

View File

@ -137,14 +137,13 @@ pub trait TerminalWindow {
}
fn paint(&mut self) -> Result<(), Error> {
let mut target = self.frame();
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
let mut target = self.frame();
let res = {
let renderer = self.renderer();
let palette = tab.palette();
@ -204,14 +203,13 @@ pub trait TerminalWindow {
.get_domain(id)
.ok_or_else(|| format_err!("spawn_tab called with unresolvable domain id!?"))?,
};
let tab = domain.spawn(size, None)?;
let tab = domain.spawn(size, None, self.get_mux_window_id())?;
let tab_id = tab.tab_id();
let len = {
let mut window = mux
.get_window_mut(self.get_mux_window_id())
let window = mux
.get_window(self.get_mux_window_id())
.ok_or_else(|| format_err!("no such window!?"))?;
window.push(&tab);
window.len()
};
self.activate_tab(len - 1)?;

View File

@ -4,7 +4,7 @@ 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};
use failure::{format_err, Error, Fallible};
use lazy_static::lazy_static;
use promise::Executor;
use serde_derive::*;
@ -114,7 +114,8 @@ pub trait FrontEnd: Downcast {
config: &Arc<Config>,
fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<WindowId, Error>;
window_id: WindowId,
) -> Fallible<()>;
fn gui_executor(&self) -> Box<dyn Executor>;
}

View File

@ -6,7 +6,7 @@ use crate::mux::tab::Tab;
use crate::mux::window::WindowId;
use crate::mux::Mux;
use crate::server::listener::spawn_listener;
use failure::{bail, Error};
use failure::{bail, Error, Fallible};
use log::info;
use promise::Executor;
use promise::SpawnFunc;
@ -80,8 +80,9 @@ impl FrontEnd for MuxServerFrontEnd {
&self,
_config: &Arc<Config>,
_fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>,
) -> Result<WindowId, Error> {
Mux::get().unwrap().add_new_window_with_tab(tab)
_tab: &Rc<dyn Tab>,
_window_id: WindowId,
) -> Fallible<()> {
Ok(())
}
}

View File

@ -17,6 +17,8 @@ mod server;
use crate::frontend::FrontEndSelection;
use crate::mux::domain::{Domain, LocalDomain};
use crate::mux::Mux;
use crate::server::client::Client;
use crate::server::domain::ClientDomain;
use portable_pty::cmdbuilder::CommandBuilder;
mod font;
@ -131,8 +133,6 @@ fn run_terminal_gui(config: Arc<config::Config>, opts: &StartCommand) -> Result<
};
let domain: Arc<dyn Domain> = if opts.broken_mux_client_as_default_domain {
use crate::server::client::Client;
use crate::server::domain::ClientDomain;
let client = Client::new(&config)?;
Arc::new(ClientDomain::new(client))
} else {
@ -147,8 +147,11 @@ fn run_terminal_gui(config: Arc<config::Config>, opts: &StartCommand) -> Result<
domain.attach()?;
let tab = mux.default_domain().spawn(PtySize::default(), cmd)?;
gui.spawn_new_window(mux.config(), &fontconfig, &tab)?;
let window_id = mux.new_empty_window();
let tab = mux
.default_domain()
.spawn(PtySize::default(), cmd, window_id)?;
gui.spawn_new_window(mux.config(), &fontconfig, &tab, window_id)?;
gui.run_forever()
}
@ -189,7 +192,6 @@ fn main() -> Result<(), Error> {
run_terminal_gui(config, &start)
}
SubCommand::Cli(_) => {
use crate::server::client::Client;
use crate::server::codec::*;
let mut client = Client::new(&config)?;
info!("ping: {:?}", client.ping()?);

View File

@ -8,6 +8,7 @@
use crate::config::Config;
use crate::frontend::guicommon::localtab::LocalTab;
use crate::mux::tab::Tab;
use crate::mux::window::WindowId;
use crate::mux::Mux;
use downcast_rs::{impl_downcast, Downcast};
use failure::{Error, Fallible};
@ -26,7 +27,12 @@ pub fn alloc_domain_id() -> DomainId {
pub trait Domain: Downcast {
/// Spawn a new command within this domain
fn spawn(&self, size: PtySize, command: Option<CommandBuilder>) -> Result<Rc<dyn Tab>, Error>;
fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,
window: WindowId,
) -> Result<Rc<dyn Tab>, Error>;
/// Returns the domain id, which is useful for obtaining
/// a handle on the domain later.
@ -57,7 +63,12 @@ impl LocalDomain {
}
impl Domain for LocalDomain {
fn spawn(&self, size: PtySize, command: Option<CommandBuilder>) -> Result<Rc<dyn Tab>, Error> {
fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,
window: WindowId,
) -> Result<Rc<dyn Tab>, Error> {
let cmd = match command {
Some(c) => c,
None => self.config.build_prog(None)?,
@ -75,7 +86,9 @@ impl Domain for LocalDomain {
let tab: Rc<dyn Tab> = Rc::new(LocalTab::new(terminal, child, pair.master, self.id));
Mux::get().unwrap().add_tab(&tab)?;
let mux = Mux::get().unwrap();
mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window)?;
Ok(tab)
}

View File

@ -1,6 +1,6 @@
use crate::config::Config;
use crate::frontend::gui_executor;
use failure::Error;
use failure::{format_err, Error, Fallible};
use failure_derive::*;
use log::{debug, error, warn};
use portable_pty::ExitStatus;
@ -208,11 +208,19 @@ impl Mux {
window.get_active().map(Rc::clone)
}
pub fn add_new_window_with_tab(&self, tab: &Rc<dyn Tab>) -> Result<WindowId, Error> {
let window = Window::new(tab);
pub fn new_empty_window(&self) -> WindowId {
let window = Window::new();
let window_id = window.window_id();
self.windows.borrow_mut().insert(window_id, window);
Ok(window_id)
window_id
}
pub fn add_tab_to_window(&self, tab: &Rc<dyn Tab>, window_id: WindowId) -> Fallible<()> {
let mut window = self
.get_window_mut(window_id)
.ok_or_else(|| format_err!("add_tab_to_window: no such window_id {}", window_id))?;
window.push(tab);
Ok(())
}
#[allow(dead_code)]

View File

@ -11,10 +11,10 @@ pub struct Window {
}
impl Window {
pub fn new(tab: &Rc<dyn Tab>) -> Self {
pub fn new() -> Self {
Self {
id: WIN_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed),
tabs: vec![Rc::clone(tab)],
tabs: vec![],
active: 0,
}
}
@ -24,6 +24,9 @@ impl Window {
}
pub fn push(&mut self, tab: &Rc<dyn Tab>) {
for t in &self.tabs {
assert_ne!(t.tab_id(), tab.tab_id(), "tab already added to this window");
}
self.tabs.push(Rc::clone(tab))
}

View File

@ -2,6 +2,7 @@ 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::window::WindowId;
use crate::mux::Mux;
use crate::server::client::Client;
use crate::server::codec::Spawn;
@ -16,7 +17,40 @@ pub struct ClientInner {
pub client: Mutex<Client>,
pub local_domain_id: DomainId,
pub remote_domain_id: DomainId,
remote_to_local_window: Mutex<HashMap<WindowId, WindowId>>,
}
impl ClientInner {
fn remote_to_local_window(&self, remote_window_id: WindowId) -> Option<WindowId> {
let map = self.remote_to_local_window.lock().unwrap();
map.get(&remote_window_id).cloned()
}
fn record_remote_to_local_window_mapping(
&self,
remote_window_id: WindowId,
local_window_id: WindowId,
) {
let mut map = self.remote_to_local_window.lock().unwrap();
map.insert(remote_window_id, local_window_id);
log::error!(
"record_remote_to_local_window_mapping: {} -> {}",
remote_window_id,
local_window_id
);
}
fn local_to_remote_window(&self, local_window_id: WindowId) -> Option<WindowId> {
let map = self.remote_to_local_window.lock().unwrap();
for (remote, local) in map.iter() {
if *local == local_window_id {
return Some(*remote);
}
}
None
}
}
pub struct ClientDomain {
inner: Arc<ClientInner>,
}
@ -33,6 +67,7 @@ impl ClientInner {
client: Mutex::new(client),
local_domain_id,
remote_domain_id,
remote_to_local_window: Mutex::new(HashMap::new()),
}
}
}
@ -49,20 +84,32 @@ impl Domain for ClientDomain {
self.inner.local_domain_id
}
fn spawn(&self, size: PtySize, command: Option<CommandBuilder>) -> Fallible<Rc<dyn Tab>> {
fn spawn(
&self,
size: PtySize,
command: Option<CommandBuilder>,
window: WindowId,
) -> Fallible<Rc<dyn Tab>> {
let remote_tab_id = {
let mut client = self.inner.client.lock().unwrap();
client
.spawn(Spawn {
domain_id: self.inner.remote_domain_id,
window_id: None,
size,
command,
})?
.tab_id
let result = client.spawn(Spawn {
domain_id: self.inner.remote_domain_id,
window_id: self.inner.local_to_remote_window(window),
size,
command,
})?;
self.inner
.record_remote_to_local_window_mapping(result.window_id, window);
result.tab_id
};
let tab: Rc<dyn Tab> = Rc::new(ClientTab::new(&self.inner, remote_tab_id));
Mux::get().unwrap().add_tab(&tab)?;
let mux = Mux::get().unwrap();
mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window)?;
Ok(tab)
}
@ -70,32 +117,40 @@ impl Domain for ClientDomain {
let mux = Mux::get().unwrap();
let mut client = self.inner.client.lock().unwrap();
let tabs = client.list_tabs()?;
let mut windows = HashMap::new();
log::error!("ListTabs result {:#?}", tabs);
for entry in tabs.tabs.iter() {
log::error!("attaching to remote tab {} {}", entry.tab_id, entry.title);
log::error!(
"attaching to remote tab {} in remote window {} {}",
entry.tab_id,
entry.window_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()
});
if let Some(local_window_id) = self.inner.remote_to_local_window(entry.window_id) {
let mut window = mux
.get_window_mut(local_window_id)
.expect("no such window!?");
log::error!("already have a local window for this one");
window.push(&tab);
} else {
log::error!("spawn new local window");
let fonts = Rc::new(FontConfiguration::new(
Arc::clone(mux.config()),
FontSystemSelection::get_default(),
));
let local_window_id = mux.new_empty_window();
self.inner
.record_remote_to_local_window_mapping(entry.window_id, local_window_id);
mux.add_tab_to_window(&tab, local_window_id)?;
front_end()
.unwrap()
.spawn_new_window(mux.config(), &fonts, &tab, local_window_id)
.unwrap();
}
}
Ok(())
}

View File

@ -95,29 +95,18 @@ impl ClientSession {
Pdu::ListTabs(ListTabs {}) => {
let result = Future::with_executor(self.executor.clone_executor(), move || {
let mux = Mux::get().unwrap();
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 {:?}",
let mut tabs = vec![];
for window_id in mux.iter_windows().into_iter() {
let window = mux.get_window(window_id).unwrap();
for tab in window.iter() {
tabs.push(WindowAndTabEntry {
window_id,
tabs.len(),
tabs
);
tabs.into_iter()
})
.collect();
tab_id: tab.tab_id(),
title: tab.get_title(),
});
}
}
log::error!("ListTabs {:#?}", tabs);
Ok(ListTabsResponse { tabs })
})
.wait()?;
@ -245,17 +234,17 @@ 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);
mux.get_window_mut(window_id).ok_or_else(|| {
format_err!("window_id {} not found on this server", window_id)
})?;
window_id
} else {
mux.add_new_window_with_tab(&tab)?
mux.new_empty_window()
};
let tab = domain.spawn(spawn.size, spawn.command, window_id)?;
Ok(SpawnResponse {
tab_id: tab.tab_id(),
window_id,