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() {
|
fn main() {
|
||||||
match_event(Event::Key(KeyEvent {
|
match_event(Event::Key(KeyEvent::new(
|
||||||
modifiers: KeyModifiers::CONTROL,
|
KeyCode::Char('z'),
|
||||||
code: KeyCode::Char('z'),
|
KeyModifiers::CONTROL,
|
||||||
kind: KeyEventKind::Press,
|
)));
|
||||||
}));
|
match_event(Event::Key(KeyEvent::new(
|
||||||
match_event(Event::Key(KeyEvent {
|
KeyCode::Left,
|
||||||
modifiers: KeyModifiers::SHIFT,
|
KeyModifiers::SHIFT,
|
||||||
code: KeyCode::Left,
|
)));
|
||||||
kind: KeyEventKind::Press,
|
match_event(Event::Key(KeyEvent::new(
|
||||||
}));
|
KeyCode::Delete,
|
||||||
match_event(Event::Key(KeyEvent {
|
KeyModifiers::ALT,
|
||||||
modifiers: KeyModifiers::ALT,
|
)));
|
||||||
code: KeyCode::Delete,
|
match_event(Event::Key(KeyEvent::new(
|
||||||
kind: KeyEventKind::Press,
|
KeyCode::Right,
|
||||||
}));
|
KeyModifiers::ALT | KeyModifiers::SHIFT,
|
||||||
match_event(Event::Key(KeyEvent {
|
)));
|
||||||
modifiers: KeyModifiers::ALT | KeyModifiers::SHIFT,
|
match_event(Event::Key(KeyEvent::new(
|
||||||
code: KeyCode::Right,
|
KeyCode::Home,
|
||||||
kind: KeyEventKind::Press,
|
KeyModifiers::ALT | KeyModifiers::CONTROL,
|
||||||
}));
|
)));
|
||||||
match_event(Event::Key(KeyEvent {
|
|
||||||
modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL,
|
|
||||||
code: KeyCode::Home,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
173
src/event.rs
173
src/event.rs
@ -377,7 +377,7 @@ impl Command for PushKeyboardEnhancementFlags {
|
|||||||
fn execute_winapi(&self) -> Result<()> {
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(
|
||||||
io::ErrorKind::Unsupported,
|
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<()> {
|
fn execute_winapi(&self) -> Result<()> {
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(
|
||||||
io::ErrorKind::Unsupported,
|
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,
|
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.
|
/// Represents a key event.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, PartialOrd, Clone, Copy)]
|
#[derive(Debug, PartialOrd, Clone, Copy)]
|
||||||
@ -563,6 +572,11 @@ pub struct KeyEvent {
|
|||||||
pub modifiers: KeyModifiers,
|
pub modifiers: KeyModifiers,
|
||||||
/// Kind of event.
|
/// Kind of event.
|
||||||
pub kind: KeyEventKind,
|
pub kind: KeyEventKind,
|
||||||
|
/// Keyboard state.
|
||||||
|
///
|
||||||
|
/// Only set if [`KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES`] has been enabled with
|
||||||
|
/// [`PushKeyboardEnhancementFlags`].
|
||||||
|
pub state: KeyEventState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyEvent {
|
impl KeyEvent {
|
||||||
@ -571,6 +585,7 @@ impl KeyEvent {
|
|||||||
code,
|
code,
|
||||||
modifiers,
|
modifiers,
|
||||||
kind: KeyEventKind::Press,
|
kind: KeyEventKind::Press,
|
||||||
|
state: KeyEventState::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,6 +598,21 @@ impl KeyEvent {
|
|||||||
code,
|
code,
|
||||||
modifiers,
|
modifiers,
|
||||||
kind,
|
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,
|
code,
|
||||||
modifiers: KeyModifiers::empty(),
|
modifiers: KeyModifiers::empty(),
|
||||||
kind: KeyEventKind::Press,
|
kind: KeyEventKind::Press,
|
||||||
|
state: KeyEventState::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -620,31 +651,104 @@ impl PartialEq for KeyEvent {
|
|||||||
code: lhs_code,
|
code: lhs_code,
|
||||||
modifiers: lhs_modifiers,
|
modifiers: lhs_modifiers,
|
||||||
kind: lhs_kind,
|
kind: lhs_kind,
|
||||||
|
state: lhs_state,
|
||||||
} = self.normalize_case();
|
} = self.normalize_case();
|
||||||
let KeyEvent {
|
let KeyEvent {
|
||||||
code: rhs_code,
|
code: rhs_code,
|
||||||
modifiers: rhs_modifiers,
|
modifiers: rhs_modifiers,
|
||||||
kind: rhs_kind,
|
kind: rhs_kind,
|
||||||
|
state: rhs_state,
|
||||||
} = other.normalize_case();
|
} = 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 Eq for KeyEvent {}
|
||||||
|
|
||||||
impl Hash 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 {
|
let KeyEvent {
|
||||||
code,
|
code,
|
||||||
modifiers,
|
modifiers,
|
||||||
kind,
|
kind,
|
||||||
|
state,
|
||||||
} = self.normalize_case();
|
} = self.normalize_case();
|
||||||
code.hash(state);
|
code.hash(hash_state);
|
||||||
modifiers.hash(state);
|
modifiers.hash(hash_state);
|
||||||
kind.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.
|
/// Represents a key.
|
||||||
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
@ -689,6 +793,61 @@ pub enum KeyCode {
|
|||||||
Null,
|
Null,
|
||||||
/// Escape key.
|
/// Escape key.
|
||||||
Esc,
|
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.
|
/// An internal event.
|
||||||
|
@ -2,8 +2,8 @@ use std::io;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
event::{
|
event::{
|
||||||
Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent,
|
Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers, MediaKeyCode,
|
||||||
MouseEventKind,
|
ModifierKeyCode, MouseButton, MouseEvent, MouseEventKind,
|
||||||
},
|
},
|
||||||
ErrorKind, Result,
|
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'B' => Some(Event::Key(KeyCode::Down.into())),
|
||||||
b'H' => Some(Event::Key(KeyCode::Home.into())),
|
b'H' => Some(Event::Key(KeyCode::Home.into())),
|
||||||
b'F' => Some(Event::Key(KeyCode::End.into())),
|
b'F' => Some(Event::Key(KeyCode::End.into())),
|
||||||
b'Z' => Some(Event::Key(KeyEvent {
|
b'Z' => Some(Event::Key(KeyEvent::new_with_kind(
|
||||||
code: KeyCode::BackTab,
|
KeyCode::BackTab,
|
||||||
modifiers: KeyModifiers::SHIFT,
|
KeyModifiers::SHIFT,
|
||||||
kind: KeyEventKind::Press,
|
KeyEventKind::Press,
|
||||||
})),
|
))),
|
||||||
b'M' => return parse_csi_normal_mouse(buffer),
|
b'M' => return parse_csi_normal_mouse(buffer),
|
||||||
b'<' => return parse_csi_sgr_mouse(buffer),
|
b'<' => return parse_csi_sgr_mouse(buffer),
|
||||||
b'I' => Some(Event::FocusGained),
|
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)))
|
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>> {
|
pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<InternalEvent>> {
|
||||||
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
assert!(buffer.starts_with(&[b'\x1B', b'['])); // ESC [
|
||||||
assert!(buffer.ends_with(&[b'u']));
|
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
|
// codepoint: ASCII Dec value
|
||||||
let codepoint = next_parsed::<u32>(&mut split)?;
|
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) {
|
if let Ok((modifier_mask, kind_code)) = modifier_and_kind_parsed(&mut split) {
|
||||||
(
|
(
|
||||||
parse_modifiers(modifier_mask),
|
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)
|
(KeyModifiers::NONE, KeyEventKind::Press)
|
||||||
};
|
};
|
||||||
|
|
||||||
let keycode = {
|
let (keycode, state) = {
|
||||||
if let Some(c) = char::from_u32(codepoint) {
|
if let Some((special_key_code, state)) = translate_functional_key_code(codepoint) {
|
||||||
match c {
|
(special_key_code, state)
|
||||||
'\x1B' => KeyCode::Esc,
|
} else if let Some(c) = char::from_u32(codepoint) {
|
||||||
'\r' => KeyCode::Enter,
|
(
|
||||||
// Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
|
match c {
|
||||||
// newlines as input is because the terminal converts \r into \n for us. When we
|
'\x1B' => KeyCode::Esc,
|
||||||
// enter raw mode, we disable that, so \n no longer has any meaning - it's better to
|
'\r' => KeyCode::Enter,
|
||||||
// use Ctrl+J. Waiting to handle it here means it gets picked up later
|
// Issue #371: \n = 0xA, which is also the keycode for Ctrl+J. The only reason we get
|
||||||
'\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter,
|
// newlines as input is because the terminal converts \r into \n for us. When we
|
||||||
'\t' => {
|
// enter raw mode, we disable that, so \n no longer has any meaning - it's better to
|
||||||
if modifiers.contains(KeyModifiers::SHIFT) {
|
// use Ctrl+J. Waiting to handle it here means it gets picked up later
|
||||||
KeyCode::BackTab
|
'\n' if !crate::terminal::sys::is_raw_mode_enabled() => KeyCode::Enter,
|
||||||
} else {
|
'\t' => {
|
||||||
KeyCode::Tab
|
if modifiers.contains(KeyModifiers::SHIFT) {
|
||||||
|
KeyCode::BackTab
|
||||||
|
} else {
|
||||||
|
KeyCode::Tab
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
'\x7F' => KeyCode::Backspace,
|
||||||
'\x7F' => KeyCode::Backspace,
|
_ => KeyCode::Char(c),
|
||||||
_ => KeyCode::Char(c),
|
},
|
||||||
}
|
KeyEventState::empty(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return Err(could_not_parse_event_error());
|
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)))
|
Ok(Some(InternalEvent::Event(input_event)))
|
||||||
}
|
}
|
||||||
@ -568,7 +691,7 @@ pub(crate) fn parse_utf8_char(buffer: &[u8]) -> Result<Option<char>> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::event::{KeyModifiers, MouseButton, MouseEvent};
|
use crate::event::{KeyEventState, KeyModifiers, MouseButton, MouseEvent};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -928,6 +1051,60 @@ mod tests {
|
|||||||
KeyModifiers::empty()
|
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]
|
#[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