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

mux: fix update issue

The heart of the issue here was due to the window-reuse logic that
tries to reuse a GUI window that is no longer associated with a mux
window.

Each GUI window subscribes to the mux for mux events, but it filters
according to is understanding of the mux_window_id that it is associated
with.

The GUI frontend maintains an mapping of GUI and mux window so that it
knows when to reuse a GUI window and when to close it.

When connecting to a remote mux, wezterm spawns a temporary connection
progress window.  Once connected, workspace reconiliation is triggered
and decides that this window can be used for something else.

As part of workspace reconciliation, this mapping can be adjusted and
the frontend will notify a GUI window that its mux window has changed.

However, that updated mux window was not visible to the mux notification
subscription so the effect was that a variety of notifications were
effectively ignored, including updates from a remote mux when the output
was changed.

To make matters worse, the workspace reconciliation could "double-tap"
window creation and create excess windows only to later realize they
weren't needed and close them out again.

This commit addresses both of these concerns.

refs: #1841
refs: #1814
This commit is contained in:
Wez Furlong 2022-04-08 10:08:10 -07:00
parent e8ad5f2b56
commit b908e2dd8c
4 changed files with 67 additions and 7 deletions

View File

@ -1157,7 +1157,7 @@ impl Client {
rpc!(resize, Resize, UnitResponse); rpc!(resize, Resize, UnitResponse);
rpc!(set_zoomed, SetPaneZoomed, UnitResponse); rpc!(set_zoomed, SetPaneZoomed, UnitResponse);
rpc!( rpc!(
get_tab_render_changes, get_pane_render_changes,
GetPaneRenderChanges, GetPaneRenderChanges,
LivenessResponse LivenessResponse
); );

View File

@ -303,6 +303,11 @@ impl RenderableInner {
delta: GetPaneRenderChangesResponse, delta: GetPaneRenderChangesResponse,
bonus_lines: Vec<(StableRowIndex, Line)>, bonus_lines: Vec<(StableRowIndex, Line)>,
) { ) {
log::trace!(
"apply_changes_to_surface local={} remote={}",
self.local_pane_id,
self.remote_pane_id
);
let now = Instant::now(); let now = Instant::now();
self.poll_interval = BASE_POLL_INTERVAL; self.poll_interval = BASE_POLL_INTERVAL;
self.last_recv_time = now; self.last_recv_time = now;
@ -343,7 +348,12 @@ impl RenderableInner {
self.dimensions = delta.dimensions; self.dimensions = delta.dimensions;
self.title = delta.title; self.title = delta.title;
self.working_dir = delta.working_dir.map(Into::into); self.working_dir = delta.working_dir.map(Into::into);
log::trace!("server says: seqno from {} -> {}", self.seqno, delta.seqno); log::trace!(
"server says: seqno from {} -> {} for local_pane_id={}",
self.seqno,
delta.seqno,
self.local_pane_id
);
self.seqno = delta.seqno; self.seqno = delta.seqno;
let config = configuration(); let config = configuration();
@ -353,6 +363,10 @@ impl RenderableInner {
dirty.remove(stable_row); dirty.remove(stable_row);
} }
log::trace!(
"apply_changes_to_surface: Generate PaneOutput event for local={}",
self.local_pane_id
);
Mux::get() Mux::get()
.unwrap() .unwrap()
.notify(mux::MuxNotification::PaneOutput(self.local_pane_id)); .notify(mux::MuxNotification::PaneOutput(self.local_pane_id));
@ -393,7 +407,10 @@ impl RenderableInner {
if self.fetch_limiter.non_blocking_admittance_check(1) { if self.fetch_limiter.non_blocking_admittance_check(1) {
self.schedule_fetch_lines(to_fetch, now); self.schedule_fetch_lines(to_fetch, now);
} else { } else {
log::trace!("exceeded throttle, drop {:?}", to_fetch); log::warn!(
"exceeded fetch throttle, drop {:?} and mark stale",
to_fetch
);
for r in to_fetch.iter() { for r in to_fetch.iter() {
for stable_row in r.clone() { for stable_row in r.clone() {
self.make_stale(stable_row); self.make_stale(stable_row);
@ -556,6 +573,10 @@ impl RenderableInner {
} }
} }
} }
log::trace!(
"Generate PaneOutput event for local_pane_id={}",
local_pane_id
);
mux.notify(mux::MuxNotification::PaneOutput(local_pane_id)); mux.notify(mux::MuxNotification::PaneOutput(local_pane_id));
Ok(()) Ok(())
} }
@ -582,7 +603,7 @@ impl RenderableInner {
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let alive = match client let alive = match client
.client .client
.get_tab_render_changes(GetPaneRenderChanges { .get_pane_render_changes(GetPaneRenderChanges {
pane_id: remote_pane_id, pane_id: remote_pane_id,
}) })
.await .await

View File

@ -7,7 +7,7 @@ use mux::client::ClientId;
use mux::window::WindowId as MuxWindowId; use mux::window::WindowId as MuxWindowId;
use mux::{Mux, MuxNotification}; use mux::{Mux, MuxNotification};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::{BTreeMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use wezterm_term::{Alert, ClipboardSelection}; use wezterm_term::{Alert, ClipboardSelection};
@ -16,6 +16,7 @@ use wezterm_toast_notification::*;
pub struct GuiFrontEnd { pub struct GuiFrontEnd {
connection: Rc<Connection>, connection: Rc<Connection>,
switching_workspaces: RefCell<bool>, switching_workspaces: RefCell<bool>,
spawned_mux_window: RefCell<HashSet<MuxWindowId>>,
known_windows: RefCell<BTreeMap<Window, MuxWindowId>>, known_windows: RefCell<BTreeMap<Window, MuxWindowId>>,
client_id: Arc<ClientId>, client_id: Arc<ClientId>,
} }
@ -36,6 +37,7 @@ impl GuiFrontEnd {
let front_end = Rc::new(GuiFrontEnd { let front_end = Rc::new(GuiFrontEnd {
connection, connection,
switching_workspaces: RefCell::new(false), switching_workspaces: RefCell::new(false),
spawned_mux_window: RefCell::new(HashSet::new()),
known_windows: RefCell::new(BTreeMap::new()), known_windows: RefCell::new(BTreeMap::new()),
client_id: client_id.clone(), client_id: client_id.clone(),
}); });
@ -188,7 +190,7 @@ impl GuiFrontEnd {
let mut mux_windows = mux_windows.into_iter(); let mut mux_windows = mux_windows.into_iter();
for (window, _old_id) in unused.into_iter() { for (window, old_id) in unused.into_iter() {
if let Some(mux_window_id) = mux_windows.next() { if let Some(mux_window_id) = mux_windows.next() {
window.notify(TermWindowNotif::SwitchToMuxWindow(mux_window_id)); window.notify(TermWindowNotif::SwitchToMuxWindow(mux_window_id));
windows.insert(window, mux_window_id); windows.insert(window, mux_window_id);
@ -196,18 +198,37 @@ impl GuiFrontEnd {
// We have more windows than are in the new workspace; // We have more windows than are in the new workspace;
// we no longer need this one! // we no longer need this one!
window.close(); window.close();
front_end().spawned_mux_window.borrow_mut().remove(&old_id);
} }
} }
log::trace!("reconcile: windows -> {:?}", windows);
*self.known_windows.borrow_mut() = windows; *self.known_windows.borrow_mut() = windows;
// then spawn any new windows that are needed // then spawn any new windows that are needed
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
while let Some(mux_window_id) = mux_windows.next() { while let Some(mux_window_id) = mux_windows.next() {
if front_end().has_mux_window(mux_window_id)
|| front_end()
.spawned_mux_window
.borrow()
.contains(&mux_window_id)
{
continue;
}
front_end()
.spawned_mux_window
.borrow_mut()
.insert(mux_window_id);
log::trace!("Creating TermWindow for mux_window_id={}", mux_window_id);
if let Err(err) = TermWindow::new_window(mux_window_id).await { if let Err(err) = TermWindow::new_window(mux_window_id).await {
log::error!("Failed to create window: {:#}", err); log::error!("Failed to create window: {:#}", err);
let mux = Mux::get().expect("switching_workspaces to trigger on main thread"); let mux = Mux::get().expect("switching_workspaces to trigger on main thread");
mux.kill_window(mux_window_id); mux.kill_window(mux_window_id);
front_end()
.spawned_mux_window
.borrow_mut()
.remove(&mux_window_id);
} }
} }
*front_end().switching_workspaces.borrow_mut() = false; *front_end().switching_workspaces.borrow_mut() = false;
@ -215,6 +236,15 @@ impl GuiFrontEnd {
.detach(); .detach();
} }
fn has_mux_window(&self, mux_window_id: MuxWindowId) -> bool {
for &mux_id in self.known_windows.borrow().values() {
if mux_id == mux_window_id {
return true;
}
}
false
}
pub fn switch_workspace(&self, workspace: &str) { pub fn switch_workspace(&self, workspace: &str) {
let mux = Mux::get().expect("mux started and running on main thread"); let mux = Mux::get().expect("mux started and running on main thread");
mux.set_active_workspace_for_client(&self.client_id, workspace); mux.set_active_workspace_for_client(&self.client_id, workspace);

View File

@ -307,6 +307,7 @@ pub struct TermWindow {
/// Terminal dimensions /// Terminal dimensions
terminal_size: PtySize, terminal_size: PtySize,
pub mux_window_id: MuxWindowId, pub mux_window_id: MuxWindowId,
pub mux_window_id_for_subscriptions: Arc<Mutex<MuxWindowId>>,
pub render_metrics: RenderMetrics, pub render_metrics: RenderMetrics,
render_state: Option<RenderState>, render_state: Option<RenderState>,
input_map: InputMap, input_map: InputMap,
@ -730,6 +731,7 @@ impl TermWindow {
palette: None, palette: None,
focused: None, focused: None,
mux_window_id, mux_window_id,
mux_window_id_for_subscriptions: Arc::new(Mutex::new(mux_window_id)),
fonts: Rc::clone(&fontconfig), fonts: Rc::clone(&fontconfig),
render_metrics, render_metrics,
dimensions, dimensions,
@ -1091,6 +1093,7 @@ impl TermWindow {
} }
TermWindowNotif::SwitchToMuxWindow(mux_window_id) => { TermWindowNotif::SwitchToMuxWindow(mux_window_id) => {
self.mux_window_id = mux_window_id; self.mux_window_id = mux_window_id;
*self.mux_window_id_for_subscriptions.lock().unwrap() = mux_window_id;
self.pane_state.borrow_mut().clear(); self.pane_state.borrow_mut().clear();
self.tab_state.borrow_mut().clear(); self.tab_state.borrow_mut().clear();
self.current_highlight.take(); self.current_highlight.take();
@ -1178,6 +1181,11 @@ impl TermWindow {
let mux = Mux::get().expect("mux is calling us"); let mux = Mux::get().expect("mux is calling us");
if mux.get_window(mux_window_id).is_none() { if mux.get_window(mux_window_id).is_none() {
// Something inconsistent: cancel subscription // Something inconsistent: cancel subscription
log::error!(
"PaneOutput: wanted mux_window_id={} from mux, but \
was not found, cancel mux subscription",
mux_window_id
);
return false; return false;
} }
let _ = pane_id; let _ = pane_id;
@ -1217,10 +1225,11 @@ impl TermWindow {
fn subscribe_to_pane_updates(&self) { fn subscribe_to_pane_updates(&self) {
let window = self.window.clone().expect("window to be valid on startup"); let window = self.window.clone().expect("window to be valid on startup");
let mux_window_id = self.mux_window_id; let mux_window_id = Arc::clone(&self.mux_window_id_for_subscriptions);
let mux = Mux::get().expect("mux started and running on main thread"); let mux = Mux::get().expect("mux started and running on main thread");
let dead = Arc::new(AtomicBool::new(false)); let dead = Arc::new(AtomicBool::new(false));
mux.subscribe(move |n| { mux.subscribe(move |n| {
let mux_window_id = *mux_window_id.lock().unwrap();
Self::mux_pane_output_event_callback(n, &window, mux_window_id, &dead) Self::mux_pane_output_event_callback(n, &window, mux_window_id, &dead)
}); });
} }