Skip to main content

handy_keys/types/
key.rs

1//! Keyboard key and mouse button definitions and parsing
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::str::FromStr;
6
7use crate::error::{Error, Result};
8
9/// Keyboard keys and mouse buttons that can be used in hotkey combinations
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[non_exhaustive]
12pub enum Key {
13    // Letters
14    A,
15    B,
16    C,
17    D,
18    E,
19    F,
20    G,
21    H,
22    I,
23    J,
24    K,
25    L,
26    M,
27    N,
28    O,
29    P,
30    Q,
31    R,
32    S,
33    T,
34    U,
35    V,
36    W,
37    X,
38    Y,
39    Z,
40
41    // Numbers
42    Num0,
43    Num1,
44    Num2,
45    Num3,
46    Num4,
47    Num5,
48    Num6,
49    Num7,
50    Num8,
51    Num9,
52
53    // Media keys
54    PlayPause,
55    Stop,
56    PrevTrack,
57    NextTrack,
58
59    // Function keys
60    F1,
61    F2,
62    F3,
63    F4,
64    F5,
65    F6,
66    F7,
67    F8,
68    F9,
69    F10,
70    F11,
71    F12,
72    F13,
73    F14,
74    F15,
75    F16,
76    F17,
77    F18,
78    F19,
79    F20,
80    /// F21–F24 exist on Windows (VK 0x84–0x87) and in evdev; macOS virtual
81    /// keycodes stop at F20, so these are unreachable from macOS hardware.
82    F21,
83    F22,
84    F23,
85    F24,
86
87    // Special keys
88    Space,
89    Return,
90    Tab,
91    Escape,
92    Delete,
93    ForwardDelete,
94    Insert,
95    /// The Pause/Break key.
96    ///
97    /// Windows delivers this key's release together with its press: the
98    /// hardware break sequence is part of the make sequence (`E1 1D 45 E1
99    /// 9D C5`, nothing on release — PS/2 behavior the HID driver preserves
100    /// for USB keyboards), so the hook sees key-down and key-up back to
101    /// back at press time. Treat it as tap/toggle only on Windows; a hold
102    /// binding would release immediately. Ctrl+Pause reports `VK_CANCEL`
103    /// rather than `VK_PAUSE`; both map here. Linux evdev reports normal
104    /// press/release for USB keyboards, so holds work there. macOS has no
105    /// virtual keycode for Pause — PC keyboards' Pause key arrives as F15.
106    Pause,
107    Home,
108    End,
109    PageUp,
110    PageDown,
111
112    // Arrow keys
113    LeftArrow,
114    RightArrow,
115    UpArrow,
116    DownArrow,
117
118    // Punctuation and symbols
119    Minus,
120    Equal,
121    LeftBracket,
122    RightBracket,
123    Backslash,
124    Semicolon,
125    Quote,
126    Comma,
127    Period,
128    Slash,
129    Grave,
130    Section,
131    // JIS keyboard keys
132    JisYen,
133    JisUnderscore,
134    JisEisu,
135    JisKana,
136
137    // Keypad
138    Keypad0,
139    Keypad1,
140    Keypad2,
141    Keypad3,
142    Keypad4,
143    Keypad5,
144    Keypad6,
145    Keypad7,
146    Keypad8,
147    Keypad9,
148    KeypadDecimal,
149    KeypadMultiply,
150    KeypadPlus,
151    KeypadClear,
152    KeypadDivide,
153    KeypadEnter,
154    KeypadMinus,
155    KeypadEquals,
156    KeypadComma,
157
158    // Lock keys
159    CapsLock,
160    ScrollLock,
161    NumLock,
162
163    /// The PrintScreen/SysRq key. Windows and Linux only: macOS has no
164    /// virtual keycode for it — PC keyboards' PrintScreen arrives as F13
165    /// there.
166    PrintScreen,
167
168    /// The context-menu (Application) key, right of AltGr on full-size PC
169    /// keyboards. `VK_APPS` on Windows, `KEY_COMPOSE` on Linux evdev;
170    /// macOS reports PC keyboards' Menu key as keycode `0x6E`.
171    ContextMenu,
172
173    /// The dictation / voice key (macOS virtual keycode `0xB0`, macOS only).
174    ///
175    /// Emitted by the microphone/dictation key on the media function row
176    /// (the F5 position on recent Apple keyboards), and by the Globe/Fn key
177    /// when System Settings configures it to start dictation. A Globe tap
178    /// configured for anything else emits `Fn` modifier flags plus a
179    /// different keycode instead.
180    ///
181    /// Hardware events carry `MaskSecondaryFn` in their flags, so hotkeys
182    /// recorded from a key press pair this with the `Fn` modifier (a
183    /// string-configured `"dictation"` hotkey without `fn` will not match
184    /// hardware presses until FN matching is reworked).
185    Dictation,
186
187    // Mouse buttons
188    MouseLeft,
189    MouseRight,
190    MouseMiddle,
191    /// Extra button 1 (often "back" on mice with side buttons)
192    MouseX1,
193    /// Extra button 2 (often "forward" on mice with side buttons)
194    MouseX2,
195}
196
197impl fmt::Display for Key {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        match self {
200            Key::A => write!(f, "A"),
201            Key::B => write!(f, "B"),
202            Key::C => write!(f, "C"),
203            Key::D => write!(f, "D"),
204            Key::E => write!(f, "E"),
205            Key::F => write!(f, "F"),
206            Key::G => write!(f, "G"),
207            Key::H => write!(f, "H"),
208            Key::I => write!(f, "I"),
209            Key::J => write!(f, "J"),
210            Key::K => write!(f, "K"),
211            Key::L => write!(f, "L"),
212            Key::M => write!(f, "M"),
213            Key::N => write!(f, "N"),
214            Key::O => write!(f, "O"),
215            Key::P => write!(f, "P"),
216            Key::Q => write!(f, "Q"),
217            Key::R => write!(f, "R"),
218            Key::S => write!(f, "S"),
219            Key::T => write!(f, "T"),
220            Key::U => write!(f, "U"),
221            Key::V => write!(f, "V"),
222            Key::W => write!(f, "W"),
223            Key::X => write!(f, "X"),
224            Key::Y => write!(f, "Y"),
225            Key::Z => write!(f, "Z"),
226            Key::Num0 => write!(f, "0"),
227            Key::Num1 => write!(f, "1"),
228            Key::Num2 => write!(f, "2"),
229            Key::Num3 => write!(f, "3"),
230            Key::Num4 => write!(f, "4"),
231            Key::Num5 => write!(f, "5"),
232            Key::Num6 => write!(f, "6"),
233            Key::Num7 => write!(f, "7"),
234            Key::Num8 => write!(f, "8"),
235            Key::Num9 => write!(f, "9"),
236            Key::PlayPause => write!(f, "PlayPause"),
237            Key::Stop => write!(f, "Stop"),
238            Key::PrevTrack => write!(f, "PrevTrack"),
239            Key::NextTrack => write!(f, "NextTrack"),
240            Key::F1 => write!(f, "F1"),
241            Key::F2 => write!(f, "F2"),
242            Key::F3 => write!(f, "F3"),
243            Key::F4 => write!(f, "F4"),
244            Key::F5 => write!(f, "F5"),
245            Key::F6 => write!(f, "F6"),
246            Key::F7 => write!(f, "F7"),
247            Key::F8 => write!(f, "F8"),
248            Key::F9 => write!(f, "F9"),
249            Key::F10 => write!(f, "F10"),
250            Key::F11 => write!(f, "F11"),
251            Key::F12 => write!(f, "F12"),
252            Key::F13 => write!(f, "F13"),
253            Key::F14 => write!(f, "F14"),
254            Key::F15 => write!(f, "F15"),
255            Key::F16 => write!(f, "F16"),
256            Key::F17 => write!(f, "F17"),
257            Key::F18 => write!(f, "F18"),
258            Key::F19 => write!(f, "F19"),
259            Key::F20 => write!(f, "F20"),
260            Key::F21 => write!(f, "F21"),
261            Key::F22 => write!(f, "F22"),
262            Key::F23 => write!(f, "F23"),
263            Key::F24 => write!(f, "F24"),
264            Key::Space => write!(f, "Space"),
265            Key::Return => write!(f, "Return"),
266            Key::Tab => write!(f, "Tab"),
267            Key::Escape => write!(f, "Escape"),
268            Key::Delete => write!(f, "Delete"),
269            Key::ForwardDelete => write!(f, "ForwardDelete"),
270            Key::Insert => write!(f, "Insert"),
271            Key::Pause => write!(f, "Pause"),
272            Key::Home => write!(f, "Home"),
273            Key::End => write!(f, "End"),
274            Key::PageUp => write!(f, "PageUp"),
275            Key::PageDown => write!(f, "PageDown"),
276            Key::LeftArrow => write!(f, "Left"),
277            Key::RightArrow => write!(f, "Right"),
278            Key::UpArrow => write!(f, "Up"),
279            Key::DownArrow => write!(f, "Down"),
280            Key::Minus => write!(f, "-"),
281            Key::Equal => write!(f, "="),
282            Key::LeftBracket => write!(f, "["),
283            Key::RightBracket => write!(f, "]"),
284            Key::Backslash => write!(f, "\\"),
285            Key::Semicolon => write!(f, ";"),
286            Key::Quote => write!(f, "'"),
287            Key::Comma => write!(f, ","),
288            Key::Period => write!(f, "."),
289            Key::Slash => write!(f, "/"),
290            Key::Grave => write!(f, "`"),
291            Key::Section => write!(f, "§"),
292            Key::JisYen => write!(f, "¥"),
293            Key::JisUnderscore => write!(f, "JisUnderscore"),
294            Key::JisEisu => write!(f, "Eisu"),
295            Key::JisKana => write!(f, "Kana"),
296            Key::Keypad0 => write!(f, "Keypad0"),
297            Key::Keypad1 => write!(f, "Keypad1"),
298            Key::Keypad2 => write!(f, "Keypad2"),
299            Key::Keypad3 => write!(f, "Keypad3"),
300            Key::Keypad4 => write!(f, "Keypad4"),
301            Key::Keypad5 => write!(f, "Keypad5"),
302            Key::Keypad6 => write!(f, "Keypad6"),
303            Key::Keypad7 => write!(f, "Keypad7"),
304            Key::Keypad8 => write!(f, "Keypad8"),
305            Key::Keypad9 => write!(f, "Keypad9"),
306            Key::KeypadDecimal => write!(f, "KeypadDecimal"),
307            Key::KeypadMultiply => write!(f, "KeypadMultiply"),
308            Key::KeypadPlus => write!(f, "KeypadPlus"),
309            Key::KeypadClear => write!(f, "KeypadClear"),
310            Key::KeypadDivide => write!(f, "KeypadDivide"),
311            Key::KeypadEnter => write!(f, "KeypadEnter"),
312            Key::KeypadMinus => write!(f, "KeypadMinus"),
313            Key::KeypadEquals => write!(f, "KeypadEquals"),
314            Key::KeypadComma => write!(f, "KeypadComma"),
315            Key::CapsLock => write!(f, "CapsLock"),
316            Key::ScrollLock => write!(f, "ScrollLock"),
317            Key::NumLock => write!(f, "NumLock"),
318            Key::PrintScreen => write!(f, "PrintScreen"),
319            Key::ContextMenu => write!(f, "ContextMenu"),
320            Key::MouseLeft => write!(f, "MouseLeft"),
321            Key::MouseRight => write!(f, "MouseRight"),
322            Key::MouseMiddle => write!(f, "MouseMiddle"),
323            Key::MouseX1 => write!(f, "MouseX1"),
324            Key::MouseX2 => write!(f, "MouseX2"),
325            Key::Dictation => write!(f, "Dictation"),
326        }
327    }
328}
329
330impl FromStr for Key {
331    type Err = Error;
332
333    /// Parse a key from its string representation (case-insensitive)
334    fn from_str(s: &str) -> Result<Self> {
335        let s = s.trim();
336        match s.to_lowercase().as_str() {
337            // Letters
338            "a" => Ok(Key::A),
339            "b" => Ok(Key::B),
340            "c" => Ok(Key::C),
341            "d" => Ok(Key::D),
342            "e" => Ok(Key::E),
343            "f" => Ok(Key::F),
344            "g" => Ok(Key::G),
345            "h" => Ok(Key::H),
346            "i" => Ok(Key::I),
347            "j" => Ok(Key::J),
348            "k" => Ok(Key::K),
349            "l" => Ok(Key::L),
350            "m" => Ok(Key::M),
351            "n" => Ok(Key::N),
352            "o" => Ok(Key::O),
353            "p" => Ok(Key::P),
354            "q" => Ok(Key::Q),
355            "r" => Ok(Key::R),
356            "s" => Ok(Key::S),
357            "t" => Ok(Key::T),
358            "u" => Ok(Key::U),
359            "v" => Ok(Key::V),
360            "w" => Ok(Key::W),
361            "x" => Ok(Key::X),
362            "y" => Ok(Key::Y),
363            "z" => Ok(Key::Z),
364
365            // Numbers
366            "0" | "num0" => Ok(Key::Num0),
367            "1" | "num1" => Ok(Key::Num1),
368            "2" | "num2" => Ok(Key::Num2),
369            "3" | "num3" => Ok(Key::Num3),
370            "4" | "num4" => Ok(Key::Num4),
371            "5" | "num5" => Ok(Key::Num5),
372            "6" | "num6" => Ok(Key::Num6),
373            "7" | "num7" => Ok(Key::Num7),
374            "8" | "num8" => Ok(Key::Num8),
375            "9" | "num9" => Ok(Key::Num9),
376
377            // Media keys
378            "playpause" => Ok(Key::PlayPause),
379            "stop" => Ok(Key::Stop),
380            "prevtrack" => Ok(Key::PrevTrack),
381            "nexttrack" => Ok(Key::NextTrack),
382
383            // Function keys
384            "f1" => Ok(Key::F1),
385            "f2" => Ok(Key::F2),
386            "f3" => Ok(Key::F3),
387            "f4" => Ok(Key::F4),
388            "f5" => Ok(Key::F5),
389            "f6" => Ok(Key::F6),
390            "f7" => Ok(Key::F7),
391            "f8" => Ok(Key::F8),
392            "f9" => Ok(Key::F9),
393            "f10" => Ok(Key::F10),
394            "f11" => Ok(Key::F11),
395            "f12" => Ok(Key::F12),
396            "f13" => Ok(Key::F13),
397            "f14" => Ok(Key::F14),
398            "f15" => Ok(Key::F15),
399            "f16" => Ok(Key::F16),
400            "f17" => Ok(Key::F17),
401            "f18" => Ok(Key::F18),
402            "f19" => Ok(Key::F19),
403            "f20" => Ok(Key::F20),
404            "f21" => Ok(Key::F21),
405            "f22" => Ok(Key::F22),
406            "f23" => Ok(Key::F23),
407            "f24" => Ok(Key::F24),
408
409            // Special keys
410            "space" | " " => Ok(Key::Space),
411            "return" | "enter" => Ok(Key::Return),
412            "tab" => Ok(Key::Tab),
413            "escape" | "esc" => Ok(Key::Escape),
414            "delete" | "backspace" => Ok(Key::Delete),
415            "forwarddelete" | "del" => Ok(Key::ForwardDelete),
416            "insert" | "ins" => Ok(Key::Insert),
417            "pause" | "break" => Ok(Key::Pause),
418            "home" => Ok(Key::Home),
419            "end" => Ok(Key::End),
420            "pageup" => Ok(Key::PageUp),
421            "pagedown" => Ok(Key::PageDown),
422
423            // Arrow keys
424            "left" | "leftarrow" => Ok(Key::LeftArrow),
425            "right" | "rightarrow" => Ok(Key::RightArrow),
426            "up" | "uparrow" => Ok(Key::UpArrow),
427            "down" | "downarrow" => Ok(Key::DownArrow),
428
429            // Punctuation and symbols
430            "-" | "minus" => Ok(Key::Minus),
431            "=" | "equal" | "equals" => Ok(Key::Equal),
432            "[" | "leftbracket" => Ok(Key::LeftBracket),
433            "]" | "rightbracket" => Ok(Key::RightBracket),
434            "\\" | "backslash" => Ok(Key::Backslash),
435            ";" | "semicolon" => Ok(Key::Semicolon),
436            "'" | "quote" => Ok(Key::Quote),
437            "," | "comma" => Ok(Key::Comma),
438            "." | "period" => Ok(Key::Period),
439            "/" | "slash" => Ok(Key::Slash),
440            "`" | "grave" | "backtick" => Ok(Key::Grave),
441            "§" | "section" => Ok(Key::Section),
442            "¥" | "jisyen" | "yen" => Ok(Key::JisYen),
443            "jisunderscore" => Ok(Key::JisUnderscore),
444            "eisu" | "jiseisu" | "英数" => Ok(Key::JisEisu),
445            "kana" | "jiskana" | "かな" => Ok(Key::JisKana),
446
447            // Keypad
448            "keypad0" => Ok(Key::Keypad0),
449            "keypad1" => Ok(Key::Keypad1),
450            "keypad2" => Ok(Key::Keypad2),
451            "keypad3" => Ok(Key::Keypad3),
452            "keypad4" => Ok(Key::Keypad4),
453            "keypad5" => Ok(Key::Keypad5),
454            "keypad6" => Ok(Key::Keypad6),
455            "keypad7" => Ok(Key::Keypad7),
456            "keypad8" => Ok(Key::Keypad8),
457            "keypad9" => Ok(Key::Keypad9),
458            "keypad." | "keypaddecimal" => Ok(Key::KeypadDecimal),
459            "keypad*" | "keypadmultiply" => Ok(Key::KeypadMultiply),
460            "keypad+" | "keypadplus" => Ok(Key::KeypadPlus),
461            "keypadclear" => Ok(Key::KeypadClear),
462            "keypad/" | "keypaddivide" => Ok(Key::KeypadDivide),
463            "keypadenter" => Ok(Key::KeypadEnter),
464            "keypad-" | "keypadminus" => Ok(Key::KeypadMinus),
465            "keypad=" | "keypadequals" => Ok(Key::KeypadEquals),
466            "keypad," | "keypadcomma" => Ok(Key::KeypadComma),
467
468            // Lock keys
469            "capslock" | "caps" => Ok(Key::CapsLock),
470            "scrolllock" | "scroll" => Ok(Key::ScrollLock),
471            "numlock" => Ok(Key::NumLock),
472
473            "printscreen" | "prtsc" | "sysrq" => Ok(Key::PrintScreen),
474            "contextmenu" | "menu" | "apps" | "application" => Ok(Key::ContextMenu),
475
476            // Mouse buttons
477            "mouseleft" | "leftclick" | "lmb" | "mouse1" => Ok(Key::MouseLeft),
478            "mouseright" | "rightclick" | "rmb" | "mouse2" => Ok(Key::MouseRight),
479            "mousemiddle" | "middleclick" | "mmb" | "mouse3" => Ok(Key::MouseMiddle),
480            "mousex1" | "mouse4" | "back" | "xbutton1" => Ok(Key::MouseX1),
481            "mousex2" | "mouse5" | "forward" | "xbutton2" => Ok(Key::MouseX2),
482
483            // Dictation / voice key (macOS keycode 0xB0). "globe" is accepted
484            // because the Globe key emits this keycode when configured to
485            // start dictation.
486            "dictation" | "voice" | "globe" => Ok(Key::Dictation),
487
488            _ => Err(Error::UnknownKey(s.to_string())),
489        }
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use super::*;
496
497    #[test]
498    fn parse_letters() {
499        assert_eq!("a".parse::<Key>().unwrap(), Key::A);
500        assert_eq!("A".parse::<Key>().unwrap(), Key::A);
501        assert_eq!("z".parse::<Key>().unwrap(), Key::Z);
502    }
503
504    #[test]
505    fn parse_numbers() {
506        assert_eq!("0".parse::<Key>().unwrap(), Key::Num0);
507        assert_eq!("9".parse::<Key>().unwrap(), Key::Num9);
508        assert_eq!("num5".parse::<Key>().unwrap(), Key::Num5);
509    }
510
511    #[test]
512    fn parse_function_keys() {
513        assert_eq!("F1".parse::<Key>().unwrap(), Key::F1);
514        assert_eq!("f12".parse::<Key>().unwrap(), Key::F12);
515        assert_eq!("F20".parse::<Key>().unwrap(), Key::F20);
516        assert_eq!("f21".parse::<Key>().unwrap(), Key::F21);
517        assert_eq!("F24".parse::<Key>().unwrap(), Key::F24);
518    }
519
520    #[test]
521    fn parse_special_keys() {
522        assert_eq!("Space".parse::<Key>().unwrap(), Key::Space);
523        assert_eq!("return".parse::<Key>().unwrap(), Key::Return);
524        assert_eq!("enter".parse::<Key>().unwrap(), Key::Return);
525        assert_eq!("Tab".parse::<Key>().unwrap(), Key::Tab);
526        assert_eq!("Escape".parse::<Key>().unwrap(), Key::Escape);
527        assert_eq!("esc".parse::<Key>().unwrap(), Key::Escape);
528        assert_eq!("Delete".parse::<Key>().unwrap(), Key::Delete);
529        assert_eq!("backspace".parse::<Key>().unwrap(), Key::Delete);
530    }
531
532    #[test]
533    fn parse_arrow_keys() {
534        assert_eq!("Left".parse::<Key>().unwrap(), Key::LeftArrow);
535        assert_eq!("leftarrow".parse::<Key>().unwrap(), Key::LeftArrow);
536        assert_eq!("Right".parse::<Key>().unwrap(), Key::RightArrow);
537        assert_eq!("Up".parse::<Key>().unwrap(), Key::UpArrow);
538        assert_eq!("Down".parse::<Key>().unwrap(), Key::DownArrow);
539    }
540
541    #[test]
542    fn parse_punctuation() {
543        assert_eq!("-".parse::<Key>().unwrap(), Key::Minus);
544        assert_eq!("minus".parse::<Key>().unwrap(), Key::Minus);
545        assert_eq!("=".parse::<Key>().unwrap(), Key::Equal);
546        assert_eq!("[".parse::<Key>().unwrap(), Key::LeftBracket);
547        assert_eq!("]".parse::<Key>().unwrap(), Key::RightBracket);
548        assert_eq!("/".parse::<Key>().unwrap(), Key::Slash);
549        assert_eq!("`".parse::<Key>().unwrap(), Key::Grave);
550    }
551
552    #[test]
553    fn parse_unknown_key_fails() {
554        assert!("unknown".parse::<Key>().is_err());
555        assert!("".parse::<Key>().is_err());
556    }
557
558    #[test]
559    fn key_display_roundtrip() {
560        // Test that parsing the display output gives the same key
561        let keys = [
562            Key::A,
563            Key::Z,
564            Key::Num0,
565            Key::Num9,
566            Key::F1,
567            Key::F12,
568            Key::F21,
569            Key::F22,
570            Key::F23,
571            Key::F24,
572            Key::Space,
573            Key::Return,
574            Key::Tab,
575            Key::Escape,
576            Key::Pause,
577            Key::LeftArrow,
578            Key::RightArrow,
579            Key::KeypadPlus,
580            Key::KeypadMinus,
581            Key::KeypadMultiply,
582            Key::KeypadDivide,
583            Key::KeypadDecimal,
584            Key::KeypadEquals,
585            Key::KeypadEnter,
586            Key::KeypadClear,
587            Key::KeypadComma,
588            Key::JisYen,
589            Key::JisUnderscore,
590            Key::JisEisu,
591            Key::JisKana,
592            Key::Dictation,
593            Key::PlayPause,
594            Key::Stop,
595            Key::PrevTrack,
596            Key::NextTrack,
597            Key::PrintScreen,
598            Key::ContextMenu,
599        ];
600        for key in keys {
601            let displayed = format!("{}", key);
602            let parsed: Key = displayed.parse().unwrap();
603            assert_eq!(parsed, key, "Roundtrip failed for {:?}", key);
604        }
605    }
606
607    #[test]
608    fn parse_dictation_aliases() {
609        assert_eq!("dictation".parse::<Key>().unwrap(), Key::Dictation);
610        assert_eq!("voice".parse::<Key>().unwrap(), Key::Dictation);
611        assert_eq!("globe".parse::<Key>().unwrap(), Key::Dictation);
612        // "fn" must keep parsing as the Fn *modifier*, not a key
613        assert!("fn".parse::<Key>().is_err());
614    }
615
616    #[test]
617    fn parse_printscreen_and_context_menu() {
618        assert_eq!("printscreen".parse::<Key>().unwrap(), Key::PrintScreen);
619        assert_eq!("prtsc".parse::<Key>().unwrap(), Key::PrintScreen);
620        assert_eq!("sysrq".parse::<Key>().unwrap(), Key::PrintScreen);
621        assert_eq!("contextmenu".parse::<Key>().unwrap(), Key::ContextMenu);
622        assert_eq!("menu".parse::<Key>().unwrap(), Key::ContextMenu);
623        assert_eq!("apps".parse::<Key>().unwrap(), Key::ContextMenu);
624        assert_eq!("application".parse::<Key>().unwrap(), Key::ContextMenu);
625    }
626
627    #[test]
628    fn parse_media_keys() {
629        // Parsing is case-insensitive and matches the Display spelling.
630        assert_eq!("playpause".parse::<Key>().unwrap(), Key::PlayPause);
631        assert_eq!("PlayPause".parse::<Key>().unwrap(), Key::PlayPause);
632        assert_eq!("stop".parse::<Key>().unwrap(), Key::Stop);
633        assert_eq!("prevtrack".parse::<Key>().unwrap(), Key::PrevTrack);
634        assert_eq!("nexttrack".parse::<Key>().unwrap(), Key::NextTrack);
635    }
636
637    #[test]
638    fn parse_pause_aliases() {
639        // Pause and Break share one physical key on PC hardware.
640        assert_eq!("pause".parse::<Key>().unwrap(), Key::Pause);
641        assert_eq!("Pause".parse::<Key>().unwrap(), Key::Pause);
642        assert_eq!("break".parse::<Key>().unwrap(), Key::Pause);
643        // "playpause" must keep parsing as the media key, not Pause
644        assert_eq!("playpause".parse::<Key>().unwrap(), Key::PlayPause);
645    }
646}