diff --git a/src/frontend/gui/termwindow.rs b/src/frontend/gui/termwindow.rs index dcf7ec49a..bd3c55591 100644 --- a/src/frontend/gui/termwindow.rs +++ b/src/frontend/gui/termwindow.rs @@ -2038,10 +2038,12 @@ impl TermWindow { (size, dims) } else { // Resize of the window dimensions may result in changed terminal dimensions - let avail_width = dimensions.pixel_width - - (config.window_padding.left + self.effective_right_padding(&config)) as usize; - let avail_height = dimensions.pixel_height - - (config.window_padding.top + config.window_padding.bottom) as usize; + let avail_width = dimensions.pixel_width.saturating_sub( + (config.window_padding.left + self.effective_right_padding(&config)) as usize, + ); + let avail_height = dimensions.pixel_height.saturating_sub( + (config.window_padding.top + config.window_padding.bottom) as usize, + ); let rows = (avail_height / self.render_metrics.cell_size.height as usize) .saturating_sub(if self.show_tab_bar { 1 } else { 0 }); diff --git a/src/mux/mod.rs b/src/mux/mod.rs index 9fd7f56f3..9c4693a76 100644 --- a/src/mux/mod.rs +++ b/src/mux/mod.rs @@ -5,7 +5,7 @@ use crate::ratelim::RateLimiter; use crate::server::pollable::{pollable_channel, PollableReceiver, PollableSender}; use anyhow::{anyhow, Error}; use domain::{Domain, DomainId}; -use log::{debug, error}; +use log::error; use portable_pty::ExitStatus; use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; @@ -223,16 +223,16 @@ impl Mux { self.add_pane(&pane) } - pub fn remove_pane(&self, pane_id: PaneId) { - debug!("removing pane {}", pane_id); + fn remove_pane_internal(&self, pane_id: PaneId) { + log::debug!("removing pane {}", pane_id); if let Some(pane) = self.panes.borrow_mut().remove(&pane_id) { + log::debug!("killing pane {}", pane_id); pane.kill(); } - self.prune_dead_windows(); } - pub fn remove_tab(&self, tab_id: TabId) { - debug!("removing tab {}", tab_id); + fn remove_tab_internal(&self, tab_id: TabId) { + log::debug!("remove_tab_internal tab {}", tab_id); let mut pane_ids = vec![]; if let Some(tab) = self.tabs.borrow_mut().remove(&tab_id) { for pos in tab.iter_panes() { @@ -240,61 +240,65 @@ impl Mux { } } for pane_id in pane_ids { - self.remove_pane(pane_id); + self.remove_pane_internal(pane_id); } + } + + fn remove_window_internal(&self, window_id: WindowId) { + log::debug!("remove_window_internal {}", window_id); + let window = self.windows.borrow_mut().remove(&window_id); + if let Some(window) = window { + for tab in window.iter() { + self.remove_tab_internal(tab.tab_id()); + } + } + } + + pub fn remove_pane(&self, pane_id: PaneId) { + self.remove_pane_internal(pane_id); + self.prune_dead_windows(); + } + + pub fn remove_tab(&self, tab_id: TabId) { + self.remove_tab_internal(tab_id); self.prune_dead_windows(); } pub fn prune_dead_windows(&self) { let live_tab_ids: Vec = self.tabs.borrow().keys().cloned().collect(); - let mut windows = self.windows.borrow_mut(); let mut dead_windows = vec![]; - for (window_id, win) in windows.iter_mut() { - win.prune_dead_tabs(&live_tab_ids); - if win.is_empty() { - log::error!("prune_dead_windows: window is now empty"); - dead_windows.push(*window_id); - } - } + let dead_tab_ids: Vec; - let dead_tab_ids: Vec = self - .tabs - .borrow() - .iter() - .filter_map(|(&id, tab)| if tab.is_dead() { Some(id) } else { None }) - .collect(); + { + let mut windows = self.windows.borrow_mut(); + for (window_id, win) in windows.iter_mut() { + win.prune_dead_tabs(&live_tab_ids); + if win.is_empty() { + log::debug!("prune_dead_windows: window is now empty"); + dead_windows.push(*window_id); + } + } + + dead_tab_ids = self + .tabs + .borrow() + .iter() + .filter_map(|(&id, tab)| if tab.is_dead() { Some(id) } else { None }) + .collect(); + } for tab_id in dead_tab_ids { log::error!("tab {} is dead", tab_id); - self.tabs.borrow_mut().remove(&tab_id); + self.remove_tab_internal(tab_id); } - /* - let dead_pane_ids: Vec = self - .panes - .borrow() - .iter() - .filter_map(|(&id, pane)| if pane.is_dead() { Some(id) } else { None }) - .collect(); - - for pane_id in dead_pane_ids { - self.panes.borrow_mut().remove(&pane_id); - } - */ - for window_id in dead_windows { - error!("removing window {}", window_id); - windows.remove(&window_id); + self.remove_window_internal(window_id); } } pub fn kill_window(&self, window_id: WindowId) { - let mut windows = self.windows.borrow_mut(); - if let Some(window) = windows.remove(&window_id) { - for tab in window.iter() { - self.tabs.borrow_mut().remove(&tab.tab_id()); - } - } + self.remove_window_internal(window_id); } pub fn get_window(&self, window_id: WindowId) -> Option> { @@ -382,12 +386,28 @@ impl Mux { } pub fn domain_was_detached(&self, domain: DomainId) { - self.panes - .borrow_mut() - .retain(|_pane_id, pane| pane.domain_id() != domain); - // Ideally we'd do this here, but that seems to cause problems - // at the moment: - // self.prune_dead_windows(); + let mut dead_panes = vec![]; + for pane in self.panes.borrow().values() { + if pane.domain_id() == domain { + dead_panes.push(pane.pane_id()); + } + } + + { + let mut windows = self.windows.borrow_mut(); + for (_, win) in windows.iter_mut() { + for tab in win.iter() { + tab.kill_panes_in_domain(domain); + } + } + } + + log::error!("domain detached panes: {:?}", dead_panes); + for pane_id in dead_panes { + self.remove_pane_internal(pane_id); + } + + self.prune_dead_windows(); } } diff --git a/src/mux/tab.rs b/src/mux/tab.rs index f3ba5572e..1c48cc24c 100644 --- a/src/mux/tab.rs +++ b/src/mux/tab.rs @@ -762,6 +762,11 @@ impl Tab { /// first. For large resizes this tends to proportionally adjust /// the relative sizes of the elements in a split. pub fn resize(&self, size: PtySize) { + if size.rows == 0 || size.cols == 0 { + // Ignore "impossible" resize requests + return; + } + // Un-zoom first, so that the layout can be reasoned about // more easily. let was_zoomed = self.zoomed.borrow().is_some(); @@ -1150,7 +1155,13 @@ impl Tab { pub fn kill_active_pane(&self) -> bool { let active_idx = *self.active.borrow(); - self.remove_pane_if(|idx, _| idx == active_idx) + let killed = self.remove_pane_if(|idx, _| idx == active_idx); + log::debug!("kill_active_pane: killed={}", killed); + killed + } + + pub fn kill_panes_in_domain(&self, domain: DomainId) -> bool { + self.remove_pane_if(|_, pane| pane.domain_id() == domain) } fn remove_pane_if(&self, f: F) -> bool @@ -1197,6 +1208,7 @@ impl Tab { // We might be the root, for example if c.is_top() && c.is_leaf() { root.replace(Tree::Empty); + dead_panes.push(pane.pane_id()); } else { root.replace(c.tree()); } diff --git a/src/server/client.rs b/src/server/client.rs index f84485a00..e1e6819ed 100644 --- a/src/server/client.rs +++ b/src/server/client.rs @@ -252,7 +252,6 @@ fn client_thread( for (_, mut promise) in promises.into_iter() { promise.result(Err(anyhow!("{}", reason))); } - // FIXME: detach the domain here return Err(err).context("Error while decoding response pdu"); } }