aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Linford <tlinford@users.noreply.github.com>2022-09-04 16:26:15 +0200
committerGitHub <noreply@github.com>2022-09-04 16:26:15 +0200
commitebbd46ea3bcc10f003ca4b83b28c550b953dc2a1 (patch)
tree1d25651e56c88aadb81b851029e557b63abbb030
parentflake.lock: Update (#1703) (diff)
downloadsl0ppij-ebbd46ea3bcc10f003ca4b83b28c550b953dc2a1.tar.gz
sl0ppij-ebbd46ea3bcc10f003ca4b83b28c550b953dc2a1.tar.xz
sl0ppij-ebbd46ea3bcc10f003ca4b83b28c550b953dc2a1.zip
feat(compatibility): mouse wheel faux scrolling in alternate screen (#1678)
* implement faux scrolling * update changelog * fix tests * cursor keys mode handling * add integration test * undo changelog reformatting
-rw-r--r--CHANGELOG.md1
-rw-r--r--zellij-server/src/panes/grid.rs3
-rw-r--r--zellij-server/src/panes/terminal_pane.rs3
-rw-r--r--zellij-server/src/screen.rs4
-rw-r--r--zellij-server/src/tab/mod.rs20
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs80
6 files changed, 99 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9970ba2f..50d0ccab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* fix: occasional startup crashes (https://github.com/zellij-org/zellij/pull/1706)
* fix: gracefully handle SSH disconnects (https://github.com/zellij-org/zellij/pull/1710)
* fix: handle osc params larger than 1024 bytes (https://github.com/zellij-org/zellij/pull/1711)
+* Terminal compatibility: implement faux scrolling when in alternate screen mode(https://github.com/zellij-org/zellij/pull/1678)
## [0.31.3] - 2022-08-18
* HOTFIX: fix up-arrow regression
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index 98ea652b..677eed16 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -1931,6 +1931,9 @@ impl Grid {
},
}
}
+ pub fn is_alternate_mode_active(&self) -> bool {
+ self.alternate_screen_state.is_some()
+ }
}
impl Perform for Grid {
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 5be45426..d8b6c605 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -650,6 +650,9 @@ impl Pane for TerminalPane {
self.grid.clear_search();
self.search_term.clear();
}
+ fn is_alternate_mode_active(&self) -> bool {
+ self.grid.is_alternate_mode_active()
+ }
}
impl TerminalPane {
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index deba6eb9..e3323b41 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -1137,7 +1137,7 @@ pub(crate) fn screen_thread_main(
},
ScreenInstruction::ScrollUpAt(point, client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab
- .scroll_terminal_up(&point, 3, client_id));
+ .handle_scrollwheel_up(&point, 3, client_id));
screen.render();
},
ScreenInstruction::ScrollDown(client_id) => {
@@ -1147,7 +1147,7 @@ pub(crate) fn screen_thread_main(
},
ScreenInstruction::ScrollDownAt(point, client_id) => {
active_tab!(screen, client_id, |tab: &mut Tab| tab
- .scroll_terminal_down(&point, 3, client_id));
+ .handle_scrollwheel_down(&point, 3, client_id));
screen.render();
},
ScreenInstruction::ScrollToBottom(client_id) => {
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 750f4944..04626bef 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -346,6 +346,10 @@ pub trait Pane {
fn clear_search(&mut self) {
// No-op by default (only terminal-panes currently have search capability)
}
+ fn is_alternate_mode_active(&self) -> bool {
+ // False by default (only terminal-panes support alternate mode)
+ false
+ }
}
impl Tab {
@@ -1690,21 +1694,33 @@ impl Tab {
}
}
}
- pub fn scroll_terminal_up(&mut self, point: &Position, lines: usize, client_id: ClientId) {
+ pub fn handle_scrollwheel_up(&mut self, point: &Position, lines: usize, client_id: ClientId) {
if let Some(pane) = self.get_pane_at(point, false) {
let relative_position = pane.relative_position(point);
if let Some(mouse_event) = pane.mouse_scroll_up(&relative_position) {
self.write_to_terminal_at(mouse_event.into_bytes(), point);
+ } else if pane.is_alternate_mode_active() {
+ // faux scrolling, send UP n times
+ // do n separate writes to make sure the sequence gets adjusted for cursor keys mode
+ for _ in 0..lines {
+ self.write_to_terminal_at("\u{1b}[A".as_bytes().to_owned(), point)
+ }
} else {
pane.scroll_up(lines, client_id);
}
}
}
- pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize, client_id: ClientId) {
+ pub fn handle_scrollwheel_down(&mut self, point: &Position, lines: usize, client_id: ClientId) {
if let Some(pane) = self.get_pane_at(point, false) {
let relative_position = pane.relative_position(point);
if let Some(mouse_event) = pane.mouse_scroll_down(&relative_position) {
self.write_to_terminal_at(mouse_event.into_bytes(), point);
+ } else if pane.is_alternate_mode_active() {
+ // faux scrolling, send DOWN n times
+ // do n separate writes to make sure the sequence gets adjusted for cursor keys mode
+ for _ in 0..lines {
+ self.write_to_terminal_at("\u{1b}[B".as_bytes().to_owned(), point)
+ }
} else {
pane.scroll_down(lines, client_id);
if !pane.is_scrolled() {
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs
index 41e2fee4..2803529f 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -1830,8 +1830,8 @@ fn pane_in_sgr_button_event_tracking_mouse_mode() {
tab.handle_middle_click(&Position::new(5, 71), client_id);
tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id);
tab.handle_middle_mouse_release(&Position::new(7, 75), client_id);
- tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id);
- tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive
assert_eq!(
*messages_to_pty_writer.lock().unwrap(),
@@ -1896,8 +1896,8 @@ fn pane_in_sgr_normal_event_tracking_mouse_mode() {
tab.handle_middle_click(&Position::new(5, 71), client_id);
tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id);
tab.handle_middle_mouse_release(&Position::new(7, 75), client_id);
- tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id);
- tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive
assert_eq!(
*messages_to_pty_writer.lock().unwrap(),
@@ -1962,8 +1962,8 @@ fn pane_in_utf8_button_event_tracking_mouse_mode() {
tab.handle_middle_click(&Position::new(5, 71), client_id);
tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id);
tab.handle_middle_mouse_release(&Position::new(7, 75), client_id);
- tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id);
- tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive
assert_eq!(
*messages_to_pty_writer.lock().unwrap(),
@@ -2028,8 +2028,8 @@ fn pane_in_utf8_normal_event_tracking_mouse_mode() {
tab.handle_middle_click(&Position::new(5, 71), client_id);
tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id);
tab.handle_middle_mouse_release(&Position::new(7, 75), client_id);
- tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id);
- tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id);
+ tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive
assert_eq!(
*messages_to_pty_writer.lock().unwrap(),
@@ -2096,3 +2096,67 @@ fn pane_bracketed_paste_ignored_when_not_in_bracketed_paste_mode() {
vec!["", "test", ""]
);
}
+
+#[test]
+fn pane_faux_scrolling_in_alternate_mode() {
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let client_id: u16 = 1;
+ let lines_to_scroll = 3;
+
+ let messages_to_pty_writer = Arc::new(Mutex::new(vec![]));
+ let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> =
+ channels::unbounded();
+ let to_pty_writer = SenderWithContext::new(to_pty_writer);
+ let mut tab =
+ create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer.clone());
+
+ let _pty_writer_thread = std::thread::Builder::new()
+ .name("pty_writer".to_string())
+ .spawn({
+ let messages_to_pty_writer = messages_to_pty_writer.clone();
+ move || loop {
+ let (event, _err_ctx) = pty_writer_receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ PtyWriteInstruction::Write(msg, _) => messages_to_pty_writer
+ .lock()
+ .unwrap()
+ .push(String::from_utf8_lossy(&msg).to_string()),
+ PtyWriteInstruction::Exit => break,
+ }
+ }
+ });
+
+ let enable_alternate_screen = String::from("\u{1b}[?1049h"); // CSI ? 1049 h -> switch to the Alternate Screen Buffer
+ let set_application_mode = String::from("\u{1b}[?1h");
+
+ // no output since alternate scren not active yet
+ tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id);
+ tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id);
+
+ tab.handle_pty_bytes(1, enable_alternate_screen.as_bytes().to_vec());
+ // CSI A * lines_to_scroll, CSI B * lines_to_scroll
+ tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id);
+ tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id);
+
+ tab.handle_pty_bytes(1, set_application_mode.as_bytes().to_vec());
+ // SS3 A * lines_to_scroll, SS3 B * lines_to_scroll
+ tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id);
+ tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id);
+
+ to_pty_writer.send(PtyWriteInstruction::Exit).unwrap();
+
+ std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive
+
+ let mut expected: Vec<&str> = Vec::new();
+ expected.append(&mut vec!["\u{1b}[A"; lines_to_scroll]);
+ expected.append(&mut vec!["\u{1b}[B"; lines_to_scroll]);
+ expected.append(&mut vec!["\u{1b}OA"; lines_to_scroll]);
+ expected.append(&mut vec!["\u{1b}OB"; lines_to_scroll]);
+
+ assert_eq!(*messages_to_pty_writer.lock().unwrap(), expected);
+}