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:
parent
7456c06028
commit
a7306f520a
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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(())
|
||||
});
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
12
src/main.rs
12
src/main.rs
@ -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()?);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user