use crossterm::event::*;
use serde::{Deserialize, Serialize};
use crate::*;
#[macro_export]
macro_rules! keypress {
(@char $arg_char : expr) => {
KeyPress::Plain {
key: Key::Character($arg_char),
}
};
(@char $arg_modifiers : expr, $arg_char : expr) => {
KeyPress::WithModifiers {
mask: $arg_modifiers,
key: Key::Character($arg_char),
}
};
(@special $arg_special : expr) => {
KeyPress::Plain {
key: Key::SpecialKey($arg_special),
}
};
(@special $arg_modifiers : expr, $arg_special : expr) => {
KeyPress::WithModifiers {
mask: $arg_modifiers,
key: Key::SpecialKey($arg_special),
}
};
(@fn $arg_function : expr) => {
KeyPress::Plain {
key: Key::FunctionKey($arg_function),
}
};
(@fn $arg_modifiers : expr, $arg_function : expr) => {
KeyPress::WithModifiers {
mask: $arg_modifiers,
key: Key::FunctionKey($arg_function),
}
};
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy)]
pub enum KeyPress {
Plain { key: Key },
WithModifiers { key: Key, mask: ModifierKeysMask },
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy)]
pub enum Key {
Character(char),
SpecialKey(SpecialKey),
FunctionKey(FunctionKey),
KittyKeyboardProtocol(Enhanced),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy)]
pub enum FunctionKey {
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy)]
pub enum SpecialKey {
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
BackTab,
Delete,
Insert,
Esc,
}
pub mod convert_key_event {
use super::*;
impl TryFrom<KeyEvent> for KeyPress {
type Error = ();
fn try_from(key_event: KeyEvent) -> Result<Self, Self::Error> {
special_handling_of_character_key_event(key_event)
}
}
pub(crate) fn special_handling_of_character_key_event(
key_event: KeyEvent,
) -> Result<KeyPress, ()> {
return match key_event {
KeyEvent {
kind: KeyEventKind::Press,
..
} => {
process_only_key_event_kind_press(key_event)
}
_=> {
Err(())
}
};
fn process_only_key_event_kind_press(
key_event: KeyEvent,
) -> Result<KeyPress, ()> {
match key_event {
KeyEvent {
code: KeyCode::Char(character),
modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT, .. } => {
generate_character_key(character)
},
_ => {
let maybe_modifiers_keys_mask: Option<ModifierKeysMask> = convert_key_modifiers(&key_event.modifiers);
let maybe_key: Option<Key> = copy_code_from_key_event(&key_event);
if let Some(key) = maybe_key {
if let Some(mask) = maybe_modifiers_keys_mask {
generate_non_character_key_with_modifiers(key, mask)
} else {
generate_non_character_key_without_modifiers(key)
}
} else {
Err(())
}
}
}
}
fn generate_character_key(character: char) -> Result<KeyPress, ()> {
Ok(keypress! { @char character })
}
fn generate_non_character_key_without_modifiers(
key: Key,
) -> Result<KeyPress, ()> {
Ok(KeyPress::Plain { key })
}
fn generate_non_character_key_with_modifiers(
key: Key,
mask: ModifierKeysMask,
) -> Result<KeyPress, ()> {
Ok(KeyPress::WithModifiers { mask, key })
}
}
#[macro_export]
macro_rules! keyevent {
(
code: $arg_key_code: expr,
modifiers: $arg_key_modifiers: expr
) => {
KeyEvent::new($arg_key_code, $arg_key_modifiers)
};
}
fn match_fn_key(fn_key: u8) -> Option<Key> {
match fn_key {
1 => Key::FunctionKey(FunctionKey::F1).into(),
2 => Key::FunctionKey(FunctionKey::F2).into(),
3 => Key::FunctionKey(FunctionKey::F3).into(),
4 => Key::FunctionKey(FunctionKey::F4).into(),
5 => Key::FunctionKey(FunctionKey::F5).into(),
6 => Key::FunctionKey(FunctionKey::F6).into(),
7 => Key::FunctionKey(FunctionKey::F7).into(),
8 => Key::FunctionKey(FunctionKey::F8).into(),
9 => Key::FunctionKey(FunctionKey::F9).into(),
10 => Key::FunctionKey(FunctionKey::F10).into(),
11 => Key::FunctionKey(FunctionKey::F11).into(),
12 => Key::FunctionKey(FunctionKey::F12).into(),
_ => None,
}
}
pub fn copy_code_from_key_event(key_event: &KeyEvent) -> Option<Key> {
type KC = KeyCode;
match key_event.code {
KC::Null => None,
KC::Backspace => Key::SpecialKey(SpecialKey::Backspace).into(),
KC::Enter => Key::SpecialKey(SpecialKey::Enter).into(),
KC::Left => Key::SpecialKey(SpecialKey::Left).into(),
KC::Right => Key::SpecialKey(SpecialKey::Right).into(),
KC::Up => Key::SpecialKey(SpecialKey::Up).into(),
KC::Down => Key::SpecialKey(SpecialKey::Down).into(),
KC::Home => Key::SpecialKey(SpecialKey::Home).into(),
KC::End => Key::SpecialKey(SpecialKey::End).into(),
KC::PageUp => Key::SpecialKey(SpecialKey::PageUp).into(),
KC::PageDown => Key::SpecialKey(SpecialKey::PageDown).into(),
KC::Tab => Key::SpecialKey(SpecialKey::Tab).into(),
KC::BackTab => Key::SpecialKey(SpecialKey::BackTab).into(),
KC::Delete => Key::SpecialKey(SpecialKey::Delete).into(),
KC::Insert => Key::SpecialKey(SpecialKey::Insert).into(),
KC::Esc => Key::SpecialKey(SpecialKey::Esc).into(),
KC::F(fn_key) => match_fn_key(fn_key),
KC::Char(character) => Key::Character(character).into(),
KC::CapsLock => Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(
SpecialKeyExt::CapsLock,
))
.into(),
KC::ScrollLock => Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(
SpecialKeyExt::ScrollLock,
))
.into(),
KC::NumLock => Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(
SpecialKeyExt::NumLock,
))
.into(),
KC::PrintScreen => Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(
SpecialKeyExt::PrintScreen,
))
.into(),
KC::Pause => {
Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(SpecialKeyExt::Pause))
.into()
}
KC::Menu => {
Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(SpecialKeyExt::Menu))
.into()
}
KC::KeypadBegin => Key::KittyKeyboardProtocol(Enhanced::SpecialKeyExt(
SpecialKeyExt::KeypadBegin,
))
.into(),
KC::Media(media_key) => match_enhanced_media_key(media_key),
KC::Modifier(modifier_key_code) => {
match_enhanced_modifier_key_code(modifier_key_code)
}
}
}
fn match_enhanced_media_key(media_key: MediaKeyCode) -> Option<Key> {
type KC = MediaKeyCode;
Some(match media_key {
KC::Play => Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Play)),
KC::Pause => Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Pause)),
KC::Stop => Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Stop)),
KC::PlayPause => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::PlayPause))
}
KC::Reverse => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Reverse))
}
KC::FastForward => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::FastForward))
}
KC::Rewind => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Rewind))
}
KC::TrackNext => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::TrackNext))
}
KC::TrackPrevious => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::TrackPrevious))
}
KC::Record => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::Record))
}
KC::LowerVolume => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::LowerVolume))
}
KC::RaiseVolume => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::RaiseVolume))
}
KC::MuteVolume => {
Key::KittyKeyboardProtocol(Enhanced::MediaKey(MediaKey::MuteVolume))
}
})
}
fn match_enhanced_modifier_key_code(
modifier_key_code: ModifierKeyCode,
) -> Option<Key> {
type KC = ModifierKeyCode;
Some(match modifier_key_code {
KC::LeftShift => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftShift,
)),
KC::LeftControl => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftControl,
)),
KC::LeftAlt => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftAlt,
)),
KC::LeftSuper => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftSuper,
)),
KC::LeftHyper => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftHyper,
)),
KC::LeftMeta => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::LeftMeta,
)),
KC::RightShift => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightShift,
)),
KC::RightControl => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightControl,
)),
KC::RightAlt => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightAlt,
)),
KC::RightSuper => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightSuper,
)),
KC::RightHyper => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightHyper,
)),
KC::RightMeta => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::RightMeta,
)),
KC::IsoLevel3Shift => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::IsoLevel3Shift,
)),
KC::IsoLevel5Shift => Key::KittyKeyboardProtocol(Enhanced::ModifierKeyEnum(
ModifierKeyEnum::IsoLevel5Shift,
)),
})
}
}