1use std::fmt;
3use tracing::trace;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
7pub enum Arrow {
8 Up,
9 Down,
10 Left,
11 Right,
12}
13
14impl Arrow {
15 pub fn flip(&self) -> Self {
16 match self {
17 Self::Up => Self::Down,
18 Self::Down => Self::Up,
19 Self::Left => Self::Right,
20 Self::Right => Self::Left,
21 }
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
27#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
28pub enum Input {
29 Char(char),
30 Ctrl(char),
31 Alt(char),
32 CtrlAlt(char),
33 Tab,
34 BackTab,
35 AltReturn,
36 Return,
37 Backspace,
38 Arrow(Arrow),
39 Del,
40 Home,
41 End,
42 PageUp,
43 PageDown,
44 Esc,
45 Mouse(MouseEvent),
46}
47
48impl fmt::Display for Input {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Char(c) if *c == ' ' => write!(f, "<space>"),
52 Self::Char(c) => write!(f, "{c}"),
53 Self::Ctrl(c) if *c == ' ' => write!(f, "C-<space>"),
54 Self::Ctrl(c) => write!(f, "C-{c}"),
55 Self::Alt(c) if *c == ' ' => write!(f, "A-<space>"),
56 Self::Alt(c) => write!(f, "A-{c}"),
57 Self::CtrlAlt(c) if *c == ' ' => write!(f, "C-A-<space>"),
58 Self::CtrlAlt(c) => write!(f, "C-A-{c}"),
59 Self::Tab => write!(f, "<tab>"),
60 Self::BackTab => write!(f, "S-<tab>"),
61 Self::AltReturn => write!(f, "A-<return>"),
62 Self::Return => write!(f, "<return>"),
63 Self::Backspace => write!(f, "<backspace>"),
64 Self::Arrow(a) => write!(f, "<{a:?}>"),
65 Self::Del => write!(f, "<delete>"),
66 Self::Home => write!(f, "<home>"),
67 Self::End => write!(f, "<end>"),
68 Self::PageUp => write!(f, "<page-up>"),
69 Self::PageDown => write!(f, "<page-down>"),
70 Self::Esc => write!(f, "<escape>"),
71 Self::Mouse(m) => write!(f, "<{m:?}>"), }
73 }
74}
75
76impl Input {
77 pub fn from_char(c: char) -> Self {
78 match c {
79 '\x1b' => Input::Esc,
80 '\n' | '\r' => Input::Return,
81 '\x7f' => Input::Backspace,
82 '\t' => Input::Tab,
83 c @ '\x01'..='\x1A' => Input::Ctrl((c as u8 - 0x1 + b'a') as char),
84 c @ '\x1C'..='\x1F' => Input::Ctrl((c as u8 - 0x1C + b'4') as char),
85 _ => Input::Char(c),
86 }
87 }
88
89 pub fn try_from_seq2(c1: char, c2: char) -> Option<Self> {
90 match (c1, c2) {
91 ('O', 'H') => Some(Input::Home),
92 ('O', 'F') => Some(Input::End),
93
94 ('[', 'A') => Some(Input::Arrow(Arrow::Up)),
95 ('[', 'B') => Some(Input::Arrow(Arrow::Down)),
96 ('[', 'C') => Some(Input::Arrow(Arrow::Right)),
97 ('[', 'D') => Some(Input::Arrow(Arrow::Left)),
98 ('[', 'H') => Some(Input::Home),
99 ('[', 'Z') => Some(Input::BackTab),
100 ('\x1b', c) if c.is_ascii() => match Self::from_char(c) {
101 Input::Char(c) => Some(Input::Alt(c)),
102 Input::Ctrl(c) => Some(Input::CtrlAlt(c)),
103 Input::Return => Some(Input::AltReturn),
104 _ => None,
105 },
106 _ => None,
107 }
108 }
109
110 pub fn try_from_bracket_tilde(c: char) -> Option<Self> {
111 match c {
112 '1' | '7' => Some(Input::Home),
113 '4' | '8' => Some(Input::End),
114 '3' => Some(Input::Del),
115 '5' => Some(Input::PageUp),
116 '6' => Some(Input::PageDown),
117 _ => None,
118 }
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
123#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
124pub enum MouseButton {
125 Left,
126 Middle,
127 Right,
128 WheelUp,
129 WheelDown,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
133#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
134pub enum MouseMod {
135 NoMod,
136 Alt,
137 Ctrl,
138 AltCtrl,
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
142#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
143pub enum MouseEventKind {
144 Press,
145 Hold,
146 Release,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
150#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
151pub struct MouseEvent {
152 pub k: MouseEventKind,
153 pub m: MouseMod,
154 pub b: MouseButton,
155 pub x: usize,
156 pub y: usize,
157}
158
159impl MouseEvent {
160 pub(crate) fn try_from_raw(b: usize, x: usize, y: usize, c: char) -> Option<Self> {
161 use MouseButton::*;
162 use MouseEventKind::*;
163 use MouseMod::*;
164
165 let (k, m, b) = match (b, c) {
166 (0, 'M') => (Press, NoMod, Left),
168 (1, 'M') => (Press, NoMod, Middle),
169 (2, 'M') => (Press, NoMod, Right),
170 (64, 'M') => (Press, NoMod, WheelUp),
171 (65, 'M') => (Press, NoMod, WheelDown),
172
173 (0, 'm') | (3, _) => (Release, NoMod, Left),
174 (1, 'm') => (Release, NoMod, Middle),
175 (2, 'm') => (Release, NoMod, Right),
176 (64, 'm') => (Release, NoMod, WheelUp),
177 (65, 'm') => (Release, NoMod, WheelDown),
178
179 (32, _) => (Hold, NoMod, Left),
180 (33, _) => (Hold, NoMod, Middle),
181 (34, _) => (Hold, NoMod, Right),
182
183 (8, 'M') => (Press, Alt, Left),
185 (9, 'M') => (Press, Alt, Middle),
186 (10, 'M') => (Press, Alt, Right),
187 (72, 'M') => (Press, Alt, WheelUp),
188 (73, 'M') => (Press, Alt, WheelDown),
189
190 (8, 'm') => (Release, Alt, Left),
191 (9, 'm') => (Release, Alt, Middle),
192 (10, 'm') => (Release, Alt, Right),
193 (72, 'm') => (Release, Alt, WheelUp),
194 (73, 'm') => (Release, Alt, WheelDown),
195
196 (40, _) => (Hold, Alt, Left),
197 (41, _) => (Hold, Alt, Middle),
198 (42, _) => (Hold, Alt, Right),
199
200 (16, 'M') => (Press, Ctrl, Left),
202 (17, 'M') => (Press, Ctrl, Middle),
203 (18, 'M') => (Press, Ctrl, Right),
204 (80, 'M') => (Press, Ctrl, WheelUp),
205 (81, 'M') => (Press, Ctrl, WheelDown),
206
207 (16, 'm') => (Release, Ctrl, Left),
208 (17, 'm') => (Release, Ctrl, Middle),
209 (18, 'm') => (Release, Ctrl, Right),
210 (80, 'm') => (Release, Ctrl, WheelUp),
211 (81, 'm') => (Release, Ctrl, WheelDown),
212
213 (48, _) => (Hold, Ctrl, Left),
214 (49, _) => (Hold, Ctrl, Middle),
215 (50, _) => (Hold, Ctrl, Right),
216
217 (24, 'M') => (Press, AltCtrl, Left),
219 (25, 'M') => (Press, AltCtrl, Middle),
220 (26, 'M') => (Press, AltCtrl, Right),
221 (88, 'M') => (Press, AltCtrl, WheelUp),
222 (89, 'M') => (Press, AltCtrl, WheelDown),
223
224 (24, 'm') => (Release, AltCtrl, Left),
225 (25, 'm') => (Release, AltCtrl, Middle),
226 (26, 'm') => (Release, AltCtrl, Right),
227 (88, 'm') => (Release, AltCtrl, WheelUp),
228 (89, 'm') => (Release, AltCtrl, WheelDown),
229
230 (56, _) => (Hold, AltCtrl, Left),
231 (57, _) => (Hold, AltCtrl, Middle),
232 (58, _) => (Hold, AltCtrl, Right),
233
234 _ => {
235 trace!("unmapped mouse input: b=b m=m");
236 return None;
237 }
238 };
239
240 Some(MouseEvent { k, m, b, x, y })
241 }
242}