Skip to content

Commit

Permalink
Add support for functional key codes from kitty keyboard protocol (#691)
Browse files Browse the repository at this point in the history
  • Loading branch information
pianohacker committed Jul 30, 2022
1 parent 069497b commit 551659d
Show file tree
Hide file tree
Showing 3 changed files with 412 additions and 61 deletions.
45 changes: 20 additions & 25 deletions examples/event-match-modifiers.rs
Expand Up @@ -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,
)));
}
173 changes: 166 additions & 7 deletions src/event.rs
Expand Up @@ -377,7 +377,7 @@ impl Command for PushKeyboardEnhancementFlags {
fn execute_winapi(&self) -> Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"Keyboard progressive enhancement not implemented on Windows.",
"Keyboard progressive enhancement not implemented for the legacy Windows API.",
))
}

Expand All @@ -404,7 +404,7 @@ impl Command for PopKeyboardEnhancementFlags {
fn execute_winapi(&self) -> Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"Keyboard progressive enhancement not implemented on Windows.",
"Keyboard progressive enhancement not implemented for the legacy Windows API.",
))
}

Expand Down Expand Up @@ -553,6 +553,15 @@ pub enum KeyEventKind {
Release,
}

bitflags! {
/// Represents extra state about the key event.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyEventState: u8 {
/// The key event origins from the keypad.
const KEYPAD = 0b0000_0001;
}
}

/// Represents a key event.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialOrd, Clone, Copy)]
Expand All @@ -563,6 +572,11 @@ pub struct KeyEvent {
pub modifiers: KeyModifiers,
/// Kind of event.
pub kind: KeyEventKind,
/// Keyboard state.
///
/// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
/// [`PushKeyboardEnhancementFlags`].
pub state: KeyEventState,
}

impl KeyEvent {
Expand All @@ -571,6 +585,7 @@ impl KeyEvent {
code,
modifiers,
kind: KeyEventKind::Press,
state: KeyEventState::empty(),
}
}

Expand All @@ -583,6 +598,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,
}
}

Expand Down Expand Up @@ -610,6 +640,7 @@ impl From<KeyCode> for KeyEvent {
code,
modifiers: KeyModifiers::empty(),
kind: KeyEventKind::Press,
state: KeyEventState::empty(),
}
}
}
Expand All @@ -620,31 +651,104 @@ 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<H: Hasher>(&self, state: &mut H) {
fn hash<H: Hasher>(&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 media key (as part of [`KeyCode::Media`]).
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MediaKeyCode {
/// Play media key.
Play,
/// Pause media key.
Pause,
/// Play/Pause media key.
PlayPause,
/// Reverse media key.
Reverse,
/// Stop media key.
Stop,
/// Fast-forward media key.
FastForward,
/// Rewind media key.
Rewind,
/// Next-track media key.
TrackNext,
/// Previous-track media key.
TrackPrevious,
/// Record media key.
Record,
/// Lower-volume media key.
LowerVolume,
/// Raise-volume media key.
RaiseVolume,
/// Mute media key.
MuteVolume,
}

/// Represents a modifier key (as part of [`KeyCode::Modifier`]).
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ModifierKeyCode {
/// Left Shift key.
LeftShift,
/// Left Control key.
LeftControl,
/// Left Alt key.
LeftAlt,
/// Left Super key.
LeftSuper,
/// Left Hyper key.
LeftHyper,
/// Left Meta key.
LeftMeta,
/// Right Shift key.
RightShift,
/// Right Control key.
RightControl,
/// Right Alt key.
RightAlt,
/// Right Super key.
RightSuper,
/// Right Hyper key.
RightHyper,
/// Right Meta key.
RightMeta,
/// Iso Level3 Shift key.
IsoLevel3Shift,
/// Iso Level5 Shift key.
IsoLevel5Shift,
}

/// Represents a key.
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -689,6 +793,61 @@ pub enum KeyCode {
Null,
/// Escape key.
Esc,
/// Caps Lock key.
///
/// **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,
/// 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.
///
/// **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),
}

/// An internal event.
Expand Down

0 comments on commit 551659d

Please sign in to comment.