use crossterm::event::{KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
pub fn encode(event: MouseEvent, local_col: u16, local_row: u16) -> Option<Vec<u8>> {
let (button_base, suffix) = match event.kind {
MouseEventKind::Down(b) => (button_code(b)?, b'M'),
MouseEventKind::Up(b) => (button_code(b)?, b'm'),
MouseEventKind::Drag(b) => (button_code(b)? + 32, b'M'),
MouseEventKind::Moved => (35, b'M'),
MouseEventKind::ScrollUp => (64, b'M'),
MouseEventKind::ScrollDown => (65, b'M'),
MouseEventKind::ScrollLeft => (66, b'M'),
MouseEventKind::ScrollRight => (67, b'M'),
};
let button = button_base + modifier_offset(event.modifiers);
let col = local_col.saturating_add(1);
let row = local_row.saturating_add(1);
let mut out = Vec::with_capacity(16);
out.extend_from_slice(b"\x1b[<");
out.extend_from_slice(button.to_string().as_bytes());
out.push(b';');
out.extend_from_slice(col.to_string().as_bytes());
out.push(b';');
out.extend_from_slice(row.to_string().as_bytes());
out.push(suffix);
Some(out)
}
fn button_code(b: MouseButton) -> Option<u32> {
match b {
MouseButton::Left => Some(0),
MouseButton::Middle => Some(1),
MouseButton::Right => Some(2),
}
}
fn modifier_offset(m: KeyModifiers) -> u32 {
let mut off = 0u32;
if m.contains(KeyModifiers::SHIFT) {
off += 4;
}
if m.contains(KeyModifiers::ALT) {
off += 8;
}
if m.contains(KeyModifiers::CONTROL) {
off += 16;
}
off
}
#[cfg(test)]
mod tests {
use super::*;
fn ev(kind: MouseEventKind, col: u16, row: u16, mods: KeyModifiers) -> MouseEvent {
MouseEvent {
kind,
column: col,
row,
modifiers: mods,
}
}
#[test]
fn left_press_at_origin() {
let e = ev(
MouseEventKind::Down(MouseButton::Left),
0,
0,
KeyModifiers::NONE,
);
assert_eq!(encode(e, 0, 0).unwrap(), b"\x1b[<0;1;1M".to_vec());
}
#[test]
fn left_release_uses_lowercase_m() {
let e = ev(
MouseEventKind::Up(MouseButton::Left),
5,
5,
KeyModifiers::NONE,
);
assert_eq!(encode(e, 5, 5).unwrap(), b"\x1b[<0;6;6m".to_vec());
}
#[test]
fn right_press_is_button_2() {
let e = ev(
MouseEventKind::Down(MouseButton::Right),
10,
3,
KeyModifiers::NONE,
);
assert_eq!(encode(e, 10, 3).unwrap(), b"\x1b[<2;11;4M".to_vec());
}
#[test]
fn scroll_up_is_64() {
let e = ev(MouseEventKind::ScrollUp, 0, 0, KeyModifiers::NONE);
assert_eq!(encode(e, 0, 0).unwrap(), b"\x1b[<64;1;1M".to_vec());
}
#[test]
fn scroll_down_is_65() {
let e = ev(MouseEventKind::ScrollDown, 0, 0, KeyModifiers::NONE);
assert_eq!(encode(e, 0, 0).unwrap(), b"\x1b[<65;1;1M".to_vec());
}
#[test]
fn left_drag_adds_motion_bit() {
let e = ev(
MouseEventKind::Drag(MouseButton::Left),
2,
2,
KeyModifiers::NONE,
);
assert_eq!(encode(e, 2, 2).unwrap(), b"\x1b[<32;3;3M".to_vec());
}
#[test]
fn ctrl_left_press_adds_16() {
let e = ev(
MouseEventKind::Down(MouseButton::Left),
0,
0,
KeyModifiers::CONTROL,
);
assert_eq!(encode(e, 0, 0).unwrap(), b"\x1b[<16;1;1M".to_vec());
}
#[test]
fn shift_scroll_up_adds_4() {
let e = ev(MouseEventKind::ScrollUp, 0, 0, KeyModifiers::SHIFT);
assert_eq!(encode(e, 0, 0).unwrap(), b"\x1b[<68;1;1M".to_vec());
}
}