Skip to main content

zellij_utils/vendored/termwiz/
input.rs

1//! This module provides an InputParser struct to help with parsing
2//! input received from a terminal.
3use crate::vendored::termwiz::keymap::{Found, KeyMap};
4use crate::vendored::termwiz::readbuf::ReadBuffer;
5use bitflags::bitflags;
6use std::fmt::Write;
7
8pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
9
10bitflags! {
11    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
12    pub struct Modifiers: u16 {
13        const NONE = 0;
14        const SHIFT = 1 << 1;
15        const ALT = 1 << 2;
16        const CTRL = 1 << 3;
17        const SUPER = 1 << 4;
18        const LEFT_ALT = 1 << 5;
19        const RIGHT_ALT = 1 << 6;
20        const LEADER = 1 << 7;
21        const LEFT_CTRL = 1 << 8;
22        const RIGHT_CTRL = 1 << 9;
23        const LEFT_SHIFT = 1 << 10;
24        const RIGHT_SHIFT = 1 << 11;
25        const ENHANCED_KEY = 1 << 12;
26    }
27}
28
29impl Modifiers {
30    pub fn encode_xterm(self) -> u8 {
31        let mut number = 0;
32        if self.contains(Self::SHIFT) {
33            number |= 1;
34        }
35        if self.contains(Self::ALT) {
36            number |= 2;
37        }
38        if self.contains(Self::CTRL) {
39            number |= 4;
40        }
41        number
42    }
43
44    pub fn remove_positional_mods(self) -> Self {
45        self - (Self::LEFT_ALT
46            | Self::RIGHT_ALT
47            | Self::LEFT_CTRL
48            | Self::RIGHT_CTRL
49            | Self::LEFT_SHIFT
50            | Self::RIGHT_SHIFT
51            | Self::ENHANCED_KEY)
52    }
53}
54
55bitflags! {
56    #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
57    pub struct KittyKeyboardFlags: u16 {
58        const NONE = 0;
59        const DISAMBIGUATE_ESCAPE_CODES = 1;
60        const REPORT_EVENT_TYPES = 2;
61        const REPORT_ALTERNATE_KEYS = 4;
62        const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 8;
63        const REPORT_ASSOCIATED_TEXT = 16;
64    }
65}
66
67pub fn ctrl_mapping(c: char) -> Option<char> {
68    Some(match c {
69        '@' | '`' | ' ' | '2' => '\x00',
70        'A' | 'a' => '\x01',
71        'B' | 'b' => '\x02',
72        'C' | 'c' => '\x03',
73        'D' | 'd' => '\x04',
74        'E' | 'e' => '\x05',
75        'F' | 'f' => '\x06',
76        'G' | 'g' => '\x07',
77        'H' | 'h' => '\x08',
78        'I' | 'i' => '\x09',
79        'J' | 'j' => '\x0a',
80        'K' | 'k' => '\x0b',
81        'L' | 'l' => '\x0c',
82        'M' | 'm' => '\x0d',
83        'N' | 'n' => '\x0e',
84        'O' | 'o' => '\x0f',
85        'P' | 'p' => '\x10',
86        'Q' | 'q' => '\x11',
87        'R' | 'r' => '\x12',
88        'S' | 's' => '\x13',
89        'T' | 't' => '\x14',
90        'U' | 'u' => '\x15',
91        'V' | 'v' => '\x16',
92        'W' | 'w' => '\x17',
93        'X' | 'x' => '\x18',
94        'Y' | 'y' => '\x19',
95        'Z' | 'z' => '\x1a',
96        '[' | '3' | '{' => '\x1b',
97        '\\' | '4' | '|' => '\x1c',
98        ']' | '5' | '}' => '\x1d',
99        '^' | '6' | '~' => '\x1e',
100        '_' | '7' | '/' => '\x1f',
101        '8' | '?' => '\x7f',
102        _ => return None,
103    })
104}
105
106bitflags! {
107    #[derive(Debug, Default, Clone, PartialEq, Eq)]
108    pub struct MouseButtons: u8 {
109        const NONE = 0;
110        const LEFT = 1<<1;
111        const RIGHT = 1<<2;
112        const MIDDLE = 1<<3;
113        const VERT_WHEEL = 1<<4;
114        const HORZ_WHEEL = 1<<5;
115        /// if set then the wheel movement was in the positive
116        /// direction, else the negative direction
117        const WHEEL_POSITIVE = 1<<6;
118    }
119}
120
121pub const CSI: &str = "\x1b[";
122pub const SS3: &str = "\x1bO";
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub enum InputEvent {
126    Key(KeyEvent),
127    Mouse(MouseEvent),
128    PixelMouse(PixelMouseEvent),
129    /// Detected that the user has resized the terminal
130    Resized {
131        cols: usize,
132        rows: usize,
133    },
134    /// For terminals that support Bracketed Paste mode,
135    /// pastes are collected and reported as this variant.
136    Paste(String),
137    /// The program has woken the input thread.
138    Wake,
139    /// An Operating System Command sequence was received.
140    /// Contains the raw payload between \x1b] and the terminator.
141    OperatingSystemCommand(Vec<u8>),
142    /// A CSI-based device control / status report reply emitted by the
143    /// host terminal (not a keyboard event). This variant is only produced
144    /// for a deliberately narrow whitelist of final bytes — `t` (pixel
145    /// dimensions reply), `y` (DECRPM reply), `c` (Primary-DA reply), and
146    /// `n` (DSR reply). The raw field contains the exact byte sequence of
147    /// the original report (including the leading ESC) so it can be
148    /// forwarded verbatim without re-serialization.
149    DeviceControlReply {
150        intermediates: Vec<u8>,
151        params: Vec<u8>,
152        final_byte: u8,
153        raw: Vec<u8>,
154    },
155}
156
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct MouseEvent {
159    pub x: u16,
160    pub y: u16,
161    pub mouse_buttons: MouseButtons,
162    pub modifiers: Modifiers,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct PixelMouseEvent {
167    pub x_pixels: u16,
168    pub y_pixels: u16,
169    pub mouse_buttons: MouseButtons,
170    pub modifiers: Modifiers,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct KeyEvent {
175    /// Which key was pressed
176    pub key: KeyCode,
177    /// Which modifiers are down
178    pub modifiers: Modifiers,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum KeyboardEncoding {
183    Xterm,
184    /// <http://www.leonerd.org.uk/hacks/fixterms/>
185    CsiU,
186    /// <https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md>
187    Win32,
188    /// <https://sw.kovidgoyal.net/kitty/keyboard-protocol/>
189    Kitty(KittyKeyboardFlags),
190}
191
192/// Specifies terminal modes/configuration that can influence how a KeyCode
193/// is encoded when being sent to and application via the pty.
194#[derive(Debug, Clone, Copy)]
195pub struct KeyCodeEncodeModes {
196    pub encoding: KeyboardEncoding,
197    pub application_cursor_keys: bool,
198    pub newline_mode: bool,
199    pub modify_other_keys: Option<i64>,
200}
201
202/// Which key is pressed.  Not all of these are probable to appear
203/// on most systems.  A lot of this list is @wez trawling docs and
204/// making an entry for things that might be possible in this first pass.
205#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
206pub enum KeyCode {
207    /// The decoded unicode character
208    Char(char),
209
210    Hyper,
211    Super,
212    Meta,
213
214    /// Ctrl-break on windows
215    Cancel,
216    Backspace,
217    Tab,
218    Clear,
219    Enter,
220    Shift,
221    Escape,
222    LeftShift,
223    RightShift,
224    Control,
225    LeftControl,
226    RightControl,
227    Alt,
228    LeftAlt,
229    RightAlt,
230    Menu,
231    LeftMenu,
232    RightMenu,
233    Pause,
234    CapsLock,
235    PageUp,
236    PageDown,
237    End,
238    Home,
239    LeftArrow,
240    RightArrow,
241    UpArrow,
242    DownArrow,
243    Select,
244    Print,
245    Execute,
246    PrintScreen,
247    Insert,
248    Delete,
249    Help,
250    LeftWindows,
251    RightWindows,
252    Applications,
253    Sleep,
254    Numpad0,
255    Numpad1,
256    Numpad2,
257    Numpad3,
258    Numpad4,
259    Numpad5,
260    Numpad6,
261    Numpad7,
262    Numpad8,
263    Numpad9,
264    Multiply,
265    Add,
266    Separator,
267    Subtract,
268    Decimal,
269    Divide,
270    /// F1-F24 are possible
271    Function(u8),
272    NumLock,
273    ScrollLock,
274    Copy,
275    Cut,
276    Paste,
277    BrowserBack,
278    BrowserForward,
279    BrowserRefresh,
280    BrowserStop,
281    BrowserSearch,
282    BrowserFavorites,
283    BrowserHome,
284    VolumeMute,
285    VolumeDown,
286    VolumeUp,
287    MediaNextTrack,
288    MediaPrevTrack,
289    MediaStop,
290    MediaPlayPause,
291    ApplicationLeftArrow,
292    ApplicationRightArrow,
293    ApplicationUpArrow,
294    ApplicationDownArrow,
295    KeyPadHome,
296    KeyPadEnd,
297    KeyPadPageUp,
298    KeyPadPageDown,
299    KeyPadBegin,
300
301    #[doc(hidden)]
302    InternalPasteStart,
303    #[doc(hidden)]
304    InternalPasteEnd,
305}
306
307impl KeyCode {
308    /// if SHIFT is held and we have KeyCode::Char('c') we want to normalize
309    /// that keycode to KeyCode::Char('C'); that is what this function does.
310    pub fn normalize_shift_to_upper_case(self, modifiers: Modifiers) -> KeyCode {
311        if modifiers.contains(Modifiers::SHIFT) {
312            match self {
313                KeyCode::Char(c) if c.is_ascii_lowercase() => KeyCode::Char(c.to_ascii_uppercase()),
314                _ => self,
315            }
316        } else {
317            self
318        }
319    }
320
321    /// Return true if the key represents a modifier key.
322    pub fn is_modifier(self) -> bool {
323        matches!(
324            self,
325            Self::Hyper
326                | Self::Super
327                | Self::Meta
328                | Self::Shift
329                | Self::LeftShift
330                | Self::RightShift
331                | Self::Control
332                | Self::LeftControl
333                | Self::RightControl
334                | Self::Alt
335                | Self::LeftAlt
336                | Self::RightAlt
337                | Self::LeftWindows
338                | Self::RightWindows
339        )
340    }
341
342    /// Returns the byte sequence that represents this KeyCode and Modifier combination.
343    pub fn encode(
344        &self,
345        mods: Modifiers,
346        modes: KeyCodeEncodeModes,
347        is_down: bool,
348    ) -> Result<String> {
349        if !is_down {
350            // We only want down events
351            return Ok(String::new());
352        }
353        // We are encoding the key as an xterm-compatible sequence, which does not support
354        // positional modifiers.
355        let mods = mods.remove_positional_mods();
356
357        use KeyCode::*;
358
359        let key = self.normalize_shift_to_upper_case(mods);
360        // Normalize the modifier state for Char's that are uppercase; remove
361        // the SHIFT modifier so that reduce ambiguity below
362        let mods = match key {
363            Char(c)
364                if (c.is_ascii_punctuation() || c.is_ascii_uppercase())
365                    && mods.contains(Modifiers::SHIFT) =>
366            {
367                mods & !Modifiers::SHIFT
368            },
369            _ => mods,
370        };
371
372        // Normalize Backspace and Delete
373        let key = match key {
374            Char('\x7f') => Delete,
375            Char('\x08') => Backspace,
376            c => c,
377        };
378
379        let mut buf = String::new();
380
381        // TODO: also respect self.application_keypad
382
383        match key {
384            Char(c)
385                if is_ambiguous_ascii_ctrl(c)
386                    && mods.contains(Modifiers::CTRL)
387                    && modes.encoding == KeyboardEncoding::CsiU =>
388            {
389                csi_u_encode(&mut buf, c, mods, &modes)?;
390            },
391            Char(c) if c.is_ascii_uppercase() && mods.contains(Modifiers::CTRL) => {
392                csi_u_encode(&mut buf, c, mods, &modes)?;
393            },
394
395            Char(c) if mods.contains(Modifiers::CTRL) && modes.modify_other_keys == Some(2) => {
396                csi_u_encode(&mut buf, c, mods, &modes)?;
397            },
398            Char(c) if mods.contains(Modifiers::CTRL) && ctrl_mapping(c).is_some() => {
399                let c = ctrl_mapping(c).unwrap();
400                if mods.contains(Modifiers::ALT) {
401                    buf.push(0x1b as char);
402                }
403                buf.push(c);
404            },
405
406            // When alt is pressed, send escape first to indicate to the peer that
407            // ALT is pressed.  We do this only for ascii alnum characters because
408            // eg: on macOS generates altgr style glyphs and keeps the ALT key
409            // in the modifier set.  This confuses eg: zsh which then just displays
410            // <fffffffff> as the input, so we want to avoid that.
411            Char(c)
412                if (c.is_ascii_alphanumeric() || c.is_ascii_punctuation())
413                    && mods.contains(Modifiers::ALT) =>
414            {
415                buf.push(0x1b as char);
416                buf.push(c);
417            },
418
419            Backspace => {
420                // Backspace sends the default VERASE which is confusingly
421                // the DEL ascii codepoint rather than BS.
422                // We only send BS when CTRL is held.
423                if mods.contains(Modifiers::CTRL) {
424                    csi_u_encode(&mut buf, '\x08', mods, &modes)?;
425                } else if mods.contains(Modifiers::SHIFT) {
426                    csi_u_encode(&mut buf, '\x7f', mods, &modes)?;
427                } else {
428                    if mods.contains(Modifiers::ALT) {
429                        buf.push(0x1b as char);
430                    }
431                    buf.push('\x7f');
432                }
433            },
434
435            Enter | Escape => {
436                let c = match key {
437                    Enter => '\r',
438                    Escape => '\x1b',
439                    _ => unreachable!(),
440                };
441                if mods.contains(Modifiers::SHIFT) || mods.contains(Modifiers::CTRL) {
442                    csi_u_encode(&mut buf, c, mods, &modes)?;
443                } else {
444                    if mods.contains(Modifiers::ALT) {
445                        buf.push(0x1b as char);
446                    }
447                    buf.push(c);
448                    if modes.newline_mode && key == Enter {
449                        buf.push(0x0a as char);
450                    }
451                }
452            },
453
454            Tab if !mods.is_empty() && modes.modify_other_keys.is_some() => {
455                csi_u_encode(&mut buf, '\t', mods, &modes)?;
456            },
457
458            Tab => {
459                if mods.contains(Modifiers::ALT) {
460                    buf.push(0x1b as char);
461                }
462                let mods = mods & !Modifiers::ALT;
463                if mods == Modifiers::CTRL {
464                    buf.push_str("\x1b[9;5u");
465                } else if mods == Modifiers::CTRL | Modifiers::SHIFT {
466                    buf.push_str("\x1b[1;5Z");
467                } else if mods == Modifiers::SHIFT {
468                    buf.push_str("\x1b[Z");
469                } else {
470                    buf.push('\t');
471                }
472            },
473
474            Char(c) => {
475                if mods.is_empty() {
476                    buf.push(c);
477                } else {
478                    csi_u_encode(&mut buf, c, mods, &modes)?;
479                }
480            },
481
482            Home
483            | KeyPadHome
484            | End
485            | KeyPadEnd
486            | UpArrow
487            | DownArrow
488            | RightArrow
489            | LeftArrow
490            | ApplicationUpArrow
491            | ApplicationDownArrow
492            | ApplicationRightArrow
493            | ApplicationLeftArrow => {
494                let (force_app, c) = match key {
495                    UpArrow => (false, 'A'),
496                    DownArrow => (false, 'B'),
497                    RightArrow => (false, 'C'),
498                    LeftArrow => (false, 'D'),
499                    KeyPadHome | Home => (false, 'H'),
500                    End | KeyPadEnd => (false, 'F'),
501                    ApplicationUpArrow => (true, 'A'),
502                    ApplicationDownArrow => (true, 'B'),
503                    ApplicationRightArrow => (true, 'C'),
504                    ApplicationLeftArrow => (true, 'D'),
505                    _ => unreachable!(),
506                };
507
508                let csi_or_ss3 = if force_app || modes.application_cursor_keys {
509                    // Use SS3 in application mode
510                    SS3
511                } else {
512                    // otherwise use regular CSI
513                    CSI
514                };
515
516                if mods.contains(Modifiers::ALT)
517                    || mods.contains(Modifiers::SHIFT)
518                    || mods.contains(Modifiers::CTRL)
519                {
520                    write!(buf, "{}1;{}{}", CSI, 1 + mods.encode_xterm(), c)?;
521                } else {
522                    write!(buf, "{}{}", csi_or_ss3, c)?;
523                }
524            },
525
526            PageUp | PageDown | KeyPadPageUp | KeyPadPageDown | Insert | Delete => {
527                let c = match key {
528                    Insert => 2,
529                    Delete => 3,
530                    KeyPadPageUp | PageUp => 5,
531                    KeyPadPageDown | PageDown => 6,
532                    _ => unreachable!(),
533                };
534
535                if mods.contains(Modifiers::ALT)
536                    || mods.contains(Modifiers::SHIFT)
537                    || mods.contains(Modifiers::CTRL)
538                {
539                    write!(buf, "\x1b[{};{}~", c, 1 + mods.encode_xterm())?;
540                } else {
541                    write!(buf, "\x1b[{}~", c)?;
542                }
543            },
544
545            Function(n) => {
546                if mods.is_empty() && n < 5 {
547                    // F1-F4 are encoded using SS3 if there are no modifiers
548                    write!(
549                        buf,
550                        "{}",
551                        match n {
552                            1 => "\x1bOP",
553                            2 => "\x1bOQ",
554                            3 => "\x1bOR",
555                            4 => "\x1bOS",
556                            _ => unreachable!("wat?"),
557                        }
558                    )?;
559                } else if n < 5 {
560                    // Special case for F1-F4 with modifiers
561                    let code = match n {
562                        1 => 'P',
563                        2 => 'Q',
564                        3 => 'R',
565                        4 => 'S',
566                        _ => unreachable!("wat?"),
567                    };
568                    write!(buf, "\x1b[1;{}{code}", 1 + mods.encode_xterm())?;
569                } else {
570                    // Higher numbered F-keys using CSI instead of SS3.
571                    let intro = match n {
572                        1 => "\x1b[11",
573                        2 => "\x1b[12",
574                        3 => "\x1b[13",
575                        4 => "\x1b[14",
576                        5 => "\x1b[15",
577                        6 => "\x1b[17",
578                        7 => "\x1b[18",
579                        8 => "\x1b[19",
580                        9 => "\x1b[20",
581                        10 => "\x1b[21",
582                        11 => "\x1b[23",
583                        12 => "\x1b[24",
584                        13 => "\x1b[25",
585                        14 => "\x1b[26",
586                        15 => "\x1b[28",
587                        16 => "\x1b[29",
588                        17 => "\x1b[31",
589                        18 => "\x1b[32",
590                        19 => "\x1b[33",
591                        20 => "\x1b[34",
592                        21 => "\x1b[42",
593                        22 => "\x1b[43",
594                        23 => "\x1b[44",
595                        24 => "\x1b[45",
596                        _ => return Err(format!("unhandled fkey number {}", n).into()),
597                    };
598                    let encoded_mods = mods.encode_xterm();
599                    if encoded_mods == 0 {
600                        // If no modifiers are held, don't send the modifier
601                        // sequence, as the modifier encoding is a CSI-u extension.
602                        write!(buf, "{}~", intro)?;
603                    } else {
604                        write!(buf, "{};{}~", intro, 1 + encoded_mods)?;
605                    }
606                }
607            },
608
609            Numpad0 | Numpad3 | Numpad9 | Decimal => {
610                let intro = match key {
611                    Numpad0 => "\x1b[2",
612                    Numpad3 => "\x1b[6",
613                    Numpad9 => "\x1b[6",
614                    Decimal => "\x1b[3",
615                    _ => unreachable!(),
616                };
617
618                let encoded_mods = mods.encode_xterm();
619                if encoded_mods == 0 {
620                    write!(buf, "{}~", intro)?;
621                } else {
622                    write!(buf, "{};{}~", intro, 1 + encoded_mods)?;
623                }
624            },
625
626            Numpad1 | Numpad2 | Numpad4 | Numpad5 | KeyPadBegin | Numpad6 | Numpad7 | Numpad8 => {
627                let c = match key {
628                    Numpad1 => "F",
629                    Numpad2 => "B",
630                    Numpad4 => "D",
631                    KeyPadBegin | Numpad5 => "E",
632                    Numpad6 => "C",
633                    Numpad7 => "H",
634                    Numpad8 => "A",
635                    _ => unreachable!(),
636                };
637
638                let encoded_mods = mods.encode_xterm();
639                if encoded_mods == 0 {
640                    write!(buf, "{}{}", CSI, c)?;
641                } else {
642                    write!(buf, "{}1;{}{}", CSI, 1 + encoded_mods, c)?;
643                }
644            },
645
646            Multiply | Add | Separator | Subtract | Divide => {},
647
648            // Modifier keys pressed on their own don't expand to anything
649            Control | LeftControl | RightControl | Alt | LeftAlt | RightAlt | Menu | LeftMenu
650            | RightMenu | Super | Hyper | Shift | LeftShift | RightShift | Meta | LeftWindows
651            | RightWindows | NumLock | ScrollLock | Cancel | Clear | Pause | CapsLock | Select
652            | Print | PrintScreen | Execute | Help | Applications | Sleep | Copy | Cut | Paste
653            | BrowserBack | BrowserForward | BrowserRefresh | BrowserStop | BrowserSearch
654            | BrowserFavorites | BrowserHome | VolumeMute | VolumeDown | VolumeUp
655            | MediaNextTrack | MediaPrevTrack | MediaStop | MediaPlayPause | InternalPasteStart
656            | InternalPasteEnd => {},
657        };
658
659        Ok(buf)
660    }
661}
662
663/// characters that when masked for CTRL could be an ascii control character
664/// or could be a key that a user legitimately wants to process in their
665/// terminal application
666fn is_ambiguous_ascii_ctrl(c: char) -> bool {
667    matches!(c, 'i' | 'I' | 'm' | 'M' | '[' | '{' | '@')
668}
669
670fn is_ascii(c: char) -> bool {
671    (c as u32) < 0x80
672}
673
674fn csi_u_encode(
675    buf: &mut String,
676    c: char,
677    mods: Modifiers,
678    modes: &KeyCodeEncodeModes,
679) -> Result<()> {
680    if modes.encoding == KeyboardEncoding::CsiU && is_ascii(c) {
681        write!(buf, "\x1b[{};{}u", c as u32, 1 + mods.encode_xterm())?;
682        return Ok(());
683    }
684
685    // <https://invisible-island.net/xterm/modified-keys.html>
686    match (c, modes.modify_other_keys) {
687        ('c' | 'd' | '\x1b' | '\x7f' | '\x08', Some(1)) => {
688            // Exclude well-known keys from modifyOtherKeys mode 1
689        },
690        (c, Some(_)) => {
691            write!(buf, "\x1b[27;{};{}~", 1 + mods.encode_xterm(), c as u32)?;
692            return Ok(());
693        },
694        _ => {},
695    }
696
697    let c = if mods.contains(Modifiers::CTRL) && ctrl_mapping(c).is_some() {
698        ctrl_mapping(c).unwrap()
699    } else {
700        c
701    };
702    if mods.contains(Modifiers::ALT) {
703        buf.push(0x1b as char);
704    }
705    write!(buf, "{}", c)?;
706    Ok(())
707}
708
709#[derive(Debug, Clone, Copy, PartialEq, Eq)]
710enum MouseButton {
711    Button1Press,
712    Button1Release,
713    Button1Drag,
714    Button2Press,
715    Button2Release,
716    Button2Drag,
717    Button3Press,
718    Button3Release,
719    Button3Drag,
720    Button4Press,
721    Button4Release,
722    Button5Press,
723    Button5Release,
724    Button6Press,
725    Button6Release,
726    Button7Press,
727    Button7Release,
728    None,
729}
730
731fn decode_mouse_button(control: u8, p0: i64) -> Option<MouseButton> {
732    match (control, p0 & 0b110_0011) {
733        (b'M', 0) => Some(MouseButton::Button1Press),
734        (b'm', 0) => Some(MouseButton::Button1Release),
735        (b'M', 1) => Some(MouseButton::Button2Press),
736        (b'm', 1) => Some(MouseButton::Button2Release),
737        (b'M', 2) => Some(MouseButton::Button3Press),
738        (b'm', 2) => Some(MouseButton::Button3Release),
739        (b'M', 64) => Some(MouseButton::Button4Press),
740        (b'm', 64) => Some(MouseButton::Button4Release),
741        (b'M', 65) => Some(MouseButton::Button5Press),
742        (b'm', 65) => Some(MouseButton::Button5Release),
743        (b'M', 66) => Some(MouseButton::Button6Press),
744        (b'm', 66) => Some(MouseButton::Button6Release),
745        (b'M', 67) => Some(MouseButton::Button7Press),
746        (b'm', 67) => Some(MouseButton::Button7Release),
747        (b'M', 32) => Some(MouseButton::Button1Drag),
748        (b'M', 33) => Some(MouseButton::Button2Drag),
749        (b'M', 34) => Some(MouseButton::Button3Drag),
750        (b'M', 35) | (b'm', 35) | (b'M', 3) | (b'm', 3) => Some(MouseButton::None),
751        _ => ::core::option::Option::None,
752    }
753}
754
755impl From<MouseButton> for MouseButtons {
756    fn from(button: MouseButton) -> MouseButtons {
757        match button {
758            MouseButton::Button1Press | MouseButton::Button1Drag => MouseButtons::LEFT,
759            MouseButton::Button2Press | MouseButton::Button2Drag => MouseButtons::MIDDLE,
760            MouseButton::Button3Press | MouseButton::Button3Drag => MouseButtons::RIGHT,
761            MouseButton::Button4Press => MouseButtons::VERT_WHEEL | MouseButtons::WHEEL_POSITIVE,
762            MouseButton::Button5Press => MouseButtons::VERT_WHEEL,
763            MouseButton::Button6Press => MouseButtons::HORZ_WHEEL | MouseButtons::WHEEL_POSITIVE,
764            MouseButton::Button7Press => MouseButtons::HORZ_WHEEL,
765            _ => MouseButtons::NONE,
766        }
767    }
768}
769
770fn decode_mouse_modifiers(p0: i64) -> Modifiers {
771    let mut modifiers = Modifiers::NONE;
772    if p0 & 4 != 0 {
773        modifiers |= Modifiers::SHIFT;
774    }
775    if p0 & 8 != 0 {
776        modifiers |= Modifiers::ALT;
777    }
778    if p0 & 16 != 0 {
779        modifiers |= Modifiers::CTRL;
780    }
781    modifiers
782}
783
784/// Try to parse an SGR mouse sequence from the buffer.
785/// Returns Some((InputEvent, bytes_consumed)) on success.
786/// Returns None if the buffer does not contain a complete SGR mouse sequence.
787fn parse_sgr_mouse(buf: &[u8]) -> Option<(InputEvent, usize)> {
788    // Must start with \x1b[<
789    if buf.len() < 6 || !buf.starts_with(b"\x1b[<") {
790        return None;
791    }
792    let rest = &buf[3..]; // skip \x1b[<
793
794    // Find the terminating M or m
795    let term_pos = rest.iter().position(|&b| b == b'M' || b == b'm')?;
796    let control = rest[term_pos];
797    let params_str = std::str::from_utf8(&rest[..term_pos]).ok()?;
798
799    // Parse three semicolon-separated integers
800    let mut parts = params_str.splitn(3, ';');
801    let p0: i64 = parts.next()?.parse().ok()?;
802    let p1: i64 = parts.next()?.parse().ok()?;
803    let p2: i64 = parts.next()?.parse().ok()?;
804
805    let button = decode_mouse_button(control, p0)?;
806    let modifiers = decode_mouse_modifiers(p0);
807    let mouse_buttons: MouseButtons = button.into();
808
809    let consumed = 3 + term_pos + 1; // \x1b[< + params + M/m
810
811    Some((
812        InputEvent::Mouse(MouseEvent {
813            x: p1 as u16,
814            y: p2 as u16,
815            mouse_buttons,
816            modifiers,
817        }),
818        consumed,
819    ))
820}
821
822/// Attempt to parse an OSC (Operating System Command) sequence from the buffer.
823/// Returns `Some((InputEvent::OperatingSystemCommand(payload), len))` if a complete
824/// OSC sequence is found, where `payload` is the bytes between `\x1b]` and the
825/// terminator, and `len` is the total number of bytes consumed.
826/// Returns `None` if the buffer does not start with `\x1b]` or the sequence is incomplete.
827/// Attempt to parse a CSI-based host-terminal report (device-attribute
828/// responses, DSR replies, DECRPM, pixel-dims reply, etc.) from the start
829/// of `buf`.
830///
831/// Only a narrow whitelist of final bytes is recognised: `t`, `y`, `c`,
832/// `n`. Any other final byte returns `None` so the bytes fall through to
833/// the regular CSI key-mapping machinery.
834///
835/// Returns `Some((event, len))` on a full match, `None` if the bytes do
836/// not look like a whitelisted CSI report (caller should try the next
837/// parser) and reserves returning None with the buffer starting with
838/// `ESC [` for two distinct cases — not currently disambiguated here:
839/// - truly malformed / unsupported sequence, or
840/// - incomplete input; caller handles incompleteness via `maybe_more`.
841/// Return `Some(len)` if `buf` starts with a structurally complete CSI
842/// sequence (`\x1b[ <params>* <intermediates>* <final>` per ECMA-48 §5.4),
843/// regardless of whether the final byte is one we have a use for. Used by
844/// `process_bytes` to advance past CSI sequences the keymap doesn't
845/// recognise — most importantly Kitty keyboard-protocol events
846/// `\x1b[<keycode>;<mods>u`. Without this, the keymap returns
847/// `Found::NeedData` (it sees the bytes as a possible prefix of a longer
848/// registered key) and the parser wedges holding bytes that will never
849/// extend into anything.
850///
851/// Returns `None` if the buffer doesn't start with `\x1b[`, contains a
852/// non-CSI byte, or hasn't yet received its final byte.
853fn complete_csi_len(buf: &[u8]) -> Option<usize> {
854    if buf.get(0) != Some(&0x1b) || buf.get(1) != Some(&b'[') {
855        return None;
856    }
857    let mut i = 2;
858    let max_scan = buf.len().min(256);
859    while i < max_scan {
860        let b = buf[i];
861        match b {
862            // Parameters (digits, ;, :, ?, <, =, >) and intermediates (space..'/').
863            0x30..=0x3F | 0x20..=0x2F => i += 1,
864            // Any byte in the final-byte range terminates a CSI sequence.
865            0x40..=0x7E => return Some(i + 1),
866            // Anything else means this isn't a well-formed CSI.
867            _ => return None,
868        }
869    }
870    None
871}
872
873fn parse_csi_report(buf: &[u8]) -> Option<(InputEvent, usize)> {
874    if buf.get(0) != Some(&0x1b) || buf.get(1) != Some(&b'[') {
875        return None;
876    }
877    // Scan forward looking for a final byte in the whitelist, or bail if
878    // we hit something that clearly is not a CSI report (a non-printable
879    // byte other than the known final bytes).
880    let mut i = 2;
881    let mut intermediates: Vec<u8> = Vec::new();
882    let mut params: Vec<u8> = Vec::new();
883    // Parameters (0x30..=0x3F) come first, then intermediates (0x20..=0x2F),
884    // then a final byte (0x40..=0x7E). We only scan up to a reasonable
885    // length to avoid pathological buffers.
886    let max_scan = buf.len().min(256);
887    while i < max_scan {
888        let b = buf[i];
889        match b {
890            // Parameters: digits, `;`, `:`, `?`, `<`, `=`, `>`
891            0x30..=0x3F => {
892                params.push(b);
893                i += 1;
894            },
895            // Intermediates: space, `!`, `"`, ... `/`
896            0x20..=0x2F => {
897                intermediates.push(b);
898                i += 1;
899            },
900            // Final byte (0x40..=0x7E): must be one of the whitelisted bytes.
901            b't' | b'y' | b'c' | b'n' => {
902                let raw = buf[0..=i].to_vec();
903                return Some((
904                    InputEvent::DeviceControlReply {
905                        intermediates,
906                        params,
907                        final_byte: b,
908                        raw,
909                    },
910                    i + 1,
911                ));
912            },
913            0x40..=0x7E => {
914                // Final byte outside the whitelist — not ours.
915                return None;
916            },
917            _ => {
918                // Something unexpected inside the CSI — give up.
919                return None;
920            },
921        }
922    }
923    None
924}
925
926fn parse_osc(buf: &[u8]) -> Option<(InputEvent, usize)> {
927    // OSC sequences start with ESC ] (0x1b 0x5d)
928    if buf.get(0) != Some(&0x1b) || buf.get(1) != Some(&b']') {
929        return None;
930    }
931    let mut i = 2;
932    while i < buf.len() {
933        match buf.get(i) {
934            Some(&0x07) => {
935                // BEL terminator
936                let payload = buf.get(2..i).unwrap_or_default().to_vec();
937                return Some((InputEvent::OperatingSystemCommand(payload), i + 1));
938            },
939            Some(&0x1b) => {
940                // Possible ST terminator (ESC \)
941                if buf.get(i + 1) == Some(&b'\\') {
942                    let payload = buf.get(2..i).unwrap_or_default().to_vec();
943                    return Some((InputEvent::OperatingSystemCommand(payload), i + 2));
944                }
945                // Bare ESC inside OSC — malformed, but don't consume further
946                return None;
947            },
948            Some(_) => {
949                i += 1;
950            },
951            None => {
952                // Should not happen since i < buf.len(), but handle gracefully
953                return None;
954            },
955        }
956    }
957    None // incomplete — no terminator found yet
958}
959
960#[derive(Debug, Clone, Copy, PartialEq, Eq)]
961enum InputState {
962    Normal,
963    EscapeMaybeAlt,
964    Pasting(usize),
965}
966
967#[derive(Debug)]
968pub struct InputParser {
969    key_map: KeyMap<InputEvent>,
970    buf: ReadBuffer,
971    state: InputState,
972}
973
974#[cfg(windows)]
975mod windows {
976    use super::*;
977    use std;
978    use winapi::um::wincon::{
979        INPUT_RECORD, KEY_EVENT, KEY_EVENT_RECORD, MOUSE_EVENT, MOUSE_EVENT_RECORD,
980        WINDOW_BUFFER_SIZE_EVENT, WINDOW_BUFFER_SIZE_RECORD,
981    };
982    use winapi::um::winuser;
983
984    fn modifiers_from_ctrl_key_state(state: u32) -> Modifiers {
985        use winapi::um::wincon::*;
986
987        let mut mods = Modifiers::NONE;
988
989        if (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0 {
990            mods |= Modifiers::ALT;
991        }
992
993        if (state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0 {
994            mods |= Modifiers::CTRL;
995        }
996
997        if (state & SHIFT_PRESSED) != 0 {
998            mods |= Modifiers::SHIFT;
999        }
1000
1001        mods
1002    }
1003
1004    impl InputParser {
1005        fn decode_key_record<F: FnMut(InputEvent)>(
1006            &mut self,
1007            event: &KEY_EVENT_RECORD,
1008            callback: &mut F,
1009        ) {
1010            if event.bKeyDown == 0 {
1011                return;
1012            }
1013
1014            let key_code = match std::char::from_u32(*unsafe { event.uChar.UnicodeChar() } as u32) {
1015                Some(unicode) if unicode > '\x00' => {
1016                    let mut buf = [0u8; 4];
1017                    self.buf
1018                        .extend_with(unicode.encode_utf8(&mut buf).as_bytes());
1019                    self.process_bytes(callback, true);
1020                    return;
1021                },
1022                _ => match event.wVirtualKeyCode as i32 {
1023                    winuser::VK_CANCEL => KeyCode::Cancel,
1024                    winuser::VK_BACK => KeyCode::Backspace,
1025                    winuser::VK_TAB => KeyCode::Tab,
1026                    winuser::VK_CLEAR => KeyCode::Clear,
1027                    winuser::VK_RETURN => KeyCode::Enter,
1028                    winuser::VK_SHIFT => KeyCode::Shift,
1029                    winuser::VK_CONTROL => KeyCode::Control,
1030                    winuser::VK_MENU => KeyCode::Menu,
1031                    winuser::VK_PAUSE => KeyCode::Pause,
1032                    winuser::VK_CAPITAL => KeyCode::CapsLock,
1033                    winuser::VK_ESCAPE => KeyCode::Escape,
1034                    winuser::VK_PRIOR => KeyCode::PageUp,
1035                    winuser::VK_NEXT => KeyCode::PageDown,
1036                    winuser::VK_END => KeyCode::End,
1037                    winuser::VK_HOME => KeyCode::Home,
1038                    winuser::VK_LEFT => KeyCode::LeftArrow,
1039                    winuser::VK_RIGHT => KeyCode::RightArrow,
1040                    winuser::VK_UP => KeyCode::UpArrow,
1041                    winuser::VK_DOWN => KeyCode::DownArrow,
1042                    winuser::VK_SELECT => KeyCode::Select,
1043                    winuser::VK_PRINT => KeyCode::Print,
1044                    winuser::VK_EXECUTE => KeyCode::Execute,
1045                    winuser::VK_SNAPSHOT => KeyCode::PrintScreen,
1046                    winuser::VK_INSERT => KeyCode::Insert,
1047                    winuser::VK_DELETE => KeyCode::Delete,
1048                    winuser::VK_HELP => KeyCode::Help,
1049                    winuser::VK_LWIN => KeyCode::LeftWindows,
1050                    winuser::VK_RWIN => KeyCode::RightWindows,
1051                    winuser::VK_APPS => KeyCode::Applications,
1052                    winuser::VK_SLEEP => KeyCode::Sleep,
1053                    winuser::VK_NUMPAD0 => KeyCode::Numpad0,
1054                    winuser::VK_NUMPAD1 => KeyCode::Numpad1,
1055                    winuser::VK_NUMPAD2 => KeyCode::Numpad2,
1056                    winuser::VK_NUMPAD3 => KeyCode::Numpad3,
1057                    winuser::VK_NUMPAD4 => KeyCode::Numpad4,
1058                    winuser::VK_NUMPAD5 => KeyCode::Numpad5,
1059                    winuser::VK_NUMPAD6 => KeyCode::Numpad6,
1060                    winuser::VK_NUMPAD7 => KeyCode::Numpad7,
1061                    winuser::VK_NUMPAD8 => KeyCode::Numpad8,
1062                    winuser::VK_NUMPAD9 => KeyCode::Numpad9,
1063                    winuser::VK_MULTIPLY => KeyCode::Multiply,
1064                    winuser::VK_ADD => KeyCode::Add,
1065                    winuser::VK_SEPARATOR => KeyCode::Separator,
1066                    winuser::VK_SUBTRACT => KeyCode::Subtract,
1067                    winuser::VK_DECIMAL => KeyCode::Decimal,
1068                    winuser::VK_DIVIDE => KeyCode::Divide,
1069                    winuser::VK_F1 => KeyCode::Function(1),
1070                    winuser::VK_F2 => KeyCode::Function(2),
1071                    winuser::VK_F3 => KeyCode::Function(3),
1072                    winuser::VK_F4 => KeyCode::Function(4),
1073                    winuser::VK_F5 => KeyCode::Function(5),
1074                    winuser::VK_F6 => KeyCode::Function(6),
1075                    winuser::VK_F7 => KeyCode::Function(7),
1076                    winuser::VK_F8 => KeyCode::Function(8),
1077                    winuser::VK_F9 => KeyCode::Function(9),
1078                    winuser::VK_F10 => KeyCode::Function(10),
1079                    winuser::VK_F11 => KeyCode::Function(11),
1080                    winuser::VK_F12 => KeyCode::Function(12),
1081                    winuser::VK_F13 => KeyCode::Function(13),
1082                    winuser::VK_F14 => KeyCode::Function(14),
1083                    winuser::VK_F15 => KeyCode::Function(15),
1084                    winuser::VK_F16 => KeyCode::Function(16),
1085                    winuser::VK_F17 => KeyCode::Function(17),
1086                    winuser::VK_F18 => KeyCode::Function(18),
1087                    winuser::VK_F19 => KeyCode::Function(19),
1088                    winuser::VK_F20 => KeyCode::Function(20),
1089                    winuser::VK_F21 => KeyCode::Function(21),
1090                    winuser::VK_F22 => KeyCode::Function(22),
1091                    winuser::VK_F23 => KeyCode::Function(23),
1092                    winuser::VK_F24 => KeyCode::Function(24),
1093                    winuser::VK_NUMLOCK => KeyCode::NumLock,
1094                    winuser::VK_SCROLL => KeyCode::ScrollLock,
1095                    winuser::VK_LSHIFT => KeyCode::LeftShift,
1096                    winuser::VK_RSHIFT => KeyCode::RightShift,
1097                    winuser::VK_LCONTROL => KeyCode::LeftControl,
1098                    winuser::VK_RCONTROL => KeyCode::RightControl,
1099                    winuser::VK_LMENU => KeyCode::LeftMenu,
1100                    winuser::VK_RMENU => KeyCode::RightMenu,
1101                    winuser::VK_BROWSER_BACK => KeyCode::BrowserBack,
1102                    winuser::VK_BROWSER_FORWARD => KeyCode::BrowserForward,
1103                    winuser::VK_BROWSER_REFRESH => KeyCode::BrowserRefresh,
1104                    winuser::VK_BROWSER_STOP => KeyCode::BrowserStop,
1105                    winuser::VK_BROWSER_SEARCH => KeyCode::BrowserSearch,
1106                    winuser::VK_BROWSER_FAVORITES => KeyCode::BrowserFavorites,
1107                    winuser::VK_BROWSER_HOME => KeyCode::BrowserHome,
1108                    winuser::VK_VOLUME_MUTE => KeyCode::VolumeMute,
1109                    winuser::VK_VOLUME_DOWN => KeyCode::VolumeDown,
1110                    winuser::VK_VOLUME_UP => KeyCode::VolumeUp,
1111                    winuser::VK_MEDIA_NEXT_TRACK => KeyCode::MediaNextTrack,
1112                    winuser::VK_MEDIA_PREV_TRACK => KeyCode::MediaPrevTrack,
1113                    winuser::VK_MEDIA_STOP => KeyCode::MediaStop,
1114                    winuser::VK_MEDIA_PLAY_PAUSE => KeyCode::MediaPlayPause,
1115                    _ => return,
1116                },
1117            };
1118            let mut modifiers = modifiers_from_ctrl_key_state(event.dwControlKeyState);
1119
1120            let key_code = key_code.normalize_shift_to_upper_case(modifiers);
1121            if let KeyCode::Char(c) = key_code {
1122                if c.is_ascii_uppercase() {
1123                    modifiers.remove(Modifiers::SHIFT);
1124                }
1125            }
1126
1127            let input_event = InputEvent::Key(KeyEvent {
1128                key: key_code,
1129                modifiers,
1130            });
1131            for _ in 0..event.wRepeatCount {
1132                callback(input_event.clone());
1133            }
1134        }
1135
1136        fn decode_mouse_record<F: FnMut(InputEvent)>(
1137            &self,
1138            event: &MOUSE_EVENT_RECORD,
1139            callback: &mut F,
1140        ) {
1141            use winapi::um::wincon::*;
1142            let mut buttons = MouseButtons::NONE;
1143
1144            if (event.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) != 0 {
1145                buttons |= MouseButtons::LEFT;
1146            }
1147            if (event.dwButtonState & RIGHTMOST_BUTTON_PRESSED) != 0 {
1148                buttons |= MouseButtons::RIGHT;
1149            }
1150            if (event.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) != 0 {
1151                buttons |= MouseButtons::MIDDLE;
1152            }
1153
1154            let modifiers = modifiers_from_ctrl_key_state(event.dwControlKeyState);
1155
1156            if (event.dwEventFlags & MOUSE_WHEELED) != 0 {
1157                buttons |= MouseButtons::VERT_WHEEL;
1158                if (event.dwButtonState >> 8) != 0 {
1159                    buttons |= MouseButtons::WHEEL_POSITIVE;
1160                }
1161            } else if (event.dwEventFlags & MOUSE_HWHEELED) != 0 {
1162                buttons |= MouseButtons::HORZ_WHEEL;
1163                if (event.dwButtonState >> 8) != 0 {
1164                    buttons |= MouseButtons::WHEEL_POSITIVE;
1165                }
1166            }
1167
1168            let mouse = InputEvent::Mouse(MouseEvent {
1169                x: event.dwMousePosition.X as u16,
1170                y: event.dwMousePosition.Y as u16,
1171                mouse_buttons: buttons,
1172                modifiers,
1173            });
1174
1175            if (event.dwEventFlags & DOUBLE_CLICK) != 0 {
1176                callback(mouse.clone());
1177            }
1178            callback(mouse);
1179        }
1180
1181        fn decode_resize_record<F: FnMut(InputEvent)>(
1182            &self,
1183            event: &WINDOW_BUFFER_SIZE_RECORD,
1184            callback: &mut F,
1185        ) {
1186            callback(InputEvent::Resized {
1187                rows: event.dwSize.Y as usize,
1188                cols: event.dwSize.X as usize,
1189            });
1190        }
1191
1192        pub fn decode_input_records<F: FnMut(InputEvent)>(
1193            &mut self,
1194            records: &[INPUT_RECORD],
1195            callback: &mut F,
1196        ) {
1197            for record in records {
1198                match record.EventType {
1199                    KEY_EVENT => {
1200                        self.decode_key_record(unsafe { record.Event.KeyEvent() }, callback)
1201                    },
1202                    MOUSE_EVENT => {
1203                        self.decode_mouse_record(unsafe { record.Event.MouseEvent() }, callback)
1204                    },
1205                    WINDOW_BUFFER_SIZE_EVENT => self.decode_resize_record(
1206                        unsafe { record.Event.WindowBufferSizeEvent() },
1207                        callback,
1208                    ),
1209                    _ => {},
1210                }
1211            }
1212            self.process_bytes(callback, false);
1213        }
1214    }
1215}
1216
1217impl Default for InputParser {
1218    fn default() -> Self {
1219        Self::new()
1220    }
1221}
1222
1223impl InputParser {
1224    pub fn new() -> Self {
1225        Self {
1226            key_map: Self::build_basic_key_map(),
1227            buf: ReadBuffer::new(),
1228            state: InputState::Normal,
1229        }
1230    }
1231
1232    fn build_basic_key_map() -> KeyMap<InputEvent> {
1233        let mut map = KeyMap::new();
1234
1235        let modifier_combos = &[
1236            ("", Modifiers::NONE),
1237            (";1", Modifiers::NONE),
1238            (";2", Modifiers::SHIFT),
1239            (";3", Modifiers::ALT),
1240            (";4", Modifiers::ALT | Modifiers::SHIFT),
1241            (";5", Modifiers::CTRL),
1242            (";6", Modifiers::CTRL | Modifiers::SHIFT),
1243            (";7", Modifiers::CTRL | Modifiers::ALT),
1244            (";8", Modifiers::CTRL | Modifiers::ALT | Modifiers::SHIFT),
1245        ];
1246        let meta = Modifiers::ALT;
1247        let meta_modifier_combos = &[
1248            (";9", meta),
1249            (";10", meta | Modifiers::SHIFT),
1250            (";11", meta | Modifiers::ALT),
1251            (";12", meta | Modifiers::ALT | Modifiers::SHIFT),
1252            (";13", meta | Modifiers::CTRL),
1253            (";14", meta | Modifiers::CTRL | Modifiers::SHIFT),
1254            (";15", meta | Modifiers::CTRL | Modifiers::ALT),
1255            (
1256                ";16",
1257                meta | Modifiers::CTRL | Modifiers::ALT | Modifiers::SHIFT,
1258            ),
1259        ];
1260
1261        let modifier_combos_including_meta =
1262            || modifier_combos.iter().chain(meta_modifier_combos.iter());
1263
1264        for alpha in b'A'..=b'Z' {
1265            // Ctrl-[A..=Z] are sent as 1..=26
1266            let ctrl = [alpha & 0x1f];
1267            map.insert(
1268                &ctrl,
1269                InputEvent::Key(KeyEvent {
1270                    key: KeyCode::Char((alpha as char).to_ascii_lowercase()),
1271                    modifiers: Modifiers::CTRL,
1272                }),
1273            );
1274
1275            // ALT A-Z is often sent with a leading ESC
1276            let alt = [0x1b, alpha];
1277            map.insert(
1278                &alt,
1279                InputEvent::Key(KeyEvent {
1280                    key: KeyCode::Char(alpha as char),
1281                    modifiers: Modifiers::ALT,
1282                }),
1283            );
1284        }
1285
1286        for c in 0..=0x7fu8 {
1287            for (suffix, modifiers) in modifier_combos {
1288                // `CSI u` encodings for the ascii range;
1289                // see http://www.leonerd.org.uk/hacks/fixterms/
1290                let key = format!("\x1b[{}{}u", c, suffix);
1291                map.insert(
1292                    key,
1293                    InputEvent::Key(KeyEvent {
1294                        key: KeyCode::Char(c as char),
1295                        modifiers: *modifiers,
1296                    }),
1297                );
1298
1299                if !suffix.is_empty() {
1300                    // xterm modifyOtherKeys sequences
1301                    let key = format!("\x1b[27{};{}~", suffix, c);
1302                    map.insert(
1303                        key,
1304                        InputEvent::Key(KeyEvent {
1305                            key: match c {
1306                                8 | 0x7f => KeyCode::Backspace,
1307                                0x1b => KeyCode::Escape,
1308                                9 => KeyCode::Tab,
1309                                10 | 13 => KeyCode::Enter,
1310                                _ => KeyCode::Char(c as char),
1311                            },
1312                            modifiers: *modifiers,
1313                        }),
1314                    );
1315                }
1316            }
1317        }
1318
1319        // Common arrow keys
1320        for (keycode, dir) in &[
1321            (KeyCode::UpArrow, b'A'),
1322            (KeyCode::DownArrow, b'B'),
1323            (KeyCode::RightArrow, b'C'),
1324            (KeyCode::LeftArrow, b'D'),
1325            (KeyCode::Home, b'H'),
1326            (KeyCode::End, b'F'),
1327        ] {
1328            // Arrow keys in normal mode encoded using CSI
1329            let arrow = [0x1b, b'[', *dir];
1330            map.insert(
1331                &arrow,
1332                InputEvent::Key(KeyEvent {
1333                    key: *keycode,
1334                    modifiers: Modifiers::NONE,
1335                }),
1336            );
1337            for (suffix, modifiers) in modifier_combos_including_meta() {
1338                let key = format!("\x1b[1{}{}", suffix, *dir as char);
1339                map.insert(
1340                    key,
1341                    InputEvent::Key(KeyEvent {
1342                        key: *keycode,
1343                        modifiers: *modifiers,
1344                    }),
1345                );
1346            }
1347        }
1348        for &(keycode, dir) in &[
1349            (KeyCode::UpArrow, b'a'),
1350            (KeyCode::DownArrow, b'b'),
1351            (KeyCode::RightArrow, b'c'),
1352            (KeyCode::LeftArrow, b'd'),
1353        ] {
1354            // rxvt-specific modified arrows.
1355            for &(seq, mods) in &[
1356                ([0x1b, b'[', dir], Modifiers::SHIFT),
1357                ([0x1b, b'O', dir], Modifiers::CTRL),
1358            ] {
1359                map.insert(
1360                    &seq,
1361                    InputEvent::Key(KeyEvent {
1362                        key: keycode,
1363                        modifiers: mods,
1364                    }),
1365                );
1366            }
1367        }
1368
1369        for (keycode, dir) in &[
1370            (KeyCode::ApplicationUpArrow, b'A'),
1371            (KeyCode::ApplicationDownArrow, b'B'),
1372            (KeyCode::ApplicationRightArrow, b'C'),
1373            (KeyCode::ApplicationLeftArrow, b'D'),
1374        ] {
1375            // Arrow keys in application cursor mode encoded using SS3
1376            let app = [0x1b, b'O', *dir];
1377            map.insert(
1378                &app,
1379                InputEvent::Key(KeyEvent {
1380                    key: *keycode,
1381                    modifiers: Modifiers::NONE,
1382                }),
1383            );
1384            for (suffix, modifiers) in modifier_combos {
1385                let key = format!("\x1bO1{}{}", suffix, *dir as char);
1386                map.insert(
1387                    key,
1388                    InputEvent::Key(KeyEvent {
1389                        key: *keycode,
1390                        modifiers: *modifiers,
1391                    }),
1392                );
1393            }
1394        }
1395
1396        // Function keys 1-4 with no modifiers encoded using SS3
1397        for (keycode, c) in &[
1398            (KeyCode::Function(1), b'P'),
1399            (KeyCode::Function(2), b'Q'),
1400            (KeyCode::Function(3), b'R'),
1401            (KeyCode::Function(4), b'S'),
1402        ] {
1403            let key = [0x1b, b'O', *c];
1404            map.insert(
1405                &key,
1406                InputEvent::Key(KeyEvent {
1407                    key: *keycode,
1408                    modifiers: Modifiers::NONE,
1409                }),
1410            );
1411        }
1412
1413        // Function keys 1-4 with modifiers
1414        for (keycode, c) in &[
1415            (KeyCode::Function(1), b'P'),
1416            (KeyCode::Function(2), b'Q'),
1417            (KeyCode::Function(3), b'R'),
1418            (KeyCode::Function(4), b'S'),
1419        ] {
1420            for (suffix, modifiers) in modifier_combos_including_meta() {
1421                let key = format!("\x1b[1{suffix}{code}", code = *c as char, suffix = suffix);
1422                map.insert(
1423                    key,
1424                    InputEvent::Key(KeyEvent {
1425                        key: *keycode,
1426                        modifiers: *modifiers,
1427                    }),
1428                );
1429            }
1430        }
1431
1432        // Function keys with modifiers encoded using CSI.
1433        // http://aperiodic.net/phil/archives/Geekery/term-function-keys.html
1434        for (range, offset) in &[
1435            // F1-F5 encoded as 11-15
1436            (1..=5, 10),
1437            // F6-F10 encoded as 17-21
1438            (6..=10, 11),
1439            // F11-F14 encoded as 23-26
1440            (11..=14, 12),
1441            // F15-F16 encoded as 28-29
1442            (15..=16, 13),
1443            // F17-F20 encoded as 31-34
1444            (17..=20, 14),
1445        ] {
1446            for n in range.clone() {
1447                for (suffix, modifiers) in modifier_combos_including_meta() {
1448                    let key = format!("\x1b[{code}{suffix}~", code = n + offset, suffix = suffix);
1449                    map.insert(
1450                        key,
1451                        InputEvent::Key(KeyEvent {
1452                            key: KeyCode::Function(n),
1453                            modifiers: *modifiers,
1454                        }),
1455                    );
1456                }
1457            }
1458        }
1459
1460        for (keycode, c) in &[
1461            (KeyCode::Insert, b'2'),
1462            (KeyCode::Delete, b'3'),
1463            (KeyCode::Home, b'1'),
1464            (KeyCode::End, b'4'),
1465            (KeyCode::PageUp, b'5'),
1466            (KeyCode::PageDown, b'6'),
1467            // rxvt
1468            (KeyCode::Home, b'7'),
1469            (KeyCode::End, b'8'),
1470        ] {
1471            for (suffix, modifiers) in &[
1472                (b'~', Modifiers::NONE),
1473                (b'$', Modifiers::SHIFT),
1474                (b'^', Modifiers::CTRL),
1475                (b'@', Modifiers::SHIFT | Modifiers::CTRL),
1476            ] {
1477                let key = [0x1b, b'[', *c, *suffix];
1478                map.insert(
1479                    key,
1480                    InputEvent::Key(KeyEvent {
1481                        key: *keycode,
1482                        modifiers: *modifiers,
1483                    }),
1484                );
1485            }
1486        }
1487
1488        map.insert(
1489            &[0x7f],
1490            InputEvent::Key(KeyEvent {
1491                key: KeyCode::Backspace,
1492                modifiers: Modifiers::NONE,
1493            }),
1494        );
1495
1496        map.insert(
1497            &[0x8],
1498            InputEvent::Key(KeyEvent {
1499                key: KeyCode::Backspace,
1500                modifiers: Modifiers::NONE,
1501            }),
1502        );
1503
1504        map.insert(
1505            &[0x1b],
1506            InputEvent::Key(KeyEvent {
1507                key: KeyCode::Escape,
1508                modifiers: Modifiers::NONE,
1509            }),
1510        );
1511
1512        map.insert(
1513            &[b'\t'],
1514            InputEvent::Key(KeyEvent {
1515                key: KeyCode::Tab,
1516                modifiers: Modifiers::NONE,
1517            }),
1518        );
1519        map.insert(
1520            b"\x1b[Z",
1521            InputEvent::Key(KeyEvent {
1522                key: KeyCode::Tab,
1523                modifiers: Modifiers::SHIFT,
1524            }),
1525        );
1526
1527        map.insert(
1528            &[b'\r'],
1529            InputEvent::Key(KeyEvent {
1530                key: KeyCode::Enter,
1531                modifiers: Modifiers::NONE,
1532            }),
1533        );
1534        map.insert(
1535            &[b'\n'],
1536            InputEvent::Key(KeyEvent {
1537                key: KeyCode::Enter,
1538                modifiers: Modifiers::NONE,
1539            }),
1540        );
1541
1542        map.insert(
1543            b"\x1b[200~",
1544            InputEvent::Key(KeyEvent {
1545                key: KeyCode::InternalPasteStart,
1546                modifiers: Modifiers::NONE,
1547            }),
1548        );
1549        map.insert(
1550            b"\x1b[201~",
1551            InputEvent::Key(KeyEvent {
1552                key: KeyCode::InternalPasteEnd,
1553                modifiers: Modifiers::NONE,
1554            }),
1555        );
1556        map.insert(
1557            b"\x1b[",
1558            InputEvent::Key(KeyEvent {
1559                key: KeyCode::Char('['),
1560                modifiers: Modifiers::ALT,
1561            }),
1562        );
1563
1564        map
1565    }
1566
1567    /// Returns the first char from a str and the length of that char
1568    /// in *bytes*.
1569    fn first_char_and_len(s: &str) -> (char, usize) {
1570        let mut iter = s.chars();
1571        let c = iter.next().unwrap();
1572        (c, c.len_utf8())
1573    }
1574
1575    /// This is a horrible function to pull off the first unicode character
1576    /// from the sequence of bytes and return it and the remaining slice.
1577    fn decode_one_char(bytes: &[u8]) -> Option<(char, usize)> {
1578        let bytes = &bytes[..bytes.len().min(4)];
1579        match std::str::from_utf8(bytes) {
1580            Ok(s) => {
1581                let (c, len) = Self::first_char_and_len(s);
1582                Some((c, len))
1583            },
1584            Err(err) => {
1585                let (valid, _after_valid) = bytes.split_at(err.valid_up_to());
1586                if !valid.is_empty() {
1587                    let s = unsafe { std::str::from_utf8_unchecked(valid) };
1588                    let (c, len) = Self::first_char_and_len(s);
1589                    Some((c, len))
1590                } else {
1591                    None
1592                }
1593            },
1594        }
1595    }
1596
1597    fn dispatch_callback<F: FnMut(InputEvent)>(&mut self, mut callback: F, event: InputEvent) {
1598        match (self.state, &event) {
1599            (
1600                InputState::Normal,
1601                InputEvent::Key(KeyEvent {
1602                    key: KeyCode::InternalPasteStart,
1603                    ..
1604                }),
1605            ) => {
1606                self.state = InputState::Pasting(0);
1607            },
1608            (
1609                InputState::EscapeMaybeAlt,
1610                InputEvent::Key(KeyEvent {
1611                    key: KeyCode::InternalPasteStart,
1612                    ..
1613                }),
1614            ) => {
1615                // The prior ESC was not part of an ALT sequence, so emit
1616                // it before we start collecting for paste.
1617                callback(InputEvent::Key(KeyEvent {
1618                    key: KeyCode::Escape,
1619                    modifiers: Modifiers::NONE,
1620                }));
1621                self.state = InputState::Pasting(0);
1622            },
1623            (InputState::EscapeMaybeAlt, InputEvent::Key(KeyEvent { key, modifiers })) => {
1624                // Treat this as ALT-key
1625                let key = *key;
1626                let modifiers = *modifiers;
1627                self.state = InputState::Normal;
1628                callback(InputEvent::Key(KeyEvent {
1629                    key,
1630                    modifiers: modifiers | Modifiers::ALT,
1631                }));
1632            },
1633            (InputState::EscapeMaybeAlt, _) => {
1634                // The prior ESC was not part of an ALT sequence, so emit
1635                // both it and the current event
1636                callback(InputEvent::Key(KeyEvent {
1637                    key: KeyCode::Escape,
1638                    modifiers: Modifiers::NONE,
1639                }));
1640                callback(event);
1641            },
1642            (_, _) => callback(event),
1643        }
1644    }
1645
1646    /// If a parked ESC is currently held in `EscapeMaybeAlt`, emit it as a
1647    /// real `Esc` keystroke and return to `Normal`. Called from
1648    /// `process_bytes` before dispatching any structured sequence (SGR
1649    /// mouse, OSC, whitelisted CSI host-reply) that the upcoming bytes
1650    /// match — those sequences are autonomous host events and cannot be
1651    /// ALT-combined with the parked ESC, so the ESC must be flushed
1652    /// before the sequence is emitted.
1653    fn flush_parked_esc_if_held<F: FnMut(InputEvent)>(&mut self, callback: &mut F) {
1654        if self.state == InputState::EscapeMaybeAlt {
1655            callback(InputEvent::Key(KeyEvent {
1656                key: KeyCode::Escape,
1657                modifiers: Modifiers::NONE,
1658            }));
1659            self.state = InputState::Normal;
1660        }
1661    }
1662
1663    fn process_bytes<F: FnMut(InputEvent)>(&mut self, mut callback: F, maybe_more: bool) {
1664        while !self.buf.is_empty() {
1665            match self.state {
1666                InputState::Pasting(offset) => {
1667                    let end_paste = b"\x1b[201~";
1668                    if let Some(idx) = self.buf.find_subsequence(offset, end_paste) {
1669                        let pasted =
1670                            String::from_utf8_lossy(&self.buf.as_slice()[0..idx]).to_string();
1671                        self.buf.advance(pasted.len() + end_paste.len());
1672                        callback(InputEvent::Paste(pasted));
1673                        self.state = InputState::Normal;
1674                    } else {
1675                        self.state =
1676                            InputState::Pasting(self.buf.len().saturating_sub(end_paste.len()));
1677                        return;
1678                    }
1679                },
1680                InputState::EscapeMaybeAlt | InputState::Normal => {
1681                    // Structured terminal sequences — SGR mouse, OSC, whitelisted
1682                    // CSI host-replies — are autonomous host events and cannot be
1683                    // ALT-combined with a leading Esc keystroke. Run these checks
1684                    // in *both* Normal and EscapeMaybeAlt: if we're sitting on a
1685                    // parked ESC (EscapeMaybeAlt) and the upcoming bytes match one
1686                    // of these patterns, the ESC must be a real Esc keystroke, so
1687                    // flush it before dispatching the sequence. Otherwise a parked
1688                    // ESC immediately followed by `\x1b[<...M` (xterm flushes Esc
1689                    // alone, then a mouse motion in the next read) would dispatch
1690                    // as a spurious ALT+`[` because the keymap registers `\x1b[`
1691                    // as Alt+`[`.
1692                    if self.buf.as_slice().get(0) == Some(&b'\x1b') {
1693                        if let Some((event, len)) = parse_sgr_mouse(self.buf.as_slice()) {
1694                            self.flush_parked_esc_if_held(&mut callback);
1695                            self.buf.advance(len);
1696                            callback(event);
1697                            continue;
1698                        }
1699
1700                        // OSC sequence check — must come before the incomplete-SGR-mouse early return
1701                        if let Some((event, len)) = parse_osc(self.buf.as_slice()) {
1702                            self.flush_parked_esc_if_held(&mut callback);
1703                            self.buf.advance(len);
1704                            callback(event);
1705                            continue;
1706                        }
1707
1708                        // Incomplete OSC — buffer and wait for more data
1709                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b]") {
1710                            self.flush_parked_esc_if_held(&mut callback);
1711                            return;
1712                        }
1713
1714                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b[<") {
1715                            self.flush_parked_esc_if_held(&mut callback);
1716                            return;
1717                        }
1718
1719                        // CSI-based host-terminal report (pixel-dims reply,
1720                        // DECRPM, DSR, Primary-DA). Must come before the
1721                        // regular CSI key-mapping machinery, which would
1722                        // otherwise match "\x1b[" as an escape prefix and
1723                        // pass the bytes through as keyboard input.
1724                        if let Some((event, len)) = parse_csi_report(self.buf.as_slice()) {
1725                            self.flush_parked_esc_if_held(&mut callback);
1726                            self.buf.advance(len);
1727                            callback(event);
1728                            continue;
1729                        }
1730
1731                        // Incomplete CSI ?... report (DECRPM, DSR 997, etc.) —
1732                        // wait for more data so the report-classification path
1733                        // can match the full sequence rather than letting the
1734                        // keymap dispatch the leading bytes as separate keys.
1735                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b[?") {
1736                            self.flush_parked_esc_if_held(&mut callback);
1737                            return;
1738                        }
1739                    }
1740
1741                    match (
1742                        self.key_map.lookup(self.buf.as_slice(), maybe_more),
1743                        maybe_more,
1744                    ) {
1745                        // If we got an unambiguous ESC and we have more data to
1746                        // follow, then this is likely the Meta version of the
1747                        // following keypress.  Buffer up the escape key and
1748                        // consume it from the input.  dispatch_callback() will
1749                        // emit either the ESC or the ALT modified following key.
1750                        (
1751                            Found::Exact(
1752                                len,
1753                                InputEvent::Key(KeyEvent {
1754                                    key: KeyCode::Escape,
1755                                    modifiers: Modifiers::NONE,
1756                                }),
1757                            ),
1758                            _,
1759                        ) if self.state == InputState::Normal && self.buf.len() > len => {
1760                            self.state = InputState::EscapeMaybeAlt;
1761                            self.buf.advance(len);
1762                        },
1763                        (Found::Exact(len, event), _) | (Found::Ambiguous(len, event), false) => {
1764                            self.dispatch_callback(&mut callback, event.clone());
1765                            self.buf.advance(len);
1766                        },
1767                        (Found::Ambiguous(_, _), true) | (Found::NeedData, true) => {
1768                            // The keymap is signalling "this buffer
1769                            // could still grow into a registered key,
1770                            // give me more bytes." That verdict is
1771                            // wrong when the buffer already holds a
1772                            // structurally complete CSI sequence whose
1773                            // final byte isn't in the keymap — most
1774                            // importantly Kitty keyboard-protocol
1775                            // events `\x1b[<keycode>;<mods>u`, which
1776                            // never grow into anything the keymap
1777                            // knows. Returning here would wedge
1778                            // `self.buf` indefinitely, swallowing every
1779                            // host reply that arrives behind it (the
1780                            // OSC + DA1 bytes for a forwarded
1781                            // `OSC 11;?` query among them) and stalling
1782                            // host-color forwards until session exit.
1783                            //
1784                            // Both `Ambiguous(_, true)` and
1785                            // `NeedData(true)` reach this point in
1786                            // practice: for `\x1b[<digits>;<digits>u`
1787                            // the trie reports `Ambiguous(1, Escape)`
1788                            // (it has ESC alone as a match and ESC[…]
1789                            // as longer prefixes), so the fix must
1790                            // cover both verdicts.
1791                            //
1792                            // Skip past the unrecognised CSI without
1793                            // emitting an event; callers that need
1794                            // keyboard dispatch (kitty_parser, the
1795                            // separate `input_parser` instance fed the
1796                            // residue from `StdinAnsiParser`) see the
1797                            // same bytes via `strip_replies`, which
1798                            // already treats unwhitelisted-final CSIs
1799                            // as `Malformed` and pushes them through.
1800                            if let Some(len) = complete_csi_len(self.buf.as_slice()) {
1801                                self.buf.advance(len);
1802                                continue;
1803                            }
1804                            return;
1805                        },
1806                        (Found::None, _) | (Found::NeedData, false) => {
1807                            // No pre-defined key, so pull out a unicode character
1808                            if let Some((c, len)) = Self::decode_one_char(self.buf.as_slice()) {
1809                                self.buf.advance(len);
1810                                self.dispatch_callback(
1811                                    &mut callback,
1812                                    InputEvent::Key(KeyEvent {
1813                                        key: KeyCode::Char(c),
1814                                        modifiers: Modifiers::NONE,
1815                                    }),
1816                                );
1817                            } else {
1818                                // We need more data to recognize the input, so
1819                                // yield the remainder of the slice
1820                                return;
1821                            }
1822                        },
1823                    }
1824                },
1825            }
1826        }
1827    }
1828
1829    /// Push a sequence of bytes into the parser.
1830    /// Each time input is recognized, the provided `callback` will be passed
1831    /// the decoded `InputEvent`.
1832    /// If not enough data are available to fully decode a sequence, the
1833    /// remaining data will be buffered until the next call.
1834    /// The `maybe_more` flag controls how ambiguous partial sequences are
1835    /// handled. The intent is that `maybe_more` should be set to true if
1836    /// you believe that you will be able to provide more data momentarily.
1837    /// This will cause the parser to defer judgement on partial prefix
1838    /// matches. You should attempt to read and pass the new data in
1839    /// immediately afterwards. If you have attempted a read and no data is
1840    /// immediately available, you should follow up with a call to parse
1841    /// with an empty slice and `maybe_more=false` to allow the partial
1842    /// data to be recognized and processed.
1843    pub fn parse<F: FnMut(InputEvent)>(&mut self, bytes: &[u8], callback: F, maybe_more: bool) {
1844        self.buf.extend_with(bytes);
1845        self.process_bytes(callback, maybe_more);
1846    }
1847
1848    pub fn parse_as_vec(&mut self, bytes: &[u8], maybe_more: bool) -> Vec<InputEvent> {
1849        let mut result = Vec::new();
1850        self.parse(bytes, |event| result.push(event), maybe_more);
1851        result
1852    }
1853
1854    #[cfg(windows)]
1855    pub fn decode_input_records_as_vec(
1856        &mut self,
1857        records: &[winapi::um::wincon::INPUT_RECORD],
1858    ) -> Vec<InputEvent> {
1859        let mut result = Vec::new();
1860        self.decode_input_records(records, &mut |event| result.push(event));
1861        result
1862    }
1863}
1864
1865#[cfg(test)]
1866mod test {
1867    use super::*;
1868
1869    const NO_MORE: bool = false;
1870    const MAYBE_MORE: bool = true;
1871
1872    #[test]
1873    fn simple() {
1874        let mut p = InputParser::new();
1875        let inputs = p.parse_as_vec(b"hello", NO_MORE);
1876        assert_eq!(
1877            vec![
1878                InputEvent::Key(KeyEvent {
1879                    modifiers: Modifiers::NONE,
1880                    key: KeyCode::Char('h'),
1881                }),
1882                InputEvent::Key(KeyEvent {
1883                    modifiers: Modifiers::NONE,
1884                    key: KeyCode::Char('e'),
1885                }),
1886                InputEvent::Key(KeyEvent {
1887                    modifiers: Modifiers::NONE,
1888                    key: KeyCode::Char('l'),
1889                }),
1890                InputEvent::Key(KeyEvent {
1891                    modifiers: Modifiers::NONE,
1892                    key: KeyCode::Char('l'),
1893                }),
1894                InputEvent::Key(KeyEvent {
1895                    modifiers: Modifiers::NONE,
1896                    key: KeyCode::Char('o'),
1897                }),
1898            ],
1899            inputs
1900        );
1901    }
1902
1903    #[test]
1904    fn control_characters() {
1905        let mut p = InputParser::new();
1906        let inputs = p.parse_as_vec(b"\x03\x1bJ\x7f", NO_MORE);
1907        assert_eq!(
1908            vec![
1909                InputEvent::Key(KeyEvent {
1910                    modifiers: Modifiers::CTRL,
1911                    key: KeyCode::Char('c'),
1912                }),
1913                InputEvent::Key(KeyEvent {
1914                    modifiers: Modifiers::ALT,
1915                    key: KeyCode::Char('J'),
1916                }),
1917                InputEvent::Key(KeyEvent {
1918                    modifiers: Modifiers::NONE,
1919                    key: KeyCode::Backspace,
1920                }),
1921            ],
1922            inputs
1923        );
1924    }
1925
1926    #[test]
1927    fn arrow_keys() {
1928        let mut p = InputParser::new();
1929        let inputs = p.parse_as_vec(b"\x1bOA\x1bOB\x1bOC\x1bOD", NO_MORE);
1930        assert_eq!(
1931            vec![
1932                InputEvent::Key(KeyEvent {
1933                    modifiers: Modifiers::NONE,
1934                    key: KeyCode::ApplicationUpArrow,
1935                }),
1936                InputEvent::Key(KeyEvent {
1937                    modifiers: Modifiers::NONE,
1938                    key: KeyCode::ApplicationDownArrow,
1939                }),
1940                InputEvent::Key(KeyEvent {
1941                    modifiers: Modifiers::NONE,
1942                    key: KeyCode::ApplicationRightArrow,
1943                }),
1944                InputEvent::Key(KeyEvent {
1945                    modifiers: Modifiers::NONE,
1946                    key: KeyCode::ApplicationLeftArrow,
1947                }),
1948            ],
1949            inputs
1950        );
1951    }
1952
1953    #[test]
1954    fn partial() {
1955        let mut p = InputParser::new();
1956        let mut inputs = Vec::new();
1957        // Fragment this F-key sequence across two different pushes
1958        p.parse(b"\x1b[11", |evt| inputs.push(evt), true);
1959        p.parse(b"~", |evt| inputs.push(evt), true);
1960        // make sure we recognize it as just the F-key
1961        assert_eq!(
1962            vec![InputEvent::Key(KeyEvent {
1963                modifiers: Modifiers::NONE,
1964                key: KeyCode::Function(1),
1965            })],
1966            inputs
1967        );
1968    }
1969
1970    #[test]
1971    fn partial_ambig() {
1972        let mut p = InputParser::new();
1973
1974        assert_eq!(
1975            vec![InputEvent::Key(KeyEvent {
1976                key: KeyCode::Escape,
1977                modifiers: Modifiers::NONE,
1978            })],
1979            p.parse_as_vec(b"\x1b", false)
1980        );
1981
1982        let mut inputs = Vec::new();
1983        // An incomplete F-key sequence fragmented across two different pushes
1984        p.parse(b"\x1b[11", |evt| inputs.push(evt), MAYBE_MORE);
1985        p.parse(b"", |evt| inputs.push(evt), NO_MORE);
1986        // since we finish with maybe_more false (NO_MORE), the results should be the longest matching
1987        // parts of said f-key sequence
1988        assert_eq!(
1989            vec![
1990                InputEvent::Key(KeyEvent {
1991                    modifiers: Modifiers::ALT,
1992                    key: KeyCode::Char('['),
1993                }),
1994                InputEvent::Key(KeyEvent {
1995                    modifiers: Modifiers::NONE,
1996                    key: KeyCode::Char('1'),
1997                }),
1998                InputEvent::Key(KeyEvent {
1999                    modifiers: Modifiers::NONE,
2000                    key: KeyCode::Char('1'),
2001                }),
2002            ],
2003            inputs
2004        );
2005    }
2006
2007    #[test]
2008    fn partial_mouse() {
2009        let mut p = InputParser::new();
2010        let mut inputs = Vec::new();
2011        // Fragment this mouse sequence across two different pushes
2012        p.parse(b"\x1b[<0;0;0", |evt| inputs.push(evt), true);
2013        p.parse(b"M", |evt| inputs.push(evt), true);
2014        // make sure we recognize it as just the mouse event
2015        assert_eq!(
2016            vec![InputEvent::Mouse(MouseEvent {
2017                x: 0,
2018                y: 0,
2019                mouse_buttons: MouseButtons::LEFT,
2020                modifiers: Modifiers::NONE,
2021            })],
2022            inputs
2023        );
2024    }
2025
2026    #[test]
2027    fn partial_mouse_ambig() {
2028        let mut p = InputParser::new();
2029        let mut inputs = Vec::new();
2030        // Fragment this mouse sequence across two different pushes
2031        p.parse(b"\x1b[<", |evt| inputs.push(evt), MAYBE_MORE);
2032        p.parse(b"0;0;0", |evt| inputs.push(evt), NO_MORE);
2033        // since we finish with maybe_more false (NO_MORE), the results should be the longest matching
2034        // parts of said mouse sequence
2035        assert_eq!(
2036            vec![
2037                InputEvent::Key(KeyEvent {
2038                    modifiers: Modifiers::ALT,
2039                    key: KeyCode::Char('['),
2040                }),
2041                InputEvent::Key(KeyEvent {
2042                    modifiers: Modifiers::NONE,
2043                    key: KeyCode::Char('<'),
2044                }),
2045                InputEvent::Key(KeyEvent {
2046                    modifiers: Modifiers::NONE,
2047                    key: KeyCode::Char('0'),
2048                }),
2049                InputEvent::Key(KeyEvent {
2050                    modifiers: Modifiers::NONE,
2051                    key: KeyCode::Char(';'),
2052                }),
2053                InputEvent::Key(KeyEvent {
2054                    modifiers: Modifiers::NONE,
2055                    key: KeyCode::Char('0'),
2056                }),
2057                InputEvent::Key(KeyEvent {
2058                    modifiers: Modifiers::NONE,
2059                    key: KeyCode::Char(';'),
2060                }),
2061                InputEvent::Key(KeyEvent {
2062                    modifiers: Modifiers::NONE,
2063                    key: KeyCode::Char('0'),
2064                }),
2065            ],
2066            inputs
2067        );
2068    }
2069
2070    #[test]
2071    fn alt_left_bracket() {
2072        // tests that `Alt` + `[` is recognized as a single
2073        // event rather than two events (one `Esc` the second `Char('[')`)
2074        let mut p = InputParser::new();
2075
2076        let mut inputs = Vec::new();
2077        p.parse(b"\x1b[", |evt| inputs.push(evt), false);
2078
2079        assert_eq!(
2080            vec![InputEvent::Key(KeyEvent {
2081                modifiers: Modifiers::ALT,
2082                key: KeyCode::Char('['),
2083            }),],
2084            inputs
2085        );
2086    }
2087
2088    #[test]
2089    fn modify_other_keys_parse() {
2090        let mut p = InputParser::new();
2091        let inputs = p.parse_as_vec(
2092            b"\x1b[27;5;13~\x1b[27;5;9~\x1b[27;6;8~\x1b[27;2;127~\x1b[27;6;27~",
2093            NO_MORE,
2094        );
2095        assert_eq!(
2096            vec![
2097                InputEvent::Key(KeyEvent {
2098                    key: KeyCode::Enter,
2099                    modifiers: Modifiers::CTRL,
2100                }),
2101                InputEvent::Key(KeyEvent {
2102                    key: KeyCode::Tab,
2103                    modifiers: Modifiers::CTRL,
2104                }),
2105                InputEvent::Key(KeyEvent {
2106                    key: KeyCode::Backspace,
2107                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2108                }),
2109                InputEvent::Key(KeyEvent {
2110                    key: KeyCode::Backspace,
2111                    modifiers: Modifiers::SHIFT,
2112                }),
2113                InputEvent::Key(KeyEvent {
2114                    key: KeyCode::Escape,
2115                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2116                }),
2117            ],
2118            inputs
2119        );
2120    }
2121
2122    #[test]
2123    fn modify_other_keys_encode() {
2124        let mode = KeyCodeEncodeModes {
2125            encoding: KeyboardEncoding::Xterm,
2126            newline_mode: false,
2127            application_cursor_keys: false,
2128            modify_other_keys: None,
2129        };
2130        let mode_1 = KeyCodeEncodeModes {
2131            encoding: KeyboardEncoding::Xterm,
2132            newline_mode: false,
2133            application_cursor_keys: false,
2134            modify_other_keys: Some(1),
2135        };
2136        let mode_2 = KeyCodeEncodeModes {
2137            encoding: KeyboardEncoding::Xterm,
2138            newline_mode: false,
2139            application_cursor_keys: false,
2140            modify_other_keys: Some(2),
2141        };
2142
2143        assert_eq!(
2144            KeyCode::Enter.encode(Modifiers::CTRL, mode, true).unwrap(),
2145            "\r".to_string()
2146        );
2147        assert_eq!(
2148            KeyCode::Enter
2149                .encode(Modifiers::CTRL, mode_1, true)
2150                .unwrap(),
2151            "\x1b[27;5;13~".to_string()
2152        );
2153        assert_eq!(
2154            KeyCode::Enter
2155                .encode(Modifiers::CTRL | Modifiers::SHIFT, mode_1, true)
2156                .unwrap(),
2157            "\x1b[27;6;13~".to_string()
2158        );
2159
2160        // This case is not conformant with xterm!
2161        // xterm just returns tab for CTRL-Tab when modify_other_keys
2162        // is not set.
2163        assert_eq!(
2164            KeyCode::Tab.encode(Modifiers::CTRL, mode, true).unwrap(),
2165            "\x1b[9;5u".to_string()
2166        );
2167        assert_eq!(
2168            KeyCode::Tab.encode(Modifiers::CTRL, mode_1, true).unwrap(),
2169            "\x1b[27;5;9~".to_string()
2170        );
2171        assert_eq!(
2172            KeyCode::Tab
2173                .encode(Modifiers::CTRL | Modifiers::SHIFT, mode_1, true)
2174                .unwrap(),
2175            "\x1b[27;6;9~".to_string()
2176        );
2177
2178        assert_eq!(
2179            KeyCode::Char('c')
2180                .encode(Modifiers::CTRL, mode, true)
2181                .unwrap(),
2182            "\x03".to_string()
2183        );
2184        assert_eq!(
2185            KeyCode::Char('c')
2186                .encode(Modifiers::CTRL, mode_1, true)
2187                .unwrap(),
2188            "\x03".to_string()
2189        );
2190        assert_eq!(
2191            KeyCode::Char('c')
2192                .encode(Modifiers::CTRL, mode_2, true)
2193                .unwrap(),
2194            "\x1b[27;5;99~".to_string()
2195        );
2196
2197        assert_eq!(
2198            KeyCode::Char('1')
2199                .encode(Modifiers::CTRL, mode, true)
2200                .unwrap(),
2201            "1".to_string()
2202        );
2203        assert_eq!(
2204            KeyCode::Char('1')
2205                .encode(Modifiers::CTRL, mode_2, true)
2206                .unwrap(),
2207            "\x1b[27;5;49~".to_string()
2208        );
2209
2210        assert_eq!(
2211            KeyCode::Char(',')
2212                .encode(Modifiers::CTRL, mode, true)
2213                .unwrap(),
2214            ",".to_string()
2215        );
2216        assert_eq!(
2217            KeyCode::Char(',')
2218                .encode(Modifiers::CTRL, mode_2, true)
2219                .unwrap(),
2220            "\x1b[27;5;44~".to_string()
2221        );
2222    }
2223
2224    #[test]
2225    fn encode_issue_892() {
2226        let mode = KeyCodeEncodeModes {
2227            encoding: KeyboardEncoding::Xterm,
2228            newline_mode: false,
2229            application_cursor_keys: false,
2230            modify_other_keys: None,
2231        };
2232
2233        assert_eq!(
2234            KeyCode::LeftArrow
2235                .encode(Modifiers::NONE, mode, true)
2236                .unwrap(),
2237            "\x1b[D".to_string()
2238        );
2239        assert_eq!(
2240            KeyCode::LeftArrow
2241                .encode(Modifiers::ALT, mode, true)
2242                .unwrap(),
2243            "\x1b[1;3D".to_string()
2244        );
2245        assert_eq!(
2246            KeyCode::Home.encode(Modifiers::NONE, mode, true).unwrap(),
2247            "\x1b[H".to_string()
2248        );
2249        assert_eq!(
2250            KeyCode::Home.encode(Modifiers::ALT, mode, true).unwrap(),
2251            "\x1b[1;3H".to_string()
2252        );
2253        assert_eq!(
2254            KeyCode::End.encode(Modifiers::NONE, mode, true).unwrap(),
2255            "\x1b[F".to_string()
2256        );
2257        assert_eq!(
2258            KeyCode::End.encode(Modifiers::ALT, mode, true).unwrap(),
2259            "\x1b[1;3F".to_string()
2260        );
2261        assert_eq!(
2262            KeyCode::Tab.encode(Modifiers::ALT, mode, true).unwrap(),
2263            "\x1b\t".to_string()
2264        );
2265        assert_eq!(
2266            KeyCode::PageUp.encode(Modifiers::ALT, mode, true).unwrap(),
2267            "\x1b[5;3~".to_string()
2268        );
2269        assert_eq!(
2270            KeyCode::Function(1)
2271                .encode(Modifiers::NONE, mode, true)
2272                .unwrap(),
2273            "\x1bOP".to_string()
2274        );
2275    }
2276
2277    #[test]
2278    fn partial_bracketed_paste() {
2279        let mut p = InputParser::new();
2280
2281        let input = b"\x1b[200~1234";
2282        let input2 = b"5678\x1b[201~";
2283
2284        let mut inputs = vec![];
2285
2286        p.parse(input, |e| inputs.push(e), false);
2287        p.parse(input2, |e| inputs.push(e), false);
2288
2289        assert_eq!(vec![InputEvent::Paste("12345678".to_owned())], inputs)
2290    }
2291
2292    #[test]
2293    fn mouse_horizontal_scroll() {
2294        let mut p = InputParser::new();
2295
2296        let input = b"\x1b[<66;42;12M\x1b[<67;42;12M";
2297        let res = p.parse_as_vec(input, MAYBE_MORE);
2298
2299        assert_eq!(
2300            vec![
2301                InputEvent::Mouse(MouseEvent {
2302                    x: 42,
2303                    y: 12,
2304                    mouse_buttons: MouseButtons::HORZ_WHEEL | MouseButtons::WHEEL_POSITIVE,
2305                    modifiers: Modifiers::NONE,
2306                }),
2307                InputEvent::Mouse(MouseEvent {
2308                    x: 42,
2309                    y: 12,
2310                    mouse_buttons: MouseButtons::HORZ_WHEEL,
2311                    modifiers: Modifiers::NONE,
2312                })
2313            ],
2314            res
2315        );
2316    }
2317
2318    #[test]
2319    fn encode_issue_3478_xterm() {
2320        let mode = KeyCodeEncodeModes {
2321            encoding: KeyboardEncoding::Xterm,
2322            newline_mode: false,
2323            application_cursor_keys: false,
2324            modify_other_keys: None,
2325        };
2326
2327        assert_eq!(
2328            KeyCode::Numpad0
2329                .encode(Modifiers::NONE, mode, true)
2330                .unwrap(),
2331            "\u{1b}[2~".to_string()
2332        );
2333        assert_eq!(
2334            KeyCode::Numpad0
2335                .encode(Modifiers::SHIFT, mode, true)
2336                .unwrap(),
2337            "\u{1b}[2;2~".to_string()
2338        );
2339
2340        assert_eq!(
2341            KeyCode::Numpad1
2342                .encode(Modifiers::NONE, mode, true)
2343                .unwrap(),
2344            "\u{1b}[F".to_string()
2345        );
2346        assert_eq!(
2347            KeyCode::Numpad1
2348                .encode(Modifiers::NONE | Modifiers::SHIFT, mode, true)
2349                .unwrap(),
2350            "\u{1b}[1;2F".to_string()
2351        );
2352    }
2353
2354    #[test]
2355    fn encode_tab_with_modifiers() {
2356        let mode = KeyCodeEncodeModes {
2357            encoding: KeyboardEncoding::Xterm,
2358            newline_mode: false,
2359            application_cursor_keys: false,
2360            modify_other_keys: None,
2361        };
2362
2363        let mods_to_result = [
2364            (Modifiers::SHIFT, "\u{1b}[Z"),
2365            (Modifiers::SHIFT | Modifiers::LEFT_SHIFT, "\u{1b}[Z"),
2366            (Modifiers::SHIFT | Modifiers::RIGHT_SHIFT, "\u{1b}[Z"),
2367            (Modifiers::CTRL, "\u{1b}[9;5u"),
2368            (Modifiers::CTRL | Modifiers::LEFT_CTRL, "\u{1b}[9;5u"),
2369            (Modifiers::CTRL | Modifiers::RIGHT_CTRL, "\u{1b}[9;5u"),
2370            (
2371                Modifiers::SHIFT | Modifiers::CTRL | Modifiers::LEFT_CTRL | Modifiers::LEFT_SHIFT,
2372                "\u{1b}[1;5Z",
2373            ),
2374        ];
2375        for (mods, result) in mods_to_result {
2376            assert_eq!(
2377                KeyCode::Tab.encode(mods, mode, true).unwrap(),
2378                result,
2379                "{:?}",
2380                mods
2381            );
2382        }
2383    }
2384
2385    #[test]
2386    fn mouse_button1_press() {
2387        let mut p = InputParser::new();
2388        let res = p.parse_as_vec(b"\x1b[<0;42;12M", true);
2389        assert_eq!(
2390            res,
2391            vec![InputEvent::Mouse(MouseEvent {
2392                x: 42,
2393                y: 12,
2394                mouse_buttons: MouseButtons::LEFT,
2395                modifiers: Modifiers::NONE,
2396            })]
2397        );
2398    }
2399
2400    #[test]
2401    fn mouse_button1_release() {
2402        let mut p = InputParser::new();
2403        let res = p.parse_as_vec(b"\x1b[<0;42;12m", true);
2404        assert_eq!(
2405            res,
2406            vec![InputEvent::Mouse(MouseEvent {
2407                x: 42,
2408                y: 12,
2409                mouse_buttons: MouseButtons::NONE,
2410                modifiers: Modifiers::NONE,
2411            })]
2412        );
2413    }
2414
2415    #[test]
2416    fn mouse_button3_with_shift() {
2417        let mut p = InputParser::new();
2418        // button 2 (right) = 2, SHIFT adds 4 to p0 -> 6
2419        let res = p.parse_as_vec(b"\x1b[<6;10;20M", true);
2420        assert_eq!(
2421            res,
2422            vec![InputEvent::Mouse(MouseEvent {
2423                x: 10,
2424                y: 20,
2425                mouse_buttons: MouseButtons::RIGHT,
2426                modifiers: Modifiers::SHIFT,
2427            })]
2428        );
2429    }
2430
2431    #[test]
2432    fn mouse_drag() {
2433        let mut p = InputParser::new();
2434        // button1 drag = 32
2435        let res = p.parse_as_vec(b"\x1b[<32;5;5M", true);
2436        assert_eq!(
2437            res,
2438            vec![InputEvent::Mouse(MouseEvent {
2439                x: 5,
2440                y: 5,
2441                mouse_buttons: MouseButtons::LEFT,
2442                modifiers: Modifiers::NONE,
2443            })]
2444        );
2445    }
2446
2447    #[test]
2448    fn mouse_vertical_scroll_up() {
2449        let mut p = InputParser::new();
2450        // button4 press = 64
2451        let res = p.parse_as_vec(b"\x1b[<64;1;1M", true);
2452        assert_eq!(
2453            res,
2454            vec![InputEvent::Mouse(MouseEvent {
2455                x: 1,
2456                y: 1,
2457                mouse_buttons: MouseButtons::VERT_WHEEL | MouseButtons::WHEEL_POSITIVE,
2458                modifiers: Modifiers::NONE,
2459            })]
2460        );
2461    }
2462
2463    #[test]
2464    fn mouse_vertical_scroll_down() {
2465        let mut p = InputParser::new();
2466        // button5 press = 65
2467        let res = p.parse_as_vec(b"\x1b[<65;1;1M", true);
2468        assert_eq!(
2469            res,
2470            vec![InputEvent::Mouse(MouseEvent {
2471                x: 1,
2472                y: 1,
2473                mouse_buttons: MouseButtons::VERT_WHEEL,
2474                modifiers: Modifiers::NONE,
2475            })]
2476        );
2477    }
2478
2479    #[test]
2480    fn mouse_motion_no_buttons() {
2481        let mut p = InputParser::new();
2482        // motion with no buttons = 35
2483        let res = p.parse_as_vec(b"\x1b[<35;10;10M", true);
2484        assert_eq!(
2485            res,
2486            vec![InputEvent::Mouse(MouseEvent {
2487                x: 10,
2488                y: 10,
2489                mouse_buttons: MouseButtons::NONE,
2490                modifiers: Modifiers::NONE,
2491            })]
2492        );
2493    }
2494
2495    #[test]
2496    fn mouse_with_ctrl_alt() {
2497        let mut p = InputParser::new();
2498        // button1 press = 0, ALT=8, CTRL=16 -> 0+8+16=24
2499        let res = p.parse_as_vec(b"\x1b[<24;1;1M", true);
2500        assert_eq!(
2501            res,
2502            vec![InputEvent::Mouse(MouseEvent {
2503                x: 1,
2504                y: 1,
2505                mouse_buttons: MouseButtons::LEFT,
2506                modifiers: Modifiers::ALT | Modifiers::CTRL,
2507            })]
2508        );
2509    }
2510
2511    #[test]
2512    fn mouse_large_coordinates() {
2513        let mut p = InputParser::new();
2514        let res = p.parse_as_vec(b"\x1b[<0;999;999M", true);
2515        assert_eq!(
2516            res,
2517            vec![InputEvent::Mouse(MouseEvent {
2518                x: 999,
2519                y: 999,
2520                mouse_buttons: MouseButtons::LEFT,
2521                modifiers: Modifiers::NONE,
2522            })]
2523        );
2524    }
2525
2526    #[test]
2527    fn mouse_followed_by_key() {
2528        let mut p = InputParser::new();
2529        let res = p.parse_as_vec(b"\x1b[<0;1;1Mhello", false);
2530        assert_eq!(res.len(), 6); // 1 mouse + 5 chars
2531        assert!(matches!(res[0], InputEvent::Mouse(_)));
2532        assert!(matches!(res[1], InputEvent::Key(_)));
2533    }
2534
2535    #[test]
2536    fn two_mouse_events_back_to_back() {
2537        let mut p = InputParser::new();
2538        let res = p.parse_as_vec(b"\x1b[<0;1;1M\x1b[<0;2;2M", true);
2539        assert_eq!(res.len(), 2);
2540    }
2541
2542    /// Regression for the xterm Esc-during-mouse-drag bug:
2543    /// xterm flushes a real Esc keypress as a single `\x1b` byte. If a mouse
2544    /// motion arrives in the next stdin read, upstream `StdinAnsiParser` may
2545    /// concatenate them into `\x1b\x1b[<...M`. Termwiz must parse this as
2546    /// two events (Esc then Mouse), not as Alt+`[` (which would happen if
2547    /// the keymap's `\x1b[`=Alt+`[` registration short-circuits the SGR
2548    /// mouse parser while in `EscapeMaybeAlt` state).
2549    #[test]
2550    fn esc_then_sgr_mouse_emits_esc_and_mouse() {
2551        let mut p = InputParser::new();
2552        let res = p.parse_as_vec(b"\x1b\x1b[<35;42;12M", MAYBE_MORE);
2553        assert_eq!(
2554            res,
2555            vec![
2556                InputEvent::Key(KeyEvent {
2557                    key: KeyCode::Escape,
2558                    modifiers: Modifiers::NONE,
2559                }),
2560                InputEvent::Mouse(MouseEvent {
2561                    x: 42,
2562                    y: 12,
2563                    mouse_buttons: MouseButtons::NONE,
2564                    modifiers: Modifiers::NONE,
2565                }),
2566            ]
2567        );
2568    }
2569
2570    /// Same regression but for the cross-`parse()` case where the parked
2571    /// ESC is in `EscapeMaybeAlt` state from a prior call. The SGR mouse
2572    /// sequence arrives in a subsequent call.
2573    #[test]
2574    fn esc_then_sgr_mouse_across_parse_calls() {
2575        let mut p = InputParser::new();
2576
2577        // First call: lone ESC byte. Termwiz parks no state because the
2578        // first arm only fires when there are bytes after the ESC; with
2579        // `MAYBE_MORE` it leaves the ESC pending in its internal buf and
2580        // emits nothing yet.
2581        let mut res = p.parse_as_vec(b"\x1b", MAYBE_MORE);
2582        assert!(
2583            res.is_empty(),
2584            "lone ESC should not emit yet under MAYBE_MORE"
2585        );
2586
2587        // Second call: the mouse sequence arrives. The buffered ESC plus
2588        // these bytes form `\x1b\x1b[<...M` (the inner buf already has the
2589        // ESC; this call's bytes start with another ESC because that's
2590        // what xterm sends for the mouse sequence). Result must still be
2591        // Esc + Mouse, not Alt+`[`.
2592        res = p.parse_as_vec(b"\x1b[<35;42;12M", MAYBE_MORE);
2593        assert_eq!(
2594            res,
2595            vec![
2596                InputEvent::Key(KeyEvent {
2597                    key: KeyCode::Escape,
2598                    modifiers: Modifiers::NONE,
2599                }),
2600                InputEvent::Mouse(MouseEvent {
2601                    x: 42,
2602                    y: 12,
2603                    mouse_buttons: MouseButtons::NONE,
2604                    modifiers: Modifiers::NONE,
2605                }),
2606            ]
2607        );
2608    }
2609
2610    /// Real Alt+Esc keystroke (`\x1b\x1b` with no further bytes) must
2611    /// still be recognised as Alt+Esc — the fix above must not regress
2612    /// this convention.
2613    #[test]
2614    fn alt_esc_still_recognized() {
2615        let mut p = InputParser::new();
2616        let res = p.parse_as_vec(b"\x1b\x1b", NO_MORE);
2617        assert_eq!(
2618            res,
2619            vec![InputEvent::Key(KeyEvent {
2620                key: KeyCode::Escape,
2621                modifiers: Modifiers::ALT,
2622            })]
2623        );
2624    }
2625
2626    /// Esc keystroke followed by an OSC host reply (e.g. an OSC 11 color
2627    /// query response that arrives concatenated after a stray Esc byte
2628    /// the user pressed) must emit Esc and the OSC, not Alt-modify the
2629    /// OSC bytes.
2630    #[test]
2631    fn esc_then_osc_emits_esc_and_osc() {
2632        let mut p = InputParser::new();
2633        let res = p.parse_as_vec(b"\x1b\x1b]11;rgb:ffff/ffff/ffff\x1b\\", MAYBE_MORE);
2634        assert_eq!(
2635            res,
2636            vec![
2637                InputEvent::Key(KeyEvent {
2638                    key: KeyCode::Escape,
2639                    modifiers: Modifiers::NONE,
2640                }),
2641                InputEvent::OperatingSystemCommand(b"11;rgb:ffff/ffff/ffff".to_vec()),
2642            ]
2643        );
2644    }
2645
2646    /// Esc followed by a CSI host-reply (whitelisted final byte). Must
2647    /// emit Esc and the report, never Alt+`[`.
2648    #[test]
2649    fn esc_then_csi_report_emits_esc_and_report() {
2650        let mut p = InputParser::new();
2651        // \x1b[?2026;0$y is a DECRPM reply for synchronised output mode.
2652        // Wrapped behind a stray Esc keystroke prefix.
2653        let res = p.parse_as_vec(b"\x1b\x1b[?2026;0$y", MAYBE_MORE);
2654        assert!(
2655            !res.is_empty(),
2656            "expected at least one event from Esc + CSI report"
2657        );
2658        assert!(
2659            matches!(
2660                res[0],
2661                InputEvent::Key(KeyEvent {
2662                    key: KeyCode::Escape,
2663                    modifiers: Modifiers::NONE,
2664                })
2665            ),
2666            "first event must be a bare Esc keystroke, got {:?}",
2667            res[0]
2668        );
2669        // The CSI report dispatches as DeviceControlReply via the
2670        // `parse_csi_report` whitelist. Anything but Alt+`[` is acceptable
2671        // for the second event; what we are guarding against is the
2672        // spurious Alt+`[` dispatch.
2673        for ev in &res {
2674            if let InputEvent::Key(KeyEvent { key, modifiers }) = ev {
2675                assert!(
2676                    !(matches!(key, KeyCode::Char('[')) && modifiers.contains(Modifiers::ALT)),
2677                    "must not emit Alt+`[`; got {:?}",
2678                    ev
2679                );
2680            }
2681        }
2682    }
2683
2684    #[test]
2685    fn invalid_sgr_mouse_falls_through() {
2686        let mut p = InputParser::new();
2687        // Invalid: missing terminator, not enough params
2688        let res = p.parse_as_vec(b"\x1b[<0;1M", false);
2689        // Should NOT parse as mouse - falls through to keymap
2690        assert!(res.iter().all(|e| matches!(e, InputEvent::Key(_))));
2691    }
2692
2693    #[test]
2694    fn osc_bel_terminated() {
2695        // Complete OSC sequence with BEL terminator
2696        let mut p = InputParser::new();
2697        let inputs = p.parse_as_vec(b"\x1b]99;i=test:p=title;Hello\x07", NO_MORE);
2698        assert_eq!(
2699            vec![InputEvent::OperatingSystemCommand(
2700                b"99;i=test:p=title;Hello".to_vec()
2701            )],
2702            inputs
2703        );
2704    }
2705
2706    #[test]
2707    fn osc_st_terminated() {
2708        // Complete OSC sequence with ST terminator (ESC \)
2709        let mut p = InputParser::new();
2710        let inputs = p.parse_as_vec(b"\x1b]99;i=test:p=title;Hello\x1b\\", NO_MORE);
2711        assert_eq!(
2712            vec![InputEvent::OperatingSystemCommand(
2713                b"99;i=test:p=title;Hello".to_vec()
2714            )],
2715            inputs
2716        );
2717    }
2718
2719    #[test]
2720    fn osc_partial_across_reads() {
2721        // OSC sequence split across two reads — must buffer first part
2722        let mut p = InputParser::new();
2723        let mut inputs = Vec::new();
2724        p.parse(
2725            b"\x1b]99;i=test:p=title;Hel",
2726            |evt| inputs.push(evt),
2727            MAYBE_MORE,
2728        );
2729        assert!(inputs.is_empty(), "no events yet - sequence incomplete");
2730        p.parse(b"lo\x1b\\", |evt| inputs.push(evt), MAYBE_MORE);
2731        assert_eq!(
2732            vec![InputEvent::OperatingSystemCommand(
2733                b"99;i=test:p=title;Hello".to_vec()
2734            )],
2735            inputs
2736        );
2737    }
2738
2739    #[test]
2740    fn osc_followed_by_keypress() {
2741        // OSC sequence then regular key in same buffer
2742        let mut p = InputParser::new();
2743        let inputs = p.parse_as_vec(b"\x1b]99;i=test;clicked\x07x", NO_MORE);
2744        assert_eq!(
2745            vec![
2746                InputEvent::OperatingSystemCommand(b"99;i=test;clicked".to_vec()),
2747                InputEvent::Key(KeyEvent {
2748                    modifiers: Modifiers::NONE,
2749                    key: KeyCode::Char('x'),
2750                }),
2751            ],
2752            inputs
2753        );
2754    }
2755
2756    #[test]
2757    fn keypress_followed_by_osc() {
2758        // Regular key then OSC sequence in same buffer
2759        let mut p = InputParser::new();
2760        let inputs = p.parse_as_vec(b"x\x1b]99;i=test;clicked\x07", NO_MORE);
2761        assert_eq!(
2762            vec![
2763                InputEvent::Key(KeyEvent {
2764                    modifiers: Modifiers::NONE,
2765                    key: KeyCode::Char('x'),
2766                }),
2767                InputEvent::OperatingSystemCommand(b"99;i=test;clicked".to_vec()),
2768            ],
2769            inputs
2770        );
2771    }
2772
2773    #[test]
2774    fn osc_incomplete_degrades_to_keys() {
2775        // Incomplete OSC that never gets a terminator — when finalized with
2776        // maybe_more=false, must degrade to individual key events (not hang)
2777        let mut p = InputParser::new();
2778        let mut inputs = Vec::new();
2779        p.parse(b"\x1b]99;no-terminator", |evt| inputs.push(evt), MAYBE_MORE);
2780        assert!(inputs.is_empty(), "buffered while maybe_more=true");
2781        p.parse(b"", |evt| inputs.push(evt), NO_MORE);
2782        assert!(!inputs.is_empty(), "must emit something on finalization");
2783    }
2784
2785    #[test]
2786    fn osc_non_99_code() {
2787        // Non-99 OSC codes are also captured as OperatingSystemCommand
2788        let mut p = InputParser::new();
2789        let inputs = p.parse_as_vec(b"\x1b]11;rgb:0000/0000/0000\x1b\\", NO_MORE);
2790        assert_eq!(
2791            vec![InputEvent::OperatingSystemCommand(
2792                b"11;rgb:0000/0000/0000".to_vec()
2793            )],
2794            inputs
2795        );
2796    }
2797
2798    #[test]
2799    fn osc_empty_payload() {
2800        // Edge case: OSC with no payload between \x1b] and terminator
2801        let mut p = InputParser::new();
2802        let inputs = p.parse_as_vec(b"\x1b]\x07", NO_MORE);
2803        assert_eq!(
2804            vec![InputEvent::OperatingSystemCommand(b"".to_vec())],
2805            inputs
2806        );
2807    }
2808
2809    #[test]
2810    fn csi_not_captured_as_osc() {
2811        // ESC [ (CSI) must NOT be captured as an OSC sequence.
2812        // This validates that only ESC ] triggers OSC parsing.
2813        let mut p = InputParser::new();
2814        let inputs = p.parse_as_vec(b"\x1b[A", NO_MORE);
2815        assert_eq!(
2816            vec![InputEvent::Key(KeyEvent {
2817                modifiers: Modifiers::NONE,
2818                key: KeyCode::UpArrow,
2819            })],
2820            inputs
2821        );
2822    }
2823
2824    // =====================================================================
2825    // parse_csi_report (CSI report whitelist for host-reply forwarding)
2826    // =====================================================================
2827
2828    fn csi_reply(intermediates: &[u8], params: &[u8], final_byte: u8, raw: &[u8]) -> InputEvent {
2829        InputEvent::DeviceControlReply {
2830            intermediates: intermediates.to_vec(),
2831            params: params.to_vec(),
2832            final_byte,
2833            raw: raw.to_vec(),
2834        }
2835    }
2836
2837    #[test]
2838    fn csi_report_recognises_each_whitelisted_final_byte() {
2839        // `t` — pixel-dimension reply form `\x1b[4;H;Wt`.
2840        let bytes = b"\x1b[4;600;800t";
2841        let (evt, consumed) = parse_csi_report(bytes).expect("t accepted");
2842        assert_eq!(consumed, bytes.len());
2843        assert_eq!(evt, csi_reply(b"", b"4;600;800", b't', bytes));
2844
2845        // `y` — DECRPM, e.g. sync-output support. Intermediate `$`.
2846        let bytes = b"\x1b[?2026;1$y";
2847        let (evt, consumed) = parse_csi_report(bytes).expect("y accepted");
2848        assert_eq!(consumed, bytes.len());
2849        assert_eq!(evt, csi_reply(b"$", b"?2026;1", b'y', bytes));
2850
2851        // `c` — Primary-DA reply (barrier).
2852        let bytes = b"\x1b[?62;1;6c";
2853        let (evt, consumed) = parse_csi_report(bytes).expect("c accepted");
2854        assert_eq!(consumed, bytes.len());
2855        assert_eq!(evt, csi_reply(b"", b"?62;1;6", b'c', bytes));
2856
2857        // `n` — DSR reply (used for theme notifications).
2858        let bytes = b"\x1b[?997;1n";
2859        let (evt, consumed) = parse_csi_report(bytes).expect("n accepted");
2860        assert_eq!(consumed, bytes.len());
2861        assert_eq!(evt, csi_reply(b"", b"?997;1", b'n', bytes));
2862    }
2863
2864    #[test]
2865    fn csi_report_preserves_intermediates() {
2866        // DECRPM uses `$` as its intermediate byte — it must land in
2867        // `intermediates`, not `params`.
2868        let bytes = b"\x1b[?2026;2$y";
2869        let (evt, _len) = parse_csi_report(bytes).expect("DECRPM accepted");
2870        let InputEvent::DeviceControlReply {
2871            intermediates,
2872            params,
2873            final_byte,
2874            raw,
2875        } = evt
2876        else {
2877            panic!("expected DeviceControlReply, got {:?}", evt);
2878        };
2879        assert_eq!(intermediates, b"$");
2880        assert_eq!(params, b"?2026;2");
2881        assert_eq!(final_byte, b'y');
2882        assert_eq!(raw, bytes);
2883    }
2884
2885    #[test]
2886    fn csi_report_rejects_non_whitelisted_final_bytes() {
2887        // `A` = cursor-up (keyboard input, not a report).
2888        assert!(parse_csi_report(b"\x1b[A").is_none());
2889        // `R` = cursor-position report — not whitelisted; must pass
2890        // through to the keyboard path.
2891        assert!(parse_csi_report(b"\x1b[24;80R").is_none());
2892        // `m` = SGR; appears in render streams but should never reach
2893        // stdin as a report.
2894        assert!(parse_csi_report(b"\x1b[0m").is_none());
2895    }
2896
2897    #[test]
2898    fn csi_report_returns_none_on_truncated_input() {
2899        // No final byte within the supplied slice → caller should wait
2900        // for more bytes; `parse_csi_report` must not "commit" to a
2901        // partial parse.
2902        assert!(parse_csi_report(b"\x1b[4;600;800").is_none());
2903        // Only the lead-in; parameters haven't started.
2904        assert!(parse_csi_report(b"\x1b[").is_none());
2905        // Empty input — zero bytes to consume.
2906        assert!(parse_csi_report(b"").is_none());
2907    }
2908
2909    #[test]
2910    fn csi_report_raw_preserves_input_byte_for_byte() {
2911        // `raw` must include the leading ESC through the final byte
2912        // inclusive, without adding or dropping any byte — the
2913        // forwarding path writes it verbatim to the pane's pty.
2914        let bytes = b"\x1b[4;16;8t";
2915        let (evt, consumed) = parse_csi_report(bytes).expect("accepted");
2916        assert_eq!(consumed, bytes.len());
2917        let InputEvent::DeviceControlReply { raw, .. } = evt else {
2918            panic!("wrong variant");
2919        };
2920        assert_eq!(&raw[..], bytes, "raw must be byte-identical to input");
2921    }
2922}