1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-28 07:55:03 +03:00

wezterm: introduce concept of Pane to the Mux model

This adds an extra level of indirection to the Mux model;
previously we allowed for Windows to contain an ordered
collection of Tabs, where each Tab represented some kind
of pty.

This change effectively renames the existing Tab trait to Pane
and introduces a new Tab container, so the new model is that
a Window contains an ordered collection of Tabs, and each Tab
can have a single optional Pane.

refs: https://github.com/wez/wezterm/issues/157
This commit is contained in:
Wez Furlong 2020-06-26 16:49:07 -07:00
parent 3484478ba2
commit a6316315bb
27 changed files with 835 additions and 597 deletions

View File

@ -95,8 +95,8 @@ The default key bindings are:
| `CTRL` | `=` | `IncreaseFontSize` | | `CTRL` | `=` | `IncreaseFontSize` |
| `SUPER` | `0` | `ResetFontSize` | | `SUPER` | `0` | `ResetFontSize` |
| `CTRL` | `0` | `ResetFontSize` | | `CTRL` | `0` | `ResetFontSize` |
| `SUPER` | `t` | `SpawnTab="CurrentTabDomain"` | | `SUPER` | `t` | `SpawnTab="CurrentPaneDomain"` |
| `CTRL+SHIFT` | `t` | `SpawnTab="CurrentTabDomain"` | | `CTRL+SHIFT` | `t` | `SpawnTab="CurrentPaneDomain"` |
| `SUPER+SHIFT` | `T` | `SpawnTab="DefaultDomain"` | | `SUPER+SHIFT` | `T` | `SpawnTab="DefaultDomain"` |
| `SUPER` | `w` | `CloseCurrentTab` | | `SUPER` | `w` | `CloseCurrentTab` |
| `SUPER` | `1` | `ActivateTab=0` | | `SUPER` | `1` | `ActivateTab=0` |
@ -284,7 +284,7 @@ return {
-- Create a new tab in the default domain -- Create a new tab in the default domain
{key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab="DefaultDomain"}}, {key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab="DefaultDomain"}},
-- Create a new tab in the same domain as the current tab -- Create a new tab in the same domain as the current tab
{key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab="CurrentTabDomain"}}, {key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab="CurrentPaneDomain"}},
-- Create a tab in a named domain -- Create a tab in a named domain
{key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab={DomainName="unix"}}}, {key="t", mods="SHIFT|ALT", action=wezterm.action{SpawnTab={DomainName="unix"}}},
} }

View File

@ -74,7 +74,7 @@ impl FrontEnd for GuiFrontEnd {
fn spawn_new_window( fn spawn_new_window(
&self, &self,
fontconfig: &Rc<FontConfiguration>, fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>, tab: &Rc<Tab>,
window_id: MuxWindowId, window_id: MuxWindowId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
termwindow::TermWindow::new_window(&configuration(), fontconfig, tab, window_id) termwindow::TermWindow::new_window(&configuration(), fontconfig, tab, window_id)

View File

@ -2,7 +2,7 @@ use crate::frontend::gui::selection::{SelectionCoordinate, SelectionRange};
use crate::frontend::gui::termwindow::TermWindow; use crate::frontend::gui::termwindow::TermWindow;
use crate::mux::domain::DomainId; use crate::mux::domain::DomainId;
use crate::mux::renderable::*; use crate::mux::renderable::*;
use crate::mux::tab::{Tab, TabId}; use crate::mux::tab::{Pane, PaneId};
use portable_pty::PtySize; use portable_pty::PtySize;
use rangeset::RangeSet; use rangeset::RangeSet;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
@ -18,13 +18,13 @@ use wezterm_term::{
use window::WindowOps; use window::WindowOps;
pub struct CopyOverlay { pub struct CopyOverlay {
delegate: Rc<dyn Tab>, delegate: Rc<dyn Pane>,
render: RefCell<CopyRenderable>, render: RefCell<CopyRenderable>,
} }
struct CopyRenderable { struct CopyRenderable {
cursor: StableCursorPosition, cursor: StableCursorPosition,
delegate: Rc<dyn Tab>, delegate: Rc<dyn Pane>,
start: Option<SelectionCoordinate>, start: Option<SelectionCoordinate>,
viewport: Option<StableRowIndex>, viewport: Option<StableRowIndex>,
/// We use this to cancel ourselves later /// We use this to cancel ourselves later
@ -38,20 +38,20 @@ struct Dimensions {
} }
impl CopyOverlay { impl CopyOverlay {
pub fn with_tab(term_window: &TermWindow, tab: &Rc<dyn Tab>) -> Rc<dyn Tab> { pub fn with_pane(term_window: &TermWindow, pane: &Rc<dyn Pane>) -> Rc<dyn Pane> {
let mut cursor = tab.renderer().get_cursor_position(); let mut cursor = pane.renderer().get_cursor_position();
cursor.shape = termwiz::surface::CursorShape::SteadyBlock; cursor.shape = termwiz::surface::CursorShape::SteadyBlock;
let window = term_window.window.clone().unwrap(); let window = term_window.window.clone().unwrap();
let render = CopyRenderable { let render = CopyRenderable {
cursor, cursor,
window, window,
delegate: Rc::clone(tab), delegate: Rc::clone(pane),
start: None, start: None,
viewport: term_window.get_viewport(tab.tab_id()), viewport: term_window.get_viewport(pane.pane_id()),
}; };
Rc::new(CopyOverlay { Rc::new(CopyOverlay {
delegate: Rc::clone(tab), delegate: Rc::clone(pane),
render: RefCell::new(render), render: RefCell::new(render),
}) })
} }
@ -99,10 +99,10 @@ impl CopyRenderable {
} }
fn adjust_selection(&self, start: SelectionCoordinate, range: SelectionRange) { fn adjust_selection(&self, start: SelectionCoordinate, range: SelectionRange) {
let tab_id = self.delegate.tab_id(); let pane_id = self.delegate.pane_id();
self.window.apply(move |term_window, window| { self.window.apply(move |term_window, window| {
if let Some(term_window) = term_window.downcast_mut::<TermWindow>() { if let Some(term_window) = term_window.downcast_mut::<TermWindow>() {
let mut selection = term_window.selection(tab_id); let mut selection = term_window.selection(pane_id);
selection.start = Some(start); selection.start = Some(start);
selection.range = Some(range); selection.range = Some(range);
window.invalidate(); window.invalidate();
@ -152,10 +152,10 @@ impl CopyRenderable {
fn set_viewport(&self, row: Option<StableRowIndex>) { fn set_viewport(&self, row: Option<StableRowIndex>) {
let dims = self.delegate.renderer().get_dimensions(); let dims = self.delegate.renderer().get_dimensions();
let tab_id = self.delegate.tab_id(); let pane_id = self.delegate.pane_id();
self.window.apply(move |term_window, _window| { self.window.apply(move |term_window, _window| {
if let Some(term_window) = term_window.downcast_mut::<TermWindow>() { if let Some(term_window) = term_window.downcast_mut::<TermWindow>() {
term_window.set_viewport(tab_id, row, dims); term_window.set_viewport(pane_id, row, dims);
} }
Ok(()) Ok(())
}); });
@ -163,7 +163,7 @@ impl CopyRenderable {
fn close(&self) { fn close(&self) {
self.set_viewport(None); self.set_viewport(None);
TermWindow::schedule_cancel_overlay(self.window.clone(), self.delegate.tab_id()); TermWindow::schedule_cancel_overlay(self.window.clone(), self.delegate.pane_id());
} }
fn page_up(&mut self) { fn page_up(&mut self) {
@ -375,9 +375,9 @@ impl CopyRenderable {
} }
} }
impl Tab for CopyOverlay { impl Pane for CopyOverlay {
fn tab_id(&self) -> TabId { fn pane_id(&self) -> PaneId {
self.delegate.tab_id() self.delegate.pane_id()
} }
fn renderer(&self) -> RefMut<dyn Renderable> { fn renderer(&self) -> RefMut<dyn Renderable> {

View File

@ -1,5 +1,5 @@
use crate::frontend::gui::termwindow::TermWindow; use crate::frontend::gui::termwindow::TermWindow;
use crate::mux::tab::{Tab, TabId}; use crate::mux::tab::{Pane, Tab, TabId};
use crate::termwiztermtab::{allocate, TermWizTerminal}; use crate::termwiztermtab::{allocate, TermWizTerminal};
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
@ -16,10 +16,10 @@ pub use tabnavigator::tab_navigator;
pub fn start_overlay<T, F>( pub fn start_overlay<T, F>(
term_window: &TermWindow, term_window: &TermWindow,
tab: &Rc<dyn Tab>, tab: &Rc<Tab>,
func: F, func: F,
) -> ( ) -> (
Rc<dyn Tab>, Rc<dyn Pane>,
Pin<Box<dyn std::future::Future<Output = Option<anyhow::Result<T>>>>>, Pin<Box<dyn std::future::Future<Output = Option<anyhow::Result<T>>>>>,
) )
where where
@ -27,7 +27,9 @@ where
F: Send + 'static + FnOnce(TabId, TermWizTerminal) -> anyhow::Result<T>, F: Send + 'static + FnOnce(TabId, TermWizTerminal) -> anyhow::Result<T>,
{ {
let tab_id = tab.tab_id(); let tab_id = tab.tab_id();
let dims = tab.renderer().get_dimensions(); let pane = tab.get_active_pane().expect("tab to have pane");
// TODO: our overlay should overlap the overall contents of the pane
let dims = pane.renderer().get_dimensions();
let (tw_term, tw_tab) = allocate(dims.cols, dims.viewport_rows); let (tw_term, tw_tab) = allocate(dims.cols, dims.viewport_rows);
let window = term_window.window.clone().unwrap(); let window = term_window.window.clone().unwrap();

View File

@ -2,8 +2,8 @@ use crate::frontend::gui::selection::{SelectionCoordinate, SelectionRange};
use crate::frontend::gui::termwindow::TermWindow; use crate::frontend::gui::termwindow::TermWindow;
use crate::mux::domain::DomainId; use crate::mux::domain::DomainId;
use crate::mux::renderable::*; use crate::mux::renderable::*;
use crate::mux::tab::{Pane, PaneId};
use crate::mux::tab::{Pattern, SearchResult}; use crate::mux::tab::{Pattern, SearchResult};
use crate::mux::tab::{Tab, TabId};
use portable_pty::PtySize; use portable_pty::PtySize;
use rangeset::RangeSet; use rangeset::RangeSet;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
@ -20,7 +20,7 @@ use window::WindowOps;
pub struct SearchOverlay { pub struct SearchOverlay {
renderer: RefCell<SearchRenderable>, renderer: RefCell<SearchRenderable>,
delegate: Rc<dyn Tab>, delegate: Rc<dyn Pane>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -30,7 +30,7 @@ struct MatchResult {
} }
struct SearchRenderable { struct SearchRenderable {
delegate: Rc<dyn Tab>, delegate: Rc<dyn Pane>,
/// The text that the user entered /// The text that the user entered
pattern: Pattern, pattern: Pattern,
/// The most recently queried set of matches /// The most recently queried set of matches
@ -50,13 +50,17 @@ struct SearchRenderable {
} }
impl SearchOverlay { impl SearchOverlay {
pub fn with_tab(term_window: &TermWindow, tab: &Rc<dyn Tab>, pattern: Pattern) -> Rc<dyn Tab> { pub fn with_pane(
let viewport = term_window.get_viewport(tab.tab_id()); term_window: &TermWindow,
let dims = tab.renderer().get_dimensions(); pane: &Rc<dyn Pane>,
pattern: Pattern,
) -> Rc<dyn Pane> {
let viewport = term_window.get_viewport(pane.pane_id());
let dims = pane.renderer().get_dimensions();
let window = term_window.window.clone().unwrap(); let window = term_window.window.clone().unwrap();
let mut renderer = SearchRenderable { let mut renderer = SearchRenderable {
delegate: Rc::clone(tab), delegate: Rc::clone(pane),
pattern, pattern,
results: vec![], results: vec![],
by_line: HashMap::new(), by_line: HashMap::new(),
@ -75,7 +79,7 @@ impl SearchOverlay {
Rc::new(SearchOverlay { Rc::new(SearchOverlay {
renderer: RefCell::new(renderer), renderer: RefCell::new(renderer),
delegate: Rc::clone(tab), delegate: Rc::clone(pane),
}) })
} }
@ -93,9 +97,9 @@ impl SearchOverlay {
} }
} }
impl Tab for SearchOverlay { impl Pane for SearchOverlay {
fn tab_id(&self) -> TabId { fn pane_id(&self) -> PaneId {
self.delegate.tab_id() self.delegate.pane_id()
} }
fn renderer(&self) -> RefMut<dyn Renderable> { fn renderer(&self) -> RefMut<dyn Renderable> {
@ -263,15 +267,15 @@ impl SearchRenderable {
} }
fn close(&self) { fn close(&self) {
TermWindow::schedule_cancel_overlay(self.window.clone(), self.delegate.tab_id()); TermWindow::schedule_cancel_overlay(self.window.clone(), self.delegate.pane_id());
} }
fn set_viewport(&self, row: Option<StableRowIndex>) { fn set_viewport(&self, row: Option<StableRowIndex>) {
let dims = self.delegate.renderer().get_dimensions(); let dims = self.delegate.renderer().get_dimensions();
let tab_id = self.delegate.tab_id(); let pane_id = self.delegate.pane_id();
self.window.apply(move |term_window, _window| { self.window.apply(move |term_window, _window| {
if let Some(term_window) = term_window.downcast_mut::<TermWindow>() { if let Some(term_window) = term_window.downcast_mut::<TermWindow>() {
term_window.set_viewport(tab_id, row, dims); term_window.set_viewport(pane_id, row, dims);
} }
Ok(()) Ok(())
}); });
@ -337,20 +341,20 @@ impl SearchRenderable {
self.dirty_results.add(bar_pos); self.dirty_results.add(bar_pos);
if !self.pattern.is_empty() { if !self.pattern.is_empty() {
let tab: Rc<dyn Tab> = self.delegate.clone(); let pane: Rc<dyn Pane> = self.delegate.clone();
let window = self.window.clone(); let window = self.window.clone();
let pattern = self.pattern.clone(); let pattern = self.pattern.clone();
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let mut results = tab.search(pattern).await?; let mut results = pane.search(pattern).await?;
results.sort(); results.sort();
let tab_id = tab.tab_id(); let pane_id = pane.pane_id();
let mut results = Some(results); let mut results = Some(results);
window.apply(move |term_window, _window| { window.apply(move |term_window, _window| {
let term_window = term_window let term_window = term_window
.downcast_mut::<TermWindow>() .downcast_mut::<TermWindow>()
.expect("to be TermWindow"); .expect("to be TermWindow");
let state = term_window.tab_state(tab_id); let state = term_window.pane_state(pane_id);
if let Some(overlay) = state.overlay.as_ref() { if let Some(overlay) = state.overlay.as_ref() {
if let Some(search_overlay) = overlay.downcast_ref::<SearchOverlay>() { if let Some(search_overlay) = overlay.downcast_ref::<SearchOverlay>() {
let mut r = search_overlay.renderer.borrow_mut(); let mut r = search_overlay.renderer.borrow_mut();
@ -377,10 +381,10 @@ impl SearchRenderable {
} }
fn clear_selection(&mut self) { fn clear_selection(&mut self) {
let tab_id = self.delegate.tab_id(); let pane_id = self.delegate.pane_id();
self.window.apply(move |term_window, _window| { self.window.apply(move |term_window, _window| {
if let Some(term_window) = term_window.downcast_mut::<TermWindow>() { if let Some(term_window) = term_window.downcast_mut::<TermWindow>() {
let mut selection = term_window.selection(tab_id); let mut selection = term_window.selection(pane_id);
selection.start.take(); selection.start.take();
selection.range.take(); selection.range.take();
} }
@ -392,10 +396,10 @@ impl SearchRenderable {
self.result_pos.replace(n); self.result_pos.replace(n);
let result = self.results[n].clone(); let result = self.results[n].clone();
let tab_id = self.delegate.tab_id(); let pane_id = self.delegate.pane_id();
self.window.apply(move |term_window, _window| { self.window.apply(move |term_window, _window| {
if let Some(term_window) = term_window.downcast_mut::<TermWindow>() { if let Some(term_window) = term_window.downcast_mut::<TermWindow>() {
let mut selection = term_window.selection(tab_id); let mut selection = term_window.selection(pane_id);
let start = SelectionCoordinate { let start = SelectionCoordinate {
x: result.start_x, x: result.start_x,
y: result.start_y, y: result.start_y,

View File

@ -58,18 +58,22 @@ impl TabBarState {
let per_tab_overhead = 2; let per_tab_overhead = 2;
let system_overhead = 3; let system_overhead = 3;
let tab_titles: Vec<_> = window let tab_titles: Vec<String> = window
.iter() .iter()
.map(|w| { .map(|tab| {
let mut title = w.get_title(); if let Some(pane) = tab.get_active_pane() {
// We have a preferred soft minimum on tab width to make it let mut title = pane.get_title();
// easier to click on tab titles, but we'll still go below // We have a preferred soft minimum on tab width to make it
// this if there are too many tabs to fit the window at // easier to click on tab titles, but we'll still go below
// this width. // this if there are too many tabs to fit the window at
while title.len() < 5 { // this width.
title.push(' '); while title.len() < 5 {
title.push(' ');
}
title
} else {
"no pane".to_string()
} }
title
}) })
.collect(); .collect();
let titles_len: usize = tab_titles.iter().map(|s| unicode_column_width(s)).sum(); let titles_len: usize = tab_titles.iter().map(|s| unicode_column_width(s)).sum();

File diff suppressed because it is too large Load Diff

View File

@ -102,7 +102,7 @@ pub trait FrontEnd: Downcast {
fn spawn_new_window( fn spawn_new_window(
&self, &self,
fontconfig: &Rc<FontConfiguration>, fontconfig: &Rc<FontConfiguration>,
tab: &Rc<dyn Tab>, tab: &Rc<Tab>,
window_id: WindowId, window_id: WindowId,
) -> anyhow::Result<()>; ) -> anyhow::Result<()>;
} }

View File

@ -66,7 +66,7 @@ impl FrontEnd for MuxServerFrontEnd {
fn spawn_new_window( fn spawn_new_window(
&self, &self,
_fontconfig: &Rc<FontConfiguration>, _fontconfig: &Rc<FontConfiguration>,
_tab: &Rc<dyn Tab>, _tab: &Rc<Tab>,
_window_id: WindowId, _window_id: WindowId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
Ok(()) Ok(())

View File

@ -29,7 +29,7 @@ pub enum SpawnTabDomain {
/// Use the default domain /// Use the default domain
DefaultDomain, DefaultDomain,
/// Use the domain from the current tab in the associated window /// Use the domain from the current tab in the associated window
CurrentTabDomain, CurrentPaneDomain,
/// Use a specific domain by id /// Use a specific domain by id
Domain(DomainId), Domain(DomainId),
/// Use a specific domain by name /// Use a specific domain by name
@ -38,7 +38,7 @@ pub enum SpawnTabDomain {
impl Default for SpawnTabDomain { impl Default for SpawnTabDomain {
fn default() -> Self { fn default() -> Self {
Self::CurrentTabDomain Self::CurrentPaneDomain
} }
} }
@ -187,17 +187,17 @@ impl InputMap {
[ [
KeyModifiers::SUPER, KeyModifiers::SUPER,
KeyCode::Char('t'), KeyCode::Char('t'),
SpawnTab(SpawnTabDomain::CurrentTabDomain) SpawnTab(SpawnTabDomain::CurrentPaneDomain)
], ],
[ [
ctrl_shift, ctrl_shift,
KeyCode::Char('T'), KeyCode::Char('T'),
SpawnTab(SpawnTabDomain::CurrentTabDomain) SpawnTab(SpawnTabDomain::CurrentPaneDomain)
], ],
[ [
KeyModifiers::SUPER | KeyModifiers::SHIFT, KeyModifiers::SUPER | KeyModifiers::SHIFT,
KeyCode::Char('T'), KeyCode::Char('T'),
SpawnTab(SpawnTabDomain::CurrentTabDomain) SpawnTab(SpawnTabDomain::CurrentPaneDomain)
], ],
[KeyModifiers::SUPER, KeyCode::Char('1'), ActivateTab(0)], [KeyModifiers::SUPER, KeyCode::Char('1'), ActivateTab(0)],
[KeyModifiers::SUPER, KeyCode::Char('2'), ActivateTab(1)], [KeyModifiers::SUPER, KeyCode::Char('2'), ActivateTab(1)],

View File

@ -1,6 +1,6 @@
use crate::mux::domain::DomainId; use crate::mux::domain::DomainId;
use crate::mux::renderable::Renderable; use crate::mux::renderable::Renderable;
use crate::mux::tab::{alloc_tab_id, Tab, TabId}; use crate::mux::tab::{alloc_pane_id, Pane, PaneId};
use crate::mux::tab::{Pattern, SearchResult}; use crate::mux::tab::{Pattern, SearchResult};
use anyhow::Error; use anyhow::Error;
use async_trait::async_trait; use async_trait::async_trait;
@ -11,8 +11,8 @@ use url::Url;
use wezterm_term::color::ColorPalette; use wezterm_term::color::ColorPalette;
use wezterm_term::{Clipboard, KeyCode, KeyModifiers, MouseEvent, StableRowIndex, Terminal}; use wezterm_term::{Clipboard, KeyCode, KeyModifiers, MouseEvent, StableRowIndex, Terminal};
pub struct LocalTab { pub struct LocalPane {
tab_id: TabId, pane_id: PaneId,
terminal: RefCell<Terminal>, terminal: RefCell<Terminal>,
process: RefCell<Box<dyn Child>>, process: RefCell<Box<dyn Child>>,
pty: RefCell<Box<dyn MasterPty>>, pty: RefCell<Box<dyn MasterPty>>,
@ -20,10 +20,9 @@ pub struct LocalTab {
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Tab for LocalTab { impl Pane for LocalPane {
#[inline] fn pane_id(&self) -> PaneId {
fn tab_id(&self) -> TabId { self.pane_id
self.tab_id
} }
fn renderer(&self) -> RefMut<dyn Renderable> { fn renderer(&self) -> RefMut<dyn Renderable> {
@ -34,7 +33,7 @@ impl Tab for LocalTab {
if let Ok(None) = self.process.borrow_mut().try_wait() { if let Ok(None) = self.process.borrow_mut().try_wait() {
false false
} else { } else {
log::error!("is_dead: {:?}", self.tab_id); log::error!("is_dead: {:?}", self.pane_id);
true true
} }
} }
@ -213,16 +212,16 @@ impl Tab for LocalTab {
} }
} }
impl LocalTab { impl LocalPane {
pub fn new( pub fn new(
terminal: Terminal, terminal: Terminal,
process: Box<dyn Child>, process: Box<dyn Child>,
pty: Box<dyn MasterPty>, pty: Box<dyn MasterPty>,
domain_id: DomainId, domain_id: DomainId,
) -> Self { ) -> Self {
let tab_id = alloc_tab_id(); let pane_id = alloc_pane_id();
Self { Self {
tab_id, pane_id,
terminal: RefCell::new(terminal), terminal: RefCell::new(terminal),
process: RefCell::new(process), process: RefCell::new(process),
pty: RefCell::new(pty), pty: RefCell::new(pty),
@ -231,7 +230,7 @@ impl LocalTab {
} }
} }
impl Drop for LocalTab { impl Drop for LocalPane {
fn drop(&mut self) { fn drop(&mut self) {
// Avoid lingering zombies // Avoid lingering zombies
self.process.borrow_mut().kill().ok(); self.process.borrow_mut().kill().ok();

View File

@ -921,6 +921,10 @@ fn run() -> anyhow::Result<()> {
name: "TABID".to_string(), name: "TABID".to_string(),
alignment: Alignment::Right, alignment: Alignment::Right,
}, },
Column {
name: "PANEID".to_string(),
alignment: Alignment::Right,
},
Column { Column {
name: "SIZE".to_string(), name: "SIZE".to_string(),
alignment: Alignment::Left, alignment: Alignment::Left,
@ -940,6 +944,7 @@ fn run() -> anyhow::Result<()> {
data.push(vec![ data.push(vec![
entry.window_id.to_string(), entry.window_id.to_string(),
entry.tab_id.to_string(), entry.tab_id.to_string(),
entry.pane_id.to_string(),
format!("{}x{}", entry.size.cols, entry.size.rows), format!("{}x{}", entry.size.cols, entry.size.rows),
entry.title.clone(), entry.title.clone(),
entry entry

View File

@ -6,7 +6,8 @@
//! of an ssh session somewhere. //! of an ssh session somewhere.
use crate::config::configuration; use crate::config::configuration;
use crate::localtab::LocalTab; use crate::localtab::LocalPane;
use crate::mux::tab::Pane;
use crate::mux::tab::Tab; use crate::mux::tab::Tab;
use crate::mux::window::WindowId; use crate::mux::window::WindowId;
use crate::mux::Mux; use crate::mux::Mux;
@ -39,7 +40,7 @@ pub trait Domain: Downcast {
command: Option<CommandBuilder>, command: Option<CommandBuilder>,
command_dir: Option<String>, command_dir: Option<String>,
window: WindowId, window: WindowId,
) -> Result<Rc<dyn Tab>, Error>; ) -> Result<Rc<Tab>, Error>;
/// Returns false if the `spawn` method will never succeed. /// Returns false if the `spawn` method will never succeed.
/// There are some internal placeholder domains that are /// There are some internal placeholder domains that are
@ -102,7 +103,7 @@ impl Domain for LocalDomain {
command: Option<CommandBuilder>, command: Option<CommandBuilder>,
command_dir: Option<String>, command_dir: Option<String>,
window: WindowId, window: WindowId,
) -> Result<Rc<dyn Tab>, Error> { ) -> Result<Rc<Tab>, Error> {
let config = configuration(); let config = configuration();
let mut cmd = match command { let mut cmd = match command {
Some(mut cmd) => { Some(mut cmd) => {
@ -138,7 +139,10 @@ impl Domain for LocalDomain {
); );
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab: Rc<dyn Tab> = Rc::new(LocalTab::new(terminal, child, pair.master, self.id)); let pane: Rc<dyn Pane> = Rc::new(LocalPane::new(terminal, child, pair.master, self.id));
let tab = Rc::new(Tab::new());
tab.assign_pane(&pane);
mux.add_tab(&tab)?; mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window)?; mux.add_tab_to_window(&tab, window)?;

View File

@ -1,3 +1,4 @@
use crate::mux::tab::{Pane, PaneId};
use crate::mux::tab::{Tab, TabId}; use crate::mux::tab::{Tab, TabId};
use crate::mux::window::{Window, WindowId}; use crate::mux::window::{Window, WindowId};
use crate::ratelim::RateLimiter; use crate::ratelim::RateLimiter;
@ -22,7 +23,7 @@ pub mod window;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum MuxNotification { pub enum MuxNotification {
TabOutput(TabId), PaneOutput(PaneId),
} }
static SUB_ID: AtomicUsize = AtomicUsize::new(0); static SUB_ID: AtomicUsize = AtomicUsize::new(0);
@ -30,7 +31,8 @@ static SUB_ID: AtomicUsize = AtomicUsize::new(0);
pub type MuxSubscriber = PollableReceiver<MuxNotification>; pub type MuxSubscriber = PollableReceiver<MuxNotification>;
pub struct Mux { pub struct Mux {
tabs: RefCell<HashMap<TabId, Rc<dyn Tab>>>, tabs: RefCell<HashMap<TabId, Rc<Tab>>>,
panes: RefCell<HashMap<PaneId, Rc<dyn Pane>>>,
windows: RefCell<HashMap<WindowId, Window>>, windows: RefCell<HashMap<WindowId, Window>>,
default_domain: RefCell<Option<Arc<dyn Domain>>>, default_domain: RefCell<Option<Arc<dyn Domain>>>,
domains: RefCell<HashMap<DomainId, Arc<dyn Domain>>>, domains: RefCell<HashMap<DomainId, Arc<dyn Domain>>>,
@ -38,7 +40,7 @@ pub struct Mux {
subscribers: RefCell<HashMap<usize, PollableSender<MuxNotification>>>, subscribers: RefCell<HashMap<usize, PollableSender<MuxNotification>>>,
} }
fn read_from_tab_pty(tab_id: TabId, mut reader: Box<dyn std::io::Read>) { fn read_from_pane_pty(pane_id: PaneId, mut reader: Box<dyn std::io::Read>) {
const BUFSIZE: usize = 32 * 1024; const BUFSIZE: usize = 32 * 1024;
let mut buf = [0; BUFSIZE]; let mut buf = [0; BUFSIZE];
@ -47,11 +49,11 @@ fn read_from_tab_pty(tab_id: TabId, mut reader: Box<dyn std::io::Read>) {
loop { loop {
match reader.read(&mut buf) { match reader.read(&mut buf) {
Ok(size) if size == 0 => { Ok(size) if size == 0 => {
error!("read_pty EOF: tab_id {}", tab_id); error!("read_pty EOF: pane_id {}", pane_id);
break; break;
} }
Err(err) => { Err(err) => {
error!("read_pty failed: tab {} {:?}", tab_id, err); error!("read_pty failed: pane {} {:?}", pane_id, err);
break; break;
} }
Ok(size) => { Ok(size) => {
@ -66,9 +68,9 @@ fn read_from_tab_pty(tab_id: TabId, mut reader: Box<dyn std::io::Read>) {
pos += len; pos += len;
promise::spawn::spawn_into_main_thread_with_low_priority(async move { promise::spawn::spawn_into_main_thread_with_low_priority(async move {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
if let Some(tab) = mux.get_tab(tab_id) { if let Some(pane) = mux.get_pane(pane_id) {
tab.advance_bytes(&data); pane.advance_bytes(&data);
mux.notify(MuxNotification::TabOutput(tab_id)); mux.notify(MuxNotification::PaneOutput(pane_id));
} }
}); });
} }
@ -83,7 +85,7 @@ fn read_from_tab_pty(tab_id: TabId, mut reader: Box<dyn std::io::Read>) {
} }
promise::spawn::spawn_into_main_thread(async move { promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
mux.remove_tab(tab_id); mux.remove_pane(pane_id);
}); });
} }
@ -106,6 +108,7 @@ impl Mux {
Self { Self {
tabs: RefCell::new(HashMap::new()), tabs: RefCell::new(HashMap::new()),
panes: RefCell::new(HashMap::new()),
windows: RefCell::new(HashMap::new()), windows: RefCell::new(HashMap::new()),
default_domain: RefCell::new(default_domain), default_domain: RefCell::new(default_domain),
domains_by_name: RefCell::new(domains_by_name), domains_by_name: RefCell::new(domains_by_name),
@ -178,20 +181,38 @@ impl Mux {
res res
} }
pub fn get_tab(&self, tab_id: TabId) -> Option<Rc<dyn Tab>> { pub fn get_pane(&self, pane_id: PaneId) -> Option<Rc<dyn Pane>> {
self.panes.borrow().get(&pane_id).map(Rc::clone)
}
pub fn get_tab(&self, tab_id: TabId) -> Option<Rc<Tab>> {
self.tabs.borrow().get(&tab_id).map(Rc::clone) self.tabs.borrow().get(&tab_id).map(Rc::clone)
} }
pub fn add_tab(&self, tab: &Rc<dyn Tab>) -> Result<(), Error> { pub fn add_pane(&self, pane: &Rc<dyn Pane>) -> Result<(), Error> {
self.tabs.borrow_mut().insert(tab.tab_id(), Rc::clone(tab)); self.panes
.borrow_mut()
let reader = tab.reader()?; .insert(pane.pane_id(), Rc::clone(pane));
let tab_id = tab.tab_id(); let reader = pane.reader()?;
thread::spawn(move || read_from_tab_pty(tab_id, reader)); let pane_id = pane.pane_id();
thread::spawn(move || read_from_pane_pty(pane_id, reader));
Ok(()) Ok(())
} }
pub fn add_tab(&self, tab: &Rc<Tab>) -> Result<(), Error> {
self.tabs.borrow_mut().insert(tab.tab_id(), Rc::clone(tab));
let pane = tab
.get_active_pane()
.ok_or_else(|| anyhow!("tab MUST have an active pane"))?;
self.add_pane(&pane)
}
pub fn remove_pane(&self, pane_id: TabId) {
debug!("removing pane {}", pane_id);
self.panes.borrow_mut().remove(&pane_id);
self.prune_dead_windows();
}
pub fn remove_tab(&self, tab_id: TabId) { pub fn remove_tab(&self, tab_id: TabId) {
debug!("removing tab {}", tab_id); debug!("removing tab {}", tab_id);
self.tabs.borrow_mut().remove(&tab_id); self.tabs.borrow_mut().remove(&tab_id);
@ -220,6 +241,17 @@ impl Mux {
self.tabs.borrow_mut().remove(&tab_id); self.tabs.borrow_mut().remove(&tab_id);
} }
let dead_pane_ids: Vec<TabId> = 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 { for window_id in dead_windows {
error!("removing window {}", window_id); error!("removing window {}", window_id);
windows.remove(&window_id); windows.remove(&window_id);
@ -253,7 +285,7 @@ impl Mux {
})) }))
} }
pub fn get_active_tab_for_window(&self, window_id: WindowId) -> Option<Rc<dyn Tab>> { pub fn get_active_tab_for_window(&self, window_id: WindowId) -> Option<Rc<Tab>> {
let window = self.get_window(window_id)?; let window = self.get_window(window_id)?;
window.get_active().map(Rc::clone) window.get_active().map(Rc::clone)
} }
@ -265,7 +297,7 @@ impl Mux {
window_id window_id
} }
pub fn add_tab_to_window(&self, tab: &Rc<dyn Tab>, window_id: WindowId) -> anyhow::Result<()> { pub fn add_tab_to_window(&self, tab: &Rc<Tab>, window_id: WindowId) -> anyhow::Result<()> {
let mut window = self let mut window = self
.get_window_mut(window_id) .get_window_mut(window_id)
.ok_or_else(|| anyhow!("add_tab_to_window: no such window_id {}", window_id))?; .ok_or_else(|| anyhow!("add_tab_to_window: no such window_id {}", window_id))?;
@ -277,8 +309,8 @@ impl Mux {
self.tabs.borrow().is_empty() self.tabs.borrow().is_empty()
} }
pub fn iter_tabs(&self) -> Vec<Rc<dyn Tab>> { pub fn iter_panes(&self) -> Vec<Rc<dyn Pane>> {
self.tabs self.panes
.borrow() .borrow()
.iter() .iter()
.map(|(_, v)| Rc::clone(v)) .map(|(_, v)| Rc::clone(v))
@ -294,9 +326,9 @@ impl Mux {
} }
pub fn domain_was_detached(&self, domain: DomainId) { pub fn domain_was_detached(&self, domain: DomainId) {
self.tabs self.panes
.borrow_mut() .borrow_mut()
.retain(|_tab_id, tab| tab.domain_id() != domain); .retain(|_pane_id, pane| pane.domain_id() != domain);
// Ideally we'd do this here, but that seems to cause problems // Ideally we'd do this here, but that seems to cause problems
// at the moment: // at the moment:
// self.prune_dead_windows(); // self.prune_dead_windows();

View File

@ -5,7 +5,8 @@ use async_trait::async_trait;
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
use portable_pty::PtySize; use portable_pty::PtySize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cell::RefMut; use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use url::Url; use url::Url;
use wezterm_term::color::ColorPalette; use wezterm_term::color::ColorPalette;
@ -14,14 +15,17 @@ use wezterm_term::{Clipboard, KeyCode, KeyModifiers, MouseEvent, StableRowIndex}
static TAB_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0); static TAB_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
pub type TabId = usize; pub type TabId = usize;
pub fn alloc_tab_id() -> TabId { static PANE_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
TAB_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed) pub type PaneId = usize;
pub fn alloc_pane_id() -> PaneId {
PANE_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)
} }
const PASTE_CHUNK_SIZE: usize = 1024; const PASTE_CHUNK_SIZE: usize = 1024;
struct Paste { struct Paste {
tab_id: TabId, pane_id: PaneId,
text: String, text: String,
offset: usize, offset: usize,
} }
@ -31,12 +35,12 @@ fn schedule_next_paste(paste: &Arc<Mutex<Paste>>) {
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let mut locked = paste.lock().unwrap(); let mut locked = paste.lock().unwrap();
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux.get_tab(locked.tab_id).unwrap(); let pane = mux.get_pane(locked.pane_id).unwrap();
let remain = locked.text.len() - locked.offset; let remain = locked.text.len() - locked.offset;
let chunk = remain.min(PASTE_CHUNK_SIZE); let chunk = remain.min(PASTE_CHUNK_SIZE);
let text_slice = &locked.text[locked.offset..locked.offset + chunk]; let text_slice = &locked.text[locked.offset..locked.offset + chunk];
tab.send_paste(text_slice).unwrap(); pane.send_paste(text_slice).unwrap();
if chunk < remain { if chunk < remain {
// There is more to send // There is more to send
@ -84,9 +88,46 @@ pub struct SearchResult {
pub end_x: usize, pub end_x: usize,
} }
/// A Tab is a container of Panes
/// At this time only a single pane is supported
pub struct Tab {
id: TabId,
pane: RefCell<Option<Rc<dyn Pane>>>,
}
impl Tab {
pub fn new() -> Self {
Self {
id: TAB_ID.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed),
pane: RefCell::new(None),
}
}
pub fn tab_id(&self) -> TabId {
self.id
}
pub fn is_dead(&self) -> bool {
if let Some(pane) = self.get_active_pane() {
pane.is_dead()
} else {
true
}
}
pub fn get_active_pane(&self) -> Option<Rc<dyn Pane>> {
self.pane.borrow().as_ref().map(Rc::clone)
}
pub fn assign_pane(&self, pane: &Rc<dyn Pane>) {
self.pane.borrow_mut().replace(Rc::clone(pane));
}
}
/// A Pane represents a view on a terminal
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait Tab: Downcast { pub trait Pane: Downcast {
fn tab_id(&self) -> TabId; fn pane_id(&self) -> PaneId;
fn renderer(&self) -> RefMut<dyn Renderable>; fn renderer(&self) -> RefMut<dyn Renderable>;
fn get_title(&self) -> String; fn get_title(&self) -> String;
fn send_paste(&self, text: &str) -> anyhow::Result<()>; fn send_paste(&self, text: &str) -> anyhow::Result<()>;
@ -131,7 +172,7 @@ pub trait Tab: Downcast {
self.send_paste(&text[0..PASTE_CHUNK_SIZE])?; self.send_paste(&text[0..PASTE_CHUNK_SIZE])?;
let paste = Arc::new(Mutex::new(Paste { let paste = Arc::new(Mutex::new(Paste {
tab_id: self.tab_id(), pane_id: self.pane_id(),
text, text,
offset: PASTE_CHUNK_SIZE, offset: PASTE_CHUNK_SIZE,
})); }));
@ -140,4 +181,4 @@ pub trait Tab: Downcast {
Ok(()) Ok(())
} }
} }
impl_downcast!(Tab); impl_downcast!(Pane);

View File

@ -8,7 +8,7 @@ pub type WindowId = usize;
pub struct Window { pub struct Window {
id: WindowId, id: WindowId,
tabs: Vec<Rc<dyn Tab>>, tabs: Vec<Rc<Tab>>,
active: usize, active: usize,
clipboard: Option<Arc<dyn Clipboard>>, clipboard: Option<Arc<dyn Clipboard>>,
invalidated: bool, invalidated: bool,
@ -33,26 +33,28 @@ impl Window {
self.id self.id
} }
fn check_that_tab_isnt_already_in_window(&self, tab: &Rc<dyn Tab>) { fn check_that_tab_isnt_already_in_window(&self, tab: &Rc<Tab>) {
for t in &self.tabs { for t in &self.tabs {
assert_ne!(t.tab_id(), tab.tab_id(), "tab already added to this window"); assert_ne!(t.tab_id(), tab.tab_id(), "tab already added to this window");
} }
} }
fn assign_clipboard_to_tab(&self, tab: &Rc<dyn Tab>) { fn assign_clipboard_to_tab(&self, tab: &Rc<Tab>) {
if let Some(clip) = self.clipboard.as_ref() { if let Some(clip) = self.clipboard.as_ref() {
tab.set_clipboard(clip); if let Some(pane) = tab.get_active_pane() {
pane.set_clipboard(clip);
}
} }
} }
pub fn insert(&mut self, index: usize, tab: &Rc<dyn Tab>) { pub fn insert(&mut self, index: usize, tab: &Rc<Tab>) {
self.check_that_tab_isnt_already_in_window(tab); self.check_that_tab_isnt_already_in_window(tab);
self.assign_clipboard_to_tab(tab); self.assign_clipboard_to_tab(tab);
self.tabs.insert(index, Rc::clone(tab)); self.tabs.insert(index, Rc::clone(tab));
self.invalidated = true; self.invalidated = true;
} }
pub fn push(&mut self, tab: &Rc<dyn Tab>) { pub fn push(&mut self, tab: &Rc<Tab>) {
self.check_that_tab_isnt_already_in_window(tab); self.check_that_tab_isnt_already_in_window(tab);
self.assign_clipboard_to_tab(tab); self.assign_clipboard_to_tab(tab);
self.tabs.push(Rc::clone(tab)); self.tabs.push(Rc::clone(tab));
@ -67,7 +69,7 @@ impl Window {
self.tabs.len() self.tabs.len()
} }
pub fn get_by_idx(&self, idx: usize) -> Option<&Rc<dyn Tab>> { pub fn get_by_idx(&self, idx: usize) -> Option<&Rc<Tab>> {
self.tabs.get(idx) self.tabs.get(idx)
} }
@ -80,7 +82,7 @@ impl Window {
None None
} }
pub fn remove_by_idx(&mut self, idx: usize) -> Rc<dyn Tab> { pub fn remove_by_idx(&mut self, idx: usize) -> Rc<Tab> {
self.invalidated = true; self.invalidated = true;
self.tabs.remove(idx) self.tabs.remove(idx)
} }
@ -104,7 +106,7 @@ impl Window {
res res
} }
pub fn get_active(&self) -> Option<&Rc<dyn Tab>> { pub fn get_active(&self) -> Option<&Rc<Tab>> {
self.get_by_idx(self.active) self.get_by_idx(self.active)
} }
@ -119,7 +121,7 @@ impl Window {
self.active = idx; self.active = idx;
} }
pub fn iter(&self) -> impl Iterator<Item = &Rc<dyn Tab>> { pub fn iter(&self) -> impl Iterator<Item = &Rc<Tab>> {
self.tabs.iter() self.tabs.iter()
} }
@ -128,10 +130,14 @@ impl Window {
.tabs .tabs
.iter() .iter()
.filter_map(|tab| { .filter_map(|tab| {
if tab.is_dead() { if let Some(pane) = tab.get_active_pane() {
Some(tab.tab_id()) if pane.is_dead() {
return Some(tab.tab_id());
} else {
None
}
} else { } else {
None Some(tab.tab_id())
} }
}) })
.collect(); .collect();

View File

@ -6,7 +6,7 @@ use crate::mux::Mux;
use crate::server::codec::*; use crate::server::codec::*;
use crate::server::domain::{ClientDomain, ClientDomainConfig}; use crate::server::domain::{ClientDomain, ClientDomainConfig};
use crate::server::pollable::*; use crate::server::pollable::*;
use crate::server::tab::ClientTab; use crate::server::tab::ClientPane;
use crate::server::UnixStream; use crate::server::UnixStream;
use crate::ssh::ssh_connect_with_ui; use crate::ssh::ssh_connect_with_ui;
use anyhow::{anyhow, bail, Context, Error}; use anyhow::{anyhow, bail, Context, Error};
@ -89,7 +89,7 @@ macro_rules! rpc {
} }
fn process_unilateral(local_domain_id: DomainId, decoded: DecodedPdu) -> anyhow::Result<()> { fn process_unilateral(local_domain_id: DomainId, decoded: DecodedPdu) -> anyhow::Result<()> {
if let Some(tab_id) = decoded.pdu.tab_id() { if let Some(pane_id) = decoded.pdu.pane_id() {
let pdu = decoded.pdu; let pdu = decoded.pdu;
promise::spawn::spawn_into_main_thread(async move { promise::spawn::spawn_into_main_thread(async move {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
@ -102,27 +102,30 @@ fn process_unilateral(local_domain_id: DomainId, decoded: DecodedPdu) -> anyhow:
anyhow!("domain {} is not a ClientDomain instance", local_domain_id) anyhow!("domain {} is not a ClientDomain instance", local_domain_id)
})?; })?;
let local_tab_id = client_domain let local_pane_id =
.remote_to_local_tab_id(tab_id) client_domain
.ok_or_else(|| anyhow!("remote tab id {} does not have a local tab id", tab_id))?; .remote_to_local_pane_id(pane_id)
let tab = mux .ok_or_else(|| {
.get_tab(local_tab_id) anyhow!("remote pane id {} does not have a local pane id", pane_id)
.ok_or_else(|| anyhow!("no such tab {}", local_tab_id))?; })?;
let client_tab = tab.downcast_ref::<ClientTab>().ok_or_else(|| { let pane = mux
.get_pane(local_pane_id)
.ok_or_else(|| anyhow!("no such pane {}", local_pane_id))?;
let client_pane = pane.downcast_ref::<ClientPane>().ok_or_else(|| {
log::error!( log::error!(
"received unilateral PDU for tab {} which is \ "received unilateral PDU for pane {} which is \
not an instance of ClientTab: {:?}", not an instance of ClientPane: {:?}",
local_tab_id, local_pane_id,
pdu pdu
); );
anyhow!( anyhow!(
"received unilateral PDU for tab {} which is \ "received unilateral PDU for pane {} which is \
not an instance of ClientTab: {:?}", not an instance of ClientPane: {:?}",
local_tab_id, local_pane_id,
pdu pdu
) )
})?; })?;
client_tab.process_unilateral(pdu) client_pane.process_unilateral(pdu)
}); });
} else { } else {
bail!("don't know how to handle {:?}", decoded); bail!("don't know how to handle {:?}", decoded);
@ -871,22 +874,22 @@ impl Client {
rpc!(ping, Ping = (), Pong); rpc!(ping, Ping = (), Pong);
rpc!(list_tabs, ListTabs = (), ListTabsResponse); rpc!(list_tabs, ListTabs = (), ListTabsResponse);
rpc!(spawn, Spawn, SpawnResponse); rpc!(spawn, Spawn, SpawnResponse);
rpc!(write_to_tab, WriteToTab, UnitResponse); rpc!(write_to_pane, WriteToPane, UnitResponse);
rpc!(send_paste, SendPaste, UnitResponse); rpc!(send_paste, SendPaste, UnitResponse);
rpc!(key_down, SendKeyDown, UnitResponse); rpc!(key_down, SendKeyDown, UnitResponse);
rpc!(mouse_event, SendMouseEvent, UnitResponse); rpc!(mouse_event, SendMouseEvent, UnitResponse);
rpc!(resize, Resize, UnitResponse); rpc!(resize, Resize, UnitResponse);
rpc!( rpc!(
get_tab_render_changes, get_tab_render_changes,
GetTabRenderChanges, GetPaneRenderChanges,
TabLivenessResponse LivenessResponse
); );
rpc!(get_lines, GetLines, GetLinesResponse); rpc!(get_lines, GetLines, GetLinesResponse);
rpc!(get_codec_version, GetCodecVersion, GetCodecVersionResponse); rpc!(get_codec_version, GetCodecVersion, GetCodecVersionResponse);
rpc!(get_tls_creds, GetTlsCreds = (), GetTlsCredsResponse); rpc!(get_tls_creds, GetTlsCreds = (), GetTlsCredsResponse);
rpc!( rpc!(
search_scrollback, search_scrollback,
SearchTabScrollbackRequest, SearchScrollbackRequest,
SearchTabScrollbackResponse SearchScrollbackResponse
); );
} }

View File

@ -13,7 +13,7 @@
use crate::mux::domain::DomainId; use crate::mux::domain::DomainId;
use crate::mux::renderable::{RenderableDimensions, StableCursorPosition}; use crate::mux::renderable::{RenderableDimensions, StableCursorPosition};
use crate::mux::tab::TabId; use crate::mux::tab::{PaneId, TabId};
use crate::mux::window::WindowId; use crate::mux::window::WindowId;
use anyhow::{bail, Context as _, Error}; use anyhow::{bail, Context as _, Error};
use leb128; use leb128;
@ -246,7 +246,7 @@ macro_rules! pdu {
/// The overall version of the codec. /// The overall version of the codec.
/// This must be bumped when backwards incompatible changes /// This must be bumped when backwards incompatible changes
/// are made to the types and protocol. /// are made to the types and protocol.
pub const CODEC_VERSION: usize = 4; pub const CODEC_VERSION: usize = 5;
// Defines the Pdu enum. // Defines the Pdu enum.
// Each struct has an explicit identifying number. // Each struct has an explicit identifying number.
@ -260,7 +260,7 @@ pdu! {
ListTabsResponse: 4, ListTabsResponse: 4,
Spawn: 7, Spawn: 7,
SpawnResponse: 8, SpawnResponse: 8,
WriteToTab: 9, WriteToPane: 9,
UnitResponse: 10, UnitResponse: 10,
SendKeyDown: 11, SendKeyDown: 11,
SendMouseEvent: 12, SendMouseEvent: 12,
@ -269,15 +269,15 @@ pdu! {
SetClipboard: 20, SetClipboard: 20,
GetLines: 22, GetLines: 22,
GetLinesResponse: 23, GetLinesResponse: 23,
GetTabRenderChanges: 24, GetPaneRenderChanges: 24,
GetTabRenderChangesResponse: 25, GetPaneRenderChangesResponse: 25,
GetCodecVersion: 26, GetCodecVersion: 26,
GetCodecVersionResponse: 27, GetCodecVersionResponse: 27,
GetTlsCreds: 28, GetTlsCreds: 28,
GetTlsCredsResponse: 29, GetTlsCredsResponse: 29,
TabLivenessResponse: 30, LivenessResponse: 30,
SearchTabScrollbackRequest: 31, SearchScrollbackRequest: 31,
SearchTabScrollbackResponse: 32, SearchScrollbackResponse: 32,
} }
impl Pdu { impl Pdu {
@ -347,12 +347,12 @@ impl Pdu {
} }
} }
pub fn tab_id(&self) -> Option<TabId> { pub fn pane_id(&self) -> Option<PaneId> {
match self { match self {
Pdu::GetTabRenderChangesResponse(GetTabRenderChangesResponse { tab_id, .. }) => { Pdu::GetPaneRenderChangesResponse(GetPaneRenderChangesResponse { pane_id, .. }) => {
Some(*tab_id) Some(*pane_id)
} }
Pdu::SetClipboard(SetClipboard { tab_id, .. }) => Some(*tab_id), Pdu::SetClipboard(SetClipboard { pane_id, .. }) => Some(*pane_id),
_ => None, _ => None,
} }
} }
@ -431,8 +431,10 @@ impl Into<String> for SerdeUrl {
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct WindowAndTabEntry { pub struct WindowAndTabEntry {
// FIXME: rename to WindowTabPaneEntry ?
pub window_id: WindowId, pub window_id: WindowId,
pub tab_id: TabId, pub tab_id: TabId,
pub pane_id: TabId,
pub title: String, pub title: String,
pub size: PtySize, pub size: PtySize,
pub working_dir: Option<SerdeUrl>, pub working_dir: Option<SerdeUrl>,
@ -456,24 +458,25 @@ pub struct Spawn {
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SpawnResponse { pub struct SpawnResponse {
pub tab_id: TabId, pub tab_id: TabId,
pub pane_id: PaneId,
pub window_id: WindowId, pub window_id: WindowId,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct WriteToTab { pub struct WriteToPane {
pub tab_id: TabId, pub pane_id: PaneId,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SendPaste { pub struct SendPaste {
pub tab_id: TabId, pub pane_id: PaneId,
pub data: String, pub data: String,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SendKeyDown { pub struct SendKeyDown {
pub tab_id: TabId, pub pane_id: TabId,
pub event: termwiz::input::KeyEvent, pub event: termwiz::input::KeyEvent,
pub input_serial: InputSerial, pub input_serial: InputSerial,
} }
@ -514,36 +517,36 @@ impl Into<InputSerial> for std::time::SystemTime {
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SendMouseEvent { pub struct SendMouseEvent {
pub tab_id: TabId, pub pane_id: PaneId,
pub event: wezterm_term::input::MouseEvent, pub event: wezterm_term::input::MouseEvent,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SetClipboard { pub struct SetClipboard {
pub tab_id: TabId, pub pane_id: PaneId,
pub clipboard: Option<String>, pub clipboard: Option<String>,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct Resize { pub struct Resize {
pub tab_id: TabId, pub pane_id: PaneId,
pub size: PtySize, pub size: PtySize,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct GetTabRenderChanges { pub struct GetPaneRenderChanges {
pub tab_id: TabId, pub pane_id: PaneId,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct TabLivenessResponse { pub struct LivenessResponse {
pub tab_id: TabId, pub pane_id: PaneId,
pub tab_alive: bool, pub is_alive: bool,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct GetTabRenderChangesResponse { pub struct GetPaneRenderChangesResponse {
pub tab_id: TabId, pub pane_id: PaneId,
pub mouse_grabbed: bool, pub mouse_grabbed: bool,
pub cursor_position: StableCursorPosition, pub cursor_position: StableCursorPosition,
pub dimensions: RenderableDimensions, pub dimensions: RenderableDimensions,
@ -559,7 +562,7 @@ pub struct GetTabRenderChangesResponse {
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct GetLines { pub struct GetLines {
pub tab_id: TabId, pub pane_id: PaneId,
pub lines: Vec<Range<StableRowIndex>>, pub lines: Vec<Range<StableRowIndex>>,
} }
@ -696,18 +699,18 @@ impl Into<Vec<(StableRowIndex, Line)>> for SerializedLines {
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct GetLinesResponse { pub struct GetLinesResponse {
pub tab_id: TabId, pub pane_id: PaneId,
pub lines: SerializedLines, pub lines: SerializedLines,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SearchTabScrollbackRequest { pub struct SearchScrollbackRequest {
pub tab_id: TabId, pub pane_id: PaneId,
pub pattern: crate::mux::tab::Pattern, pub pattern: crate::mux::tab::Pattern,
} }
#[derive(Deserialize, Serialize, PartialEq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct SearchTabScrollbackResponse { pub struct SearchScrollbackResponse {
pub results: Vec<crate::mux::tab::SearchResult>, pub results: Vec<crate::mux::tab::SearchResult>,
} }

View File

@ -3,12 +3,12 @@ use crate::connui::ConnectionUI;
use crate::font::FontConfiguration; use crate::font::FontConfiguration;
use crate::frontend::front_end; use crate::frontend::front_end;
use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState}; use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
use crate::mux::tab::{Tab, TabId}; use crate::mux::tab::{Pane, PaneId, Tab, TabId};
use crate::mux::window::WindowId; use crate::mux::window::WindowId;
use crate::mux::Mux; use crate::mux::Mux;
use crate::server::client::Client; use crate::server::client::Client;
use crate::server::codec::{ListTabsResponse, Spawn}; use crate::server::codec::{ListTabsResponse, Spawn};
use crate::server::tab::ClientTab; use crate::server::tab::ClientPane;
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use async_trait::async_trait; use async_trait::async_trait;
use portable_pty::{CommandBuilder, PtySize}; use portable_pty::{CommandBuilder, PtySize};
@ -24,6 +24,7 @@ pub struct ClientInner {
pub remote_domain_id: DomainId, pub remote_domain_id: DomainId,
remote_to_local_window: Mutex<HashMap<WindowId, WindowId>>, remote_to_local_window: Mutex<HashMap<WindowId, WindowId>>,
remote_to_local_tab: Mutex<HashMap<TabId, TabId>>, remote_to_local_tab: Mutex<HashMap<TabId, TabId>>,
remote_to_local_pane: Mutex<HashMap<PaneId, PaneId>>,
} }
impl ClientInner { impl ClientInner {
@ -56,31 +57,55 @@ impl ClientInner {
None None
} }
pub fn remove_old_tab_mapping(&self, remote_tab_id: TabId) { pub fn remote_to_local_pane_id(&self, remote_pane_id: PaneId) -> Option<TabId> {
let mut tab_map = self.remote_to_local_tab.lock().unwrap(); let mut pane_map = self.remote_to_local_pane.lock().unwrap();
tab_map.remove(&remote_tab_id);
}
pub fn remote_to_local_tab_id(&self, remote_tab_id: TabId) -> Option<TabId> { if let Some(id) = pane_map.get(&remote_pane_id) {
let mut tab_map = self.remote_to_local_tab.lock().unwrap();
if let Some(id) = tab_map.get(&remote_tab_id) {
return Some(*id); return Some(*id);
} }
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
for tab in mux.iter_tabs() { for pane in mux.iter_panes() {
if let Some(tab) = tab.downcast_ref::<ClientTab>() { if let Some(pane) = pane.downcast_ref::<ClientPane>() {
if tab.remote_tab_id() == remote_tab_id { if pane.remote_pane_id() == remote_pane_id {
let local_tab_id = tab.tab_id(); let local_pane_id = pane.pane_id();
tab_map.insert(remote_tab_id, local_tab_id); pane_map.insert(remote_pane_id, local_pane_id);
return Some(local_tab_id); return Some(local_pane_id);
} }
} }
} }
None None
} }
pub fn remove_old_pane_mapping(&self, remote_pane_id: PaneId) {
let mut pane_map = self.remote_to_local_pane.lock().unwrap();
pane_map.remove(&remote_pane_id);
}
pub fn remove_old_tab_mapping(&self, remote_tab_id: TabId) {
let mut tab_map = self.remote_to_local_tab.lock().unwrap();
tab_map.remove(&remote_tab_id);
}
fn record_remote_to_local_tab_mapping(&self, remote_tab_id: TabId, local_tab_id: TabId) {
let mut map = self.remote_to_local_tab.lock().unwrap();
map.insert(remote_tab_id, local_tab_id);
log::info!(
"record_remote_to_local_tab_mapping: {} -> {}",
remote_tab_id,
local_tab_id
);
}
pub fn remote_to_local_tab_id(&self, remote_tab_id: TabId) -> Option<TabId> {
let map = self.remote_to_local_tab.lock().unwrap();
for (remote, local) in map.iter() {
if *remote == remote_tab_id {
return Some(*local);
}
}
None
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -131,6 +156,7 @@ impl ClientInner {
remote_domain_id, remote_domain_id,
remote_to_local_window: Mutex::new(HashMap::new()), remote_to_local_window: Mutex::new(HashMap::new()),
remote_to_local_tab: Mutex::new(HashMap::new()), remote_to_local_tab: Mutex::new(HashMap::new()),
remote_to_local_pane: Mutex::new(HashMap::new()),
} }
} }
} }
@ -165,9 +191,9 @@ impl ClientDomain {
mux.domain_was_detached(self.local_domain_id); mux.domain_was_detached(self.local_domain_id);
} }
pub fn remote_to_local_tab_id(&self, remote_tab_id: TabId) -> Option<TabId> { pub fn remote_to_local_pane_id(&self, remote_pane_id: TabId) -> Option<TabId> {
let inner = self.inner()?; let inner = self.inner()?;
inner.remote_to_local_tab_id(remote_tab_id) inner.remote_to_local_pane_id(remote_pane_id)
} }
pub fn get_client_inner_for_domain(domain_id: DomainId) -> anyhow::Result<Arc<ClientInner>> { pub fn get_client_inner_for_domain(domain_id: DomainId) -> anyhow::Result<Arc<ClientInner>> {
@ -215,28 +241,47 @@ impl ClientDomain {
// removed it from the mux. Let's add it back, but // removed it from the mux. Let's add it back, but
// with a new id. // with a new id.
inner.remove_old_tab_mapping(entry.tab_id); inner.remove_old_tab_mapping(entry.tab_id);
tab = Rc::new(ClientTab::new( tab = Rc::new(Tab::new());
inner.record_remote_to_local_tab_mapping(entry.tab_id, tab.tab_id());
}
};
} else {
tab = Rc::new(Tab::new());
inner.record_remote_to_local_tab_mapping(entry.tab_id, tab.tab_id());
}
if let Some(pane_id) = inner.remote_to_local_pane_id(entry.pane_id) {
match mux.get_pane(pane_id) {
Some(_pane) => {}
None => {
// We likely decided that we hit EOF on the tab and
// removed it from the mux. Let's add it back, but
// with a new id.
inner.remove_old_pane_mapping(entry.pane_id);
let pane: Rc<dyn Pane> = Rc::new(ClientPane::new(
&inner, &inner,
entry.tab_id, entry.pane_id,
entry.size, entry.size,
&entry.title, &entry.title,
)); ));
mux.add_tab(&tab)?; tab.assign_pane(&pane);
mux.add_pane(&pane)?;
} }
}; };
} else { } else {
log::info!( log::info!(
"attaching to remote tab {} in remote window {} {}", "attaching to remote pane {} in remote window {} {}",
entry.tab_id, entry.pane_id,
entry.window_id, entry.window_id,
entry.title entry.title
); );
tab = Rc::new(ClientTab::new( let pane: Rc<dyn Pane> = Rc::new(ClientPane::new(
&inner, &inner,
entry.tab_id, entry.pane_id,
entry.size, entry.size,
&entry.title, &entry.title,
)); ));
tab.assign_pane(&pane);
mux.add_tab(&tab)?; mux.add_tab(&tab)?;
} }
@ -306,7 +351,7 @@ impl Domain for ClientDomain {
command: Option<CommandBuilder>, command: Option<CommandBuilder>,
command_dir: Option<String>, command_dir: Option<String>,
window: WindowId, window: WindowId,
) -> anyhow::Result<Rc<dyn Tab>> { ) -> anyhow::Result<Rc<Tab>> {
let inner = self let inner = self
.inner() .inner()
.ok_or_else(|| anyhow!("domain is not attached"))?; .ok_or_else(|| anyhow!("domain is not attached"))?;
@ -326,7 +371,10 @@ impl Domain for ClientDomain {
result.tab_id result.tab_id
}; };
let tab: Rc<dyn Tab> = Rc::new(ClientTab::new(&inner, remote_tab_id, size, "wezterm")); let pane: Rc<dyn Pane> = Rc::new(ClientPane::new(&inner, remote_tab_id, size, "wezterm"));
let tab = Rc::new(Tab::new());
tab.assign_pane(&pane);
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
mux.add_tab(&tab)?; mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window)?; mux.add_tab_to_window(&tab, window)?;

View File

@ -38,7 +38,7 @@ impl<S: ReadAndWrite> ClientSession<S> {
fn process(&mut self) -> Result<(), Error> { fn process(&mut self) -> Result<(), Error> {
let mut read_buffer = Vec::with_capacity(1024); let mut read_buffer = Vec::with_capacity(1024);
let mut tabs_to_output = HashSet::new(); let mut panes_to_output = HashSet::new();
loop { loop {
loop { loop {
@ -56,15 +56,15 @@ impl<S: ReadAndWrite> ClientSession<S> {
match self.mux_rx.try_recv() { match self.mux_rx.try_recv() {
Ok(notif) => match notif { Ok(notif) => match notif {
// Coalesce multiple TabOutputs for the same tab // Coalesce multiple TabOutputs for the same tab
MuxNotification::TabOutput(tab_id) => tabs_to_output.insert(tab_id), MuxNotification::PaneOutput(pane_id) => panes_to_output.insert(pane_id),
}, },
Err(TryRecvError::Empty) => break, Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => bail!("mux_rx is Disconnected"), Err(TryRecvError::Disconnected) => bail!("mux_rx is Disconnected"),
}; };
} }
for tab_id in tabs_to_output.drain() { for pane_id in panes_to_output.drain() {
self.handler.schedule_tab_push(tab_id); self.handler.schedule_pane_push(pane_id);
} }
let mut poll_array = [ let mut poll_array = [

View File

@ -1,5 +1,5 @@
use crate::mux::renderable::{RenderableDimensions, StableCursorPosition}; use crate::mux::renderable::{RenderableDimensions, StableCursorPosition};
use crate::mux::tab::{Tab, TabId}; use crate::mux::tab::{Pane, PaneId, TabId};
use crate::mux::Mux; use crate::mux::Mux;
use crate::server::codec::*; use crate::server::codec::*;
use crate::server::listener::PKI; use crate::server::listener::PKI;
@ -17,7 +17,7 @@ use wezterm_term::terminal::Clipboard;
use wezterm_term::StableRowIndex; use wezterm_term::StableRowIndex;
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct PerTab { struct PerPane {
cursor_position: StableCursorPosition, cursor_position: StableCursorPosition,
title: String, title: String,
working_dir: Option<Url>, working_dir: Option<Url>,
@ -26,39 +26,39 @@ struct PerTab {
mouse_grabbed: bool, mouse_grabbed: bool,
} }
impl PerTab { impl PerPane {
fn compute_changes( fn compute_changes(
&mut self, &mut self,
tab: &Rc<dyn Tab>, pane: &Rc<dyn Pane>,
force_with_input_serial: Option<InputSerial>, force_with_input_serial: Option<InputSerial>,
) -> Option<GetTabRenderChangesResponse> { ) -> Option<GetPaneRenderChangesResponse> {
let mut changed = false; let mut changed = false;
let mouse_grabbed = tab.is_mouse_grabbed(); let mouse_grabbed = pane.is_mouse_grabbed();
if mouse_grabbed != self.mouse_grabbed { if mouse_grabbed != self.mouse_grabbed {
changed = true; changed = true;
} }
let dims = tab.renderer().get_dimensions(); let dims = pane.renderer().get_dimensions();
if dims != self.dimensions { if dims != self.dimensions {
changed = true; changed = true;
} }
let cursor_position = tab.renderer().get_cursor_position(); let cursor_position = pane.renderer().get_cursor_position();
if cursor_position != self.cursor_position { if cursor_position != self.cursor_position {
changed = true; changed = true;
} }
let title = tab.get_title(); let title = pane.get_title();
if title != self.title { if title != self.title {
changed = true; changed = true;
} }
let working_dir = tab.get_current_working_dir(); let working_dir = pane.get_current_working_dir();
if working_dir != self.working_dir { if working_dir != self.working_dir {
changed = true; changed = true;
} }
let mut all_dirty_lines = tab let mut all_dirty_lines = pane
.renderer() .renderer()
.get_dirty_lines(0..dims.physical_top + dims.viewport_rows as StableRowIndex); .get_dirty_lines(0..dims.physical_top + dims.viewport_rows as StableRowIndex);
let dirty_delta = all_dirty_lines.difference(&self.dirty_lines); let dirty_delta = all_dirty_lines.difference(&self.dirty_lines);
@ -74,7 +74,7 @@ impl PerTab {
let viewport_range = let viewport_range =
dims.physical_top..dims.physical_top + dims.viewport_rows as StableRowIndex; dims.physical_top..dims.physical_top + dims.viewport_rows as StableRowIndex;
let (first_line, lines) = tab.renderer().get_lines(viewport_range); let (first_line, lines) = pane.renderer().get_lines(viewport_range);
let mut bonus_lines = lines let mut bonus_lines = lines
.into_iter() .into_iter()
.enumerate() .enumerate()
@ -87,7 +87,7 @@ impl PerTab {
// Always send the cursor's row, as that tends to the busiest and we don't // Always send the cursor's row, as that tends to the busiest and we don't
// have a sequencing concept for our idea of the remote state. // have a sequencing concept for our idea of the remote state.
let (cursor_line, lines) = tab let (cursor_line, lines) = pane
.renderer() .renderer()
.get_lines(cursor_position.y..cursor_position.y + 1); .get_lines(cursor_position.y..cursor_position.y + 1);
bonus_lines.push((cursor_line, lines[0].clone())); bonus_lines.push((cursor_line, lines[0].clone()));
@ -101,8 +101,8 @@ impl PerTab {
let dirty_lines = dirty_delta.iter().cloned().collect(); let dirty_lines = dirty_delta.iter().cloned().collect();
let bonus_lines = bonus_lines.into(); let bonus_lines = bonus_lines.into();
Some(GetTabRenderChangesResponse { Some(GetPaneRenderChangesResponse {
tab_id: tab.tab_id(), pane_id: pane.pane_id(),
mouse_grabbed, mouse_grabbed,
dirty_lines, dirty_lines,
dimensions: dims, dimensions: dims,
@ -119,15 +119,15 @@ impl PerTab {
} }
} }
fn maybe_push_tab_changes( fn maybe_push_pane_changes(
tab: &Rc<dyn Tab>, pane: &Rc<dyn Pane>,
sender: PollableSender<DecodedPdu>, sender: PollableSender<DecodedPdu>,
per_tab: Arc<Mutex<PerTab>>, per_pane: Arc<Mutex<PerPane>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut per_tab = per_tab.lock().unwrap(); let mut per_pane = per_pane.lock().unwrap();
if let Some(resp) = per_tab.compute_changes(tab, None) { if let Some(resp) = per_pane.compute_changes(pane, None) {
sender.send(DecodedPdu { sender.send(DecodedPdu {
pdu: Pdu::GetTabRenderChangesResponse(resp), pdu: Pdu::GetPaneRenderChangesResponse(resp),
serial: 0, serial: 0,
})?; })?;
} }
@ -136,33 +136,33 @@ fn maybe_push_tab_changes(
pub struct SessionHandler { pub struct SessionHandler {
to_write_tx: PollableSender<DecodedPdu>, to_write_tx: PollableSender<DecodedPdu>,
per_tab: HashMap<TabId, Arc<Mutex<PerTab>>>, per_pane: HashMap<TabId, Arc<Mutex<PerPane>>>,
} }
impl SessionHandler { impl SessionHandler {
pub fn new(to_write_tx: PollableSender<DecodedPdu>) -> Self { pub fn new(to_write_tx: PollableSender<DecodedPdu>) -> Self {
Self { Self {
to_write_tx, to_write_tx,
per_tab: HashMap::new(), per_pane: HashMap::new(),
} }
} }
fn per_tab(&mut self, tab_id: TabId) -> Arc<Mutex<PerTab>> { fn per_pane(&mut self, pane_id: PaneId) -> Arc<Mutex<PerPane>> {
Arc::clone( Arc::clone(
self.per_tab self.per_pane
.entry(tab_id) .entry(pane_id)
.or_insert_with(|| Arc::new(Mutex::new(PerTab::default()))), .or_insert_with(|| Arc::new(Mutex::new(PerPane::default()))),
) )
} }
pub fn schedule_tab_push(&mut self, tab_id: TabId) { pub fn schedule_pane_push(&mut self, pane_id: PaneId) {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
maybe_push_tab_changes(&tab, sender, per_tab)?; maybe_push_pane_changes(&pane, sender, per_pane)?;
Ok::<(), anyhow::Error>(()) Ok::<(), anyhow::Error>(())
}); });
} }
@ -202,20 +202,23 @@ impl SessionHandler {
for window_id in mux.iter_windows().into_iter() { for window_id in mux.iter_windows().into_iter() {
let window = mux.get_window(window_id).unwrap(); let window = mux.get_window(window_id).unwrap();
for tab in window.iter() { for tab in window.iter() {
let dims = tab.renderer().get_dimensions(); if let Some(pane) = tab.get_active_pane() {
let working_dir = tab.get_current_working_dir(); let dims = pane.renderer().get_dimensions();
tabs.push(WindowAndTabEntry { let working_dir = pane.get_current_working_dir();
window_id, tabs.push(WindowAndTabEntry {
tab_id: tab.tab_id(), window_id,
title: tab.get_title(), tab_id: tab.tab_id(),
size: PtySize { pane_id: pane.pane_id(),
cols: dims.cols as u16, title: pane.get_title(),
rows: dims.viewport_rows as u16, size: PtySize {
pixel_height: 0, cols: dims.cols as u16,
pixel_width: 0, rows: dims.viewport_rows as u16,
}, pixel_height: 0,
working_dir: working_dir.map(Into::into), pixel_width: 0,
}); },
working_dir: working_dir.map(Into::into),
});
}
} }
} }
log::error!("ListTabs {:#?}", tabs); log::error!("ListTabs {:#?}", tabs);
@ -226,36 +229,36 @@ impl SessionHandler {
}); });
} }
Pdu::WriteToTab(WriteToTab { tab_id, data }) => { Pdu::WriteToPane(WriteToPane { pane_id, data }) => {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.writer().write_all(&data)?; pane.writer().write_all(&data)?;
maybe_push_tab_changes(&tab, sender, per_tab)?; maybe_push_pane_changes(&pane, sender, per_pane)?;
Ok(Pdu::UnitResponse(UnitResponse {})) Ok(Pdu::UnitResponse(UnitResponse {}))
}, },
send_response, send_response,
); );
}); });
} }
Pdu::SendPaste(SendPaste { tab_id, data }) => { Pdu::SendPaste(SendPaste { pane_id, data }) => {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.send_paste(&data)?; pane.send_paste(&data)?;
maybe_push_tab_changes(&tab, sender, per_tab)?; maybe_push_pane_changes(&pane, sender, per_pane)?;
Ok(Pdu::UnitResponse(UnitResponse {})) Ok(Pdu::UnitResponse(UnitResponse {}))
}, },
send_response, send_response,
@ -263,37 +266,37 @@ impl SessionHandler {
}); });
} }
Pdu::SearchTabScrollbackRequest(SearchTabScrollbackRequest { tab_id, pattern }) => { Pdu::SearchScrollbackRequest(SearchScrollbackRequest { pane_id, pattern }) => {
use crate::mux::tab::Pattern; use crate::mux::tab::Pattern;
async fn do_search(tab_id: TabId, pattern: Pattern) -> anyhow::Result<Pdu> { async fn do_search(pane_id: TabId, pattern: Pattern) -> anyhow::Result<Pdu> {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.search(pattern).await.map(|results| { pane.search(pattern).await.map(|results| {
Pdu::SearchTabScrollbackResponse(SearchTabScrollbackResponse { results }) Pdu::SearchScrollbackResponse(SearchScrollbackResponse { results })
}) })
} }
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let result = do_search(tab_id, pattern).await; let result = do_search(pane_id, pattern).await;
send_response(result); send_response(result);
}); });
}); });
} }
Pdu::Resize(Resize { tab_id, size }) => { Pdu::Resize(Resize { pane_id, size }) => {
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.resize(size)?; pane.resize(size)?;
Ok(Pdu::UnitResponse(UnitResponse {})) Ok(Pdu::UnitResponse(UnitResponse {}))
}, },
send_response, send_response,
@ -302,28 +305,29 @@ impl SessionHandler {
} }
Pdu::SendKeyDown(SendKeyDown { Pdu::SendKeyDown(SendKeyDown {
tab_id, pane_id,
event, event,
input_serial, input_serial,
}) => { }) => {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.key_down(event.key, event.modifiers)?; pane.key_down(event.key, event.modifiers)?;
// For a key press, we want to always send back the // For a key press, we want to always send back the
// cursor position so that the predictive echo doesn't // cursor position so that the predictive echo doesn't
// leave the cursor in the wrong place // leave the cursor in the wrong place
let mut per_tab = per_tab.lock().unwrap(); let mut per_pane = per_pane.lock().unwrap();
if let Some(resp) = per_tab.compute_changes(&tab, Some(input_serial)) { if let Some(resp) = per_pane.compute_changes(&pane, Some(input_serial))
{
sender.send(DecodedPdu { sender.send(DecodedPdu {
pdu: Pdu::GetTabRenderChangesResponse(resp), pdu: Pdu::GetPaneRenderChangesResponse(resp),
serial: 0, serial: 0,
})?; })?;
} }
@ -333,18 +337,18 @@ impl SessionHandler {
) )
}); });
} }
Pdu::SendMouseEvent(SendMouseEvent { tab_id, event }) => { Pdu::SendMouseEvent(SendMouseEvent { pane_id, event }) => {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
tab.mouse_event(event)?; pane.mouse_event(event)?;
maybe_push_tab_changes(&tab, sender, per_tab)?; maybe_push_pane_changes(&pane, sender, per_pane)?;
Ok(Pdu::UnitResponse(UnitResponse {})) Ok(Pdu::UnitResponse(UnitResponse {}))
}, },
send_response, send_response,
@ -359,23 +363,23 @@ impl SessionHandler {
}); });
} }
Pdu::GetTabRenderChanges(GetTabRenderChanges { tab_id, .. }) => { Pdu::GetPaneRenderChanges(GetPaneRenderChanges { pane_id, .. }) => {
let sender = self.to_write_tx.clone(); let sender = self.to_write_tx.clone();
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab_alive = match mux.get_tab(tab_id) { let is_alive = match mux.get_pane(pane_id) {
Some(tab) => { Some(pane) => {
maybe_push_tab_changes(&tab, sender, per_tab)?; maybe_push_pane_changes(&pane, sender, per_pane)?;
true true
} }
None => false, None => false,
}; };
Ok(Pdu::TabLivenessResponse(TabLivenessResponse { Ok(Pdu::LivenessResponse(LivenessResponse {
tab_id, pane_id,
tab_alive, is_alive,
})) }))
}, },
send_response, send_response,
@ -383,30 +387,30 @@ impl SessionHandler {
}); });
} }
Pdu::GetLines(GetLines { tab_id, lines }) => { Pdu::GetLines(GetLines { pane_id, lines }) => {
let per_tab = self.per_tab(tab_id); let per_pane = self.per_pane(pane_id);
spawn_into_main_thread(async move { spawn_into_main_thread(async move {
catch( catch(
move || { move || {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(tab_id) .get_pane(pane_id)
.ok_or_else(|| anyhow!("no such tab {}", tab_id))?; .ok_or_else(|| anyhow!("no such pane {}", pane_id))?;
let mut renderer = tab.renderer(); let mut renderer = pane.renderer();
let mut lines_and_indices = vec![]; let mut lines_and_indices = vec![];
let mut per_tab = per_tab.lock().unwrap(); let mut per_pane = per_pane.lock().unwrap();
for range in lines { for range in lines {
let (first_row, lines) = renderer.get_lines(range); let (first_row, lines) = renderer.get_lines(range);
for (idx, line) in lines.into_iter().enumerate() { for (idx, line) in lines.into_iter().enumerate() {
let stable_row = first_row + idx as StableRowIndex; let stable_row = first_row + idx as StableRowIndex;
per_tab.mark_clean(stable_row); per_pane.mark_clean(stable_row);
lines_and_indices.push((stable_row, line)); lines_and_indices.push((stable_row, line));
} }
} }
Ok(Pdu::GetLinesResponse(GetLinesResponse { Ok(Pdu::GetLinesResponse(GetLinesResponse {
tab_id, pane_id,
lines: lines_and_indices.into(), lines: lines_and_indices.into(),
})) }))
}, },
@ -441,10 +445,10 @@ impl SessionHandler {
| Pdu::ListTabsResponse { .. } | Pdu::ListTabsResponse { .. }
| Pdu::SetClipboard { .. } | Pdu::SetClipboard { .. }
| Pdu::SpawnResponse { .. } | Pdu::SpawnResponse { .. }
| Pdu::GetTabRenderChangesResponse { .. } | Pdu::GetPaneRenderChangesResponse { .. }
| Pdu::UnitResponse { .. } | Pdu::UnitResponse { .. }
| Pdu::TabLivenessResponse { .. } | Pdu::LivenessResponse { .. }
| Pdu::SearchTabScrollbackResponse { .. } | Pdu::SearchScrollbackResponse { .. }
| Pdu::GetLinesResponse { .. } | Pdu::GetLinesResponse { .. }
| Pdu::GetCodecVersionResponse { .. } | Pdu::GetCodecVersionResponse { .. }
| Pdu::GetTlsCredsResponse { .. } | Pdu::GetTlsCredsResponse { .. }
@ -468,7 +472,7 @@ where
struct RemoteClipboard { struct RemoteClipboard {
sender: PollableSender<DecodedPdu>, sender: PollableSender<DecodedPdu>,
tab_id: TabId, pane_id: TabId,
} }
impl Clipboard for RemoteClipboard { impl Clipboard for RemoteClipboard {
@ -480,7 +484,7 @@ impl Clipboard for RemoteClipboard {
self.sender.send(DecodedPdu { self.sender.send(DecodedPdu {
serial: 0, serial: 0,
pdu: Pdu::SetClipboard(SetClipboard { pdu: Pdu::SetClipboard(SetClipboard {
tab_id: self.tab_id, pane_id: self.pane_id,
clipboard, clipboard,
}), }),
})?; })?;
@ -506,13 +510,18 @@ async fn domain_spawn(spawn: Spawn, sender: PollableSender<DecodedPdu>) -> anyho
.spawn(spawn.size, spawn.command, spawn.command_dir, window_id) .spawn(spawn.size, spawn.command, spawn.command_dir, window_id)
.await?; .await?;
let pane = tab
.get_active_pane()
.ok_or_else(|| anyhow!("missing active pane on tab!?"))?;
let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard { let clip: Arc<dyn Clipboard> = Arc::new(RemoteClipboard {
tab_id: tab.tab_id(), pane_id: pane.pane_id(),
sender, sender,
}); });
tab.set_clipboard(&clip); pane.set_clipboard(&clip);
Ok::<Pdu, anyhow::Error>(Pdu::SpawnResponse(SpawnResponse { Ok::<Pdu, anyhow::Error>(Pdu::SpawnResponse(SpawnResponse {
pane_id: pane.pane_id(),
tab_id: tab.tab_id(), tab_id: tab.tab_id(),
window_id, window_id,
})) }))

View File

@ -1,7 +1,7 @@
use crate::config::configuration; use crate::config::configuration;
use crate::mux::domain::DomainId; use crate::mux::domain::DomainId;
use crate::mux::renderable::{Renderable, RenderableDimensions}; use crate::mux::renderable::{Renderable, RenderableDimensions};
use crate::mux::tab::{alloc_tab_id, Pattern, SearchResult, Tab, TabId}; use crate::mux::tab::{alloc_pane_id, Pane, Pattern, SearchResult, TabId};
use crate::ratelim::RateLimiter; use crate::ratelim::RateLimiter;
use crate::server::codec::*; use crate::server::codec::*;
use crate::server::domain::ClientInner; use crate::server::domain::ClientInner;
@ -21,33 +21,33 @@ use url::Url;
use wezterm_term::color::ColorPalette; use wezterm_term::color::ColorPalette;
use wezterm_term::{Clipboard, KeyCode, KeyModifiers, MouseEvent}; use wezterm_term::{Clipboard, KeyCode, KeyModifiers, MouseEvent};
pub struct ClientTab { pub struct ClientPane {
client: Arc<ClientInner>, client: Arc<ClientInner>,
local_tab_id: TabId, local_pane_id: TabId,
remote_tab_id: TabId, remote_pane_id: TabId,
pub renderable: RefCell<RenderableState>, pub renderable: RefCell<RenderableState>,
writer: RefCell<TabWriter>, writer: RefCell<PaneWriter>,
reader: Pipe, reader: Pipe,
mouse: Rc<RefCell<MouseState>>, mouse: Rc<RefCell<MouseState>>,
clipboard: RefCell<Option<Arc<dyn Clipboard>>>, clipboard: RefCell<Option<Arc<dyn Clipboard>>>,
mouse_grabbed: RefCell<bool>, mouse_grabbed: RefCell<bool>,
} }
impl ClientTab { impl ClientPane {
pub fn new( pub fn new(
client: &Arc<ClientInner>, client: &Arc<ClientInner>,
remote_tab_id: TabId, remote_pane_id: TabId,
size: PtySize, size: PtySize,
title: &str, title: &str,
) -> Self { ) -> Self {
let local_tab_id = alloc_tab_id(); let local_pane_id = alloc_pane_id();
let writer = TabWriter { let writer = PaneWriter {
client: Arc::clone(client), client: Arc::clone(client),
remote_tab_id, remote_pane_id,
}; };
let mouse = Rc::new(RefCell::new(MouseState::new( let mouse = Rc::new(RefCell::new(MouseState::new(
remote_tab_id, remote_pane_id,
client.client.clone(), client.client.clone(),
))); )));
@ -57,8 +57,8 @@ impl ClientTab {
let render = RenderableState { let render = RenderableState {
inner: RefCell::new(RenderableInner::new( inner: RefCell::new(RenderableInner::new(
client, client,
remote_tab_id, remote_pane_id,
local_tab_id, local_pane_id,
RenderableDimensions { RenderableDimensions {
cols: size.cols as _, cols: size.cols as _,
viewport_rows: size.rows as _, viewport_rows: size.rows as _,
@ -76,8 +76,8 @@ impl ClientTab {
Self { Self {
client: Arc::clone(client), client: Arc::clone(client),
mouse, mouse,
remote_tab_id, remote_pane_id,
local_tab_id, local_pane_id,
renderable: RefCell::new(render), renderable: RefCell::new(render),
writer: RefCell::new(writer), writer: RefCell::new(writer),
reader, reader,
@ -88,7 +88,7 @@ impl ClientTab {
pub fn process_unilateral(&self, pdu: Pdu) -> anyhow::Result<()> { pub fn process_unilateral(&self, pdu: Pdu) -> anyhow::Result<()> {
match pdu { match pdu {
Pdu::GetTabRenderChangesResponse(delta) => { Pdu::GetPaneRenderChangesResponse(delta) => {
*self.mouse_grabbed.borrow_mut() = delta.mouse_grabbed; *self.mouse_grabbed.borrow_mut() = delta.mouse_grabbed;
self.renderable self.renderable
.borrow() .borrow()
@ -102,7 +102,7 @@ impl ClientTab {
clip.set_contents(clipboard)?; clip.set_contents(clipboard)?;
} }
None => { None => {
log::error!("ClientTab: Ignoring SetClipboard request {:?}", clipboard); log::error!("ClientPane: Ignoring SetClipboard request {:?}", clipboard);
} }
} }
} }
@ -111,15 +111,15 @@ impl ClientTab {
Ok(()) Ok(())
} }
pub fn remote_tab_id(&self) -> TabId { pub fn remote_pane_id(&self) -> TabId {
self.remote_tab_id self.remote_pane_id
} }
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Tab for ClientTab { impl Pane for ClientPane {
fn tab_id(&self) -> TabId { fn pane_id(&self) -> TabId {
self.local_tab_id self.local_pane_id
} }
fn renderer(&self) -> RefMut<dyn Renderable> { fn renderer(&self) -> RefMut<dyn Renderable> {
self.renderable.borrow_mut() self.renderable.borrow_mut()
@ -137,7 +137,7 @@ impl Tab for ClientTab {
fn send_paste(&self, text: &str) -> anyhow::Result<()> { fn send_paste(&self, text: &str) -> anyhow::Result<()> {
let client = Arc::clone(&self.client); let client = Arc::clone(&self.client);
let remote_tab_id = self.remote_tab_id; let remote_pane_id = self.remote_pane_id;
self.renderable self.renderable
.borrow() .borrow()
.inner .inner
@ -149,7 +149,7 @@ impl Tab for ClientTab {
client client
.client .client
.send_paste(SendPaste { .send_paste(SendPaste {
tab_id: remote_tab_id, pane_id: remote_pane_id,
data, data,
}) })
.await .await
@ -163,7 +163,7 @@ impl Tab for ClientTab {
} }
fn reader(&self) -> anyhow::Result<Box<dyn std::io::Read + Send>> { fn reader(&self) -> anyhow::Result<Box<dyn std::io::Read + Send>> {
info!("made reader for ClientTab"); info!("made reader for ClientPane");
Ok(Box::new(self.reader.read.try_clone()?)) Ok(Box::new(self.reader.read.try_clone()?))
} }
@ -186,12 +186,12 @@ impl Tab for ClientTab {
inner.make_all_stale(); inner.make_all_stale();
let client = Arc::clone(&self.client); let client = Arc::clone(&self.client);
let remote_tab_id = self.remote_tab_id; let remote_pane_id = self.remote_pane_id;
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
client client
.client .client
.resize(Resize { .resize(Resize {
tab_id: remote_tab_id, pane_id: remote_pane_id,
size, size,
}) })
.await .await
@ -205,13 +205,13 @@ impl Tab for ClientTab {
match self match self
.client .client
.client .client
.search_scrollback(SearchTabScrollbackRequest { .search_scrollback(SearchScrollbackRequest {
tab_id: self.remote_tab_id, pane_id: self.remote_pane_id,
pattern, pattern,
}) })
.await .await
{ {
Ok(SearchTabScrollbackResponse { results }) => Ok(results), Ok(SearchScrollbackResponse { results }) => Ok(results),
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
@ -226,12 +226,12 @@ impl Tab for ClientTab {
inner.predict_from_key_event(key, mods); inner.predict_from_key_event(key, mods);
} }
let client = Arc::clone(&self.client); let client = Arc::clone(&self.client);
let remote_tab_id = self.remote_tab_id; let remote_pane_id = self.remote_pane_id;
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
client client
.client .client
.key_down(SendKeyDown { .key_down(SendKeyDown {
tab_id: remote_tab_id, pane_id: remote_pane_id,
event: KeyEvent { event: KeyEvent {
key, key,
modifiers: mods, modifiers: mods,
@ -261,7 +261,7 @@ impl Tab for ClientTab {
} }
fn advance_bytes(&self, _buf: &[u8]) { fn advance_bytes(&self, _buf: &[u8]) {
panic!("ClientTab::advance_bytes not impl"); panic!("ClientPane::advance_bytes not impl");
} }
fn is_dead(&self) -> bool { fn is_dead(&self) -> bool {
@ -309,15 +309,15 @@ impl Tab for ClientTab {
} }
} }
struct TabWriter { struct PaneWriter {
client: Arc<ClientInner>, client: Arc<ClientInner>,
remote_tab_id: TabId, remote_pane_id: TabId,
} }
impl std::io::Write for TabWriter { impl std::io::Write for PaneWriter {
fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> { fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
promise::spawn::block_on(self.client.client.write_to_tab(WriteToTab { promise::spawn::block_on(self.client.client.write_to_pane(WriteToPane {
tab_id: self.remote_tab_id, pane_id: self.remote_pane_id,
data: data.to_vec(), data: data.to_vec(),
})) }))
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?; .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))?;

View File

@ -1,4 +1,4 @@
pub use clienttab::ClientTab; pub use clienttab::ClientPane;
mod clienttab; mod clienttab;
mod mousestate; mod mousestate;

View File

@ -11,13 +11,13 @@ pub struct MouseState {
pending: AtomicBool, pending: AtomicBool,
queue: VecDeque<MouseEvent>, queue: VecDeque<MouseEvent>,
client: Client, client: Client,
remote_tab_id: TabId, remote_pane_id: TabId,
} }
impl MouseState { impl MouseState {
pub fn new(remote_tab_id: TabId, client: Client) -> Self { pub fn new(remote_pane_id: TabId, client: Client) -> Self {
Self { Self {
remote_tab_id, remote_pane_id,
client, client,
pending: AtomicBool::new(false), pending: AtomicBool::new(false),
queue: VecDeque::new(), queue: VecDeque::new(),
@ -71,12 +71,12 @@ impl MouseState {
let state = Rc::clone(&state); let state = Rc::clone(&state);
mouse.pending.store(true, Ordering::SeqCst); mouse.pending.store(true, Ordering::SeqCst);
let remote_tab_id = mouse.remote_tab_id; let remote_pane_id = mouse.remote_pane_id;
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
client client
.mouse_event(SendMouseEvent { .mouse_event(SendMouseEvent {
tab_id: remote_tab_id, pane_id: remote_pane_id,
event, event,
}) })
.await .await

View File

@ -5,7 +5,7 @@ use crate::mux::Mux;
use crate::ratelim::RateLimiter; use crate::ratelim::RateLimiter;
use crate::server::codec::*; use crate::server::codec::*;
use crate::server::domain::ClientInner; use crate::server::domain::ClientInner;
use crate::server::tab::clienttab::ClientTab; use crate::server::tab::clienttab::ClientPane;
use anyhow::anyhow; use anyhow::anyhow;
use lru::LruCache; use lru::LruCache;
use promise::BrokenPromise; use promise::BrokenPromise;
@ -55,8 +55,8 @@ impl LineEntry {
pub struct RenderableInner { pub struct RenderableInner {
client: Arc<ClientInner>, client: Arc<ClientInner>,
remote_tab_id: TabId, remote_pane_id: TabId,
local_tab_id: TabId, local_pane_id: TabId,
last_poll: Instant, last_poll: Instant,
pub dead: bool, pub dead: bool,
poll_in_progress: AtomicBool, poll_in_progress: AtomicBool,
@ -86,8 +86,8 @@ pub struct RenderableState {
impl RenderableInner { impl RenderableInner {
pub fn new( pub fn new(
client: &Arc<ClientInner>, client: &Arc<ClientInner>,
remote_tab_id: TabId, remote_pane_id: TabId,
local_tab_id: TabId, local_pane_id: TabId,
dimensions: RenderableDimensions, dimensions: RenderableDimensions,
title: &str, title: &str,
fetch_limiter: RateLimiter, fetch_limiter: RateLimiter,
@ -96,8 +96,8 @@ impl RenderableInner {
Self { Self {
client: Arc::clone(client), client: Arc::clone(client),
remote_tab_id, remote_pane_id,
local_tab_id, local_pane_id,
last_poll: now, last_poll: now,
dead: false, dead: false,
poll_in_progress: AtomicBool::new(false), poll_in_progress: AtomicBool::new(false),
@ -299,7 +299,7 @@ impl RenderableInner {
self.poll_interval = BASE_POLL_INTERVAL; self.poll_interval = BASE_POLL_INTERVAL;
} }
pub fn apply_changes_to_surface(&mut self, delta: GetTabRenderChangesResponse) { pub fn apply_changes_to_surface(&mut self, delta: GetPaneRenderChangesResponse) {
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;
@ -350,7 +350,7 @@ impl RenderableInner {
if !dirty.is_empty() { if !dirty.is_empty() {
Mux::get() Mux::get()
.unwrap() .unwrap()
.notify(crate::mux::MuxNotification::TabOutput(self.local_tab_id)); .notify(crate::mux::MuxNotification::PaneOutput(self.local_pane_id));
} }
let mut to_fetch = RangeSet::new(); let mut to_fetch = RangeSet::new();
@ -478,40 +478,40 @@ impl RenderableInner {
return; return;
} }
let local_tab_id = self.local_tab_id; let local_pane_id = self.local_pane_id;
log::trace!( log::trace!(
"will fetch lines {:?} for remote tab id {} at {:?}", "will fetch lines {:?} for remote tab id {} at {:?}",
to_fetch, to_fetch,
self.remote_tab_id, self.remote_pane_id,
now, now,
); );
let client = Arc::clone(&self.client); let client = Arc::clone(&self.client);
let remote_tab_id = self.remote_tab_id; let remote_pane_id = self.remote_pane_id;
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let result = client let result = client
.client .client
.get_lines(GetLines { .get_lines(GetLines {
tab_id: remote_tab_id, pane_id: remote_pane_id,
lines: to_fetch.clone().into(), lines: to_fetch.clone().into(),
}) })
.await; .await;
Self::apply_lines(local_tab_id, result, to_fetch, now) Self::apply_lines(local_pane_id, result, to_fetch, now)
}); });
} }
fn apply_lines( fn apply_lines(
local_tab_id: TabId, local_pane_id: TabId,
result: anyhow::Result<GetLinesResponse>, result: anyhow::Result<GetLinesResponse>,
to_fetch: RangeSet<StableRowIndex>, to_fetch: RangeSet<StableRowIndex>,
now: Instant, now: Instant,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let pane = mux
.get_tab(local_tab_id) .get_pane(local_pane_id)
.ok_or_else(|| anyhow!("no such tab {}", local_tab_id))?; .ok_or_else(|| anyhow!("no such tab {}", local_pane_id))?;
if let Some(client_tab) = tab.downcast_ref::<ClientTab>() { if let Some(client_tab) = pane.downcast_ref::<ClientPane>() {
let renderable = client_tab.renderable.borrow_mut(); let renderable = client_tab.renderable.borrow_mut();
let mut inner = renderable.inner.borrow_mut(); let mut inner = renderable.inner.borrow_mut();
@ -566,18 +566,18 @@ impl RenderableInner {
self.last_poll = Instant::now(); self.last_poll = Instant::now();
self.poll_in_progress.store(true, Ordering::SeqCst); self.poll_in_progress.store(true, Ordering::SeqCst);
let remote_tab_id = self.remote_tab_id; let remote_pane_id = self.remote_pane_id;
let local_tab_id = self.local_tab_id; let local_pane_id = self.local_pane_id;
let client = Arc::clone(&self.client); let client = Arc::clone(&self.client);
promise::spawn::spawn(async move { promise::spawn::spawn(async move {
let alive = match client let alive = match client
.client .client
.get_tab_render_changes(GetTabRenderChanges { .get_tab_render_changes(GetPaneRenderChanges {
tab_id: remote_tab_id, pane_id: remote_pane_id,
}) })
.await .await
{ {
Ok(resp) => resp.tab_alive, Ok(resp) => resp.is_alive,
// if we got a timeout on a reconnectable, don't // if we got a timeout on a reconnectable, don't
// consider the tab to be dead; that helps to // consider the tab to be dead; that helps to
// avoid having a tab get shuffled around // avoid having a tab get shuffled around
@ -586,9 +586,9 @@ impl RenderableInner {
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab = mux let tab = mux
.get_tab(local_tab_id) .get_pane(local_pane_id)
.ok_or_else(|| anyhow!("no such tab {}", local_tab_id))?; .ok_or_else(|| anyhow!("no such tab {}", local_pane_id))?;
if let Some(client_tab) = tab.downcast_ref::<ClientTab>() { if let Some(client_tab) = tab.downcast_ref::<ClientPane>() {
let renderable = client_tab.renderable.borrow_mut(); let renderable = client_tab.renderable.borrow_mut();
let mut inner = renderable.inner.borrow_mut(); let mut inner = renderable.inner.borrow_mut();

View File

@ -1,7 +1,7 @@
use crate::connui::ConnectionUI; use crate::connui::ConnectionUI;
use crate::localtab::LocalTab; use crate::localtab::LocalPane;
use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState}; use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
use crate::mux::tab::Tab; use crate::mux::tab::{Pane, Tab};
use crate::mux::window::WindowId; use crate::mux::window::WindowId;
use crate::mux::Mux; use crate::mux::Mux;
use anyhow::{anyhow, bail, Context, Error}; use anyhow::{anyhow, bail, Context, Error};
@ -241,7 +241,7 @@ impl Domain for RemoteSshDomain {
command: Option<CommandBuilder>, command: Option<CommandBuilder>,
_command_dir: Option<String>, _command_dir: Option<String>,
window: WindowId, window: WindowId,
) -> Result<Rc<dyn Tab>, Error> { ) -> Result<Rc<Tab>, Error> {
let cmd = match command { let cmd = match command {
Some(c) => c, Some(c) => c,
None => CommandBuilder::new_default_prog(), None => CommandBuilder::new_default_prog(),
@ -264,7 +264,9 @@ impl Domain for RemoteSshDomain {
); );
let mux = Mux::get().unwrap(); let mux = Mux::get().unwrap();
let tab: Rc<dyn Tab> = Rc::new(LocalTab::new(terminal, child, pair.master, self.id)); let pane: Rc<dyn Pane> = Rc::new(LocalPane::new(terminal, child, pair.master, self.id));
let tab = Rc::new(Tab::new());
tab.assign_pane(&pane);
mux.add_tab(&tab)?; mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window)?; mux.add_tab_to_window(&tab, window)?;

View File

@ -7,7 +7,7 @@ use crate::font::FontConfiguration;
use crate::frontend::front_end; use crate::frontend::front_end;
use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState}; use crate::mux::domain::{alloc_domain_id, Domain, DomainId, DomainState};
use crate::mux::renderable::Renderable; use crate::mux::renderable::Renderable;
use crate::mux::tab::{alloc_tab_id, Tab, TabId}; use crate::mux::tab::{alloc_pane_id, Pane, PaneId, Tab};
use crate::mux::window::WindowId; use crate::mux::window::WindowId;
use crate::mux::Mux; use crate::mux::Mux;
use anyhow::{bail, Error}; use anyhow::{bail, Error};
@ -51,8 +51,8 @@ impl Domain for TermWizTerminalDomain {
_command: Option<CommandBuilder>, _command: Option<CommandBuilder>,
_command_dir: Option<String>, _command_dir: Option<String>,
_window: WindowId, _window: WindowId,
) -> anyhow::Result<Rc<dyn Tab>> { ) -> anyhow::Result<Rc<Tab>> {
bail!("cannot spawn tabs in a TermWizTerminalTab"); bail!("cannot spawn tabs in a TermWizTerminalPane");
} }
fn spawnable(&self) -> bool { fn spawnable(&self) -> bool {
@ -79,8 +79,8 @@ impl Domain for TermWizTerminalDomain {
} }
} }
pub struct TermWizTerminalTab { pub struct TermWizTerminalPane {
tab_id: TabId, pane_id: PaneId,
domain_id: DomainId, domain_id: DomainId,
terminal: RefCell<wezterm_term::Terminal>, terminal: RefCell<wezterm_term::Terminal>,
input_tx: Sender<InputEvent>, input_tx: Sender<InputEvent>,
@ -89,7 +89,7 @@ pub struct TermWizTerminalTab {
render_rx: FileDescriptor, render_rx: FileDescriptor,
} }
impl TermWizTerminalTab { impl TermWizTerminalPane {
fn new( fn new(
domain_id: DomainId, domain_id: DomainId,
width: usize, width: usize,
@ -97,7 +97,7 @@ impl TermWizTerminalTab {
input_tx: Sender<InputEvent>, input_tx: Sender<InputEvent>,
render_rx: FileDescriptor, render_rx: FileDescriptor,
) -> Self { ) -> Self {
let tab_id = alloc_tab_id(); let pane_id = alloc_pane_id();
let terminal = RefCell::new(wezterm_term::Terminal::new( let terminal = RefCell::new(wezterm_term::Terminal::new(
height, height,
@ -111,7 +111,7 @@ impl TermWizTerminalTab {
)); ));
Self { Self {
tab_id, pane_id,
domain_id, domain_id,
terminal, terminal,
writer: RefCell::new(Vec::new()), writer: RefCell::new(Vec::new()),
@ -122,9 +122,9 @@ impl TermWizTerminalTab {
} }
} }
impl Tab for TermWizTerminalTab { impl Pane for TermWizTerminalPane {
fn tab_id(&self) -> TabId { fn pane_id(&self) -> PaneId {
self.tab_id self.pane_id
} }
fn renderer(&self) -> RefMut<dyn Renderable> { fn renderer(&self) -> RefMut<dyn Renderable> {
@ -304,11 +304,11 @@ impl termwiz::terminal::Terminal for TermWizTerminal {
} }
fn enter_alternate_screen(&mut self) -> anyhow::Result<()> { fn enter_alternate_screen(&mut self) -> anyhow::Result<()> {
bail!("TermWizTerminalTab has no alt screen"); bail!("TermWizTerminalPane has no alt screen");
} }
fn exit_alternate_screen(&mut self) -> anyhow::Result<()> { fn exit_alternate_screen(&mut self) -> anyhow::Result<()> {
bail!("TermWizTerminalTab has no alt screen"); bail!("TermWizTerminalPane has no alt screen");
} }
fn get_screen_size(&mut self) -> anyhow::Result<ScreenSize> { fn get_screen_size(&mut self) -> anyhow::Result<ScreenSize> {
@ -316,7 +316,7 @@ impl termwiz::terminal::Terminal for TermWizTerminal {
} }
fn set_screen_size(&mut self, _size: ScreenSize) -> anyhow::Result<()> { fn set_screen_size(&mut self, _size: ScreenSize) -> anyhow::Result<()> {
bail!("TermWizTerminalTab cannot set screen size"); bail!("TermWizTerminalPane cannot set screen size");
} }
fn render(&mut self, changes: &[Change]) -> anyhow::Result<()> { fn render(&mut self, changes: &[Change]) -> anyhow::Result<()> {
@ -346,7 +346,7 @@ impl termwiz::terminal::Terminal for TermWizTerminal {
} }
} }
pub fn allocate(width: usize, height: usize) -> (TermWizTerminal, Rc<dyn Tab>) { pub fn allocate(width: usize, height: usize) -> (TermWizTerminal, Rc<dyn Pane>) {
let render_pipe = Pipe::new().expect("Pipe creation not to fail"); let render_pipe = Pipe::new().expect("Pipe creation not to fail");
let (input_tx, input_rx) = channel(); let (input_tx, input_rx) = channel();
@ -368,14 +368,15 @@ pub fn allocate(width: usize, height: usize) -> (TermWizTerminal, Rc<dyn Tab>) {
}; };
let domain_id = 0; let domain_id = 0;
let tab = TermWizTerminalTab::new(domain_id, width, height, input_tx, render_pipe.read); let pane = TermWizTerminalPane::new(domain_id, width, height, input_tx, render_pipe.read);
// Add the tab to the mux so that the output is processed // Add the tab to the mux so that the output is processed
let tab: Rc<dyn Tab> = Rc::new(tab); let pane: Rc<dyn Pane> = Rc::new(pane);
let mux = Mux::get().unwrap();
mux.add_tab(&tab).expect("to be able to add tab to mux");
(tw_term, tab) let mux = Mux::get().unwrap();
mux.add_pane(&pane).expect("to be able to add pane to mux");
(tw_term, pane)
} }
fn new_wezterm_terminfo_renderer() -> TerminfoRenderer { fn new_wezterm_terminfo_renderer() -> TerminfoRenderer {
@ -445,8 +446,11 @@ pub async fn run<
let window_id = mux.new_empty_window(); let window_id = mux.new_empty_window();
let tab = TermWizTerminalTab::new(domain.domain_id(), width, height, input_tx, render_rx); let pane = TermWizTerminalPane::new(domain.domain_id(), width, height, input_tx, render_rx);
let tab: Rc<dyn Tab> = Rc::new(tab); let pane: Rc<dyn Pane> = Rc::new(pane);
let tab = Rc::new(Tab::new());
tab.assign_pane(&pane);
mux.add_tab(&tab)?; mux.add_tab(&tab)?;
mux.add_tab_to_window(&tab, window_id)?; mux.add_tab_to_window(&tab, window_id)?;