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(|e, _consumed| callback(e), 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(|e, _consumed| callback(e), 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, usize)>(
1598        &mut self,
1599        mut callback: F,
1600        event: InputEvent,
1601    ) {
1602        // `self.buf` is already advanced past this event, so `self.buf.len()` is
1603        // the remainder `parse_with_consumed` diffs into a per-event byte count.
1604        match (self.state, &event) {
1605            (
1606                InputState::Normal,
1607                InputEvent::Key(KeyEvent {
1608                    key: KeyCode::InternalPasteStart,
1609                    ..
1610                }),
1611            ) => {
1612                self.state = InputState::Pasting(0);
1613            },
1614            (
1615                InputState::EscapeMaybeAlt,
1616                InputEvent::Key(KeyEvent {
1617                    key: KeyCode::InternalPasteStart,
1618                    ..
1619                }),
1620            ) => {
1621                // The prior ESC was not part of an ALT sequence, so emit
1622                // it before we start collecting for paste.
1623                callback(
1624                    InputEvent::Key(KeyEvent {
1625                        key: KeyCode::Escape,
1626                        modifiers: Modifiers::NONE,
1627                    }),
1628                    self.buf.len(),
1629                );
1630                self.state = InputState::Pasting(0);
1631            },
1632            (InputState::EscapeMaybeAlt, InputEvent::Key(KeyEvent { key, modifiers })) => {
1633                // Treat this as ALT-key
1634                let key = *key;
1635                let modifiers = *modifiers;
1636                self.state = InputState::Normal;
1637                callback(
1638                    InputEvent::Key(KeyEvent {
1639                        key,
1640                        modifiers: modifiers | Modifiers::ALT,
1641                    }),
1642                    self.buf.len(),
1643                );
1644            },
1645            (InputState::EscapeMaybeAlt, _) => {
1646                // The prior ESC was not part of an ALT sequence, so emit
1647                // both it and the current event
1648                callback(
1649                    InputEvent::Key(KeyEvent {
1650                        key: KeyCode::Escape,
1651                        modifiers: Modifiers::NONE,
1652                    }),
1653                    self.buf.len(),
1654                );
1655                callback(event, self.buf.len());
1656            },
1657            (_, _) => callback(event, self.buf.len()),
1658        }
1659    }
1660
1661    /// If a parked ESC is currently held in `EscapeMaybeAlt`, emit it as a
1662    /// real `Esc` keystroke and return to `Normal`. Called from
1663    /// `process_bytes` before dispatching any structured sequence (SGR
1664    /// mouse, OSC, whitelisted CSI host-reply) that the upcoming bytes
1665    /// match — those sequences are autonomous host events and cannot be
1666    /// ALT-combined with the parked ESC, so the ESC must be flushed
1667    /// before the sequence is emitted.
1668    fn flush_parked_esc_if_held<F: FnMut(InputEvent, usize)>(&mut self, callback: &mut F) {
1669        if self.state == InputState::EscapeMaybeAlt {
1670            callback(
1671                InputEvent::Key(KeyEvent {
1672                    key: KeyCode::Escape,
1673                    modifiers: Modifiers::NONE,
1674                }),
1675                self.buf.len(),
1676            );
1677            self.state = InputState::Normal;
1678        }
1679    }
1680
1681    fn process_bytes<F: FnMut(InputEvent, usize)>(&mut self, mut callback: F, maybe_more: bool) {
1682        while !self.buf.is_empty() {
1683            match self.state {
1684                InputState::Pasting(offset) => {
1685                    let end_paste = b"\x1b[201~";
1686                    if let Some(idx) = self.buf.find_subsequence(offset, end_paste) {
1687                        let pasted =
1688                            String::from_utf8_lossy(&self.buf.as_slice()[0..idx]).to_string();
1689                        self.buf.advance(pasted.len() + end_paste.len());
1690                        callback(InputEvent::Paste(pasted), self.buf.len());
1691                        self.state = InputState::Normal;
1692                    } else {
1693                        self.state =
1694                            InputState::Pasting(self.buf.len().saturating_sub(end_paste.len()));
1695                        return;
1696                    }
1697                },
1698                InputState::EscapeMaybeAlt | InputState::Normal => {
1699                    // Structured terminal sequences — SGR mouse, OSC, whitelisted
1700                    // CSI host-replies — are autonomous host events and cannot be
1701                    // ALT-combined with a leading Esc keystroke. Run these checks
1702                    // in *both* Normal and EscapeMaybeAlt: if we're sitting on a
1703                    // parked ESC (EscapeMaybeAlt) and the upcoming bytes match one
1704                    // of these patterns, the ESC must be a real Esc keystroke, so
1705                    // flush it before dispatching the sequence. Otherwise a parked
1706                    // ESC immediately followed by `\x1b[<...M` (xterm flushes Esc
1707                    // alone, then a mouse motion in the next read) would dispatch
1708                    // as a spurious ALT+`[` because the keymap registers `\x1b[`
1709                    // as Alt+`[`.
1710                    if self.buf.as_slice().get(0) == Some(&b'\x1b') {
1711                        if let Some((event, len)) = parse_sgr_mouse(self.buf.as_slice()) {
1712                            self.flush_parked_esc_if_held(&mut callback);
1713                            self.buf.advance(len);
1714                            callback(event, self.buf.len());
1715                            continue;
1716                        }
1717
1718                        // OSC sequence check — must come before the incomplete-SGR-mouse early return
1719                        if let Some((event, len)) = parse_osc(self.buf.as_slice()) {
1720                            self.flush_parked_esc_if_held(&mut callback);
1721                            self.buf.advance(len);
1722                            callback(event, self.buf.len());
1723                            continue;
1724                        }
1725
1726                        // Incomplete OSC — buffer and wait for more data
1727                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b]") {
1728                            self.flush_parked_esc_if_held(&mut callback);
1729                            return;
1730                        }
1731
1732                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b[<") {
1733                            self.flush_parked_esc_if_held(&mut callback);
1734                            return;
1735                        }
1736
1737                        // CSI-based host-terminal report (pixel-dims reply,
1738                        // DECRPM, DSR, Primary-DA). Must come before the
1739                        // regular CSI key-mapping machinery, which would
1740                        // otherwise match "\x1b[" as an escape prefix and
1741                        // pass the bytes through as keyboard input.
1742                        if let Some((event, len)) = parse_csi_report(self.buf.as_slice()) {
1743                            self.flush_parked_esc_if_held(&mut callback);
1744                            self.buf.advance(len);
1745                            callback(event, self.buf.len());
1746                            continue;
1747                        }
1748
1749                        // Incomplete CSI ?... report (DECRPM, DSR 997, etc.) —
1750                        // wait for more data so the report-classification path
1751                        // can match the full sequence rather than letting the
1752                        // keymap dispatch the leading bytes as separate keys.
1753                        if maybe_more && self.buf.as_slice().starts_with(b"\x1b[?") {
1754                            self.flush_parked_esc_if_held(&mut callback);
1755                            return;
1756                        }
1757                    }
1758
1759                    match (
1760                        self.key_map.lookup(self.buf.as_slice(), maybe_more),
1761                        maybe_more,
1762                    ) {
1763                        // If we got an unambiguous ESC and we have more data to
1764                        // follow, then this is likely the Meta version of the
1765                        // following keypress.  Buffer up the escape key and
1766                        // consume it from the input.  dispatch_callback() will
1767                        // emit either the ESC or the ALT modified following key.
1768                        (
1769                            Found::Exact(
1770                                len,
1771                                InputEvent::Key(KeyEvent {
1772                                    key: KeyCode::Escape,
1773                                    modifiers: Modifiers::NONE,
1774                                }),
1775                            ),
1776                            _,
1777                        ) if self.state == InputState::Normal && self.buf.len() > len => {
1778                            self.state = InputState::EscapeMaybeAlt;
1779                            self.buf.advance(len);
1780                        },
1781                        (Found::Exact(len, event), _) | (Found::Ambiguous(len, event), false) => {
1782                            // Advance before dispatching so `self.buf.len()` inside
1783                            // `dispatch_callback` already reflects this key's consumption.
1784                            self.buf.advance(len);
1785                            self.dispatch_callback(&mut callback, event.clone());
1786                        },
1787                        (Found::Ambiguous(_, _), true) | (Found::NeedData, true) => {
1788                            // The keymap is signalling "this buffer
1789                            // could still grow into a registered key,
1790                            // give me more bytes." That verdict is
1791                            // wrong when the buffer already holds a
1792                            // structurally complete CSI sequence whose
1793                            // final byte isn't in the keymap — most
1794                            // importantly Kitty keyboard-protocol
1795                            // events `\x1b[<keycode>;<mods>u`, which
1796                            // never grow into anything the keymap
1797                            // knows. Returning here would wedge
1798                            // `self.buf` indefinitely, swallowing every
1799                            // host reply that arrives behind it (the
1800                            // OSC + DA1 bytes for a forwarded
1801                            // `OSC 11;?` query among them) and stalling
1802                            // host-color forwards until session exit.
1803                            //
1804                            // Both `Ambiguous(_, true)` and
1805                            // `NeedData(true)` reach this point in
1806                            // practice: for `\x1b[<digits>;<digits>u`
1807                            // the trie reports `Ambiguous(1, Escape)`
1808                            // (it has ESC alone as a match and ESC[…]
1809                            // as longer prefixes), so the fix must
1810                            // cover both verdicts.
1811                            //
1812                            // Skip past the unrecognised CSI without
1813                            // emitting an event; callers that need
1814                            // keyboard dispatch (kitty_parser, the
1815                            // separate `input_parser` instance fed the
1816                            // residue from `StdinAnsiParser`) see the
1817                            // same bytes via `strip_replies`, which
1818                            // already treats unwhitelisted-final CSIs
1819                            // as `Malformed` and pushes them through.
1820                            if let Some(len) = complete_csi_len(self.buf.as_slice()) {
1821                                self.buf.advance(len);
1822                                continue;
1823                            }
1824                            return;
1825                        },
1826                        (Found::None, _) | (Found::NeedData, false) => {
1827                            // No pre-defined key, so pull out a unicode character
1828                            if let Some((c, len)) = Self::decode_one_char(self.buf.as_slice()) {
1829                                self.buf.advance(len);
1830                                self.dispatch_callback(
1831                                    &mut callback,
1832                                    InputEvent::Key(KeyEvent {
1833                                        key: KeyCode::Char(c),
1834                                        modifiers: Modifiers::NONE,
1835                                    }),
1836                                );
1837                            } else {
1838                                // We need more data to recognize the input, so
1839                                // yield the remainder of the slice
1840                                return;
1841                            }
1842                        },
1843                    }
1844                },
1845            }
1846        }
1847    }
1848
1849    /// Push a sequence of bytes into the parser.
1850    /// Each time input is recognized, the provided `callback` will be passed
1851    /// the decoded `InputEvent`.
1852    /// If not enough data are available to fully decode a sequence, the
1853    /// remaining data will be buffered until the next call.
1854    /// The `maybe_more` flag controls how ambiguous partial sequences are
1855    /// handled. The intent is that `maybe_more` should be set to true if
1856    /// you believe that you will be able to provide more data momentarily.
1857    /// This will cause the parser to defer judgement on partial prefix
1858    /// matches. You should attempt to read and pass the new data in
1859    /// immediately afterwards. If you have attempted a read and no data is
1860    /// immediately available, you should follow up with a call to parse
1861    /// with an empty slice and `maybe_more=false` to allow the partial
1862    /// data to be recognized and processed.
1863    pub fn parse<F: FnMut(InputEvent)>(&mut self, bytes: &[u8], callback: F, maybe_more: bool) {
1864        // rebind (not `mut callback: F`) to keep the upstream signature intact
1865        let mut callback = callback;
1866        self.parse_with_consumed(bytes, |event, _consumed| callback(event), maybe_more);
1867    }
1868
1869    /// Like [`InputParser::parse`], but the callback also receives the number
1870    /// of input bytes consumed to produce each event. This allows a caller
1871    /// that forwards raw bytes alongside decoded events to attribute to each
1872    /// event exactly the bytes that produced it when a single chunk of input
1873    /// decodes into multiple events.
1874    pub fn parse_with_consumed<F: FnMut(InputEvent, usize)>(
1875        &mut self,
1876        bytes: &[u8],
1877        mut callback: F,
1878        maybe_more: bool,
1879    ) {
1880        self.buf.extend_with(bytes);
1881        // `process_bytes` reports the bytes still buffered after each event; the
1882        // drop between successive remainders is what that event consumed.
1883        let mut prev_remaining = self.buf.len();
1884        self.process_bytes(
1885            |event, remaining| {
1886                let consumed = prev_remaining.saturating_sub(remaining);
1887                prev_remaining = remaining;
1888                callback(event, consumed);
1889            },
1890            maybe_more,
1891        );
1892    }
1893
1894    /// Number of bytes still held unprocessed in the parser's internal
1895    /// buffer. A caller that mirrors this ring separately (e.g. to forward
1896    /// raw bytes alongside decoded events) can reconcile its own buffer to
1897    /// exactly the same length so the two never drift apart.
1898    pub fn buffered_len(&self) -> usize {
1899        self.buf.len()
1900    }
1901
1902    pub fn parse_as_vec(&mut self, bytes: &[u8], maybe_more: bool) -> Vec<InputEvent> {
1903        let mut result = Vec::new();
1904        self.parse(bytes, |event| result.push(event), maybe_more);
1905        result
1906    }
1907
1908    #[cfg(windows)]
1909    pub fn decode_input_records_as_vec(
1910        &mut self,
1911        records: &[winapi::um::wincon::INPUT_RECORD],
1912    ) -> Vec<InputEvent> {
1913        let mut result = Vec::new();
1914        self.decode_input_records(records, &mut |event| result.push(event));
1915        result
1916    }
1917}
1918
1919#[cfg(test)]
1920mod test {
1921    use super::*;
1922
1923    const NO_MORE: bool = false;
1924    const MAYBE_MORE: bool = true;
1925
1926    #[test]
1927    fn simple() {
1928        let mut p = InputParser::new();
1929        let inputs = p.parse_as_vec(b"hello", NO_MORE);
1930        assert_eq!(
1931            vec![
1932                InputEvent::Key(KeyEvent {
1933                    modifiers: Modifiers::NONE,
1934                    key: KeyCode::Char('h'),
1935                }),
1936                InputEvent::Key(KeyEvent {
1937                    modifiers: Modifiers::NONE,
1938                    key: KeyCode::Char('e'),
1939                }),
1940                InputEvent::Key(KeyEvent {
1941                    modifiers: Modifiers::NONE,
1942                    key: KeyCode::Char('l'),
1943                }),
1944                InputEvent::Key(KeyEvent {
1945                    modifiers: Modifiers::NONE,
1946                    key: KeyCode::Char('l'),
1947                }),
1948                InputEvent::Key(KeyEvent {
1949                    modifiers: Modifiers::NONE,
1950                    key: KeyCode::Char('o'),
1951                }),
1952            ],
1953            inputs
1954        );
1955    }
1956
1957    #[test]
1958    fn control_characters() {
1959        let mut p = InputParser::new();
1960        let inputs = p.parse_as_vec(b"\x03\x1bJ\x7f", NO_MORE);
1961        assert_eq!(
1962            vec![
1963                InputEvent::Key(KeyEvent {
1964                    modifiers: Modifiers::CTRL,
1965                    key: KeyCode::Char('c'),
1966                }),
1967                InputEvent::Key(KeyEvent {
1968                    modifiers: Modifiers::ALT,
1969                    key: KeyCode::Char('J'),
1970                }),
1971                InputEvent::Key(KeyEvent {
1972                    modifiers: Modifiers::NONE,
1973                    key: KeyCode::Backspace,
1974                }),
1975            ],
1976            inputs
1977        );
1978    }
1979
1980    #[test]
1981    fn arrow_keys() {
1982        let mut p = InputParser::new();
1983        let inputs = p.parse_as_vec(b"\x1bOA\x1bOB\x1bOC\x1bOD", NO_MORE);
1984        assert_eq!(
1985            vec![
1986                InputEvent::Key(KeyEvent {
1987                    modifiers: Modifiers::NONE,
1988                    key: KeyCode::ApplicationUpArrow,
1989                }),
1990                InputEvent::Key(KeyEvent {
1991                    modifiers: Modifiers::NONE,
1992                    key: KeyCode::ApplicationDownArrow,
1993                }),
1994                InputEvent::Key(KeyEvent {
1995                    modifiers: Modifiers::NONE,
1996                    key: KeyCode::ApplicationRightArrow,
1997                }),
1998                InputEvent::Key(KeyEvent {
1999                    modifiers: Modifiers::NONE,
2000                    key: KeyCode::ApplicationLeftArrow,
2001                }),
2002            ],
2003            inputs
2004        );
2005    }
2006
2007    /// Parse `bytes` and pair each event with the raw bytes it consumed,
2008    /// draining from a copy of the input the same way the client's stdin
2009    /// loop attributes raw bytes to events.
2010    fn parse_with_raw_bytes(bytes: &[u8], maybe_more: bool) -> Vec<(InputEvent, Vec<u8>)> {
2011        let mut p = InputParser::new();
2012        let mut collected: Vec<(InputEvent, usize)> = Vec::new();
2013        p.parse_with_consumed(bytes, |ev, n| collected.push((ev, n)), maybe_more);
2014        let mut buffer: Vec<u8> = bytes.to_vec();
2015        collected
2016            .into_iter()
2017            .map(|(ev, n)| {
2018                let take = n.min(buffer.len());
2019                let raw: Vec<u8> = buffer.drain(..take).collect();
2020                (ev, raw)
2021            })
2022            .collect()
2023    }
2024
2025    #[test]
2026    fn typed_char_keeps_only_its_own_bytes_before_mouse_reports() {
2027        // A keystroke and two mouse reports arrive in one read: the key must be
2028        // paired with only its own byte and each report with its own bytes.
2029        let events = parse_with_raw_bytes(b"a\x1b[<35;52;16M\x1b[<35;49;16M", MAYBE_MORE);
2030        assert_eq!(
2031            events.len(),
2032            3,
2033            "expected key + 2 mouse events, got {:?}",
2034            events
2035        );
2036        assert!(
2037            matches!(
2038                events[0].0,
2039                InputEvent::Key(KeyEvent {
2040                    key: KeyCode::Char('a'),
2041                    ..
2042                })
2043            ),
2044            "first event should be the typed key, got {:?}",
2045            events[0].0
2046        );
2047        assert_eq!(
2048            events[0].1, b"a",
2049            "the keystroke must not carry the trailing mouse bytes"
2050        );
2051        assert!(matches!(events[1].0, InputEvent::Mouse(_)));
2052        assert_eq!(events[1].1, b"\x1b[<35;52;16M");
2053        assert!(matches!(events[2].0, InputEvent::Mouse(_)));
2054        assert_eq!(events[2].1, b"\x1b[<35;49;16M");
2055    }
2056
2057    #[test]
2058    fn typed_char_keeps_only_its_own_bytes_after_mouse_reports() {
2059        // A mouse report precedes the keystroke in the read; the key must still
2060        // be paired with only its own byte.
2061        let events = parse_with_raw_bytes(b"\x1b[<35;52;16Ma", MAYBE_MORE);
2062        assert_eq!(events.len(), 2, "got {:?}", events);
2063        assert!(matches!(events[0].0, InputEvent::Mouse(_)));
2064        assert_eq!(events[0].1, b"\x1b[<35;52;16M");
2065        assert!(matches!(
2066            events[1].0,
2067            InputEvent::Key(KeyEvent {
2068                key: KeyCode::Char('a'),
2069                ..
2070            })
2071        ));
2072        assert_eq!(events[1].1, b"a");
2073    }
2074
2075    #[test]
2076    fn consecutive_chars_before_mouse_each_keep_one_byte() {
2077        // Consecutive keystrokes in one read are each paired with their own byte.
2078        let events = parse_with_raw_bytes(b"ab\x1b[<35;52;16M", MAYBE_MORE);
2079        assert_eq!(events.len(), 3, "got {:?}", events);
2080        assert_eq!(events[0].1, b"a");
2081        assert_eq!(events[1].1, b"b");
2082        assert_eq!(events[2].1, b"\x1b[<35;52;16M");
2083    }
2084
2085    #[test]
2086    fn single_event_keeps_all_its_bytes() {
2087        // A read that decodes into a single event pairs it with all of the
2088        // read's bytes, including a multi-byte sequence (`\x1bOA`).
2089        let events = parse_with_raw_bytes(b"\x1bOA", NO_MORE);
2090        assert_eq!(events.len(), 1, "got {:?}", events);
2091        assert!(matches!(
2092            events[0].0,
2093            InputEvent::Key(KeyEvent {
2094                key: KeyCode::ApplicationUpArrow,
2095                ..
2096            })
2097        ));
2098        assert_eq!(events[0].1, b"\x1bOA");
2099    }
2100
2101    #[test]
2102    fn lone_esc_batch_then_mouse_report_batch() {
2103        // A lone ESC arrives in one batch and a complete mouse report in the
2104        // next. The ESC is held until the following batch disambiguates it;
2105        // both events are then emitted, each paired with its own bytes.
2106        let mut p = InputParser::new();
2107        let mut events: Vec<(InputEvent, usize)> = Vec::new();
2108        let mut buffer: Vec<u8> = Vec::new();
2109
2110        buffer.extend_from_slice(b"\x1b");
2111        p.parse_with_consumed(b"\x1b", |ev, n| events.push((ev, n)), MAYBE_MORE);
2112        assert!(
2113            events.is_empty(),
2114            "a lone ESC with more data possibly coming must be held, got {:?}",
2115            events
2116        );
2117
2118        buffer.extend_from_slice(b"\x1b[<35;62;16M");
2119        p.parse_with_consumed(b"\x1b[<35;62;16M", |ev, n| events.push((ev, n)), MAYBE_MORE);
2120        assert_eq!(events.len(), 2, "got {:?}", events);
2121        assert!(matches!(
2122            events[0].0,
2123            InputEvent::Key(KeyEvent {
2124                key: KeyCode::Escape,
2125                ..
2126            })
2127        ));
2128        assert_eq!(events[0].1, 1, "the ESC consumed its single byte");
2129        assert!(matches!(events[1].0, InputEvent::Mouse(_)));
2130        assert_eq!(events[1].1, 12, "the mouse report consumed its 12 bytes");
2131
2132        // Draining the accumulated bytes per event, the way the client's
2133        // stdin loop does, pairs each event with its own raw bytes.
2134        let esc_bytes: Vec<u8> = buffer.drain(..events[0].1).collect();
2135        let mouse_bytes: Vec<u8> = buffer.drain(..events[1].1).collect();
2136        assert_eq!(esc_bytes, b"\x1b");
2137        assert_eq!(mouse_bytes, b"\x1b[<35;62;16M");
2138        assert!(buffer.is_empty());
2139    }
2140
2141    #[test]
2142    fn paste_start_alone_is_consumed_silently_and_not_buffered() {
2143        let mut p = InputParser::new();
2144        let mut events: Vec<(InputEvent, usize)> = Vec::new();
2145        p.parse_with_consumed(b"\x1b[200~", |ev, n| events.push((ev, n)), MAYBE_MORE);
2146        assert!(
2147            events.is_empty(),
2148            "a lone paste-start marker must produce no events, got {:?}",
2149            events
2150        );
2151        assert_eq!(
2152            p.buffered_len(),
2153            0,
2154            "the paste-start bytes are consumed out of the parser buffer without any event reporting them"
2155        );
2156    }
2157
2158    #[test]
2159    fn paste_start_with_partial_payload_buffers_only_the_payload() {
2160        let mut p = InputParser::new();
2161        let mut events: Vec<(InputEvent, usize)> = Vec::new();
2162        p.parse_with_consumed(b"\x1b[200~hel", |ev, n| events.push((ev, n)), MAYBE_MORE);
2163        assert!(events.is_empty(), "got {:?}", events);
2164        assert_eq!(
2165            p.buffered_len(),
2166            3,
2167            "only the pending paste payload remains buffered; the 6 marker bytes were consumed silently"
2168        );
2169    }
2170
2171    #[test]
2172    fn parked_esc_before_partial_utf8_is_consumed_out_of_the_buffer() {
2173        let mut p = InputParser::new();
2174        let mut events: Vec<(InputEvent, usize)> = Vec::new();
2175        p.parse_with_consumed(b"\x1b\xc3", |ev, n| events.push((ev, n)), MAYBE_MORE);
2176        assert!(events.is_empty(), "got {:?}", events);
2177        assert_eq!(
2178            p.buffered_len(),
2179            1,
2180            "the parked ESC is held in parser state, not in the buffer; only the partial UTF-8 byte remains"
2181        );
2182    }
2183
2184    #[test]
2185    fn newline_then_carriage_return_are_two_enter_events_with_their_own_bytes() {
2186        // In the legacy encoding a terminal sends `\r` for the Enter key and
2187        // `\n` for a control-j style newline; the keymap decodes both to
2188        // Enter. Arriving together they are two Enter events, each paired
2189        // with its own byte.
2190        let events = parse_with_raw_bytes(b"\n\r", MAYBE_MORE);
2191        assert_eq!(events.len(), 2, "got {:?}", events);
2192        for (event, raw) in &events {
2193            assert!(
2194                matches!(
2195                    event,
2196                    InputEvent::Key(KeyEvent {
2197                        key: KeyCode::Enter,
2198                        ..
2199                    })
2200                ),
2201                "expected an Enter key event, got {:?}",
2202                event
2203            );
2204            assert_eq!(raw.len(), 1, "each Enter is paired with a single byte");
2205        }
2206        assert_eq!(events[0].1, b"\n");
2207        assert_eq!(events[1].1, b"\r");
2208    }
2209
2210    #[test]
2211    fn partial() {
2212        let mut p = InputParser::new();
2213        let mut inputs = Vec::new();
2214        // Fragment this F-key sequence across two different pushes
2215        p.parse(b"\x1b[11", |evt| inputs.push(evt), true);
2216        p.parse(b"~", |evt| inputs.push(evt), true);
2217        // make sure we recognize it as just the F-key
2218        assert_eq!(
2219            vec![InputEvent::Key(KeyEvent {
2220                modifiers: Modifiers::NONE,
2221                key: KeyCode::Function(1),
2222            })],
2223            inputs
2224        );
2225    }
2226
2227    #[test]
2228    fn partial_ambig() {
2229        let mut p = InputParser::new();
2230
2231        assert_eq!(
2232            vec![InputEvent::Key(KeyEvent {
2233                key: KeyCode::Escape,
2234                modifiers: Modifiers::NONE,
2235            })],
2236            p.parse_as_vec(b"\x1b", false)
2237        );
2238
2239        let mut inputs = Vec::new();
2240        // An incomplete F-key sequence fragmented across two different pushes
2241        p.parse(b"\x1b[11", |evt| inputs.push(evt), MAYBE_MORE);
2242        p.parse(b"", |evt| inputs.push(evt), NO_MORE);
2243        // since we finish with maybe_more false (NO_MORE), the results should be the longest matching
2244        // parts of said f-key sequence
2245        assert_eq!(
2246            vec![
2247                InputEvent::Key(KeyEvent {
2248                    modifiers: Modifiers::ALT,
2249                    key: KeyCode::Char('['),
2250                }),
2251                InputEvent::Key(KeyEvent {
2252                    modifiers: Modifiers::NONE,
2253                    key: KeyCode::Char('1'),
2254                }),
2255                InputEvent::Key(KeyEvent {
2256                    modifiers: Modifiers::NONE,
2257                    key: KeyCode::Char('1'),
2258                }),
2259            ],
2260            inputs
2261        );
2262    }
2263
2264    #[test]
2265    fn partial_mouse() {
2266        let mut p = InputParser::new();
2267        let mut inputs = Vec::new();
2268        // Fragment this mouse sequence across two different pushes
2269        p.parse(b"\x1b[<0;0;0", |evt| inputs.push(evt), true);
2270        p.parse(b"M", |evt| inputs.push(evt), true);
2271        // make sure we recognize it as just the mouse event
2272        assert_eq!(
2273            vec![InputEvent::Mouse(MouseEvent {
2274                x: 0,
2275                y: 0,
2276                mouse_buttons: MouseButtons::LEFT,
2277                modifiers: Modifiers::NONE,
2278            })],
2279            inputs
2280        );
2281    }
2282
2283    #[test]
2284    fn partial_mouse_ambig() {
2285        let mut p = InputParser::new();
2286        let mut inputs = Vec::new();
2287        // Fragment this mouse sequence across two different pushes
2288        p.parse(b"\x1b[<", |evt| inputs.push(evt), MAYBE_MORE);
2289        p.parse(b"0;0;0", |evt| inputs.push(evt), NO_MORE);
2290        // since we finish with maybe_more false (NO_MORE), the results should be the longest matching
2291        // parts of said mouse sequence
2292        assert_eq!(
2293            vec![
2294                InputEvent::Key(KeyEvent {
2295                    modifiers: Modifiers::ALT,
2296                    key: KeyCode::Char('['),
2297                }),
2298                InputEvent::Key(KeyEvent {
2299                    modifiers: Modifiers::NONE,
2300                    key: KeyCode::Char('<'),
2301                }),
2302                InputEvent::Key(KeyEvent {
2303                    modifiers: Modifiers::NONE,
2304                    key: KeyCode::Char('0'),
2305                }),
2306                InputEvent::Key(KeyEvent {
2307                    modifiers: Modifiers::NONE,
2308                    key: KeyCode::Char(';'),
2309                }),
2310                InputEvent::Key(KeyEvent {
2311                    modifiers: Modifiers::NONE,
2312                    key: KeyCode::Char('0'),
2313                }),
2314                InputEvent::Key(KeyEvent {
2315                    modifiers: Modifiers::NONE,
2316                    key: KeyCode::Char(';'),
2317                }),
2318                InputEvent::Key(KeyEvent {
2319                    modifiers: Modifiers::NONE,
2320                    key: KeyCode::Char('0'),
2321                }),
2322            ],
2323            inputs
2324        );
2325    }
2326
2327    #[test]
2328    fn alt_left_bracket() {
2329        // tests that `Alt` + `[` is recognized as a single
2330        // event rather than two events (one `Esc` the second `Char('[')`)
2331        let mut p = InputParser::new();
2332
2333        let mut inputs = Vec::new();
2334        p.parse(b"\x1b[", |evt| inputs.push(evt), false);
2335
2336        assert_eq!(
2337            vec![InputEvent::Key(KeyEvent {
2338                modifiers: Modifiers::ALT,
2339                key: KeyCode::Char('['),
2340            }),],
2341            inputs
2342        );
2343    }
2344
2345    #[test]
2346    fn modify_other_keys_parse() {
2347        let mut p = InputParser::new();
2348        let inputs = p.parse_as_vec(
2349            b"\x1b[27;5;13~\x1b[27;5;9~\x1b[27;6;8~\x1b[27;2;127~\x1b[27;6;27~",
2350            NO_MORE,
2351        );
2352        assert_eq!(
2353            vec![
2354                InputEvent::Key(KeyEvent {
2355                    key: KeyCode::Enter,
2356                    modifiers: Modifiers::CTRL,
2357                }),
2358                InputEvent::Key(KeyEvent {
2359                    key: KeyCode::Tab,
2360                    modifiers: Modifiers::CTRL,
2361                }),
2362                InputEvent::Key(KeyEvent {
2363                    key: KeyCode::Backspace,
2364                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2365                }),
2366                InputEvent::Key(KeyEvent {
2367                    key: KeyCode::Backspace,
2368                    modifiers: Modifiers::SHIFT,
2369                }),
2370                InputEvent::Key(KeyEvent {
2371                    key: KeyCode::Escape,
2372                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2373                }),
2374            ],
2375            inputs
2376        );
2377    }
2378
2379    #[test]
2380    fn modify_other_keys_encode() {
2381        let mode = KeyCodeEncodeModes {
2382            encoding: KeyboardEncoding::Xterm,
2383            newline_mode: false,
2384            application_cursor_keys: false,
2385            modify_other_keys: None,
2386        };
2387        let mode_1 = KeyCodeEncodeModes {
2388            encoding: KeyboardEncoding::Xterm,
2389            newline_mode: false,
2390            application_cursor_keys: false,
2391            modify_other_keys: Some(1),
2392        };
2393        let mode_2 = KeyCodeEncodeModes {
2394            encoding: KeyboardEncoding::Xterm,
2395            newline_mode: false,
2396            application_cursor_keys: false,
2397            modify_other_keys: Some(2),
2398        };
2399
2400        assert_eq!(
2401            KeyCode::Enter.encode(Modifiers::CTRL, mode, true).unwrap(),
2402            "\r".to_string()
2403        );
2404        assert_eq!(
2405            KeyCode::Enter
2406                .encode(Modifiers::CTRL, mode_1, true)
2407                .unwrap(),
2408            "\x1b[27;5;13~".to_string()
2409        );
2410        assert_eq!(
2411            KeyCode::Enter
2412                .encode(Modifiers::CTRL | Modifiers::SHIFT, mode_1, true)
2413                .unwrap(),
2414            "\x1b[27;6;13~".to_string()
2415        );
2416
2417        // This case is not conformant with xterm!
2418        // xterm just returns tab for CTRL-Tab when modify_other_keys
2419        // is not set.
2420        assert_eq!(
2421            KeyCode::Tab.encode(Modifiers::CTRL, mode, true).unwrap(),
2422            "\x1b[9;5u".to_string()
2423        );
2424        assert_eq!(
2425            KeyCode::Tab.encode(Modifiers::CTRL, mode_1, true).unwrap(),
2426            "\x1b[27;5;9~".to_string()
2427        );
2428        assert_eq!(
2429            KeyCode::Tab
2430                .encode(Modifiers::CTRL | Modifiers::SHIFT, mode_1, true)
2431                .unwrap(),
2432            "\x1b[27;6;9~".to_string()
2433        );
2434
2435        assert_eq!(
2436            KeyCode::Char('c')
2437                .encode(Modifiers::CTRL, mode, true)
2438                .unwrap(),
2439            "\x03".to_string()
2440        );
2441        assert_eq!(
2442            KeyCode::Char('c')
2443                .encode(Modifiers::CTRL, mode_1, true)
2444                .unwrap(),
2445            "\x03".to_string()
2446        );
2447        assert_eq!(
2448            KeyCode::Char('c')
2449                .encode(Modifiers::CTRL, mode_2, true)
2450                .unwrap(),
2451            "\x1b[27;5;99~".to_string()
2452        );
2453
2454        assert_eq!(
2455            KeyCode::Char('1')
2456                .encode(Modifiers::CTRL, mode, true)
2457                .unwrap(),
2458            "1".to_string()
2459        );
2460        assert_eq!(
2461            KeyCode::Char('1')
2462                .encode(Modifiers::CTRL, mode_2, true)
2463                .unwrap(),
2464            "\x1b[27;5;49~".to_string()
2465        );
2466
2467        assert_eq!(
2468            KeyCode::Char(',')
2469                .encode(Modifiers::CTRL, mode, true)
2470                .unwrap(),
2471            ",".to_string()
2472        );
2473        assert_eq!(
2474            KeyCode::Char(',')
2475                .encode(Modifiers::CTRL, mode_2, true)
2476                .unwrap(),
2477            "\x1b[27;5;44~".to_string()
2478        );
2479    }
2480
2481    #[test]
2482    fn encode_issue_892() {
2483        let mode = KeyCodeEncodeModes {
2484            encoding: KeyboardEncoding::Xterm,
2485            newline_mode: false,
2486            application_cursor_keys: false,
2487            modify_other_keys: None,
2488        };
2489
2490        assert_eq!(
2491            KeyCode::LeftArrow
2492                .encode(Modifiers::NONE, mode, true)
2493                .unwrap(),
2494            "\x1b[D".to_string()
2495        );
2496        assert_eq!(
2497            KeyCode::LeftArrow
2498                .encode(Modifiers::ALT, mode, true)
2499                .unwrap(),
2500            "\x1b[1;3D".to_string()
2501        );
2502        assert_eq!(
2503            KeyCode::Home.encode(Modifiers::NONE, mode, true).unwrap(),
2504            "\x1b[H".to_string()
2505        );
2506        assert_eq!(
2507            KeyCode::Home.encode(Modifiers::ALT, mode, true).unwrap(),
2508            "\x1b[1;3H".to_string()
2509        );
2510        assert_eq!(
2511            KeyCode::End.encode(Modifiers::NONE, mode, true).unwrap(),
2512            "\x1b[F".to_string()
2513        );
2514        assert_eq!(
2515            KeyCode::End.encode(Modifiers::ALT, mode, true).unwrap(),
2516            "\x1b[1;3F".to_string()
2517        );
2518        assert_eq!(
2519            KeyCode::Tab.encode(Modifiers::ALT, mode, true).unwrap(),
2520            "\x1b\t".to_string()
2521        );
2522        assert_eq!(
2523            KeyCode::PageUp.encode(Modifiers::ALT, mode, true).unwrap(),
2524            "\x1b[5;3~".to_string()
2525        );
2526        assert_eq!(
2527            KeyCode::Function(1)
2528                .encode(Modifiers::NONE, mode, true)
2529                .unwrap(),
2530            "\x1bOP".to_string()
2531        );
2532    }
2533
2534    #[test]
2535    fn partial_bracketed_paste() {
2536        let mut p = InputParser::new();
2537
2538        let input = b"\x1b[200~1234";
2539        let input2 = b"5678\x1b[201~";
2540
2541        let mut inputs = vec![];
2542
2543        p.parse(input, |e| inputs.push(e), false);
2544        p.parse(input2, |e| inputs.push(e), false);
2545
2546        assert_eq!(vec![InputEvent::Paste("12345678".to_owned())], inputs)
2547    }
2548
2549    #[test]
2550    fn mouse_horizontal_scroll() {
2551        let mut p = InputParser::new();
2552
2553        let input = b"\x1b[<66;42;12M\x1b[<67;42;12M";
2554        let res = p.parse_as_vec(input, MAYBE_MORE);
2555
2556        assert_eq!(
2557            vec![
2558                InputEvent::Mouse(MouseEvent {
2559                    x: 42,
2560                    y: 12,
2561                    mouse_buttons: MouseButtons::HORZ_WHEEL | MouseButtons::WHEEL_POSITIVE,
2562                    modifiers: Modifiers::NONE,
2563                }),
2564                InputEvent::Mouse(MouseEvent {
2565                    x: 42,
2566                    y: 12,
2567                    mouse_buttons: MouseButtons::HORZ_WHEEL,
2568                    modifiers: Modifiers::NONE,
2569                })
2570            ],
2571            res
2572        );
2573    }
2574
2575    #[test]
2576    fn encode_issue_3478_xterm() {
2577        let mode = KeyCodeEncodeModes {
2578            encoding: KeyboardEncoding::Xterm,
2579            newline_mode: false,
2580            application_cursor_keys: false,
2581            modify_other_keys: None,
2582        };
2583
2584        assert_eq!(
2585            KeyCode::Numpad0
2586                .encode(Modifiers::NONE, mode, true)
2587                .unwrap(),
2588            "\u{1b}[2~".to_string()
2589        );
2590        assert_eq!(
2591            KeyCode::Numpad0
2592                .encode(Modifiers::SHIFT, mode, true)
2593                .unwrap(),
2594            "\u{1b}[2;2~".to_string()
2595        );
2596
2597        assert_eq!(
2598            KeyCode::Numpad1
2599                .encode(Modifiers::NONE, mode, true)
2600                .unwrap(),
2601            "\u{1b}[F".to_string()
2602        );
2603        assert_eq!(
2604            KeyCode::Numpad1
2605                .encode(Modifiers::NONE | Modifiers::SHIFT, mode, true)
2606                .unwrap(),
2607            "\u{1b}[1;2F".to_string()
2608        );
2609    }
2610
2611    #[test]
2612    fn encode_tab_with_modifiers() {
2613        let mode = KeyCodeEncodeModes {
2614            encoding: KeyboardEncoding::Xterm,
2615            newline_mode: false,
2616            application_cursor_keys: false,
2617            modify_other_keys: None,
2618        };
2619
2620        let mods_to_result = [
2621            (Modifiers::SHIFT, "\u{1b}[Z"),
2622            (Modifiers::SHIFT | Modifiers::LEFT_SHIFT, "\u{1b}[Z"),
2623            (Modifiers::SHIFT | Modifiers::RIGHT_SHIFT, "\u{1b}[Z"),
2624            (Modifiers::CTRL, "\u{1b}[9;5u"),
2625            (Modifiers::CTRL | Modifiers::LEFT_CTRL, "\u{1b}[9;5u"),
2626            (Modifiers::CTRL | Modifiers::RIGHT_CTRL, "\u{1b}[9;5u"),
2627            (
2628                Modifiers::SHIFT | Modifiers::CTRL | Modifiers::LEFT_CTRL | Modifiers::LEFT_SHIFT,
2629                "\u{1b}[1;5Z",
2630            ),
2631        ];
2632        for (mods, result) in mods_to_result {
2633            assert_eq!(
2634                KeyCode::Tab.encode(mods, mode, true).unwrap(),
2635                result,
2636                "{:?}",
2637                mods
2638            );
2639        }
2640    }
2641
2642    #[test]
2643    fn mouse_button1_press() {
2644        let mut p = InputParser::new();
2645        let res = p.parse_as_vec(b"\x1b[<0;42;12M", true);
2646        assert_eq!(
2647            res,
2648            vec![InputEvent::Mouse(MouseEvent {
2649                x: 42,
2650                y: 12,
2651                mouse_buttons: MouseButtons::LEFT,
2652                modifiers: Modifiers::NONE,
2653            })]
2654        );
2655    }
2656
2657    #[test]
2658    fn mouse_button1_release() {
2659        let mut p = InputParser::new();
2660        let res = p.parse_as_vec(b"\x1b[<0;42;12m", true);
2661        assert_eq!(
2662            res,
2663            vec![InputEvent::Mouse(MouseEvent {
2664                x: 42,
2665                y: 12,
2666                mouse_buttons: MouseButtons::NONE,
2667                modifiers: Modifiers::NONE,
2668            })]
2669        );
2670    }
2671
2672    #[test]
2673    fn mouse_button3_with_shift() {
2674        let mut p = InputParser::new();
2675        // button 2 (right) = 2, SHIFT adds 4 to p0 -> 6
2676        let res = p.parse_as_vec(b"\x1b[<6;10;20M", true);
2677        assert_eq!(
2678            res,
2679            vec![InputEvent::Mouse(MouseEvent {
2680                x: 10,
2681                y: 20,
2682                mouse_buttons: MouseButtons::RIGHT,
2683                modifiers: Modifiers::SHIFT,
2684            })]
2685        );
2686    }
2687
2688    #[test]
2689    fn mouse_drag() {
2690        let mut p = InputParser::new();
2691        // button1 drag = 32
2692        let res = p.parse_as_vec(b"\x1b[<32;5;5M", true);
2693        assert_eq!(
2694            res,
2695            vec![InputEvent::Mouse(MouseEvent {
2696                x: 5,
2697                y: 5,
2698                mouse_buttons: MouseButtons::LEFT,
2699                modifiers: Modifiers::NONE,
2700            })]
2701        );
2702    }
2703
2704    #[test]
2705    fn mouse_vertical_scroll_up() {
2706        let mut p = InputParser::new();
2707        // button4 press = 64
2708        let res = p.parse_as_vec(b"\x1b[<64;1;1M", true);
2709        assert_eq!(
2710            res,
2711            vec![InputEvent::Mouse(MouseEvent {
2712                x: 1,
2713                y: 1,
2714                mouse_buttons: MouseButtons::VERT_WHEEL | MouseButtons::WHEEL_POSITIVE,
2715                modifiers: Modifiers::NONE,
2716            })]
2717        );
2718    }
2719
2720    #[test]
2721    fn mouse_vertical_scroll_down() {
2722        let mut p = InputParser::new();
2723        // button5 press = 65
2724        let res = p.parse_as_vec(b"\x1b[<65;1;1M", true);
2725        assert_eq!(
2726            res,
2727            vec![InputEvent::Mouse(MouseEvent {
2728                x: 1,
2729                y: 1,
2730                mouse_buttons: MouseButtons::VERT_WHEEL,
2731                modifiers: Modifiers::NONE,
2732            })]
2733        );
2734    }
2735
2736    #[test]
2737    fn mouse_motion_no_buttons() {
2738        let mut p = InputParser::new();
2739        // motion with no buttons = 35
2740        let res = p.parse_as_vec(b"\x1b[<35;10;10M", true);
2741        assert_eq!(
2742            res,
2743            vec![InputEvent::Mouse(MouseEvent {
2744                x: 10,
2745                y: 10,
2746                mouse_buttons: MouseButtons::NONE,
2747                modifiers: Modifiers::NONE,
2748            })]
2749        );
2750    }
2751
2752    #[test]
2753    fn mouse_with_ctrl_alt() {
2754        let mut p = InputParser::new();
2755        // button1 press = 0, ALT=8, CTRL=16 -> 0+8+16=24
2756        let res = p.parse_as_vec(b"\x1b[<24;1;1M", true);
2757        assert_eq!(
2758            res,
2759            vec![InputEvent::Mouse(MouseEvent {
2760                x: 1,
2761                y: 1,
2762                mouse_buttons: MouseButtons::LEFT,
2763                modifiers: Modifiers::ALT | Modifiers::CTRL,
2764            })]
2765        );
2766    }
2767
2768    #[test]
2769    fn mouse_large_coordinates() {
2770        let mut p = InputParser::new();
2771        let res = p.parse_as_vec(b"\x1b[<0;999;999M", true);
2772        assert_eq!(
2773            res,
2774            vec![InputEvent::Mouse(MouseEvent {
2775                x: 999,
2776                y: 999,
2777                mouse_buttons: MouseButtons::LEFT,
2778                modifiers: Modifiers::NONE,
2779            })]
2780        );
2781    }
2782
2783    #[test]
2784    fn mouse_followed_by_key() {
2785        let mut p = InputParser::new();
2786        let res = p.parse_as_vec(b"\x1b[<0;1;1Mhello", false);
2787        assert_eq!(res.len(), 6); // 1 mouse + 5 chars
2788        assert!(matches!(res[0], InputEvent::Mouse(_)));
2789        assert!(matches!(res[1], InputEvent::Key(_)));
2790    }
2791
2792    #[test]
2793    fn two_mouse_events_back_to_back() {
2794        let mut p = InputParser::new();
2795        let res = p.parse_as_vec(b"\x1b[<0;1;1M\x1b[<0;2;2M", true);
2796        assert_eq!(res.len(), 2);
2797    }
2798
2799    /// Regression for the xterm Esc-during-mouse-drag bug:
2800    /// xterm flushes a real Esc keypress as a single `\x1b` byte. If a mouse
2801    /// motion arrives in the next stdin read, upstream `StdinAnsiParser` may
2802    /// concatenate them into `\x1b\x1b[<...M`. Termwiz must parse this as
2803    /// two events (Esc then Mouse), not as Alt+`[` (which would happen if
2804    /// the keymap's `\x1b[`=Alt+`[` registration short-circuits the SGR
2805    /// mouse parser while in `EscapeMaybeAlt` state).
2806    #[test]
2807    fn esc_then_sgr_mouse_emits_esc_and_mouse() {
2808        let mut p = InputParser::new();
2809        let res = p.parse_as_vec(b"\x1b\x1b[<35;42;12M", MAYBE_MORE);
2810        assert_eq!(
2811            res,
2812            vec![
2813                InputEvent::Key(KeyEvent {
2814                    key: KeyCode::Escape,
2815                    modifiers: Modifiers::NONE,
2816                }),
2817                InputEvent::Mouse(MouseEvent {
2818                    x: 42,
2819                    y: 12,
2820                    mouse_buttons: MouseButtons::NONE,
2821                    modifiers: Modifiers::NONE,
2822                }),
2823            ]
2824        );
2825    }
2826
2827    /// Same regression but for the cross-`parse()` case where the parked
2828    /// ESC is in `EscapeMaybeAlt` state from a prior call. The SGR mouse
2829    /// sequence arrives in a subsequent call.
2830    #[test]
2831    fn esc_then_sgr_mouse_across_parse_calls() {
2832        let mut p = InputParser::new();
2833
2834        // First call: lone ESC byte. Termwiz parks no state because the
2835        // first arm only fires when there are bytes after the ESC; with
2836        // `MAYBE_MORE` it leaves the ESC pending in its internal buf and
2837        // emits nothing yet.
2838        let mut res = p.parse_as_vec(b"\x1b", MAYBE_MORE);
2839        assert!(
2840            res.is_empty(),
2841            "lone ESC should not emit yet under MAYBE_MORE"
2842        );
2843
2844        // Second call: the mouse sequence arrives. The buffered ESC plus
2845        // these bytes form `\x1b\x1b[<...M` (the inner buf already has the
2846        // ESC; this call's bytes start with another ESC because that's
2847        // what xterm sends for the mouse sequence). Result must still be
2848        // Esc + Mouse, not Alt+`[`.
2849        res = p.parse_as_vec(b"\x1b[<35;42;12M", MAYBE_MORE);
2850        assert_eq!(
2851            res,
2852            vec![
2853                InputEvent::Key(KeyEvent {
2854                    key: KeyCode::Escape,
2855                    modifiers: Modifiers::NONE,
2856                }),
2857                InputEvent::Mouse(MouseEvent {
2858                    x: 42,
2859                    y: 12,
2860                    mouse_buttons: MouseButtons::NONE,
2861                    modifiers: Modifiers::NONE,
2862                }),
2863            ]
2864        );
2865    }
2866
2867    /// Real Alt+Esc keystroke (`\x1b\x1b` with no further bytes) must
2868    /// still be recognised as Alt+Esc — the fix above must not regress
2869    /// this convention.
2870    #[test]
2871    fn alt_esc_still_recognized() {
2872        let mut p = InputParser::new();
2873        let res = p.parse_as_vec(b"\x1b\x1b", NO_MORE);
2874        assert_eq!(
2875            res,
2876            vec![InputEvent::Key(KeyEvent {
2877                key: KeyCode::Escape,
2878                modifiers: Modifiers::ALT,
2879            })]
2880        );
2881    }
2882
2883    /// Esc keystroke followed by an OSC host reply (e.g. an OSC 11 color
2884    /// query response that arrives concatenated after a stray Esc byte
2885    /// the user pressed) must emit Esc and the OSC, not Alt-modify the
2886    /// OSC bytes.
2887    #[test]
2888    fn esc_then_osc_emits_esc_and_osc() {
2889        let mut p = InputParser::new();
2890        let res = p.parse_as_vec(b"\x1b\x1b]11;rgb:ffff/ffff/ffff\x1b\\", MAYBE_MORE);
2891        assert_eq!(
2892            res,
2893            vec![
2894                InputEvent::Key(KeyEvent {
2895                    key: KeyCode::Escape,
2896                    modifiers: Modifiers::NONE,
2897                }),
2898                InputEvent::OperatingSystemCommand(b"11;rgb:ffff/ffff/ffff".to_vec()),
2899            ]
2900        );
2901    }
2902
2903    /// Esc followed by a CSI host-reply (whitelisted final byte). Must
2904    /// emit Esc and the report, never Alt+`[`.
2905    #[test]
2906    fn esc_then_csi_report_emits_esc_and_report() {
2907        let mut p = InputParser::new();
2908        // \x1b[?2026;0$y is a DECRPM reply for synchronised output mode.
2909        // Wrapped behind a stray Esc keystroke prefix.
2910        let res = p.parse_as_vec(b"\x1b\x1b[?2026;0$y", MAYBE_MORE);
2911        assert!(
2912            !res.is_empty(),
2913            "expected at least one event from Esc + CSI report"
2914        );
2915        assert!(
2916            matches!(
2917                res[0],
2918                InputEvent::Key(KeyEvent {
2919                    key: KeyCode::Escape,
2920                    modifiers: Modifiers::NONE,
2921                })
2922            ),
2923            "first event must be a bare Esc keystroke, got {:?}",
2924            res[0]
2925        );
2926        // The CSI report dispatches as DeviceControlReply via the
2927        // `parse_csi_report` whitelist. Anything but Alt+`[` is acceptable
2928        // for the second event; what we are guarding against is the
2929        // spurious Alt+`[` dispatch.
2930        for ev in &res {
2931            if let InputEvent::Key(KeyEvent { key, modifiers }) = ev {
2932                assert!(
2933                    !(matches!(key, KeyCode::Char('[')) && modifiers.contains(Modifiers::ALT)),
2934                    "must not emit Alt+`[`; got {:?}",
2935                    ev
2936                );
2937            }
2938        }
2939    }
2940
2941    #[test]
2942    fn invalid_sgr_mouse_falls_through() {
2943        let mut p = InputParser::new();
2944        // Invalid: missing terminator, not enough params
2945        let res = p.parse_as_vec(b"\x1b[<0;1M", false);
2946        // Should NOT parse as mouse - falls through to keymap
2947        assert!(res.iter().all(|e| matches!(e, InputEvent::Key(_))));
2948    }
2949
2950    #[test]
2951    fn osc_bel_terminated() {
2952        // Complete OSC sequence with BEL terminator
2953        let mut p = InputParser::new();
2954        let inputs = p.parse_as_vec(b"\x1b]99;i=test:p=title;Hello\x07", NO_MORE);
2955        assert_eq!(
2956            vec![InputEvent::OperatingSystemCommand(
2957                b"99;i=test:p=title;Hello".to_vec()
2958            )],
2959            inputs
2960        );
2961    }
2962
2963    #[test]
2964    fn osc_st_terminated() {
2965        // Complete OSC sequence with ST terminator (ESC \)
2966        let mut p = InputParser::new();
2967        let inputs = p.parse_as_vec(b"\x1b]99;i=test:p=title;Hello\x1b\\", NO_MORE);
2968        assert_eq!(
2969            vec![InputEvent::OperatingSystemCommand(
2970                b"99;i=test:p=title;Hello".to_vec()
2971            )],
2972            inputs
2973        );
2974    }
2975
2976    #[test]
2977    fn osc_partial_across_reads() {
2978        // OSC sequence split across two reads — must buffer first part
2979        let mut p = InputParser::new();
2980        let mut inputs = Vec::new();
2981        p.parse(
2982            b"\x1b]99;i=test:p=title;Hel",
2983            |evt| inputs.push(evt),
2984            MAYBE_MORE,
2985        );
2986        assert!(inputs.is_empty(), "no events yet - sequence incomplete");
2987        p.parse(b"lo\x1b\\", |evt| inputs.push(evt), MAYBE_MORE);
2988        assert_eq!(
2989            vec![InputEvent::OperatingSystemCommand(
2990                b"99;i=test:p=title;Hello".to_vec()
2991            )],
2992            inputs
2993        );
2994    }
2995
2996    #[test]
2997    fn osc_followed_by_keypress() {
2998        // OSC sequence then regular key in same buffer
2999        let mut p = InputParser::new();
3000        let inputs = p.parse_as_vec(b"\x1b]99;i=test;clicked\x07x", NO_MORE);
3001        assert_eq!(
3002            vec![
3003                InputEvent::OperatingSystemCommand(b"99;i=test;clicked".to_vec()),
3004                InputEvent::Key(KeyEvent {
3005                    modifiers: Modifiers::NONE,
3006                    key: KeyCode::Char('x'),
3007                }),
3008            ],
3009            inputs
3010        );
3011    }
3012
3013    #[test]
3014    fn keypress_followed_by_osc() {
3015        // Regular key then OSC sequence in same buffer
3016        let mut p = InputParser::new();
3017        let inputs = p.parse_as_vec(b"x\x1b]99;i=test;clicked\x07", NO_MORE);
3018        assert_eq!(
3019            vec![
3020                InputEvent::Key(KeyEvent {
3021                    modifiers: Modifiers::NONE,
3022                    key: KeyCode::Char('x'),
3023                }),
3024                InputEvent::OperatingSystemCommand(b"99;i=test;clicked".to_vec()),
3025            ],
3026            inputs
3027        );
3028    }
3029
3030    #[test]
3031    fn osc_incomplete_degrades_to_keys() {
3032        // Incomplete OSC that never gets a terminator — when finalized with
3033        // maybe_more=false, must degrade to individual key events (not hang)
3034        let mut p = InputParser::new();
3035        let mut inputs = Vec::new();
3036        p.parse(b"\x1b]99;no-terminator", |evt| inputs.push(evt), MAYBE_MORE);
3037        assert!(inputs.is_empty(), "buffered while maybe_more=true");
3038        p.parse(b"", |evt| inputs.push(evt), NO_MORE);
3039        assert!(!inputs.is_empty(), "must emit something on finalization");
3040    }
3041
3042    #[test]
3043    fn osc_non_99_code() {
3044        // Non-99 OSC codes are also captured as OperatingSystemCommand
3045        let mut p = InputParser::new();
3046        let inputs = p.parse_as_vec(b"\x1b]11;rgb:0000/0000/0000\x1b\\", NO_MORE);
3047        assert_eq!(
3048            vec![InputEvent::OperatingSystemCommand(
3049                b"11;rgb:0000/0000/0000".to_vec()
3050            )],
3051            inputs
3052        );
3053    }
3054
3055    #[test]
3056    fn osc_empty_payload() {
3057        // Edge case: OSC with no payload between \x1b] and terminator
3058        let mut p = InputParser::new();
3059        let inputs = p.parse_as_vec(b"\x1b]\x07", NO_MORE);
3060        assert_eq!(
3061            vec![InputEvent::OperatingSystemCommand(b"".to_vec())],
3062            inputs
3063        );
3064    }
3065
3066    #[test]
3067    fn csi_not_captured_as_osc() {
3068        // ESC [ (CSI) must NOT be captured as an OSC sequence.
3069        // This validates that only ESC ] triggers OSC parsing.
3070        let mut p = InputParser::new();
3071        let inputs = p.parse_as_vec(b"\x1b[A", NO_MORE);
3072        assert_eq!(
3073            vec![InputEvent::Key(KeyEvent {
3074                modifiers: Modifiers::NONE,
3075                key: KeyCode::UpArrow,
3076            })],
3077            inputs
3078        );
3079    }
3080
3081    // =====================================================================
3082    // parse_csi_report (CSI report whitelist for host-reply forwarding)
3083    // =====================================================================
3084
3085    fn csi_reply(intermediates: &[u8], params: &[u8], final_byte: u8, raw: &[u8]) -> InputEvent {
3086        InputEvent::DeviceControlReply {
3087            intermediates: intermediates.to_vec(),
3088            params: params.to_vec(),
3089            final_byte,
3090            raw: raw.to_vec(),
3091        }
3092    }
3093
3094    #[test]
3095    fn csi_report_recognises_each_whitelisted_final_byte() {
3096        // `t` — pixel-dimension reply form `\x1b[4;H;Wt`.
3097        let bytes = b"\x1b[4;600;800t";
3098        let (evt, consumed) = parse_csi_report(bytes).expect("t accepted");
3099        assert_eq!(consumed, bytes.len());
3100        assert_eq!(evt, csi_reply(b"", b"4;600;800", b't', bytes));
3101
3102        // `y` — DECRPM, e.g. sync-output support. Intermediate `$`.
3103        let bytes = b"\x1b[?2026;1$y";
3104        let (evt, consumed) = parse_csi_report(bytes).expect("y accepted");
3105        assert_eq!(consumed, bytes.len());
3106        assert_eq!(evt, csi_reply(b"$", b"?2026;1", b'y', bytes));
3107
3108        // `c` — Primary-DA reply (barrier).
3109        let bytes = b"\x1b[?62;1;6c";
3110        let (evt, consumed) = parse_csi_report(bytes).expect("c accepted");
3111        assert_eq!(consumed, bytes.len());
3112        assert_eq!(evt, csi_reply(b"", b"?62;1;6", b'c', bytes));
3113
3114        // `n` — DSR reply (used for theme notifications).
3115        let bytes = b"\x1b[?997;1n";
3116        let (evt, consumed) = parse_csi_report(bytes).expect("n accepted");
3117        assert_eq!(consumed, bytes.len());
3118        assert_eq!(evt, csi_reply(b"", b"?997;1", b'n', bytes));
3119    }
3120
3121    #[test]
3122    fn csi_report_preserves_intermediates() {
3123        // DECRPM uses `$` as its intermediate byte — it must land in
3124        // `intermediates`, not `params`.
3125        let bytes = b"\x1b[?2026;2$y";
3126        let (evt, _len) = parse_csi_report(bytes).expect("DECRPM accepted");
3127        let InputEvent::DeviceControlReply {
3128            intermediates,
3129            params,
3130            final_byte,
3131            raw,
3132        } = evt
3133        else {
3134            panic!("expected DeviceControlReply, got {:?}", evt);
3135        };
3136        assert_eq!(intermediates, b"$");
3137        assert_eq!(params, b"?2026;2");
3138        assert_eq!(final_byte, b'y');
3139        assert_eq!(raw, bytes);
3140    }
3141
3142    #[test]
3143    fn csi_report_rejects_non_whitelisted_final_bytes() {
3144        // `A` = cursor-up (keyboard input, not a report).
3145        assert!(parse_csi_report(b"\x1b[A").is_none());
3146        // `R` = cursor-position report — not whitelisted; must pass
3147        // through to the keyboard path.
3148        assert!(parse_csi_report(b"\x1b[24;80R").is_none());
3149        // `m` = SGR; appears in render streams but should never reach
3150        // stdin as a report.
3151        assert!(parse_csi_report(b"\x1b[0m").is_none());
3152    }
3153
3154    #[test]
3155    fn csi_report_returns_none_on_truncated_input() {
3156        // No final byte within the supplied slice → caller should wait
3157        // for more bytes; `parse_csi_report` must not "commit" to a
3158        // partial parse.
3159        assert!(parse_csi_report(b"\x1b[4;600;800").is_none());
3160        // Only the lead-in; parameters haven't started.
3161        assert!(parse_csi_report(b"\x1b[").is_none());
3162        // Empty input — zero bytes to consume.
3163        assert!(parse_csi_report(b"").is_none());
3164    }
3165
3166    #[test]
3167    fn csi_report_raw_preserves_input_byte_for_byte() {
3168        // `raw` must include the leading ESC through the final byte
3169        // inclusive, without adding or dropping any byte — the
3170        // forwarding path writes it verbatim to the pane's pty.
3171        let bytes = b"\x1b[4;16;8t";
3172        let (evt, consumed) = parse_csi_report(bytes).expect("accepted");
3173        assert_eq!(consumed, bytes.len());
3174        let InputEvent::DeviceControlReply { raw, .. } = evt else {
3175            panic!("wrong variant");
3176        };
3177        assert_eq!(&raw[..], bytes, "raw must be byte-identical to input");
3178    }
3179}