Skip to content

Commit 2a612e0

Browse files
authored
Add extra modifiers/state from kitty keyboard protocol (#696)
1 parent 2362bc2 commit 2a612e0

File tree

3 files changed

+196
-12
lines changed

3 files changed

+196
-12
lines changed

examples/event-read.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
55
use std::io::stdout;
66

7-
use crossterm::event::poll;
7+
use crossterm::event::{
8+
poll, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
9+
};
810
use crossterm::{
911
cursor::position,
1012
event::{
@@ -70,13 +72,27 @@ fn main() -> Result<()> {
7072
enable_raw_mode()?;
7173

7274
let mut stdout = stdout();
73-
execute!(stdout, EnableFocusChange, EnableMouseCapture)?;
75+
execute!(
76+
stdout,
77+
EnableFocusChange,
78+
EnableMouseCapture,
79+
PushKeyboardEnhancementFlags(
80+
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
81+
| KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
82+
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
83+
)
84+
)?;
7485

7586
if let Err(e) = print_events() {
7687
println!("Error: {:?}\r", e);
7788
}
7889

79-
execute!(stdout, DisableFocusChange, DisableMouseCapture)?;
90+
execute!(
91+
stdout,
92+
PopKeyboardEnhancementFlags,
93+
DisableFocusChange,
94+
DisableMouseCapture
95+
)?;
8096

8197
disable_raw_mode()
8298
}

src/event.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,12 +534,19 @@ pub enum MouseButton {
534534
}
535535

536536
bitflags! {
537-
/// Represents key modifiers (shift, control, alt).
537+
/// Represents key modifiers (shift, control, alt, etc.).
538+
///
539+
/// **Note:** `SUPER`, `HYPER`, and `META` can only be read if
540+
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
541+
/// [`PushKeyboardEnhancementFlags`].
538542
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
539543
pub struct KeyModifiers: u8 {
540544
const SHIFT = 0b0000_0001;
541545
const CONTROL = 0b0000_0010;
542546
const ALT = 0b0000_0100;
547+
const SUPER = 0b0000_1000;
548+
const HYPER = 0b0001_0000;
549+
const META = 0b0010_0000;
543550
const NONE = 0b0000_0000;
544551
}
545552
}
@@ -555,10 +562,23 @@ pub enum KeyEventKind {
555562

556563
bitflags! {
557564
/// Represents extra state about the key event.
565+
///
566+
/// **Note:** This state can only be read if
567+
/// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
568+
/// [`PushKeyboardEnhancementFlags`].
558569
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
559570
pub struct KeyEventState: u8 {
560571
/// The key event origins from the keypad.
561572
const KEYPAD = 0b0000_0001;
573+
/// Caps Lock was enabled for this key event.
574+
///
575+
/// **Note:** this is set for the initial press of Num Lock itself.
576+
const CAPS_LOCK = 0b0000_1000;
577+
/// Num Lock was enabled for this key event.
578+
///
579+
/// **Note:** this is set for the initial press of Num Lock itself.
580+
const NUM_LOCK = 0b0000_1000;
581+
const NONE = 0b0000_0000;
562582
}
563583
}
564584

src/event/sys/unix/parse.rs

Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result<Option<InternalEvent>> {
169169
b'<' => return parse_csi_sgr_mouse(buffer),
170170
b'I' => Some(Event::FocusGained),
171171
b'O' => Some(Event::FocusLost),
172+
b';' => return parse_csi_modifier_key_code(buffer),
172173
b'0'..=b'9' => {
173174
// Numbered escape code.
174175
if buffer.len() == 3 {
@@ -251,9 +252,30 @@ fn parse_modifiers(mask: u8) -> KeyModifiers {
251252
if modifier_mask & 4 != 0 {
252253
modifiers |= KeyModifiers::CONTROL;
253254
}
255+
if modifier_mask & 8 != 0 {
256+
modifiers |= KeyModifiers::SUPER;
257+
}
258+
if modifier_mask & 16 != 0 {
259+
modifiers |= KeyModifiers::HYPER;
260+
}
261+
if modifier_mask & 32 != 0 {
262+
modifiers |= KeyModifiers::META;
263+
}
254264
modifiers
255265
}
256266

267+
fn parse_modifiers_to_state(mask: u8) -> KeyEventState {
268+
let modifier_mask = mask.saturating_sub(1);
269+
let mut state = KeyEventState::empty();
270+
if modifier_mask & 64 != 0 {
271+
state |= KeyEventState::CAPS_LOCK;
272+
}
273+
if modifier_mask & 128 != 0 {
274+
state |= KeyEventState::NUM_LOCK;
275+
}
276+
state
277+
}
278+
257279
fn parse_key_event_kind(kind: u8) -> KeyEventKind {
258280
match kind {
259281
1 => KeyEventKind::Press,
@@ -265,11 +287,32 @@ fn parse_key_event_kind(kind: u8) -> KeyEventKind {
265287

266288
pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
267289
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
290+
//
291+
let s = std::str::from_utf8(&buffer[2..buffer.len() - 1])
292+
.map_err(|_| could_not_parse_event_error())?;
293+
let mut split = s.split(';');
268294

269-
let modifier_mask = buffer[buffer.len() - 2];
270-
let key = buffer[buffer.len() - 1];
295+
split.next();
271296

272-
let modifiers = parse_modifiers(modifier_mask);
297+
let (modifiers, kind) =
298+
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
299+
(
300+
parse_modifiers(modifier_mask),
301+
parse_key_event_kind(kind_code),
302+
)
303+
} else if buffer.len() > 3 {
304+
(
305+
parse_modifiers(
306+
(buffer[buffer.len() - 2] as char)
307+
.to_digit(10)
308+
.ok_or_else(could_not_parse_event_error)? as u8,
309+
),
310+
KeyEventKind::Press,
311+
)
312+
} else {
313+
(KeyModifiers::NONE, KeyEventKind::Press)
314+
};
315+
let key = buffer[buffer.len() - 1];
273316

274317
let keycode = match key {
275318
b'A' => KeyCode::Up,
@@ -285,7 +328,7 @@ pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<Intern
285328
_ => return Err(could_not_parse_event_error()),
286329
};
287330

288-
let input_event = Event::Key(KeyEvent::new(keycode, modifiers));
331+
let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind));
289332

290333
Ok(Some(InternalEvent::Event(input_event)))
291334
}
@@ -404,17 +447,18 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
404447
// codepoint: ASCII Dec value
405448
let codepoint = next_parsed::<u32>(&mut split)?;
406449

407-
let (mut modifiers, kind) =
450+
let (mut modifiers, kind, state_from_modifiers) =
408451
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
409452
(
410453
parse_modifiers(modifier_mask),
411454
parse_key_event_kind(kind_code),
455+
parse_modifiers_to_state(modifier_mask),
412456
)
413457
} else {
414-
(KeyModifiers::NONE, KeyEventKind::Press)
458+
(KeyModifiers::NONE, KeyEventKind::Press, KeyEventState::NONE)
415459
};
416460

417-
let (keycode, state) = {
461+
let (keycode, state_from_keycode) = {
418462
if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
419463
(special_key_code, state)
420464
} 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
455499
ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => {
456500
modifiers.set(KeyModifiers::SHIFT, true)
457501
}
502+
ModifierKeyCode::LeftSuper | ModifierKeyCode::RightSuper => {
503+
modifiers.set(KeyModifiers::SUPER, true)
504+
}
505+
ModifierKeyCode::LeftHyper | ModifierKeyCode::RightHyper => {
506+
modifiers.set(KeyModifiers::HYPER, true)
507+
}
508+
ModifierKeyCode::LeftMeta | ModifierKeyCode::RightMeta => {
509+
modifiers.set(KeyModifiers::META, true)
510+
}
458511
_ => {}
459512
}
460513
}
461514

462515
let input_event = Event::Key(KeyEvent::new_with_kind_and_state(
463-
keycode, modifiers, kind, state,
516+
keycode,
517+
modifiers,
518+
kind,
519+
state_from_keycode | state_from_modifiers,
464520
));
465521

466522
Ok(Some(InternalEvent::Event(input_event)))
@@ -1169,5 +1225,97 @@ mod tests {
11691225
KeyEventKind::Release,
11701226
)))),
11711227
);
1228+
assert_eq!(
1229+
parse_csi_u_encoded_key_code(b"\x1B[57450u").unwrap(),
1230+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1231+
KeyCode::Modifier(ModifierKeyCode::RightSuper),
1232+
KeyModifiers::SUPER,
1233+
)))),
1234+
);
1235+
assert_eq!(
1236+
parse_csi_u_encoded_key_code(b"\x1B[57451u").unwrap(),
1237+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1238+
KeyCode::Modifier(ModifierKeyCode::RightHyper),
1239+
KeyModifiers::HYPER,
1240+
)))),
1241+
);
1242+
assert_eq!(
1243+
parse_csi_u_encoded_key_code(b"\x1B[57452u").unwrap(),
1244+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1245+
KeyCode::Modifier(ModifierKeyCode::RightMeta),
1246+
KeyModifiers::META,
1247+
)))),
1248+
);
1249+
}
1250+
1251+
#[test]
1252+
fn test_parse_csi_u_encoded_key_code_with_extra_modifiers() {
1253+
assert_eq!(
1254+
parse_csi_u_encoded_key_code(b"\x1B[97;9u").unwrap(),
1255+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1256+
KeyCode::Char('a'),
1257+
KeyModifiers::SUPER
1258+
)))),
1259+
);
1260+
assert_eq!(
1261+
parse_csi_u_encoded_key_code(b"\x1B[97;17u").unwrap(),
1262+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1263+
KeyCode::Char('a'),
1264+
KeyModifiers::HYPER,
1265+
)))),
1266+
);
1267+
assert_eq!(
1268+
parse_csi_u_encoded_key_code(b"\x1B[97;33u").unwrap(),
1269+
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
1270+
KeyCode::Char('a'),
1271+
KeyModifiers::META,
1272+
)))),
1273+
);
1274+
}
1275+
1276+
#[test]
1277+
fn test_parse_csi_u_encoded_key_code_with_extra_state() {
1278+
assert_eq!(
1279+
parse_csi_u_encoded_key_code(b"\x1B[97;65u").unwrap(),
1280+
Some(InternalEvent::Event(Event::Key(
1281+
KeyEvent::new_with_kind_and_state(
1282+
KeyCode::Char('a'),
1283+
KeyModifiers::empty(),
1284+
KeyEventKind::Press,
1285+
KeyEventState::CAPS_LOCK,
1286+
)
1287+
))),
1288+
);
1289+
assert_eq!(
1290+
parse_csi_u_encoded_key_code(b"\x1B[49;129u").unwrap(),
1291+
Some(InternalEvent::Event(Event::Key(
1292+
KeyEvent::new_with_kind_and_state(
1293+
KeyCode::Char('1'),
1294+
KeyModifiers::empty(),
1295+
KeyEventKind::Press,
1296+
KeyEventState::NUM_LOCK,
1297+
)
1298+
))),
1299+
);
1300+
}
1301+
1302+
#[test]
1303+
fn test_parse_csi_special_key_code_with_types() {
1304+
assert_eq!(
1305+
parse_event(b"\x1B[;1:3B", false).unwrap(),
1306+
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
1307+
KeyCode::Down,
1308+
KeyModifiers::empty(),
1309+
KeyEventKind::Release,
1310+
)))),
1311+
);
1312+
assert_eq!(
1313+
parse_event(b"\x1B[1;1:3B", false).unwrap(),
1314+
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
1315+
KeyCode::Down,
1316+
KeyModifiers::empty(),
1317+
KeyEventKind::Release,
1318+
)))),
1319+
);
11721320
}
11731321
}

0 commit comments

Comments
 (0)