Add support for functional key codes from kitty keyboard protocol (#691)

This commit is contained in:
Jesse Weaver 2022-07-30 02:33:59 -06:00 committed by GitHub
parent 069497b43b
commit 551659dee3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 412 additions and 61 deletions

View File

@ -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,
}));
} }

View File

@ -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.

View File

@ -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,8 +414,11 @@ 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) {
(special_key_code, state)
} else if let Some(c) = char::from_u32(codepoint) {
(
match c { match c {
'\x1B' => KeyCode::Esc, '\x1B' => KeyCode::Esc,
'\r' => KeyCode::Enter, '\r' => KeyCode::Enter,
@ -332,13 +436,32 @@ pub(crate) fn parse_csi_u_encoded_key_code(buffer: &[u8]) -> Result<Option<Inter
} }
'\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,
)))),
);
}
} }