1use crate::window::PhysicalKey;
26
27impl PhysicalKey {
28 #[must_use]
33 pub const fn from_evdev(code: u32) -> Self {
34 use PhysicalKey::*;
35 match code {
36 1 => Escape,
37 2 => Digit1, 3 => Digit2, 4 => Digit3, 5 => Digit4, 6 => Digit5,
38 7 => Digit6, 8 => Digit7, 9 => Digit8, 10 => Digit9, 11 => Digit0,
39 12 => Minus, 13 => Equal, 14 => Backspace, 15 => Tab,
40 16 => KeyQ, 17 => KeyW, 18 => KeyE, 19 => KeyR, 20 => KeyT,
41 21 => KeyY, 22 => KeyU, 23 => KeyI, 24 => KeyO, 25 => KeyP,
42 26 => BracketLeft, 27 => BracketRight, 28 => Enter, 29 => ControlLeft,
43 30 => KeyA, 31 => KeyS, 32 => KeyD, 33 => KeyF, 34 => KeyG,
44 35 => KeyH, 36 => KeyJ, 37 => KeyK, 38 => KeyL,
45 39 => Semicolon, 40 => Quote, 41 => Backquote, 42 => ShiftLeft, 43 => Backslash,
46 44 => KeyZ, 45 => KeyX, 46 => KeyC, 47 => KeyV, 48 => KeyB,
47 49 => KeyN, 50 => KeyM, 51 => Comma, 52 => Period, 53 => Slash,
48 54 => ShiftRight, 55 => NumpadMultiply, 56 => AltLeft, 57 => Space, 58 => CapsLock,
49 59 => F1, 60 => F2, 61 => F3, 62 => F4, 63 => F5,
50 64 => F6, 65 => F7, 66 => F8, 67 => F9, 68 => F10,
51 69 => NumLock, 70 => ScrollLock,
52 71 => Numpad7, 72 => Numpad8, 73 => Numpad9, 74 => NumpadSubtract,
53 75 => Numpad4, 76 => Numpad5, 77 => Numpad6, 78 => NumpadAdd,
54 79 => Numpad1, 80 => Numpad2, 81 => Numpad3, 82 => Numpad0, 83 => NumpadDecimal,
55 86 => IntlBackslash, 87 => F11, 88 => F12,
58 89 => IntlRo,
59 92 => Convert, 93 => KanaMode, 94 => NonConvert, 95 => NumpadComma,
60 96 => NumpadEnter, 97 => ControlRight, 98 => NumpadDivide,
61 99 => PrintScreen, 100 => AltRight,
62 102 => Home, 103 => ArrowUp, 104 => PageUp, 105 => ArrowLeft,
63 106 => ArrowRight, 107 => End, 108 => ArrowDown, 109 => PageDown,
64 110 => Insert, 111 => Delete,
65 117 => NumpadEqual, 119 => Pause,
66 121 => NumpadComma, 122 => Lang1, 123 => Lang2, 124 => IntlYen,
67 125 => MetaLeft, 126 => MetaRight, 127 => ContextMenu,
68 183 => F13, 184 => F14, 185 => F15, 186 => F16, 187 => F17, 188 => F18,
69 189 => F19, 190 => F20, 191 => F21, 192 => F22, 193 => F23, 194 => F24,
70 _ => Unidentified,
71 }
72 }
73
74 #[must_use]
80 pub const fn from_x11_keycode(keycode: u32) -> Self {
81 if keycode < 8 {
82 return PhysicalKey::Unidentified;
83 }
84 Self::from_evdev(keycode - 8)
85 }
86
87 #[must_use]
93 pub const fn from_windows_scancode(scancode: u32, extended: bool) -> Self {
94 use PhysicalKey::*;
95 if extended {
96 return match scancode {
97 0x1C => NumpadEnter,
98 0x1D => ControlRight,
99 0x35 => NumpadDivide,
100 0x37 => PrintScreen,
101 0x38 => AltRight,
102 0x45 => Pause,
103 0x47 => Home,
104 0x48 => ArrowUp,
105 0x49 => PageUp,
106 0x4B => ArrowLeft,
107 0x4D => ArrowRight,
108 0x4F => End,
109 0x50 => ArrowDown,
110 0x51 => PageDown,
111 0x52 => Insert,
112 0x53 => Delete,
113 0x5B => MetaLeft,
114 0x5C => MetaRight,
115 0x5D => ContextMenu,
116 _ => Unidentified,
117 };
118 }
119 match scancode {
120 0x01 => Escape,
121 0x02 => Digit1, 0x03 => Digit2, 0x04 => Digit3, 0x05 => Digit4, 0x06 => Digit5,
122 0x07 => Digit6, 0x08 => Digit7, 0x09 => Digit8, 0x0A => Digit9, 0x0B => Digit0,
123 0x0C => Minus, 0x0D => Equal, 0x0E => Backspace, 0x0F => Tab,
124 0x10 => KeyQ, 0x11 => KeyW, 0x12 => KeyE, 0x13 => KeyR, 0x14 => KeyT,
125 0x15 => KeyY, 0x16 => KeyU, 0x17 => KeyI, 0x18 => KeyO, 0x19 => KeyP,
126 0x1A => BracketLeft, 0x1B => BracketRight, 0x1C => Enter, 0x1D => ControlLeft,
127 0x1E => KeyA, 0x1F => KeyS, 0x20 => KeyD, 0x21 => KeyF, 0x22 => KeyG,
128 0x23 => KeyH, 0x24 => KeyJ, 0x25 => KeyK, 0x26 => KeyL,
129 0x27 => Semicolon, 0x28 => Quote, 0x29 => Backquote, 0x2A => ShiftLeft,
130 0x2B => Backslash,
131 0x2C => KeyZ, 0x2D => KeyX, 0x2E => KeyC, 0x2F => KeyV, 0x30 => KeyB,
132 0x31 => KeyN, 0x32 => KeyM, 0x33 => Comma, 0x34 => Period, 0x35 => Slash,
133 0x36 => ShiftRight, 0x37 => NumpadMultiply, 0x38 => AltLeft,
134 0x39 => Space, 0x3A => CapsLock,
135 0x3B => F1, 0x3C => F2, 0x3D => F3, 0x3E => F4, 0x3F => F5,
136 0x40 => F6, 0x41 => F7, 0x42 => F8, 0x43 => F9, 0x44 => F10,
137 0x45 => NumLock, 0x46 => ScrollLock,
138 0x47 => Numpad7, 0x48 => Numpad8, 0x49 => Numpad9, 0x4A => NumpadSubtract,
139 0x4B => Numpad4, 0x4C => Numpad5, 0x4D => Numpad6, 0x4E => NumpadAdd,
140 0x4F => Numpad1, 0x50 => Numpad2, 0x51 => Numpad3, 0x52 => Numpad0,
141 0x53 => NumpadDecimal,
142 0x56 => IntlBackslash, 0x57 => F11, 0x58 => F12, 0x59 => NumpadEqual,
143 0x64 => F13, 0x65 => F14, 0x66 => F15, 0x67 => F16, 0x68 => F17,
144 0x69 => F18, 0x6A => F19, 0x6B => F20, 0x6C => F21, 0x6D => F22,
145 0x6E => F23, 0x76 => F24,
146 0x70 => KanaMode, 0x73 => IntlRo, 0x79 => Convert, 0x7B => NonConvert,
147 0x7D => IntlYen,
148 _ => Unidentified,
149 }
150 }
151
152 #[must_use]
158 pub const fn from_macos_keycode(keycode: u16) -> Self {
159 use PhysicalKey::*;
160 match keycode {
161 0x00 => KeyA, 0x01 => KeyS, 0x02 => KeyD, 0x03 => KeyF, 0x04 => KeyH,
162 0x05 => KeyG, 0x06 => KeyZ, 0x07 => KeyX, 0x08 => KeyC, 0x09 => KeyV,
163 0x0A => IntlBackslash,
165 0x0B => KeyB, 0x0C => KeyQ, 0x0D => KeyW, 0x0E => KeyE, 0x0F => KeyR,
166 0x10 => KeyY, 0x11 => KeyT,
167 0x12 => Digit1, 0x13 => Digit2, 0x14 => Digit3, 0x15 => Digit4,
168 0x16 => Digit6, 0x17 => Digit5, 0x18 => Equal, 0x19 => Digit9,
169 0x1A => Digit7, 0x1B => Minus, 0x1C => Digit8, 0x1D => Digit0,
170 0x1E => BracketRight, 0x1F => KeyO, 0x20 => KeyU, 0x21 => BracketLeft,
171 0x22 => KeyI, 0x23 => KeyP, 0x24 => Enter, 0x25 => KeyL, 0x26 => KeyJ,
172 0x27 => Quote, 0x28 => KeyK, 0x29 => Semicolon, 0x2A => Backslash,
173 0x2B => Comma, 0x2C => Slash, 0x2D => KeyN, 0x2E => KeyM, 0x2F => Period,
174 0x30 => Tab, 0x31 => Space, 0x32 => Backquote, 0x33 => Backspace,
175 0x35 => Escape,
176 0x36 => MetaRight, 0x37 => MetaLeft, 0x38 => ShiftLeft, 0x39 => CapsLock,
177 0x3A => AltLeft, 0x3B => ControlLeft, 0x3C => ShiftRight, 0x3D => AltRight,
178 0x3E => ControlRight,
179 0x40 => F17, 0x41 => NumpadDecimal, 0x43 => NumpadMultiply, 0x45 => NumpadAdd,
181 0x47 => NumLock,
183 0x4B => NumpadDivide, 0x4C => NumpadEnter, 0x4E => NumpadSubtract,
184 0x4F => F18, 0x50 => F19, 0x51 => NumpadEqual,
185 0x52 => Numpad0, 0x53 => Numpad1, 0x54 => Numpad2, 0x55 => Numpad3,
186 0x56 => Numpad4, 0x57 => Numpad5, 0x58 => Numpad6, 0x59 => Numpad7,
187 0x5A => F20, 0x5B => Numpad8, 0x5C => Numpad9,
188 0x5D => IntlYen, 0x5E => IntlRo, 0x5F => NumpadComma,
189 0x60 => F5, 0x61 => F6, 0x62 => F7, 0x63 => F3, 0x64 => F8, 0x65 => F9,
190 0x66 => Lang2, 0x67 => F11, 0x68 => Lang1, 0x69 => F13, 0x6A => F16,
191 0x6B => F14, 0x6D => F10, 0x6E => ContextMenu, 0x6F => F12,
192 0x71 => F15, 0x72 => Insert, 0x73 => Home, 0x74 => PageUp, 0x75 => Delete,
193 0x76 => F4, 0x77 => End, 0x78 => F2, 0x79 => PageDown, 0x7A => F1,
194 0x7B => ArrowLeft, 0x7C => ArrowRight, 0x7D => ArrowDown, 0x7E => ArrowUp,
195 _ => Unidentified,
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
208 fn the_same_position_gets_the_same_name_on_every_platform() {
209 let cases: &[(PhysicalKey, u32, u32, u16)] = &[
211 (PhysicalKey::KeyA, 30, 0x1E, 0x00),
212 (PhysicalKey::KeyW, 17, 0x11, 0x0D),
213 (PhysicalKey::KeyZ, 44, 0x2C, 0x06),
214 (PhysicalKey::Digit1, 2, 0x02, 0x12),
215 (PhysicalKey::Digit0, 11, 0x0B, 0x1D),
216 (PhysicalKey::Space, 57, 0x39, 0x31),
217 (PhysicalKey::Enter, 28, 0x1C, 0x24),
218 (PhysicalKey::Tab, 15, 0x0F, 0x30),
219 (PhysicalKey::Escape, 1, 0x01, 0x35),
220 (PhysicalKey::Backspace, 14, 0x0E, 0x33),
221 (PhysicalKey::ShiftLeft, 42, 0x2A, 0x38),
222 (PhysicalKey::ShiftRight, 54, 0x36, 0x3C),
223 (PhysicalKey::CapsLock, 58, 0x3A, 0x39),
224 (PhysicalKey::F1, 59, 0x3B, 0x7A),
225 (PhysicalKey::F12, 88, 0x58, 0x6F),
226 (PhysicalKey::Numpad0, 82, 0x52, 0x52),
227 (PhysicalKey::NumpadAdd, 78, 0x4E, 0x45),
228 (PhysicalKey::Comma, 51, 0x33, 0x2B),
229 (PhysicalKey::Slash, 53, 0x35, 0x2C),
230 (PhysicalKey::Backquote, 41, 0x29, 0x32),
231 (PhysicalKey::IntlBackslash, 86, 0x56, 0x0A),
232 ];
233 for (want, evdev, win, mac) in cases {
234 assert_eq!(PhysicalKey::from_evdev(*evdev), *want, "evdev {evdev}");
235 assert_eq!(
236 PhysicalKey::from_windows_scancode(*win, false),
237 *want,
238 "windows {win:#04X}",
239 );
240 assert_eq!(PhysicalKey::from_macos_keycode(*mac), *want, "macos {mac:#04X}");
241 assert_eq!(PhysicalKey::from_x11_keycode(evdev + 8), *want, "x11 {}", evdev + 8);
243 }
244 }
245
246 #[test]
249 fn the_windows_extended_bit_separates_the_duplicated_scancodes() {
250 let pairs: &[(u32, PhysicalKey, PhysicalKey)] = &[
251 (0x1C, PhysicalKey::Enter, PhysicalKey::NumpadEnter),
252 (0x1D, PhysicalKey::ControlLeft, PhysicalKey::ControlRight),
253 (0x35, PhysicalKey::Slash, PhysicalKey::NumpadDivide),
254 (0x38, PhysicalKey::AltLeft, PhysicalKey::AltRight),
255 (0x45, PhysicalKey::NumLock, PhysicalKey::Pause),
256 (0x47, PhysicalKey::Numpad7, PhysicalKey::Home),
257 (0x48, PhysicalKey::Numpad8, PhysicalKey::ArrowUp),
258 (0x4B, PhysicalKey::Numpad4, PhysicalKey::ArrowLeft),
259 (0x4D, PhysicalKey::Numpad6, PhysicalKey::ArrowRight),
260 (0x50, PhysicalKey::Numpad2, PhysicalKey::ArrowDown),
261 (0x52, PhysicalKey::Numpad0, PhysicalKey::Insert),
262 (0x53, PhysicalKey::NumpadDecimal, PhysicalKey::Delete),
263 ];
264 for (sc, plain, ext) in pairs {
265 assert_eq!(PhysicalKey::from_windows_scancode(*sc, false), *plain, "{sc:#04X}");
266 assert_eq!(PhysicalKey::from_windows_scancode(*sc, true), *ext, "E0 {sc:#04X}");
267 assert_ne!(plain, ext);
268 }
269 }
270
271 #[test]
274 fn an_unnamed_code_is_unidentified() {
275 assert_eq!(PhysicalKey::from_evdev(0), PhysicalKey::Unidentified);
276 assert_eq!(PhysicalKey::from_evdev(9999), PhysicalKey::Unidentified);
277 assert_eq!(PhysicalKey::from_windows_scancode(0xFE, false), PhysicalKey::Unidentified);
278 assert_eq!(PhysicalKey::from_windows_scancode(0x02, true), PhysicalKey::Unidentified);
279 assert_eq!(PhysicalKey::from_macos_keycode(0xFF), PhysicalKey::Unidentified);
280 assert_eq!(PhysicalKey::from_macos_keycode(0x3F), PhysicalKey::Unidentified);
282 }
283
284 #[test]
287 fn an_x11_keycode_below_the_offset_cannot_wrap() {
288 for kc in 0..8 {
289 assert_eq!(PhysicalKey::from_x11_keycode(kc), PhysicalKey::Unidentified, "{kc}");
290 }
291 assert_eq!(PhysicalKey::from_x11_keycode(8), PhysicalKey::Unidentified); assert_eq!(PhysicalKey::from_x11_keycode(9), PhysicalKey::Escape); }
294
295 #[test]
298 fn left_and_right_modifiers_are_distinct_positions() {
299 assert_ne!(PhysicalKey::from_evdev(29), PhysicalKey::from_evdev(97));
300 assert_eq!(PhysicalKey::from_evdev(29), PhysicalKey::ControlLeft);
301 assert_eq!(PhysicalKey::from_evdev(97), PhysicalKey::ControlRight);
302 assert_eq!(PhysicalKey::from_macos_keycode(0x37), PhysicalKey::MetaLeft);
303 assert_eq!(PhysicalKey::from_macos_keycode(0x36), PhysicalKey::MetaRight);
304 }
305}