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

mux: pass current window_id to Domain::attach

This commit allows the currently active window to:

* Spawn a new tab in the active window (rather than spawning
  a new window) to host the connection status
* Auto-close that connection UI tab (rather than the whole window)
  when the window is no longer needed
* Pass the current window through to use as the primary window when
  assigning remote window/tabs.

The net effect of this is that there are fewer transient windows,
and that it is easier to connect a set of domains to the active
workspace.

refs: https://github.com/wez/wezterm/issues/1874
This commit is contained in:
Wez Furlong 2022-04-17 18:01:19 -07:00
parent 19db4228f0
commit 03d8f10d11
11 changed files with 110 additions and 54 deletions

View File

@ -15,6 +15,7 @@ As features stabilize some brief notes about them will accumulate here.
* ssh client now supports `BindAddress`. Thanks to [@gpanders](https://github.com/gpanders)! [#1875](https://github.com/wez/wezterm/pull/1875)
* [PaneInformation.domain_name](config/lua/PaneInformation.md) and [pane:get_domain_name()](config/lua/pane/get_domain_name.md) which return the name of the domain with which a pane is associated. [#1881](https://github.com/wez/wezterm/issues/1881)
* You may now use `CTRL-n` and `CTRL-p` (in addition to the up/down arrow and vi motion keys) to change the selected row in the Launcher. Thanks to [@Junnplus](https://github.com/Junnplus)! [#1880](https://github.com/wez/wezterm/pull/1880)
* Attaching multiplexer domains now attaches the first window as a tab in the active window, rather than opening a new window. [#1874](https://github.com/wez/wezterm/issues/1874)
#### Changed
* Debian packages now register wezterm as an alternative for `x-terminal-emulator`. Thanks to [@xpufx](https://github.com/xpufx)! [#1883](https://github.com/wez/wezterm/pull/1883)

View File

@ -235,6 +235,13 @@ impl HeadlessImpl {
}
}
#[derive(Default, Clone, Copy, Debug)]
pub struct ConnectionUIParams {
pub size: PtySize,
pub disable_close_delay: bool,
pub window_id: Option<crate::WindowId>,
}
#[derive(Clone)]
pub struct ConnectionUI {
tx: Sender<UIRequest>,
@ -242,20 +249,22 @@ pub struct ConnectionUI {
impl ConnectionUI {
pub fn new() -> Self {
let enable_close_delay = true;
Self::with_dimensions(PtySize::default(), enable_close_delay)
Self::with_params(Default::default())
}
pub fn with_dimensions(size: PtySize, enable_close_delay: bool) -> Self {
pub fn with_params(params: ConnectionUIParams) -> Self {
let (tx, rx) = unbounded();
promise::spawn::spawn_into_main_thread(termwiztermtab::run(size, move |term| {
promise::spawn::spawn_into_main_thread(termwiztermtab::run(
params.size,
params.window_id,
move |term| {
let mut ui = ConnectionUIImpl { term, rx };
let status = ui.run().unwrap_or_else(|e| {
log::error!("while running ConnectionUI loop: {:?}", e);
CloseStatus::Implicit
});
if enable_close_delay && status == CloseStatus::Implicit {
if !params.disable_close_delay && status == CloseStatus::Implicit {
ui.sleep(
"(this window will close automatically)",
Duration::new(120, 0),
@ -263,14 +272,17 @@ impl ConnectionUI {
.ok();
}
Ok(())
}))
},
))
.detach();
Self { tx }
}
pub fn new_with_no_close_delay() -> Self {
let enable_close_delay = false;
Self::with_dimensions(PtySize::default(), enable_close_delay)
Self::with_params(ConnectionUIParams {
disable_close_delay: true,
..Default::default()
})
}
pub fn new_headless() -> Self {

View File

@ -118,7 +118,7 @@ pub trait Domain: Downcast {
}
/// Re-attach to any tabs that might be pre-existing in this domain
async fn attach(&self) -> anyhow::Result<()>;
async fn attach(&self, window_id: Option<WindowId>) -> anyhow::Result<()>;
/// Detach all tabs
fn detach(&self) -> anyhow::Result<()>;
@ -322,7 +322,7 @@ impl Domain for LocalDomain {
&self.name
}
async fn attach(&self) -> anyhow::Result<()> {
async fn attach(&self, _window_id: Option<WindowId>) -> anyhow::Result<()> {
Ok(())
}

View File

@ -678,7 +678,7 @@ impl Domain for RemoteSshDomain {
&self.name
}
async fn attach(&self) -> anyhow::Result<()> {
async fn attach(&self, _window_id: Option<crate::WindowId>) -> anyhow::Result<()> {
Ok(())
}

View File

@ -64,7 +64,7 @@ impl Domain for TermWizTerminalDomain {
fn domain_name(&self) -> &str {
"TermWizTerminalDomain"
}
async fn attach(&self) -> anyhow::Result<()> {
async fn attach(&self, _window_id: Option<WindowId>) -> anyhow::Result<()> {
Ok(())
}
@ -226,6 +226,10 @@ impl Pane for TermWizTerminalPane {
self.terminal.borrow_mut().perform_actions(actions)
}
fn kill(&self) {
*self.dead.borrow_mut() = true;
}
fn is_dead(&self) -> bool {
*self.dead.borrow()
}
@ -445,11 +449,13 @@ pub async fn run<
F: Send + 'static + FnOnce(TermWizTerminal) -> anyhow::Result<T>,
>(
size: PtySize,
window_id: Option<WindowId>,
f: F,
) -> anyhow::Result<T> {
let render_pipe = Pipe::new().expect("Pipe creation not to fail");
let render_rx = render_pipe.read;
let (input_tx, input_rx) = channel();
let should_close_window = window_id.is_none();
let renderer = config::lua::new_wezterm_terminfo_renderer();
@ -472,14 +478,22 @@ pub async fn run<
input_tx: Sender<InputEvent>,
render_rx: FileDescriptor,
size: PtySize,
) -> anyhow::Result<WindowId> {
window_id: Option<WindowId>,
) -> anyhow::Result<(PaneId, WindowId)> {
let mux = Mux::get().unwrap();
// TODO: make a singleton
let domain: Arc<dyn Domain> = Arc::new(TermWizTerminalDomain::new());
mux.add_domain(&domain);
let window_id = mux.new_empty_window(None);
let window_builder;
let window_id = match window_id {
Some(id) => id,
None => {
window_builder = mux.new_empty_window(None);
*window_builder
}
};
let pane = TermWizTerminalPane::new(domain.domain_id(), size, input_tx, render_rx);
let pane: Rc<dyn Pane> = Rc::new(pane);
@ -488,13 +502,19 @@ pub async fn run<
tab.assign_pane(&pane);
mux.add_tab_and_active_pane(&tab)?;
mux.add_tab_to_window(&tab, *window_id)?;
mux.add_tab_to_window(&tab, window_id)?;
Ok(*window_id)
let mut window = mux
.get_window_mut(window_id)
.ok_or_else(|| anyhow::anyhow!("invalid window id {}", window_id))?;
let tab_idx = window.len().saturating_sub(1);
window.save_and_then_set_active(tab_idx);
Ok((pane.pane_id(), window_id))
}
let window_id: WindowId = promise::spawn::spawn_into_main_thread(async move {
register_tab(input_tx, render_rx, size).await
let (pane_id, window_id) = promise::spawn::spawn_into_main_thread(async move {
register_tab(input_tx, render_rx, size, window_id).await
})
.await?;
@ -507,7 +527,11 @@ pub async fn run<
// on the screen so let's ask the mux to kill off our window now.
promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap();
if should_close_window {
mux.kill_window(window_id);
} else if let Some(pane) = mux.get_pane(pane_id) {
pane.kill();
}
})
.detach();

View File

@ -221,7 +221,7 @@ impl Domain for TmuxDomain {
"tmux"
}
async fn attach(&self) -> anyhow::Result<()> {
async fn attach(&self, _window_id: Option<crate::WindowId>) -> anyhow::Result<()> {
Ok(())
}

View File

@ -5,7 +5,7 @@ use async_trait::async_trait;
use codec::{ListPanesResponse, SpawnV2, SplitPane};
use config::keyassignment::SpawnTabDomain;
use config::{SshDomain, TlsDomainClient, UnixDomain};
use mux::connui::ConnectionUI;
use mux::connui::{ConnectionUI, ConnectionUIParams};
use mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
use mux::pane::{Pane, PaneId};
use mux::tab::{SplitDirection, Tab, TabId};
@ -306,7 +306,7 @@ impl ClientDomain {
let inner = Self::get_client_inner_for_domain(domain_id)?;
let panes = inner.client.list_panes().await?;
Self::process_pane_list(inner, panes)?;
Self::process_pane_list(inner, panes, None)?;
ui.close();
Ok(())
@ -315,12 +315,16 @@ impl ClientDomain {
pub async fn resync(&self) -> anyhow::Result<()> {
if let Some(inner) = self.inner.borrow().as_ref() {
let panes = inner.client.list_panes().await?;
Self::process_pane_list(Arc::clone(inner), panes)?;
Self::process_pane_list(Arc::clone(inner), panes, None)?;
}
Ok(())
}
fn process_pane_list(inner: Arc<ClientInner>, panes: ListPanesResponse) -> anyhow::Result<()> {
fn process_pane_list(
inner: Arc<ClientInner>,
panes: ListPanesResponse,
mut primary_window_id: Option<WindowId>,
) -> anyhow::Result<()> {
let mux = Mux::get().expect("to be called on main thread");
log::debug!("ListPanes result {:#?}", panes);
@ -400,6 +404,9 @@ impl ClientDomain {
if window.idx_by_id(tab.tab_id()).is_none() {
window.push(&tab);
}
} else if let Some(local_window_id) = primary_window_id.take() {
inner.record_remote_to_local_window_mapping(remote_window_id, local_window_id);
mux.add_tab_to_window(&tab, local_window_id)?;
} else {
let local_window_id = mux.new_empty_window(workspace.take());
inner.record_remote_to_local_window_mapping(remote_window_id, *local_window_id);
@ -415,6 +422,7 @@ impl ClientDomain {
domain_id: DomainId,
client: Client,
panes: ListPanesResponse,
primary_window_id: Option<WindowId>,
) -> anyhow::Result<()> {
let mux = Mux::get().unwrap();
let domain = mux
@ -428,7 +436,7 @@ impl ClientDomain {
let inner = Arc::new(ClientInner::new(domain_id, client, threshold));
*domain.inner.borrow_mut() = Some(Arc::clone(&inner));
Self::process_pane_list(inner, panes)?;
Self::process_pane_list(inner, panes, primary_window_id)?;
Ok(())
}
@ -563,7 +571,7 @@ impl Domain for ClientDomain {
Ok(pane)
}
async fn attach(&self) -> anyhow::Result<()> {
async fn attach(&self, window_id: Option<WindowId>) -> anyhow::Result<()> {
if self.state() == DomainState::Attached {
// Already attached
return Ok(());
@ -573,7 +581,10 @@ impl Domain for ClientDomain {
let config = self.config.clone();
let activity = mux::activity::Activity::new();
let ui = ConnectionUI::new();
let ui = ConnectionUI::with_params(ConnectionUIParams {
window_id,
..Default::default()
});
ui.title("wezterm: Connecting...");
ui.async_run_and_log_error({
@ -606,7 +617,7 @@ impl Domain for ClientDomain {
"Server has {} tabs. Attaching to local UI...\n",
panes.tabs.len()
));
ClientDomain::finish_attach(domain_id, client, panes)
ClientDomain::finish_attach(domain_id, client, panes, window_id)
}
})
.await

View File

@ -179,10 +179,10 @@ fn run_serial(config: config::ConfigHandle, opts: &SerialCommand) -> anyhow::Res
let mux = setup_mux(domain.clone(), &config, Some("local"), None)?;
let gui = crate::frontend::try_new()?;
block_on(domain.attach())?; // FIXME: blocking
{
let window_id = mux.new_empty_window(None);
block_on(domain.attach(Some(*window_id)))?; // FIXME: blocking
// FIXME: blocking
let _tab = block_on(domain.spawn(config.initial_size(), None, None, *window_id))?;
}
@ -223,7 +223,6 @@ async fn async_run_with_domain_as_default(
// And configure their desired domain as the default
mux.add_domain(&domain);
mux.set_default_domain(&domain);
domain.attach().await?;
spawn_tab_in_default_domain_if_mux_is_empty(cmd).await
}
@ -278,7 +277,9 @@ async fn spawn_tab_in_default_domain_if_mux_is_empty(
let mux = Mux::get().unwrap();
let domain = mux.default_domain();
domain.attach().await?;
let window_id = mux.new_empty_window(None);
domain.attach(Some(*window_id)).await?;
let have_panes_in_domain = mux
.iter_panes()
@ -301,7 +302,6 @@ async fn spawn_tab_in_default_domain_if_mux_is_empty(
true
});
let window_id = mux.new_empty_window(None);
let _tab = domain
.spawn(config.initial_size(), cmd, None, *window_id)
.await?;
@ -357,7 +357,7 @@ async fn connect_to_auto_connect_domains() -> anyhow::Result<()> {
for dom in domains {
if let Some(dom) = dom.downcast_ref::<ClientDomain>() {
if dom.connect_automatically() {
dom.attach().await?;
dom.attach(None).await?;
}
}
}

View File

@ -65,6 +65,7 @@ pub struct LauncherArgs {
title: String,
active_workspace: String,
workspaces: Vec<String>,
mux_window_id: WindowId,
}
impl LauncherArgs {
@ -158,6 +159,7 @@ impl LauncherArgs {
title: title.to_string(),
workspaces,
active_workspace,
mux_window_id,
}
}
}
@ -175,6 +177,7 @@ struct LauncherState {
window: ::window::Window,
filtering: bool,
flags: LauncherFlags,
mux_window_id: WindowId,
}
impl LauncherState {
@ -421,11 +424,12 @@ impl LauncherState {
fn launch(&self, active_idx: usize) {
match self.filtered_entries[active_idx].clone().kind {
EntryKind::Attach { domain } => {
let window_id = self.mux_window_id;
promise::spawn::spawn_into_main_thread(async move {
// We can't inline do_domain_attach here directly
// because the compiler would then want its body
// to be Send :-/
do_domain_attach(domain);
do_domain_attach(domain, window_id);
})
.detach();
}
@ -599,6 +603,7 @@ pub fn launcher(
window,
filtering: args.flags.contains(LauncherFlags::FUZZY),
flags: args.flags,
mux_window_id: args.mux_window_id,
};
term.set_raw_mode()?;
@ -609,13 +614,13 @@ pub fn launcher(
state.run_loop(&mut term)
}
fn do_domain_attach(domain: DomainId) {
fn do_domain_attach(domain: DomainId, window: WindowId) {
promise::spawn::spawn(async move {
let mux = Mux::get().unwrap();
let domain = mux
.get_domain(domain)
.ok_or_else(|| anyhow!("launcher attach called with unresolvable domain id!?"))?;
domain.attach().await
domain.attach(Some(window)).await
})
.detach();
}

View File

@ -3,7 +3,7 @@ use anyhow::anyhow;
use config::{configuration, wezterm_version};
use http_req::request::{HttpVersion, Request};
use http_req::uri::Uri;
use mux::connui::ConnectionUI;
use mux::connui::{ConnectionUI, ConnectionUIParams};
use portable_pty::PtySize;
use regex::Regex;
use serde::*;
@ -155,14 +155,17 @@ fn show_update_available(release: Release) {
}
let mut updater = UPDATER_WINDOW.lock().unwrap();
let enable_close_delay = false;
let size = PtySize {
cols: 80,
rows: 35,
pixel_width: 0,
pixel_height: 0,
};
let ui = ConnectionUI::with_dimensions(size, enable_close_delay);
let ui = ConnectionUI::with_params(ConnectionUIParams {
size,
disable_close_delay: true,
window_id: None,
});
ui.title("WezTerm Update Available");
let install = if cfg!(windows) {

View File

@ -204,10 +204,10 @@ async fn async_run(cmd: Option<CommandBuilder>) -> anyhow::Result<()> {
let mux = Mux::get().unwrap();
let domain = mux.default_domain();
domain.attach().await?;
let window_id = mux.new_empty_window(None);
domain.attach(Some(*window_id)).await?;
let config = config::configuration();
let window_id = mux.new_empty_window(None);
let _tab = mux
.default_domain()
.spawn(config.initial_size(), cmd, None, *window_id)