diff --git a/examples/event-read.rs b/examples/event-read.rs index 0200ab266..3feef25e0 100644 --- a/examples/event-read.rs +++ b/examples/event-read.rs @@ -81,6 +81,7 @@ fn main() -> Result<()> { PushKeyboardEnhancementFlags( KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS | KeyboardEnhancementFlags::REPORT_EVENT_TYPES ) )?; diff --git a/src/event.rs b/src/event.rs index dfd17eda1..c1180b2b2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -319,10 +319,9 @@ bitflags! { /// [`KeyEventKind::Release`] when keys are autorepeated or released. const REPORT_EVENT_TYPES = 0b0000_0010; // Send [alternate keycodes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#key-codes) - // in addition to the base keycode. - // - // *Note*: these are not yet supported by crossterm. - // const REPORT_ALTERNATE_KEYS = 0b0000_0100; + // in addition to the base keycode. The alternate keycode overrides the base keycode in + // resulting `KeyEvent`s. + const REPORT_ALTERNATE_KEYS = 0b0000_0100; /// Represent all keyboard events as CSI-u sequences. This is required to get repeat/release /// events for plain-text keys. const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 0b0000_1000; diff --git a/src/event/sys/unix/parse.rs b/src/event/sys/unix/parse.rs index 99ad7ea18..c0b63f23e 100644 --- a/src/event/sys/unix/parse.rs +++ b/src/event/sys/unix/parse.rs @@ -275,10 +275,9 @@ fn parse_csi_keyboard_enhancement_flags(buffer: &[u8]) -> Result Result(&mut split)?; + // In `CSI u`, this is parsed as: + // + // CSI codepoint ; modifiers u + // codepoint: ASCII Dec value + // + // The Kitty Keyboard Protocol extends this with optional components that can be + // enabled progressively. The full sequence is parsed as: + // + // CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u + let mut codepoints = split + .next() + .ok_or_else(could_not_parse_event_error)? + .split(':'); + + let codepoint = codepoints + .next() + .ok_or_else(could_not_parse_event_error)? + .parse::() + .map_err(|_| could_not_parse_event_error())?; let (mut modifiers, kind, state_from_modifiers) = if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { @@ -520,7 +538,7 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result Result().ok()) + .and_then(char::from_u32) + { + keycode = KeyCode::Char(shifted_c); + modifiers.set(KeyModifiers::SHIFT, false); + } + } + let input_event = Event::Key(KeyEvent::new_with_kind_and_state( keycode, modifiers, @@ -1410,6 +1443,26 @@ mod tests { ); } + #[test] + fn test_parse_csi_u_with_shifted_keycode() { + assert_eq!( + // A-S-9 is equivalent to A-( + parse_event(b"\x1B[57:40;4u", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('('), + KeyModifiers::ALT, + )))), + ); + assert_eq!( + // A-S-minus is equivalent to A-_ + parse_event(b"\x1B[45:95;4u", false).unwrap(), + Some(InternalEvent::Event(Event::Key(KeyEvent::new( + KeyCode::Char('_'), + KeyModifiers::ALT, + )))), + ); + } + #[test] fn test_parse_csi_special_key_code_with_types() { assert_eq!(