edit/
input.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Parses VT sequences into input events.
5//!
6//! In the future this allows us to take apart the application and
7//! support input schemes that aren't VT, such as UEFI, or GUI.
8
9use std::mem;
10
11use crate::helpers::{CoordType, Point, Size};
12use crate::vt;
13
14/// Represents a key/modifier combination.
15///
16/// TODO: Is this a good idea? I did it to allow typing `kbmod::CTRL | vk::A`.
17/// The reason it's an awkward u32 and not a struct is to hopefully make ABIs easier later.
18/// Of course you could just translate on the ABI boundary, but my hope is that this
19/// design lets me realize some restrictions early on that I can't foresee yet.
20#[repr(transparent)]
21#[derive(Clone, Copy, PartialEq, Eq)]
22pub struct InputKey(u32);
23
24impl InputKey {
25    pub(crate) const fn new(v: u32) -> Self {
26        Self(v)
27    }
28
29    pub(crate) const fn from_ascii(ch: char) -> Option<Self> {
30        if ch == ' ' || (ch >= '0' && ch <= '9') {
31            Some(Self(ch as u32))
32        } else if ch >= 'a' && ch <= 'z' {
33            Some(Self(ch as u32 & !0x20)) // Shift a-z to A-Z
34        } else if ch >= 'A' && ch <= 'Z' {
35            Some(Self(kbmod::SHIFT.0 | ch as u32))
36        } else {
37            None
38        }
39    }
40
41    pub(crate) const fn value(&self) -> u32 {
42        self.0
43    }
44
45    pub(crate) const fn key(&self) -> Self {
46        Self(self.0 & 0x00FFFFFF)
47    }
48
49    pub(crate) const fn modifiers(&self) -> InputKeyMod {
50        InputKeyMod(self.0 & 0xFF000000)
51    }
52
53    pub(crate) const fn modifiers_contains(&self, modifier: InputKeyMod) -> bool {
54        (self.0 & modifier.0) != 0
55    }
56
57    pub(crate) const fn with_modifiers(&self, modifiers: InputKeyMod) -> Self {
58        Self(self.0 | modifiers.0)
59    }
60}
61
62/// A keyboard modifier. Ctrl/Alt/Shift.
63#[repr(transparent)]
64#[derive(Clone, Copy, PartialEq, Eq)]
65pub struct InputKeyMod(u32);
66
67impl InputKeyMod {
68    const fn new(v: u32) -> Self {
69        Self(v)
70    }
71
72    pub(crate) const fn contains(&self, modifier: Self) -> bool {
73        (self.0 & modifier.0) != 0
74    }
75}
76
77impl std::ops::BitOr<InputKeyMod> for InputKey {
78    type Output = Self;
79
80    fn bitor(self, rhs: InputKeyMod) -> Self {
81        Self(self.0 | rhs.0)
82    }
83}
84
85impl std::ops::BitOr<InputKey> for InputKeyMod {
86    type Output = InputKey;
87
88    fn bitor(self, rhs: InputKey) -> InputKey {
89        InputKey(self.0 | rhs.0)
90    }
91}
92
93impl std::ops::BitOrAssign for InputKeyMod {
94    fn bitor_assign(&mut self, rhs: Self) {
95        self.0 |= rhs.0;
96    }
97}
98
99/// Keyboard keys.
100///
101/// The codes defined here match the VK_* constants on Windows.
102/// It's a convenient way to handle keyboard input, even on other platforms.
103pub mod vk {
104    use super::InputKey;
105
106    pub const NULL: InputKey = InputKey::new('\0' as u32);
107    pub const BACK: InputKey = InputKey::new(0x08);
108    pub const TAB: InputKey = InputKey::new('\t' as u32);
109    pub const RETURN: InputKey = InputKey::new('\r' as u32);
110    pub const ESCAPE: InputKey = InputKey::new(0x1B);
111    pub const SPACE: InputKey = InputKey::new(' ' as u32);
112    pub const PRIOR: InputKey = InputKey::new(0x21);
113    pub const NEXT: InputKey = InputKey::new(0x22);
114
115    pub const END: InputKey = InputKey::new(0x23);
116    pub const HOME: InputKey = InputKey::new(0x24);
117
118    pub const LEFT: InputKey = InputKey::new(0x25);
119    pub const UP: InputKey = InputKey::new(0x26);
120    pub const RIGHT: InputKey = InputKey::new(0x27);
121    pub const DOWN: InputKey = InputKey::new(0x28);
122
123    pub const INSERT: InputKey = InputKey::new(0x2D);
124    pub const DELETE: InputKey = InputKey::new(0x2E);
125
126    pub const N0: InputKey = InputKey::new('0' as u32);
127    pub const N1: InputKey = InputKey::new('1' as u32);
128    pub const N2: InputKey = InputKey::new('2' as u32);
129    pub const N3: InputKey = InputKey::new('3' as u32);
130    pub const N4: InputKey = InputKey::new('4' as u32);
131    pub const N5: InputKey = InputKey::new('5' as u32);
132    pub const N6: InputKey = InputKey::new('6' as u32);
133    pub const N7: InputKey = InputKey::new('7' as u32);
134    pub const N8: InputKey = InputKey::new('8' as u32);
135    pub const N9: InputKey = InputKey::new('9' as u32);
136
137    pub const A: InputKey = InputKey::new('A' as u32);
138    pub const B: InputKey = InputKey::new('B' as u32);
139    pub const C: InputKey = InputKey::new('C' as u32);
140    pub const D: InputKey = InputKey::new('D' as u32);
141    pub const E: InputKey = InputKey::new('E' as u32);
142    pub const F: InputKey = InputKey::new('F' as u32);
143    pub const G: InputKey = InputKey::new('G' as u32);
144    pub const H: InputKey = InputKey::new('H' as u32);
145    pub const I: InputKey = InputKey::new('I' as u32);
146    pub const J: InputKey = InputKey::new('J' as u32);
147    pub const K: InputKey = InputKey::new('K' as u32);
148    pub const L: InputKey = InputKey::new('L' as u32);
149    pub const M: InputKey = InputKey::new('M' as u32);
150    pub const N: InputKey = InputKey::new('N' as u32);
151    pub const O: InputKey = InputKey::new('O' as u32);
152    pub const P: InputKey = InputKey::new('P' as u32);
153    pub const Q: InputKey = InputKey::new('Q' as u32);
154    pub const R: InputKey = InputKey::new('R' as u32);
155    pub const S: InputKey = InputKey::new('S' as u32);
156    pub const T: InputKey = InputKey::new('T' as u32);
157    pub const U: InputKey = InputKey::new('U' as u32);
158    pub const V: InputKey = InputKey::new('V' as u32);
159    pub const W: InputKey = InputKey::new('W' as u32);
160    pub const X: InputKey = InputKey::new('X' as u32);
161    pub const Y: InputKey = InputKey::new('Y' as u32);
162    pub const Z: InputKey = InputKey::new('Z' as u32);
163
164    pub const NUMPAD0: InputKey = InputKey::new(0x60);
165    pub const NUMPAD1: InputKey = InputKey::new(0x61);
166    pub const NUMPAD2: InputKey = InputKey::new(0x62);
167    pub const NUMPAD3: InputKey = InputKey::new(0x63);
168    pub const NUMPAD4: InputKey = InputKey::new(0x64);
169    pub const NUMPAD5: InputKey = InputKey::new(0x65);
170    pub const NUMPAD6: InputKey = InputKey::new(0x66);
171    pub const NUMPAD7: InputKey = InputKey::new(0x67);
172    pub const NUMPAD8: InputKey = InputKey::new(0x68);
173    pub const NUMPAD9: InputKey = InputKey::new(0x69);
174    pub const MULTIPLY: InputKey = InputKey::new(0x6A);
175    pub const ADD: InputKey = InputKey::new(0x6B);
176    pub const SEPARATOR: InputKey = InputKey::new(0x6C);
177    pub const SUBTRACT: InputKey = InputKey::new(0x6D);
178    pub const DECIMAL: InputKey = InputKey::new(0x6E);
179    pub const DIVIDE: InputKey = InputKey::new(0x6F);
180
181    pub const F1: InputKey = InputKey::new(0x70);
182    pub const F2: InputKey = InputKey::new(0x71);
183    pub const F3: InputKey = InputKey::new(0x72);
184    pub const F4: InputKey = InputKey::new(0x73);
185    pub const F5: InputKey = InputKey::new(0x74);
186    pub const F6: InputKey = InputKey::new(0x75);
187    pub const F7: InputKey = InputKey::new(0x76);
188    pub const F8: InputKey = InputKey::new(0x77);
189    pub const F9: InputKey = InputKey::new(0x78);
190    pub const F10: InputKey = InputKey::new(0x79);
191    pub const F11: InputKey = InputKey::new(0x7A);
192    pub const F12: InputKey = InputKey::new(0x7B);
193    pub const F13: InputKey = InputKey::new(0x7C);
194    pub const F14: InputKey = InputKey::new(0x7D);
195    pub const F15: InputKey = InputKey::new(0x7E);
196    pub const F16: InputKey = InputKey::new(0x7F);
197    pub const F17: InputKey = InputKey::new(0x80);
198    pub const F18: InputKey = InputKey::new(0x81);
199    pub const F19: InputKey = InputKey::new(0x82);
200    pub const F20: InputKey = InputKey::new(0x83);
201    pub const F21: InputKey = InputKey::new(0x84);
202    pub const F22: InputKey = InputKey::new(0x85);
203    pub const F23: InputKey = InputKey::new(0x86);
204    pub const F24: InputKey = InputKey::new(0x87);
205}
206
207/// Keyboard modifiers.
208pub mod kbmod {
209    use super::InputKeyMod;
210
211    pub const NONE: InputKeyMod = InputKeyMod::new(0x00000000);
212    pub const CTRL: InputKeyMod = InputKeyMod::new(0x01000000);
213    pub const ALT: InputKeyMod = InputKeyMod::new(0x02000000);
214    pub const SHIFT: InputKeyMod = InputKeyMod::new(0x04000000);
215
216    pub const CTRL_ALT: InputKeyMod = InputKeyMod::new(0x03000000);
217    pub const CTRL_SHIFT: InputKeyMod = InputKeyMod::new(0x05000000);
218    pub const ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x06000000);
219    pub const CTRL_ALT_SHIFT: InputKeyMod = InputKeyMod::new(0x07000000);
220}
221
222/// Mouse input state. Up/Down, Left/Right, etc.
223#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
224pub enum InputMouseState {
225    #[default]
226    None,
227
228    // These 3 carry their state between frames.
229    Left,
230    Middle,
231    Right,
232
233    // These 2 get reset to None on the next frame.
234    Release,
235    Scroll,
236}
237
238/// Mouse input.
239#[derive(Clone, Copy)]
240pub struct InputMouse {
241    /// The state of the mouse.Up/Down, Left/Right, etc.
242    pub state: InputMouseState,
243    /// Any keyboard modifiers that are held down.
244    pub modifiers: InputKeyMod,
245    /// Position of the mouse in the viewport.
246    pub position: Point,
247    /// Scroll delta.
248    pub scroll: Point,
249}
250
251/// Primary result type of the parser.
252pub enum Input<'input> {
253    /// Window resize event.
254    Resize(Size),
255    /// Text input.
256    /// Note that [`Input::Keyboard`] events can also be text.
257    Text(&'input str),
258    /// A clipboard paste.
259    Paste(Vec<u8>),
260    /// Keyboard input.
261    Keyboard(InputKey),
262    /// Mouse input.
263    Mouse(InputMouse),
264}
265
266/// Parses VT sequences into input events.
267pub struct Parser {
268    bracketed_paste: bool,
269    bracketed_paste_buf: Vec<u8>,
270    x10_mouse_want: bool,
271    x10_mouse_buf: [u8; 3],
272    x10_mouse_len: usize,
273}
274
275impl Parser {
276    /// Creates a new parser that turns VT sequences into input events.
277    ///
278    /// Keep the instance alive for the lifetime of the input stream.
279    pub fn new() -> Self {
280        Self {
281            bracketed_paste: false,
282            bracketed_paste_buf: Vec::new(),
283            x10_mouse_want: false,
284            x10_mouse_buf: [0; 3],
285            x10_mouse_len: 0,
286        }
287    }
288
289    /// Takes an [`vt::Stream`] and returns a [`Stream`]
290    /// that turns VT sequences into input events.
291    pub fn parse<'parser, 'vt, 'input>(
292        &'parser mut self,
293        stream: vt::Stream<'vt, 'input>,
294    ) -> Stream<'parser, 'vt, 'input> {
295        Stream { parser: self, stream }
296    }
297}
298
299/// An iterator that parses VT sequences into input events.
300pub struct Stream<'parser, 'vt, 'input> {
301    parser: &'parser mut Parser,
302    stream: vt::Stream<'vt, 'input>,
303}
304
305impl<'input> Iterator for Stream<'_, '_, 'input> {
306    type Item = Input<'input>;
307
308    fn next(&mut self) -> Option<Input<'input>> {
309        loop {
310            if self.parser.bracketed_paste {
311                return self.handle_bracketed_paste();
312            }
313
314            if self.parser.x10_mouse_want {
315                return self.parse_x10_mouse_coordinates();
316            }
317
318            const KEYPAD_LUT: [u8; 8] = [
319                vk::UP.value() as u8,    // A
320                vk::DOWN.value() as u8,  // B
321                vk::RIGHT.value() as u8, // C
322                vk::LEFT.value() as u8,  // D
323                0,                       // E
324                vk::END.value() as u8,   // F
325                0,                       // G
326                vk::HOME.value() as u8,  // H
327            ];
328
329            match self.stream.next()? {
330                vt::Token::Text(text) => {
331                    return Some(Input::Text(text));
332                }
333                vt::Token::Ctrl(ch) => match ch {
334                    '\0' | '\t' | '\r' => return Some(Input::Keyboard(InputKey::new(ch as u32))),
335                    '\n' => return Some(Input::Keyboard(kbmod::CTRL | vk::RETURN)),
336                    ..='\x1a' => {
337                        // Shift control code to A-Z
338                        let key = ch as u32 | 0x40;
339                        return Some(Input::Keyboard(kbmod::CTRL | InputKey::new(key)));
340                    }
341                    '\x7f' => return Some(Input::Keyboard(vk::BACK)),
342                    _ => {}
343                },
344                vt::Token::Esc(ch) => {
345                    match ch {
346                        '\0' => return Some(Input::Keyboard(vk::ESCAPE)),
347                        '\n' => return Some(Input::Keyboard(kbmod::CTRL_ALT | vk::RETURN)),
348                        ' '..='~' => {
349                            let ch = ch as u32;
350                            let key = ch & !0x20; // Shift a-z to A-Z
351                            let modifiers =
352                                if (ch & 0x20) != 0 { kbmod::ALT } else { kbmod::ALT_SHIFT };
353                            return Some(Input::Keyboard(modifiers | InputKey::new(key)));
354                        }
355                        _ => {}
356                    }
357                }
358                vt::Token::SS3(ch) => match ch {
359                    'A'..='H' => {
360                        let vk = KEYPAD_LUT[ch as usize - 'A' as usize];
361                        if vk != 0 {
362                            return Some(Input::Keyboard(InputKey::new(vk as u32)));
363                        }
364                    }
365                    'P'..='S' => {
366                        let key = vk::F1.value() + ch as u32 - 'P' as u32;
367                        return Some(Input::Keyboard(InputKey::new(key)));
368                    }
369                    _ => {}
370                },
371                vt::Token::Csi(csi) => {
372                    match csi.final_byte {
373                        'A'..='H' => {
374                            let vk = KEYPAD_LUT[csi.final_byte as usize - 'A' as usize];
375                            if vk != 0 {
376                                return Some(Input::Keyboard(
377                                    InputKey::new(vk as u32) | Self::parse_modifiers(csi),
378                                ));
379                            }
380                        }
381                        'Z' => return Some(Input::Keyboard(kbmod::SHIFT | vk::TAB)),
382                        '~' => {
383                            const LUT: [u8; 35] = [
384                                0,
385                                vk::HOME.value() as u8,   // 1
386                                vk::INSERT.value() as u8, // 2
387                                vk::DELETE.value() as u8, // 3
388                                vk::END.value() as u8,    // 4
389                                vk::PRIOR.value() as u8,  // 5
390                                vk::NEXT.value() as u8,   // 6
391                                0,
392                                0,
393                                0,
394                                0,
395                                0,
396                                0,
397                                0,
398                                0,
399                                vk::F5.value() as u8, // 15
400                                0,
401                                vk::F6.value() as u8,  // 17
402                                vk::F7.value() as u8,  // 18
403                                vk::F8.value() as u8,  // 19
404                                vk::F9.value() as u8,  // 20
405                                vk::F10.value() as u8, // 21
406                                0,
407                                vk::F11.value() as u8, // 23
408                                vk::F12.value() as u8, // 24
409                                vk::F13.value() as u8, // 25
410                                vk::F14.value() as u8, // 26
411                                0,
412                                vk::F15.value() as u8, // 28
413                                vk::F16.value() as u8, // 29
414                                0,
415                                vk::F17.value() as u8, // 31
416                                vk::F18.value() as u8, // 32
417                                vk::F19.value() as u8, // 33
418                                vk::F20.value() as u8, // 34
419                            ];
420                            const LUT_LEN: u16 = LUT.len() as u16;
421
422                            match csi.params[0] {
423                                0..LUT_LEN => {
424                                    let vk = LUT[csi.params[0] as usize];
425                                    if vk != 0 {
426                                        return Some(Input::Keyboard(
427                                            InputKey::new(vk as u32) | Self::parse_modifiers(csi),
428                                        ));
429                                    }
430                                }
431                                200 => self.parser.bracketed_paste = true,
432                                _ => {}
433                            }
434                        }
435                        'm' | 'M' if csi.private_byte == '<' => {
436                            let btn = csi.params[0];
437                            let mut mouse = InputMouse {
438                                state: InputMouseState::None,
439                                modifiers: kbmod::NONE,
440                                position: Default::default(),
441                                scroll: Default::default(),
442                            };
443
444                            mouse.state = InputMouseState::None;
445                            if (btn & 0x40) != 0 {
446                                mouse.state = InputMouseState::Scroll;
447                                mouse.scroll.y += if (btn & 0x01) != 0 { 3 } else { -3 };
448                            } else if csi.final_byte == 'M' {
449                                const STATES: [InputMouseState; 4] = [
450                                    InputMouseState::Left,
451                                    InputMouseState::Middle,
452                                    InputMouseState::Right,
453                                    InputMouseState::None,
454                                ];
455                                mouse.state = STATES[(btn as usize) & 0x03];
456                            }
457
458                            mouse.modifiers = kbmod::NONE;
459                            mouse.modifiers |=
460                                if (btn & 0x04) != 0 { kbmod::SHIFT } else { kbmod::NONE };
461                            mouse.modifiers |=
462                                if (btn & 0x08) != 0 { kbmod::ALT } else { kbmod::NONE };
463                            mouse.modifiers |=
464                                if (btn & 0x10f) != 0 { kbmod::CTRL } else { kbmod::NONE };
465
466                            mouse.position.x = csi.params[1] as CoordType - 1;
467                            mouse.position.y = csi.params[2] as CoordType - 1;
468                            return Some(Input::Mouse(mouse));
469                        }
470                        'M' if csi.param_count == 0 => {
471                            self.parser.x10_mouse_want = true;
472                        }
473                        't' if csi.params[0] == 8 => {
474                            // Window Size
475                            let width = (csi.params[2] as CoordType).clamp(1, 32767);
476                            let height = (csi.params[1] as CoordType).clamp(1, 32767);
477                            return Some(Input::Resize(Size { width, height }));
478                        }
479                        _ => {}
480                    }
481                }
482                _ => {}
483            }
484        }
485    }
486}
487
488impl<'input> Stream<'_, '_, 'input> {
489    /// Once we encounter the start of a bracketed paste
490    /// we seek to the end of the paste in this function.
491    ///
492    /// A bracketed paste is basically:
493    /// ```text
494    /// <ESC>[201~    lots of text    <ESC>[201~
495    /// ```
496    ///
497    /// That in between text is then expected to be taken literally.
498    /// It can be in between anything though, including other escape sequences.
499    /// This is the reason why this is a separate method.
500    #[cold]
501    fn handle_bracketed_paste(&mut self) -> Option<Input<'input>> {
502        let beg = self.stream.offset();
503        let mut end = beg;
504
505        while let Some(token) = self.stream.next() {
506            if let vt::Token::Csi(csi) = token
507                && csi.final_byte == '~'
508                && csi.params[0] == 201
509            {
510                self.parser.bracketed_paste = false;
511                break;
512            }
513            end = self.stream.offset();
514        }
515
516        if end != beg {
517            self.parser
518                .bracketed_paste_buf
519                .extend_from_slice(&self.stream.input().as_bytes()[beg..end]);
520        }
521
522        if !self.parser.bracketed_paste {
523            Some(Input::Paste(mem::take(&mut self.parser.bracketed_paste_buf)))
524        } else {
525            None
526        }
527    }
528
529    /// Implements the X10 mouse protocol via `CSI M CbCxCy`.
530    ///
531    /// You want to send numeric mouse coordinates.
532    /// You have CSI sequences with numeric parameters.
533    /// So, of course you put the coordinates as shifted ASCII characters after
534    /// the end of the sequence. Limited coordinate range and complicated parsing!
535    /// This is so puzzling to me. The existence of this function makes me unhappy.
536    #[cold]
537    fn parse_x10_mouse_coordinates(&mut self) -> Option<Input<'input>> {
538        self.parser.x10_mouse_len +=
539            self.stream.read(&mut self.parser.x10_mouse_buf[self.parser.x10_mouse_len..]);
540        if self.parser.x10_mouse_len < 3 {
541            return None;
542        }
543
544        let button = self.parser.x10_mouse_buf[0] & 0b11;
545        let modifier = self.parser.x10_mouse_buf[0] & 0b11100;
546        let x = self.parser.x10_mouse_buf[1] as CoordType - 0x21;
547        let y = self.parser.x10_mouse_buf[2] as CoordType - 0x21;
548        let action = match button {
549            0 => InputMouseState::Left,
550            1 => InputMouseState::Middle,
551            2 => InputMouseState::Right,
552            _ => InputMouseState::None,
553        };
554        let modifiers = match modifier {
555            4 => kbmod::SHIFT,
556            8 => kbmod::ALT,
557            16 => kbmod::CTRL,
558            _ => kbmod::NONE,
559        };
560
561        self.parser.x10_mouse_want = false;
562        self.parser.x10_mouse_len = 0;
563
564        Some(Input::Mouse(InputMouse {
565            state: action,
566            modifiers,
567            position: Point { x, y },
568            scroll: Default::default(),
569        }))
570    }
571
572    fn parse_modifiers(csi: &vt::Csi) -> InputKeyMod {
573        let mut modifiers = kbmod::NONE;
574        let p1 = csi.params[1].saturating_sub(1);
575        if (p1 & 0x01) != 0 {
576            modifiers |= kbmod::SHIFT;
577        }
578        if (p1 & 0x02) != 0 {
579            modifiers |= kbmod::ALT;
580        }
581        if (p1 & 0x04) != 0 {
582            modifiers |= kbmod::CTRL;
583        }
584        modifiers
585    }
586}