use serde::{Deserialize, Serialize}; use typeshare::typeshare; /// Shared canonical keyboard key identifiers used across frontend and backend. /// /// The enum names intentionally mirror `KeyboardEvent.code` style values so the /// browser, virtual keyboard, and HID backend can all speak the same language. #[typeshare] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum CanonicalKey { KeyA, KeyB, KeyC, KeyD, KeyE, KeyF, KeyG, KeyH, KeyI, KeyJ, KeyK, KeyL, KeyM, KeyN, KeyO, KeyP, KeyQ, KeyR, KeyS, KeyT, KeyU, KeyV, KeyW, KeyX, KeyY, KeyZ, Digit1, Digit2, Digit3, Digit4, Digit5, Digit6, Digit7, Digit8, Digit9, Digit0, Enter, Escape, Backspace, Tab, Space, Minus, Equal, BracketLeft, BracketRight, Backslash, Semicolon, Quote, Backquote, Comma, Period, Slash, CapsLock, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause, Insert, Home, PageUp, Delete, End, PageDown, ArrowRight, ArrowLeft, ArrowDown, ArrowUp, NumLock, NumpadDivide, NumpadMultiply, NumpadSubtract, NumpadAdd, NumpadEnter, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9, Numpad0, NumpadDecimal, IntlBackslash, ContextMenu, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, ControlLeft, ShiftLeft, AltLeft, MetaLeft, ControlRight, ShiftRight, AltRight, MetaRight, } impl CanonicalKey { /// Convert the canonical key to a stable wire code. /// /// The wire code intentionally matches the USB HID usage for keyboard page /// keys so existing low-level behavior stays intact while the semantic type /// becomes explicit. pub const fn to_hid_usage(self) -> u8 { match self { Self::KeyA => 0x04, Self::KeyB => 0x05, Self::KeyC => 0x06, Self::KeyD => 0x07, Self::KeyE => 0x08, Self::KeyF => 0x09, Self::KeyG => 0x0A, Self::KeyH => 0x0B, Self::KeyI => 0x0C, Self::KeyJ => 0x0D, Self::KeyK => 0x0E, Self::KeyL => 0x0F, Self::KeyM => 0x10, Self::KeyN => 0x11, Self::KeyO => 0x12, Self::KeyP => 0x13, Self::KeyQ => 0x14, Self::KeyR => 0x15, Self::KeyS => 0x16, Self::KeyT => 0x17, Self::KeyU => 0x18, Self::KeyV => 0x19, Self::KeyW => 0x1A, Self::KeyX => 0x1B, Self::KeyY => 0x1C, Self::KeyZ => 0x1D, Self::Digit1 => 0x1E, Self::Digit2 => 0x1F, Self::Digit3 => 0x20, Self::Digit4 => 0x21, Self::Digit5 => 0x22, Self::Digit6 => 0x23, Self::Digit7 => 0x24, Self::Digit8 => 0x25, Self::Digit9 => 0x26, Self::Digit0 => 0x27, Self::Enter => 0x28, Self::Escape => 0x29, Self::Backspace => 0x2A, Self::Tab => 0x2B, Self::Space => 0x2C, Self::Minus => 0x2D, Self::Equal => 0x2E, Self::BracketLeft => 0x2F, Self::BracketRight => 0x30, Self::Backslash => 0x31, Self::Semicolon => 0x33, Self::Quote => 0x34, Self::Backquote => 0x35, Self::Comma => 0x36, Self::Period => 0x37, Self::Slash => 0x38, Self::CapsLock => 0x39, Self::F1 => 0x3A, Self::F2 => 0x3B, Self::F3 => 0x3C, Self::F4 => 0x3D, Self::F5 => 0x3E, Self::F6 => 0x3F, Self::F7 => 0x40, Self::F8 => 0x41, Self::F9 => 0x42, Self::F10 => 0x43, Self::F11 => 0x44, Self::F12 => 0x45, Self::PrintScreen => 0x46, Self::ScrollLock => 0x47, Self::Pause => 0x48, Self::Insert => 0x49, Self::Home => 0x4A, Self::PageUp => 0x4B, Self::Delete => 0x4C, Self::End => 0x4D, Self::PageDown => 0x4E, Self::ArrowRight => 0x4F, Self::ArrowLeft => 0x50, Self::ArrowDown => 0x51, Self::ArrowUp => 0x52, Self::NumLock => 0x53, Self::NumpadDivide => 0x54, Self::NumpadMultiply => 0x55, Self::NumpadSubtract => 0x56, Self::NumpadAdd => 0x57, Self::NumpadEnter => 0x58, Self::Numpad1 => 0x59, Self::Numpad2 => 0x5A, Self::Numpad3 => 0x5B, Self::Numpad4 => 0x5C, Self::Numpad5 => 0x5D, Self::Numpad6 => 0x5E, Self::Numpad7 => 0x5F, Self::Numpad8 => 0x60, Self::Numpad9 => 0x61, Self::Numpad0 => 0x62, Self::NumpadDecimal => 0x63, Self::IntlBackslash => 0x64, Self::ContextMenu => 0x65, Self::F13 => 0x68, Self::F14 => 0x69, Self::F15 => 0x6A, Self::F16 => 0x6B, Self::F17 => 0x6C, Self::F18 => 0x6D, Self::F19 => 0x6E, Self::F20 => 0x6F, Self::F21 => 0x70, Self::F22 => 0x71, Self::F23 => 0x72, Self::F24 => 0x73, Self::ControlLeft => 0xE0, Self::ShiftLeft => 0xE1, Self::AltLeft => 0xE2, Self::MetaLeft => 0xE3, Self::ControlRight => 0xE4, Self::ShiftRight => 0xE5, Self::AltRight => 0xE6, Self::MetaRight => 0xE7, } } /// Convert a wire code / USB HID usage to its canonical key. pub const fn from_hid_usage(usage: u8) -> Option { match usage { 0x04 => Some(Self::KeyA), 0x05 => Some(Self::KeyB), 0x06 => Some(Self::KeyC), 0x07 => Some(Self::KeyD), 0x08 => Some(Self::KeyE), 0x09 => Some(Self::KeyF), 0x0A => Some(Self::KeyG), 0x0B => Some(Self::KeyH), 0x0C => Some(Self::KeyI), 0x0D => Some(Self::KeyJ), 0x0E => Some(Self::KeyK), 0x0F => Some(Self::KeyL), 0x10 => Some(Self::KeyM), 0x11 => Some(Self::KeyN), 0x12 => Some(Self::KeyO), 0x13 => Some(Self::KeyP), 0x14 => Some(Self::KeyQ), 0x15 => Some(Self::KeyR), 0x16 => Some(Self::KeyS), 0x17 => Some(Self::KeyT), 0x18 => Some(Self::KeyU), 0x19 => Some(Self::KeyV), 0x1A => Some(Self::KeyW), 0x1B => Some(Self::KeyX), 0x1C => Some(Self::KeyY), 0x1D => Some(Self::KeyZ), 0x1E => Some(Self::Digit1), 0x1F => Some(Self::Digit2), 0x20 => Some(Self::Digit3), 0x21 => Some(Self::Digit4), 0x22 => Some(Self::Digit5), 0x23 => Some(Self::Digit6), 0x24 => Some(Self::Digit7), 0x25 => Some(Self::Digit8), 0x26 => Some(Self::Digit9), 0x27 => Some(Self::Digit0), 0x28 => Some(Self::Enter), 0x29 => Some(Self::Escape), 0x2A => Some(Self::Backspace), 0x2B => Some(Self::Tab), 0x2C => Some(Self::Space), 0x2D => Some(Self::Minus), 0x2E => Some(Self::Equal), 0x2F => Some(Self::BracketLeft), 0x30 => Some(Self::BracketRight), 0x31 => Some(Self::Backslash), 0x33 => Some(Self::Semicolon), 0x34 => Some(Self::Quote), 0x35 => Some(Self::Backquote), 0x36 => Some(Self::Comma), 0x37 => Some(Self::Period), 0x38 => Some(Self::Slash), 0x39 => Some(Self::CapsLock), 0x3A => Some(Self::F1), 0x3B => Some(Self::F2), 0x3C => Some(Self::F3), 0x3D => Some(Self::F4), 0x3E => Some(Self::F5), 0x3F => Some(Self::F6), 0x40 => Some(Self::F7), 0x41 => Some(Self::F8), 0x42 => Some(Self::F9), 0x43 => Some(Self::F10), 0x44 => Some(Self::F11), 0x45 => Some(Self::F12), 0x46 => Some(Self::PrintScreen), 0x47 => Some(Self::ScrollLock), 0x48 => Some(Self::Pause), 0x49 => Some(Self::Insert), 0x4A => Some(Self::Home), 0x4B => Some(Self::PageUp), 0x4C => Some(Self::Delete), 0x4D => Some(Self::End), 0x4E => Some(Self::PageDown), 0x4F => Some(Self::ArrowRight), 0x50 => Some(Self::ArrowLeft), 0x51 => Some(Self::ArrowDown), 0x52 => Some(Self::ArrowUp), 0x53 => Some(Self::NumLock), 0x54 => Some(Self::NumpadDivide), 0x55 => Some(Self::NumpadMultiply), 0x56 => Some(Self::NumpadSubtract), 0x57 => Some(Self::NumpadAdd), 0x58 => Some(Self::NumpadEnter), 0x59 => Some(Self::Numpad1), 0x5A => Some(Self::Numpad2), 0x5B => Some(Self::Numpad3), 0x5C => Some(Self::Numpad4), 0x5D => Some(Self::Numpad5), 0x5E => Some(Self::Numpad6), 0x5F => Some(Self::Numpad7), 0x60 => Some(Self::Numpad8), 0x61 => Some(Self::Numpad9), 0x62 => Some(Self::Numpad0), 0x63 => Some(Self::NumpadDecimal), 0x64 => Some(Self::IntlBackslash), 0x65 => Some(Self::ContextMenu), 0x68 => Some(Self::F13), 0x69 => Some(Self::F14), 0x6A => Some(Self::F15), 0x6B => Some(Self::F16), 0x6C => Some(Self::F17), 0x6D => Some(Self::F18), 0x6E => Some(Self::F19), 0x6F => Some(Self::F20), 0x70 => Some(Self::F21), 0x71 => Some(Self::F22), 0x72 => Some(Self::F23), 0x73 => Some(Self::F24), 0xE0 => Some(Self::ControlLeft), 0xE1 => Some(Self::ShiftLeft), 0xE2 => Some(Self::AltLeft), 0xE3 => Some(Self::MetaLeft), 0xE4 => Some(Self::ControlRight), 0xE5 => Some(Self::ShiftRight), 0xE6 => Some(Self::AltRight), 0xE7 => Some(Self::MetaRight), _ => None, } } pub const fn is_modifier(self) -> bool { matches!( self, Self::ControlLeft | Self::ShiftLeft | Self::AltLeft | Self::MetaLeft | Self::ControlRight | Self::ShiftRight | Self::AltRight | Self::MetaRight ) } pub const fn modifier_bit(self) -> Option { match self { Self::ControlLeft => Some(0x01), Self::ShiftLeft => Some(0x02), Self::AltLeft => Some(0x04), Self::MetaLeft => Some(0x08), Self::ControlRight => Some(0x10), Self::ShiftRight => Some(0x20), Self::AltRight => Some(0x40), Self::MetaRight => Some(0x80), _ => None, } } }