use denise::KeyCode;
pub fn key_code(virtual_key: u16, extended: bool) -> KeyCode {
match virtual_key {
vk @ 0x41..=0x5A => LETTERS[(vk - 0x41) as usize],
vk @ 0x30..=0x39 => DIGITS[(vk - 0x30) as usize],
VK_OEM_MINUS => KeyCode::Minus,
VK_OEM_PLUS => KeyCode::Equal,
VK_OEM_4 => KeyCode::BracketLeft,
VK_OEM_6 => KeyCode::BracketRight,
VK_OEM_5 => KeyCode::Backslash,
VK_OEM_1 => KeyCode::Semicolon,
VK_OEM_7 => KeyCode::Quote,
VK_OEM_3 => KeyCode::Backquote,
VK_OEM_COMMA => KeyCode::Comma,
VK_OEM_PERIOD => KeyCode::Period,
VK_OEM_2 => KeyCode::Slash,
VK_OEM_102 => KeyCode::IntlBackslash,
VK_SPACE => KeyCode::Space,
VK_RETURN if extended => KeyCode::NumpadEnter,
VK_RETURN => KeyCode::Enter,
VK_TAB => KeyCode::Tab,
VK_BACK => KeyCode::Backspace,
VK_ESCAPE => KeyCode::Escape,
VK_DELETE => KeyCode::Delete,
VK_INSERT => KeyCode::Insert,
VK_HOME => KeyCode::Home,
VK_END => KeyCode::End,
VK_PRIOR => KeyCode::PageUp,
VK_NEXT => KeyCode::PageDown,
VK_UP => KeyCode::ArrowUp,
VK_DOWN => KeyCode::ArrowDown,
VK_LEFT => KeyCode::ArrowLeft,
VK_RIGHT => KeyCode::ArrowRight,
VK_LSHIFT => KeyCode::ShiftLeft,
VK_RSHIFT => KeyCode::ShiftRight,
VK_LCONTROL => KeyCode::ControlLeft,
VK_RCONTROL => KeyCode::ControlRight,
VK_LMENU => KeyCode::AltLeft,
VK_RMENU => KeyCode::AltRight,
VK_LWIN => KeyCode::SuperLeft,
VK_RWIN => KeyCode::SuperRight,
VK_SHIFT => KeyCode::ShiftLeft,
VK_CONTROL if extended => KeyCode::ControlRight,
VK_CONTROL => KeyCode::ControlLeft,
VK_MENU if extended => KeyCode::AltRight,
VK_MENU => KeyCode::AltLeft,
VK_CAPITAL => KeyCode::CapsLock,
VK_NUMLOCK => KeyCode::NumLock,
VK_SCROLL => KeyCode::ScrollLock,
VK_F1 => KeyCode::F1,
VK_F2 => KeyCode::F2,
VK_F3 => KeyCode::F3,
VK_F4 => KeyCode::F4,
VK_F5 => KeyCode::F5,
VK_F6 => KeyCode::F6,
VK_F7 => KeyCode::F7,
VK_F8 => KeyCode::F8,
VK_F9 => KeyCode::F9,
VK_F10 => KeyCode::F10,
VK_F11 => KeyCode::F11,
VK_F12 => KeyCode::F12,
VK_NUMPAD0 => KeyCode::Numpad0,
VK_NUMPAD1 => KeyCode::Numpad1,
VK_NUMPAD2 => KeyCode::Numpad2,
VK_NUMPAD3 => KeyCode::Numpad3,
VK_NUMPAD4 => KeyCode::Numpad4,
VK_NUMPAD5 => KeyCode::Numpad5,
VK_NUMPAD6 => KeyCode::Numpad6,
VK_NUMPAD7 => KeyCode::Numpad7,
VK_NUMPAD8 => KeyCode::Numpad8,
VK_NUMPAD9 => KeyCode::Numpad9,
VK_ADD => KeyCode::NumpadAdd,
VK_SUBTRACT => KeyCode::NumpadSubtract,
VK_MULTIPLY => KeyCode::NumpadMultiply,
VK_DIVIDE => KeyCode::NumpadDivide,
VK_DECIMAL => KeyCode::NumpadDecimal,
_ => KeyCode::Unidentified(virtual_key as u32),
}
}
pub const UNSIDED: [u16; 3] = [VK_SHIFT, VK_CONTROL, VK_MENU];
pub const VK_BACK: u16 = 0x08;
pub const VK_TAB: u16 = 0x09;
pub const VK_RETURN: u16 = 0x0D;
pub const VK_SHIFT: u16 = 0x10;
pub const VK_CONTROL: u16 = 0x11;
pub const VK_MENU: u16 = 0x12;
pub const VK_CAPITAL: u16 = 0x14;
pub const VK_ESCAPE: u16 = 0x1B;
pub const VK_SPACE: u16 = 0x20;
pub const VK_PRIOR: u16 = 0x21;
pub const VK_NEXT: u16 = 0x22;
pub const VK_END: u16 = 0x23;
pub const VK_HOME: u16 = 0x24;
pub const VK_LEFT: u16 = 0x25;
pub const VK_UP: u16 = 0x26;
pub const VK_RIGHT: u16 = 0x27;
pub const VK_DOWN: u16 = 0x28;
pub const VK_INSERT: u16 = 0x2D;
pub const VK_DELETE: u16 = 0x2E;
pub const VK_LWIN: u16 = 0x5B;
pub const VK_RWIN: u16 = 0x5C;
pub const VK_NUMPAD0: u16 = 0x60;
pub const VK_NUMPAD1: u16 = 0x61;
pub const VK_NUMPAD2: u16 = 0x62;
pub const VK_NUMPAD3: u16 = 0x63;
pub const VK_NUMPAD4: u16 = 0x64;
pub const VK_NUMPAD5: u16 = 0x65;
pub const VK_NUMPAD6: u16 = 0x66;
pub const VK_NUMPAD7: u16 = 0x67;
pub const VK_NUMPAD8: u16 = 0x68;
pub const VK_NUMPAD9: u16 = 0x69;
pub const VK_MULTIPLY: u16 = 0x6A;
pub const VK_ADD: u16 = 0x6B;
pub const VK_SUBTRACT: u16 = 0x6D;
pub const VK_DECIMAL: u16 = 0x6E;
pub const VK_DIVIDE: u16 = 0x6F;
pub const VK_F1: u16 = 0x70;
pub const VK_F2: u16 = 0x71;
pub const VK_F3: u16 = 0x72;
pub const VK_F4: u16 = 0x73;
pub const VK_F5: u16 = 0x74;
pub const VK_F6: u16 = 0x75;
pub const VK_F7: u16 = 0x76;
pub const VK_F8: u16 = 0x77;
pub const VK_F9: u16 = 0x78;
pub const VK_F10: u16 = 0x79;
pub const VK_F11: u16 = 0x7A;
pub const VK_F12: u16 = 0x7B;
pub const VK_NUMLOCK: u16 = 0x90;
pub const VK_SCROLL: u16 = 0x91;
pub const VK_LSHIFT: u16 = 0xA0;
pub const VK_RSHIFT: u16 = 0xA1;
pub const VK_LCONTROL: u16 = 0xA2;
pub const VK_RCONTROL: u16 = 0xA3;
pub const VK_LMENU: u16 = 0xA4;
pub const VK_RMENU: u16 = 0xA5;
pub const VK_OEM_1: u16 = 0xBA;
pub const VK_OEM_PLUS: u16 = 0xBB;
pub const VK_OEM_COMMA: u16 = 0xBC;
pub const VK_OEM_MINUS: u16 = 0xBD;
pub const VK_OEM_PERIOD: u16 = 0xBE;
pub const VK_OEM_2: u16 = 0xBF;
pub const VK_OEM_3: u16 = 0xC0;
pub const VK_OEM_4: u16 = 0xDB;
pub const VK_OEM_5: u16 = 0xDC;
pub const VK_OEM_6: u16 = 0xDD;
pub const VK_OEM_7: u16 = 0xDE;
pub const VK_OEM_102: u16 = 0xE2;
const LETTERS: [KeyCode; 26] = [
KeyCode::A,
KeyCode::B,
KeyCode::C,
KeyCode::D,
KeyCode::E,
KeyCode::F,
KeyCode::G,
KeyCode::H,
KeyCode::I,
KeyCode::J,
KeyCode::K,
KeyCode::L,
KeyCode::M,
KeyCode::N,
KeyCode::O,
KeyCode::P,
KeyCode::Q,
KeyCode::R,
KeyCode::S,
KeyCode::T,
KeyCode::U,
KeyCode::V,
KeyCode::W,
KeyCode::X,
KeyCode::Y,
KeyCode::Z,
];
const DIGITS: [KeyCode; 10] = [
KeyCode::Digit0,
KeyCode::Digit1,
KeyCode::Digit2,
KeyCode::Digit3,
KeyCode::Digit4,
KeyCode::Digit5,
KeyCode::Digit6,
KeyCode::Digit7,
KeyCode::Digit8,
KeyCode::Digit9,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn letters_and_digits_are_their_ascii_values() {
assert_eq!(key_code(b'A' as u16, false), KeyCode::A);
assert_eq!(key_code(b'Z' as u16, false), KeyCode::Z);
assert_eq!(key_code(b'0' as u16, false), KeyCode::Digit0);
assert_eq!(key_code(b'9' as u16, false), KeyCode::Digit9);
}
#[test]
fn the_extended_bit_separates_the_pairs_it_has_to() {
assert_eq!(key_code(VK_MENU, false), KeyCode::AltLeft);
assert_eq!(key_code(VK_MENU, true), KeyCode::AltRight);
assert_eq!(key_code(VK_CONTROL, false), KeyCode::ControlLeft);
assert_eq!(key_code(VK_CONTROL, true), KeyCode::ControlRight);
assert_eq!(key_code(VK_RETURN, false), KeyCode::Enter);
assert_eq!(key_code(VK_RETURN, true), KeyCode::NumpadEnter);
}
#[test]
fn the_iso_key_is_named() {
assert_eq!(key_code(VK_OEM_102, false), KeyCode::IntlBackslash);
}
#[test]
fn no_two_virtual_keys_name_the_same_position() {
let mut seen: Vec<(u16, KeyCode)> = Vec::new();
for virtual_key in 0u16..=0xFF {
if UNSIDED.contains(&virtual_key) {
continue;
}
let code = key_code(virtual_key, false);
if matches!(code, KeyCode::Unidentified(_)) {
continue;
}
if let Some((other, _)) = seen.iter().find(|(_, c)| *c == code) {
panic!("{virtual_key:#04X} and {other:#04X} both name {code:?}");
}
seen.push((virtual_key, code));
}
assert_eq!(seen.len(), 101, "the table changed size");
}
#[test]
fn the_unsided_codes_alias_the_sided_ones() {
assert_eq!(key_code(VK_SHIFT, false), key_code(VK_LSHIFT, false));
assert_eq!(key_code(VK_CONTROL, false), key_code(VK_LCONTROL, false));
assert_eq!(key_code(VK_CONTROL, true), key_code(VK_RCONTROL, false));
assert_eq!(key_code(VK_MENU, false), key_code(VK_LMENU, false));
assert_eq!(key_code(VK_MENU, true), key_code(VK_RMENU, false));
}
#[test]
fn an_unknown_code_keeps_its_number() {
assert_eq!(key_code(0xFE, false), KeyCode::Unidentified(0xFE));
}
#[cfg(windows)]
#[test]
fn the_constants_match_the_official_bindings() {
use windows::Win32::UI::Input::KeyboardAndMouse as vk;
let pairs: [(u16, u16, &str); 24] = [
(VK_BACK, vk::VK_BACK.0, "VK_BACK"),
(VK_TAB, vk::VK_TAB.0, "VK_TAB"),
(VK_RETURN, vk::VK_RETURN.0, "VK_RETURN"),
(VK_SHIFT, vk::VK_SHIFT.0, "VK_SHIFT"),
(VK_CONTROL, vk::VK_CONTROL.0, "VK_CONTROL"),
(VK_MENU, vk::VK_MENU.0, "VK_MENU"),
(VK_CAPITAL, vk::VK_CAPITAL.0, "VK_CAPITAL"),
(VK_ESCAPE, vk::VK_ESCAPE.0, "VK_ESCAPE"),
(VK_SPACE, vk::VK_SPACE.0, "VK_SPACE"),
(VK_PRIOR, vk::VK_PRIOR.0, "VK_PRIOR"),
(VK_NEXT, vk::VK_NEXT.0, "VK_NEXT"),
(VK_INSERT, vk::VK_INSERT.0, "VK_INSERT"),
(VK_DELETE, vk::VK_DELETE.0, "VK_DELETE"),
(VK_LWIN, vk::VK_LWIN.0, "VK_LWIN"),
(VK_NUMPAD0, vk::VK_NUMPAD0.0, "VK_NUMPAD0"),
(VK_DECIMAL, vk::VK_DECIMAL.0, "VK_DECIMAL"),
(VK_F1, vk::VK_F1.0, "VK_F1"),
(VK_F12, vk::VK_F12.0, "VK_F12"),
(VK_NUMLOCK, vk::VK_NUMLOCK.0, "VK_NUMLOCK"),
(VK_LCONTROL, vk::VK_LCONTROL.0, "VK_LCONTROL"),
(VK_RMENU, vk::VK_RMENU.0, "VK_RMENU"),
(VK_OEM_1, vk::VK_OEM_1.0, "VK_OEM_1"),
(VK_OEM_7, vk::VK_OEM_7.0, "VK_OEM_7"),
(VK_OEM_102, vk::VK_OEM_102.0, "VK_OEM_102"),
];
for (ours, theirs, name) in pairs {
assert_eq!(
ours, theirs,
"{name} is {ours:#04X} here and {theirs:#04X} in the bindings"
);
}
}
}