floem 0.2.0

A native Rust UI library with fine-grained reactivity
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
use std::{
    fmt::Display,
    hash::{Hash, Hasher},
    str::FromStr,
};

use crate::keyboard::{Key, KeyCode, NativeKey, PhysicalKey};

#[derive(Clone, Debug, Eq)]
pub enum KeyInput {
    Keyboard(crate::keyboard::Key, crate::keyboard::PhysicalKey),
    Pointer(crate::pointer::PointerButton),
}

impl KeyInput {
    fn keyboard_from_str(s: &str) -> Option<(Key, PhysicalKey)> {
        // Checks if it is a character key
        fn is_key_string(s: &str) -> bool {
            s.chars().all(|c| !c.is_control()) && s.chars().skip(1).all(|c| !c.is_ascii())
        }

        fn char_to_keycode(char: &str) -> PhysicalKey {
            match char {
                "a" => PhysicalKey::Code(KeyCode::KeyA),
                "b" => PhysicalKey::Code(KeyCode::KeyB),
                "c" => PhysicalKey::Code(KeyCode::KeyC),
                "d" => PhysicalKey::Code(KeyCode::KeyD),
                "e" => PhysicalKey::Code(KeyCode::KeyE),
                "f" => PhysicalKey::Code(KeyCode::KeyF),
                "g" => PhysicalKey::Code(KeyCode::KeyG),
                "h" => PhysicalKey::Code(KeyCode::KeyH),
                "i" => PhysicalKey::Code(KeyCode::KeyI),
                "j" => PhysicalKey::Code(KeyCode::KeyJ),
                "k" => PhysicalKey::Code(KeyCode::KeyK),
                "l" => PhysicalKey::Code(KeyCode::KeyL),
                "m" => PhysicalKey::Code(KeyCode::KeyM),
                "n" => PhysicalKey::Code(KeyCode::KeyN),
                "o" => PhysicalKey::Code(KeyCode::KeyO),
                "p" => PhysicalKey::Code(KeyCode::KeyP),
                "q" => PhysicalKey::Code(KeyCode::KeyQ),
                "r" => PhysicalKey::Code(KeyCode::KeyR),
                "s" => PhysicalKey::Code(KeyCode::KeyS),
                "t" => PhysicalKey::Code(KeyCode::KeyT),
                "u" => PhysicalKey::Code(KeyCode::KeyU),
                "v" => PhysicalKey::Code(KeyCode::KeyV),
                "w" => PhysicalKey::Code(KeyCode::KeyW),
                "x" => PhysicalKey::Code(KeyCode::KeyX),
                "y" => PhysicalKey::Code(KeyCode::KeyY),
                "z" => PhysicalKey::Code(KeyCode::KeyZ),
                "=" => PhysicalKey::Code(KeyCode::Equal),
                "-" => PhysicalKey::Code(KeyCode::Minus),
                "0" => PhysicalKey::Code(KeyCode::Digit0),
                "1" => PhysicalKey::Code(KeyCode::Digit1),
                "2" => PhysicalKey::Code(KeyCode::Digit2),
                "3" => PhysicalKey::Code(KeyCode::Digit3),
                "4" => PhysicalKey::Code(KeyCode::Digit4),
                "5" => PhysicalKey::Code(KeyCode::Digit5),
                "6" => PhysicalKey::Code(KeyCode::Digit6),
                "7" => PhysicalKey::Code(KeyCode::Digit7),
                "8" => PhysicalKey::Code(KeyCode::Digit8),
                "9" => PhysicalKey::Code(KeyCode::Digit9),
                "`" => PhysicalKey::Code(KeyCode::Backquote),
                "/" => PhysicalKey::Code(KeyCode::Slash),
                "\\" => PhysicalKey::Code(KeyCode::Backslash),
                "," => PhysicalKey::Code(KeyCode::Comma),
                "." => PhysicalKey::Code(KeyCode::Period),
                "*" => PhysicalKey::Code(KeyCode::NumpadMultiply),
                "+" => PhysicalKey::Code(KeyCode::NumpadAdd),
                ";" => PhysicalKey::Code(KeyCode::Semicolon),
                "'" => PhysicalKey::Code(KeyCode::Quote),
                "[" => PhysicalKey::Code(KeyCode::BracketLeft),
                "]" => PhysicalKey::Code(KeyCode::BracketRight),
                "<" => PhysicalKey::Code(KeyCode::IntlBackslash),
                _ => PhysicalKey::Code(KeyCode::Fn),
            }
        }

        // Import into scope to reduce noise
        use crate::keyboard::NamedKey::*;
        Some(match s {
            s if is_key_string(s) => {
                let char = Key::Character(s.into());
                (char.clone(), char_to_keycode(char.to_text().unwrap()))
            }
            "unidentified" => (
                Key::Unidentified(NativeKey::Unidentified),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "alt" => (Key::Named(Alt), PhysicalKey::Code(KeyCode::AltLeft)),
            "altgraph" => (Key::Named(AltGraph), PhysicalKey::Code(KeyCode::AltRight)),
            "capslock" => (Key::Named(CapsLock), PhysicalKey::Code(KeyCode::CapsLock)),
            "control" => (Key::Named(Control), PhysicalKey::Code(KeyCode::ControlLeft)),
            "fn" => (Key::Named(Fn), PhysicalKey::Code(KeyCode::Fn)),
            "fnlock" => (Key::Named(FnLock), PhysicalKey::Code(KeyCode::FnLock)),
            "meta" => (Key::Named(Meta), PhysicalKey::Code(KeyCode::Meta)),
            "numlock" => (Key::Named(NumLock), PhysicalKey::Code(KeyCode::NumLock)),
            "scrolllock" => (
                Key::Named(ScrollLock),
                PhysicalKey::Code(KeyCode::ScrollLock),
            ),
            "shift" => (Key::Named(Shift), PhysicalKey::Code(KeyCode::ShiftLeft)),
            "symbol" => (Key::Named(Symbol), PhysicalKey::Code(KeyCode::Fn)),
            "symbollock" => (Key::Named(SymbolLock), PhysicalKey::Code(KeyCode::Fn)),
            "hyper" => (Key::Named(Hyper), PhysicalKey::Code(KeyCode::Hyper)),
            "super" => (Key::Named(Super), PhysicalKey::Code(KeyCode::Meta)),
            "enter" => (Key::Named(Enter), PhysicalKey::Code(KeyCode::Enter)),
            "tab" => (Key::Named(Tab), PhysicalKey::Code(KeyCode::Tab)),
            "arrowdown" => (Key::Named(ArrowDown), PhysicalKey::Code(KeyCode::ArrowDown)),
            "arrowleft" => (Key::Named(ArrowLeft), PhysicalKey::Code(KeyCode::ArrowLeft)),
            "arrowright" => (
                Key::Named(ArrowRight),
                PhysicalKey::Code(KeyCode::ArrowRight),
            ),
            "arrowup" => (Key::Named(ArrowUp), PhysicalKey::Code(KeyCode::ArrowUp)),
            "end" => (Key::Named(End), PhysicalKey::Code(KeyCode::End)),
            "home" => (Key::Named(Home), PhysicalKey::Code(KeyCode::Home)),
            "pagedown" => (Key::Named(PageDown), PhysicalKey::Code(KeyCode::PageDown)),
            "pageup" => (Key::Named(PageUp), PhysicalKey::Code(KeyCode::PageUp)),
            "backspace" => (Key::Named(Backspace), PhysicalKey::Code(KeyCode::Backspace)),
            "clear" => (Key::Named(Clear), PhysicalKey::Code(KeyCode::Fn)),
            "copy" => (Key::Named(Copy), PhysicalKey::Code(KeyCode::Copy)),
            "crsel" => (Key::Named(CrSel), PhysicalKey::Code(KeyCode::Fn)),
            "cut" => (Key::Named(Cut), PhysicalKey::Code(KeyCode::Cut)),
            "delete" => (Key::Named(Delete), PhysicalKey::Code(KeyCode::Delete)),
            "eraseeof" => (Key::Named(EraseEof), PhysicalKey::Code(KeyCode::Fn)),
            "exsel" => (Key::Named(ExSel), PhysicalKey::Code(KeyCode::Fn)),
            "insert" => (Key::Named(Insert), PhysicalKey::Code(KeyCode::Insert)),
            "paste" => (Key::Named(Paste), PhysicalKey::Code(KeyCode::Paste)),
            "redo" => (Key::Named(Redo), PhysicalKey::Code(KeyCode::Fn)),
            "undo" => (Key::Named(Undo), PhysicalKey::Code(KeyCode::Undo)),
            "accept" => (Key::Named(Accept), PhysicalKey::Code(KeyCode::Fn)),
            "again" => (Key::Named(Again), PhysicalKey::Code(KeyCode::Again)),
            "attn" => (Key::Named(Attn), PhysicalKey::Code(KeyCode::Fn)),
            "cancel" => (Key::Named(Cancel), PhysicalKey::Code(KeyCode::Fn)),
            "contextmenu" => (
                Key::Named(ContextMenu),
                PhysicalKey::Code(KeyCode::ContextMenu),
            ),
            "escape" => (Key::Named(Escape), PhysicalKey::Code(KeyCode::Escape)),
            "execute" => (Key::Named(Execute), PhysicalKey::Code(KeyCode::Fn)),
            "find" => (Key::Named(Find), PhysicalKey::Code(KeyCode::Find)),
            "help" => (Key::Named(Help), PhysicalKey::Code(KeyCode::Help)),
            "pause" => (Key::Named(Pause), PhysicalKey::Code(KeyCode::Pause)),
            "play" => (Key::Named(Play), PhysicalKey::Code(KeyCode::MediaPlayPause)),
            "props" => (Key::Named(Props), PhysicalKey::Code(KeyCode::Props)),
            "select" => (Key::Named(Select), PhysicalKey::Code(KeyCode::Select)),
            "zoomin" => (Key::Named(ZoomIn), PhysicalKey::Code(KeyCode::Fn)),
            "zoomout" => (Key::Named(ZoomOut), PhysicalKey::Code(KeyCode::Fn)),
            "brightnessdown" => (Key::Named(BrightnessDown), PhysicalKey::Code(KeyCode::Fn)),
            "brightnessup" => (Key::Named(BrightnessUp), PhysicalKey::Code(KeyCode::Fn)),
            "eject" => (Key::Named(Eject), PhysicalKey::Code(KeyCode::Eject)),
            "logoff" => (Key::Named(LogOff), PhysicalKey::Code(KeyCode::Fn)),
            "power" => (Key::Named(Power), PhysicalKey::Code(KeyCode::Power)),
            "poweroff" => (Key::Named(PowerOff), PhysicalKey::Code(KeyCode::Fn)),
            "printscreen" => (
                Key::Named(PrintScreen),
                PhysicalKey::Code(KeyCode::PrintScreen),
            ),
            "hibernate" => (Key::Named(Hibernate), PhysicalKey::Code(KeyCode::Fn)),
            "standby" => (Key::Named(Standby), PhysicalKey::Code(KeyCode::Fn)),
            "wakeup" => (Key::Named(WakeUp), PhysicalKey::Code(KeyCode::WakeUp)),
            "allcandidates" => (Key::Named(AllCandidates), PhysicalKey::Code(KeyCode::Fn)),
            "alphanumeric" => (Key::Named(Alphanumeric), PhysicalKey::Code(KeyCode::Fn)),
            "codeinput" => (Key::Named(CodeInput), PhysicalKey::Code(KeyCode::Fn)),
            "compose" => (Key::Named(Compose), PhysicalKey::Code(KeyCode::Fn)),
            "convert" => (Key::Named(Convert), PhysicalKey::Code(KeyCode::Convert)),
            "dead" => (Key::Dead(None), PhysicalKey::Code(KeyCode::Fn)),
            "finalmode" => (Key::Named(FinalMode), PhysicalKey::Code(KeyCode::Fn)),
            "groupfirst" => (Key::Named(GroupFirst), PhysicalKey::Code(KeyCode::Fn)),
            "grouplast" => (Key::Named(GroupLast), PhysicalKey::Code(KeyCode::Fn)),
            "groupnext" => (Key::Named(GroupNext), PhysicalKey::Code(KeyCode::Fn)),
            "groupprevious" => (Key::Named(GroupPrevious), PhysicalKey::Code(KeyCode::Fn)),
            "modechange" => (Key::Named(ModeChange), PhysicalKey::Code(KeyCode::Fn)),
            "nextcandidate" => (Key::Named(NextCandidate), PhysicalKey::Code(KeyCode::Fn)),
            "nonconvert" => (
                Key::Named(NonConvert),
                PhysicalKey::Code(KeyCode::NonConvert),
            ),
            "previouscandidate" => (
                Key::Named(PreviousCandidate),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "process" => (Key::Named(Process), PhysicalKey::Code(KeyCode::Fn)),
            "singlecandidate" => (Key::Named(SingleCandidate), PhysicalKey::Code(KeyCode::Fn)),
            "hangulmode" => (Key::Named(HangulMode), PhysicalKey::Code(KeyCode::Fn)),
            "hanjamode" => (Key::Named(HanjaMode), PhysicalKey::Code(KeyCode::Fn)),
            "junjamode" => (Key::Named(JunjaMode), PhysicalKey::Code(KeyCode::Fn)),
            "eisu" => (Key::Named(Eisu), PhysicalKey::Code(KeyCode::Fn)),
            "hankaku" => (Key::Named(Hankaku), PhysicalKey::Code(KeyCode::Fn)),
            "hiragana" => (Key::Named(Hiragana), PhysicalKey::Code(KeyCode::Hiragana)),
            "hiraganakatakana" => (Key::Named(HiraganaKatakana), PhysicalKey::Code(KeyCode::Fn)),
            "kanamode" => (Key::Named(KanaMode), PhysicalKey::Code(KeyCode::KanaMode)),
            "kanjimode" => (Key::Named(KanjiMode), PhysicalKey::Code(KeyCode::Fn)),
            "katakana" => (Key::Named(Katakana), PhysicalKey::Code(KeyCode::Katakana)),
            "romaji" => (Key::Named(Romaji), PhysicalKey::Code(KeyCode::Fn)),
            "zenkaku" => (Key::Named(Zenkaku), PhysicalKey::Code(KeyCode::Fn)),
            "zenkakuhankaku" => (Key::Named(ZenkakuHankaku), PhysicalKey::Code(KeyCode::Fn)),
            "f1" => (Key::Named(F1), PhysicalKey::Code(KeyCode::F1)),
            "f2" => (Key::Named(F2), PhysicalKey::Code(KeyCode::F2)),
            "f3" => (Key::Named(F3), PhysicalKey::Code(KeyCode::F3)),
            "f4" => (Key::Named(F4), PhysicalKey::Code(KeyCode::F4)),
            "f5" => (Key::Named(F5), PhysicalKey::Code(KeyCode::F5)),
            "f6" => (Key::Named(F6), PhysicalKey::Code(KeyCode::F6)),
            "f7" => (Key::Named(F7), PhysicalKey::Code(KeyCode::F7)),
            "f8" => (Key::Named(F8), PhysicalKey::Code(KeyCode::F8)),
            "f9" => (Key::Named(F9), PhysicalKey::Code(KeyCode::F9)),
            "f10" => (Key::Named(F10), PhysicalKey::Code(KeyCode::F10)),
            "f11" => (Key::Named(F11), PhysicalKey::Code(KeyCode::F11)),
            "f12" => (Key::Named(F12), PhysicalKey::Code(KeyCode::F12)),
            "soft1" => (Key::Named(Soft1), PhysicalKey::Code(KeyCode::Fn)),
            "soft2" => (Key::Named(Soft2), PhysicalKey::Code(KeyCode::Fn)),
            "soft3" => (Key::Named(Soft3), PhysicalKey::Code(KeyCode::Fn)),
            "soft4" => (Key::Named(Soft4), PhysicalKey::Code(KeyCode::Fn)),
            "channeldown" => (Key::Named(ChannelDown), PhysicalKey::Code(KeyCode::Fn)),
            "channelup" => (Key::Named(ChannelUp), PhysicalKey::Code(KeyCode::Fn)),
            "close" => (Key::Named(Close), PhysicalKey::Code(KeyCode::Fn)),
            "mailforward" => (Key::Named(MailForward), PhysicalKey::Code(KeyCode::Fn)),
            "mailreply" => (Key::Named(MailReply), PhysicalKey::Code(KeyCode::Fn)),
            "mailsend" => (Key::Named(MailSend), PhysicalKey::Code(KeyCode::Fn)),
            "mediaclose" => (Key::Named(MediaClose), PhysicalKey::Code(KeyCode::Fn)),
            "mediafastforward" => (Key::Named(MediaFastForward), PhysicalKey::Code(KeyCode::Fn)),
            "mediapause" => (Key::Named(MediaPause), PhysicalKey::Code(KeyCode::Fn)),
            "mediaplay" => (Key::Named(MediaPlay), PhysicalKey::Code(KeyCode::Fn)),
            "mediaplaypause" => (
                Key::Named(MediaPlayPause),
                PhysicalKey::Code(KeyCode::MediaPlayPause),
            ),
            "mediarecord" => (Key::Named(MediaRecord), PhysicalKey::Code(KeyCode::Fn)),
            "mediarewind" => (Key::Named(MediaRewind), PhysicalKey::Code(KeyCode::Fn)),
            "mediastop" => (Key::Named(MediaStop), PhysicalKey::Code(KeyCode::MediaStop)),
            "mediatracknext" => (
                Key::Named(MediaTrackNext),
                PhysicalKey::Code(KeyCode::MediaTrackNext),
            ),
            "mediatrackprevious" => (
                Key::Named(MediaTrackPrevious),
                PhysicalKey::Code(KeyCode::MediaTrackPrevious),
            ),
            "new" => (Key::Named(New), PhysicalKey::Code(KeyCode::Fn)),
            "open" => (Key::Named(Open), PhysicalKey::Code(KeyCode::Open)),
            "print" => (Key::Named(Print), PhysicalKey::Code(KeyCode::Fn)),
            "save" => (Key::Named(Save), PhysicalKey::Code(KeyCode::Fn)),
            "spellcheck" => (Key::Named(SpellCheck), PhysicalKey::Code(KeyCode::Fn)),
            "key11" => (Key::Named(Key11), PhysicalKey::Code(KeyCode::Fn)),
            "key12" => (Key::Named(Key12), PhysicalKey::Code(KeyCode::Fn)),
            "audiobalanceleft" => (Key::Named(AudioBalanceLeft), PhysicalKey::Code(KeyCode::Fn)),
            "audiobalanceright" => (
                Key::Named(AudioBalanceRight),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "audiobassboostdown" => (
                Key::Named(AudioBassBoostDown),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "audiobassboosttoggle" => (
                Key::Named(AudioBassBoostToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "audiobassboostup" => (Key::Named(AudioBassBoostUp), PhysicalKey::Code(KeyCode::Fn)),
            "audiofaderfront" => (Key::Named(AudioFaderFront), PhysicalKey::Code(KeyCode::Fn)),
            "audiofaderrear" => (Key::Named(AudioFaderRear), PhysicalKey::Code(KeyCode::Fn)),
            "audiosurroundmodenext" => (
                Key::Named(AudioSurroundModeNext),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "audiotrebledown" => (Key::Named(AudioTrebleDown), PhysicalKey::Code(KeyCode::Fn)),
            "audiotrebleup" => (Key::Named(AudioTrebleUp), PhysicalKey::Code(KeyCode::Fn)),
            "audiovolumedown" => (
                Key::Named(AudioVolumeDown),
                PhysicalKey::Code(KeyCode::AudioVolumeDown),
            ),
            "audiovolumeup" => (
                Key::Named(AudioVolumeUp),
                PhysicalKey::Code(KeyCode::AudioVolumeUp),
            ),
            "audiovolumemute" => (
                Key::Named(AudioVolumeMute),
                PhysicalKey::Code(KeyCode::AudioVolumeMute),
            ),
            "microphonetoggle" => (Key::Named(MicrophoneToggle), PhysicalKey::Code(KeyCode::Fn)),
            "microphonevolumedown" => (
                Key::Named(MicrophoneVolumeDown),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "microphonevolumeup" => (
                Key::Named(MicrophoneVolumeUp),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "microphonevolumemute" => (
                Key::Named(MicrophoneVolumeMute),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "speechcorrectionlist" => (
                Key::Named(SpeechCorrectionList),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "speechinputtoggle" => (
                Key::Named(SpeechInputToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchapplication1" => (
                Key::Named(LaunchApplication1),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchapplication2" => (
                Key::Named(LaunchApplication2),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchcalendar" => (Key::Named(LaunchCalendar), PhysicalKey::Code(KeyCode::Fn)),
            "launchcontacts" => (Key::Named(LaunchContacts), PhysicalKey::Code(KeyCode::Fn)),
            "launchmail" => (
                Key::Named(LaunchMail),
                PhysicalKey::Code(KeyCode::LaunchMail),
            ),
            "launchmediaplayer" => (
                Key::Named(LaunchMediaPlayer),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchmusicplayer" => (
                Key::Named(LaunchMusicPlayer),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchphone" => (Key::Named(LaunchPhone), PhysicalKey::Code(KeyCode::Fn)),
            "launchscreensaver" => (
                Key::Named(LaunchScreenSaver),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchspreadsheet" => (
                Key::Named(LaunchSpreadsheet),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "launchwebbrowser" => (Key::Named(LaunchWebBrowser), PhysicalKey::Code(KeyCode::Fn)),
            "launchwebcam" => (Key::Named(LaunchWebCam), PhysicalKey::Code(KeyCode::Fn)),
            "launchwordprocessor" => (
                Key::Named(LaunchWordProcessor),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "browserback" => (
                Key::Named(BrowserBack),
                PhysicalKey::Code(KeyCode::BrowserBack),
            ),
            "browserfavorites" => (
                Key::Named(BrowserFavorites),
                PhysicalKey::Code(KeyCode::BrowserFavorites),
            ),
            "browserforward" => (
                Key::Named(BrowserForward),
                PhysicalKey::Code(KeyCode::BrowserForward),
            ),
            "browserhome" => (
                Key::Named(BrowserHome),
                PhysicalKey::Code(KeyCode::BrowserHome),
            ),
            "browserrefresh" => (
                Key::Named(BrowserRefresh),
                PhysicalKey::Code(KeyCode::BrowserRefresh),
            ),
            "browsersearch" => (
                Key::Named(BrowserSearch),
                PhysicalKey::Code(KeyCode::BrowserSearch),
            ),
            "browserstop" => (
                Key::Named(BrowserStop),
                PhysicalKey::Code(KeyCode::BrowserStop),
            ),
            "appswitch" => (Key::Named(AppSwitch), PhysicalKey::Code(KeyCode::Fn)),
            "call" => (Key::Named(Call), PhysicalKey::Code(KeyCode::Fn)),
            "camera" => (Key::Named(Camera), PhysicalKey::Code(KeyCode::Fn)),
            "camerafocus" => (Key::Named(CameraFocus), PhysicalKey::Code(KeyCode::Fn)),
            "endcall" => (Key::Named(EndCall), PhysicalKey::Code(KeyCode::Fn)),
            "goback" => (Key::Named(GoBack), PhysicalKey::Code(KeyCode::Fn)),
            "gohome" => (Key::Named(GoHome), PhysicalKey::Code(KeyCode::Fn)),
            "headsethook" => (Key::Named(HeadsetHook), PhysicalKey::Code(KeyCode::Fn)),
            "lastnumberredial" => (Key::Named(LastNumberRedial), PhysicalKey::Code(KeyCode::Fn)),
            "notification" => (Key::Named(Notification), PhysicalKey::Code(KeyCode::Fn)),
            "mannermode" => (Key::Named(MannerMode), PhysicalKey::Code(KeyCode::Fn)),
            "voicedial" => (Key::Named(VoiceDial), PhysicalKey::Code(KeyCode::Fn)),
            "tv" => (Key::Named(TV), PhysicalKey::Code(KeyCode::Fn)),
            "tv3dmode" => (Key::Named(TV3DMode), PhysicalKey::Code(KeyCode::Fn)),
            "tvantennacable" => (Key::Named(TVAntennaCable), PhysicalKey::Code(KeyCode::Fn)),
            "tvaudiodescription" => (
                Key::Named(TVAudioDescription),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvaudiodescriptionmixdown" => (
                Key::Named(TVAudioDescriptionMixDown),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvaudiodescriptionmixup" => (
                Key::Named(TVAudioDescriptionMixUp),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvcontentsmenu" => (Key::Named(TVContentsMenu), PhysicalKey::Code(KeyCode::Fn)),
            "tvdataservice" => (Key::Named(TVDataService), PhysicalKey::Code(KeyCode::Fn)),
            "tvinput" => (Key::Named(TVInput), PhysicalKey::Code(KeyCode::Fn)),
            "tvinputcomponent1" => (
                Key::Named(TVInputComponent1),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvinputcomponent2" => (
                Key::Named(TVInputComponent2),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvinputcomposite1" => (
                Key::Named(TVInputComposite1),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvinputcomposite2" => (
                Key::Named(TVInputComposite2),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvinputhdmi1" => (Key::Named(TVInputHDMI1), PhysicalKey::Code(KeyCode::Fn)),
            "tvinputhdmi2" => (Key::Named(TVInputHDMI2), PhysicalKey::Code(KeyCode::Fn)),
            "tvinputhdmi3" => (Key::Named(TVInputHDMI3), PhysicalKey::Code(KeyCode::Fn)),
            "tvinputhdmi4" => (Key::Named(TVInputHDMI4), PhysicalKey::Code(KeyCode::Fn)),
            "tvinputvga1" => (Key::Named(TVInputVGA1), PhysicalKey::Code(KeyCode::Fn)),
            "tvmediacontext" => (Key::Named(TVMediaContext), PhysicalKey::Code(KeyCode::Fn)),
            "tvnetwork" => (Key::Named(TVNetwork), PhysicalKey::Code(KeyCode::Fn)),
            "tvnumberentry" => (Key::Named(TVNumberEntry), PhysicalKey::Code(KeyCode::Fn)),
            "tvpower" => (Key::Named(TVPower), PhysicalKey::Code(KeyCode::Fn)),
            "tvradioservice" => (Key::Named(TVRadioService), PhysicalKey::Code(KeyCode::Fn)),
            "tvsatellite" => (Key::Named(TVSatellite), PhysicalKey::Code(KeyCode::Fn)),
            "tvsatellitebs" => (Key::Named(TVSatelliteBS), PhysicalKey::Code(KeyCode::Fn)),
            "tvsatellitecs" => (Key::Named(TVSatelliteCS), PhysicalKey::Code(KeyCode::Fn)),
            "tvsatellitetoggle" => (
                Key::Named(TVSatelliteToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvterrestrialanalog" => (
                Key::Named(TVTerrestrialAnalog),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvterrestrialdigital" => (
                Key::Named(TVTerrestrialDigital),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "tvtimer" => (Key::Named(TVTimer), PhysicalKey::Code(KeyCode::Fn)),
            "avrinput" => (Key::Named(AVRInput), PhysicalKey::Code(KeyCode::Fn)),
            "avrpower" => (Key::Named(AVRPower), PhysicalKey::Code(KeyCode::Fn)),
            "colorf0red" => (Key::Named(ColorF0Red), PhysicalKey::Code(KeyCode::Fn)),
            "colorf1green" => (Key::Named(ColorF1Green), PhysicalKey::Code(KeyCode::Fn)),
            "colorf2yellow" => (Key::Named(ColorF2Yellow), PhysicalKey::Code(KeyCode::Fn)),
            "colorf3blue" => (Key::Named(ColorF3Blue), PhysicalKey::Code(KeyCode::Fn)),
            "colorf4grey" => (Key::Named(ColorF4Grey), PhysicalKey::Code(KeyCode::Fn)),
            "colorf5brown" => (Key::Named(ColorF5Brown), PhysicalKey::Code(KeyCode::Fn)),
            "closedcaptiontoggle" => (
                Key::Named(ClosedCaptionToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "dimmer" => (Key::Named(Dimmer), PhysicalKey::Code(KeyCode::Fn)),
            "displayswap" => (Key::Named(DisplaySwap), PhysicalKey::Code(KeyCode::Fn)),
            "dvr" => (Key::Named(DVR), PhysicalKey::Code(KeyCode::Fn)),
            "exit" => (Key::Named(Exit), PhysicalKey::Code(KeyCode::Fn)),
            "favoriteclear0" => (Key::Named(FavoriteClear0), PhysicalKey::Code(KeyCode::Fn)),
            "favoriteclear1" => (Key::Named(FavoriteClear1), PhysicalKey::Code(KeyCode::Fn)),
            "favoriteclear2" => (Key::Named(FavoriteClear2), PhysicalKey::Code(KeyCode::Fn)),
            "favoriteclear3" => (Key::Named(FavoriteClear3), PhysicalKey::Code(KeyCode::Fn)),
            "favoriterecall0" => (Key::Named(FavoriteRecall0), PhysicalKey::Code(KeyCode::Fn)),
            "favoriterecall1" => (Key::Named(FavoriteRecall1), PhysicalKey::Code(KeyCode::Fn)),
            "favoriterecall2" => (Key::Named(FavoriteRecall2), PhysicalKey::Code(KeyCode::Fn)),
            "favoriterecall3" => (Key::Named(FavoriteRecall3), PhysicalKey::Code(KeyCode::Fn)),
            "favoritestore0" => (Key::Named(FavoriteStore0), PhysicalKey::Code(KeyCode::Fn)),
            "favoritestore1" => (Key::Named(FavoriteStore1), PhysicalKey::Code(KeyCode::Fn)),
            "favoritestore2" => (Key::Named(FavoriteStore2), PhysicalKey::Code(KeyCode::Fn)),
            "favoritestore3" => (Key::Named(FavoriteStore3), PhysicalKey::Code(KeyCode::Fn)),
            "guide" => (Key::Named(Guide), PhysicalKey::Code(KeyCode::Fn)),
            "guidenextday" => (Key::Named(GuideNextDay), PhysicalKey::Code(KeyCode::Fn)),
            "guidepreviousday" => (Key::Named(GuidePreviousDay), PhysicalKey::Code(KeyCode::Fn)),
            "info" => (Key::Named(Info), PhysicalKey::Code(KeyCode::Fn)),
            "instantreplay" => (Key::Named(InstantReplay), PhysicalKey::Code(KeyCode::Fn)),
            "link" => (Key::Named(Link), PhysicalKey::Code(KeyCode::Fn)),
            "listprogram" => (Key::Named(ListProgram), PhysicalKey::Code(KeyCode::Fn)),
            "livecontent" => (Key::Named(LiveContent), PhysicalKey::Code(KeyCode::Fn)),
            "lock" => (Key::Named(Lock), PhysicalKey::Code(KeyCode::Fn)),
            "mediaapps" => (Key::Named(MediaApps), PhysicalKey::Code(KeyCode::Fn)),
            "mediaaudiotrack" => (Key::Named(MediaAudioTrack), PhysicalKey::Code(KeyCode::Fn)),
            "medialast" => (Key::Named(MediaLast), PhysicalKey::Code(KeyCode::Fn)),
            "mediaskipbackward" => (
                Key::Named(MediaSkipBackward),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "mediaskipforward" => (Key::Named(MediaSkipForward), PhysicalKey::Code(KeyCode::Fn)),
            "mediastepbackward" => (
                Key::Named(MediaStepBackward),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "mediastepforward" => (Key::Named(MediaStepForward), PhysicalKey::Code(KeyCode::Fn)),
            "mediatopmenu" => (Key::Named(MediaTopMenu), PhysicalKey::Code(KeyCode::Fn)),
            "navigatein" => (Key::Named(NavigateIn), PhysicalKey::Code(KeyCode::Fn)),
            "navigatenext" => (Key::Named(NavigateNext), PhysicalKey::Code(KeyCode::Fn)),
            "navigateout" => (Key::Named(NavigateOut), PhysicalKey::Code(KeyCode::Fn)),
            "navigateprevious" => (Key::Named(NavigatePrevious), PhysicalKey::Code(KeyCode::Fn)),
            "nextfavoritechannel" => (
                Key::Named(NextFavoriteChannel),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "nextuserprofile" => (Key::Named(NextUserProfile), PhysicalKey::Code(KeyCode::Fn)),
            "ondemand" => (Key::Named(OnDemand), PhysicalKey::Code(KeyCode::Fn)),
            "pairing" => (Key::Named(Pairing), PhysicalKey::Code(KeyCode::Fn)),
            "pinpdown" => (Key::Named(PinPDown), PhysicalKey::Code(KeyCode::Fn)),
            "pinpmove" => (Key::Named(PinPMove), PhysicalKey::Code(KeyCode::Fn)),
            "pinptoggle" => (Key::Named(PinPToggle), PhysicalKey::Code(KeyCode::Fn)),
            "pinpup" => (Key::Named(PinPUp), PhysicalKey::Code(KeyCode::Fn)),
            "playspeeddown" => (Key::Named(PlaySpeedDown), PhysicalKey::Code(KeyCode::Fn)),
            "playspeedreset" => (Key::Named(PlaySpeedReset), PhysicalKey::Code(KeyCode::Fn)),
            "playspeedup" => (Key::Named(PlaySpeedUp), PhysicalKey::Code(KeyCode::Fn)),
            "randomtoggle" => (Key::Named(RandomToggle), PhysicalKey::Code(KeyCode::Fn)),
            "rclowbattery" => (Key::Named(RcLowBattery), PhysicalKey::Code(KeyCode::Fn)),
            "recordspeednext" => (Key::Named(RecordSpeedNext), PhysicalKey::Code(KeyCode::Fn)),
            "rfbypass" => (Key::Named(RfBypass), PhysicalKey::Code(KeyCode::Fn)),
            "scanchannelstoggle" => (
                Key::Named(ScanChannelsToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "screenmodenext" => (Key::Named(ScreenModeNext), PhysicalKey::Code(KeyCode::Fn)),
            "settings" => (Key::Named(Settings), PhysicalKey::Code(KeyCode::Fn)),
            "splitscreentoggle" => (
                Key::Named(SplitScreenToggle),
                PhysicalKey::Code(KeyCode::Fn),
            ),
            "stbinput" => (Key::Named(STBInput), PhysicalKey::Code(KeyCode::Fn)),
            "stbpower" => (Key::Named(STBPower), PhysicalKey::Code(KeyCode::Fn)),
            "subtitle" => (Key::Named(Subtitle), PhysicalKey::Code(KeyCode::Fn)),
            "teletext" => (Key::Named(Teletext), PhysicalKey::Code(KeyCode::Fn)),
            "videomodenext" => (Key::Named(VideoModeNext), PhysicalKey::Code(KeyCode::Fn)),
            "wink" => (Key::Named(Wink), PhysicalKey::Code(KeyCode::Fn)),
            "zoomtoggle" => (Key::Named(ZoomToggle), PhysicalKey::Code(KeyCode::Fn)),

            // Custom key name mappings
            "esc" => (Key::Named(Escape), PhysicalKey::Code(KeyCode::Escape)),
            "space" => (Key::Named(Space), PhysicalKey::Code(KeyCode::Space)),
            "bs" => (Key::Named(Backspace), PhysicalKey::Code(KeyCode::Backspace)),
            "up" => (Key::Named(ArrowUp), PhysicalKey::Code(KeyCode::ArrowUp)),
            "down" => (Key::Named(ArrowDown), PhysicalKey::Code(KeyCode::ArrowDown)),
            "right" => (
                Key::Named(ArrowRight),
                PhysicalKey::Code(KeyCode::ArrowRight),
            ),
            "left" => (Key::Named(ArrowLeft), PhysicalKey::Code(KeyCode::ArrowLeft)),
            "del" => (Key::Named(Delete), PhysicalKey::Code(KeyCode::Delete)),

            _ => return None,
        })
    }

    fn mouse_from_str(s: &str) -> Option<crate::pointer::PointerButton> {
        use crate::pointer::PointerButton as B;

        Some(match s {
            "mousemiddle" => B::Auxiliary,
            "mouseforward" => B::X2,
            "mousebackward" => B::X1,
            _ => return None,
        })
    }
}

impl Display for KeyInput {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use crate::pointer::PointerButton as B;

        match self {
            Self::Keyboard(_key, key_code) => match key_code {
                PhysicalKey::Unidentified(_) => f.write_str("Unidentified"),
                PhysicalKey::Code(KeyCode::Backquote) => f.write_str("`"),
                PhysicalKey::Code(KeyCode::Backslash) => f.write_str("\\"),
                PhysicalKey::Code(KeyCode::BracketLeft) => f.write_str("["),
                PhysicalKey::Code(KeyCode::BracketRight) => f.write_str("]"),
                PhysicalKey::Code(KeyCode::Comma) => f.write_str(","),
                PhysicalKey::Code(KeyCode::Digit0) => f.write_str("0"),
                PhysicalKey::Code(KeyCode::Digit1) => f.write_str("1"),
                PhysicalKey::Code(KeyCode::Digit2) => f.write_str("2"),
                PhysicalKey::Code(KeyCode::Digit3) => f.write_str("3"),
                PhysicalKey::Code(KeyCode::Digit4) => f.write_str("4"),
                PhysicalKey::Code(KeyCode::Digit5) => f.write_str("5"),
                PhysicalKey::Code(KeyCode::Digit6) => f.write_str("6"),
                PhysicalKey::Code(KeyCode::Digit7) => f.write_str("7"),
                PhysicalKey::Code(KeyCode::Digit8) => f.write_str("8"),
                PhysicalKey::Code(KeyCode::Digit9) => f.write_str("9"),
                PhysicalKey::Code(KeyCode::Equal) => f.write_str("="),
                PhysicalKey::Code(KeyCode::IntlBackslash) => f.write_str("<"),
                PhysicalKey::Code(KeyCode::IntlRo) => f.write_str("IntlRo"),
                PhysicalKey::Code(KeyCode::IntlYen) => f.write_str("IntlYen"),
                PhysicalKey::Code(KeyCode::KeyA) => f.write_str("A"),
                PhysicalKey::Code(KeyCode::KeyB) => f.write_str("B"),
                PhysicalKey::Code(KeyCode::KeyC) => f.write_str("C"),
                PhysicalKey::Code(KeyCode::KeyD) => f.write_str("D"),
                PhysicalKey::Code(KeyCode::KeyE) => f.write_str("E"),
                PhysicalKey::Code(KeyCode::KeyF) => f.write_str("F"),
                PhysicalKey::Code(KeyCode::KeyG) => f.write_str("G"),
                PhysicalKey::Code(KeyCode::KeyH) => f.write_str("H"),
                PhysicalKey::Code(KeyCode::KeyI) => f.write_str("I"),
                PhysicalKey::Code(KeyCode::KeyJ) => f.write_str("J"),
                PhysicalKey::Code(KeyCode::KeyK) => f.write_str("K"),
                PhysicalKey::Code(KeyCode::KeyL) => f.write_str("L"),
                PhysicalKey::Code(KeyCode::KeyM) => f.write_str("M"),
                PhysicalKey::Code(KeyCode::KeyN) => f.write_str("N"),
                PhysicalKey::Code(KeyCode::KeyO) => f.write_str("O"),
                PhysicalKey::Code(KeyCode::KeyP) => f.write_str("P"),
                PhysicalKey::Code(KeyCode::KeyQ) => f.write_str("Q"),
                PhysicalKey::Code(KeyCode::KeyR) => f.write_str("R"),
                PhysicalKey::Code(KeyCode::KeyS) => f.write_str("S"),
                PhysicalKey::Code(KeyCode::KeyT) => f.write_str("T"),
                PhysicalKey::Code(KeyCode::KeyU) => f.write_str("U"),
                PhysicalKey::Code(KeyCode::KeyV) => f.write_str("V"),
                PhysicalKey::Code(KeyCode::KeyW) => f.write_str("W"),
                PhysicalKey::Code(KeyCode::KeyX) => f.write_str("X"),
                PhysicalKey::Code(KeyCode::KeyY) => f.write_str("Y"),
                PhysicalKey::Code(KeyCode::KeyZ) => f.write_str("Z"),
                PhysicalKey::Code(KeyCode::Minus) => f.write_str("-"),
                PhysicalKey::Code(KeyCode::Period) => f.write_str("."),
                PhysicalKey::Code(KeyCode::Quote) => f.write_str("'"),
                PhysicalKey::Code(KeyCode::Semicolon) => f.write_str(";"),
                PhysicalKey::Code(KeyCode::Slash) => f.write_str("/"),
                PhysicalKey::Code(KeyCode::AltLeft) => f.write_str("Alt"),
                PhysicalKey::Code(KeyCode::AltRight) => f.write_str("Alt"),
                PhysicalKey::Code(KeyCode::Backspace) => f.write_str("backspace"),
                PhysicalKey::Code(KeyCode::CapsLock) => f.write_str("CapsLock"),
                PhysicalKey::Code(KeyCode::ContextMenu) => f.write_str("ContextMenu"),
                PhysicalKey::Code(KeyCode::ControlLeft) => f.write_str("Ctrl"),
                PhysicalKey::Code(KeyCode::ControlRight) => f.write_str("Ctrl"),
                PhysicalKey::Code(KeyCode::Enter) => f.write_str("Enter"),
                PhysicalKey::Code(KeyCode::SuperLeft) => match std::env::consts::OS {
                    "macos" => f.write_str("Cmd"),
                    "windows" => f.write_str("Win"),
                    _ => f.write_str("Meta"),
                },
                PhysicalKey::Code(KeyCode::SuperRight) => match std::env::consts::OS {
                    "macos" => f.write_str("Cmd"),
                    "windows" => f.write_str("Win"),
                    _ => f.write_str("Meta"),
                },
                PhysicalKey::Code(KeyCode::ShiftLeft) => f.write_str("Shift"),
                PhysicalKey::Code(KeyCode::ShiftRight) => f.write_str("Shift"),
                PhysicalKey::Code(KeyCode::Space) => f.write_str("Space"),
                PhysicalKey::Code(KeyCode::Tab) => f.write_str("Tab"),
                PhysicalKey::Code(KeyCode::Convert) => f.write_str("Convert"),
                PhysicalKey::Code(KeyCode::KanaMode) => f.write_str("KanaMode"),
                PhysicalKey::Code(KeyCode::Lang1) => f.write_str("Lang1"),
                PhysicalKey::Code(KeyCode::Lang2) => f.write_str("Lang2"),
                PhysicalKey::Code(KeyCode::Lang3) => f.write_str("Lang3"),
                PhysicalKey::Code(KeyCode::Lang4) => f.write_str("Lang4"),
                PhysicalKey::Code(KeyCode::Lang5) => f.write_str("Lang5"),
                PhysicalKey::Code(KeyCode::NonConvert) => f.write_str("NonConvert"),
                PhysicalKey::Code(KeyCode::Delete) => f.write_str("Delete"),
                PhysicalKey::Code(KeyCode::End) => f.write_str("End"),
                PhysicalKey::Code(KeyCode::Help) => f.write_str("Help"),
                PhysicalKey::Code(KeyCode::Home) => f.write_str("Home"),
                PhysicalKey::Code(KeyCode::Insert) => f.write_str("Insert"),
                PhysicalKey::Code(KeyCode::PageDown) => f.write_str("PageDown"),
                PhysicalKey::Code(KeyCode::PageUp) => f.write_str("PageUp"),
                PhysicalKey::Code(KeyCode::ArrowDown) => f.write_str("Down"),
                PhysicalKey::Code(KeyCode::ArrowLeft) => f.write_str("Left"),
                PhysicalKey::Code(KeyCode::ArrowRight) => f.write_str("Right"),
                PhysicalKey::Code(KeyCode::ArrowUp) => f.write_str("Up"),
                PhysicalKey::Code(KeyCode::NumLock) => f.write_str("NumLock"),
                PhysicalKey::Code(KeyCode::Numpad0) => f.write_str("Numpad0"),
                PhysicalKey::Code(KeyCode::Numpad1) => f.write_str("Numpad1"),
                PhysicalKey::Code(KeyCode::Numpad2) => f.write_str("Numpad2"),
                PhysicalKey::Code(KeyCode::Numpad3) => f.write_str("Numpad3"),
                PhysicalKey::Code(KeyCode::Numpad4) => f.write_str("Numpad4"),
                PhysicalKey::Code(KeyCode::Numpad5) => f.write_str("Numpad5"),
                PhysicalKey::Code(KeyCode::Numpad6) => f.write_str("Numpad6"),
                PhysicalKey::Code(KeyCode::Numpad7) => f.write_str("Numpad7"),
                PhysicalKey::Code(KeyCode::Numpad8) => f.write_str("Numpad8"),
                PhysicalKey::Code(KeyCode::Numpad9) => f.write_str("Numpad9"),
                PhysicalKey::Code(KeyCode::NumpadAdd) => f.write_str("NumpadAdd"),
                PhysicalKey::Code(KeyCode::NumpadBackspace) => f.write_str("NumpadBackspace"),
                PhysicalKey::Code(KeyCode::NumpadClear) => f.write_str("NumpadClear"),
                PhysicalKey::Code(KeyCode::NumpadClearEntry) => f.write_str("NumpadClearEntry"),
                PhysicalKey::Code(KeyCode::NumpadComma) => f.write_str("NumpadComma"),
                PhysicalKey::Code(KeyCode::NumpadDecimal) => f.write_str("NumpadDecimal"),
                PhysicalKey::Code(KeyCode::NumpadDivide) => f.write_str("NumpadDivide"),
                PhysicalKey::Code(KeyCode::NumpadEnter) => f.write_str("NumpadEnter"),
                PhysicalKey::Code(KeyCode::NumpadEqual) => f.write_str("NumpadEqual"),
                PhysicalKey::Code(KeyCode::NumpadHash) => f.write_str("NumpadHash"),
                PhysicalKey::Code(KeyCode::NumpadMemoryAdd) => f.write_str("NumpadMemoryAdd"),
                PhysicalKey::Code(KeyCode::NumpadMemoryClear) => f.write_str("NumpadMemoryClear"),
                PhysicalKey::Code(KeyCode::NumpadMemoryRecall) => f.write_str("NumpadMemoryRecall"),
                PhysicalKey::Code(KeyCode::NumpadMemoryStore) => f.write_str("NumpadMemoryStore"),
                PhysicalKey::Code(KeyCode::NumpadMemorySubtract) => {
                    f.write_str("NumpadMemorySubtract")
                }
                PhysicalKey::Code(KeyCode::NumpadMultiply) => f.write_str("NumpadMultiply"),
                PhysicalKey::Code(KeyCode::NumpadParenLeft) => f.write_str("NumpadParenLeft"),
                PhysicalKey::Code(KeyCode::NumpadParenRight) => f.write_str("NumpadParenRight"),
                PhysicalKey::Code(KeyCode::NumpadStar) => f.write_str("NumpadStar"),
                PhysicalKey::Code(KeyCode::NumpadSubtract) => f.write_str("NumpadSubtract"),
                PhysicalKey::Code(KeyCode::Escape) => f.write_str("Escape"),
                PhysicalKey::Code(KeyCode::Fn) => f.write_str("Fn"),
                PhysicalKey::Code(KeyCode::FnLock) => f.write_str("FnLock"),
                PhysicalKey::Code(KeyCode::PrintScreen) => f.write_str("PrintScreen"),
                PhysicalKey::Code(KeyCode::ScrollLock) => f.write_str("ScrollLock"),
                PhysicalKey::Code(KeyCode::Pause) => f.write_str("Pause"),
                PhysicalKey::Code(KeyCode::BrowserBack) => f.write_str("BrowserBack"),
                PhysicalKey::Code(KeyCode::BrowserFavorites) => f.write_str("BrowserFavorites"),
                PhysicalKey::Code(KeyCode::BrowserForward) => f.write_str("BrowserForward"),
                PhysicalKey::Code(KeyCode::BrowserHome) => f.write_str("BrowserHome"),
                PhysicalKey::Code(KeyCode::BrowserRefresh) => f.write_str("BrowserRefresh"),
                PhysicalKey::Code(KeyCode::BrowserSearch) => f.write_str("BrowserSearch"),
                PhysicalKey::Code(KeyCode::BrowserStop) => f.write_str("BrowserStop"),
                PhysicalKey::Code(KeyCode::Eject) => f.write_str("Eject"),
                PhysicalKey::Code(KeyCode::LaunchApp1) => f.write_str("LaunchApp1"),
                PhysicalKey::Code(KeyCode::LaunchApp2) => f.write_str("LaunchApp2"),
                PhysicalKey::Code(KeyCode::LaunchMail) => f.write_str("LaunchMail"),
                PhysicalKey::Code(KeyCode::MediaPlayPause) => f.write_str("MediaPlayPause"),
                PhysicalKey::Code(KeyCode::MediaSelect) => f.write_str("MediaSelect"),
                PhysicalKey::Code(KeyCode::MediaStop) => f.write_str("MediaStop"),
                PhysicalKey::Code(KeyCode::MediaTrackNext) => f.write_str("MediaTrackNext"),
                PhysicalKey::Code(KeyCode::MediaTrackPrevious) => f.write_str("MediaTrackPrevious"),
                PhysicalKey::Code(KeyCode::Power) => f.write_str("Power"),
                PhysicalKey::Code(KeyCode::Sleep) => f.write_str("Sleep"),
                PhysicalKey::Code(KeyCode::AudioVolumeDown) => f.write_str("AudioVolumeDown"),
                PhysicalKey::Code(KeyCode::AudioVolumeMute) => f.write_str("AudioVolumeMute"),
                PhysicalKey::Code(KeyCode::AudioVolumeUp) => f.write_str("AudioVolumeUp"),
                PhysicalKey::Code(KeyCode::WakeUp) => f.write_str("WakeUp"),
                PhysicalKey::Code(KeyCode::Meta) => match std::env::consts::OS {
                    "macos" => f.write_str("Cmd"),
                    "windows" => f.write_str("Win"),
                    _ => f.write_str("Meta"),
                },
                PhysicalKey::Code(KeyCode::Hyper) => f.write_str("Hyper"),
                PhysicalKey::Code(KeyCode::Turbo) => f.write_str("Turbo"),
                PhysicalKey::Code(KeyCode::Abort) => f.write_str("Abort"),
                PhysicalKey::Code(KeyCode::Resume) => f.write_str("Resume"),
                PhysicalKey::Code(KeyCode::Suspend) => f.write_str("Suspend"),
                PhysicalKey::Code(KeyCode::Again) => f.write_str("Again"),
                PhysicalKey::Code(KeyCode::Copy) => f.write_str("Copy"),
                PhysicalKey::Code(KeyCode::Cut) => f.write_str("Cut"),
                PhysicalKey::Code(KeyCode::Find) => f.write_str("Find"),
                PhysicalKey::Code(KeyCode::Open) => f.write_str("Open"),
                PhysicalKey::Code(KeyCode::Paste) => f.write_str("Paste"),
                PhysicalKey::Code(KeyCode::Props) => f.write_str("Props"),
                PhysicalKey::Code(KeyCode::Select) => f.write_str("Select"),
                PhysicalKey::Code(KeyCode::Undo) => f.write_str("Undo"),
                PhysicalKey::Code(KeyCode::Hiragana) => f.write_str("Hiragana"),
                PhysicalKey::Code(KeyCode::Katakana) => f.write_str("Katakana"),
                PhysicalKey::Code(KeyCode::F1) => f.write_str("F1"),
                PhysicalKey::Code(KeyCode::F2) => f.write_str("F2"),
                PhysicalKey::Code(KeyCode::F3) => f.write_str("F3"),
                PhysicalKey::Code(KeyCode::F4) => f.write_str("F4"),
                PhysicalKey::Code(KeyCode::F5) => f.write_str("F5"),
                PhysicalKey::Code(KeyCode::F6) => f.write_str("F6"),
                PhysicalKey::Code(KeyCode::F7) => f.write_str("F7"),
                PhysicalKey::Code(KeyCode::F8) => f.write_str("F8"),
                PhysicalKey::Code(KeyCode::F9) => f.write_str("F9"),
                PhysicalKey::Code(KeyCode::F10) => f.write_str("F10"),
                PhysicalKey::Code(KeyCode::F11) => f.write_str("F11"),
                PhysicalKey::Code(KeyCode::F12) => f.write_str("F12"),
                PhysicalKey::Code(KeyCode::F13) => f.write_str("F13"),
                PhysicalKey::Code(KeyCode::F14) => f.write_str("F14"),
                PhysicalKey::Code(KeyCode::F15) => f.write_str("F15"),
                PhysicalKey::Code(KeyCode::F16) => f.write_str("F16"),
                PhysicalKey::Code(KeyCode::F17) => f.write_str("F17"),
                PhysicalKey::Code(KeyCode::F18) => f.write_str("F18"),
                PhysicalKey::Code(KeyCode::F19) => f.write_str("F19"),
                PhysicalKey::Code(KeyCode::F20) => f.write_str("F20"),
                PhysicalKey::Code(KeyCode::F21) => f.write_str("F21"),
                PhysicalKey::Code(KeyCode::F22) => f.write_str("F22"),
                PhysicalKey::Code(KeyCode::F23) => f.write_str("F23"),
                PhysicalKey::Code(KeyCode::F24) => f.write_str("F24"),
                PhysicalKey::Code(KeyCode::F25) => f.write_str("F25"),
                PhysicalKey::Code(KeyCode::F26) => f.write_str("F26"),
                PhysicalKey::Code(KeyCode::F27) => f.write_str("F27"),
                PhysicalKey::Code(KeyCode::F28) => f.write_str("F28"),
                PhysicalKey::Code(KeyCode::F29) => f.write_str("F29"),
                PhysicalKey::Code(KeyCode::F30) => f.write_str("F30"),
                PhysicalKey::Code(KeyCode::F31) => f.write_str("F31"),
                PhysicalKey::Code(KeyCode::F32) => f.write_str("F32"),
                PhysicalKey::Code(KeyCode::F33) => f.write_str("F33"),
                PhysicalKey::Code(KeyCode::F34) => f.write_str("F34"),
                PhysicalKey::Code(KeyCode::F35) => f.write_str("F35"),
                _ => f.write_str("Unidentified"),
            },
            Self::Pointer(B::Auxiliary) => f.write_str("MouseMiddle"),
            Self::Pointer(B::X2) => f.write_str("MouseForward"),
            Self::Pointer(B::X1) => f.write_str("MouseBackward"),
            Self::Pointer(_) => f.write_str("MouseUnimplemented"),
        }
    }
}

impl FromStr for KeyInput {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.to_lowercase();

        KeyInput::keyboard_from_str(&s)
            .map(|key| KeyInput::Keyboard(key.0, key.1))
            .or_else(|| KeyInput::mouse_from_str(&s).map(KeyInput::Pointer))
            .ok_or(())
    }
}

impl Hash for KeyInput {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            Self::Keyboard(_key, key_code) => key_code.hash(state),
            // TODO: Implement `Hash` for `druid::MouseButton`
            Self::Pointer(btn) => (*btn as u8).hash(state),
        }
    }
}

impl PartialEq for KeyInput {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (KeyInput::Keyboard(_key_a, key_code_a), KeyInput::Keyboard(_key_b, key_code_b)) => {
                key_code_a.eq(key_code_b)
            }
            (KeyInput::Pointer(a), KeyInput::Pointer(b)) => a.eq(b),
            _ => false,
        }
    }
}