From b5a7a345626ce247d3e663ae4d2f5d0cb63e769e Mon Sep 17 00:00:00 2001 From: ashokDevs Date: Wed, 5 Aug 2026 22:00:21 +0530 Subject: [PATCH] fix: recognize kitty CSI-u codepoints for F1 through F12 Codepoints 57364-57375 (F1-F12) were missing from the kitty-protocol codepoint table, so terminals that send function keys as full CSI-u sequences (e.g. Ghostty, which enables the kitty keyboard protocol by default) had those keys silently dropped. Confirmed live against a running herdr session: injecting the raw F3/F4 codepoint sequences did nothing before this fix and correctly triggered previous_tab/next_tab after. refs #1809 --- src/input/parse.rs | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/input/parse.rs b/src/input/parse.rs index 861280dd84..b4fe0948ea 100644 --- a/src/input/parse.rs +++ b/src/input/parse.rs @@ -262,6 +262,7 @@ fn kitty_codepoint_to_keycode(codepoint: u32) -> Option { 57361 => Some(KeyCode::PrintScreen), 57362 => Some(KeyCode::Pause), 57363 => Some(KeyCode::Menu), + 57364..=57375 => Some(KeyCode::F((codepoint - 57364 + 1) as u8)), 57376..=57398 => Some(KeyCode::F((codepoint - 57376 + 13) as u8)), 57399 => Some(KeyCode::Char('0')), 57400 => Some(KeyCode::Char('1')), @@ -318,15 +319,10 @@ fn kitty_codepoint_to_keycode(codepoint: u32) -> Option { 57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)), 57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)), 57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)), - value if is_kitty_functional_codepoint(value) => None, value => char::from_u32(value).map(KeyCode::Char), } } -fn is_kitty_functional_codepoint(codepoint: u32) -> bool { - (57358..=57454).contains(&codepoint) -} - #[allow(dead_code)] // Reserved for the upcoming raw stdin parser. fn key_modifiers_from_u8(modifier: u8) -> KeyModifiers { let mut mods = KeyModifiers::empty(); @@ -851,8 +847,27 @@ mod tests { } #[test] - fn unknown_kitty_functional_key_remains_unsupported() { - assert!(parse_terminal_key_sequence("\x1b[57364;1u").is_none()); + fn kitty_f1_through_f12_codepoints_are_recognized() { + let cases = [ + ("\x1b[57364;1u", KeyCode::F(1)), + ("\x1b[57365;1u", KeyCode::F(2)), + ("\x1b[57366;1u", KeyCode::F(3)), + ("\x1b[57367;1u", KeyCode::F(4)), + ("\x1b[57368;1u", KeyCode::F(5)), + ("\x1b[57375;1u", KeyCode::F(12)), + ("\x1b[57376;1u", KeyCode::F(13)), + ]; + + for (sequence, code) in cases { + let parsed = parse_terminal_key_sequence(sequence).unwrap(); + assert_terminal_key_eq( + parsed, + code, + KeyModifiers::empty(), + crossterm::event::KeyEventKind::Press, + None, + ); + } } fn assert_fixture_corpus_parses(corpus: &str) {