ad_editor/
key.rs

1//! Keypresses and related user interactions.
2use 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// using 'showkey -a' to view keycodes is useful for adding to this
26#[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:?}>"), // TODO: actual impl
72        }
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            // No modifiers
167            (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            // Alt modifier
184            (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            // Ctrl modifier
201            (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            // Alt+Ctrl modifiers
218            (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}