aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-17 01:41:17 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-17 01:41:17 +0300
commit3ecacfddae86d603bfd0d1a0c01602b1d34b1df3 (patch)
tree277941c065295af2bd161c6be6fd7a2555c1f060 /src/app
parenta36da363f14639085bec91d7a378d6c2eeaa6c58 (diff)
feat: add direct terminal attach stream
Diffstat (limited to 'src/app')
-rw-r--r--src/app/input/mouse.rs4
-rw-r--r--src/app/input/navigate.rs5
-rw-r--r--src/app/input/terminal.rs45
-rw-r--r--src/app/mod.rs1
-rw-r--r--src/app/state.rs6
5 files changed, 37 insertions, 24 deletions
diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs
index 10ef9c18..827e37e3 100644
--- a/src/app/input/mouse.rs
+++ b/src/app/input/mouse.rs
@@ -1143,10 +1143,10 @@ impl AppState {
}
fn forward_pane_reported_wheel(&self, info: &PaneInfo, mouse: MouseEvent) -> bool {
- let Some(ws) = self.active.and_then(|i| self.workspaces.get(i)) else {
+ let Some(ws_idx) = self.active else {
return false;
};
- let Some(rt) = ws.runtimes.get(&info.id) else {
+ let Some(rt) = self.runtime_for_pane_in_workspace(ws_idx, info.id) else {
return false;
};
if !rt
diff --git a/src/app/input/navigate.rs b/src/app/input/navigate.rs
index 9fc11605..6445b6ab 100644
--- a/src/app/input/navigate.rs
+++ b/src/app/input/navigate.rs
@@ -261,8 +261,9 @@ impl App {
let pane_id = ws
.focused_pane_id()
.ok_or_else(|| std::io::Error::other("no focused pane"))?;
- let scrollback = ws
- .focused_runtime()
+ let scrollback = self
+ .state
+ .runtime_for_pane_in_workspace(ws_idx, pane_id)
.ok_or_else(|| std::io::Error::other("focused pane has no scrollback runtime"))?
.recent_text(usize::MAX);
diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs
index 3f0bff2c..90a1a0e2 100644
--- a/src/app/input/terminal.rs
+++ b/src/app/input/terminal.rs
@@ -508,16 +508,18 @@ mod tests {
app.state.mode = Mode::Terminal;
app.state.view.pane_infos = pane_infos;
- let start_metrics = app.state.workspaces[0]
- .runtime(pane_id)
+ let start_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("initial scroll metrics");
assert_eq!(start_metrics.offset_from_bottom, 0);
app.handle_terminal_key_headless(TerminalKey::new(KeyCode::PageUp, KeyModifiers::empty()));
- let end_metrics = app.state.workspaces[0]
- .runtime(pane_id)
+ let end_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageUp");
assert_eq!(
@@ -550,8 +552,9 @@ mod tests {
app.state.view.pane_infos = pane_infos;
app.handle_terminal_key_headless(TerminalKey::new(KeyCode::PageUp, KeyModifiers::empty()));
- let after_up = app.state.workspaces[0]
- .runtime(pane_id)
+ let after_up = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageUp");
assert!(after_up.offset_from_bottom > 0);
@@ -560,8 +563,9 @@ mod tests {
KeyCode::PageDown,
KeyModifiers::empty(),
));
- let after_down = app.state.workspaces[0]
- .runtime(pane_id)
+ let after_down = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageDown");
assert_eq!(after_down.offset_from_bottom, 0);
@@ -591,8 +595,9 @@ mod tests {
app.state.view.pane_infos = pane_infos;
app.handle_terminal_key_headless(TerminalKey::new(KeyCode::PageUp, KeyModifiers::empty()));
- let after_press = app.state.workspaces[0]
- .runtime(pane_id)
+ let after_press = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageUp press");
assert_eq!(
@@ -605,8 +610,9 @@ mod tests {
.with_kind(KeyEventKind::Release),
);
- let after_release = app.state.workspaces[0]
- .runtime(pane_id)
+ let after_release = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageUp release");
assert_eq!(
@@ -640,8 +646,9 @@ mod tests {
app.handle_terminal_key_headless(TerminalKey::new(KeyCode::PageUp, KeyModifiers::CONTROL));
- let metrics = app.state.workspaces[0]
- .runtime(pane_id)
+ let metrics = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after modified PageUp");
assert_eq!(metrics.offset_from_bottom, 0);
@@ -672,16 +679,18 @@ mod tests {
app.state.mode = Mode::Terminal;
app.state.view.pane_infos = pane_infos;
- let start_metrics = app.state.workspaces[0]
- .runtime(pane_id)
+ let start_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("initial scroll metrics");
assert_eq!(start_metrics.offset_from_bottom, 0);
app.handle_terminal_key_headless(TerminalKey::new(KeyCode::PageUp, KeyModifiers::empty()));
- let end_metrics = app.state.workspaces[0]
- .runtime(pane_id)
+ let end_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(0, pane_id)
.and_then(crate::pane::PaneRuntime::scroll_metrics)
.expect("scroll metrics after PageUp");
// Forwarded to pane, so test runtime doesn't process it — scroll stays at bottom.
diff --git a/src/app/mod.rs b/src/app/mod.rs
index b1fb9775..c5654db2 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -312,6 +312,7 @@ impl App {
let mut state = AppState {
terminals: std::collections::HashMap::new(),
terminal_runtimes: std::collections::HashMap::new(),
+ direct_attach_resize_locks: std::collections::HashSet::new(),
workspaces,
active,
selected,
diff --git a/src/app/state.rs b/src/app/state.rs
index daed47f0..ff5357b2 100644
--- a/src/app/state.rs
+++ b/src/app/state.rs
@@ -822,6 +822,8 @@ pub struct AppState {
std::collections::HashMap<crate::terminal::TerminalId, crate::terminal::TerminalState>,
pub terminal_runtimes:
std::collections::HashMap<crate::terminal::TerminalId, crate::terminal::TerminalRuntime>,
+ /// Terminal ids whose size is currently owned by a direct attach client.
+ pub direct_attach_resize_locks: std::collections::HashSet<crate::terminal::TerminalId>,
pub workspaces: Vec<Workspace>,
pub active: Option<usize>,
pub selected: usize,
@@ -933,8 +935,7 @@ impl AppState {
self.mode == Mode::Terminal
&& self
.active
- .and_then(|idx| self.workspaces.get(idx))
- .and_then(crate::workspace::Workspace::focused_runtime)
+ .and_then(|idx| self.focused_runtime_in_workspace(idx))
.and_then(crate::pane::PaneRuntime::input_state)
.is_some_and(crate::pane::InputState::mouse_reporting_enabled)
}
@@ -1060,6 +1061,7 @@ impl AppState {
Self {
terminals: std::collections::HashMap::new(),
terminal_runtimes: std::collections::HashMap::new(),
+ direct_attach_resize_locks: std::collections::HashSet::new(),
workspaces: Vec::new(),
active: None,
selected: 0,