Skip to main content

vt_input_push_parser/
lib.rs

1use vt_push_parser::event::{CSI, SS2, SS3, VTEvent};
2use vt_push_parser::{VTPushParser, capture};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum PasteEvent {
6    Start,
7    End,
8    Continue,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum MouseButton {
13    Left,
14    Middle,
15    Right,
16    WheelUp,
17    WheelDown,
18    WheelLeft,
19    WheelRight,
20    Extra(u8),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum MouseEventKind {
25    Press(MouseButton),
26    Release(MouseButton),
27    Drag(MouseButton),
28    Motion,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct MouseEvent {
33    pub kind: MouseEventKind,
34    pub x: u16,
35    pub y: u16,
36    pub modifiers: Modifier,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum InputEvent<'a> {
41    /// A named key on the keyboard
42    Key(keys::Key, Modifier),
43    /// An unnamed key representable by a single character, but with modifiers
44    /// that are more than just "shift".
45    KeyChar(char, Modifier),
46    /// A single text-producing character.
47    Char(char),
48    /// A mouse event.
49    Mouse(MouseEvent),
50    /// A report from the terminal.
51    // Report(InputReport),
52    /// A bracketed paste event.
53    Paste(PasteEvent, &'a [u8]),
54    /// A raw VT CSI event.
55    Csi(CSI<'a>),
56    /// A raw VT OSC event.
57    Osc(&'a [u8]),
58    /// A raw VT DCS event.
59    Dcs(&'a [u8]),
60    /// A raw VT SS2 event.
61    Ss2(SS2),
62    /// A raw VT SS3 event.
63    Ss3(SS3),
64}
65
66/// Common input reports.
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub enum InputReport {
69    // CSI Ps1 ; Ps2 R
70    CursorPosition(u16, u16),
71    // CSI Pl; Pc; Pp R (DECXCPR)
72    CursorPositionPage(u16, u16, u16),
73    // CSI Pn ; n or CSI ? Pn ; n
74    DeviceStatus(bool, u16),
75    // CSI ? Pn1 ; Pn2 ; n or CSI ? Pn1 ; Pn2 ; n
76    DeviceStatus2(bool, u16, u16),
77    // CSI Ps1; ... Psn (if Ps! is 61-64, identifies VT level, others are bitflags)
78    DeviceAttributes1(u8, u32),
79    // CSI > Pp ; Pv ; Pc c
80    DeviceAttributes2(u16, u16, u8),
81    // CSI Ps1 ; Ps2 $ y or CSI ? Ps1 ; Ps2 $ y
82    ReportMode(bool, u16, u8),
83    // CSI 1 t or CSI 2 t
84    ReportWindowState(bool),
85    // CSI 3 ; x ; y t
86    ReportWindowPosition(u16, u16),
87    // CSI 4 ; h ; w t
88    ReportWindowSizePixel(u16, u16),
89    // CSI 5 ; h ; w t
90    ReportTextSizeCharacter(u16, u16),
91    // CSI 6 ; h ; w t
92    ReportScreenSizeCharacter(u16, u16),
93    // OSC Ps1 ; rgb:... BEL
94    ReportDynamicColor(u8, u16, u16, u16),
95    // ESC I
96    FocusIn,
97    // ESC O
98    FocusOut,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102#[repr(transparent)]
103pub struct Modifier(pub(crate) u8);
104
105bitflags::bitflags! {
106    impl Modifier: u8 {
107        const SHIFT = 1 << 0;
108        const ALT = 1 << 1;
109        const CTRL = 1 << 2;
110
111        const KEYPAD = 1 << 3;
112        const SUN = 1 << 4;
113        const VT52 = 1 << 5;
114    }
115}
116
117mod keys {
118    use super::Modifier;
119
120    macro_rules! e {
121        ($depth:literal, $c:literal, ($($mod:ident),*)) => {
122            MatchResult::Match {
123                length: $depth,
124                what: pack_mod($c as _, (Modifier::empty() $( | Modifier::$mod )*).0),
125            }
126        };
127        ($depth:literal, $name:ident, ($($mod:ident),*)) => {
128            MatchResult::Match {
129                length: $depth,
130                what: pack_mod(Key::$name as _, 0x80 | (Modifier::empty() $( | Modifier::$mod )*).0),
131            }
132        };
133    }
134
135    // Byte-packed to 8 bytes
136    #[derive(Debug, Clone, PartialEq, Eq)]
137    pub(crate) enum MatchResult {
138        /// We didn't find a match and we know that at least this many bytes
139        /// won't match.
140        NoMatch {
141            length: u8,
142        },
143        PendingMatch,
144        Match {
145            length: u8,
146            what: u32,
147        },
148    }
149
150    #[inline(always)]
151    const fn pack(char: char, alt: bool) -> u32 {
152        char as u32 | (((if alt { Modifier::ALT.0 as _ } else { 0 }) as u32) << 24)
153    }
154
155    #[inline(always)]
156    const fn pack_mod(char: u32, modifiers: u8) -> u32 {
157        char | (modifiers as u32) << 24
158    }
159
160    #[inline(always)]
161    fn utf8_1(alt: bool, byte: u8) -> MatchResult {
162        MatchResult::Match {
163            length: 1 + alt as u8,
164            what: pack(byte as char, alt),
165        }
166    }
167
168    #[inline(always)]
169    fn utf8_2(alt: bool, bytes: &[u8]) -> MatchResult {
170        if bytes.len() < 2 {
171            return MatchResult::PendingMatch;
172        }
173        // 110xxxxx 10xxxxxx
174        let Some(char) =
175            char::from_u32((bytes[0] & 0b11111) as u32 | ((bytes[1] & 0b111111) as u32) << 6)
176        else {
177            return MatchResult::Match {
178                length: 2 + alt as u8,
179                what: pack('\u{FFFD}', false),
180            };
181        };
182        MatchResult::Match {
183            length: 2 + alt as u8,
184            what: pack(char, alt),
185        }
186    }
187
188    #[inline(always)]
189    fn utf8_3(alt: bool, bytes: &[u8]) -> MatchResult {
190        if bytes.len() < 3 {
191            return MatchResult::PendingMatch;
192        }
193        // 1110xxxx 10xxxxxx 10xxxxxx
194        let Some(char) = char::from_u32(
195            (((bytes[0] & 0b1111) as u32) << 12)
196                | (((bytes[1] & 0b111111) as u32) << 6)
197                | ((bytes[2] & 0b111111) as u32),
198        ) else {
199            return MatchResult::Match {
200                length: 3 + alt as u8,
201                what: pack('\u{FFFD}', false),
202            };
203        };
204        MatchResult::Match {
205            length: 3 + alt as u8,
206            what: pack(char, alt),
207        }
208    }
209
210    #[inline(always)]
211    fn utf8_4(alt: bool, bytes: &[u8]) -> MatchResult {
212        if bytes.len() < 4 {
213            return MatchResult::PendingMatch;
214        }
215        // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
216        let Some(char) = char::from_u32(
217            (((bytes[0] & 0b111) as u32) << 18)
218                | (((bytes[1] & 0b111111) as u32) << 12)
219                | (((bytes[2] & 0b111111) as u32) << 6)
220                | ((bytes[3] & 0b111111) as u32),
221        ) else {
222            return MatchResult::Match {
223                length: 4 + alt as u8,
224                what: pack('\u{FFFD}', false),
225            };
226        };
227        MatchResult::Match {
228            length: 4 + alt as u8,
229            what: pack(char, alt),
230        }
231    }
232
233    #[inline(always)]
234    fn invalid_utf8(_: u8) -> MatchResult {
235        MatchResult::Match {
236            length: 1,
237            what: pack('\u{FFFD}', false),
238        }
239    }
240
241    pub fn find_sequence(bytes: &[u8]) -> MatchResult {
242        match find_sequence_nonidle_gen(bytes) {
243            MatchResult::NoMatch { length } => {
244                if matches!(bytes, [0x1b, 0x1b, ..]) {
245                    MatchResult::Match {
246                        length: 1,
247                        what: pack('\x1b', false),
248                    }
249                } else {
250                    MatchResult::NoMatch { length }
251                }
252            }
253            res => res,
254        }
255    }
256
257    #[allow(unused)]
258    pub fn find_sequence_idle(bytes: &[u8]) -> MatchResult {
259        match find_sequence_idle_gen(bytes) {
260            MatchResult::NoMatch { length } => match bytes {
261                [0x1b] => MatchResult::Match {
262                    length: 1,
263                    what: pack('\x1b', false),
264                },
265                [0x1b, 0x1b] => MatchResult::Match {
266                    length: 2,
267                    what: pack('\x1b', true),
268                },
269                [0x1b, 0x1b, ..] => MatchResult::Match {
270                    length: 1,
271                    what: pack('\x1b', false),
272                },
273                _ => MatchResult::NoMatch { length },
274            },
275            res => res,
276        }
277    }
278
279    include!(concat!(env!("OUT_DIR"), "/keys.rs"));
280}
281
282#[derive(Debug, Default)]
283enum CaptureState {
284    #[default]
285    None,
286    #[allow(dead_code)]
287    Paste,
288    Mouse,
289}
290
291/// Decodes the mouse button byte used by X10, SGR, and urxvt protocols.
292///
293/// Returns (button, modifiers, is_motion).
294fn decode_mouse_button_byte(cb: u16) -> (MouseButton, Modifier, bool) {
295    let modifiers = Modifier::from_bits_truncate(
296        (if cb & 0x04 != 0 { Modifier::SHIFT.0 } else { 0 })
297            | (if cb & 0x08 != 0 { Modifier::ALT.0 } else { 0 })
298            | (if cb & 0x10 != 0 { Modifier::CTRL.0 } else { 0 }),
299    );
300    let is_motion = cb & 0x20 != 0;
301    let button_bits = cb & 0xC3; // bits 0-1 and 6-7
302    let button = match button_bits {
303        0 => MouseButton::Left,
304        1 => MouseButton::Middle,
305        2 => MouseButton::Right,
306        64 => MouseButton::WheelUp,
307        65 => MouseButton::WheelDown,
308        66 => MouseButton::WheelLeft,
309        67 => MouseButton::WheelRight,
310        n => MouseButton::Extra(n as u8),
311    };
312    (button, modifiers, is_motion)
313}
314
315enum HandleAction<'a> {
316    Handled,
317    Capture(CaptureState, capture::VTCaptureInternal),
318    Unhandled(VTEvent<'a>),
319}
320
321/// A push parser for the VT/xterm input protocol.
322///
323/// This parser enables parsing of key sequences alongside the normal VT/xterm
324/// protocol. In addition, most common input control sequences are automatically
325/// decoded.
326///
327/// Unrecognized sequences are emitted as raw [`VTEvent`]s.
328pub struct VTPushParserInput {
329    key_buffer: [u8; keys::MAX_SEQUENCE_LEN],
330    key_buffer_len: usize,
331    data_accumulator: Vec<u8>,
332
333    capture: capture::VTCaptureInternal,
334    capture_state: CaptureState,
335    parser: VTPushParser,
336}
337
338fn handle_vt_event<'a>(event: VTEvent<'a>, cb: &mut impl FnMut(InputEvent)) -> HandleAction<'a> {
339    if let VTEvent::Csi(ref csi) = event {
340        match (csi.private, csi.final_byte) {
341            // SGR mouse press/drag/motion: CSI < Pb ; Px ; Py M
342            (Some(b'<'), b'M') => {
343                if let (Some(pb), Some(px), Some(py)) = (
344                    csi.params.try_parse::<u16>(0),
345                    csi.params.try_parse::<u16>(1),
346                    csi.params.try_parse::<u16>(2),
347                ) {
348                    let (button, modifiers, is_motion) = decode_mouse_button_byte(pb);
349                    let kind = if is_motion {
350                        MouseEventKind::Drag(button)
351                    } else {
352                        MouseEventKind::Press(button)
353                    };
354                    cb(InputEvent::Mouse(MouseEvent {
355                        kind,
356                        x: px.saturating_sub(1),
357                        y: py.saturating_sub(1),
358                        modifiers,
359                    }));
360                    return HandleAction::Handled;
361                }
362            }
363            // SGR mouse release: CSI < Pb ; Px ; Py m
364            (Some(b'<'), b'm') => {
365                if let (Some(pb), Some(px), Some(py)) = (
366                    csi.params.try_parse::<u16>(0),
367                    csi.params.try_parse::<u16>(1),
368                    csi.params.try_parse::<u16>(2),
369                ) {
370                    let (button, modifiers, _) = decode_mouse_button_byte(pb);
371                    cb(InputEvent::Mouse(MouseEvent {
372                        kind: MouseEventKind::Release(button),
373                        x: px.saturating_sub(1),
374                        y: py.saturating_sub(1),
375                        modifiers,
376                    }));
377                    return HandleAction::Handled;
378                }
379            }
380            // X10 or urxvt mouse: CSI [Pb ; Px ; Py] M
381            (None, b'M') => {
382                if csi.params.is_empty() {
383                    // X10 mouse: CSI M followed by 3 raw bytes
384                    return HandleAction::Capture(
385                        CaptureState::Mouse,
386                        capture::VTCaptureInternal::Count(3),
387                    );
388                } else if let (Some(pb), Some(px), Some(py)) = (
389                    csi.params.try_parse::<u16>(0),
390                    csi.params.try_parse::<u16>(1),
391                    csi.params.try_parse::<u16>(2),
392                ) {
393                    // urxvt mouse: CSI Pb ; Px ; Py M
394                    let (button, modifiers, is_motion) = decode_mouse_button_byte(pb);
395                    let kind = if is_motion {
396                        MouseEventKind::Drag(button)
397                    } else {
398                        MouseEventKind::Press(button)
399                    };
400                    cb(InputEvent::Mouse(MouseEvent {
401                        kind,
402                        x: px.saturating_sub(1),
403                        y: py.saturating_sub(1),
404                        modifiers,
405                    }));
406                    return HandleAction::Handled;
407                }
408            }
409            (None, b'u') => match csi.params.len() {
410                1 => {}
411                2 => {}
412                _ => {}
413            },
414            (None | Some(b'>'), b'~') => match csi.params.len() {
415                2 => {}
416                3 => {}
417                _ => {}
418            },
419            _ => {}
420        }
421    }
422
423    HandleAction::Unhandled(event)
424}
425
426fn handle_key_event(what: u32, mut cb: impl FnMut(InputEvent)) {
427    if what & 0x80000000 != 0 {
428        let key = keys::Key::try_from(what as u8).unwrap();
429        cb(InputEvent::Key(key, Modifier(((what >> 24) & !0x80) as u8)));
430    } else {
431        cb(InputEvent::KeyChar(
432            char::from_u32(what & 0xffffff).unwrap(),
433            Modifier((what >> 24) as u8),
434        ));
435    }
436}
437
438impl Default for VTPushParserInput {
439    fn default() -> Self {
440        Self::new()
441    }
442}
443
444impl VTPushParserInput {
445    pub fn new() -> Self {
446        Self {
447            key_buffer: [0; keys::MAX_SEQUENCE_LEN],
448            key_buffer_len: 0,
449            capture: capture::VTCaptureInternal::None,
450            capture_state: CaptureState::None,
451            parser: VTPushParser::new(),
452            data_accumulator: Vec::with_capacity(256),
453        }
454    }
455
456    pub fn feed_with(&mut self, mut bytes: &[u8], mut cb: impl FnMut(InputEvent)) {
457        // Handle the result of handle_vt_event, setting up capture or emitting
458        // unhandled events as raw CSI.
459        macro_rules! dispatch_vt {
460            ($action:expr, $cb:expr, $capture:expr, $capture_state:expr) => {
461                match $action {
462                    HandleAction::Handled => {}
463                    HandleAction::Capture(state, internal) => {
464                        *$capture = internal;
465                        *$capture_state = state;
466                    }
467                    HandleAction::Unhandled(event) => {
468                        let input_event = match event {
469                            VTEvent::Csi(csi) => Some(InputEvent::Csi(csi)),
470                            VTEvent::OscEnd { data, .. } => Some(InputEvent::Osc(data)),
471                            VTEvent::DcsEnd(data) => Some(InputEvent::Dcs(data)),
472                            VTEvent::Ss2(ss2) => Some(InputEvent::Ss2(ss2)),
473                            VTEvent::Ss3(ss3) => Some(InputEvent::Ss3(ss3)),
474                            _ => None,
475                        };
476                        if let Some(e) = input_event {
477                            $cb(e);
478                        }
479                    }
480                }
481            };
482        }
483
484        loop {
485            // First, check if we have an active capture.
486            if let Some(captured) = self.capture.feed(&mut bytes) {
487                self.data_accumulator.extend_from_slice(captured);
488                match std::mem::take(&mut self.capture_state) {
489                    CaptureState::None => {}
490                    CaptureState::Paste => {
491                        cb(InputEvent::Paste(PasteEvent::End, &self.data_accumulator));
492                        self.data_accumulator.clear();
493                    }
494                    CaptureState::Mouse => {
495                        let data = &self.data_accumulator;
496                        if data.len() >= 3 {
497                            // X10 protocol: all three bytes are offset by 32
498                            let cb_byte = (data[0] as u16).saturating_sub(32);
499                            let x = (data[1] as u16).saturating_sub(33);
500                            let y = (data[2] as u16).saturating_sub(33);
501                            // In X10 protocol, button byte 3 (bits 0-1) means release
502                            let is_release = cb_byte & 0x03 == 3;
503                            let (button, modifiers, is_motion) = decode_mouse_button_byte(cb_byte);
504                            let kind = if is_release {
505                                // X10 doesn't tell us which button was released
506                                MouseEventKind::Release(MouseButton::Left)
507                            } else if is_motion {
508                                MouseEventKind::Drag(button)
509                            } else {
510                                MouseEventKind::Press(button)
511                            };
512                            cb(InputEvent::Mouse(MouseEvent {
513                                kind,
514                                x,
515                                y,
516                                modifiers,
517                            }));
518                        }
519                        self.data_accumulator.clear();
520                    }
521                }
522                continue;
523            }
524
525            // If no active capture, feed the parser if it's not in ground state, otherwise the key buffer.
526            if !self.parser.is_ground() {
527                let capture = &mut self.capture;
528                let capture_state = &mut self.capture_state;
529                let read = self.parser.feed_with_abortable(bytes, |event: VTEvent| {
530                    let action = handle_vt_event(event, &mut cb);
531                    dispatch_vt!(action, cb, capture, capture_state);
532                    false
533                });
534                bytes = &bytes[read..];
535                continue;
536            }
537
538            // If the key buffer has some bytes, feed more to it
539            if self.key_buffer_len > 0 {
540                let to_copy_into = &mut self.key_buffer[self.key_buffer_len
541                    ..keys::MAX_SEQUENCE_LEN.min(self.key_buffer_len + bytes.len())];
542                to_copy_into.copy_from_slice(&bytes[..to_copy_into.len()]);
543                self.key_buffer_len += to_copy_into.len();
544                bytes = &bytes[to_copy_into.len()..];
545
546                match keys::find_sequence(&self.key_buffer[..self.key_buffer_len]) {
547                    keys::MatchResult::Match { length, what } => {
548                        self.key_buffer.rotate_left(length as _);
549                        self.key_buffer_len -= length as usize;
550                        handle_key_event(what, &mut cb);
551                    }
552                    keys::MatchResult::NoMatch { length } => {
553                        // We don't have a match and we know that at least this many bytes
554                        // won't match.
555                        self.key_buffer.rotate_left(length as _);
556                        let capture = &mut self.capture;
557                        let capture_state = &mut self.capture_state;
558                        // Feed them unconditionally to the parser
559                        self.parser.feed_with_abortable(
560                            &self.key_buffer[keys::MAX_SEQUENCE_LEN - length as usize..],
561                            |event: VTEvent<'_>| {
562                                let action = handle_vt_event(event, &mut cb);
563                                dispatch_vt!(action, cb, capture, capture_state);
564                                false
565                            },
566                        );
567                    }
568                    keys::MatchResult::PendingMatch => {
569                        // We need more bytes to complete the match, which means we need another
570                        // feed call.
571                        return;
572                    }
573                }
574            }
575
576            // If we get this far, we're in the ground state for everything...
577
578            if self.key_buffer_len == 0 {
579                match keys::find_sequence(bytes) {
580                    keys::MatchResult::Match { length, what } => {
581                        bytes = &bytes[length as usize..];
582                        handle_key_event(what, &mut cb);
583                    }
584                    keys::MatchResult::NoMatch { length } => {
585                        // We don't have a match and we know that at least this many bytes
586                        // won't match.
587                        let capture = &mut self.capture;
588                        let capture_state = &mut self.capture_state;
589                        self.parser.feed_with_abortable(
590                            &bytes[..length as usize],
591                            |event: VTEvent<'_>| {
592                                let action = handle_vt_event(event, &mut cb);
593                                dispatch_vt!(action, cb, capture, capture_state);
594                                false
595                            },
596                        );
597                        bytes = &bytes[length as usize..];
598                    }
599                    keys::MatchResult::PendingMatch => {
600                        // We need more bytes to complete the match, which means we need another
601                        // feed call.
602                        return;
603                    }
604                }
605            }
606        }
607    }
608}
609
610#[cfg(test)]
611mod tests {
612    use super::*;
613
614    #[test]
615    fn test_codegen() {
616        use keys::{find_sequence, find_sequence_idle};
617
618        macro_rules! m {
619            ($bytes:literal, $length:literal) => {
620                let seq = find_sequence($bytes);
621                assert!(
622                    matches!(
623                        seq,
624                        keys::MatchResult::Match {
625                            length: $length,
626                            what: _
627                        }
628                    ),
629                    "{:?} => {seq:?}, expected match when not idle",
630                    $bytes
631                );
632                let seq = find_sequence_idle($bytes);
633                assert!(
634                    matches!(
635                        seq,
636                        keys::MatchResult::Match {
637                            length: $length,
638                            what: _
639                        }
640                    ),
641                    "{:?} => {seq:?}, expected match when idle",
642                    $bytes
643                );
644            };
645            ($bytes:literal, no) => {
646                let seq = find_sequence($bytes);
647                assert!(
648                    matches!(seq, keys::MatchResult::NoMatch { length: _ }),
649                    "{:?} => {seq:?}, expected no match",
650                    $bytes
651                );
652            };
653            ($bytes:literal, no, $length:literal) => {
654                let seq = find_sequence($bytes);
655                assert!(
656                    matches!(seq, keys::MatchResult::PendingMatch),
657                    "{:?} => {seq:?}, expected pending match when not idle",
658                    $bytes
659                );
660                let seq = find_sequence_idle($bytes);
661                assert!(
662                    matches!(
663                        seq,
664                        keys::MatchResult::Match {
665                            length: $length,
666                            what: _
667                        }
668                    ),
669                    "{:?} => {seq:?}, expected match when idle",
670                    $bytes
671                );
672            };
673        }
674
675        // Unambiguous, same matches regardless of more data
676        m!(b"\x1b[A", 3);
677        m!(b"\x1bA", 2);
678        m!(b"\x1bOA", 3);
679        m!(b"\x1b\x01", 2);
680        m!(b"\x1b\x1b[Z", 4);
681        m!(b"\x1b1", 2); // ESC + 1
682
683        // Ambiguous
684        m!(b"\x1b", no, 1); // could be ESC or ESC ...
685        m!(b"\x1b\x1b", no, 2); // could be ESC ESC or ESC CSI Z, for example
686        m!(b"\x1bO", no, 2); // could be ESC O or ESC O A (SS3)
687        m!(b"\x1b?", no, 2); // could be ESC ? or ESC ? . (VT52 keypad)
688
689        // Super ambiguous escape chains
690        m!(b"\x1b\x1b\x1b", 1); // could be a series of escapes, alt escapes, or whatever. yield one escape to be safe
691        m!(b"\x1b\x1b\x1b\x1b", 1); // could be a series of escapes, alt escapes, or whatever. yield one escape to be safe
692
693        // Shorter matches w/more data
694        m!(b"\x1b!\x1b", 2); // ESC + ! (ALT + SHIFT + 1)
695        m!(b"\x1b\x1b1", 1); // ESC
696        m!(b"\x1b1\x1b", 2); // ESC + 1
697        m!(b"\x1b1m", 2); // ESC + 1
698
699        // Weak matches
700        m!(b"\x1b\x1b[I", 1); // ESC + FocusIn (ESC is a weak match)
701        m!(b"\x1b\x1bON", 1); // ESC + SS3 (ESC)
702        m!(b"\x1b[", no, 2); // ESC + [ if idle, otherwise wait for more
703        m!(b"\x1b\x1b[", no, 1); // ESC if idle, otherwise wait for more
704
705        // No match
706        m!(b"\x1b[I", no); // valid CSI
707        m!(b"\x1b=", no);
708        m!(b"\x1b<", no);
709        m!(b"\x1b>", no);
710
711        // Maybe re-visit
712        m!(b"\x1bO\x1b", no); // disambiguation must end a string, but we might be able to do a lookahead here
713    }
714
715    #[test]
716    fn test_find_sequence() {
717        let bytes = b"\x1ba\x1b[Aa\xf0\x9f\x9b\x9c\x1b[B".as_slice();
718        let mut input_parser = VTPushParserInput::new();
719        let mut events = vec![];
720        input_parser.feed_with(bytes, |event| {
721            events.push(format!("{event:?}"));
722        });
723        assert!(!events.is_empty());
724    }
725
726    fn collect_events(bytes: &[u8]) -> Vec<InputEvent<'static>> {
727        let mut events = vec![];
728        let mut input_parser = VTPushParserInput::new();
729        input_parser.feed_with(bytes, |event| {
730            // Safety: we only inspect the event, and Mouse/Key events don't borrow
731            let event: InputEvent<'static> = unsafe { std::mem::transmute(event) };
732            events.push(event);
733        });
734        events
735    }
736
737    // SGR mouse tests
738
739    #[test]
740    fn test_sgr_mouse_left_click() {
741        // CSI < 0 ; 10 ; 20 M  (left button press at 10,20)
742        let events = collect_events(b"\x1b[<0;10;20M");
743        assert_eq!(events.len(), 1);
744        assert_eq!(
745            events[0],
746            InputEvent::Mouse(MouseEvent {
747                kind: MouseEventKind::Press(MouseButton::Left),
748                x: 9,
749                y: 19,
750                modifiers: Modifier::empty(),
751            })
752        );
753    }
754
755    #[test]
756    fn test_sgr_mouse_right_click() {
757        // CSI < 2 ; 5 ; 3 M
758        let events = collect_events(b"\x1b[<2;5;3M");
759        assert_eq!(events.len(), 1);
760        assert_eq!(
761            events[0],
762            InputEvent::Mouse(MouseEvent {
763                kind: MouseEventKind::Press(MouseButton::Right),
764                x: 4,
765                y: 2,
766                modifiers: Modifier::empty(),
767            })
768        );
769    }
770
771    #[test]
772    fn test_sgr_mouse_middle_click() {
773        // CSI < 1 ; 1 ; 1 M
774        let events = collect_events(b"\x1b[<1;1;1M");
775        assert_eq!(events.len(), 1);
776        assert_eq!(
777            events[0],
778            InputEvent::Mouse(MouseEvent {
779                kind: MouseEventKind::Press(MouseButton::Middle),
780                x: 0,
781                y: 0,
782                modifiers: Modifier::empty(),
783            })
784        );
785    }
786
787    #[test]
788    fn test_sgr_mouse_release() {
789        // CSI < 0 ; 10 ; 20 m  (left button release)
790        let events = collect_events(b"\x1b[<0;10;20m");
791        assert_eq!(events.len(), 1);
792        assert_eq!(
793            events[0],
794            InputEvent::Mouse(MouseEvent {
795                kind: MouseEventKind::Release(MouseButton::Left),
796                x: 9,
797                y: 19,
798                modifiers: Modifier::empty(),
799            })
800        );
801    }
802
803    #[test]
804    fn test_sgr_mouse_wheel_up() {
805        // CSI < 64 ; 10 ; 20 M
806        let events = collect_events(b"\x1b[<64;10;20M");
807        assert_eq!(events.len(), 1);
808        assert_eq!(
809            events[0],
810            InputEvent::Mouse(MouseEvent {
811                kind: MouseEventKind::Press(MouseButton::WheelUp),
812                x: 9,
813                y: 19,
814                modifiers: Modifier::empty(),
815            })
816        );
817    }
818
819    #[test]
820    fn test_sgr_mouse_wheel_down() {
821        // CSI < 65 ; 10 ; 20 M
822        let events = collect_events(b"\x1b[<65;10;20M");
823        assert_eq!(events.len(), 1);
824        assert_eq!(
825            events[0],
826            InputEvent::Mouse(MouseEvent {
827                kind: MouseEventKind::Press(MouseButton::WheelDown),
828                x: 9,
829                y: 19,
830                modifiers: Modifier::empty(),
831            })
832        );
833    }
834
835    #[test]
836    fn test_sgr_mouse_drag() {
837        // CSI < 32 ; 15 ; 25 M  (left button drag, bit 5 = motion)
838        let events = collect_events(b"\x1b[<32;15;25M");
839        assert_eq!(events.len(), 1);
840        assert_eq!(
841            events[0],
842            InputEvent::Mouse(MouseEvent {
843                kind: MouseEventKind::Drag(MouseButton::Left),
844                x: 14,
845                y: 24,
846                modifiers: Modifier::empty(),
847            })
848        );
849    }
850
851    #[test]
852    fn test_sgr_mouse_shift_click() {
853        // CSI < 4 ; 10 ; 20 M  (shift + left click, bit 2 = shift)
854        let events = collect_events(b"\x1b[<4;10;20M");
855        assert_eq!(events.len(), 1);
856        assert_eq!(
857            events[0],
858            InputEvent::Mouse(MouseEvent {
859                kind: MouseEventKind::Press(MouseButton::Left),
860                x: 9,
861                y: 19,
862                modifiers: Modifier::SHIFT,
863            })
864        );
865    }
866
867    #[test]
868    fn test_sgr_mouse_ctrl_click() {
869        // CSI < 16 ; 10 ; 20 M  (ctrl + left click, bit 4 = ctrl)
870        let events = collect_events(b"\x1b[<16;10;20M");
871        assert_eq!(events.len(), 1);
872        assert_eq!(
873            events[0],
874            InputEvent::Mouse(MouseEvent {
875                kind: MouseEventKind::Press(MouseButton::Left),
876                x: 9,
877                y: 19,
878                modifiers: Modifier::CTRL,
879            })
880        );
881    }
882
883    #[test]
884    fn test_sgr_mouse_alt_click() {
885        // CSI < 8 ; 10 ; 20 M  (alt + left click, bit 3 = alt)
886        let events = collect_events(b"\x1b[<8;10;20M");
887        assert_eq!(events.len(), 1);
888        assert_eq!(
889            events[0],
890            InputEvent::Mouse(MouseEvent {
891                kind: MouseEventKind::Press(MouseButton::Left),
892                x: 9,
893                y: 19,
894                modifiers: Modifier::ALT,
895            })
896        );
897    }
898
899    #[test]
900    fn test_sgr_mouse_large_coordinates() {
901        // CSI < 0 ; 500 ; 300 M
902        let events = collect_events(b"\x1b[<0;500;300M");
903        assert_eq!(events.len(), 1);
904        assert_eq!(
905            events[0],
906            InputEvent::Mouse(MouseEvent {
907                kind: MouseEventKind::Press(MouseButton::Left),
908                x: 499,
909                y: 299,
910                modifiers: Modifier::empty(),
911            })
912        );
913    }
914
915    // urxvt mouse tests
916
917    #[test]
918    fn test_urxvt_mouse_left_click() {
919        // CSI 0 ; 10 ; 20 M  (urxvt: no '<' private prefix)
920        let events = collect_events(b"\x1b[0;10;20M");
921        assert_eq!(events.len(), 1);
922        assert_eq!(
923            events[0],
924            InputEvent::Mouse(MouseEvent {
925                kind: MouseEventKind::Press(MouseButton::Left),
926                x: 9,
927                y: 19,
928                modifiers: Modifier::empty(),
929            })
930        );
931    }
932
933    // X10 mouse tests
934
935    #[test]
936    fn test_x10_mouse_left_click() {
937        // CSI M followed by 3 bytes: button=0+32=0x20, x=10+32=0x2a, y=20+32=0x34
938        let events = collect_events(b"\x1b[M\x20\x2a\x34");
939        assert_eq!(events.len(), 1);
940        assert_eq!(
941            events[0],
942            InputEvent::Mouse(MouseEvent {
943                kind: MouseEventKind::Press(MouseButton::Left),
944                x: 9,
945                y: 19,
946                modifiers: Modifier::empty(),
947            })
948        );
949    }
950
951    #[test]
952    fn test_x10_mouse_right_click() {
953        // button=2+32=0x22, x=5+32=0x25, y=3+32=0x23
954        let events = collect_events(b"\x1b[M\x22\x25\x23");
955        assert_eq!(events.len(), 1);
956        assert_eq!(
957            events[0],
958            InputEvent::Mouse(MouseEvent {
959                kind: MouseEventKind::Press(MouseButton::Right),
960                x: 4,
961                y: 2,
962                modifiers: Modifier::empty(),
963            })
964        );
965    }
966
967    #[test]
968    fn test_x10_mouse_release() {
969        // button=3+32=0x23 means release, x=10+32=0x2a, y=20+32=0x34
970        let events = collect_events(b"\x1b[M\x23\x2a\x34");
971        assert_eq!(events.len(), 1);
972        assert_eq!(
973            events[0],
974            InputEvent::Mouse(MouseEvent {
975                kind: MouseEventKind::Release(MouseButton::Left),
976                x: 9,
977                y: 19,
978                modifiers: Modifier::empty(),
979            })
980        );
981    }
982
983    #[test]
984    fn test_x10_mouse_ctrl_click() {
985        // button=0+16(ctrl)+32=0x30, x=10+32=0x2a, y=20+32=0x34
986        let events = collect_events(b"\x1b[M\x30\x2a\x34");
987        assert_eq!(events.len(), 1);
988        assert_eq!(
989            events[0],
990            InputEvent::Mouse(MouseEvent {
991                kind: MouseEventKind::Press(MouseButton::Left),
992                x: 9,
993                y: 19,
994                modifiers: Modifier::CTRL,
995            })
996        );
997    }
998
999    #[test]
1000    fn test_sgr_mouse_followed_by_key() {
1001        // Mouse event followed by a key press
1002        let events = collect_events(b"\x1b[<0;10;20Ma");
1003        assert_eq!(events.len(), 2);
1004        assert_eq!(
1005            events[0],
1006            InputEvent::Mouse(MouseEvent {
1007                kind: MouseEventKind::Press(MouseButton::Left),
1008                x: 9,
1009                y: 19,
1010                modifiers: Modifier::empty(),
1011            })
1012        );
1013        assert_eq!(events[1], InputEvent::KeyChar('a', Modifier::empty()));
1014    }
1015}