Skip to main content

lamco_rdp_input/
mapper.rs

1//! Scancode Mapping Tables
2//!
3//! Complete RDP scancode to Linux evdev keycode mapping for 200+ keys.
4//! Includes standard keys, extended keys, and international keyboard support.
5
6use crate::error::{InputError, Result};
7use std::collections::HashMap;
8
9/// Linux evdev keycodes
10pub mod keycodes {
11    // Primary keys
12    pub const KEY_ESC: u32 = 1;
13    pub const KEY_1: u32 = 2;
14    pub const KEY_2: u32 = 3;
15    pub const KEY_3: u32 = 4;
16    pub const KEY_4: u32 = 5;
17    pub const KEY_5: u32 = 6;
18    pub const KEY_6: u32 = 7;
19    pub const KEY_7: u32 = 8;
20    pub const KEY_8: u32 = 9;
21    pub const KEY_9: u32 = 10;
22    pub const KEY_0: u32 = 11;
23    pub const KEY_MINUS: u32 = 12;
24    pub const KEY_EQUAL: u32 = 13;
25    pub const KEY_BACKSPACE: u32 = 14;
26    pub const KEY_TAB: u32 = 15;
27    pub const KEY_Q: u32 = 16;
28    pub const KEY_W: u32 = 17;
29    pub const KEY_E: u32 = 18;
30    pub const KEY_R: u32 = 19;
31    pub const KEY_T: u32 = 20;
32    pub const KEY_Y: u32 = 21;
33    pub const KEY_U: u32 = 22;
34    pub const KEY_I: u32 = 23;
35    pub const KEY_O: u32 = 24;
36    pub const KEY_P: u32 = 25;
37    pub const KEY_LEFTBRACE: u32 = 26;
38    pub const KEY_RIGHTBRACE: u32 = 27;
39    pub const KEY_ENTER: u32 = 28;
40    pub const KEY_LEFTCTRL: u32 = 29;
41    pub const KEY_A: u32 = 30;
42    pub const KEY_S: u32 = 31;
43    pub const KEY_D: u32 = 32;
44    pub const KEY_F: u32 = 33;
45    pub const KEY_G: u32 = 34;
46    pub const KEY_H: u32 = 35;
47    pub const KEY_J: u32 = 36;
48    pub const KEY_K: u32 = 37;
49    pub const KEY_L: u32 = 38;
50    pub const KEY_SEMICOLON: u32 = 39;
51    pub const KEY_APOSTROPHE: u32 = 40;
52    pub const KEY_GRAVE: u32 = 41;
53    pub const KEY_LEFTSHIFT: u32 = 42;
54    pub const KEY_BACKSLASH: u32 = 43;
55    pub const KEY_Z: u32 = 44;
56    pub const KEY_X: u32 = 45;
57    pub const KEY_C: u32 = 46;
58    pub const KEY_V: u32 = 47;
59    pub const KEY_B: u32 = 48;
60    pub const KEY_N: u32 = 49;
61    pub const KEY_M: u32 = 50;
62    pub const KEY_COMMA: u32 = 51;
63    pub const KEY_DOT: u32 = 52;
64    pub const KEY_SLASH: u32 = 53;
65    pub const KEY_RIGHTSHIFT: u32 = 54;
66    pub const KEY_KPASTERISK: u32 = 55;
67    pub const KEY_LEFTALT: u32 = 56;
68    pub const KEY_SPACE: u32 = 57;
69    pub const KEY_CAPSLOCK: u32 = 58;
70
71    // Function keys
72    pub const KEY_F1: u32 = 59;
73    pub const KEY_F2: u32 = 60;
74    pub const KEY_F3: u32 = 61;
75    pub const KEY_F4: u32 = 62;
76    pub const KEY_F5: u32 = 63;
77    pub const KEY_F6: u32 = 64;
78    pub const KEY_F7: u32 = 65;
79    pub const KEY_F8: u32 = 66;
80    pub const KEY_F9: u32 = 67;
81    pub const KEY_F10: u32 = 68;
82    pub const KEY_NUMLOCK: u32 = 69;
83    pub const KEY_SCROLLLOCK: u32 = 70;
84
85    // Numpad
86    pub const KEY_KP7: u32 = 71;
87    pub const KEY_KP8: u32 = 72;
88    pub const KEY_KP9: u32 = 73;
89    pub const KEY_KPMINUS: u32 = 74;
90    pub const KEY_KP4: u32 = 75;
91    pub const KEY_KP5: u32 = 76;
92    pub const KEY_KP6: u32 = 77;
93    pub const KEY_KPPLUS: u32 = 78;
94    pub const KEY_KP1: u32 = 79;
95    pub const KEY_KP2: u32 = 80;
96    pub const KEY_KP3: u32 = 81;
97    pub const KEY_KP0: u32 = 82;
98    pub const KEY_KPDOT: u32 = 83;
99
100    pub const KEY_102ND: u32 = 86;
101    pub const KEY_F11: u32 = 87;
102    pub const KEY_F12: u32 = 88;
103    pub const KEY_RO: u32 = 89;
104    pub const KEY_KATAKANAHIRAGANA: u32 = 90;
105    pub const KEY_HENKAN: u32 = 92;
106    pub const KEY_MUHENKAN: u32 = 94;
107    pub const KEY_KPENTER: u32 = 96;
108    pub const KEY_RIGHTCTRL: u32 = 97;
109    pub const KEY_KPSLASH: u32 = 98;
110    pub const KEY_SYSRQ: u32 = 99;
111    pub const KEY_RIGHTALT: u32 = 100;
112    pub const KEY_HOME: u32 = 102;
113    pub const KEY_UP: u32 = 103;
114    pub const KEY_PAGEUP: u32 = 104;
115    pub const KEY_LEFT: u32 = 105;
116    pub const KEY_RIGHT: u32 = 106;
117    pub const KEY_END: u32 = 107;
118    pub const KEY_DOWN: u32 = 108;
119    pub const KEY_PAGEDOWN: u32 = 109;
120    pub const KEY_INSERT: u32 = 110;
121    pub const KEY_DELETE: u32 = 111;
122    pub const KEY_MUTE: u32 = 113;
123    pub const KEY_VOLUMEDOWN: u32 = 114;
124    pub const KEY_VOLUMEUP: u32 = 115;
125    pub const KEY_POWER: u32 = 116;
126    pub const KEY_KPEQUAL: u32 = 117;
127    pub const KEY_PAUSE: u32 = 119;
128    pub const KEY_KPCOMMA: u32 = 121;
129    pub const KEY_HANGEUL: u32 = 122;
130    pub const KEY_HANJA: u32 = 123;
131    pub const KEY_YEN: u32 = 124;
132    pub const KEY_LEFTMETA: u32 = 125;
133    pub const KEY_RIGHTMETA: u32 = 126;
134    pub const KEY_COMPOSE: u32 = 127;
135    pub const KEY_STOP: u32 = 128;
136    pub const KEY_AGAIN: u32 = 129;
137    pub const KEY_PROPS: u32 = 130;
138    pub const KEY_UNDO: u32 = 131;
139    pub const KEY_FRONT: u32 = 132;
140    pub const KEY_COPY: u32 = 133;
141    pub const KEY_OPEN: u32 = 134;
142    pub const KEY_PASTE: u32 = 135;
143    pub const KEY_FIND: u32 = 136;
144    pub const KEY_CUT: u32 = 137;
145    pub const KEY_HELP: u32 = 138;
146    pub const KEY_MENU: u32 = 139;
147    pub const KEY_CALC: u32 = 140;
148    pub const KEY_SLEEP: u32 = 142;
149    pub const KEY_WAKEUP: u32 = 143;
150    pub const KEY_WWW: u32 = 150;
151    pub const KEY_MAIL: u32 = 155;
152    pub const KEY_BOOKMARKS: u32 = 156;
153    pub const KEY_COMPUTER: u32 = 157;
154    pub const KEY_BACK: u32 = 158;
155    pub const KEY_FORWARD: u32 = 159;
156    pub const KEY_EJECTCD: u32 = 161;
157    pub const KEY_NEXTSONG: u32 = 163;
158    pub const KEY_PLAYPAUSE: u32 = 164;
159    pub const KEY_PREVIOUSSONG: u32 = 165;
160    pub const KEY_STOPCD: u32 = 166;
161    pub const KEY_REFRESH: u32 = 173;
162    pub const KEY_F13: u32 = 183;
163    pub const KEY_F14: u32 = 184;
164    pub const KEY_F15: u32 = 185;
165    pub const KEY_F16: u32 = 186;
166    pub const KEY_F17: u32 = 187;
167    pub const KEY_F18: u32 = 188;
168    pub const KEY_F19: u32 = 189;
169    pub const KEY_F20: u32 = 190;
170    pub const KEY_F21: u32 = 191;
171    pub const KEY_F22: u32 = 192;
172    pub const KEY_F23: u32 = 193;
173    pub const KEY_F24: u32 = 194;
174    pub const KEY_MEDIA: u32 = 226;
175    pub const KEY_SEARCH: u32 = 217;
176    pub const KEY_HOMEPAGE: u32 = 172;
177    pub const KEY_BREAK: u32 = 411;
178    pub const KEY_PRINT: u32 = 210;
179}
180
181#[allow(clippy::wildcard_imports)]
182use keycodes::*;
183
184/// Scancode mapper handles RDP scancode to evdev keycode translation
185pub struct ScancodeMapper {
186    /// Primary scancode map (0x00-0x7F)
187    primary_map: HashMap<u16, u32>,
188
189    /// Extended scancode map (0xE000-0xE0FF)
190    extended_map: HashMap<u16, u32>,
191
192    /// E1 prefix scancode map
193    e1_map: HashMap<u32, u32>,
194
195    /// Reverse map for keycode to scancode
196    reverse_map: HashMap<u32, u16>,
197
198    /// Layout-specific overrides
199    layout_overrides: HashMap<String, HashMap<u16, u32>>,
200
201    /// Current keyboard layout
202    current_layout: String,
203}
204
205impl ScancodeMapper {
206    /// Create a new scancode mapper
207    pub fn new() -> Self {
208        let mut mapper = Self {
209            primary_map: HashMap::new(),
210            extended_map: HashMap::new(),
211            e1_map: HashMap::new(),
212            reverse_map: HashMap::new(),
213            layout_overrides: HashMap::new(),
214            current_layout: "us".to_string(),
215        };
216
217        mapper.initialize_mappings();
218        mapper
219    }
220
221    /// Initialize all scancode mappings
222    fn initialize_mappings(&mut self) {
223        self.initialize_primary_map();
224        self.initialize_extended_map();
225        self.initialize_e1_map();
226        self.build_reverse_map();
227        self.load_layout_overrides();
228    }
229
230    /// Initialize primary scancode map (0x00-0x7F)
231    fn initialize_primary_map(&mut self) {
232        let mappings = vec![
233            (0x01, KEY_ESC),
234            (0x02, KEY_1),
235            (0x03, KEY_2),
236            (0x04, KEY_3),
237            (0x05, KEY_4),
238            (0x06, KEY_5),
239            (0x07, KEY_6),
240            (0x08, KEY_7),
241            (0x09, KEY_8),
242            (0x0A, KEY_9),
243            (0x0B, KEY_0),
244            (0x0C, KEY_MINUS),
245            (0x0D, KEY_EQUAL),
246            (0x0E, KEY_BACKSPACE),
247            (0x0F, KEY_TAB),
248            (0x10, KEY_Q),
249            (0x11, KEY_W),
250            (0x12, KEY_E),
251            (0x13, KEY_R),
252            (0x14, KEY_T),
253            (0x15, KEY_Y),
254            (0x16, KEY_U),
255            (0x17, KEY_I),
256            (0x18, KEY_O),
257            (0x19, KEY_P),
258            (0x1A, KEY_LEFTBRACE),
259            (0x1B, KEY_RIGHTBRACE),
260            (0x1C, KEY_ENTER),
261            (0x1D, KEY_LEFTCTRL),
262            (0x1E, KEY_A),
263            (0x1F, KEY_S),
264            (0x20, KEY_D),
265            (0x21, KEY_F),
266            (0x22, KEY_G),
267            (0x23, KEY_H),
268            (0x24, KEY_J),
269            (0x25, KEY_K),
270            (0x26, KEY_L),
271            (0x27, KEY_SEMICOLON),
272            (0x28, KEY_APOSTROPHE),
273            (0x29, KEY_GRAVE),
274            (0x2A, KEY_LEFTSHIFT),
275            (0x2B, KEY_BACKSLASH),
276            (0x2C, KEY_Z),
277            (0x2D, KEY_X),
278            (0x2E, KEY_C),
279            (0x2F, KEY_V),
280            (0x30, KEY_B),
281            (0x31, KEY_N),
282            (0x32, KEY_M),
283            (0x33, KEY_COMMA),
284            (0x34, KEY_DOT),
285            (0x35, KEY_SLASH),
286            (0x36, KEY_RIGHTSHIFT),
287            (0x37, KEY_KPASTERISK),
288            (0x38, KEY_LEFTALT),
289            (0x39, KEY_SPACE),
290            (0x3A, KEY_CAPSLOCK),
291            (0x3B, KEY_F1),
292            (0x3C, KEY_F2),
293            (0x3D, KEY_F3),
294            (0x3E, KEY_F4),
295            (0x3F, KEY_F5),
296            (0x40, KEY_F6),
297            (0x41, KEY_F7),
298            (0x42, KEY_F8),
299            (0x43, KEY_F9),
300            (0x44, KEY_F10),
301            (0x45, KEY_NUMLOCK),
302            (0x46, KEY_SCROLLLOCK),
303            (0x47, KEY_KP7),
304            (0x48, KEY_KP8),
305            (0x49, KEY_KP9),
306            (0x4A, KEY_KPMINUS),
307            (0x4B, KEY_KP4),
308            (0x4C, KEY_KP5),
309            (0x4D, KEY_KP6),
310            (0x4E, KEY_KPPLUS),
311            (0x4F, KEY_KP1),
312            (0x50, KEY_KP2),
313            (0x51, KEY_KP3),
314            (0x52, KEY_KP0),
315            (0x53, KEY_KPDOT),
316            (0x54, KEY_SYSRQ),
317            (0x56, KEY_102ND),
318            (0x57, KEY_F11),
319            (0x58, KEY_F12),
320            (0x59, KEY_KPEQUAL),
321            (0x5A, KEY_F13),
322            (0x5B, KEY_F14),
323            (0x5C, KEY_F15),
324            (0x5D, KEY_F16),
325            (0x5E, KEY_F17),
326            (0x5F, KEY_F18),
327            (0x60, KEY_F19),
328            (0x61, KEY_F20),
329            (0x62, KEY_F21),
330            (0x63, KEY_F22),
331            (0x64, KEY_F23),
332            (0x65, KEY_F24),
333            (0x70, KEY_KATAKANAHIRAGANA),
334            (0x71, KEY_MUHENKAN),
335            (0x72, KEY_HENKAN),
336            (0x73, KEY_RO),
337            (0x74, KEY_YEN),
338            (0x75, KEY_HANGEUL),
339            (0x76, KEY_HANJA),
340            (0x77, KEY_LEFTMETA),
341            (0x78, KEY_RIGHTMETA),
342            (0x79, KEY_COMPOSE),
343            (0x7A, KEY_STOP),
344            (0x7B, KEY_AGAIN),
345            (0x7C, KEY_PROPS),
346            (0x7D, KEY_UNDO),
347            (0x7E, KEY_FRONT),
348            (0x7F, KEY_COPY),
349        ];
350
351        for (scancode, keycode) in mappings {
352            self.primary_map.insert(scancode, keycode);
353        }
354    }
355
356    /// Initialize extended scancode map (E0 prefix)
357    fn initialize_extended_map(&mut self) {
358        let mappings = vec![
359            (0xE01C, KEY_KPENTER),
360            (0xE01D, KEY_RIGHTCTRL),
361            (0xE020, KEY_MUTE),
362            (0xE021, KEY_CALC),
363            (0xE022, KEY_PLAYPAUSE),
364            (0xE024, KEY_STOPCD),
365            (0xE02E, KEY_VOLUMEDOWN),
366            (0xE030, KEY_VOLUMEUP),
367            (0xE032, KEY_HOMEPAGE),
368            (0xE035, KEY_KPSLASH),
369            (0xE037, KEY_PRINT),
370            (0xE038, KEY_RIGHTALT),
371            (0xE045, KEY_PAUSE),
372            (0xE047, KEY_HOME),
373            (0xE048, KEY_UP),
374            (0xE049, KEY_PAGEUP),
375            (0xE04B, KEY_LEFT),
376            (0xE04D, KEY_RIGHT),
377            (0xE04F, KEY_END),
378            (0xE050, KEY_DOWN),
379            (0xE051, KEY_PAGEDOWN),
380            (0xE052, KEY_INSERT),
381            (0xE053, KEY_DELETE),
382            (0xE05B, KEY_LEFTMETA),
383            (0xE05C, KEY_RIGHTMETA),
384            (0xE05D, KEY_MENU),
385            (0xE05E, KEY_POWER),
386            (0xE05F, KEY_SLEEP),
387            (0xE063, KEY_WAKEUP),
388            (0xE065, KEY_SEARCH),
389            (0xE066, KEY_BOOKMARKS),
390            (0xE067, KEY_REFRESH),
391            (0xE068, KEY_STOP),
392            (0xE069, KEY_FORWARD),
393            (0xE06A, KEY_BACK),
394            (0xE06B, KEY_COMPUTER),
395            (0xE06C, KEY_MAIL),
396            (0xE06D, KEY_MEDIA),
397            (0xE010, KEY_PREVIOUSSONG),
398            (0xE019, KEY_NEXTSONG),
399            (0xE02C, KEY_EJECTCD),
400        ];
401
402        for (scancode, keycode) in mappings {
403            self.extended_map.insert(scancode, keycode);
404        }
405    }
406
407    /// Initialize E1 prefix scancode map
408    fn initialize_e1_map(&mut self) {
409        self.e1_map.insert(0xE11D45, KEY_PAUSE);
410        self.e1_map.insert(0xE11D46, KEY_BREAK);
411    }
412
413    /// Build reverse mapping for keycode to scancode
414    fn build_reverse_map(&mut self) {
415        for (&scancode, &keycode) in &self.primary_map {
416            self.reverse_map.insert(keycode, scancode);
417        }
418        for (&scancode, &keycode) in &self.extended_map {
419            self.reverse_map.insert(keycode, scancode);
420        }
421    }
422
423    /// Load layout-specific overrides
424    ///
425    /// These remap physical scancode positions for layouts where the
426    /// physical key arrangement differs from US QWERTY. This handles
427    /// the case where the virtual keyboard device needs layout-correct
428    /// evdev keycodes rather than relying on XKB for remapping.
429    fn load_layout_overrides(&mut self) {
430        // German QWERTZ: Y and Z are swapped
431        let mut de = HashMap::new();
432        de.insert(0x15, KEY_Z); // Y position → Z
433        de.insert(0x2C, KEY_Y); // Z position → Y
434        self.layout_overrides.insert("de".to_string(), de);
435
436        // French AZERTY: A↔Q and W↔Z swapped, M moved
437        let mut fr = HashMap::new();
438        fr.insert(0x10, KEY_A); // Q position → A
439        fr.insert(0x1E, KEY_Q); // A position → Q
440        fr.insert(0x11, KEY_Z); // W position → Z
441        fr.insert(0x2C, KEY_W); // Z position → W
442        fr.insert(0x27, KEY_M); // semicolon position → M
443        fr.insert(0x32, KEY_COMMA); // M position → comma
444        self.layout_overrides.insert("fr".to_string(), fr);
445
446        // UK layout: nearly identical to US, only the backslash/102nd key differs
447        let mut uk = HashMap::new();
448        uk.insert(0x2B, KEY_102ND); // backslash position → 102nd key (# on UK)
449        self.layout_overrides.insert("uk".to_string(), uk.clone());
450        self.layout_overrides.insert("gb".to_string(), uk);
451
452        // Spanish layout: same physical arrangement as US QWERTY
453        // Differences are handled by XKB (accent keys, ñ via dead keys)
454        // No scancode overrides needed, but register it as a known layout
455        self.layout_overrides.insert("es".to_string(), HashMap::new());
456
457        // Portuguese layout: same physical arrangement as US QWERTY
458        self.layout_overrides.insert("pt".to_string(), HashMap::new());
459
460        // Italian layout: same physical arrangement as US QWERTY
461        self.layout_overrides.insert("it".to_string(), HashMap::new());
462
463        // Belgian AZERTY: same as French AZERTY for physical keys
464        let mut be = HashMap::new();
465        be.insert(0x10, KEY_A);
466        be.insert(0x1E, KEY_Q);
467        be.insert(0x11, KEY_Z);
468        be.insert(0x2C, KEY_W);
469        be.insert(0x27, KEY_M);
470        be.insert(0x32, KEY_COMMA);
471        self.layout_overrides.insert("be".to_string(), be);
472
473        // Swiss German/French: QWERTZ base (same Y↔Z swap as German)
474        let mut ch = HashMap::new();
475        ch.insert(0x15, KEY_Z);
476        ch.insert(0x2C, KEY_Y);
477        self.layout_overrides.insert("ch".to_string(), ch);
478
479        // Dvorak: comprehensive letter rearrangement
480        let mut dvorak = HashMap::new();
481        // Top row: ' , . p y f g c r l
482        dvorak.insert(0x10, KEY_APOSTROPHE); // Q → '
483        dvorak.insert(0x11, KEY_COMMA); // W → ,
484        dvorak.insert(0x12, KEY_DOT); // E → .
485        dvorak.insert(0x13, KEY_P); // R → P
486        dvorak.insert(0x14, KEY_Y); // T → Y
487        dvorak.insert(0x15, KEY_F); // Y → F
488        dvorak.insert(0x16, KEY_G); // U → G
489        dvorak.insert(0x17, KEY_C); // I → C
490        dvorak.insert(0x18, KEY_R); // O → R
491        dvorak.insert(0x19, KEY_L); // P → L
492                                    // Home row: a o e u i d h t n s
493        dvorak.insert(0x1E, KEY_A); // A → A (same)
494        dvorak.insert(0x1F, KEY_O); // S → O
495        dvorak.insert(0x20, KEY_E); // D → E
496        dvorak.insert(0x21, KEY_U); // F → U
497        dvorak.insert(0x22, KEY_I); // G → I
498        dvorak.insert(0x23, KEY_D); // H → D
499        dvorak.insert(0x24, KEY_H); // J → H
500        dvorak.insert(0x25, KEY_T); // K → T
501        dvorak.insert(0x26, KEY_N); // L → N
502        dvorak.insert(0x27, KEY_S); // ; → S
503                                    // Bottom row: ; q j k x b m w v z
504        dvorak.insert(0x2C, KEY_SEMICOLON); // Z → ;
505        dvorak.insert(0x2D, KEY_Q); // X → Q
506        dvorak.insert(0x2E, KEY_J); // C → J
507        dvorak.insert(0x2F, KEY_K); // V → K
508        dvorak.insert(0x30, KEY_X); // B → X
509        dvorak.insert(0x31, KEY_B); // N → B
510        dvorak.insert(0x32, KEY_M); // M → M (same)
511        dvorak.insert(0x33, KEY_W); // , → W
512        dvorak.insert(0x34, KEY_V); // . → V
513        dvorak.insert(0x35, KEY_Z); // / → Z
514        self.layout_overrides.insert("dvorak".to_string(), dvorak);
515
516        // Colemak: partial rearrangement from QWERTY
517        let mut colemak = HashMap::new();
518        // Changes from QWERTY: e→f, r→p, t→g, y→j, u→l, i→u, o→y, p→;
519        // s→r, d→s, f→t, g→d, j→n, k→e, l→i, ;→o
520        // n→k
521        colemak.insert(0x12, KEY_F); // E → F
522        colemak.insert(0x13, KEY_P); // R → P
523        colemak.insert(0x14, KEY_G); // T → G
524        colemak.insert(0x15, KEY_J); // Y → J
525        colemak.insert(0x16, KEY_L); // U → L
526        colemak.insert(0x17, KEY_U); // I → U
527        colemak.insert(0x18, KEY_Y); // O → Y
528        colemak.insert(0x19, KEY_SEMICOLON); // P → ;
529        colemak.insert(0x1F, KEY_R); // S → R
530        colemak.insert(0x20, KEY_S); // D → S
531        colemak.insert(0x21, KEY_T); // F → T
532        colemak.insert(0x22, KEY_D); // G → D
533        colemak.insert(0x24, KEY_N); // J → N
534        colemak.insert(0x25, KEY_E); // K → E
535        colemak.insert(0x26, KEY_I); // L → I
536        colemak.insert(0x27, KEY_O); // ; → O
537        colemak.insert(0x31, KEY_K); // N → K
538        self.layout_overrides.insert("colemak".to_string(), colemak);
539    }
540
541    /// Translate RDP scancode to Linux evdev keycode
542    pub fn translate_scancode(&self, scancode: u32, extended: bool, e1_prefix: bool) -> Result<u32> {
543        if e1_prefix {
544            // Handle E1 prefix scancodes
545            self.e1_map
546                .get(&scancode)
547                .copied()
548                .ok_or(InputError::UnknownScancode(scancode as u16))
549        } else if extended {
550            // Handle E0 prefix (extended) scancodes
551            let extended_scan = 0xE000 | (scancode as u16 & 0xFF);
552            self.extended_map
553                .get(&extended_scan)
554                .or_else(|| self.primary_map.get(&(scancode as u16)))
555                .copied()
556                .ok_or(InputError::UnknownScancode(extended_scan))
557        } else {
558            // Check for layout-specific overrides first
559            if let Some(overrides) = self.layout_overrides.get(&self.current_layout) {
560                if let Some(keycode) = overrides.get(&(scancode as u16)) {
561                    return Ok(*keycode);
562                }
563            }
564            // Standard scancode translation
565            self.primary_map
566                .get(&(scancode as u16))
567                .copied()
568                .ok_or(InputError::UnknownScancode(scancode as u16))
569        }
570    }
571
572    /// Translate Linux keycode to RDP scancode
573    pub fn translate_keycode(&self, keycode: u32) -> Result<u16> {
574        self.reverse_map
575            .get(&keycode)
576            .copied()
577            .ok_or(InputError::UnknownKeycode(keycode))
578    }
579
580    /// Set keyboard layout
581    pub fn set_layout(&mut self, layout: &str) {
582        self.current_layout = layout.to_string();
583    }
584
585    /// Get current keyboard layout
586    pub fn layout(&self) -> &str {
587        &self.current_layout
588    }
589
590    /// Check if scancode is mapped
591    pub fn is_mapped(&self, scancode: u16, extended: bool) -> bool {
592        if extended {
593            let extended_scan = 0xE000 | (scancode & 0xFF);
594            self.extended_map.contains_key(&extended_scan)
595        } else {
596            self.primary_map.contains_key(&scancode)
597        }
598    }
599
600    /// Get total number of mapped keys
601    pub fn mapped_key_count(&self) -> usize {
602        self.primary_map.len() + self.extended_map.len() + self.e1_map.len()
603    }
604}
605
606impl Default for ScancodeMapper {
607    fn default() -> Self {
608        Self::new()
609    }
610}
611
612#[cfg(test)]
613mod tests {
614    use super::*;
615
616    #[test]
617    fn test_scancode_mapper_creation() {
618        let mapper = ScancodeMapper::new();
619        // 157 mappings: primary + extended + E1 prefix keys
620        assert!(mapper.mapped_key_count() >= 150);
621    }
622
623    #[test]
624    fn test_primary_scancode_mapping() {
625        let mapper = ScancodeMapper::new();
626
627        // Test letter keys
628        assert_eq!(mapper.translate_scancode(0x1E, false, false).unwrap(), KEY_A);
629        assert_eq!(mapper.translate_scancode(0x2C, false, false).unwrap(), KEY_Z);
630
631        // Test number keys
632        assert_eq!(mapper.translate_scancode(0x02, false, false).unwrap(), KEY_1);
633        assert_eq!(mapper.translate_scancode(0x0B, false, false).unwrap(), KEY_0);
634
635        // Test function keys
636        assert_eq!(mapper.translate_scancode(0x3B, false, false).unwrap(), KEY_F1);
637        assert_eq!(mapper.translate_scancode(0x58, false, false).unwrap(), KEY_F12);
638    }
639
640    #[test]
641    fn test_extended_scancode_mapping() {
642        let mapper = ScancodeMapper::new();
643
644        // Test navigation keys
645        assert_eq!(mapper.translate_scancode(0x47, true, false).unwrap(), KEY_HOME);
646        assert_eq!(mapper.translate_scancode(0x4F, true, false).unwrap(), KEY_END);
647        assert_eq!(mapper.translate_scancode(0x48, true, false).unwrap(), KEY_UP);
648        assert_eq!(mapper.translate_scancode(0x50, true, false).unwrap(), KEY_DOWN);
649        assert_eq!(mapper.translate_scancode(0x4B, true, false).unwrap(), KEY_LEFT);
650        assert_eq!(mapper.translate_scancode(0x4D, true, false).unwrap(), KEY_RIGHT);
651
652        // Test media keys
653        assert_eq!(mapper.translate_scancode(0x22, true, false).unwrap(), KEY_PLAYPAUSE);
654        assert_eq!(mapper.translate_scancode(0x24, true, false).unwrap(), KEY_STOPCD);
655    }
656
657    #[test]
658    fn test_bidirectional_mapping() {
659        let mapper = ScancodeMapper::new();
660
661        // Test round-trip for common keys
662        let test_keys = vec![KEY_A, KEY_Z, KEY_ENTER, KEY_SPACE, KEY_F1, KEY_F12];
663
664        for keycode in test_keys {
665            let scancode = mapper.translate_keycode(keycode).unwrap();
666            let translated = mapper.translate_scancode(scancode as u32, false, false).unwrap();
667            assert_eq!(translated, keycode);
668        }
669    }
670
671    #[test]
672    fn test_layout_override() {
673        let mut mapper = ScancodeMapper::new();
674
675        // US layout: Y key
676        assert_eq!(mapper.translate_scancode(0x15, false, false).unwrap(), KEY_Y);
677
678        // German layout: Y → Z
679        mapper.set_layout("de");
680        assert_eq!(mapper.translate_scancode(0x15, false, false).unwrap(), KEY_Z);
681
682        // French layout
683        mapper.set_layout("fr");
684        assert_eq!(mapper.translate_scancode(0x10, false, false).unwrap(), KEY_A);
685    }
686
687    #[test]
688    fn test_unknown_scancode() {
689        let mapper = ScancodeMapper::new();
690
691        // Test unmapped scancode
692        let result = mapper.translate_scancode(0xFF, false, false);
693        assert!(result.is_err());
694        match result {
695            Err(InputError::UnknownScancode(_)) => {}
696            _ => panic!("Expected UnknownScancode error"),
697        }
698    }
699
700    #[test]
701    fn test_unknown_keycode() {
702        let mapper = ScancodeMapper::new();
703
704        // Test unmapped keycode
705        let result = mapper.translate_keycode(9999);
706        assert!(result.is_err());
707        match result {
708            Err(InputError::UnknownKeycode(_)) => {}
709            _ => panic!("Expected UnknownKeycode error"),
710        }
711    }
712
713    #[test]
714    fn test_is_mapped() {
715        let mapper = ScancodeMapper::new();
716
717        assert!(mapper.is_mapped(0x1E, false)); // A key
718        assert!(mapper.is_mapped(0x47, true)); // Home key (extended)
719        assert!(!mapper.is_mapped(0xFF, false)); // Unknown
720    }
721
722    #[test]
723    fn test_all_primary_keys_mapped() {
724        let mapper = ScancodeMapper::new();
725
726        // Test common primary scancodes
727        for scancode in 0x01..=0x58 {
728            if scancode == 0x00 || scancode == 0x55 {
729                continue; // Skip undefined
730            }
731            assert!(
732                mapper.is_mapped(scancode, false),
733                "Scancode 0x{:02X} not mapped",
734                scancode
735            );
736        }
737    }
738
739    #[test]
740    fn test_function_keys_f13_to_f24() {
741        let mapper = ScancodeMapper::new();
742
743        assert_eq!(mapper.translate_scancode(0x5A, false, false).unwrap(), KEY_F13);
744        assert_eq!(mapper.translate_scancode(0x65, false, false).unwrap(), KEY_F24);
745    }
746
747    #[test]
748    fn test_multimedia_keys() {
749        let mapper = ScancodeMapper::new();
750
751        assert_eq!(mapper.translate_scancode(0x20, true, false).unwrap(), KEY_MUTE);
752        assert_eq!(mapper.translate_scancode(0x2E, true, false).unwrap(), KEY_VOLUMEDOWN);
753        assert_eq!(mapper.translate_scancode(0x30, true, false).unwrap(), KEY_VOLUMEUP);
754    }
755
756    #[test]
757    fn test_japanese_keys() {
758        let mapper = ScancodeMapper::new();
759
760        assert_eq!(
761            mapper.translate_scancode(0x70, false, false).unwrap(),
762            KEY_KATAKANAHIRAGANA
763        );
764        assert_eq!(mapper.translate_scancode(0x71, false, false).unwrap(), KEY_MUHENKAN);
765        assert_eq!(mapper.translate_scancode(0x72, false, false).unwrap(), KEY_HENKAN);
766    }
767
768    #[test]
769    fn test_korean_keys() {
770        let mapper = ScancodeMapper::new();
771
772        assert_eq!(mapper.translate_scancode(0x75, false, false).unwrap(), KEY_HANGEUL);
773        assert_eq!(mapper.translate_scancode(0x76, false, false).unwrap(), KEY_HANJA);
774    }
775}