Skip to main content

kbd_evdev/
convert.rs

1//! Extension traits for converting between evdev key codes and `kbd` key types.
2//!
3//! These are extension traits rather than `From`/`Into` impls because of the
4//! orphan rule: neither `evdev::KeyCode` nor `kbd::Key` is defined in
5//! this crate, so we cannot implement foreign traits for foreign types.
6//!
7//! # Usage
8//!
9//! ```rust
10//! use evdev::KeyCode;
11//! use kbd::prelude::*;
12//! use kbd_evdev::EvdevKeyCodeExt;
13//!
14//! let key: Key = KeyCode::KEY_A.to_key();
15//! assert_eq!(key, Key::A);
16//! ```
17
18use evdev::KeyCode;
19use kbd::key::Key;
20
21mod private {
22    pub trait Sealed {}
23    impl Sealed for evdev::KeyCode {}
24    impl Sealed for kbd::key::Key {}
25}
26
27/// Extension trait on [`evdev::KeyCode`] for converting to [`kbd::key::Key`].
28///
29/// Returns [`Key::UNIDENTIFIED`] for key codes that have no `kbd` mapping
30/// (e.g., `KEY_PROG2`, `KEY_COFFEE`).
31///
32/// This trait is sealed and cannot be implemented outside this crate.
33pub trait EvdevKeyCodeExt: private::Sealed {
34    /// Convert this evdev key code to a [`Key`].
35    ///
36    /// Returns [`Key::UNIDENTIFIED`] for key codes that don't have a mapping.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use evdev::KeyCode;
42    /// use kbd::prelude::*;
43    /// use kbd_evdev::EvdevKeyCodeExt;
44    ///
45    /// assert_eq!(KeyCode::KEY_A.to_key(), Key::A);
46    /// assert_eq!(KeyCode::KEY_LEFTCTRL.to_key(), Key::CONTROL_LEFT);
47    ///
48    /// // Unmapped codes return UNIDENTIFIED
49    /// assert_eq!(KeyCode::KEY_PROG2.to_key(), Key::UNIDENTIFIED);
50    /// ```
51    #[must_use]
52    fn to_key(self) -> Key;
53}
54
55/// Extension trait on [`kbd::key::Key`] for converting to [`evdev::KeyCode`].
56///
57/// [`Key::UNIDENTIFIED`] and any key without a known evdev equivalent maps
58/// to `KeyCode::KEY_UNKNOWN`.
59///
60/// This trait is sealed and cannot be implemented outside this crate.
61pub trait KbdKeyExt: private::Sealed {
62    /// Convert this key to an evdev [`KeyCode`].
63    ///
64    /// [`Key::UNIDENTIFIED`] maps to `KeyCode::KEY_UNKNOWN`.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use evdev::KeyCode;
70    /// use kbd::prelude::*;
71    /// use kbd_evdev::KbdKeyExt;
72    ///
73    /// assert_eq!(Key::A.to_key_code(), KeyCode::KEY_A);
74    /// assert_eq!(Key::CONTROL_LEFT.to_key_code(), KeyCode::KEY_LEFTCTRL);
75    ///
76    /// // UNIDENTIFIED maps to KEY_UNKNOWN
77    /// assert_eq!(Key::UNIDENTIFIED.to_key_code(), KeyCode::KEY_UNKNOWN);
78    /// ```
79    #[must_use]
80    fn to_key_code(self) -> KeyCode;
81}
82
83impl EvdevKeyCodeExt for KeyCode {
84    #[allow(clippy::too_many_lines)]
85    fn to_key(self) -> Key {
86        match self {
87            KeyCode::KEY_A => Key::A,
88            KeyCode::KEY_B => Key::B,
89            KeyCode::KEY_C => Key::C,
90            KeyCode::KEY_D => Key::D,
91            KeyCode::KEY_E => Key::E,
92            KeyCode::KEY_F => Key::F,
93            KeyCode::KEY_G => Key::G,
94            KeyCode::KEY_H => Key::H,
95            KeyCode::KEY_I => Key::I,
96            KeyCode::KEY_J => Key::J,
97            KeyCode::KEY_K => Key::K,
98            KeyCode::KEY_L => Key::L,
99            KeyCode::KEY_M => Key::M,
100            KeyCode::KEY_N => Key::N,
101            KeyCode::KEY_O => Key::O,
102            KeyCode::KEY_P => Key::P,
103            KeyCode::KEY_Q => Key::Q,
104            KeyCode::KEY_R => Key::R,
105            KeyCode::KEY_S => Key::S,
106            KeyCode::KEY_T => Key::T,
107            KeyCode::KEY_U => Key::U,
108            KeyCode::KEY_V => Key::V,
109            KeyCode::KEY_W => Key::W,
110            KeyCode::KEY_X => Key::X,
111            KeyCode::KEY_Y => Key::Y,
112            KeyCode::KEY_Z => Key::Z,
113            KeyCode::KEY_0 => Key::DIGIT0,
114            KeyCode::KEY_1 => Key::DIGIT1,
115            KeyCode::KEY_2 => Key::DIGIT2,
116            KeyCode::KEY_3 => Key::DIGIT3,
117            KeyCode::KEY_4 => Key::DIGIT4,
118            KeyCode::KEY_5 => Key::DIGIT5,
119            KeyCode::KEY_6 => Key::DIGIT6,
120            KeyCode::KEY_7 => Key::DIGIT7,
121            KeyCode::KEY_8 => Key::DIGIT8,
122            KeyCode::KEY_9 => Key::DIGIT9,
123            KeyCode::KEY_F1 => Key::F1,
124            KeyCode::KEY_F2 => Key::F2,
125            KeyCode::KEY_F3 => Key::F3,
126            KeyCode::KEY_F4 => Key::F4,
127            KeyCode::KEY_F5 => Key::F5,
128            KeyCode::KEY_F6 => Key::F6,
129            KeyCode::KEY_F7 => Key::F7,
130            KeyCode::KEY_F8 => Key::F8,
131            KeyCode::KEY_F9 => Key::F9,
132            KeyCode::KEY_F10 => Key::F10,
133            KeyCode::KEY_F11 => Key::F11,
134            KeyCode::KEY_F12 => Key::F12,
135            KeyCode::KEY_F13 => Key::F13,
136            KeyCode::KEY_F14 => Key::F14,
137            KeyCode::KEY_F15 => Key::F15,
138            KeyCode::KEY_F16 => Key::F16,
139            KeyCode::KEY_F17 => Key::F17,
140            KeyCode::KEY_F18 => Key::F18,
141            KeyCode::KEY_F19 => Key::F19,
142            KeyCode::KEY_F20 => Key::F20,
143            KeyCode::KEY_F21 => Key::F21,
144            KeyCode::KEY_F22 => Key::F22,
145            KeyCode::KEY_F23 => Key::F23,
146            KeyCode::KEY_F24 => Key::F24,
147            KeyCode::KEY_ENTER => Key::ENTER,
148            KeyCode::KEY_ESC => Key::ESCAPE,
149            KeyCode::KEY_SPACE => Key::SPACE,
150            KeyCode::KEY_TAB => Key::TAB,
151            KeyCode::KEY_DELETE => Key::DELETE,
152            KeyCode::KEY_BACKSPACE => Key::BACKSPACE,
153            KeyCode::KEY_INSERT => Key::INSERT,
154            KeyCode::KEY_CAPSLOCK => Key::CAPS_LOCK,
155            KeyCode::KEY_HOME => Key::HOME,
156            KeyCode::KEY_END => Key::END,
157            KeyCode::KEY_PAGEUP => Key::PAGE_UP,
158            KeyCode::KEY_PAGEDOWN => Key::PAGE_DOWN,
159            KeyCode::KEY_UP => Key::ARROW_UP,
160            KeyCode::KEY_DOWN => Key::ARROW_DOWN,
161            KeyCode::KEY_LEFT => Key::ARROW_LEFT,
162            KeyCode::KEY_RIGHT => Key::ARROW_RIGHT,
163            KeyCode::KEY_MINUS => Key::MINUS,
164            KeyCode::KEY_EQUAL => Key::EQUAL,
165            KeyCode::KEY_LEFTBRACE => Key::BRACKET_LEFT,
166            KeyCode::KEY_RIGHTBRACE => Key::BRACKET_RIGHT,
167            KeyCode::KEY_BACKSLASH => Key::BACKSLASH,
168            KeyCode::KEY_SEMICOLON => Key::SEMICOLON,
169            KeyCode::KEY_APOSTROPHE => Key::QUOTE,
170            KeyCode::KEY_GRAVE => Key::BACKQUOTE,
171            KeyCode::KEY_COMMA => Key::COMMA,
172            KeyCode::KEY_DOT => Key::PERIOD,
173            KeyCode::KEY_SLASH => Key::SLASH,
174            KeyCode::KEY_KP0 => Key::NUMPAD0,
175            KeyCode::KEY_KP1 => Key::NUMPAD1,
176            KeyCode::KEY_KP2 => Key::NUMPAD2,
177            KeyCode::KEY_KP3 => Key::NUMPAD3,
178            KeyCode::KEY_KP4 => Key::NUMPAD4,
179            KeyCode::KEY_KP5 => Key::NUMPAD5,
180            KeyCode::KEY_KP6 => Key::NUMPAD6,
181            KeyCode::KEY_KP7 => Key::NUMPAD7,
182            KeyCode::KEY_KP8 => Key::NUMPAD8,
183            KeyCode::KEY_KP9 => Key::NUMPAD9,
184            KeyCode::KEY_KPDOT => Key::NUMPAD_DECIMAL,
185            KeyCode::KEY_KPPLUS => Key::NUMPAD_ADD,
186            KeyCode::KEY_KPMINUS => Key::NUMPAD_SUBTRACT,
187            KeyCode::KEY_KPASTERISK => Key::NUMPAD_MULTIPLY,
188            KeyCode::KEY_KPSLASH => Key::NUMPAD_DIVIDE,
189            KeyCode::KEY_KPENTER => Key::NUMPAD_ENTER,
190            KeyCode::KEY_LEFTCTRL => Key::CONTROL_LEFT,
191            KeyCode::KEY_RIGHTCTRL => Key::CONTROL_RIGHT,
192            KeyCode::KEY_LEFTSHIFT => Key::SHIFT_LEFT,
193            KeyCode::KEY_RIGHTSHIFT => Key::SHIFT_RIGHT,
194            KeyCode::KEY_LEFTALT => Key::ALT_LEFT,
195            KeyCode::KEY_RIGHTALT => Key::ALT_RIGHT,
196            KeyCode::KEY_LEFTMETA => Key::META_LEFT,
197            KeyCode::KEY_RIGHTMETA => Key::META_RIGHT,
198            KeyCode::KEY_VOLUMEUP => Key::AUDIO_VOLUME_UP,
199            KeyCode::KEY_VOLUMEDOWN => Key::AUDIO_VOLUME_DOWN,
200            KeyCode::KEY_MUTE => Key::AUDIO_VOLUME_MUTE,
201            KeyCode::KEY_PLAYPAUSE => Key::MEDIA_PLAY_PAUSE,
202            KeyCode::KEY_STOPCD => Key::MEDIA_STOP,
203            KeyCode::KEY_NEXTSONG => Key::MEDIA_TRACK_NEXT,
204            KeyCode::KEY_PREVIOUSSONG => Key::MEDIA_TRACK_PREVIOUS,
205            KeyCode::KEY_SYSRQ => Key::PRINT_SCREEN,
206            KeyCode::KEY_SCROLLLOCK => Key::SCROLL_LOCK,
207            KeyCode::KEY_PAUSE => Key::PAUSE,
208            KeyCode::KEY_NUMLOCK => Key::NUM_LOCK,
209            KeyCode::KEY_COMPOSE => Key::CONTEXT_MENU,
210            KeyCode::KEY_POWER => Key::POWER,
211            KeyCode::KEY_SLEEP => Key::SLEEP,
212            KeyCode::KEY_WAKEUP => Key::WAKE_UP,
213            KeyCode::KEY_EJECTCD => Key::EJECT,
214            KeyCode::KEY_BRIGHTNESSDOWN => Key::BRIGHTNESS_DOWN,
215            KeyCode::KEY_BRIGHTNESSUP => Key::BRIGHTNESS_UP,
216            // Browser keys
217            KeyCode::KEY_BACK => Key::BROWSER_BACK,
218            KeyCode::KEY_FORWARD => Key::BROWSER_FORWARD,
219            KeyCode::KEY_HOMEPAGE => Key::BROWSER_HOME,
220            KeyCode::KEY_REFRESH => Key::BROWSER_REFRESH,
221            KeyCode::KEY_SEARCH => Key::BROWSER_SEARCH,
222            KeyCode::KEY_STOP => Key::BROWSER_STOP,
223            KeyCode::KEY_BOOKMARKS => Key::BROWSER_FAVORITES,
224            // Extended media
225            KeyCode::KEY_PLAY => Key::MEDIA_PLAY,
226            KeyCode::KEY_PAUSECD => Key::MEDIA_PAUSE,
227            KeyCode::KEY_FASTFORWARD => Key::MEDIA_FAST_FORWARD,
228            KeyCode::KEY_REWIND => Key::MEDIA_REWIND,
229            KeyCode::KEY_RECORD => Key::MEDIA_RECORD,
230            KeyCode::KEY_MEDIA => Key::MEDIA_SELECT,
231            KeyCode::KEY_MICMUTE => Key::MICROPHONE_MUTE_TOGGLE,
232            // Clipboard / editing
233            KeyCode::KEY_COPY => Key::COPY,
234            KeyCode::KEY_CUT => Key::CUT,
235            KeyCode::KEY_PASTE => Key::PASTE,
236            KeyCode::KEY_UNDO => Key::UNDO,
237            KeyCode::KEY_FIND => Key::FIND,
238            KeyCode::KEY_HELP => Key::HELP,
239            KeyCode::KEY_OPEN => Key::OPEN,
240            KeyCode::KEY_SELECT => Key::SELECT,
241            KeyCode::KEY_AGAIN => Key::AGAIN,
242            KeyCode::KEY_PROPS => Key::PROPS,
243            KeyCode::KEY_CANCEL => Key::ABORT,
244            KeyCode::KEY_SUSPEND => Key::SUSPEND,
245            KeyCode::KEY_FRONT => Key::RESUME,
246            // CJK / international
247            KeyCode::KEY_HENKAN => Key::CONVERT,
248            KeyCode::KEY_MUHENKAN => Key::NON_CONVERT,
249            KeyCode::KEY_KATAKANAHIRAGANA => Key::KANA_MODE,
250            KeyCode::KEY_HIRAGANA => Key::HIRAGANA,
251            KeyCode::KEY_KATAKANA => Key::KATAKANA,
252            KeyCode::KEY_HANGEUL => Key::LANG1,
253            KeyCode::KEY_HANJA => Key::LANG2,
254            KeyCode::KEY_102ND => Key::INTL_BACKSLASH,
255            KeyCode::KEY_RO => Key::INTL_RO,
256            KeyCode::KEY_YEN => Key::INTL_YEN,
257            // App launch
258            KeyCode::KEY_PROG1 => Key::LAUNCH_APP1,
259            KeyCode::KEY_CALC => Key::LAUNCH_APP2,
260            KeyCode::KEY_MAIL => Key::LAUNCH_MAIL,
261            // Fn
262            KeyCode::KEY_FN => Key::FN,
263            // Extended numpad
264            KeyCode::KEY_KPEQUAL => Key::NUMPAD_EQUAL,
265            KeyCode::KEY_KPCOMMA => Key::NUMPAD_COMMA,
266            KeyCode::KEY_KPLEFTPAREN => Key::NUMPAD_PAREN_LEFT,
267            KeyCode::KEY_KPRIGHTPAREN => Key::NUMPAD_PAREN_RIGHT,
268            _ => Key::UNIDENTIFIED,
269        }
270    }
271}
272
273impl KbdKeyExt for Key {
274    #[allow(clippy::too_many_lines)]
275    fn to_key_code(self) -> KeyCode {
276        match self {
277            Key::A => KeyCode::KEY_A,
278            Key::B => KeyCode::KEY_B,
279            Key::C => KeyCode::KEY_C,
280            Key::D => KeyCode::KEY_D,
281            Key::E => KeyCode::KEY_E,
282            Key::F => KeyCode::KEY_F,
283            Key::G => KeyCode::KEY_G,
284            Key::H => KeyCode::KEY_H,
285            Key::I => KeyCode::KEY_I,
286            Key::J => KeyCode::KEY_J,
287            Key::K => KeyCode::KEY_K,
288            Key::L => KeyCode::KEY_L,
289            Key::M => KeyCode::KEY_M,
290            Key::N => KeyCode::KEY_N,
291            Key::O => KeyCode::KEY_O,
292            Key::P => KeyCode::KEY_P,
293            Key::Q => KeyCode::KEY_Q,
294            Key::R => KeyCode::KEY_R,
295            Key::S => KeyCode::KEY_S,
296            Key::T => KeyCode::KEY_T,
297            Key::U => KeyCode::KEY_U,
298            Key::V => KeyCode::KEY_V,
299            Key::W => KeyCode::KEY_W,
300            Key::X => KeyCode::KEY_X,
301            Key::Y => KeyCode::KEY_Y,
302            Key::Z => KeyCode::KEY_Z,
303            Key::DIGIT0 => KeyCode::KEY_0,
304            Key::DIGIT1 => KeyCode::KEY_1,
305            Key::DIGIT2 => KeyCode::KEY_2,
306            Key::DIGIT3 => KeyCode::KEY_3,
307            Key::DIGIT4 => KeyCode::KEY_4,
308            Key::DIGIT5 => KeyCode::KEY_5,
309            Key::DIGIT6 => KeyCode::KEY_6,
310            Key::DIGIT7 => KeyCode::KEY_7,
311            Key::DIGIT8 => KeyCode::KEY_8,
312            Key::DIGIT9 => KeyCode::KEY_9,
313            Key::F1 => KeyCode::KEY_F1,
314            Key::F2 => KeyCode::KEY_F2,
315            Key::F3 => KeyCode::KEY_F3,
316            Key::F4 => KeyCode::KEY_F4,
317            Key::F5 => KeyCode::KEY_F5,
318            Key::F6 => KeyCode::KEY_F6,
319            Key::F7 => KeyCode::KEY_F7,
320            Key::F8 => KeyCode::KEY_F8,
321            Key::F9 => KeyCode::KEY_F9,
322            Key::F10 => KeyCode::KEY_F10,
323            Key::F11 => KeyCode::KEY_F11,
324            Key::F12 => KeyCode::KEY_F12,
325            Key::F13 => KeyCode::KEY_F13,
326            Key::F14 => KeyCode::KEY_F14,
327            Key::F15 => KeyCode::KEY_F15,
328            Key::F16 => KeyCode::KEY_F16,
329            Key::F17 => KeyCode::KEY_F17,
330            Key::F18 => KeyCode::KEY_F18,
331            Key::F19 => KeyCode::KEY_F19,
332            Key::F20 => KeyCode::KEY_F20,
333            Key::F21 => KeyCode::KEY_F21,
334            Key::F22 => KeyCode::KEY_F22,
335            Key::F23 => KeyCode::KEY_F23,
336            Key::F24 => KeyCode::KEY_F24,
337            Key::ENTER => KeyCode::KEY_ENTER,
338            Key::ESCAPE => KeyCode::KEY_ESC,
339            Key::SPACE => KeyCode::KEY_SPACE,
340            Key::TAB => KeyCode::KEY_TAB,
341            Key::DELETE => KeyCode::KEY_DELETE,
342            Key::BACKSPACE => KeyCode::KEY_BACKSPACE,
343            Key::INSERT => KeyCode::KEY_INSERT,
344            Key::CAPS_LOCK => KeyCode::KEY_CAPSLOCK,
345            Key::HOME => KeyCode::KEY_HOME,
346            Key::END => KeyCode::KEY_END,
347            Key::PAGE_UP => KeyCode::KEY_PAGEUP,
348            Key::PAGE_DOWN => KeyCode::KEY_PAGEDOWN,
349            Key::ARROW_UP => KeyCode::KEY_UP,
350            Key::ARROW_DOWN => KeyCode::KEY_DOWN,
351            Key::ARROW_LEFT => KeyCode::KEY_LEFT,
352            Key::ARROW_RIGHT => KeyCode::KEY_RIGHT,
353            Key::MINUS => KeyCode::KEY_MINUS,
354            Key::EQUAL => KeyCode::KEY_EQUAL,
355            Key::BRACKET_LEFT => KeyCode::KEY_LEFTBRACE,
356            Key::BRACKET_RIGHT => KeyCode::KEY_RIGHTBRACE,
357            Key::BACKSLASH => KeyCode::KEY_BACKSLASH,
358            Key::SEMICOLON => KeyCode::KEY_SEMICOLON,
359            Key::QUOTE => KeyCode::KEY_APOSTROPHE,
360            Key::BACKQUOTE => KeyCode::KEY_GRAVE,
361            Key::COMMA => KeyCode::KEY_COMMA,
362            Key::PERIOD => KeyCode::KEY_DOT,
363            Key::SLASH => KeyCode::KEY_SLASH,
364            Key::NUMPAD0 => KeyCode::KEY_KP0,
365            Key::NUMPAD1 => KeyCode::KEY_KP1,
366            Key::NUMPAD2 => KeyCode::KEY_KP2,
367            Key::NUMPAD3 => KeyCode::KEY_KP3,
368            Key::NUMPAD4 => KeyCode::KEY_KP4,
369            Key::NUMPAD5 => KeyCode::KEY_KP5,
370            Key::NUMPAD6 => KeyCode::KEY_KP6,
371            Key::NUMPAD7 => KeyCode::KEY_KP7,
372            Key::NUMPAD8 => KeyCode::KEY_KP8,
373            Key::NUMPAD9 => KeyCode::KEY_KP9,
374            Key::NUMPAD_DECIMAL => KeyCode::KEY_KPDOT,
375            Key::NUMPAD_ADD => KeyCode::KEY_KPPLUS,
376            Key::NUMPAD_SUBTRACT => KeyCode::KEY_KPMINUS,
377            Key::NUMPAD_MULTIPLY => KeyCode::KEY_KPASTERISK,
378            Key::NUMPAD_DIVIDE => KeyCode::KEY_KPSLASH,
379            Key::NUMPAD_ENTER => KeyCode::KEY_KPENTER,
380            Key::CONTROL_LEFT => KeyCode::KEY_LEFTCTRL,
381            Key::CONTROL_RIGHT => KeyCode::KEY_RIGHTCTRL,
382            Key::SHIFT_LEFT => KeyCode::KEY_LEFTSHIFT,
383            Key::SHIFT_RIGHT => KeyCode::KEY_RIGHTSHIFT,
384            Key::ALT_LEFT => KeyCode::KEY_LEFTALT,
385            Key::ALT_RIGHT => KeyCode::KEY_RIGHTALT,
386            Key::META_LEFT => KeyCode::KEY_LEFTMETA,
387            Key::META_RIGHT => KeyCode::KEY_RIGHTMETA,
388            Key::AUDIO_VOLUME_UP => KeyCode::KEY_VOLUMEUP,
389            Key::AUDIO_VOLUME_DOWN => KeyCode::KEY_VOLUMEDOWN,
390            Key::AUDIO_VOLUME_MUTE => KeyCode::KEY_MUTE,
391            Key::MEDIA_PLAY_PAUSE => KeyCode::KEY_PLAYPAUSE,
392            Key::MEDIA_STOP => KeyCode::KEY_STOPCD,
393            Key::MEDIA_TRACK_NEXT => KeyCode::KEY_NEXTSONG,
394            Key::MEDIA_TRACK_PREVIOUS => KeyCode::KEY_PREVIOUSSONG,
395            Key::PRINT_SCREEN => KeyCode::KEY_SYSRQ,
396            Key::SCROLL_LOCK => KeyCode::KEY_SCROLLLOCK,
397            Key::PAUSE => KeyCode::KEY_PAUSE,
398            Key::NUM_LOCK => KeyCode::KEY_NUMLOCK,
399            Key::CONTEXT_MENU => KeyCode::KEY_COMPOSE,
400            Key::POWER => KeyCode::KEY_POWER,
401            Key::SLEEP => KeyCode::KEY_SLEEP,
402            Key::WAKE_UP => KeyCode::KEY_WAKEUP,
403            Key::EJECT => KeyCode::KEY_EJECTCD,
404            Key::BRIGHTNESS_DOWN => KeyCode::KEY_BRIGHTNESSDOWN,
405            Key::BRIGHTNESS_UP => KeyCode::KEY_BRIGHTNESSUP,
406            Key::BROWSER_BACK => KeyCode::KEY_BACK,
407            Key::BROWSER_FORWARD => KeyCode::KEY_FORWARD,
408            Key::BROWSER_HOME => KeyCode::KEY_HOMEPAGE,
409            Key::BROWSER_REFRESH => KeyCode::KEY_REFRESH,
410            Key::BROWSER_SEARCH => KeyCode::KEY_SEARCH,
411            Key::BROWSER_STOP => KeyCode::KEY_STOP,
412            Key::BROWSER_FAVORITES => KeyCode::KEY_BOOKMARKS,
413            Key::MEDIA_PLAY => KeyCode::KEY_PLAY,
414            Key::MEDIA_PAUSE => KeyCode::KEY_PAUSECD,
415            Key::MEDIA_FAST_FORWARD => KeyCode::KEY_FASTFORWARD,
416            Key::MEDIA_REWIND => KeyCode::KEY_REWIND,
417            Key::MEDIA_RECORD => KeyCode::KEY_RECORD,
418            Key::MEDIA_SELECT => KeyCode::KEY_MEDIA,
419            Key::MICROPHONE_MUTE_TOGGLE => KeyCode::KEY_MICMUTE,
420            Key::COPY => KeyCode::KEY_COPY,
421            Key::CUT => KeyCode::KEY_CUT,
422            Key::PASTE => KeyCode::KEY_PASTE,
423            Key::UNDO => KeyCode::KEY_UNDO,
424            Key::FIND => KeyCode::KEY_FIND,
425            Key::HELP => KeyCode::KEY_HELP,
426            Key::OPEN => KeyCode::KEY_OPEN,
427            Key::SELECT => KeyCode::KEY_SELECT,
428            Key::AGAIN => KeyCode::KEY_AGAIN,
429            Key::PROPS => KeyCode::KEY_PROPS,
430            Key::ABORT => KeyCode::KEY_CANCEL,
431            Key::SUSPEND => KeyCode::KEY_SUSPEND,
432            Key::RESUME => KeyCode::KEY_FRONT,
433            Key::CONVERT => KeyCode::KEY_HENKAN,
434            Key::NON_CONVERT => KeyCode::KEY_MUHENKAN,
435            Key::KANA_MODE => KeyCode::KEY_KATAKANAHIRAGANA,
436            Key::HIRAGANA => KeyCode::KEY_HIRAGANA,
437            Key::KATAKANA => KeyCode::KEY_KATAKANA,
438            Key::LANG1 => KeyCode::KEY_HANGEUL,
439            Key::LANG2 => KeyCode::KEY_HANJA,
440            Key::INTL_BACKSLASH => KeyCode::KEY_102ND,
441            Key::INTL_RO => KeyCode::KEY_RO,
442            Key::INTL_YEN => KeyCode::KEY_YEN,
443            Key::LAUNCH_APP1 => KeyCode::KEY_PROG1,
444            Key::LAUNCH_APP2 => KeyCode::KEY_CALC,
445            Key::LAUNCH_MAIL => KeyCode::KEY_MAIL,
446            Key::FN => KeyCode::KEY_FN,
447            Key::NUMPAD_EQUAL => KeyCode::KEY_KPEQUAL,
448            Key::NUMPAD_COMMA => KeyCode::KEY_KPCOMMA,
449            Key::NUMPAD_PAREN_LEFT => KeyCode::KEY_KPLEFTPAREN,
450            Key::NUMPAD_PAREN_RIGHT => KeyCode::KEY_KPRIGHTPAREN,
451            // Key wraps keyboard_types::Code which is #[non_exhaustive].
452            // Codes without a known evdev mapping (including Key::UNIDENTIFIED)
453            // fall back to KEY_UNKNOWN.
454            _ => KeyCode::KEY_UNKNOWN,
455        }
456    }
457}
458
459#[cfg(test)]
460mod tests {
461    use evdev::KeyCode;
462    use kbd::key::Key;
463
464    use super::EvdevKeyCodeExt;
465    use super::KbdKeyExt;
466
467    #[test]
468    fn keycode_to_key_round_trip() {
469        for key in [
470            Key::A,
471            Key::Z,
472            Key::F24,
473            Key::ENTER,
474            Key::CAPS_LOCK,
475            Key::NUMPAD_ENTER,
476            Key::CONTROL_LEFT,
477            Key::META_RIGHT,
478        ] {
479            let code = key.to_key_code();
480            let parsed = code.to_key();
481            assert_eq!(parsed, key, "round-trip failed for {key:?}");
482        }
483    }
484
485    #[test]
486    fn unmapped_keycode_maps_to_unknown() {
487        // Use a key code that has no Key:: constant or mapping
488        let key = KeyCode::KEY_PROG2.to_key();
489        assert_eq!(key, Key::UNIDENTIFIED);
490    }
491
492    #[test]
493    fn media_keys_round_trip() {
494        for key in [
495            Key::AUDIO_VOLUME_UP,
496            Key::AUDIO_VOLUME_DOWN,
497            Key::AUDIO_VOLUME_MUTE,
498            Key::MEDIA_PLAY_PAUSE,
499            Key::MEDIA_STOP,
500            Key::MEDIA_TRACK_NEXT,
501            Key::MEDIA_TRACK_PREVIOUS,
502        ] {
503            let code = key.to_key_code();
504            let parsed = code.to_key();
505            assert_eq!(parsed, key, "round-trip failed for {key:?}");
506        }
507    }
508
509    #[test]
510    fn system_keys_round_trip() {
511        for key in [
512            Key::PRINT_SCREEN,
513            Key::SCROLL_LOCK,
514            Key::PAUSE,
515            Key::NUM_LOCK,
516            Key::CONTEXT_MENU,
517            Key::POWER,
518        ] {
519            let code = key.to_key_code();
520            let parsed = code.to_key();
521            assert_eq!(parsed, key, "round-trip failed for {key:?}");
522        }
523    }
524
525    #[test]
526    fn extended_keys_round_trip() {
527        let extended_keys = [
528            Key::SLEEP,
529            Key::WAKE_UP,
530            Key::EJECT,
531            Key::BRIGHTNESS_DOWN,
532            Key::BRIGHTNESS_UP,
533            Key::BROWSER_BACK,
534            Key::BROWSER_FORWARD,
535            Key::BROWSER_HOME,
536            Key::BROWSER_REFRESH,
537            Key::BROWSER_SEARCH,
538            Key::BROWSER_STOP,
539            Key::BROWSER_FAVORITES,
540            Key::MEDIA_PLAY,
541            Key::MEDIA_PAUSE,
542            Key::MEDIA_FAST_FORWARD,
543            Key::MEDIA_REWIND,
544            Key::MEDIA_RECORD,
545            Key::MEDIA_SELECT,
546            Key::MICROPHONE_MUTE_TOGGLE,
547            Key::COPY,
548            Key::CUT,
549            Key::PASTE,
550            Key::UNDO,
551            Key::FIND,
552            Key::HELP,
553            Key::OPEN,
554            Key::SELECT,
555            Key::AGAIN,
556            Key::PROPS,
557            Key::ABORT,
558            Key::SUSPEND,
559            Key::RESUME,
560            Key::CONVERT,
561            Key::NON_CONVERT,
562            Key::KANA_MODE,
563            Key::HIRAGANA,
564            Key::KATAKANA,
565            Key::LANG1,
566            Key::LANG2,
567            Key::INTL_BACKSLASH,
568            Key::INTL_RO,
569            Key::INTL_YEN,
570            Key::LAUNCH_APP1,
571            Key::LAUNCH_APP2,
572            Key::LAUNCH_MAIL,
573            Key::FN,
574            Key::NUMPAD_EQUAL,
575            Key::NUMPAD_COMMA,
576            Key::NUMPAD_PAREN_LEFT,
577            Key::NUMPAD_PAREN_RIGHT,
578        ];
579        for key in extended_keys {
580            let code = key.to_key_code();
581            let parsed = code.to_key();
582            assert_eq!(parsed, key, "round-trip failed for {key:?}");
583        }
584    }
585
586    #[test]
587    fn unknown_key_maps_to_key_unknown() {
588        let code = Key::UNIDENTIFIED.to_key_code();
589        assert_eq!(code, KeyCode::KEY_UNKNOWN);
590    }
591
592    #[test]
593    fn all_letters_round_trip() {
594        let letters = [
595            Key::A,
596            Key::B,
597            Key::C,
598            Key::D,
599            Key::E,
600            Key::F,
601            Key::G,
602            Key::H,
603            Key::I,
604            Key::J,
605            Key::K,
606            Key::L,
607            Key::M,
608            Key::N,
609            Key::O,
610            Key::P,
611            Key::Q,
612            Key::R,
613            Key::S,
614            Key::T,
615            Key::U,
616            Key::V,
617            Key::W,
618            Key::X,
619            Key::Y,
620            Key::Z,
621        ];
622        for key in letters {
623            let code = key.to_key_code();
624            let parsed = code.to_key();
625            assert_eq!(parsed, key, "round-trip failed for {key:?}");
626        }
627    }
628
629    #[test]
630    fn all_modifiers_round_trip() {
631        let modifiers = [
632            Key::CONTROL_LEFT,
633            Key::CONTROL_RIGHT,
634            Key::SHIFT_LEFT,
635            Key::SHIFT_RIGHT,
636            Key::ALT_LEFT,
637            Key::ALT_RIGHT,
638            Key::META_LEFT,
639            Key::META_RIGHT,
640        ];
641        for key in modifiers {
642            let code = key.to_key_code();
643            let parsed = code.to_key();
644            assert_eq!(parsed, key, "round-trip failed for {key:?}");
645        }
646    }
647
648    #[test]
649    fn all_digits_round_trip() {
650        for key in [
651            Key::DIGIT0,
652            Key::DIGIT1,
653            Key::DIGIT2,
654            Key::DIGIT3,
655            Key::DIGIT4,
656            Key::DIGIT5,
657            Key::DIGIT6,
658            Key::DIGIT7,
659            Key::DIGIT8,
660            Key::DIGIT9,
661        ] {
662            let code = key.to_key_code();
663            let parsed = code.to_key();
664            assert_eq!(parsed, key, "round-trip failed for {key:?}");
665        }
666    }
667
668    #[test]
669    fn all_function_keys_round_trip() {
670        for key in [
671            Key::F1,
672            Key::F2,
673            Key::F3,
674            Key::F4,
675            Key::F5,
676            Key::F6,
677            Key::F7,
678            Key::F8,
679            Key::F9,
680            Key::F10,
681            Key::F11,
682            Key::F12,
683            Key::F13,
684            Key::F14,
685            Key::F15,
686            Key::F16,
687            Key::F17,
688            Key::F18,
689            Key::F19,
690            Key::F20,
691            Key::F21,
692            Key::F22,
693            Key::F23,
694            Key::F24,
695        ] {
696            let code = key.to_key_code();
697            let parsed = code.to_key();
698            assert_eq!(parsed, key, "round-trip failed for {key:?}");
699        }
700    }
701
702    #[test]
703    fn all_numpad_keys_round_trip() {
704        for key in [
705            Key::NUMPAD0,
706            Key::NUMPAD1,
707            Key::NUMPAD2,
708            Key::NUMPAD3,
709            Key::NUMPAD4,
710            Key::NUMPAD5,
711            Key::NUMPAD6,
712            Key::NUMPAD7,
713            Key::NUMPAD8,
714            Key::NUMPAD9,
715            Key::NUMPAD_DECIMAL,
716            Key::NUMPAD_ADD,
717            Key::NUMPAD_SUBTRACT,
718            Key::NUMPAD_MULTIPLY,
719            Key::NUMPAD_DIVIDE,
720            Key::NUMPAD_ENTER,
721            Key::NUMPAD_EQUAL,
722            Key::NUMPAD_COMMA,
723            Key::NUMPAD_PAREN_LEFT,
724            Key::NUMPAD_PAREN_RIGHT,
725        ] {
726            let code = key.to_key_code();
727            let parsed = code.to_key();
728            assert_eq!(parsed, key, "round-trip failed for {key:?}");
729        }
730    }
731
732    #[test]
733    fn punctuation_keys_round_trip() {
734        for key in [
735            Key::MINUS,
736            Key::EQUAL,
737            Key::BRACKET_LEFT,
738            Key::BRACKET_RIGHT,
739            Key::BACKSLASH,
740            Key::SEMICOLON,
741            Key::QUOTE,
742            Key::BACKQUOTE,
743            Key::COMMA,
744            Key::PERIOD,
745            Key::SLASH,
746        ] {
747            let code = key.to_key_code();
748            let parsed = code.to_key();
749            assert_eq!(parsed, key, "round-trip failed for {key:?}");
750        }
751    }
752
753    #[test]
754    fn navigation_keys_round_trip() {
755        for key in [
756            Key::ARROW_UP,
757            Key::ARROW_DOWN,
758            Key::ARROW_LEFT,
759            Key::ARROW_RIGHT,
760            Key::HOME,
761            Key::END,
762            Key::PAGE_UP,
763            Key::PAGE_DOWN,
764            Key::INSERT,
765            Key::DELETE,
766        ] {
767            let code = key.to_key_code();
768            let parsed = code.to_key();
769            assert_eq!(parsed, key, "round-trip failed for {key:?}");
770        }
771    }
772
773    #[test]
774    fn multiple_unmapped_keycodes_return_unidentified() {
775        for code in [
776            KeyCode::KEY_PROG2,
777            KeyCode::KEY_COFFEE,
778            KeyCode::KEY_DASHBOARD,
779        ] {
780            assert_eq!(
781                code.to_key(),
782                Key::UNIDENTIFIED,
783                "expected UNIDENTIFIED for {code:?}"
784            );
785        }
786    }
787
788    #[test]
789    fn cjk_keys_round_trip() {
790        for key in [
791            Key::CONVERT,
792            Key::NON_CONVERT,
793            Key::KANA_MODE,
794            Key::HIRAGANA,
795            Key::KATAKANA,
796            Key::LANG1,
797            Key::LANG2,
798            Key::INTL_BACKSLASH,
799            Key::INTL_RO,
800            Key::INTL_YEN,
801        ] {
802            let code = key.to_key_code();
803            let parsed = code.to_key();
804            assert_eq!(parsed, key, "round-trip failed for {key:?}");
805        }
806    }
807}