aboutsummaryrefslogtreecommitdiff
path: root/src/app/input
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/input')
-rw-r--r--src/app/input/terminal.rs56
1 files changed, 51 insertions, 5 deletions
diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs
index b7a23322..282fa263 100644
--- a/src/app/input/terminal.rs
+++ b/src/app/input/terminal.rs
@@ -88,17 +88,19 @@ impl App {
self.state
.runtime_for_pane_in_workspace(&self.terminal_runtimes, ws_idx, pane_id)?;
- // Intercept plain PageUp/PageDown presses for pane scrollback when the
- // focused pane doesn't handle its own scrolling (e.g., a plain shell
- // with mouse off). Modified page keys are pane shortcuts, and release
- // events should not produce a second host-scroll action.
+ // Intercept plain PageUp/PageDown presses for pane scrollback only
+ // when the focused pane looks like a shell transcript. Normal-screen
+ // pagers such as `less -X` keep the primary screen but enter
+ // application cursor mode while they own special keys.
+ // Modified page keys are pane shortcuts, and release events should not
+ // produce a second host-scroll action.
// Only intercept when we know the pane state; if input_state is unknown,
// fail-open and forward the key to the pane.
if matches!(key_event.code, KeyCode::PageUp | KeyCode::PageDown)
&& key_event.modifiers.is_empty()
{
if let Some(input_state) = rt.input_state() {
- if !input_state.alternate_screen && !input_state.mouse_reporting_enabled() {
+ if input_state.plain_page_keys_use_host_scrollback() {
if key_event.kind == crossterm::event::KeyEventKind::Release {
return None;
}
@@ -1191,4 +1193,48 @@ mod tests {
// Forwarded to pane, so test runtime doesn't process it — scroll stays at bottom.
assert_eq!(end_metrics.offset_from_bottom, 0);
}
+
+ #[tokio::test]
+ async fn page_up_forwarded_to_primary_screen_application_cursor_pane() {
+ let mut app = app_for_mouse_test();
+ let mut ws = Workspace::test_new("test");
+ let pane_id = ws.tabs[0].root_pane;
+ let pane_infos = ws.tabs[0].layout.panes(Rect::new(26, 2, 80, 18));
+ let info = pane_infos[0].clone();
+ let mut bytes = b"\x1b[?1h".to_vec();
+ bytes.extend_from_slice(&numbered_lines_bytes(64));
+ let (runtime, mut input_rx) =
+ crate::terminal::TerminalRuntime::test_with_channel_and_scrollback_bytes(
+ info.inner_rect.width,
+ info.inner_rect.height,
+ 16 * 1024,
+ &bytes,
+ 4,
+ );
+ ws.tabs[0].runtimes.insert(pane_id, runtime);
+
+ app.state.workspaces = vec![ws];
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.mode = Mode::Terminal;
+ app.state.view.pane_infos = pane_infos;
+
+ let start_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(&app.terminal_runtimes, 0, pane_id)
+ .and_then(crate::terminal::TerminalRuntime::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 forwarded = input_rx.try_recv().expect("forwarded PageUp");
+ assert_eq!(forwarded.as_ref(), b"\x1b[5~");
+ let end_metrics = app
+ .state
+ .runtime_for_pane_in_workspace(&app.terminal_runtimes, 0, pane_id)
+ .and_then(crate::terminal::TerminalRuntime::scroll_metrics)
+ .expect("scroll metrics after PageUp");
+ assert_eq!(end_metrics.offset_from_bottom, 0);
+ }
}