1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum MouseEvent {
10 Press(u8),
11 Release(u8),
12 Move(Option<u8>),
14 Wheel(bool),
16}
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub struct MouseMods {
21 pub shift: bool,
22 pub alt: bool,
23 pub ctrl: bool,
24}
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
28pub struct MouseModes {
29 pub x10: bool,
30 pub normal: bool,
31 pub drag: bool,
32 pub any: bool,
33 pub sgr: bool,
34 pub pixels: bool,
35}
36
37impl MouseModes {
38 pub fn tracking(&self) -> bool {
40 self.x10 || self.normal || self.drag || self.any
41 }
42
43 pub fn set(&mut self, mode: u32, on: bool) -> bool {
45 match mode {
46 9 => self.x10 = on,
47 1000 => self.normal = on,
48 1002 => self.drag = on,
49 1003 => self.any = on,
50 1006 => self.sgr = on,
51 1016 => self.pixels = on,
52 _ => return false,
53 }
54 true
55 }
56
57 pub fn set_modes(&self) -> Vec<u32> {
59 [(9, self.x10), (1000, self.normal), (1002, self.drag), (1003, self.any), (1006, self.sgr), (1016, self.pixels)]
60 .iter()
61 .filter(|m| m.1)
62 .map(|m| m.0)
63 .collect()
64 }
65
66 pub fn report(
69 &self,
70 event: MouseEvent,
71 cell: (usize, usize),
72 pixel: (usize, usize),
73 mods: MouseMods,
74 ) -> Option<Vec<u8>> {
75 if !self.tracking() {
76 return None;
77 }
78 let (code, release) = match event {
79 MouseEvent::Press(b) => (b.min(2) as u32, false),
80 MouseEvent::Release(_) if self.x10 && !self.normal && !self.drag && !self.any => return None,
81 MouseEvent::Release(b) => (if self.sgr || self.pixels { b.min(2) as u32 } else { 3 }, true),
82 MouseEvent::Move(held) => {
83 if !(self.any || self.drag && held.is_some()) {
84 return None;
85 }
86 (32 + held.map_or(3, |b| b.min(2) as u32), false)
87 }
88 MouseEvent::Wheel(up) if !self.x10 || self.normal || self.drag || self.any => {
89 (if up { 64 } else { 65 }, false)
90 }
91 MouseEvent::Wheel(_) => return None,
92 };
93 let only_x10 = self.x10 && !self.normal && !self.drag && !self.any;
95 let keys =
96 if only_x10 { 0 } else { ((mods.shift as u32) * 4) | ((mods.alt as u32) * 8) | ((mods.ctrl as u32) * 16) };
97 let b = code | keys;
98 if self.sgr || self.pixels {
99 let (x, y) = if self.pixels { pixel } else { cell };
100 let end = if release { 'm' } else { 'M' };
101 return Some(format!("\x1b[<{b};{};{}{end}", x + 1, y + 1).into_bytes());
102 }
103 let (x, y) = (cell.0 + 33, cell.1 + 33);
105 (x <= 255 && y <= 255).then(|| vec![0x1B, b'[', b'M', (32 + b) as u8, x as u8, y as u8])
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 fn modes(list: &[u32]) -> MouseModes {
114 let mut m = MouseModes::default();
115 for &n in list {
116 m.set(n, true);
117 }
118 m
119 }
120
121 #[test]
122 fn reports() {
123 let none = MouseMods::default();
124 let at = (4, 2);
125 let px = (37, 21);
126 assert_eq!(MouseModes::default().report(MouseEvent::Press(0), at, px, none), None);
128 let x10 = modes(&[9]);
130 assert_eq!(x10.report(MouseEvent::Press(0), at, px, none).unwrap(), b"\x1b[M \x25\x23");
131 assert_eq!(x10.report(MouseEvent::Release(0), at, px, none), None);
132 let normal = modes(&[1000]);
134 assert_eq!(normal.report(MouseEvent::Release(0), at, px, none).unwrap(), b"\x1b[M#\x25\x23");
135 let ctrl = MouseMods { ctrl: true, ..none };
136 assert_eq!(normal.report(MouseEvent::Press(2), at, px, ctrl).unwrap(), b"\x1b[M\x32\x25\x23");
137 assert_eq!(normal.report(MouseEvent::Move(Some(0)), at, px, none), None);
138 let drag = modes(&[1002, 1006]);
140 assert_eq!(drag.report(MouseEvent::Move(Some(0)), at, px, none).unwrap(), b"\x1b[<32;5;3M");
141 assert_eq!(drag.report(MouseEvent::Move(None), at, px, none), None);
142 let any = modes(&[1003, 1006]);
143 assert_eq!(any.report(MouseEvent::Move(None), at, px, none).unwrap(), b"\x1b[<35;5;3M");
144 assert_eq!(any.report(MouseEvent::Release(2), at, px, none).unwrap(), b"\x1b[<2;5;3m");
145 assert_eq!(any.report(MouseEvent::Wheel(false), at, px, none).unwrap(), b"\x1b[<65;5;3M");
146 let pixels = modes(&[1000, 1016]);
148 assert_eq!(pixels.report(MouseEvent::Press(0), at, px, none).unwrap(), b"\x1b[<0;38;22M");
149 assert_eq!(normal.report(MouseEvent::Press(0), (230, 0), px, none), None);
151 }
152}