From a8ae84a617c3f8c6c87e2ebae16ae3e240448663 Mon Sep 17 00:00:00 2001 From: Jesse Weaver Date: Thu, 28 Jul 2022 00:52:07 -0600 Subject: [PATCH] Read keypad keys as normal keycodes with special flag --- examples/event-match-modifiers.rs | 45 ++++---- src/event.rs | 132 +++++++++++++----------- src/event/sys/unix/parse.rs | 166 ++++++++++++++++++------------ 3 files changed, 194 insertions(+), 149 deletions(-) diff --git a/examples/event-match-modifiers.rs b/examples/event-match-modifiers.rs index 9f82998c6..183109dea 100644 --- a/examples/event-match-modifiers.rs +++ b/examples/event-match-modifiers.rs @@ -45,29 +45,24 @@ fn match_event(read_event: Event) { } fn main() { - match_event(Event::Key(KeyEvent { - modifiers: KeyModifiers::CONTROL, - code: KeyCode::Char('z'), - kind: KeyEventKind::Press, - })); - match_event(Event::Key(KeyEvent { - modifiers: KeyModifiers::SHIFT, - code: KeyCode::Left, - kind: KeyEventKind::Press, - })); - match_event(Event::Key(KeyEvent { - modifiers: KeyModifiers::ALT, - code: KeyCode::Delete, - kind: KeyEventKind::Press, - })); - match_event(Event::Key(KeyEvent { - modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT, - code: KeyCode::Right, - kind: KeyEventKind::Press, - })); - match_event(Event::Key(KeyEvent { - modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL, - code: KeyCode::Home, - kind: KeyEventKind::Press, - })); + match_event(Event::Key(KeyEvent::new( + KeyCode::Char('z'), + KeyModifiers::CONTROL, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Left, + KeyModifiers::SHIFT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Delete, + KeyModifiers::ALT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Right, + KeyModifiers::ALT | KeyModifiers::SHIFT, + ))); + match_event(Event::Key(KeyEvent::new( + KeyCode::Home, + KeyModifiers::ALT | KeyModifiers::CONTROL, + ))); } diff --git a/src/event.rs b/src/event.rs index 54cbbfc84..f7453b8ed 100644 --- a/src/event.rs +++ b/src/event.rs @@ -553,6 +553,14 @@ pub enum KeyEventKind { Release, } +bitflags! { + /// Represents extra state about the key event. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct KeyEventState: u8 { + const KEYPAD = 0b0000_0001; + } +} + /// Represents a key event. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, PartialOrd, Clone, Copy)] @@ -563,6 +571,8 @@ pub struct KeyEvent { pub modifiers: KeyModifiers, /// Kind of event. pub kind: KeyEventKind, + /// Keyboard state. + pub state: KeyEventState, } impl KeyEvent { @@ -571,6 +581,7 @@ impl KeyEvent { code, modifiers, kind: KeyEventKind::Press, + state: KeyEventState::empty(), } } @@ -583,6 +594,21 @@ impl KeyEvent { code, modifiers, kind, + state: KeyEventState::empty(), + } + } + + pub const fn new_with_kind_and_state( + code: KeyCode, + modifiers: KeyModifiers, + kind: KeyEventKind, + state: KeyEventState, + ) -> KeyEvent { + KeyEvent { + code, + modifiers, + kind, + state, } } @@ -610,6 +636,7 @@ impl From for KeyEvent { code, modifiers: KeyModifiers::empty(), kind: KeyEventKind::Press, + state: KeyEventState::empty(), } } } @@ -620,79 +647,38 @@ impl PartialEq for KeyEvent { code: lhs_code, modifiers: lhs_modifiers, kind: lhs_kind, + state: lhs_state, } = self.normalize_case(); let KeyEvent { code: rhs_code, modifiers: rhs_modifiers, kind: rhs_kind, + state: rhs_state, } = other.normalize_case(); - (lhs_code == rhs_code) && (lhs_modifiers == rhs_modifiers) && (lhs_kind == rhs_kind) + (lhs_code == rhs_code) + && (lhs_modifiers == rhs_modifiers) + && (lhs_kind == rhs_kind) + && (lhs_state == rhs_state) } } impl Eq for KeyEvent {} impl Hash for KeyEvent { - fn hash(&self, state: &mut H) { + fn hash(&self, hash_state: &mut H) { let KeyEvent { code, modifiers, kind, + state, } = self.normalize_case(); - code.hash(state); - modifiers.hash(state); - kind.hash(state); + code.hash(hash_state); + modifiers.hash(hash_state); + kind.hash(hash_state); + state.hash(hash_state); } } -/// Represents a key on the keypad (as part of [`KeyCode::Keypad`]). -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum KeypadKeyCode { - /// A number key. - /// - /// `KeypadKeyCode::Number(1)` represents F1 key, etc. - Number(u8), - /// Keypad Decimal (`.`) key. - Decimal, - /// Keypad Divide (`/`) key. - Divide, - /// Keypad Multiply (`*`) key. - Multiply, - /// Keypad Subtract (`-`) key. - Subtract, - /// Keypad Add (`+`) key. - Add, - /// Keypad Enter key. - Enter, - /// Keypad Equal (`=`) key. - Equal, - /// Keypad Separator key. - Separator, - /// Keypad Left key. - Left, - /// Keypad Right key. - Right, - /// Keypad Up key. - Up, - /// Keypad Down key. - Down, - /// Keypad PageUp key. - PageUp, - /// Keypad PageDown key. - PageDown, - /// Keypad Home key. - Home, - /// Keypad End key. - End, - /// Keypad Insert key. - Insert, - /// Keypad Delete key. - Delete, - /// Keypad Begin key. - Begin, -} - /// Represents a media key (as part of [`KeyCode::Media`]). #[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -805,28 +791,58 @@ pub enum KeyCode { Esc, /// Caps Lock key. /// - /// **Note:** this and all following keys can only be read if + /// **Note:** this key can only be read if /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with /// [`PushKeyboardEnhancementFlags`]. CapsLock, /// Scroll Lock key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. ScrollLock, /// Num Lock key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. NumLock, /// Print Screen key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. PrintScreen, /// Pause key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. Pause, /// Menu key. + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. Menu, - /// A key on the keypad. - Keypad(KeypadKeyCode), + /// The "Begin" key (often mapped to the 5 key when Num Lock is turned on). + /// + /// **Note:** this key can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. + KeypadBegin, /// A media key. + /// + /// **Note:** these keys can only be read if + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with + /// [`PushKeyboardEnhancementFlags`]. Media(MediaKeyCode), /// A modifier key. /// - /// The [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] flag is required to - /// read these keys. + /// **Note:** these keys can only be read if **both** + /// [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] and + /// [`KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES`] have been enabled with + /// [`PushKeyboardEnhancementFlags`]. Modifier(ModifierKeyCode), } diff --git a/src/event/sys/unix/parse.rs b/src/event/sys/unix/parse.rs index 04486859b..37d5ecf70 100644 --- a/src/event/sys/unix/parse.rs +++ b/src/event/sys/unix/parse.rs @@ -2,7 +2,7 @@ use std::io; use crate::{ event::{ - Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, KeypadKeyCode, MediaKeyCode, + Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode, ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind, }, ErrorKind, Result, @@ -160,11 +160,11 @@ pub(crate) fn parse_csi(buffer: &[u8]) -> Result> { b'B' => Some(Event::Key(KeyCode::Down.into())), b'H' => Some(Event::Key(KeyCode::Home.into())), b'F' => Some(Event::Key(KeyCode::End.into())), - b'Z' => Some(Event::Key(KeyEvent { - code: KeyCode::BackTab, - modifiers: KeyModifiers::SHIFT, - kind: KeyEventKind::Press, - })), + b'Z' => Some(Event::Key(KeyEvent::new_with_kind( + KeyCode::BackTab, + KeyModifiers::SHIFT, + KeyEventKind::Press, + ))), b'M' => return parse_csi_normal_mouse(buffer), b'<' => return parse_csi_sgr_mouse(buffer), b'I' => Some(Event::FocusGained), @@ -290,8 +290,43 @@ pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result Option { - match codepoint { +fn translate_functional_key_code(codepoint: u32) -> Option<(KeyCode, KeyEventState)> { + if let Some(keycode) = match codepoint { + 57399 => Some(KeyCode::Char('0')), + 57400 => Some(KeyCode::Char('1')), + 57401 => Some(KeyCode::Char('2')), + 57402 => Some(KeyCode::Char('3')), + 57403 => Some(KeyCode::Char('4')), + 57404 => Some(KeyCode::Char('5')), + 57405 => Some(KeyCode::Char('6')), + 57406 => Some(KeyCode::Char('7')), + 57407 => Some(KeyCode::Char('8')), + 57408 => Some(KeyCode::Char('9')), + 57409 => Some(KeyCode::Char('.')), + 57410 => Some(KeyCode::Char('/')), + 57411 => Some(KeyCode::Char('*')), + 57412 => Some(KeyCode::Char('-')), + 57413 => Some(KeyCode::Char('+')), + 57414 => Some(KeyCode::Enter), + 57415 => Some(KeyCode::Char('=')), + 57416 => Some(KeyCode::Char(',')), + 57417 => Some(KeyCode::Left), + 57418 => Some(KeyCode::Right), + 57419 => Some(KeyCode::Up), + 57420 => Some(KeyCode::Down), + 57421 => Some(KeyCode::PageUp), + 57422 => Some(KeyCode::PageDown), + 57423 => Some(KeyCode::Home), + 57424 => Some(KeyCode::End), + 57425 => Some(KeyCode::Insert), + 57426 => Some(KeyCode::Delete), + 57427 => Some(KeyCode::KeypadBegin), + _ => None, + } { + return Some((keycode, KeyEventState::KEYPAD)); + } + + if let Some(keycode) = match codepoint { 57358 => Some(KeyCode::CapsLock), 57359 => Some(KeyCode::ScrollLock), 57360 => Some(KeyCode::NumLock), @@ -321,35 +356,6 @@ fn translate_functional_key_code(codepoint: u32) -> Option { 57396 => Some(KeyCode::F(33)), 57397 => Some(KeyCode::F(34)), 57398 => Some(KeyCode::F(35)), - 57399 => Some(KeyCode::Keypad(KeypadKeyCode::Number(0))), - 57400 => Some(KeyCode::Keypad(KeypadKeyCode::Number(1))), - 57401 => Some(KeyCode::Keypad(KeypadKeyCode::Number(2))), - 57402 => Some(KeyCode::Keypad(KeypadKeyCode::Number(3))), - 57403 => Some(KeyCode::Keypad(KeypadKeyCode::Number(4))), - 57404 => Some(KeyCode::Keypad(KeypadKeyCode::Number(5))), - 57405 => Some(KeyCode::Keypad(KeypadKeyCode::Number(6))), - 57406 => Some(KeyCode::Keypad(KeypadKeyCode::Number(7))), - 57407 => Some(KeyCode::Keypad(KeypadKeyCode::Number(8))), - 57408 => Some(KeyCode::Keypad(KeypadKeyCode::Number(9))), - 57409 => Some(KeyCode::Keypad(KeypadKeyCode::Decimal)), - 57410 => Some(KeyCode::Keypad(KeypadKeyCode::Divide)), - 57411 => Some(KeyCode::Keypad(KeypadKeyCode::Multiply)), - 57412 => Some(KeyCode::Keypad(KeypadKeyCode::Subtract)), - 57413 => Some(KeyCode::Keypad(KeypadKeyCode::Add)), - 57414 => Some(KeyCode::Keypad(KeypadKeyCode::Enter)), - 57415 => Some(KeyCode::Keypad(KeypadKeyCode::Equal)), - 57416 => Some(KeyCode::Keypad(KeypadKeyCode::Separator)), - 57417 => Some(KeyCode::Keypad(KeypadKeyCode::Left)), - 57418 => Some(KeyCode::Keypad(KeypadKeyCode::Right)), - 57419 => Some(KeyCode::Keypad(KeypadKeyCode::Up)), - 57420 => Some(KeyCode::Keypad(KeypadKeyCode::Down)), - 57421 => Some(KeyCode::Keypad(KeypadKeyCode::PageUp)), - 57422 => Some(KeyCode::Keypad(KeypadKeyCode::PageDown)), - 57423 => Some(KeyCode::Keypad(KeypadKeyCode::Home)), - 57424 => Some(KeyCode::Keypad(KeypadKeyCode::End)), - 57425 => Some(KeyCode::Keypad(KeypadKeyCode::Insert)), - 57426 => Some(KeyCode::Keypad(KeypadKeyCode::Delete)), - 57427 => Some(KeyCode::Keypad(KeypadKeyCode::Begin)), 57428 => Some(KeyCode::Media(MediaKeyCode::Play)), 57429 => Some(KeyCode::Media(MediaKeyCode::Pause)), 57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)), @@ -378,7 +384,11 @@ fn translate_functional_key_code(codepoint: u32) -> Option { 57453 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel3Shift)), 57454 => Some(KeyCode::Modifier(ModifierKeyCode::IsoLevel5Shift)), _ => None, + } { + return Some((keycode, KeyEventState::empty())); } + + None } pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result> { @@ -404,28 +414,31 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result KeyCode::Esc, - '\r' => KeyCode::Enter, - // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get - // newlines as input is because the terminal converts \r into \n for us. When we - // enter raw mode, we disable that, so \n no longer has any meaning - it's better to - // use Ctrl+J. Waiting to handle it here means it gets picked up later - '\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter, - '\t' => { - if modifiers.contains(KeyModifiers::SHIFT) { - KeyCode::BackTab - } else { - KeyCode::Tab + ( + match c { + '\x1B' => KeyCode::Esc, + '\r' => KeyCode::Enter, + // Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get + // newlines as input is because the terminal converts \r into \n for us. When we + // enter raw mode, we disable that, so \n no longer has any meaning - it's better to + // use Ctrl+J. Waiting to handle it here means it gets picked up later + '\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter, + '\t' => { + if modifiers.contains(KeyModifiers::SHIFT) { + KeyCode::BackTab + } else { + KeyCode::Tab + } } - } - '\x7F' => KeyCode::Backspace, - _ => KeyCode::Char(c), - } + '\x7F' => KeyCode::Backspace, + _ => KeyCode::Char(c), + }, + KeyEventState::empty(), + ) } else { return Err(could_not_parse_event_error()); } @@ -446,7 +459,9 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result Result> { #[cfg(test)] mod tests { - use crate::event::{KeyModifiers, MouseButton, MouseEvent}; + use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent}; use super::*; @@ -1050,13 +1065,6 @@ mod tests { KeyModifiers::empty() )))), ); - assert_eq!( - parse_csi_u_encoded_key_code(b"\x1B[57399u").unwrap(), - Some(InternalEvent::Event(Event::Key(KeyEvent::new( - KeyCode::Keypad(KeypadKeyCode::Number(0)), - KeyModifiers::empty() - )))), - ); assert_eq!( parse_csi_u_encoded_key_code(b"\x1B[57428u").unwrap(), Some(InternalEvent::Event(Event::Key(KeyEvent::new( @@ -1073,6 +1081,32 @@ mod tests { ); } + #[test] + fn test_parse_csi_u_encoded_keypad_code() { + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57399u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Char('0'), + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::KEYPAD, + ) + ))), + ); + assert_eq!( + parse_csi_u_encoded_key_code(b"\x1B[57419u").unwrap(), + Some(InternalEvent::Event(Event::Key( + KeyEvent::new_with_kind_and_state( + KeyCode::Up, + KeyModifiers::empty(), + KeyEventKind::Press, + KeyEventState::KEYPAD, + ) + ))), + ); + } + #[test] fn test_parse_csi_u_encoded_key_code_with_types() { assert_eq!(