use vt100::{MouseProtocolEncoding, MouseProtocolMode, Screen};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScrollAction {
Scrollback(i16),
Forward(Vec<u8>),
}
pub fn resolve_scroll(delta: i16, screen: &Screen) -> ScrollAction {
if delta == 0 || !screen.alternate_screen() {
return ScrollAction::Scrollback(delta);
}
let up = delta > 0;
if screen.mouse_protocol_mode() == MouseProtocolMode::None {
ScrollAction::Forward(arrow_key_seq(
up,
delta.unsigned_abs() as usize,
screen.application_cursor(),
))
} else {
ScrollAction::Forward(wheel_mouse_seq(up, screen.mouse_protocol_encoding()))
}
}
fn wheel_mouse_seq(up: bool, encoding: MouseProtocolEncoding) -> Vec<u8> {
let button: u16 = if up { 64 } else { 65 };
match encoding {
MouseProtocolEncoding::Sgr => format!("\x1b[<{button};1;1M").into_bytes(),
_ => vec![0x1b, b'[', b'M', (button + 32) as u8, 1 + 32, 1 + 32],
}
}
fn arrow_key_seq(up: bool, count: usize, application_cursor: bool) -> Vec<u8> {
let seq: &[u8] = match (up, application_cursor) {
(true, false) => b"\x1b[A",
(false, false) => b"\x1b[B",
(true, true) => b"\x1bOA",
(false, true) => b"\x1bOB",
};
seq.repeat(count.max(1))
}
#[cfg(test)]
mod tests {
use super::*;
use vt100::Parser;
fn parser_with(seqs: &[&[u8]]) -> Parser {
let mut p = Parser::new(24, 80, 1000);
for s in seqs {
p.process(s);
}
p
}
#[test]
fn normal_screen_scrolls_scrollback() {
let p = parser_with(&[]);
assert_eq!(resolve_scroll(3, p.screen()), ScrollAction::Scrollback(3));
assert_eq!(resolve_scroll(-3, p.screen()), ScrollAction::Scrollback(-3));
}
#[test]
fn zero_delta_is_noop_scrollback() {
let p = parser_with(&[b"\x1b[?1049h"]);
assert_eq!(resolve_scroll(0, p.screen()), ScrollAction::Scrollback(0));
}
#[test]
fn alt_screen_without_mouse_sends_arrow_keys() {
let p = parser_with(&[b"\x1b[?1049h"]);
assert_eq!(
resolve_scroll(3, p.screen()),
ScrollAction::Forward(b"\x1b[A\x1b[A\x1b[A".to_vec())
);
assert_eq!(
resolve_scroll(-3, p.screen()),
ScrollAction::Forward(b"\x1b[B\x1b[B\x1b[B".to_vec())
);
}
#[test]
fn alt_screen_with_application_cursor_uses_ss3() {
let p = parser_with(&[b"\x1b[?1049h", b"\x1b[?1h"]);
assert_eq!(
resolve_scroll(3, p.screen()),
ScrollAction::Forward(b"\x1bOA\x1bOA\x1bOA".to_vec())
);
}
#[test]
fn alt_screen_with_mouse_sends_x10_wheel() {
let p = parser_with(&[b"\x1b[?1049h", b"\x1b[?1000h"]);
assert_eq!(
resolve_scroll(3, p.screen()),
ScrollAction::Forward(vec![0x1b, b'[', b'M', 96, 33, 33])
);
assert_eq!(
resolve_scroll(-3, p.screen()),
ScrollAction::Forward(vec![0x1b, b'[', b'M', 97, 33, 33])
);
}
#[test]
fn alt_screen_with_sgr_mouse_sends_sgr_wheel() {
let p = parser_with(&[b"\x1b[?1049h", b"\x1b[?1000h", b"\x1b[?1006h"]);
assert_eq!(
resolve_scroll(3, p.screen()),
ScrollAction::Forward(b"\x1b[<64;1;1M".to_vec())
);
assert_eq!(
resolve_scroll(-3, p.screen()),
ScrollAction::Forward(b"\x1b[<65;1;1M".to_vec())
);
}
}