#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MouseEvent {
Press(u8),
Release(u8),
Move(Option<u8>),
Wheel(bool),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MouseMods {
pub shift: bool,
pub alt: bool,
pub ctrl: bool,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MouseModes {
pub x10: bool,
pub normal: bool,
pub drag: bool,
pub any: bool,
pub sgr: bool,
pub pixels: bool,
}
impl MouseModes {
pub fn tracking(&self) -> bool {
self.x10 || self.normal || self.drag || self.any
}
pub fn set(&mut self, mode: u32, on: bool) -> bool {
match mode {
9 => self.x10 = on,
1000 => self.normal = on,
1002 => self.drag = on,
1003 => self.any = on,
1006 => self.sgr = on,
1016 => self.pixels = on,
_ => return false,
}
true
}
pub fn set_modes(&self) -> Vec<u32> {
[(9, self.x10), (1000, self.normal), (1002, self.drag), (1003, self.any), (1006, self.sgr), (1016, self.pixels)]
.iter()
.filter(|m| m.1)
.map(|m| m.0)
.collect()
}
pub fn report(
&self,
event: MouseEvent,
cell: (usize, usize),
pixel: (usize, usize),
mods: MouseMods,
) -> Option<Vec<u8>> {
if !self.tracking() {
return None;
}
let (code, release) = match event {
MouseEvent::Press(b) => (b.min(2) as u32, false),
MouseEvent::Release(_) if self.x10 && !self.normal && !self.drag && !self.any => return None,
MouseEvent::Release(b) => (if self.sgr || self.pixels { b.min(2) as u32 } else { 3 }, true),
MouseEvent::Move(held) => {
if !(self.any || self.drag && held.is_some()) {
return None;
}
(32 + held.map_or(3, |b| b.min(2) as u32), false)
}
MouseEvent::Wheel(up) if !self.x10 || self.normal || self.drag || self.any => {
(if up { 64 } else { 65 }, false)
}
MouseEvent::Wheel(_) => return None,
};
let only_x10 = self.x10 && !self.normal && !self.drag && !self.any;
let keys =
if only_x10 { 0 } else { ((mods.shift as u32) * 4) | ((mods.alt as u32) * 8) | ((mods.ctrl as u32) * 16) };
let b = code | keys;
if self.sgr || self.pixels {
let (x, y) = if self.pixels { pixel } else { cell };
let end = if release { 'm' } else { 'M' };
return Some(format!("\x1b[<{b};{};{}{end}", x + 1, y + 1).into_bytes());
}
let (x, y) = (cell.0 + 33, cell.1 + 33);
(x <= 255 && y <= 255).then(|| vec![0x1B, b'[', b'M', (32 + b) as u8, x as u8, y as u8])
}
}
#[cfg(test)]
mod tests {
use super::*;
fn modes(list: &[u32]) -> MouseModes {
let mut m = MouseModes::default();
for &n in list {
m.set(n, true);
}
m
}
#[test]
fn reports() {
let none = MouseMods::default();
let at = (4, 2);
let px = (37, 21);
assert_eq!(MouseModes::default().report(MouseEvent::Press(0), at, px, none), None);
let x10 = modes(&[9]);
assert_eq!(x10.report(MouseEvent::Press(0), at, px, none).unwrap(), b"\x1b[M \x25\x23");
assert_eq!(x10.report(MouseEvent::Release(0), at, px, none), None);
let normal = modes(&[1000]);
assert_eq!(normal.report(MouseEvent::Release(0), at, px, none).unwrap(), b"\x1b[M#\x25\x23");
let ctrl = MouseMods { ctrl: true, ..none };
assert_eq!(normal.report(MouseEvent::Press(2), at, px, ctrl).unwrap(), b"\x1b[M\x32\x25\x23");
assert_eq!(normal.report(MouseEvent::Move(Some(0)), at, px, none), None);
let drag = modes(&[1002, 1006]);
assert_eq!(drag.report(MouseEvent::Move(Some(0)), at, px, none).unwrap(), b"\x1b[<32;5;3M");
assert_eq!(drag.report(MouseEvent::Move(None), at, px, none), None);
let any = modes(&[1003, 1006]);
assert_eq!(any.report(MouseEvent::Move(None), at, px, none).unwrap(), b"\x1b[<35;5;3M");
assert_eq!(any.report(MouseEvent::Release(2), at, px, none).unwrap(), b"\x1b[<2;5;3m");
assert_eq!(any.report(MouseEvent::Wheel(false), at, px, none).unwrap(), b"\x1b[<65;5;3M");
let pixels = modes(&[1000, 1016]);
assert_eq!(pixels.report(MouseEvent::Press(0), at, px, none).unwrap(), b"\x1b[<0;38;22M");
assert_eq!(normal.report(MouseEvent::Press(0), (230, 0), px, none), None);
}
}