Add extra modifiers/state from kitty keyboard protocol (#696)

This commit is contained in:
Jesse Weaver 2022-08-10 01:09:14 -06:00 committed by GitHub
parent 2362bc2cc6
commit 2a612e0f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 196 additions and 12 deletions

View File

@ -4,7 +4,9 @@
use std::io::stdout; use std::io::stdout;
use crossterm::event::poll; use crossterm::event::{
poll, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::{ use crossterm::{
cursor::position, cursor::position,
event::{ event::{
@ -70,13 +72,27 @@ fn main() -> Result<()> {
enable_raw_mode()?; enable_raw_mode()?;
let mut stdout = stdout(); let mut stdout = stdout();
execute!(stdout, EnableFocusChange, EnableMouseCapture)?; execute!(
stdout,
EnableFocusChange,
EnableMouseCapture,
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
)
)?;
if let Err(e) = print_events() { if let Err(e) = print_events() {
println!("Error: {:?}\r", e); println!("Error: {:?}\r", e);
} }
execute!(stdout, DisableFocusChange, DisableMouseCapture)?; execute!(
stdout,
PopKeyboardEnhancementFlags,
DisableFocusChange,
DisableMouseCapture
)?;
disable_raw_mode() disable_raw_mode()
} }

View File

@ -534,12 +534,19 @@ pub enum MouseButton {
} }
bitflags! { bitflags! {
/// Represents key modifiers (shift, control, alt). /// Represents key modifiers (shift, control, alt, etc.).
///
/// **Note:** `SUPER`, `HYPER`, and `META` can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyModifiers: u8 { pub struct KeyModifiers: u8 {
const SHIFT = 0b0000_0001; const SHIFT = 0b0000_0001;
const CONTROL = 0b0000_0010; const CONTROL = 0b0000_0010;
const ALT = 0b0000_0100; const ALT = 0b0000_0100;
const SUPER = 0b0000_1000;
const HYPER = 0b0001_0000;
const META = 0b0010_0000;
const NONE = 0b0000_0000; const NONE = 0b0000_0000;
} }
} }
@ -555,10 +562,23 @@ pub enum KeyEventKind {
bitflags! { bitflags! {
/// Represents extra state about the key event. /// Represents extra state about the key event.
///
/// **Note:** This state can only be read if
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyEventState: u8 { pub struct KeyEventState: u8 {
/// The key event origins from the keypad. /// The key event origins from the keypad.
const KEYPAD = 0b0000_0001; const KEYPAD = 0b0000_0001;
/// Caps Lock was enabled for this key event.
///
/// **Note:** this is set for the initial press of Num Lock itself.
const CAPS_LOCK = 0b0000_1000;
/// Num Lock was enabled for this key event.
///
/// **Note:** this is set for the initial press of Num Lock itself.
const NUM_LOCK = 0b0000_1000;
const NONE = 0b0000_0000;
} }
} }

View File

@ -169,6 +169,7 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
b'<' => return parse_csi_sgr_mouse(buffer), b'<' => return parse_csi_sgr_mouse(buffer),
b'I' => Some(Event::FocusGained), b'I' => Some(Event::FocusGained),
b'O' => Some(Event::FocusLost), b'O' => Some(Event::FocusLost),
b';' => return parse_csi_modifier_key_code(buffer),
b'0'..=b'9' => { b'0'..=b'9' => {
// Numbered escape code. // Numbered escape code.
if buffer.len() == 3 { if buffer.len() == 3 {
@ -251,9 +252,30 @@ fn parse_modifiers(mask: u8) -> KeyModifiers {
if modifier_mask & 4 != 0 { if modifier_mask & 4 != 0 {
modifiers |= KeyModifiers::CONTROL; modifiers |= KeyModifiers::CONTROL;
} }
if modifier_mask & 8 != 0 {
modifiers |= KeyModifiers::SUPER;
}
if modifier_mask & 16 != 0 {
modifiers |= KeyModifiers::HYPER;
}
if modifier_mask & 32 != 0 {
modifiers |= KeyModifiers::META;
}
modifiers modifiers
} }
fn parse_modifiers_to_state(mask: u8) -> KeyEventState {
let modifier_mask = mask.saturating_sub(1);
let mut state = KeyEventState::empty();
if modifier_mask & 64 != 0 {
state |= KeyEventState::CAPS_LOCK;
}
if modifier_mask & 128 != 0 {
state |= KeyEventState::NUM_LOCK;
}
state
}
fn parse_key_event_kind(kind: u8) -> KeyEventKind { fn parse_key_event_kind(kind: u8) -> KeyEventKind {
match kind { match kind {
1 => KeyEventKind::Press, 1 => KeyEventKind::Press,
@ -265,12 +287,33 @@ fn parse_key_event_kind(kind: u8) -> KeyEventKind {
pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> { pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [ assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
//
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
.map_err(|_| could_not_parse_event_error())?;
let mut split = s.split(';');
let modifier_mask = buffer[buffer.len() - 2]; split.next();
let (modifiers, kind) =
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
(
parse_modifiers(modifier_mask),
parse_key_event_kind(kind_code),
)
} else if buffer.len() > 3 {
(
parse_modifiers(
(buffer[buffer.len() - 2] as char)
.to_digit(10)
.ok_or_else(could_not_parse_event_error)? as u8,
),
KeyEventKind::Press,
)
} else {
(KeyModifiers::NONE, KeyEventKind::Press)
};
let key = buffer[buffer.len() - 1]; let key = buffer[buffer.len() - 1];
let modifiers = parse_modifiers(modifier_mask);
let keycode = match key { let keycode = match key {
b'A' => KeyCode::Up, b'A' => KeyCode::Up,
b'B' => KeyCode::Down, b'B' => KeyCode::Down,
@ -285,7 +328,7 @@ pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<Intern
_ => return Err(could_not_parse_event_error()), _ => return Err(could_not_parse_event_error()),
}; };
let input_event = Event::Key(KeyEvent::new(keycode, modifiers)); let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind));
Ok(Some(InternalEvent::Event(input_event))) Ok(Some(InternalEvent::Event(input_event)))
} }
@ -404,17 +447,18 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
// codepoint: ASCII Dec value // codepoint: ASCII Dec value
let codepoint = next_parsed::<u32>(&mut split)?; let codepoint = next_parsed::<u32>(&mut split)?;
let (mut modifiers, kind) = let (mut modifiers, kind, state_from_modifiers) =
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) { if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
( (
parse_modifiers(modifier_mask), parse_modifiers(modifier_mask),
parse_key_event_kind(kind_code), parse_key_event_kind(kind_code),
parse_modifiers_to_state(modifier_mask),
) )
} else { } else {
(KeyModifiers::NONE, KeyEventKind::Press) (KeyModifiers::NONE, KeyEventKind::Press, KeyEventState::NONE)
}; };
let (keycode, state) = { let (keycode, state_from_keycode) = {
if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) { if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
(special_key_code, state) (special_key_code, state)
} else if let Some(c) = char::from_u32(codepoint) { } else if let Some(c) = char::from_u32(codepoint) {
@ -455,12 +499,24 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => { ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => {
modifiers.set(KeyModifiers::SHIFT, true) modifiers.set(KeyModifiers::SHIFT, true)
} }
ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => {
modifiers.set(KeyModifiers::SUPER, true)
}
ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => {
modifiers.set(KeyModifiers::HYPER, true)
}
ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => {
modifiers.set(KeyModifiers::META, true)
}
_ => {} _ => {}
} }
} }
let input_event = Event::Key(KeyEvent::new_with_kind_and_state( let input_event = Event::Key(KeyEvent::new_with_kind_and_state(
keycode, modifiers, kind, state, keycode,
modifiers,
kind,
state_from_keycode | state_from_modifiers,
)); ));
Ok(Some(InternalEvent::Event(input_event))) Ok(Some(InternalEvent::Event(input_event)))
@ -1169,5 +1225,97 @@ mod tests {
KeyEventKind::Release, KeyEventKind::Release,
)))), )))),
); );
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[57450u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Modifier(ModifierKeyCode::RightSuper),
KeyModifiers::SUPER,
)))),
);
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[57451u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Modifier(ModifierKeyCode::RightHyper),
KeyModifiers::HYPER,
)))),
);
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[57452u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Modifier(ModifierKeyCode::RightMeta),
KeyModifiers::META,
)))),
);
}
#[test]
fn test_parse_csi_u_encoded_key_code_with_extra_modifiers() {
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[97;9u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::SUPER
)))),
);
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[97;17u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::HYPER,
)))),
);
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[97;33u").unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::META,
)))),
);
}
#[test]
fn test_parse_csi_u_encoded_key_code_with_extra_state() {
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[97;65u").unwrap(),
Some(InternalEvent::Event(Event::Key(
KeyEvent::new_with_kind_and_state(
KeyCode::Char('a'),
KeyModifiers::empty(),
KeyEventKind::Press,
KeyEventState::CAPS_LOCK,
)
))),
);
assert_eq!(
parse_csi_u_encoded_key_code(b"\x1B[49;129u").unwrap(),
Some(InternalEvent::Event(Event::Key(
KeyEvent::new_with_kind_and_state(
KeyCode::Char('1'),
KeyModifiers::empty(),
KeyEventKind::Press,
KeyEventState::NUM_LOCK,
)
))),
);
}
#[test]
fn test_parse_csi_special_key_code_with_types() {
assert_eq!(
parse_event(b"\x1B[;1:3B", false).unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
KeyCode::Down,
KeyModifiers::empty(),
KeyEventKind::Release,
)))),
);
assert_eq!(
parse_event(b"\x1B[1;1:3B", false).unwrap(),
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
KeyCode::Down,
KeyModifiers::empty(),
KeyEventKind::Release,
)))),
);
} }
} }