Add support for functional key codes from kitty keyboard protocol (#691)
This commit is contained in:
parent
069497b43b
commit
551659dee3
@ -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
src/event.rs
173
src/event.rs
@ -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.",
|
||||
))
|
||||
}
|
||||
|
||||
@ -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.",
|
||||
))
|
||||
}
|
||||
|
||||
@ -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)]
|
||||
@ -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 {
|
||||
@ -571,6 +585,7 @@ impl KeyEvent {
|
||||
code,
|
||||
modifiers,
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,6 +640,7 @@ impl From<KeyCode> for KeyEvent {
|
||||
code,
|
||||
modifiers: KeyModifiers::empty(),
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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))]
|
||||
@ -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.
|
||||
|
@ -2,8 +2,8 @@ use std::io;
|
||||
|
||||
use crate::{
|
||||
event::{
|
||||
Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent,
|
||||
MouseEventKind,
|
||||
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<Option<InternalEvent>> {
|
||||
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,6 +290,107 @@ pub(crate) fn parse_csi_modifier_key_code(buffer: &[u8]) -> Result<Option<Intern
|
||||
Ok(Some(InternalEvent::Event(input_event)))
|
||||
}
|
||||
|
||||
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),
|
||||
57361 => Some(KeyCode::PrintScreen),
|
||||
57362 => Some(KeyCode::Pause),
|
||||
57363 => Some(KeyCode::Menu),
|
||||
57376 => Some(KeyCode::F(13)),
|
||||
57377 => Some(KeyCode::F(14)),
|
||||
57378 => Some(KeyCode::F(15)),
|
||||
57379 => Some(KeyCode::F(16)),
|
||||
57380 => Some(KeyCode::F(17)),
|
||||
57381 => Some(KeyCode::F(18)),
|
||||
57382 => Some(KeyCode::F(19)),
|
||||
57383 => Some(KeyCode::F(20)),
|
||||
57384 => Some(KeyCode::F(21)),
|
||||
57385 => Some(KeyCode::F(22)),
|
||||
57386 => Some(KeyCode::F(23)),
|
||||
57387 => Some(KeyCode::F(24)),
|
||||
57388 => Some(KeyCode::F(25)),
|
||||
57389 => Some(KeyCode::F(26)),
|
||||
57390 => Some(KeyCode::F(27)),
|
||||
57391 => Some(KeyCode::F(28)),
|
||||
57392 => Some(KeyCode::F(29)),
|
||||
57393 => Some(KeyCode::F(30)),
|
||||
57394 => Some(KeyCode::F(31)),
|
||||
57395 => Some(KeyCode::F(32)),
|
||||
57396 => Some(KeyCode::F(33)),
|
||||
57397 => Some(KeyCode::F(34)),
|
||||
57398 => Some(KeyCode::F(35)),
|
||||
57428 => Some(KeyCode::Media(MediaKeyCode::Play)),
|
||||
57429 => Some(KeyCode::Media(MediaKeyCode::Pause)),
|
||||
57430 => Some(KeyCode::Media(MediaKeyCode::PlayPause)),
|
||||
57431 => Some(KeyCode::Media(MediaKeyCode::Reverse)),
|
||||
57432 => Some(KeyCode::Media(MediaKeyCode::Stop)),
|
||||
57433 => Some(KeyCode::Media(MediaKeyCode::FastForward)),
|
||||
57434 => Some(KeyCode::Media(MediaKeyCode::Rewind)),
|
||||
57435 => Some(KeyCode::Media(MediaKeyCode::TrackNext)),
|
||||
57436 => Some(KeyCode::Media(MediaKeyCode::TrackPrevious)),
|
||||
57437 => Some(KeyCode::Media(MediaKeyCode::Record)),
|
||||
57438 => Some(KeyCode::Media(MediaKeyCode::LowerVolume)),
|
||||
57439 => Some(KeyCode::Media(MediaKeyCode::RaiseVolume)),
|
||||
57440 => Some(KeyCode::Media(MediaKeyCode::MuteVolume)),
|
||||
57441 => Some(KeyCode::Modifier(ModifierKeyCode::LeftShift)),
|
||||
57442 => Some(KeyCode::Modifier(ModifierKeyCode::LeftControl)),
|
||||
57443 => Some(KeyCode::Modifier(ModifierKeyCode::LeftAlt)),
|
||||
57444 => Some(KeyCode::Modifier(ModifierKeyCode::LeftSuper)),
|
||||
57445 => Some(KeyCode::Modifier(ModifierKeyCode::LeftHyper)),
|
||||
57446 => Some(KeyCode::Modifier(ModifierKeyCode::LeftMeta)),
|
||||
57447 => Some(KeyCode::Modifier(ModifierKeyCode::RightShift)),
|
||||
57448 => Some(KeyCode::Modifier(ModifierKeyCode::RightControl)),
|
||||
57449 => Some(KeyCode::Modifier(ModifierKeyCode::RightAlt)),
|
||||
57450 => Some(KeyCode::Modifier(ModifierKeyCode::RightSuper)),
|
||||
57451 => Some(KeyCode::Modifier(ModifierKeyCode::RightHyper)),
|
||||
57452 => Some(KeyCode::Modifier(ModifierKeyCode::RightMeta)),
|
||||
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<Option<InternalEvent>> {
|
||||
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
||||
assert!(buffer.ends_with(&[b'u']));
|
||||
@ -303,7 +404,7 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
|
||||
// codepoint: ASCII Dec value
|
||||
let codepoint = next_parsed::<u32>(&mut split)?;
|
||||
|
||||
let (modifiers, kind) =
|
||||
let (mut modifiers, kind) =
|
||||
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
|
||||
(
|
||||
parse_modifiers(modifier_mask),
|
||||
@ -313,32 +414,54 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
|
||||
(KeyModifiers::NONE, KeyEventKind::Press)
|
||||
};
|
||||
|
||||
let keycode = {
|
||||
if let Some(c) = char::from_u32(codepoint) {
|
||||
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
|
||||
let (keycode, state) = {
|
||||
if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
|
||||
(special_key_code, state)
|
||||
} else if let Some(c) = char::from_u32(codepoint) {
|
||||
(
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
||||
let input_event = Event::Key(KeyEvent::new_with_kind(keycode, modifiers, kind));
|
||||
if let KeyCode::Modifier(modifier_keycode) = keycode {
|
||||
match modifier_keycode {
|
||||
ModifierKeyCode::LeftAlt | ModifierKeyCode::RightAlt => {
|
||||
modifiers.set(KeyModifiers::ALT, true)
|
||||
}
|
||||
ModifierKeyCode::LeftControl | ModifierKeyCode::RightControl => {
|
||||
modifiers.set(KeyModifiers::CONTROL, true)
|
||||
}
|
||||
ModifierKeyCode::LeftShift | ModifierKeyCode::RightShift => {
|
||||
modifiers.set(KeyModifiers::SHIFT, true)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let input_event = Event::Key(KeyEvent::new_with_kind_and_state(
|
||||
keycode, modifiers, kind, state,
|
||||
));
|
||||
|
||||
Ok(Some(InternalEvent::Event(input_event)))
|
||||
}
|
||||
@ -568,7 +691,7 @@ pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::event::{KeyModifiers, MouseButton, MouseEvent};
|
||||
use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -928,6 +1051,60 @@ mod tests {
|
||||
KeyModifiers::empty()
|
||||
)))),
|
||||
);
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57358u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||
KeyCode::CapsLock,
|
||||
KeyModifiers::empty()
|
||||
)))),
|
||||
);
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57376u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||
KeyCode::F(13),
|
||||
KeyModifiers::empty()
|
||||
)))),
|
||||
);
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57428u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||
KeyCode::Media(MediaKeyCode::Play),
|
||||
KeyModifiers::empty()
|
||||
)))),
|
||||
);
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57441u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new(
|
||||
KeyCode::Modifier(ModifierKeyCode::LeftShift),
|
||||
KeyModifiers::SHIFT,
|
||||
)))),
|
||||
);
|
||||
}
|
||||
|
||||
#[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]
|
||||
@ -973,4 +1150,24 @@ mod tests {
|
||||
)))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_csi_u_encoded_key_code_has_modifier_on_modifier_press() {
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57449u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
||||
KeyCode::Modifier(ModifierKeyCode::RightAlt),
|
||||
KeyModifiers::ALT,
|
||||
KeyEventKind::Press,
|
||||
)))),
|
||||
);
|
||||
assert_eq!(
|
||||
parse_csi_u_encoded_key_code(b"\x1B[57449;3:3u").unwrap(),
|
||||
Some(InternalEvent::Event(Event::Key(KeyEvent::new_with_kind(
|
||||
KeyCode::Modifier(ModifierKeyCode::RightAlt),
|
||||
KeyModifiers::ALT,
|
||||
KeyEventKind::Release,
|
||||
)))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user