mirror of
https://github.com/wez/wezterm.git
synced 2024-09-11 14:25:57 +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:
parent
19db4228f0
commit
03d8f10d11
@ -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)
|
||||
|
@ -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,35 +249,40 @@ 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| {
|
||||
let mut ui = ConnectionUIImpl { term, rx };
|
||||
let status = ui.run().unwrap_or_else(|e| {
|
||||
log::error!("while running ConnectionUI loop: {:?}", e);
|
||||
CloseStatus::Implicit
|
||||
});
|
||||
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 {
|
||||
ui.sleep(
|
||||
"(this window will close automatically)",
|
||||
Duration::new(120, 0),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
if !params.disable_close_delay && status == CloseStatus::Implicit {
|
||||
ui.sleep(
|
||||
"(this window will close automatically)",
|
||||
Duration::new(120, 0),
|
||||
)
|
||||
.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 {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
mux.kill_window(window_id);
|
||||
if should_close_window {
|
||||
mux.kill_window(window_id);
|
||||
} else if let Some(pane) = mux.get_pane(pane_id) {
|
||||
pane.kill();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user