Overhaul the Keyboard API

Overhaul the keyboard API in winit to mimic the W3C specification
to achieve better crossplatform parity. The `KeyboardInput` event
is now uses `KeyEvent` which consists of:

  - `physical_key` - a cross platform way to refer to scancodes;
  - `logical_key`  - keysym value, which shows your key respecting the
                     layout;
  - `text`         - the text produced by this keypress;
  - `location`     - the location of the key on the keyboard;
  - `repeat`       - whether the key was produced by the repeat.

And also a `platform_specific` field which encapsulates extra
information on desktop platforms, like key without modifiers
and text with all modifiers.

The `Modifiers` were also slightly reworked as in, the information
whether the left or right modifier is pressed is now also exposed
on platforms where it could be queried reliably. The support was
also added for the web and orbital platforms finishing the API
change.

This change made the `OptionAsAlt` API on macOS redundant thus it
was removed all together.

Co-authored-by: Artúr Kovács <kovacs.artur.barnabas@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
Co-authored-by: daxpedda <daxpedda@gmail.com>
Fixes: #2631.
Fixes: #2055.
Fixes: #2032.
Fixes: #1904.
Fixes: #1810.
Fixes: #1700.
Fixes: #1443.
Fixes: #1343.
Fixes: #1208.
Fixes: #1151.
Fixes: #812.
Fixes: #600.
Fixes: #361.
Fixes: #343.
This commit is contained in:
Markus Røyset 2023-05-28 20:02:59 +02:00 committed by GitHub
parent f3f46cb3f6
commit 918430979f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 9577 additions and 3419 deletions

View file

@ -1,13 +1,25 @@
use std::os::raw::c_ushort;
use std::ffi::c_void;
use core_foundation::{
base::CFRelease,
data::{CFDataGetBytePtr, CFDataRef},
};
use objc2::rc::{Id, Shared};
use smol_str::SmolStr;
use super::appkit::{NSEvent, NSEventModifierFlags};
use super::window::WinitWindow;
use crate::{
dpi::LogicalSize,
event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
platform_impl::platform::{util::Never, DEVICE_ID},
event::{ElementState, Event, KeyEvent, Modifiers},
keyboard::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
},
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
platform_impl::platform::{
ffi,
util::{get_kbd_type, Never},
},
};
#[derive(Debug)]
@ -25,268 +37,586 @@ pub(crate) enum EventProxy {
},
}
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout.
//
// Note that since keys are translated in a somewhat "dumb" way (reading character)
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
// letter to be received, and so we receive the wrong key.
//
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
Some(match c {
'a' | 'A' => VirtualKeyCode::A,
'b' | 'B' => VirtualKeyCode::B,
'c' | 'C' => VirtualKeyCode::C,
'd' | 'D' => VirtualKeyCode::D,
'e' | 'E' => VirtualKeyCode::E,
'f' | 'F' => VirtualKeyCode::F,
'g' | 'G' => VirtualKeyCode::G,
'h' | 'H' => VirtualKeyCode::H,
'i' | 'I' => VirtualKeyCode::I,
'j' | 'J' => VirtualKeyCode::J,
'k' | 'K' => VirtualKeyCode::K,
'l' | 'L' => VirtualKeyCode::L,
'm' | 'M' => VirtualKeyCode::M,
'n' | 'N' => VirtualKeyCode::N,
'o' | 'O' => VirtualKeyCode::O,
'p' | 'P' => VirtualKeyCode::P,
'q' | 'Q' => VirtualKeyCode::Q,
'r' | 'R' => VirtualKeyCode::R,
's' | 'S' => VirtualKeyCode::S,
't' | 'T' => VirtualKeyCode::T,
'u' | 'U' => VirtualKeyCode::U,
'v' | 'V' => VirtualKeyCode::V,
'w' | 'W' => VirtualKeyCode::W,
'x' | 'X' => VirtualKeyCode::X,
'y' | 'Y' => VirtualKeyCode::Y,
'z' | 'Z' => VirtualKeyCode::Z,
'1' | '!' => VirtualKeyCode::Key1,
'2' | '@' => VirtualKeyCode::Key2,
'3' | '#' => VirtualKeyCode::Key3,
'4' | '$' => VirtualKeyCode::Key4,
'5' | '%' => VirtualKeyCode::Key5,
'6' | '^' => VirtualKeyCode::Key6,
'7' | '&' => VirtualKeyCode::Key7,
'8' | '*' => VirtualKeyCode::Key8,
'9' | '(' => VirtualKeyCode::Key9,
'0' | ')' => VirtualKeyCode::Key0,
'=' | '+' => VirtualKeyCode::Equals,
'-' | '_' => VirtualKeyCode::Minus,
']' | '}' => VirtualKeyCode::RBracket,
'[' | '{' => VirtualKeyCode::LBracket,
'\'' | '"' => VirtualKeyCode::Apostrophe,
';' | ':' => VirtualKeyCode::Semicolon,
'\\' | '|' => VirtualKeyCode::Backslash,
',' | '<' => VirtualKeyCode::Comma,
'/' | '?' => VirtualKeyCode::Slash,
'.' | '>' => VirtualKeyCode::Period,
'`' | '~' => VirtualKeyCode::Grave,
_ => return None,
})
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyEventExtra {
pub text_with_all_modifiers: Option<SmolStr>,
pub key_without_modifiers: Key,
}
pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
Some(match scancode {
0x00 => VirtualKeyCode::A,
0x01 => VirtualKeyCode::S,
0x02 => VirtualKeyCode::D,
0x03 => VirtualKeyCode::F,
0x04 => VirtualKeyCode::H,
0x05 => VirtualKeyCode::G,
0x06 => VirtualKeyCode::Z,
0x07 => VirtualKeyCode::X,
0x08 => VirtualKeyCode::C,
0x09 => VirtualKeyCode::V,
//0x0a => World 1,
0x0b => VirtualKeyCode::B,
0x0c => VirtualKeyCode::Q,
0x0d => VirtualKeyCode::W,
0x0e => VirtualKeyCode::E,
0x0f => VirtualKeyCode::R,
0x10 => VirtualKeyCode::Y,
0x11 => VirtualKeyCode::T,
0x12 => VirtualKeyCode::Key1,
0x13 => VirtualKeyCode::Key2,
0x14 => VirtualKeyCode::Key3,
0x15 => VirtualKeyCode::Key4,
0x16 => VirtualKeyCode::Key6,
0x17 => VirtualKeyCode::Key5,
0x18 => VirtualKeyCode::Equals,
0x19 => VirtualKeyCode::Key9,
0x1a => VirtualKeyCode::Key7,
0x1b => VirtualKeyCode::Minus,
0x1c => VirtualKeyCode::Key8,
0x1d => VirtualKeyCode::Key0,
0x1e => VirtualKeyCode::RBracket,
0x1f => VirtualKeyCode::O,
0x20 => VirtualKeyCode::U,
0x21 => VirtualKeyCode::LBracket,
0x22 => VirtualKeyCode::I,
0x23 => VirtualKeyCode::P,
0x24 => VirtualKeyCode::Return,
0x25 => VirtualKeyCode::L,
0x26 => VirtualKeyCode::J,
0x27 => VirtualKeyCode::Apostrophe,
0x28 => VirtualKeyCode::K,
0x29 => VirtualKeyCode::Semicolon,
0x2a => VirtualKeyCode::Backslash,
0x2b => VirtualKeyCode::Comma,
0x2c => VirtualKeyCode::Slash,
0x2d => VirtualKeyCode::N,
0x2e => VirtualKeyCode::M,
0x2f => VirtualKeyCode::Period,
0x30 => VirtualKeyCode::Tab,
0x31 => VirtualKeyCode::Space,
0x32 => VirtualKeyCode::Grave,
0x33 => VirtualKeyCode::Back,
//0x34 => unkown,
0x35 => VirtualKeyCode::Escape,
0x36 => VirtualKeyCode::RWin,
0x37 => VirtualKeyCode::LWin,
0x38 => VirtualKeyCode::LShift,
//0x39 => Caps lock,
0x3a => VirtualKeyCode::LAlt,
0x3b => VirtualKeyCode::LControl,
0x3c => VirtualKeyCode::RShift,
0x3d => VirtualKeyCode::RAlt,
0x3e => VirtualKeyCode::RControl,
//0x3f => Fn key,
0x40 => VirtualKeyCode::F17,
0x41 => VirtualKeyCode::NumpadDecimal,
//0x42 -> unkown,
0x43 => VirtualKeyCode::NumpadMultiply,
//0x44 => unkown,
0x45 => VirtualKeyCode::NumpadAdd,
//0x46 => unkown,
0x47 => VirtualKeyCode::Numlock,
//0x48 => KeypadClear,
0x49 => VirtualKeyCode::VolumeUp,
0x4a => VirtualKeyCode::VolumeDown,
0x4b => VirtualKeyCode::NumpadDivide,
0x4c => VirtualKeyCode::NumpadEnter,
//0x4d => unkown,
0x4e => VirtualKeyCode::NumpadSubtract,
0x4f => VirtualKeyCode::F18,
0x50 => VirtualKeyCode::F19,
0x51 => VirtualKeyCode::NumpadEquals,
0x52 => VirtualKeyCode::Numpad0,
0x53 => VirtualKeyCode::Numpad1,
0x54 => VirtualKeyCode::Numpad2,
0x55 => VirtualKeyCode::Numpad3,
0x56 => VirtualKeyCode::Numpad4,
0x57 => VirtualKeyCode::Numpad5,
0x58 => VirtualKeyCode::Numpad6,
0x59 => VirtualKeyCode::Numpad7,
0x5a => VirtualKeyCode::F20,
0x5b => VirtualKeyCode::Numpad8,
0x5c => VirtualKeyCode::Numpad9,
0x5d => VirtualKeyCode::Yen,
//0x5e => JIS Ro,
//0x5f => unkown,
0x60 => VirtualKeyCode::F5,
0x61 => VirtualKeyCode::F6,
0x62 => VirtualKeyCode::F7,
0x63 => VirtualKeyCode::F3,
0x64 => VirtualKeyCode::F8,
0x65 => VirtualKeyCode::F9,
//0x66 => JIS Eisuu (macOS),
0x67 => VirtualKeyCode::F11,
//0x68 => JIS Kanna (macOS),
0x69 => VirtualKeyCode::F13,
0x6a => VirtualKeyCode::F16,
0x6b => VirtualKeyCode::F14,
//0x6c => unkown,
0x6d => VirtualKeyCode::F10,
//0x6e => unkown,
0x6f => VirtualKeyCode::F12,
//0x70 => unkown,
0x71 => VirtualKeyCode::F15,
0x72 => VirtualKeyCode::Insert,
0x73 => VirtualKeyCode::Home,
0x74 => VirtualKeyCode::PageUp,
0x75 => VirtualKeyCode::Delete,
0x76 => VirtualKeyCode::F4,
0x77 => VirtualKeyCode::End,
0x78 => VirtualKeyCode::F2,
0x79 => VirtualKeyCode::PageDown,
0x7a => VirtualKeyCode::F1,
0x7b => VirtualKeyCode::Left,
0x7c => VirtualKeyCode::Right,
0x7d => VirtualKeyCode::Down,
0x7e => VirtualKeyCode::Up,
//0x7f => unkown,
0xa => VirtualKeyCode::Caret,
_ => return None,
})
impl KeyEventExtModifierSupplement for KeyEvent {
fn text_with_all_modifiers(&self) -> Option<&str> {
self.platform_specific
.text_with_all_modifiers
.as_ref()
.map(|s| s.as_str())
}
fn key_without_modifiers(&self) -> Key {
self.platform_specific.key_without_modifiers.clone()
}
}
pub fn get_modifierless_char(scancode: u16) -> Key {
let mut string = [0; 16];
let input_source;
let layout;
unsafe {
input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
if input_source.is_null() {
log::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
let layout_data =
ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData);
if layout_data.is_null() {
CFRelease(input_source as *mut c_void);
log::error!("`TISGetInputSourceProperty` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
}
let keyboard_type = get_kbd_type();
let mut result_len = 0;
let mut dead_keys = 0;
let modifiers = 0;
let translate_result = unsafe {
ffi::UCKeyTranslate(
layout,
scancode,
ffi::kUCKeyActionDisplay,
modifiers,
keyboard_type as u32,
ffi::kUCKeyTranslateNoDeadKeysMask,
&mut dead_keys,
string.len() as ffi::UniCharCount,
&mut result_len,
string.as_mut_ptr(),
)
};
unsafe {
CFRelease(input_source as *mut c_void);
}
if translate_result != 0 {
log::error!(
"`UCKeyTranslate` returned with the non-zero value: {}",
translate_result
);
return Key::Unidentified(NativeKey::MacOS(scancode));
}
if result_len == 0 {
log::error!("`UCKeyTranslate` was succesful but gave a string of 0 length.");
return Key::Unidentified(NativeKey::MacOS(scancode));
}
let chars = String::from_utf16_lossy(&string[0..result_len as usize]);
Key::Character(SmolStr::new(chars))
}
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
let string = ns_event
.charactersIgnoringModifiers()
.map(|s| s.to_string())
.unwrap_or_else(String::new);
if string.is_empty() {
// Probably a dead key
let first_char = modifierless_chars.chars().next();
return Key::Dead(first_char);
}
Key::Character(SmolStr::new(string))
}
/// Create `KeyEvent` for the given `NSEvent`.
///
/// This function shouldn't be called when the IME input is in process.
pub(crate) fn create_key_event(
ns_event: &NSEvent,
is_press: bool,
is_repeat: bool,
key_override: Option<KeyCode>,
) -> KeyEvent {
use ElementState::{Pressed, Released};
let state = if is_press { Pressed } else { Released };
let scancode = ns_event.key_code();
let mut physical_key = key_override.unwrap_or_else(|| KeyCode::from_scancode(scancode as u32));
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
None
} else {
let characters = ns_event
.characters()
.map(|s| s.to_string())
.unwrap_or_else(String::new);
if characters.is_empty() {
None
} else {
if matches!(physical_key, KeyCode::Unidentified(_)) {
// The key may be one of the funky function keys
physical_key = extra_function_key_to_code(scancode, &characters);
}
Some(SmolStr::new(characters))
}
};
let key_from_code = code_to_key(physical_key, scancode);
let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) {
let key_without_modifiers = get_modifierless_char(scancode);
let modifiers = NSEvent::modifierFlags(ns_event);
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let logical_key = match text_with_all_modifiers.as_ref() {
// Only checking for ctrl here, not checking for alt because we DO want to
// include its effect in the key. For example if -on the Germay layout- one
// presses alt+8, the logical key should be "{"
// Also not checking if this is a release event because then this issue would
// still affect the key release.
Some(text) if !has_ctrl => Key::Character(text.clone()),
_ => {
let modifierless_chars = match key_without_modifiers.as_ref() {
Key::Character(ch) => ch,
_ => "",
};
get_logical_key_char(ns_event, modifierless_chars)
}
};
(logical_key, key_without_modifiers)
} else {
(key_from_code.clone(), key_from_code)
};
let text = if is_press {
logical_key.to_text().map(SmolStr::new)
} else {
None
};
let location = code_to_location(physical_key);
KeyEvent {
location,
logical_key,
physical_key,
repeat: is_repeat,
state,
text,
platform_specific: KeyEventExtra {
key_without_modifiers,
text_with_all_modifiers,
},
}
}
pub fn code_to_key(code: KeyCode, scancode: u16) -> Key {
match code {
KeyCode::Enter => Key::Enter,
KeyCode::Tab => Key::Tab,
KeyCode::Space => Key::Space,
KeyCode::Backspace => Key::Backspace,
KeyCode::Escape => Key::Escape,
KeyCode::SuperRight => Key::Super,
KeyCode::SuperLeft => Key::Super,
KeyCode::ShiftLeft => Key::Shift,
KeyCode::AltLeft => Key::Alt,
KeyCode::ControlLeft => Key::Control,
KeyCode::ShiftRight => Key::Shift,
KeyCode::AltRight => Key::Alt,
KeyCode::ControlRight => Key::Control,
KeyCode::NumLock => Key::NumLock,
KeyCode::AudioVolumeUp => Key::AudioVolumeUp,
KeyCode::AudioVolumeDown => Key::AudioVolumeDown,
// Other numpad keys all generate text on macOS (if I understand correctly)
KeyCode::NumpadEnter => Key::Enter,
KeyCode::F1 => Key::F1,
KeyCode::F2 => Key::F2,
KeyCode::F3 => Key::F3,
KeyCode::F4 => Key::F4,
KeyCode::F5 => Key::F5,
KeyCode::F6 => Key::F6,
KeyCode::F7 => Key::F7,
KeyCode::F8 => Key::F8,
KeyCode::F9 => Key::F9,
KeyCode::F10 => Key::F10,
KeyCode::F11 => Key::F11,
KeyCode::F12 => Key::F12,
KeyCode::F13 => Key::F13,
KeyCode::F14 => Key::F14,
KeyCode::F15 => Key::F15,
KeyCode::F16 => Key::F16,
KeyCode::F17 => Key::F17,
KeyCode::F18 => Key::F18,
KeyCode::F19 => Key::F19,
KeyCode::F20 => Key::F20,
KeyCode::Insert => Key::Insert,
KeyCode::Home => Key::Home,
KeyCode::PageUp => Key::PageUp,
KeyCode::Delete => Key::Delete,
KeyCode::End => Key::End,
KeyCode::PageDown => Key::PageDown,
KeyCode::ArrowLeft => Key::ArrowLeft,
KeyCode::ArrowRight => Key::ArrowRight,
KeyCode::ArrowDown => Key::ArrowDown,
KeyCode::ArrowUp => Key::ArrowUp,
_ => Key::Unidentified(NativeKey::MacOS(scancode)),
}
}
pub fn code_to_location(code: KeyCode) -> KeyLocation {
match code {
KeyCode::SuperRight => KeyLocation::Right,
KeyCode::SuperLeft => KeyLocation::Left,
KeyCode::ShiftLeft => KeyLocation::Left,
KeyCode::AltLeft => KeyLocation::Left,
KeyCode::ControlLeft => KeyLocation::Left,
KeyCode::ShiftRight => KeyLocation::Right,
KeyCode::AltRight => KeyLocation::Right,
KeyCode::ControlRight => KeyLocation::Right,
KeyCode::NumLock => KeyLocation::Numpad,
KeyCode::NumpadDecimal => KeyLocation::Numpad,
KeyCode::NumpadMultiply => KeyLocation::Numpad,
KeyCode::NumpadAdd => KeyLocation::Numpad,
KeyCode::NumpadDivide => KeyLocation::Numpad,
KeyCode::NumpadEnter => KeyLocation::Numpad,
KeyCode::NumpadSubtract => KeyLocation::Numpad,
KeyCode::NumpadEqual => KeyLocation::Numpad,
KeyCode::Numpad0 => KeyLocation::Numpad,
KeyCode::Numpad1 => KeyLocation::Numpad,
KeyCode::Numpad2 => KeyLocation::Numpad,
KeyCode::Numpad3 => KeyLocation::Numpad,
KeyCode::Numpad4 => KeyLocation::Numpad,
KeyCode::Numpad5 => KeyLocation::Numpad,
KeyCode::Numpad6 => KeyLocation::Numpad,
KeyCode::Numpad7 => KeyLocation::Numpad,
KeyCode::Numpad8 => KeyLocation::Numpad,
KeyCode::Numpad9 => KeyLocation::Numpad,
_ => KeyLocation::Standard,
}
}
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
// constants for the rest.
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
pub fn check_function_keys(string: &str) -> Option<VirtualKeyCode> {
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
if let Some(ch) = string.encode_utf16().next() {
return Some(match ch {
0xf718 => VirtualKeyCode::F21,
0xf719 => VirtualKeyCode::F22,
0xf71a => VirtualKeyCode::F23,
0xf71b => VirtualKeyCode::F24,
_ => return None,
});
match ch {
0xf718 => KeyCode::F21,
0xf719 => KeyCode::F22,
0xf71a => KeyCode::F23,
0xf71b => KeyCode::F24,
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode)),
}
} else {
KeyCode::Unidentified(NativeKeyCode::MacOS(scancode))
}
None
}
pub(super) fn event_mods(event: &NSEvent) -> ModifiersState {
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
let flags = event.modifierFlags();
let mut m = ModifiersState::empty();
m.set(
let mut state = ModifiersState::empty();
let mut pressed_mods = ModifiersKeys::empty();
state.set(
ModifiersState::SHIFT,
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
);
m.set(
ModifiersState::CTRL,
pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed());
pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed());
state.set(
ModifiersState::CONTROL,
flags.contains(NSEventModifierFlags::NSControlKeyMask),
);
m.set(
pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed());
pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed());
state.set(
ModifiersState::ALT,
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
);
m.set(
ModifiersState::LOGO,
pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed());
pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed());
state.set(
ModifiersState::SUPER,
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
);
m
}
pub(super) fn modifier_event(
event: &NSEvent,
keymask: NSEventModifierFlags,
was_key_pressed: bool,
) -> Option<WindowEvent<'static>> {
if !was_key_pressed && event.modifierFlags().contains(keymask)
|| was_key_pressed && !event.modifierFlags().contains(keymask)
{
let state = if was_key_pressed {
ElementState::Released
} else {
ElementState::Pressed
};
pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed());
pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed());
let scancode = event.scancode();
let virtual_keycode = scancode_to_keycode(scancode);
#[allow(deprecated)]
Some(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state,
scancode: scancode as _,
virtual_keycode,
modifiers: event_mods(event),
},
is_synthetic: false,
})
} else {
None
Modifiers {
state,
pressed_mods,
}
}
impl KeyCodeExtScancode for KeyCode {
fn to_scancode(self) -> Option<u32> {
match self {
KeyCode::KeyA => Some(0x00),
KeyCode::KeyS => Some(0x01),
KeyCode::KeyD => Some(0x02),
KeyCode::KeyF => Some(0x03),
KeyCode::KeyH => Some(0x04),
KeyCode::KeyG => Some(0x05),
KeyCode::KeyZ => Some(0x06),
KeyCode::KeyX => Some(0x07),
KeyCode::KeyC => Some(0x08),
KeyCode::KeyV => Some(0x09),
KeyCode::KeyB => Some(0x0b),
KeyCode::KeyQ => Some(0x0c),
KeyCode::KeyW => Some(0x0d),
KeyCode::KeyE => Some(0x0e),
KeyCode::KeyR => Some(0x0f),
KeyCode::KeyY => Some(0x10),
KeyCode::KeyT => Some(0x11),
KeyCode::Digit1 => Some(0x12),
KeyCode::Digit2 => Some(0x13),
KeyCode::Digit3 => Some(0x14),
KeyCode::Digit4 => Some(0x15),
KeyCode::Digit6 => Some(0x16),
KeyCode::Digit5 => Some(0x17),
KeyCode::Equal => Some(0x18),
KeyCode::Digit9 => Some(0x19),
KeyCode::Digit7 => Some(0x1a),
KeyCode::Minus => Some(0x1b),
KeyCode::Digit8 => Some(0x1c),
KeyCode::Digit0 => Some(0x1d),
KeyCode::BracketRight => Some(0x1e),
KeyCode::KeyO => Some(0x1f),
KeyCode::KeyU => Some(0x20),
KeyCode::BracketLeft => Some(0x21),
KeyCode::KeyI => Some(0x22),
KeyCode::KeyP => Some(0x23),
KeyCode::Enter => Some(0x24),
KeyCode::KeyL => Some(0x25),
KeyCode::KeyJ => Some(0x26),
KeyCode::Quote => Some(0x27),
KeyCode::KeyK => Some(0x28),
KeyCode::Semicolon => Some(0x29),
KeyCode::Backslash => Some(0x2a),
KeyCode::Comma => Some(0x2b),
KeyCode::Slash => Some(0x2c),
KeyCode::KeyN => Some(0x2d),
KeyCode::KeyM => Some(0x2e),
KeyCode::Period => Some(0x2f),
KeyCode::Tab => Some(0x30),
KeyCode::Space => Some(0x31),
KeyCode::Backquote => Some(0x32),
KeyCode::Backspace => Some(0x33),
KeyCode::Escape => Some(0x35),
KeyCode::SuperRight => Some(0x36),
KeyCode::SuperLeft => Some(0x37),
KeyCode::ShiftLeft => Some(0x38),
KeyCode::AltLeft => Some(0x3a),
KeyCode::ControlLeft => Some(0x3b),
KeyCode::ShiftRight => Some(0x3c),
KeyCode::AltRight => Some(0x3d),
KeyCode::ControlRight => Some(0x3e),
KeyCode::F17 => Some(0x40),
KeyCode::NumpadDecimal => Some(0x41),
KeyCode::NumpadMultiply => Some(0x43),
KeyCode::NumpadAdd => Some(0x45),
KeyCode::NumLock => Some(0x47),
KeyCode::AudioVolumeUp => Some(0x49),
KeyCode::AudioVolumeDown => Some(0x4a),
KeyCode::NumpadDivide => Some(0x4b),
KeyCode::NumpadEnter => Some(0x4c),
KeyCode::NumpadSubtract => Some(0x4e),
KeyCode::F18 => Some(0x4f),
KeyCode::F19 => Some(0x50),
KeyCode::NumpadEqual => Some(0x51),
KeyCode::Numpad0 => Some(0x52),
KeyCode::Numpad1 => Some(0x53),
KeyCode::Numpad2 => Some(0x54),
KeyCode::Numpad3 => Some(0x55),
KeyCode::Numpad4 => Some(0x56),
KeyCode::Numpad5 => Some(0x57),
KeyCode::Numpad6 => Some(0x58),
KeyCode::Numpad7 => Some(0x59),
KeyCode::F20 => Some(0x5a),
KeyCode::Numpad8 => Some(0x5b),
KeyCode::Numpad9 => Some(0x5c),
KeyCode::IntlYen => Some(0x5d),
KeyCode::F5 => Some(0x60),
KeyCode::F6 => Some(0x61),
KeyCode::F7 => Some(0x62),
KeyCode::F3 => Some(0x63),
KeyCode::F8 => Some(0x64),
KeyCode::F9 => Some(0x65),
KeyCode::F11 => Some(0x67),
KeyCode::F13 => Some(0x69),
KeyCode::F16 => Some(0x6a),
KeyCode::F14 => Some(0x6b),
KeyCode::F10 => Some(0x6d),
KeyCode::F12 => Some(0x6f),
KeyCode::F15 => Some(0x71),
KeyCode::Insert => Some(0x72),
KeyCode::Home => Some(0x73),
KeyCode::PageUp => Some(0x74),
KeyCode::Delete => Some(0x75),
KeyCode::F4 => Some(0x76),
KeyCode::End => Some(0x77),
KeyCode::F2 => Some(0x78),
KeyCode::PageDown => Some(0x79),
KeyCode::F1 => Some(0x7a),
KeyCode::ArrowLeft => Some(0x7b),
KeyCode::ArrowRight => Some(0x7c),
KeyCode::ArrowDown => Some(0x7d),
KeyCode::ArrowUp => Some(0x7e),
_ => None,
}
}
fn from_scancode(scancode: u32) -> KeyCode {
match scancode {
0x00 => KeyCode::KeyA,
0x01 => KeyCode::KeyS,
0x02 => KeyCode::KeyD,
0x03 => KeyCode::KeyF,
0x04 => KeyCode::KeyH,
0x05 => KeyCode::KeyG,
0x06 => KeyCode::KeyZ,
0x07 => KeyCode::KeyX,
0x08 => KeyCode::KeyC,
0x09 => KeyCode::KeyV,
//0x0a => World 1,
0x0b => KeyCode::KeyB,
0x0c => KeyCode::KeyQ,
0x0d => KeyCode::KeyW,
0x0e => KeyCode::KeyE,
0x0f => KeyCode::KeyR,
0x10 => KeyCode::KeyY,
0x11 => KeyCode::KeyT,
0x12 => KeyCode::Digit1,
0x13 => KeyCode::Digit2,
0x14 => KeyCode::Digit3,
0x15 => KeyCode::Digit4,
0x16 => KeyCode::Digit6,
0x17 => KeyCode::Digit5,
0x18 => KeyCode::Equal,
0x19 => KeyCode::Digit9,
0x1a => KeyCode::Digit7,
0x1b => KeyCode::Minus,
0x1c => KeyCode::Digit8,
0x1d => KeyCode::Digit0,
0x1e => KeyCode::BracketRight,
0x1f => KeyCode::KeyO,
0x20 => KeyCode::KeyU,
0x21 => KeyCode::BracketLeft,
0x22 => KeyCode::KeyI,
0x23 => KeyCode::KeyP,
0x24 => KeyCode::Enter,
0x25 => KeyCode::KeyL,
0x26 => KeyCode::KeyJ,
0x27 => KeyCode::Quote,
0x28 => KeyCode::KeyK,
0x29 => KeyCode::Semicolon,
0x2a => KeyCode::Backslash,
0x2b => KeyCode::Comma,
0x2c => KeyCode::Slash,
0x2d => KeyCode::KeyN,
0x2e => KeyCode::KeyM,
0x2f => KeyCode::Period,
0x30 => KeyCode::Tab,
0x31 => KeyCode::Space,
0x32 => KeyCode::Backquote,
0x33 => KeyCode::Backspace,
//0x34 => unknown,
0x35 => KeyCode::Escape,
0x36 => KeyCode::SuperRight,
0x37 => KeyCode::SuperLeft,
0x38 => KeyCode::ShiftLeft,
0x39 => KeyCode::CapsLock,
0x3a => KeyCode::AltLeft,
0x3b => KeyCode::ControlLeft,
0x3c => KeyCode::ShiftRight,
0x3d => KeyCode::AltRight,
0x3e => KeyCode::ControlRight,
0x3f => KeyCode::Fn,
0x40 => KeyCode::F17,
0x41 => KeyCode::NumpadDecimal,
//0x42 -> unknown,
0x43 => KeyCode::NumpadMultiply,
//0x44 => unknown,
0x45 => KeyCode::NumpadAdd,
//0x46 => unknown,
0x47 => KeyCode::NumLock,
//0x48 => KeyCode::NumpadClear,
// TODO: (Artur) for me, kVK_VolumeUp is 0x48
// macOS 10.11
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
0x49 => KeyCode::AudioVolumeUp,
0x4a => KeyCode::AudioVolumeDown,
0x4b => KeyCode::NumpadDivide,
0x4c => KeyCode::NumpadEnter,
//0x4d => unknown,
0x4e => KeyCode::NumpadSubtract,
0x4f => KeyCode::F18,
0x50 => KeyCode::F19,
0x51 => KeyCode::NumpadEqual,
0x52 => KeyCode::Numpad0,
0x53 => KeyCode::Numpad1,
0x54 => KeyCode::Numpad2,
0x55 => KeyCode::Numpad3,
0x56 => KeyCode::Numpad4,
0x57 => KeyCode::Numpad5,
0x58 => KeyCode::Numpad6,
0x59 => KeyCode::Numpad7,
0x5a => KeyCode::F20,
0x5b => KeyCode::Numpad8,
0x5c => KeyCode::Numpad9,
0x5d => KeyCode::IntlYen,
//0x5e => JIS Ro,
//0x5f => unknown,
0x60 => KeyCode::F5,
0x61 => KeyCode::F6,
0x62 => KeyCode::F7,
0x63 => KeyCode::F3,
0x64 => KeyCode::F8,
0x65 => KeyCode::F9,
//0x66 => JIS Eisuu (macOS),
0x67 => KeyCode::F11,
//0x68 => JIS Kanna (macOS),
0x69 => KeyCode::F13,
0x6a => KeyCode::F16,
0x6b => KeyCode::F14,
//0x6c => unknown,
0x6d => KeyCode::F10,
//0x6e => unknown,
0x6f => KeyCode::F12,
//0x70 => unknown,
0x71 => KeyCode::F15,
0x72 => KeyCode::Insert,
0x73 => KeyCode::Home,
0x74 => KeyCode::PageUp,
0x75 => KeyCode::Delete,
0x76 => KeyCode::F4,
0x77 => KeyCode::End,
0x78 => KeyCode::F2,
0x79 => KeyCode::PageDown,
0x7a => KeyCode::F1,
0x7b => KeyCode::ArrowLeft,
0x7c => KeyCode::ArrowRight,
0x7d => KeyCode::ArrowDown,
0x7e => KeyCode::ArrowUp,
//0x7f => unknown,
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
// backquote (`) on Windows' US layout.
0xa => KeyCode::Backquote,
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
}
}
}