termina/
parse.rs

1// CREDIT: This is nearly all crossterm (with modifications and additions).
2// <https://github.com/crossterm-rs/crossterm/blob/36d95b26a26e64b0f8c12edfe11f410a6d56a812/src/event/sys/unix/parse.rs>
3// See a below credit comment about the `decode_input_records` function however.
4// I have extended the parsing functions from
5//
6// Crossterm comments say that the parser is a bit scary and probably in need of a refactor. I
7// like this approach though since it's quite easy to read and test. I'm unsure of the performance
8// though because of the loop in `process_bytes`: we consider the bytes as an increasing slice of
9// the buffer until it becomes valid or invalid. WezTerm and Alacritty have more formal parsers
10// (`vtparse` and `vte`, respectively) but I'm unsure of using a terminal program's parser since
11// it may be larger or more complex than an application needs.
12use std::{collections::VecDeque, num::NonZeroU16, str};
13
14use crate::{
15    escape::{
16        self,
17        csi::{self, Csi, KittyKeyboardFlags, ThemeMode},
18        dcs,
19    },
20    event::{
21        KeyCode, KeyEvent, KeyEventKind, KeyEventState, MediaKeyCode, ModifierKeyCode, Modifiers,
22        MouseButton, MouseEvent, MouseEventKind,
23    },
24    style, Event,
25};
26
27/// A parser for ANSI escape sequences.
28#[derive(Debug)]
29pub struct Parser {
30    buffer: Vec<u8>,
31    /// Events which have been parsed. Pop out with `Self::pop`.
32    events: VecDeque<Event>,
33}
34
35impl Default for Parser {
36    fn default() -> Self {
37        Self {
38            buffer: Vec::with_capacity(256),
39            events: VecDeque::with_capacity(32),
40        }
41    }
42}
43
44impl Parser {
45    /// Reads and removes a parsed event from the parser.
46    pub fn pop(&mut self) -> Option<Event> {
47        self.events.pop_front()
48    }
49
50    /// Parses additional data into the buffer.
51    /// Parsed events can be retrieved using [Parser::pop].
52    ///
53    /// `maybe_more` should be set to true if the input might be a partial sequence.
54    pub fn parse(&mut self, bytes: &[u8], maybe_more: bool) {
55        self.buffer.extend_from_slice(bytes);
56        self.process_bytes(maybe_more);
57    }
58
59    fn process_bytes(&mut self, maybe_more: bool) {
60        let mut start = 0;
61        for n in 0..self.buffer.len() {
62            let end = n + 1;
63            match parse_event(
64                &self.buffer[start..end],
65                maybe_more || end < self.buffer.len(),
66            ) {
67                Ok(Some(event)) => {
68                    self.events.push_back(event);
69                    start = end;
70                }
71                Ok(None) => continue,
72                Err(_) => start = end,
73            }
74        }
75        self.advance(start);
76    }
77
78    fn advance(&mut self, len: usize) {
79        if len == 0 {
80            return;
81        }
82        let remain = self.buffer.len() - len;
83        self.buffer.rotate_left(len);
84        self.buffer.truncate(remain);
85    }
86}
87
88// CREDIT: <https://github.com/wezterm/wezterm/blob/a87358516004a652ad840bc1661bdf65ffc89b43/termwiz/src/input.rs#L676-L885>
89// I have dropped the legacy Console API handling however and switched to the `AsciiChar` part of
90// the key record. I suspect that Termwiz may be incorrect here as the Microsoft docs say that the
91// proper way to read UTF-8 is to use the `A` variant (`ReadConsoleInputA` while WezTerm uses
92// `ReadConsoleInputW`) to read a byte.
93#[cfg(windows)]
94mod windows {
95    use windows_sys::Win32::System::Console;
96
97    use crate::{OneBased, WindowSize};
98
99    use super::*;
100
101    impl Parser {
102        pub(crate) fn decode_input_records(&mut self, records: &[Console::INPUT_RECORD]) {
103            for record in records {
104                match record.EventType as u32 {
105                    Console::KEY_EVENT => {
106                        let record = unsafe { record.Event.KeyEvent };
107                        // This skips 'down's. IIRC Termwiz skips 'down's and Crossterm skips
108                        // 'up's. If we skip 'up's we don't seem to get key events at all.
109                        if record.bKeyDown == 0 {
110                            continue;
111                        }
112                        let byte = unsafe { record.uChar.AsciiChar } as u8;
113                        // The zero byte is sent when the input record is not VT.
114                        if byte == 0 {
115                            continue;
116                        }
117                        // `read_console_input` uses `ReadConsoleInputA` so we should treat the
118                        // key code as a byte and add it to the buffer.
119                        self.buffer.push(byte);
120                    }
121                    Console::WINDOW_BUFFER_SIZE_EVENT => {
122                        // NOTE: the `WINDOW_BUFFER_SIZE_EVENT` coordinates are one-based, even
123                        // though `GetConsoleScreenBufferInfo` is zero-based.
124                        let record = unsafe { record.Event.WindowBufferSizeEvent };
125                        let Some(rows) = OneBased::new(record.dwSize.Y as u16) else {
126                            continue;
127                        };
128                        let Some(cols) = OneBased::new(record.dwSize.X as u16) else {
129                            continue;
130                        };
131                        self.events.push_back(Event::WindowResized(WindowSize {
132                            rows: rows.get(),
133                            cols: cols.get(),
134                            pixel_width: None,
135                            pixel_height: None,
136                        }));
137                    }
138                    _ => (),
139                }
140            }
141            self.process_bytes(false);
142        }
143    }
144}
145
146#[derive(Debug)]
147struct MalformedSequenceError;
148
149// This is a bit hacky but cuts down on boilerplate conversions
150impl From<str::Utf8Error> for MalformedSequenceError {
151    fn from(_: str::Utf8Error) -> Self {
152        Self
153    }
154}
155
156type Result<T> = std::result::Result<T, MalformedSequenceError>;
157
158macro_rules! bail {
159    () => {
160        return Err(MalformedSequenceError)
161    };
162}
163
164fn parse_event(buffer: &[u8], maybe_more: bool) -> Result<Option<Event>> {
165    if buffer.is_empty() {
166        return Ok(None);
167    }
168
169    match buffer[0] {
170        b'\x1B' => {
171            if buffer.len() == 1 {
172                if maybe_more {
173                    // Possible Esc sequence
174                    Ok(None)
175                } else {
176                    Ok(Some(Event::Key(KeyCode::Escape.into())))
177                }
178            } else {
179                match buffer[1] {
180                    b'O' => {
181                        if buffer.len() == 2 {
182                            Ok(None)
183                        } else {
184                            match buffer[2] {
185                                b'D' => Ok(Some(Event::Key(KeyCode::Left.into()))),
186                                b'C' => Ok(Some(Event::Key(KeyCode::Right.into()))),
187                                b'A' => Ok(Some(Event::Key(KeyCode::Up.into()))),
188                                b'B' => Ok(Some(Event::Key(KeyCode::Down.into()))),
189                                b'H' => Ok(Some(Event::Key(KeyCode::Home.into()))),
190                                b'F' => Ok(Some(Event::Key(KeyCode::End.into()))),
191                                // F1-F4
192                                val @ b'P'..=b'S' => {
193                                    Ok(Some(Event::Key(KeyCode::Function(1 + val - b'P').into())))
194                                }
195                                _ => bail!(),
196                            }
197                        }
198                    }
199                    b'[' => parse_csi(buffer),
200                    b'P' => parse_dcs(buffer),
201                    b'\x1B' => Ok(Some(Event::Key(KeyCode::Escape.into()))),
202                    _ => parse_event(&buffer[1..], maybe_more).map(|event_option| {
203                        event_option.map(|event| {
204                            if let Event::Key(key_event) = event {
205                                let mut alt_key_event = key_event;
206                                alt_key_event.modifiers |= Modifiers::ALT;
207                                Event::Key(alt_key_event)
208                            } else {
209                                event
210                            }
211                        })
212                    }),
213                }
214            }
215        }
216        b'\r' => Ok(Some(Event::Key(KeyCode::Enter.into()))),
217        b'\t' => Ok(Some(Event::Key(KeyCode::Tab.into()))),
218        b'\x7F' => Ok(Some(Event::Key(KeyCode::Backspace.into()))),
219        b'\0' => Ok(Some(Event::Key(KeyEvent::new(
220            KeyCode::Char(' '),
221            Modifiers::CONTROL,
222        )))),
223        c @ b'\x01'..=b'\x1A' => Ok(Some(Event::Key(KeyEvent::new(
224            KeyCode::Char((c - 0x1 + b'a') as char),
225            Modifiers::CONTROL,
226        )))),
227        c @ b'\x1C'..=b'\x1F' => Ok(Some(Event::Key(KeyEvent::new(
228            KeyCode::Char((c - 0x1C + b'4') as char),
229            Modifiers::CONTROL,
230        )))),
231        _ => parse_utf8_char(buffer).map(|maybe_char| {
232            maybe_char.map(|ch| {
233                let modifiers = if ch.is_uppercase() {
234                    Modifiers::SHIFT
235                } else {
236                    Modifiers::NONE
237                };
238                Event::Key(KeyEvent::new(KeyCode::Char(ch), modifiers))
239            })
240        }),
241    }
242}
243
244fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
245    assert!(!buffer.is_empty());
246    match str::from_utf8(buffer) {
247        Ok(s) => Ok(Some(s.chars().next().unwrap())),
248        Err(_) => {
249            // `from_utf8` failed but it could be because we don't have enough bytes to make a
250            // valid UTF-8 codepoint. Check the validity of the bytes so far:
251            let required_bytes = match buffer[0] {
252                // https://en.wikipedia.org/wiki/UTF-8#Description
253                (0x00..=0x7F) => 1, // 0xxxxxxx
254                (0xC0..=0xDF) => 2, // 110xxxxx 10xxxxxx
255                (0xE0..=0xEF) => 3, // 1110xxxx 10xxxxxx 10xxxxxx
256                (0xF0..=0xF7) => 4, // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
257                (0x80..=0xBF) | (0xF8..=0xFF) => bail!(),
258            };
259            if required_bytes > 1 && buffer.len() > 1 {
260                for byte in &buffer[1..] {
261                    if byte & !0b0011_1111 != 0b1000_0000 {
262                        bail!()
263                    }
264                }
265            }
266            if buffer.len() < required_bytes {
267                Ok(None)
268            } else {
269                bail!()
270            }
271        }
272    }
273}
274
275fn parse_csi(buffer: &[u8]) -> Result<Option<Event>> {
276    assert!(buffer.starts_with(b"\x1B["));
277    if buffer.len() == 2 {
278        return Ok(None);
279    }
280    let maybe_event = match buffer[2] {
281        b'[' => match buffer.get(3) {
282            None => None,
283            Some(b @ b'A'..=b'E') => Some(Event::Key(KeyCode::Function(1 + b - b'A').into())),
284            Some(_) => bail!(),
285        },
286        b'D' => Some(Event::Key(KeyCode::Left.into())),
287        b'C' => Some(Event::Key(KeyCode::Right.into())),
288        b'A' => Some(Event::Key(KeyCode::Up.into())),
289        b'B' => Some(Event::Key(KeyCode::Down.into())),
290        b'H' => Some(Event::Key(KeyCode::Home.into())),
291        b'F' => Some(Event::Key(KeyCode::End.into())),
292        b'Z' => Some(Event::Key(KeyEvent {
293            code: KeyCode::BackTab,
294            modifiers: Modifiers::SHIFT,
295            kind: KeyEventKind::Press,
296            state: KeyEventState::NONE,
297        })),
298        b'M' => return parse_csi_normal_mouse(buffer),
299        b'<' => return parse_csi_sgr_mouse(buffer),
300        b'I' => Some(Event::FocusIn),
301        b'O' => Some(Event::FocusOut),
302        b';' => return parse_csi_modifier_key_code(buffer),
303        // P, Q, and S for compatibility with Kitty keyboard protocol,
304        // as the 1 in 'CSI 1 P' etc. must be omitted if there are no
305        // modifiers pressed:
306        // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-functional-keys
307        b'P' => Some(Event::Key(KeyCode::Function(1).into())),
308        b'Q' => Some(Event::Key(KeyCode::Function(2).into())),
309        b'S' => Some(Event::Key(KeyCode::Function(4).into())),
310        b'?' => match buffer[buffer.len() - 1] {
311            b'u' => return parse_csi_keyboard_enhancement_flags(buffer),
312            b'c' => return parse_csi_primary_device_attributes(buffer),
313            b'n' => return parse_csi_theme_mode(buffer),
314            b'y' => return parse_csi_synchronized_output_mode(buffer),
315            _ => None,
316        },
317        b'0'..=b'9' => {
318            // Numbered escape code.
319            if buffer.len() == 3 {
320                None
321            } else {
322                // The final byte of a CSI sequence can be in the range 64-126, so
323                // let's keep reading anything else.
324                let last_byte = buffer[buffer.len() - 1];
325                if !(64..=126).contains(&last_byte) {
326                    None
327                } else {
328                    if buffer.starts_with(b"\x1B[200~") {
329                        return parse_csi_bracketed_paste(buffer);
330                    }
331                    match last_byte {
332                        b'M' => return parse_csi_rxvt_mouse(buffer),
333                        b'~' => return parse_csi_special_key_code(buffer),
334                        b'u' => return parse_csi_u_encoded_key_code(buffer),
335                        b'R' => return parse_csi_cursor_position(buffer),
336                        _ => return parse_csi_modifier_key_code(buffer),
337                    }
338                }
339            }
340        }
341        _ => bail!(),
342    };
343    Ok(maybe_event)
344}
345
346fn next_parsed<T>(iter: &mut dyn Iterator<Item = &str>) -> Result<T>
347where
348    T: str::FromStr,
349{
350    iter.next()
351        .ok_or(MalformedSequenceError)?
352        .parse::<T>()
353        .map_err(|_| MalformedSequenceError)
354}
355
356fn modifier_and_kind_parsed(iter: &mut dyn Iterator<Item = &str>) -> Result<(u8, u8)> {
357    let mut sub_split = iter.next().ok_or(MalformedSequenceError)?.split(':');
358
359    let modifier_mask = next_parsed::<u8>(&mut sub_split)?;
360
361    if let Ok(kind_code) = next_parsed::<u8>(&mut sub_split) {
362        Ok((modifier_mask, kind_code))
363    } else {
364        Ok((modifier_mask, 1))
365    }
366}
367
368fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Event>> {
369    assert!(buffer.starts_with(b"\x1B")); // CSI
370    assert!(buffer.ends_with(b"u"));
371
372    // This function parses `CSI … u` sequences. These are sequences defined in either
373    // the `CSI u` (a.k.a. "Fix Keyboard Input on Terminals - Please", https://www.leonerd.org.uk/hacks/fixterms/)
374    // or Kitty Keyboard Protocol (https://sw.kovidgoyal.net/kitty/keyboard-protocol/) specifications.
375    // This CSI sequence is a tuple of semicolon-separated numbers.
376    let s = str::from_utf8(&buffer[2..buffer.len() - 1])?;
377    let mut split = s.split(';');
378
379    // In `CSI u`, this is parsed as:
380    //
381    //     CSI codepoint ; modifiers u
382    //     codepoint: ASCII Dec value
383    //
384    // The Kitty Keyboard Protocol extends this with optional components that can be
385    // enabled progressively. The full sequence is parsed as:
386    //
387    //     CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u
388    let mut codepoints = split.next().ok_or(MalformedSequenceError)?.split(':');
389
390    let codepoint = codepoints
391        .next()
392        .ok_or(MalformedSequenceError)?
393        .parse::<u32>()
394        .map_err(|_| MalformedSequenceError)?;
395
396    let (mut modifiers, kind, state_from_modifiers) =
397        if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
398            (
399                parse_modifiers(modifier_mask),
400                parse_key_event_kind(kind_code),
401                parse_modifiers_to_state(modifier_mask),
402            )
403        } else {
404            (Modifiers::NONE, KeyEventKind::Press, KeyEventState::NONE)
405        };
406
407    let (mut code, state_from_keycode) = {
408        if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
409            (special_key_code, state)
410        } else if let Some(c) = char::from_u32(codepoint) {
411            (
412                match c {
413                    '\x1B' => KeyCode::Escape,
414                    '\r' => KeyCode::Enter,
415                    /*
416                    // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
417                    // newlines as input is because the terminal converts \r into \n for us. When we
418                    // enter raw mode, we disable that, so \n no longer has any meaning - it's better to
419                    // use Ctrl+J. Waiting to handle it here means it gets picked up later
420                    '\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter,
421                    */
422                    '\t' => {
423                        if modifiers.contains(Modifiers::SHIFT) {
424                            KeyCode::BackTab
425                        } else {
426                            KeyCode::Tab
427                        }
428                    }
429                    '\x7F' => KeyCode::Backspace,
430                    _ => KeyCode::Char(c),
431                },
432                KeyEventState::empty(),
433            )
434        } else {
435            bail!();
436        }
437    };
438
439    if let KeyCode::Modifier(modifier_keycode) = code {
440        match modifier_keycode {
441            ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt => {
442                modifiers.set(Modifiers::ALT, true)
443            }
444            ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl => {
445                modifiers.set(Modifiers::CONTROL, true)
446            }
447            ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => {
448                modifiers.set(Modifiers::SHIFT, true)
449            }
450            ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => {
451                modifiers.set(Modifiers::SUPER, true)
452            }
453            ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => {
454                modifiers.set(Modifiers::HYPER, true)
455            }
456            ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => {
457                modifiers.set(Modifiers::META, true)
458            }
459            _ => {}
460        }
461    }
462
463    // When the "report alternate keys" flag is enabled in the Kitty Keyboard Protocol
464    // and the terminal sends a keyboard event containing shift, the sequence will
465    // contain an additional codepoint separated by a ':' character which contains
466    // the shifted character according to the keyboard layout.
467    if modifiers.contains(Modifiers::SHIFT) {
468        if let Some(shifted_c) = codepoints
469            .next()
470            .and_then(|codepoint| codepoint.parse::<u32>().ok())
471            .and_then(char::from_u32)
472        {
473            code = KeyCode::Char(shifted_c);
474            modifiers.set(Modifiers::SHIFT, false);
475        }
476    }
477
478    let event = Event::Key(KeyEvent {
479        code,
480        modifiers,
481        kind,
482        state: state_from_keycode | state_from_modifiers,
483    });
484
485    Ok(Some(event))
486}
487
488fn parse_modifiers(mask: u8) -> Modifiers {
489    let modifier_mask = mask.saturating_sub(1);
490    let mut modifiers = Modifiers::empty();
491    if modifier_mask & 1 != 0 {
492        modifiers |= Modifiers::SHIFT;
493    }
494    if modifier_mask & 2 != 0 {
495        modifiers |= Modifiers::ALT;
496    }
497    if modifier_mask & 4 != 0 {
498        modifiers |= Modifiers::CONTROL;
499    }
500    if modifier_mask & 8 != 0 {
501        modifiers |= Modifiers::SUPER;
502    }
503    if modifier_mask & 16 != 0 {
504        modifiers |= Modifiers::HYPER;
505    }
506    if modifier_mask & 32 != 0 {
507        modifiers |= Modifiers::META;
508    }
509    modifiers
510}
511
512fn parse_modifiers_to_state(mask: u8) -> KeyEventState {
513    let modifier_mask = mask.saturating_sub(1);
514    let mut state = KeyEventState::empty();
515    if modifier_mask & 64 != 0 {
516        state |= KeyEventState::CAPS_LOCK;
517    }
518    if modifier_mask & 128 != 0 {
519        state |= KeyEventState::NUM_LOCK;
520    }
521    state
522}
523
524fn parse_key_event_kind(kind: u8) -> KeyEventKind {
525    match kind {
526        1 => KeyEventKind::Press,
527        2 => KeyEventKind::Repeat,
528        3 => KeyEventKind::Release,
529        _ => KeyEventKind::Press,
530    }
531}
532
533fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<Event>> {
534    assert!(buffer.starts_with(b"\x1B[")); // CSI
535    let s = str::from_utf8(&buffer[2..buffer.len() - 1])?;
536    let mut split = s.split(';');
537
538    split.next();
539
540    let (modifiers, kind) =
541        if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
542            (
543                parse_modifiers(modifier_mask),
544                parse_key_event_kind(kind_code),
545            )
546        } else if buffer.len() > 3 {
547            (
548                parse_modifiers(
549                    (buffer[buffer.len() - 2] as char)
550                        .to_digit(10)
551                        .ok_or(MalformedSequenceError)? as u8,
552                ),
553                KeyEventKind::Press,
554            )
555        } else {
556            (Modifiers::NONE, KeyEventKind::Press)
557        };
558    let key = buffer[buffer.len() - 1];
559
560    let code = match key {
561        b'A' => KeyCode::Up,
562        b'B' => KeyCode::Down,
563        b'C' => KeyCode::Right,
564        b'D' => KeyCode::Left,
565        b'F' => KeyCode::End,
566        b'H' => KeyCode::Home,
567        b'P' => KeyCode::Function(1),
568        b'Q' => KeyCode::Function(2),
569        b'R' => KeyCode::Function(3),
570        b'S' => KeyCode::Function(4),
571        _ => bail!(),
572    };
573
574    let event = Event::Key(KeyEvent {
575        code,
576        modifiers,
577        kind,
578        state: KeyEventState::NONE,
579    });
580
581    Ok(Some(event))
582}
583
584fn parse_csi_special_key_code(buffer: &[u8]) -> Result<Option<Event>> {
585    assert!(buffer.starts_with(b"\x1B[")); // CSI
586    assert!(buffer.ends_with(b"~"));
587
588    let s = str::from_utf8(&buffer[2..buffer.len() - 1])?;
589    let mut split = s.split(';');
590
591    // This CSI sequence can be a list of semicolon-separated numbers.
592    let first = next_parsed::<u8>(&mut split)?;
593
594    let (modifiers, kind, state) =
595        if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
596            (
597                parse_modifiers(modifier_mask),
598                parse_key_event_kind(kind_code),
599                parse_modifiers_to_state(modifier_mask),
600            )
601        } else {
602            (Modifiers::NONE, KeyEventKind::Press, KeyEventState::NONE)
603        };
604
605    let code = match first {
606        1 | 7 => KeyCode::Home,
607        2 => KeyCode::Insert,
608        3 => KeyCode::Delete,
609        4 | 8 => KeyCode::End,
610        5 => KeyCode::PageUp,
611        6 => KeyCode::PageDown,
612        v @ 11..=15 => KeyCode::Function(v - 10),
613        v @ 17..=21 => KeyCode::Function(v - 11),
614        v @ 23..=26 => KeyCode::Function(v - 12),
615        v @ 28..=29 => KeyCode::Function(v - 15),
616        v @ 31..=34 => KeyCode::Function(v - 17),
617        _ => bail!(),
618    };
619
620    let event = Event::Key(KeyEvent {
621        code,
622        modifiers,
623        kind,
624        state,
625    });
626
627    Ok(Some(event))
628}
629
630fn translate_functional_key_code(codepoint: u32) -> Option<(KeyCode, KeyEventState)> {
631    if let Some(keycode) = match codepoint {
632        57399 => Some(KeyCode::Char('0')),
633        57400 => Some(KeyCode::Char('1')),
634        57401 => Some(KeyCode::Char('2')),
635        57402 => Some(KeyCode::Char('3')),
636        57403 => Some(KeyCode::Char('4')),
637        57404 => Some(KeyCode::Char('5')),
638        57405 => Some(KeyCode::Char('6')),
639        57406 => Some(KeyCode::Char('7')),
640        57407 => Some(KeyCode::Char('8')),
641        57408 => Some(KeyCode::Char('9')),
642        57409 => Some(KeyCode::Char('.')),
643        57410 => Some(KeyCode::Char('/')),
644        57411 => Some(KeyCode::Char('*')),
645        57412 => Some(KeyCode::Char('-')),
646        57413 => Some(KeyCode::Char('+')),
647        57414 => Some(KeyCode::Enter),
648        57415 => Some(KeyCode::Char('=')),
649        57416 => Some(KeyCode::Char(',')),
650        57417 => Some(KeyCode::Left),
651        57418 => Some(KeyCode::Right),
652        57419 => Some(KeyCode::Up),
653        57420 => Some(KeyCode::Down),
654        57421 => Some(KeyCode::PageUp),
655        57422 => Some(KeyCode::PageDown),
656        57423 => Some(KeyCode::Home),
657        57424 => Some(KeyCode::End),
658        57425 => Some(KeyCode::Insert),
659        57426 => Some(KeyCode::Delete),
660        57427 => Some(KeyCode::KeypadBegin),
661        _ => None,
662    } {
663        return Some((keycode, KeyEventState::KEYPAD));
664    }
665
666    if let Some(keycode) = match codepoint {
667        57358 => Some(KeyCode::CapsLock),
668        57359 => Some(KeyCode::ScrollLock),
669        57360 => Some(KeyCode::NumLock),
670        57361 => Some(KeyCode::PrintScreen),
671        57362 => Some(KeyCode::Pause),
672        57363 => Some(KeyCode::Menu),
673        57376 => Some(KeyCode::Function(13)),
674        57377 => Some(KeyCode::Function(14)),
675        57378 => Some(KeyCode::Function(15)),
676        57379 => Some(KeyCode::Function(16)),
677        57380 => Some(KeyCode::Function(17)),
678        57381 => Some(KeyCode::Function(18)),
679        57382 => Some(KeyCode::Function(19)),
680        57383 => Some(KeyCode::Function(20)),
681        57384 => Some(KeyCode::Function(21)),
682        57385 => Some(KeyCode::Function(22)),
683        57386 => Some(KeyCode::Function(23)),
684        57387 => Some(KeyCode::Function(24)),
685        57388 => Some(KeyCode::Function(25)),
686        57389 => Some(KeyCode::Function(26)),
687        57390 => Some(KeyCode::Function(27)),
688        57391 => Some(KeyCode::Function(28)),
689        57392 => Some(KeyCode::Function(29)),
690        57393 => Some(KeyCode::Function(30)),
691        57394 => Some(KeyCode::Function(31)),
692        57395 => Some(KeyCode::Function(32)),
693        57396 => Some(KeyCode::Function(33)),
694        57397 => Some(KeyCode::Function(34)),
695        57398 => Some(KeyCode::Function(35)),
696        57428 => Some(KeyCode::Media(MediaKeyCode::Play)),
697        57429 => Some(KeyCode::Media(MediaKeyCode::Pause)),
698        57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)),
699        57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)),
700        57432 => Some(KeyCode::Media(MediaKeyCode::Stop)),
701        57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)),
702        57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)),
703        57435 => Some(KeyCode::Media(MediaKeyCode::TrackNext)),
704        57436 => Some(KeyCode::Media(MediaKeyCode::TrackPrevious)),
705        57437 => Some(KeyCode::Media(MediaKeyCode::Record)),
706        57438 => Some(KeyCode::Media(MediaKeyCode::LowerVolume)),
707        57439 => Some(KeyCode::Media(MediaKeyCode::RaiseVolume)),
708        57440 => Some(KeyCode::Media(MediaKeyCode::MuteVolume)),
709        57441 => Some(KeyCode::Modifier(ModifierKeyCode::LeftShift)),
710        57442 => Some(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
711        57443 => Some(KeyCode::Modifier(ModifierKeyCode::LeftAlt)),
712        57444 => Some(KeyCode::Modifier(ModifierKeyCode::LeftSuper)),
713        57445 => Some(KeyCode::Modifier(ModifierKeyCode::LeftHyper)),
714        57446 => Some(KeyCode::Modifier(ModifierKeyCode::LeftMeta)),
715        57447 => Some(KeyCode::Modifier(ModifierKeyCode::RightShift)),
716        57448 => Some(KeyCode::Modifier(ModifierKeyCode::RightControl)),
717        57449 => Some(KeyCode::Modifier(ModifierKeyCode::RightAlt)),
718        57450 => Some(KeyCode::Modifier(ModifierKeyCode::RightSuper)),
719        57451 => Some(KeyCode::Modifier(ModifierKeyCode::RightHyper)),
720        57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)),
721        57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)),
722        57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)),
723        _ => None,
724    } {
725        return Some((keycode, KeyEventState::empty()));
726    }
727
728    None
729}
730
731fn parse_csi_rxvt_mouse(buffer: &[u8]) -> Result<Option<Event>> {
732    // rxvt mouse encoding:
733    // CSI Cb ; Cx ; Cy ; M
734
735    assert!(buffer.starts_with(b"\x1B[")); // CSI
736    assert!(buffer.ends_with(b"M"));
737
738    let s = str::from_utf8(&buffer[2..buffer.len() - 1])?;
739    let mut split = s.split(';');
740
741    let cb = next_parsed::<u8>(&mut split)?
742        .checked_sub(32)
743        .ok_or(MalformedSequenceError)?;
744    let (kind, modifiers) = parse_cb(cb)?;
745
746    let cx = next_parsed::<u16>(&mut split)? - 1;
747    let cy = next_parsed::<u16>(&mut split)? - 1;
748
749    Ok(Some(Event::Mouse(MouseEvent {
750        kind,
751        column: cx,
752        row: cy,
753        modifiers,
754    })))
755}
756
757fn parse_csi_normal_mouse(buffer: &[u8]) -> Result<Option<Event>> {
758    // Normal mouse encoding: CSI M CB Cx Cy (6 characters only).
759
760    assert!(buffer.starts_with(b"\x1B[M")); // CSI M
761
762    if buffer.len() < 6 {
763        return Ok(None);
764    }
765
766    let cb = buffer[3].checked_sub(32).ok_or(MalformedSequenceError)?;
767    let (kind, modifiers) = parse_cb(cb)?;
768
769    // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
770    // Mouse positions are encoded as (value + 32), but the upper left
771    // character position on the terminal is denoted as 1,1.
772    // So, we need to subtract 32 + 1 (33) to keep it synced with the cursor.
773    let cx = u16::from(buffer[4].saturating_sub(33));
774    let cy = u16::from(buffer[5].saturating_sub(33));
775
776    Ok(Some(Event::Mouse(MouseEvent {
777        kind,
778        column: cx,
779        row: cy,
780        modifiers,
781    })))
782}
783
784fn parse_csi_sgr_mouse(buffer: &[u8]) -> Result<Option<Event>> {
785    // CSI < Cb ; Cx ; Cy (;) (M or m)
786
787    assert!(buffer.starts_with(b"\x1B[<")); // CSI <
788
789    if !buffer.ends_with(b"m") && !buffer.ends_with(b"M") {
790        return Ok(None);
791    }
792
793    let s = str::from_utf8(&buffer[3..buffer.len() - 1])?;
794    let mut split = s.split(';');
795
796    let cb = next_parsed::<u8>(&mut split)?;
797    let (kind, modifiers) = parse_cb(cb)?;
798
799    // See http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
800    // The upper left character position on the terminal is denoted as 1,1.
801    // Subtract 1 to keep it synced with cursor
802    let cx = next_parsed::<u16>(&mut split)? - 1;
803    let cy = next_parsed::<u16>(&mut split)? - 1;
804
805    // When button 3 in Cb is used to represent mouse release, you can't tell which button was
806    // released. SGR mode solves this by having the sequence end with a lowercase m if it's a
807    // button release and an uppercase M if it's a button press.
808    //
809    // We've already checked that the last character is a lowercase or uppercase M at the start of
810    // this function, so we just need one if.
811    let kind = if buffer.last() == Some(&b'm') {
812        match kind {
813            MouseEventKind::Down(button) => MouseEventKind::Up(button),
814            other => other,
815        }
816    } else {
817        kind
818    };
819
820    Ok(Some(Event::Mouse(MouseEvent {
821        kind,
822        column: cx,
823        row: cy,
824        modifiers,
825    })))
826}
827
828/// Cb is the byte of a mouse input that contains the button being used, the key modifiers being
829/// held and whether the mouse is dragging or not.
830///
831/// Bit layout of cb, from low to high:
832///
833/// - button number
834/// - button number
835/// - shift
836/// - meta (alt)
837/// - control
838/// - mouse is dragging
839/// - button number
840/// - button number
841fn parse_cb(cb: u8) -> Result<(MouseEventKind, Modifiers)> {
842    let button_number = (cb & 0b0000_0011) | ((cb & 0b1100_0000) >> 4);
843    let dragging = cb & 0b0010_0000 == 0b0010_0000;
844
845    let kind = match (button_number, dragging) {
846        (0, false) => MouseEventKind::Down(MouseButton::Left),
847        (1, false) => MouseEventKind::Down(MouseButton::Middle),
848        (2, false) => MouseEventKind::Down(MouseButton::Right),
849        (0, true) => MouseEventKind::Drag(MouseButton::Left),
850        (1, true) => MouseEventKind::Drag(MouseButton::Middle),
851        (2, true) => MouseEventKind::Drag(MouseButton::Right),
852        (3, false) => MouseEventKind::Up(MouseButton::Left),
853        (3, true) | (4, true) | (5, true) => MouseEventKind::Moved,
854        (4, false) => MouseEventKind::ScrollUp,
855        (5, false) => MouseEventKind::ScrollDown,
856        (6, false) => MouseEventKind::ScrollLeft,
857        (7, false) => MouseEventKind::ScrollRight,
858        // We do not support other buttons.
859        _ => bail!(),
860    };
861
862    let mut modifiers = Modifiers::empty();
863
864    if cb & 0b0000_0100 == 0b0000_0100 {
865        modifiers |= Modifiers::SHIFT;
866    }
867    if cb & 0b0000_1000 == 0b0000_1000 {
868        modifiers |= Modifiers::ALT;
869    }
870    if cb & 0b0001_0000 == 0b0001_0000 {
871        modifiers |= Modifiers::CONTROL;
872    }
873
874    Ok((kind, modifiers))
875}
876
877fn parse_csi_bracketed_paste(buffer: &[u8]) -> Result<Option<Event>> {
878    // CSI 2 0 0 ~ pasted text CSI 2 0 1 ~
879    assert!(buffer.starts_with(b"\x1B[200~"));
880
881    if !buffer.ends_with(b"\x1b[201~") {
882        Ok(None)
883    } else {
884        let paste = String::from_utf8_lossy(&buffer[6..buffer.len() - 6]).to_string();
885        Ok(Some(Event::Paste(paste)))
886    }
887}
888
889fn parse_csi_cursor_position(buffer: &[u8]) -> Result<Option<Event>> {
890    // CSI Cy ; Cx R
891    //   Cy - cursor row number (starting from 1)
892    //   Cx - cursor column number (starting from 1)
893    assert!(buffer.starts_with(b"\x1B[")); // CSI
894    assert!(buffer.ends_with(b"R"));
895
896    let s = str::from_utf8(&buffer[2..buffer.len() - 1])?;
897
898    let mut split = s.split(';');
899
900    let line = next_parsed::<NonZeroU16>(&mut split)?.into();
901    let col = next_parsed::<NonZeroU16>(&mut split)?.into();
902
903    Ok(Some(Event::Csi(Csi::Cursor(
904        csi::Cursor::ActivePositionReport { line, col },
905    ))))
906}
907
908fn parse_csi_keyboard_enhancement_flags(buffer: &[u8]) -> Result<Option<Event>> {
909    // CSI ? flags u
910    assert!(buffer.starts_with(b"\x1B[?")); // ESC [ ?
911    assert!(buffer.ends_with(b"u"));
912
913    if buffer.len() < 5 {
914        return Ok(None);
915    }
916
917    let bits = buffer[3];
918    let mut flags = KittyKeyboardFlags::empty();
919
920    if bits & 1 != 0 {
921        flags |= KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
922    }
923    if bits & 2 != 0 {
924        flags |= KittyKeyboardFlags::REPORT_EVENT_TYPES;
925    }
926    if bits & 4 != 0 {
927        flags |= KittyKeyboardFlags::REPORT_ALTERNATE_KEYS;
928    }
929    if bits & 8 != 0 {
930        flags |= KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
931    }
932    // TODO: support this
933    // if bits & 16 != 0 {
934    //     flags |= KeyboardEnhancementFlags::REPORT_ASSOCIATED_TEXT;
935    // }
936
937    Ok(Some(Event::Csi(Csi::Keyboard(csi::Keyboard::ReportFlags(
938        flags,
939    )))))
940}
941
942fn parse_csi_primary_device_attributes(buffer: &[u8]) -> Result<Option<Event>> {
943    // CSI 64 ; attr1 ; attr2 ; ... ; attrn ; c
944    assert!(buffer.starts_with(b"\x1B[?"));
945    assert!(buffer.ends_with(b"c"));
946
947    // This is a stub for parsing the primary device attributes. This response is not
948    // exposed in the crossterm API so we don't need to parse the individual attributes yet.
949    // See <https://vt100.net/docs/vt510-rm/DA1.html>
950
951    Ok(Some(Event::Csi(Csi::Device(
952        csi::Device::DeviceAttributes(()),
953    ))))
954}
955
956fn parse_csi_theme_mode(buffer: &[u8]) -> Result<Option<Event>> {
957    // dark mode:  CSI ? 997 ; 1 n
958    // light mode: CSI ? 997 ; 2 n
959    assert!(buffer.starts_with(b"\x1B[?"));
960    assert!(buffer.ends_with(b"n"));
961
962    let s = str::from_utf8(&buffer[3..buffer.len() - 1])?;
963
964    let mut split = s.split(';');
965
966    if next_parsed::<u16>(&mut split)? != 997 {
967        bail!();
968    }
969
970    let theme_mode = match next_parsed::<u8>(&mut split)? {
971        1 => ThemeMode::Dark,
972        2 => ThemeMode::Light,
973        _ => bail!(),
974    };
975
976    Ok(Some(Event::Csi(Csi::Mode(csi::Mode::ReportTheme(
977        theme_mode,
978    )))))
979}
980
981fn parse_csi_synchronized_output_mode(buffer: &[u8]) -> Result<Option<Event>> {
982    // CSI ? 2026 ; 0 $ y
983    assert!(buffer.starts_with(b"\x1B[?"));
984    assert!(buffer.ends_with(b"y"));
985
986    let s = str::from_utf8(&buffer[3..buffer.len() - 1])?;
987    let s = match s.strip_suffix('$') {
988        Some(s) => s,
989        None => bail!(),
990    };
991
992    let mut split = s.split(';');
993
994    let mode = csi::DecPrivateModeCode::SynchronizedOutput;
995    if next_parsed::<u16>(&mut split)? != mode as u16 {
996        bail!();
997    }
998
999    // For synchronized output specifically, 3 is undefined and 0 and 4 are treated as "not
1000    // supported."
1001    let setting = match next_parsed::<u8>(&mut split)? {
1002        0 | 4 => csi::DecModeSetting::NotRecognized,
1003        1 => csi::DecModeSetting::Set,
1004        2 => csi::DecModeSetting::Reset,
1005        _ => bail!(),
1006    };
1007
1008    Ok(Some(Event::Csi(Csi::Mode(
1009        csi::Mode::ReportDecPrivateMode {
1010            mode: csi::DecPrivateMode::Code(mode),
1011            setting,
1012        },
1013    ))))
1014}
1015
1016fn parse_dcs(buffer: &[u8]) -> Result<Option<Event>> {
1017    assert!(buffer.starts_with(escape::DCS.as_bytes()));
1018    if !buffer.ends_with(escape::ST.as_bytes()) {
1019        return Ok(None);
1020    }
1021    match buffer[buffer.len() - 3] {
1022        // SGR response: DCS Ps $ r SGR m ST
1023        b'm' => {
1024            if buffer.get(3..5) != Some(b"$r") {
1025                bail!();
1026            }
1027            // NOTE: <https://www.xfree86.org/current/ctlseqs.html> says that '1' is a valid
1028            // request and '0' is invalid while the vt100.net docs for DECRQSS say the opposite.
1029            // Kitty and WezTerm both follow the ctlseqs doc.
1030            let is_request_valid = match buffer[2] {
1031                b'1' => true,
1032                // TODO: don't parse attributes if the request isn't valid?
1033                b'0' => false,
1034                _ => bail!(),
1035            };
1036            let s = str::from_utf8(&buffer[5..buffer.len() - 3])?;
1037            let mut sgrs = Vec::new();
1038            // TODO: is this correct? What about terminals that use ';' for true colors?
1039            for sgr in s.split(';') {
1040                sgrs.push(parse_sgr(sgr)?);
1041            }
1042            Ok(Some(Event::Dcs(dcs::Dcs::Response {
1043                is_request_valid,
1044                value: dcs::DcsResponse::GraphicRendition(sgrs),
1045            })))
1046        }
1047        _ => bail!(),
1048    }
1049}
1050
1051fn parse_sgr(buffer: &str) -> Result<csi::Sgr> {
1052    use csi::Sgr;
1053    use style::*;
1054
1055    let sgr = match buffer {
1056        "0" => Sgr::Reset,
1057        "22" => Sgr::Intensity(Intensity::Normal),
1058        "1" => Sgr::Intensity(Intensity::Bold),
1059        "2" => Sgr::Intensity(Intensity::Dim),
1060        "24" => Sgr::Underline(Underline::None),
1061        "4" => Sgr::Underline(Underline::Single),
1062        "21" => Sgr::Underline(Underline::Double),
1063        "4:3 " => Sgr::Underline(Underline::Curly),
1064        "4:4" => Sgr::Underline(Underline::Dotted),
1065        "4:5" => Sgr::Underline(Underline::Dashed),
1066        "25" => Sgr::Blink(Blink::None),
1067        "5" => Sgr::Blink(Blink::Slow),
1068        "6" => Sgr::Blink(Blink::Rapid),
1069        "3" => Sgr::Italic(true),
1070        "23" => Sgr::Italic(false),
1071        "7" => Sgr::Reverse(true),
1072        "27" => Sgr::Reverse(false),
1073        "8" => Sgr::Invisible(true),
1074        "28" => Sgr::Invisible(false),
1075        "9" => Sgr::StrikeThrough(true),
1076        "29" => Sgr::StrikeThrough(false),
1077        "53" => Sgr::Overline(true),
1078        "55" => Sgr::Overline(false),
1079        "10" => Sgr::Font(Font::Default),
1080        "11" => Sgr::Font(Font::Alternate(1)),
1081        "12" => Sgr::Font(Font::Alternate(2)),
1082        "13" => Sgr::Font(Font::Alternate(3)),
1083        "14" => Sgr::Font(Font::Alternate(4)),
1084        "15" => Sgr::Font(Font::Alternate(5)),
1085        "16" => Sgr::Font(Font::Alternate(6)),
1086        "17" => Sgr::Font(Font::Alternate(7)),
1087        "18" => Sgr::Font(Font::Alternate(8)),
1088        "19" => Sgr::Font(Font::Alternate(9)),
1089        "75" => Sgr::VerticalAlign(VerticalAlign::BaseLine),
1090        "73" => Sgr::VerticalAlign(VerticalAlign::SuperScript),
1091        "74" => Sgr::VerticalAlign(VerticalAlign::SubScript),
1092        "39" => Sgr::Foreground(ColorSpec::Reset),
1093        "30" => Sgr::Foreground(ColorSpec::BLACK),
1094        "31" => Sgr::Foreground(ColorSpec::RED),
1095        "32" => Sgr::Foreground(ColorSpec::GREEN),
1096        "33" => Sgr::Foreground(ColorSpec::YELLOW),
1097        "34" => Sgr::Foreground(ColorSpec::BLUE),
1098        "35" => Sgr::Foreground(ColorSpec::MAGENTA),
1099        "36" => Sgr::Foreground(ColorSpec::CYAN),
1100        "37" => Sgr::Foreground(ColorSpec::WHITE),
1101        "90" => Sgr::Foreground(ColorSpec::BRIGHT_BLACK),
1102        "91" => Sgr::Foreground(ColorSpec::BRIGHT_RED),
1103        "92" => Sgr::Foreground(ColorSpec::BRIGHT_GREEN),
1104        "93" => Sgr::Foreground(ColorSpec::BRIGHT_YELLOW),
1105        "94" => Sgr::Foreground(ColorSpec::BRIGHT_BLUE),
1106        "95" => Sgr::Foreground(ColorSpec::BRIGHT_MAGENTA),
1107        "96" => Sgr::Foreground(ColorSpec::BRIGHT_CYAN),
1108        "97" => Sgr::Foreground(ColorSpec::BRIGHT_WHITE),
1109        "49" => Sgr::Background(ColorSpec::Reset),
1110        "40" => Sgr::Background(ColorSpec::BLACK),
1111        "41" => Sgr::Background(ColorSpec::RED),
1112        "42" => Sgr::Background(ColorSpec::GREEN),
1113        "43" => Sgr::Background(ColorSpec::YELLOW),
1114        "44" => Sgr::Background(ColorSpec::BLUE),
1115        "45" => Sgr::Background(ColorSpec::MAGENTA),
1116        "46" => Sgr::Background(ColorSpec::CYAN),
1117        "47" => Sgr::Background(ColorSpec::WHITE),
1118        "100" => Sgr::Background(ColorSpec::BRIGHT_BLACK),
1119        "101" => Sgr::Background(ColorSpec::BRIGHT_RED),
1120        "102" => Sgr::Background(ColorSpec::BRIGHT_GREEN),
1121        "103" => Sgr::Background(ColorSpec::BRIGHT_YELLOW),
1122        "104" => Sgr::Background(ColorSpec::BRIGHT_BLUE),
1123        "105" => Sgr::Background(ColorSpec::BRIGHT_MAGENTA),
1124        "106" => Sgr::Background(ColorSpec::BRIGHT_CYAN),
1125        "107" => Sgr::Background(ColorSpec::BRIGHT_WHITE),
1126        "59" => Sgr::UnderlineColor(ColorSpec::Reset),
1127        _ => {
1128            let mut split = buffer.split(':').filter(|s| !s.is_empty());
1129            let first = next_parsed::<u8>(&mut split)?;
1130            let color = match next_parsed::<u8>(&mut split)? {
1131                2 => RgbColor {
1132                    red: next_parsed::<u8>(&mut split)?,
1133                    green: next_parsed::<u8>(&mut split)?,
1134                    blue: next_parsed::<u8>(&mut split)?,
1135                }
1136                .into(),
1137                5 => ColorSpec::PaletteIndex(next_parsed::<u8>(&mut split)?),
1138                6 => RgbaColor {
1139                    red: next_parsed::<u8>(&mut split)?,
1140                    green: next_parsed::<u8>(&mut split)?,
1141                    blue: next_parsed::<u8>(&mut split)?,
1142                    alpha: next_parsed::<u8>(&mut split)?,
1143                }
1144                .into(),
1145                _ => bail!(),
1146            };
1147            match first {
1148                38 => Sgr::Foreground(color),
1149                48 => Sgr::Background(color),
1150                58 => Sgr::UnderlineColor(color),
1151                _ => bail!(),
1152            }
1153        }
1154    };
1155    Ok(sgr)
1156}
1157
1158#[cfg(test)]
1159mod test {
1160    use super::*;
1161
1162    #[test]
1163    fn parse_dcs_sgr_response() {
1164        // Example from <https://vt100.net/docs/vt510-rm/DECRPSS.html>
1165        // > If the current graphic rendition is underline, blinking, and reverse, then the
1166        // > terminal responds with the following DECRPSS sequence:
1167        // > DCS 0 $ r 0 ; 4 ; 5 ; 7 m ST
1168        // NOTE: The vt100.net docs have the Ps part of this reversed. 0 is invalid and 1 is
1169        // valid according to the xterm docs. See `parse_dcs`.
1170        let event = parse_event(b"\x1bP0$r0;4;5;7m\x1b\\", false)
1171            .unwrap()
1172            .unwrap();
1173        assert_eq!(
1174            event,
1175            Event::Dcs(dcs::Dcs::Response {
1176                is_request_valid: false,
1177                value: dcs::DcsResponse::GraphicRendition(vec![
1178                    csi::Sgr::Reset,
1179                    csi::Sgr::Underline(style::Underline::Single),
1180                    csi::Sgr::Blink(style::Blink::Slow),
1181                    csi::Sgr::Reverse(true),
1182                ])
1183            })
1184        );
1185    }
1186}