use justerm_core::{Engine, Modifiers, MouseAction, MouseButton, MouseEvent};
fn press(col: usize, row: usize) -> MouseEvent {
MouseEvent {
button: Some(MouseButton::Left),
action: MouseAction::Press,
col,
row,
px: 0,
py: 0,
mods: Modifiers::empty(),
}
}
fn wheel(button: MouseButton, col: usize, row: usize) -> MouseEvent {
MouseEvent {
button: Some(button),
action: MouseAction::Press,
col,
row,
px: 0,
py: 0,
mods: Modifiers::empty(),
}
}
#[test]
fn wheel_left_encodes_sgr_cb_66() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h"); assert_eq!(
t.encode_mouse(wheel(MouseButton::WheelLeft, 5, 10))
.unwrap(),
b"\x1b[<66;6;11M"
);
}
#[test]
fn wheel_right_encodes_sgr_cb_67() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
assert_eq!(
t.encode_mouse(wheel(MouseButton::WheelRight, 5, 10))
.unwrap(),
b"\x1b[<67;6;11M"
);
}
#[test]
fn wheel_tilt_rides_the_x10_default_framing() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h"); assert_eq!(
t.encode_mouse(wheel(MouseButton::WheelLeft, 5, 10))
.unwrap(),
vec![0x1b, b'[', b'M', 98, 38, 43]
);
}
#[test]
fn modifier_bits_compose_with_a_wheel_button() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
let mut e = wheel(MouseButton::WheelLeft, 5, 10);
e.mods = Modifiers::CTRL;
assert_eq!(t.encode_mouse(e).unwrap(), b"\x1b[<82;6;11M");
}
#[test]
fn wheel_release_emits_nothing() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
let mut e = wheel(MouseButton::WheelUp, 5, 10);
e.action = MouseAction::Release;
assert!(
t.encode_mouse(e).is_none(),
"wheel has no release; must not emit a report",
);
}
fn btn(button: MouseButton, col: usize, row: usize) -> MouseEvent {
MouseEvent {
button: Some(button),
action: MouseAction::Press,
col,
row,
px: 0,
py: 0,
mods: Modifiers::empty(),
}
}
#[test]
fn back_button_encodes_sgr_cb_128() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h"); assert_eq!(
t.encode_mouse(btn(MouseButton::Back, 5, 10)).unwrap(),
b"\x1b[<128;6;11M"
);
}
#[test]
fn forward_button_encodes_sgr_cb_129() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
assert_eq!(
t.encode_mouse(btn(MouseButton::Forward, 5, 10)).unwrap(),
b"\x1b[<129;6;11M"
);
}
#[test]
fn other_buttons_encode_via_the_xterm_bit_formula() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
assert_eq!(
t.encode_mouse(btn(MouseButton::Other(10), 5, 10)).unwrap(),
b"\x1b[<130;6;11M"
);
assert_eq!(
t.encode_mouse(btn(MouseButton::Other(11), 5, 10)).unwrap(),
b"\x1b[<131;6;11M"
);
}
#[test]
fn back_button_has_a_real_release_unlike_the_wheel() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1006h");
let mut e = btn(MouseButton::Back, 5, 10);
e.action = MouseAction::Release;
assert_eq!(t.encode_mouse(e).unwrap(), b"\x1b[<128;6;11m");
}
#[test]
fn back_button_rides_the_x10_default_framing() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h"); assert_eq!(
t.encode_mouse(btn(MouseButton::Back, 5, 10)).unwrap(),
vec![0x1b, b'[', b'M', 160, 38, 43]
);
}
#[test]
fn x10_mouse_reports_a_bare_press() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?9h"); assert_eq!(
t.encode_mouse(btn(MouseButton::Left, 5, 10)).unwrap(),
vec![0x1b, b'[', b'M', 32, 38, 43]
);
}
#[test]
fn x10_mouse_drops_release_motion_and_wheel() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?9h");
let mut rel = btn(MouseButton::Left, 5, 10);
rel.action = MouseAction::Release;
assert!(t.encode_mouse(rel).is_none(), "X10 reports no release");
let mut mv = btn(MouseButton::Left, 5, 10);
mv.action = MouseAction::Motion;
assert!(t.encode_mouse(mv).is_none(), "X10 reports no motion");
assert!(
t.encode_mouse(btn(MouseButton::WheelUp, 5, 10)).is_none(),
"X10 reports no wheel",
);
}
#[test]
fn x10_mouse_strips_modifiers() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?9h");
let mut e = btn(MouseButton::Left, 5, 10);
e.mods = Modifiers::CTRL;
assert_eq!(
t.encode_mouse(e).unwrap(),
vec![0x1b, b'[', b'M', 32, 38, 43]
);
}
#[test]
fn decrqm_reports_x10_mode() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?9$p"); assert_eq!(t.drain_replies(), b"\x1b[?9;2$y");
t.feed(b"\x1b[?9h\x1b[?9$p"); assert_eq!(t.drain_replies(), b"\x1b[?9;1$y");
}
#[test]
fn urxvt_encoding_is_decimal_csi_m() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1015h"); assert_eq!(t.encode_mouse(press(5, 10)).unwrap(), b"\x1b[32;6;11M");
}
#[test]
fn utf8_encoding_multibyte_past_127() {
let mut t = Engine::new(300, 24);
t.feed(b"\x1b[?1000h\x1b[?1005h"); assert_eq!(
t.encode_mouse(press(200, 10)).unwrap(),
b"\x1b[M\x20\xc3\xa9\x2b"
);
}
#[test]
fn sgr_pixels_uses_consumer_pixel_coordinates() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1016h"); let mut e = press(5, 10);
e.px = 411;
e.py = 214;
assert_eq!(t.encode_mouse(e).unwrap(), b"\x1b[<0;412;215M");
}
#[test]
fn sgr_pixels_release_uses_lowercase_m() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1016h");
let mut e = press(5, 10);
e.action = MouseAction::Release;
e.px = 10;
e.py = 20;
assert_eq!(t.encode_mouse(e).unwrap(), b"\x1b[<0;11;21m");
}
#[test]
fn disabling_an_encoding_returns_to_default() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h\x1b[?1015h"); t.feed(b"\x1b[?1015l"); assert_eq!(
t.encode_mouse(press(5, 10)).unwrap(),
vec![0x1b, b'[', b'M', 32, 38, 43]
);
}
fn motion(button: Option<MouseButton>, col: usize, row: usize) -> MouseEvent {
MouseEvent {
button,
action: MouseAction::Motion,
col,
row,
px: 0,
py: 0,
mods: Modifiers::empty(),
}
}
#[test]
fn normal_tracking_drops_all_motion() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1000h"); assert!(
t.encode_mouse(motion(Some(MouseButton::Left), 5, 10))
.is_none()
);
assert!(t.encode_mouse(motion(None, 5, 10)).is_none());
}
#[test]
fn button_event_reports_drag_but_not_bare_motion() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1002h"); assert!(
t.encode_mouse(motion(Some(MouseButton::Left), 5, 10))
.is_some()
);
assert!(t.encode_mouse(motion(None, 5, 10)).is_none());
}
#[test]
fn any_event_reports_bare_motion() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1003h"); assert!(t.encode_mouse(motion(None, 5, 10)).is_some());
}