wezterm_input_types/
lib.rs

1use bitflags::*;
2#[cfg(feature = "serde")]
3use serde::*;
4use std::collections::HashMap;
5use std::convert::TryFrom;
6use std::fmt::Write;
7use std::sync::atomic::AtomicBool;
8use std::sync::Arc;
9use wezterm_dynamic::{FromDynamic, ToDynamic};
10
11pub struct PixelUnit;
12pub struct ScreenPixelUnit;
13pub type Point = euclid::Point2D<isize, PixelUnit>;
14pub type PointF = euclid::Point2D<f32, PixelUnit>;
15pub type ScreenPoint = euclid::Point2D<isize, ScreenPixelUnit>;
16
17/// Which key is pressed.  Not all of these are probable to appear
18/// on most systems.  A lot of this list is @wez trawling docs and
19/// making an entry for things that might be possible in this first pass.
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, FromDynamic, ToDynamic)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub enum KeyCode {
23    /// The decoded unicode character
24    Char(char),
25    Composed(String),
26    RawCode(u32),
27    Physical(PhysKeyCode),
28
29    Hyper,
30    Super,
31    Meta,
32
33    /// Ctrl-break on windows
34    Cancel,
35    // There is no `Backspace`; use `Char('\u{8}') instead
36
37    // There is no `Tab`; use `Char('\t')` instead
38    Clear,
39    // There is no `Enter`; use `Char('\r')` instead
40    Shift,
41    // There is no `Escape`; use `Char('\u{1b}') instead
42    LeftShift,
43    RightShift,
44    Control,
45    LeftControl,
46    RightControl,
47    Alt,
48    LeftAlt,
49    RightAlt,
50    Pause,
51    CapsLock,
52    VoidSymbol,
53    PageUp,
54    PageDown,
55    End,
56    Home,
57    LeftArrow,
58    RightArrow,
59    UpArrow,
60    DownArrow,
61    Select,
62    Print,
63    Execute,
64    PrintScreen,
65    Insert,
66    // There is no `Delete`; use `Char('\u{7f}')` instead
67    Help,
68    LeftWindows,
69    RightWindows,
70    Applications,
71    Sleep,
72    /// Numeric keypad digits 0-9
73    Numpad(u8),
74    Multiply,
75    Add,
76    Separator,
77    Subtract,
78    Decimal,
79    Divide,
80    /// F1-F24 are possible
81    Function(u8),
82    NumLock,
83    ScrollLock,
84    Copy,
85    Cut,
86    Paste,
87    BrowserBack,
88    BrowserForward,
89    BrowserRefresh,
90    BrowserStop,
91    BrowserSearch,
92    BrowserFavorites,
93    BrowserHome,
94    VolumeMute,
95    VolumeDown,
96    VolumeUp,
97    MediaNextTrack,
98    MediaPrevTrack,
99    MediaStop,
100    MediaPlayPause,
101    ApplicationLeftArrow,
102    ApplicationRightArrow,
103    ApplicationUpArrow,
104    ApplicationDownArrow,
105    KeyPadHome,
106    KeyPadEnd,
107    KeyPadPageUp,
108    KeyPadPageDown,
109    KeyPadBegin,
110}
111
112impl KeyCode {
113    /// Return true if the key represents a modifier key.
114    pub fn is_modifier(&self) -> bool {
115        match self {
116            Self::Hyper
117            | Self::CapsLock
118            | Self::Super
119            | Self::Meta
120            | Self::Shift
121            | Self::LeftShift
122            | Self::RightShift
123            | Self::Control
124            | Self::LeftControl
125            | Self::RightControl
126            | Self::Alt
127            | Self::LeftAlt
128            | Self::RightAlt
129            | Self::LeftWindows
130            | Self::RightWindows => true,
131            _ => false,
132        }
133    }
134
135    pub fn normalize_shift(&self, modifiers: Modifiers) -> (KeyCode, Modifiers) {
136        normalize_shift(self.clone(), modifiers)
137    }
138
139    pub fn composed(s: &str) -> Self {
140        // Prefer to send along a single Char when the string
141        // is just a single char, as the keymapping layer cannot
142        // bind to composed key sequences
143        let mut iter = s.chars();
144        let first_char = iter.next();
145        let next_char = iter.next();
146        match (first_char, next_char) {
147            (Some(c), None) => Self::Char(c),
148            _ => Self::Composed(s.to_string()),
149        }
150    }
151
152    /// Convert to a PhysKeyCode.
153    /// Note that by the nature of PhysKeyCode being defined in terms
154    /// of a US ANSI standard layout, essentially "latinizes" the keycode,
155    /// so the results may not make as much sense for non-latin keyboards.
156    /// It also loses the shifted state of alphabetical characters.
157    pub fn to_phys(&self) -> Option<PhysKeyCode> {
158        Some(match self {
159            Self::Char('a') | Self::Char('A') => PhysKeyCode::A,
160            Self::Char('b') | Self::Char('B') => PhysKeyCode::B,
161            Self::Char('c') | Self::Char('C') => PhysKeyCode::C,
162            Self::Char('d') | Self::Char('D') => PhysKeyCode::D,
163            Self::Char('e') | Self::Char('E') => PhysKeyCode::E,
164            Self::Char('f') | Self::Char('F') => PhysKeyCode::F,
165            Self::Char('g') | Self::Char('G') => PhysKeyCode::G,
166            Self::Char('h') | Self::Char('H') => PhysKeyCode::H,
167            Self::Char('i') | Self::Char('I') => PhysKeyCode::I,
168            Self::Char('j') | Self::Char('J') => PhysKeyCode::J,
169            Self::Char('k') | Self::Char('K') => PhysKeyCode::K,
170            Self::Char('l') | Self::Char('L') => PhysKeyCode::L,
171            Self::Char('m') | Self::Char('M') => PhysKeyCode::M,
172            Self::Char('n') | Self::Char('N') => PhysKeyCode::N,
173            Self::Char('o') | Self::Char('O') => PhysKeyCode::O,
174            Self::Char('p') | Self::Char('P') => PhysKeyCode::P,
175            Self::Char('q') | Self::Char('Q') => PhysKeyCode::Q,
176            Self::Char('r') | Self::Char('R') => PhysKeyCode::R,
177            Self::Char('s') | Self::Char('S') => PhysKeyCode::S,
178            Self::Char('t') | Self::Char('T') => PhysKeyCode::T,
179            Self::Char('u') | Self::Char('U') => PhysKeyCode::U,
180            Self::Char('v') | Self::Char('V') => PhysKeyCode::V,
181            Self::Char('w') | Self::Char('W') => PhysKeyCode::W,
182            Self::Char('x') | Self::Char('X') => PhysKeyCode::X,
183            Self::Char('y') | Self::Char('Y') => PhysKeyCode::Y,
184            Self::Char('z') | Self::Char('Z') => PhysKeyCode::Z,
185            Self::Char('0') => PhysKeyCode::K0,
186            Self::Char('1') => PhysKeyCode::K1,
187            Self::Char('2') => PhysKeyCode::K2,
188            Self::Char('3') => PhysKeyCode::K3,
189            Self::Char('4') => PhysKeyCode::K4,
190            Self::Char('5') => PhysKeyCode::K5,
191            Self::Char('6') => PhysKeyCode::K6,
192            Self::Char('7') => PhysKeyCode::K7,
193            Self::Char('8') => PhysKeyCode::K8,
194            Self::Char('9') => PhysKeyCode::K9,
195            Self::Char('\\') => PhysKeyCode::Backslash,
196            Self::Char(',') => PhysKeyCode::Comma,
197            Self::Char('\u{8}') => PhysKeyCode::Backspace,
198            Self::Char('\u{7f}') => PhysKeyCode::Delete,
199            Self::Char('=') => PhysKeyCode::Equal,
200            Self::Char('\u{1b}') => PhysKeyCode::Escape,
201            Self::Char('`') => PhysKeyCode::Grave,
202            Self::Char('\r') => PhysKeyCode::Return,
203            Self::Char('[') => PhysKeyCode::LeftBracket,
204            Self::Char(']') => PhysKeyCode::RightBracket,
205            Self::Char('-') => PhysKeyCode::Minus,
206            Self::Char('.') => PhysKeyCode::Period,
207            Self::Char('\'') => PhysKeyCode::Quote,
208            Self::Char(';') => PhysKeyCode::Semicolon,
209            Self::Char('/') => PhysKeyCode::Slash,
210            Self::Char(' ') => PhysKeyCode::Space,
211            Self::Char('\t') => PhysKeyCode::Tab,
212            Self::Numpad(0) => PhysKeyCode::Keypad0,
213            Self::Numpad(1) => PhysKeyCode::Keypad1,
214            Self::Numpad(2) => PhysKeyCode::Keypad2,
215            Self::Numpad(3) => PhysKeyCode::Keypad3,
216            Self::Numpad(4) => PhysKeyCode::Keypad4,
217            Self::Numpad(5) => PhysKeyCode::Keypad5,
218            Self::Numpad(6) => PhysKeyCode::Keypad6,
219            Self::Numpad(7) => PhysKeyCode::Keypad7,
220            Self::Numpad(8) => PhysKeyCode::Keypad8,
221            Self::Numpad(9) => PhysKeyCode::Keypad9,
222            Self::Function(1) => PhysKeyCode::F1,
223            Self::Function(2) => PhysKeyCode::F2,
224            Self::Function(3) => PhysKeyCode::F3,
225            Self::Function(4) => PhysKeyCode::F4,
226            Self::Function(5) => PhysKeyCode::F5,
227            Self::Function(6) => PhysKeyCode::F6,
228            Self::Function(7) => PhysKeyCode::F7,
229            Self::Function(8) => PhysKeyCode::F8,
230            Self::Function(9) => PhysKeyCode::F9,
231            Self::Function(10) => PhysKeyCode::F10,
232            Self::Function(11) => PhysKeyCode::F11,
233            Self::Function(12) => PhysKeyCode::F12,
234            Self::Function(13) => PhysKeyCode::F13,
235            Self::Function(14) => PhysKeyCode::F14,
236            Self::Function(15) => PhysKeyCode::F15,
237            Self::Function(16) => PhysKeyCode::F16,
238            Self::Function(17) => PhysKeyCode::F17,
239            Self::Function(18) => PhysKeyCode::F18,
240            Self::Function(19) => PhysKeyCode::F19,
241            Self::Function(20) => PhysKeyCode::F20,
242            Self::Physical(p) => *p,
243            Self::Shift | Self::LeftShift => PhysKeyCode::LeftShift,
244            Self::RightShift => PhysKeyCode::RightShift,
245            Self::Alt | Self::LeftAlt => PhysKeyCode::LeftAlt,
246            Self::RightAlt => PhysKeyCode::RightAlt,
247            Self::LeftWindows => PhysKeyCode::LeftWindows,
248            Self::RightWindows => PhysKeyCode::RightWindows,
249            Self::Control | Self::LeftControl => PhysKeyCode::LeftControl,
250            Self::RightControl => PhysKeyCode::RightControl,
251            Self::CapsLock => PhysKeyCode::CapsLock,
252            Self::PageUp => PhysKeyCode::PageUp,
253            Self::PageDown => PhysKeyCode::PageDown,
254            Self::Home => PhysKeyCode::Home,
255            Self::End => PhysKeyCode::End,
256            Self::LeftArrow => PhysKeyCode::LeftArrow,
257            Self::RightArrow => PhysKeyCode::RightArrow,
258            Self::UpArrow => PhysKeyCode::UpArrow,
259            Self::DownArrow => PhysKeyCode::DownArrow,
260            Self::Insert => PhysKeyCode::Insert,
261            Self::Help => PhysKeyCode::Help,
262            Self::Multiply => PhysKeyCode::KeypadMultiply,
263            Self::Clear => PhysKeyCode::KeypadClear,
264            Self::Decimal => PhysKeyCode::KeypadDecimal,
265            Self::Divide => PhysKeyCode::KeypadDivide,
266            Self::Add => PhysKeyCode::KeypadAdd,
267            Self::Subtract => PhysKeyCode::KeypadSubtract,
268            Self::NumLock => PhysKeyCode::NumLock,
269            Self::VolumeUp => PhysKeyCode::VolumeUp,
270            Self::VolumeDown => PhysKeyCode::VolumeDown,
271            Self::VolumeMute => PhysKeyCode::VolumeMute,
272            Self::ApplicationLeftArrow
273            | Self::ApplicationRightArrow
274            | Self::ApplicationUpArrow
275            | Self::ApplicationDownArrow
276            | Self::KeyPadHome
277            | Self::KeyPadEnd
278            | Self::KeyPadPageUp
279            | Self::KeyPadPageDown
280            | Self::KeyPadBegin
281            | Self::MediaNextTrack
282            | Self::MediaPrevTrack
283            | Self::MediaStop
284            | Self::MediaPlayPause
285            | Self::Copy
286            | Self::Cut
287            | Self::Paste
288            | Self::BrowserBack
289            | Self::BrowserForward
290            | Self::BrowserRefresh
291            | Self::BrowserStop
292            | Self::BrowserSearch
293            | Self::BrowserFavorites
294            | Self::BrowserHome
295            | Self::ScrollLock
296            | Self::Separator
297            | Self::Sleep
298            | Self::Applications
299            | Self::Execute
300            | Self::PrintScreen
301            | Self::Print
302            | Self::Select
303            | Self::VoidSymbol
304            | Self::Pause
305            | Self::Cancel
306            | Self::Hyper
307            | Self::Super
308            | Self::Meta
309            | Self::Composed(_)
310            | Self::RawCode(_)
311            | Self::Char(_)
312            | Self::Numpad(_)
313            | Self::Function(_) => return None,
314        })
315    }
316}
317
318impl TryFrom<&str> for KeyCode {
319    type Error = String;
320    fn try_from(s: &str) -> std::result::Result<Self, String> {
321        macro_rules! m {
322            ($($val:ident),* $(,)?) => {
323                match s {
324                $(
325                    stringify!($val) => return Ok(Self::$val),
326                )*
327                    _ => {}
328                }
329            }
330        }
331
332        m!(
333            Hyper,
334            Super,
335            Meta,
336            Cancel,
337            Clear,
338            Shift,
339            LeftShift,
340            RightShift,
341            Control,
342            LeftControl,
343            RightControl,
344            Alt,
345            LeftAlt,
346            RightAlt,
347            Pause,
348            CapsLock,
349            VoidSymbol,
350            PageUp,
351            PageDown,
352            End,
353            Home,
354            LeftArrow,
355            RightArrow,
356            UpArrow,
357            DownArrow,
358            Select,
359            Print,
360            Execute,
361            PrintScreen,
362            Insert,
363            Help,
364            LeftWindows,
365            RightWindows,
366            Applications,
367            Sleep,
368            Multiply,
369            Add,
370            Separator,
371            Subtract,
372            Decimal,
373            Divide,
374            NumLock,
375            ScrollLock,
376            Copy,
377            Cut,
378            Paste,
379            BrowserBack,
380            BrowserForward,
381            BrowserRefresh,
382            BrowserStop,
383            BrowserSearch,
384            BrowserFavorites,
385            BrowserHome,
386            VolumeMute,
387            VolumeDown,
388            VolumeUp,
389            MediaNextTrack,
390            MediaPrevTrack,
391            MediaStop,
392            MediaPlayPause,
393            ApplicationLeftArrow,
394            ApplicationRightArrow,
395            ApplicationUpArrow,
396            ApplicationDownArrow,
397        );
398
399        match s {
400            "Backspace" => return Ok(KeyCode::Char('\u{8}')),
401            "Tab" => return Ok(KeyCode::Char('\t')),
402            "Return" | "Enter" => return Ok(KeyCode::Char('\r')),
403            "Escape" => return Ok(KeyCode::Char('\u{1b}')),
404            "Delete" => return Ok(KeyCode::Char('\u{7f}')),
405            _ => {}
406        };
407
408        if let Some(n) = s.strip_prefix("Numpad") {
409            let n: u8 = n
410                .parse()
411                .map_err(|err| format!("parsing Numpad<NUMBER>: {:#}", err))?;
412            if n > 9 {
413                return Err("Numpad numbers must be in range 0-9".to_string());
414            }
415            return Ok(KeyCode::Numpad(n));
416        }
417
418        // Don't consider "F" to be an invalid F key!
419        if s.len() > 1 {
420            if let Some(n) = s.strip_prefix("F") {
421                let n: u8 = n
422                    .parse()
423                    .map_err(|err| format!("parsing F<NUMBER>: {:#}", err))?;
424                if n == 0 || n > 24 {
425                    return Err("Function key numbers must be in range 1-24".to_string());
426                }
427                return Ok(KeyCode::Function(n));
428            }
429        }
430
431        let chars: Vec<char> = s.chars().collect();
432        if chars.len() == 1 {
433            let k = KeyCode::Char(chars[0]);
434            Ok(k)
435        } else {
436            Err(format!("invalid KeyCode string {}", s))
437        }
438    }
439}
440
441impl ToString for KeyCode {
442    fn to_string(&self) -> String {
443        match self {
444            Self::RawCode(n) => format!("raw:{}", n),
445            Self::Char(c) => format!("mapped:{}", c),
446            Self::Physical(phys) => phys.to_string(),
447            Self::Composed(s) => s.to_string(),
448            Self::Numpad(n) => format!("Numpad{}", n),
449            Self::Function(n) => format!("F{}", n),
450            other => format!("{:?}", other),
451        }
452    }
453}
454
455bitflags! {
456    #[derive(Default, FromDynamic, ToDynamic)]
457    pub struct KeyboardLedStatus: u8 {
458        const CAPS_LOCK = 1<<1;
459        const NUM_LOCK = 1<<2;
460    }
461}
462
463impl ToString for KeyboardLedStatus {
464    fn to_string(&self) -> String {
465        let mut s = String::new();
466        if self.contains(Self::CAPS_LOCK) {
467            s.push_str("CAPS_LOCK");
468        }
469        if self.contains(Self::NUM_LOCK) {
470            if !s.is_empty() {
471                s.push('|');
472            }
473            s.push_str("NUM_LOCK");
474        }
475        s
476    }
477}
478
479bitflags! {
480    #[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
481    #[derive(Default, FromDynamic, ToDynamic)]
482    #[dynamic(into="String", try_from="String")]
483    pub struct Modifiers: u16 {
484        const NONE = 0;
485        const SHIFT = 1<<1;
486        const ALT = 1<<2;
487        const CTRL = 1<<3;
488        const SUPER = 1<<4;
489        const LEFT_ALT = 1<<5;
490        const RIGHT_ALT = 1<<6;
491        /// This is a virtual modifier used by wezterm
492        const LEADER = 1<<7;
493        const LEFT_CTRL = 1<<8;
494        const RIGHT_CTRL = 1<<9;
495        const LEFT_SHIFT = 1<<10;
496        const RIGHT_SHIFT = 1<<11;
497        const ENHANCED_KEY = 1<<12;
498    }
499}
500
501impl TryFrom<String> for Modifiers {
502    type Error = String;
503
504    fn try_from(s: String) -> Result<Modifiers, String> {
505        let mut mods = Modifiers::NONE;
506        for ele in s.split('|') {
507            // Allow for whitespace; debug printing Modifiers includes spaces
508            // around the `|` so it is desirable to be able to reverse that
509            // encoding here.
510            let ele = ele.trim();
511            if ele == "SHIFT" {
512                mods |= Modifiers::SHIFT;
513            } else if ele == "ALT" || ele == "OPT" || ele == "META" {
514                mods |= Modifiers::ALT;
515            } else if ele == "CTRL" {
516                mods |= Modifiers::CTRL;
517            } else if ele == "SUPER" || ele == "CMD" || ele == "WIN" {
518                mods |= Modifiers::SUPER;
519            } else if ele == "LEADER" {
520                mods |= Modifiers::LEADER;
521            } else if ele == "NONE" || ele == "" {
522                mods |= Modifiers::NONE;
523            } else {
524                return Err(format!("invalid modifier name {} in {}", ele, s));
525            }
526        }
527        Ok(mods)
528    }
529}
530
531impl From<&Modifiers> for String {
532    fn from(val: &Modifiers) -> Self {
533        val.to_string()
534    }
535}
536
537pub struct ModifierToStringArgs<'a> {
538    /// How to join two modifier keys. Can be empty.
539    pub separator: &'a str,
540    /// Whether to output NONE when no modifiers are present
541    pub want_none: bool,
542    /// How to render the keycaps for the UI
543    pub ui_key_cap_rendering: Option<UIKeyCapRendering>,
544}
545
546impl Modifiers {
547    pub fn encode_xterm(self) -> u8 {
548        let mut number = 0;
549        if self.contains(Self::SHIFT) {
550            number |= 1;
551        }
552        if self.contains(Self::ALT) {
553            number |= 2;
554        }
555        if self.contains(Self::CTRL) {
556            number |= 4;
557        }
558        number
559    }
560
561    #[allow(non_upper_case_globals)]
562    pub fn to_string_with_separator(&self, args: ModifierToStringArgs) -> String {
563        let mut s = String::new();
564        if args.want_none && *self == Self::NONE {
565            s.push_str("NONE");
566        }
567
568        // The unicode escapes here are nerdfont symbols; we use those because
569        // we're guaranteed to have them available, and the symbols are
570        // very legible
571        const md_apple_keyboard_command: &str = "\u{f0633}"; // 󰘳
572        const md_apple_keyboard_control: &str = "\u{f0634}"; // 󰘴
573        const md_apple_keyboard_option: &str = "\u{f0635}"; // 󰘵
574        const md_apple_keyboard_shift: &str = "\u{f0636}"; // 󰘶
575        const md_microsoft_windows: &str = "\u{f05b3}"; // 󰖳
576
577        for (value, label, unix, emacs, apple, windows, win_sym) in [
578            (
579                Self::SHIFT,
580                "SHIFT",
581                "Shift",
582                "S",
583                md_apple_keyboard_shift,
584                "Shift",
585                "Shift",
586            ),
587            (
588                Self::ALT,
589                "ALT",
590                "Alt",
591                "M",
592                md_apple_keyboard_option,
593                "Alt",
594                "Alt",
595            ),
596            (
597                Self::CTRL,
598                "CTRL",
599                "Ctrl",
600                "C",
601                md_apple_keyboard_control,
602                "Ctrl",
603                "Ctrl",
604            ),
605            (
606                Self::SUPER,
607                "SUPER",
608                "Super",
609                "Super",
610                md_apple_keyboard_command,
611                "Win",
612                md_microsoft_windows,
613            ),
614            (
615                Self::LEFT_ALT,
616                "LEFT_ALT",
617                "Alt",
618                "M",
619                md_apple_keyboard_option,
620                "Alt",
621                "Alt",
622            ),
623            (
624                Self::RIGHT_ALT,
625                "RIGHT_ALT",
626                "Alt",
627                "M",
628                md_apple_keyboard_option,
629                "Alt",
630                "Alt",
631            ),
632            (
633                Self::LEADER,
634                "LEADER",
635                "Leader",
636                "Leader",
637                "Leader",
638                "Leader",
639                "Leader",
640            ),
641            (
642                Self::LEFT_CTRL,
643                "LEFT_CTRL",
644                "Ctrl",
645                "C",
646                md_apple_keyboard_control,
647                "Ctrl",
648                "Ctrl",
649            ),
650            (
651                Self::RIGHT_CTRL,
652                "RIGHT_CTRL",
653                "Ctrl",
654                "C",
655                md_apple_keyboard_control,
656                "Ctrl",
657                "Ctrl",
658            ),
659            (
660                Self::LEFT_SHIFT,
661                "LEFT_SHIFT",
662                "Shift",
663                "S",
664                md_apple_keyboard_shift,
665                "Shift",
666                "Shift",
667            ),
668            (
669                Self::RIGHT_SHIFT,
670                "RIGHT_SHIFT",
671                "Shift",
672                "S",
673                md_apple_keyboard_shift,
674                "Shift",
675                "Shift",
676            ),
677            (
678                Self::ENHANCED_KEY,
679                "ENHANCED_KEY",
680                "ENHANCED_KEY",
681                "ENHANCED_KEY",
682                "ENHANCED_KEY",
683                "ENHANCED_KEY",
684                "ENHANCED_KEY",
685            ),
686        ] {
687            if !self.contains(value) {
688                continue;
689            }
690            if !s.is_empty() {
691                s.push_str(args.separator);
692            }
693            s.push_str(match args.ui_key_cap_rendering {
694                Some(UIKeyCapRendering::UnixLong) => unix,
695                Some(UIKeyCapRendering::Emacs) => emacs,
696                Some(UIKeyCapRendering::AppleSymbols) => apple,
697                Some(UIKeyCapRendering::WindowsLong) => windows,
698                Some(UIKeyCapRendering::WindowsSymbols) => win_sym,
699                None => label,
700            });
701        }
702
703        s
704    }
705}
706
707impl ToString for Modifiers {
708    fn to_string(&self) -> String {
709        self.to_string_with_separator(ModifierToStringArgs {
710            separator: "|",
711            want_none: true,
712            ui_key_cap_rendering: None,
713        })
714    }
715}
716
717impl Modifiers {
718    /// Remove positional and other "supplemental" bits that
719    /// are used to carry around implementation details, but that
720    /// are not bits that should be matched when matching key
721    /// assignments.
722    pub fn remove_positional_mods(self) -> Self {
723        self - (Self::LEFT_ALT
724            | Self::RIGHT_ALT
725            | Self::LEFT_CTRL
726            | Self::RIGHT_CTRL
727            | Self::LEFT_SHIFT
728            | Self::RIGHT_SHIFT
729            | Self::ENHANCED_KEY)
730    }
731}
732
733/// These keycodes identify keys based on their physical
734/// position on an ANSI-standard US keyboard.
735#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd, FromDynamic, ToDynamic)]
736#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
737pub enum PhysKeyCode {
738    A,
739    B,
740    Backslash,
741    C,
742    CapsLock,
743    Comma,
744    D,
745    Backspace,
746    DownArrow,
747    E,
748    End,
749    Equal,
750    Escape,
751    F,
752    F1,
753    F10,
754    F11,
755    F12,
756    F13,
757    F14,
758    F15,
759    F16,
760    F17,
761    F18,
762    F19,
763    F2,
764    F20,
765    F21,
766    F22,
767    F23,
768    F24,
769    F3,
770    F4,
771    F5,
772    F6,
773    F7,
774    F8,
775    F9,
776    Delete,
777    Function,
778    G,
779    Grave,
780    H,
781    Help,
782    Home,
783    I,
784    Insert,
785    J,
786    K,
787    K0,
788    K1,
789    K2,
790    K3,
791    K4,
792    K5,
793    K6,
794    K7,
795    K8,
796    K9,
797    Keypad0,
798    Keypad1,
799    Keypad2,
800    Keypad3,
801    Keypad4,
802    Keypad5,
803    Keypad6,
804    Keypad7,
805    Keypad8,
806    Keypad9,
807    KeypadClear,
808    KeypadDecimal,
809    KeypadDelete,
810    KeypadDivide,
811    KeypadEnter,
812    KeypadEquals,
813    KeypadSubtract,
814    KeypadMultiply,
815    KeypadAdd,
816    L,
817    LeftAlt,
818    LeftArrow,
819    LeftBracket,
820    LeftControl,
821    LeftShift,
822    LeftWindows,
823    M,
824    Minus,
825    VolumeMute,
826    N,
827    NumLock,
828    O,
829    P,
830    PageDown,
831    PageUp,
832    Period,
833    Q,
834    Quote,
835    R,
836    Return,
837    RightAlt,
838    RightArrow,
839    RightBracket,
840    RightControl,
841    RightShift,
842    RightWindows,
843    S,
844    Semicolon,
845    Slash,
846    Space,
847    T,
848    Tab,
849    U,
850    UpArrow,
851    V,
852    VolumeDown,
853    VolumeUp,
854    W,
855    X,
856    Y,
857    Z,
858}
859
860impl PhysKeyCode {
861    pub fn is_modifier(&self) -> bool {
862        match self {
863            Self::LeftShift
864            | Self::LeftControl
865            | Self::LeftWindows
866            | Self::LeftAlt
867            | Self::RightShift
868            | Self::RightControl
869            | Self::RightWindows
870            | Self::RightAlt => true,
871            _ => false,
872        }
873    }
874
875    pub fn to_key_code(self) -> KeyCode {
876        match self {
877            Self::LeftShift => KeyCode::LeftShift,
878            Self::LeftControl => KeyCode::LeftControl,
879            Self::LeftWindows => KeyCode::LeftWindows,
880            Self::LeftAlt => KeyCode::LeftAlt,
881            Self::RightShift => KeyCode::RightShift,
882            Self::RightControl => KeyCode::RightControl,
883            Self::RightWindows => KeyCode::RightWindows,
884            Self::RightAlt => KeyCode::RightAlt,
885            Self::LeftArrow => KeyCode::LeftArrow,
886            Self::RightArrow => KeyCode::RightArrow,
887            Self::UpArrow => KeyCode::UpArrow,
888            Self::DownArrow => KeyCode::DownArrow,
889            Self::CapsLock => KeyCode::CapsLock,
890            Self::F1 => KeyCode::Function(1),
891            Self::F2 => KeyCode::Function(2),
892            Self::F3 => KeyCode::Function(3),
893            Self::F4 => KeyCode::Function(4),
894            Self::F5 => KeyCode::Function(5),
895            Self::F6 => KeyCode::Function(6),
896            Self::F7 => KeyCode::Function(7),
897            Self::F8 => KeyCode::Function(8),
898            Self::F9 => KeyCode::Function(9),
899            Self::F10 => KeyCode::Function(10),
900            Self::F11 => KeyCode::Function(11),
901            Self::F12 => KeyCode::Function(12),
902            Self::F13 => KeyCode::Function(13),
903            Self::F14 => KeyCode::Function(14),
904            Self::F15 => KeyCode::Function(15),
905            Self::F16 => KeyCode::Function(16),
906            Self::F17 => KeyCode::Function(17),
907            Self::F18 => KeyCode::Function(18),
908            Self::F19 => KeyCode::Function(19),
909            Self::F20 => KeyCode::Function(20),
910            Self::F21 => KeyCode::Function(21),
911            Self::F22 => KeyCode::Function(22),
912            Self::F23 => KeyCode::Function(23),
913            Self::F24 => KeyCode::Function(24),
914            Self::Keypad0 => KeyCode::Numpad(0),
915            Self::Keypad1 => KeyCode::Numpad(1),
916            Self::Keypad2 => KeyCode::Numpad(2),
917            Self::Keypad3 => KeyCode::Numpad(3),
918            Self::Keypad4 => KeyCode::Numpad(4),
919            Self::Keypad5 => KeyCode::Numpad(5),
920            Self::Keypad6 => KeyCode::Numpad(6),
921            Self::Keypad7 => KeyCode::Numpad(7),
922            Self::Keypad8 => KeyCode::Numpad(8),
923            Self::Keypad9 => KeyCode::Numpad(9),
924            Self::KeypadClear => KeyCode::Clear,
925            Self::KeypadMultiply => KeyCode::Multiply,
926            Self::KeypadDecimal => KeyCode::Decimal,
927            Self::KeypadDivide => KeyCode::Divide,
928            Self::KeypadAdd => KeyCode::Add,
929            Self::KeypadSubtract => KeyCode::Subtract,
930            Self::A => KeyCode::Char('a'),
931            Self::B => KeyCode::Char('b'),
932            Self::C => KeyCode::Char('c'),
933            Self::D => KeyCode::Char('d'),
934            Self::E => KeyCode::Char('e'),
935            Self::F => KeyCode::Char('f'),
936            Self::G => KeyCode::Char('g'),
937            Self::H => KeyCode::Char('h'),
938            Self::I => KeyCode::Char('i'),
939            Self::J => KeyCode::Char('j'),
940            Self::K => KeyCode::Char('k'),
941            Self::L => KeyCode::Char('l'),
942            Self::M => KeyCode::Char('m'),
943            Self::N => KeyCode::Char('n'),
944            Self::O => KeyCode::Char('o'),
945            Self::P => KeyCode::Char('p'),
946            Self::Q => KeyCode::Char('q'),
947            Self::R => KeyCode::Char('r'),
948            Self::S => KeyCode::Char('s'),
949            Self::T => KeyCode::Char('t'),
950            Self::U => KeyCode::Char('u'),
951            Self::V => KeyCode::Char('v'),
952            Self::W => KeyCode::Char('w'),
953            Self::X => KeyCode::Char('x'),
954            Self::Y => KeyCode::Char('y'),
955            Self::Z => KeyCode::Char('z'),
956            Self::Backslash => KeyCode::Char('\\'),
957            Self::Comma => KeyCode::Char(','),
958            Self::Backspace => KeyCode::Char('\u{8}'),
959            Self::KeypadDelete | Self::Delete => KeyCode::Char('\u{7f}'),
960            Self::End => KeyCode::End,
961            Self::Home => KeyCode::Home,
962            Self::KeypadEquals | Self::Equal => KeyCode::Char('='),
963            Self::Escape => KeyCode::Char('\u{1b}'),
964            Self::Function => KeyCode::Physical(self),
965            Self::Grave => KeyCode::Char('`'),
966            Self::Help => KeyCode::Help,
967            Self::Insert => KeyCode::Insert,
968            Self::K0 => KeyCode::Char('0'),
969            Self::K1 => KeyCode::Char('1'),
970            Self::K2 => KeyCode::Char('2'),
971            Self::K3 => KeyCode::Char('3'),
972            Self::K4 => KeyCode::Char('4'),
973            Self::K5 => KeyCode::Char('5'),
974            Self::K6 => KeyCode::Char('6'),
975            Self::K7 => KeyCode::Char('7'),
976            Self::K8 => KeyCode::Char('8'),
977            Self::K9 => KeyCode::Char('9'),
978            Self::Return | Self::KeypadEnter => KeyCode::Char('\r'),
979            Self::LeftBracket => KeyCode::Char('['),
980            Self::RightBracket => KeyCode::Char(']'),
981            Self::Minus => KeyCode::Char('-'),
982            Self::VolumeMute => KeyCode::VolumeMute,
983            Self::VolumeUp => KeyCode::VolumeUp,
984            Self::VolumeDown => KeyCode::VolumeDown,
985            Self::NumLock => KeyCode::NumLock,
986            Self::PageUp => KeyCode::PageUp,
987            Self::PageDown => KeyCode::PageDown,
988            Self::Period => KeyCode::Char('.'),
989            Self::Quote => KeyCode::Char('\''),
990            Self::Semicolon => KeyCode::Char(';'),
991            Self::Slash => KeyCode::Char('/'),
992            Self::Space => KeyCode::Char(' '),
993            Self::Tab => KeyCode::Char('\t'),
994        }
995    }
996
997    fn make_map() -> HashMap<String, Self> {
998        let mut map = HashMap::new();
999
1000        macro_rules! m {
1001            ($($val:ident),* $(,)?) => {
1002                $(
1003                    let key = stringify!($val).to_string();
1004                    if key.len() == 1 {
1005                        map.insert(key.to_ascii_lowercase(), PhysKeyCode::$val);
1006                    }
1007                    map.insert(key, PhysKeyCode::$val);
1008                )*
1009            }
1010        }
1011
1012        m!(
1013            A,
1014            B,
1015            Backslash,
1016            C,
1017            CapsLock,
1018            Comma,
1019            D,
1020            Backspace,
1021            DownArrow,
1022            E,
1023            End,
1024            Equal,
1025            Escape,
1026            F,
1027            F1,
1028            F10,
1029            F11,
1030            F12,
1031            F13,
1032            F14,
1033            F15,
1034            F16,
1035            F17,
1036            F18,
1037            F19,
1038            F2,
1039            F20,
1040            F3,
1041            F4,
1042            F5,
1043            F6,
1044            F7,
1045            F8,
1046            F9,
1047            Delete,
1048            Function,
1049            G,
1050            Grave,
1051            H,
1052            Help,
1053            Home,
1054            I,
1055            Insert,
1056            J,
1057            K,
1058            Keypad0,
1059            Keypad1,
1060            Keypad2,
1061            Keypad3,
1062            Keypad4,
1063            Keypad5,
1064            Keypad6,
1065            Keypad7,
1066            Keypad8,
1067            Keypad9,
1068            KeypadClear,
1069            KeypadDecimal,
1070            KeypadDelete,
1071            KeypadDivide,
1072            KeypadEnter,
1073            KeypadEquals,
1074            KeypadSubtract,
1075            KeypadMultiply,
1076            KeypadAdd,
1077            L,
1078            LeftAlt,
1079            LeftArrow,
1080            LeftBracket,
1081            LeftControl,
1082            LeftShift,
1083            LeftWindows,
1084            M,
1085            Minus,
1086            VolumeMute,
1087            N,
1088            NumLock,
1089            O,
1090            P,
1091            PageDown,
1092            PageUp,
1093            Period,
1094            Q,
1095            Quote,
1096            R,
1097            Return,
1098            RightAlt,
1099            RightArrow,
1100            RightBracket,
1101            RightControl,
1102            RightShift,
1103            RightWindows,
1104            S,
1105            Semicolon,
1106            Slash,
1107            Space,
1108            T,
1109            Tab,
1110            U,
1111            UpArrow,
1112            V,
1113            VolumeDown,
1114            VolumeUp,
1115            W,
1116            X,
1117            Y,
1118            Z,
1119        );
1120
1121        map.insert("0".to_string(), PhysKeyCode::K0);
1122        map.insert("1".to_string(), PhysKeyCode::K1);
1123        map.insert("2".to_string(), PhysKeyCode::K2);
1124        map.insert("3".to_string(), PhysKeyCode::K3);
1125        map.insert("4".to_string(), PhysKeyCode::K4);
1126        map.insert("5".to_string(), PhysKeyCode::K5);
1127        map.insert("6".to_string(), PhysKeyCode::K6);
1128        map.insert("7".to_string(), PhysKeyCode::K7);
1129        map.insert("8".to_string(), PhysKeyCode::K8);
1130        map.insert("9".to_string(), PhysKeyCode::K9);
1131
1132        map
1133    }
1134
1135    fn make_inv_map() -> HashMap<Self, String> {
1136        let mut map = HashMap::new();
1137        for (k, v) in PHYSKEYCODE_MAP.iter() {
1138            map.insert(*v, k.clone());
1139        }
1140        map
1141    }
1142}
1143
1144lazy_static::lazy_static! {
1145    static ref PHYSKEYCODE_MAP: HashMap<String, PhysKeyCode> = PhysKeyCode::make_map();
1146    static ref INV_PHYSKEYCODE_MAP: HashMap<PhysKeyCode, String> = PhysKeyCode::make_inv_map();
1147}
1148
1149impl TryFrom<&str> for PhysKeyCode {
1150    type Error = String;
1151    fn try_from(s: &str) -> std::result::Result<PhysKeyCode, String> {
1152        if let Some(code) = PHYSKEYCODE_MAP.get(s) {
1153            Ok(*code)
1154        } else {
1155            Err(format!("invalid PhysKeyCode '{}'", s))
1156        }
1157    }
1158}
1159
1160impl ToString for PhysKeyCode {
1161    fn to_string(&self) -> String {
1162        if let Some(s) = INV_PHYSKEYCODE_MAP.get(self) {
1163            s.to_string()
1164        } else {
1165            format!("{:?}", self)
1166        }
1167    }
1168}
1169
1170bitflags! {
1171    #[derive(Default)]
1172    pub struct MouseButtons: u8 {
1173        const NONE = 0;
1174        #[allow(clippy::identity_op)]
1175        const LEFT = 1<<0;
1176        const RIGHT = 1<<1;
1177        const MIDDLE = 1<<2;
1178        const X1 = 1<<3;
1179        const X2 = 1<<4;
1180    }
1181}
1182
1183#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1184pub enum MousePress {
1185    Left,
1186    Right,
1187    Middle,
1188}
1189
1190#[derive(Debug, Clone, PartialEq, Eq)]
1191pub enum MouseEventKind {
1192    Move,
1193    Press(MousePress),
1194    Release(MousePress),
1195    VertWheel(i16),
1196    HorzWheel(i16),
1197}
1198
1199#[derive(Debug, Clone, PartialEq, Eq)]
1200pub struct MouseEvent {
1201    pub kind: MouseEventKind,
1202    /// Coordinates of the mouse relative to the top left of the window
1203    pub coords: Point,
1204    /// The mouse position in screen coordinates
1205    pub screen_coords: crate::ScreenPoint,
1206    pub mouse_buttons: MouseButtons,
1207    pub modifiers: Modifiers,
1208}
1209
1210#[derive(Debug, Clone)]
1211pub struct Handled(Arc<AtomicBool>);
1212
1213impl Handled {
1214    pub fn new() -> Self {
1215        Self(Arc::new(AtomicBool::new(false)))
1216    }
1217
1218    pub fn set_handled(&self) {
1219        self.0.store(true, std::sync::atomic::Ordering::Relaxed);
1220    }
1221
1222    pub fn is_handled(&self) -> bool {
1223        self.0.load(std::sync::atomic::Ordering::Relaxed)
1224    }
1225}
1226
1227impl PartialEq for Handled {
1228    fn eq(&self, _: &Self) -> bool {
1229        true
1230    }
1231}
1232
1233impl Eq for Handled {}
1234
1235/// A key event prior to any dead key or IME composition
1236#[derive(Debug, Clone, Eq, PartialEq)]
1237pub struct RawKeyEvent {
1238    pub key: KeyCode,
1239    pub modifiers: Modifiers,
1240    pub leds: KeyboardLedStatus,
1241
1242    /// The physical location of the key on an ANSI-Standard US layout
1243    pub phys_code: Option<PhysKeyCode>,
1244    /// The OS and hardware dependent key code for the key
1245    pub raw_code: u32,
1246
1247    /// The *other* OS and hardware dependent key code for the key
1248    #[cfg(windows)]
1249    pub scan_code: u32,
1250
1251    /// How many times this key repeats
1252    pub repeat_count: u16,
1253
1254    /// If true, this is a key down rather than a key up event
1255    pub key_is_down: bool,
1256    pub handled: Handled,
1257}
1258
1259impl RawKeyEvent {
1260    /// Mark the event as handled, in order to prevent additional
1261    /// processing.
1262    pub fn set_handled(&self) {
1263        self.handled.set_handled();
1264    }
1265
1266    /// <https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions>
1267    #[deny(warnings)]
1268    fn kitty_function_code(&self) -> Option<u32> {
1269        use KeyCode::*;
1270        Some(match self.key {
1271            // Tab => 9,
1272            // Backspace => 127,
1273            // CapsLock => 57358,
1274            // ScrollLock => 57359,
1275            // NumLock => 57360,
1276            // PrintScreen => 57361,
1277            // Pause => 57362,
1278            // Menu => 57363,
1279            Function(n) if n >= 13 && n <= 35 => 57376 + n as u32 - 13,
1280            Numpad(n) => n as u32 + 57399,
1281            Decimal => 57409,
1282            Divide => 57410,
1283            Multiply => 57411,
1284            Subtract => 57412,
1285            Add => 57413,
1286            // KeypadEnter => 57414,
1287            // KeypadEquals => 57415,
1288            Separator => 57416,
1289            ApplicationLeftArrow => 57417,
1290            ApplicationRightArrow => 57418,
1291            ApplicationUpArrow => 57419,
1292            ApplicationDownArrow => 57420,
1293            KeyPadHome => 57423,
1294            KeyPadEnd => 57424,
1295            KeyPadBegin => 57427,
1296            KeyPadPageUp => 57421,
1297            KeyPadPageDown => 57422,
1298            Insert => 57425,
1299            // KeypadDelete => 57426,
1300            MediaPlayPause => 57430,
1301            MediaStop => 57432,
1302            MediaNextTrack => 57435,
1303            MediaPrevTrack => 57436,
1304            VolumeDown => 57436,
1305            VolumeUp => 57439,
1306            VolumeMute => 57440,
1307            LeftShift => 57441,
1308            LeftControl => 57442,
1309            LeftAlt => 57443,
1310            LeftWindows => 57444,
1311            RightShift => 57447,
1312            RightControl => 57448,
1313            RightAlt => 57449,
1314            RightWindows => 57450,
1315            _ => match &self.phys_code {
1316                Some(phys) => {
1317                    use PhysKeyCode::*;
1318
1319                    match *phys {
1320                        Escape => 27,
1321                        Return => 13,
1322                        Tab => 9,
1323                        Backspace => 127,
1324                        CapsLock => 57358,
1325                        // ScrollLock => 57359,
1326                        NumLock => 57360,
1327                        // PrintScreen => 57361,
1328                        // Pause => 57362,
1329                        // Menu => 57363,
1330                        F13 => 57376,
1331                        F14 => 57377,
1332                        F15 => 57378,
1333                        F16 => 57379,
1334                        F17 => 57380,
1335                        F18 => 57381,
1336                        F19 => 57382,
1337                        F20 => 57383,
1338                        F21 => 57384,
1339                        F22 => 57385,
1340                        F23 => 57386,
1341                        F24 => 57387,
1342                        /*
1343                        F25 => 57388,
1344                        F26 => 57389,
1345                        F27 => 57390,
1346                        F28 => 57391,
1347                        F29 => 57392,
1348                        F30 => 57393,
1349                        F31 => 57394,
1350                        F32 => 57395,
1351                        F33 => 57396,
1352                        F34 => 57397,
1353                        */
1354                        Keypad0 => 57399,
1355                        Keypad1 => 57400,
1356                        Keypad2 => 57401,
1357                        Keypad3 => 57402,
1358                        Keypad4 => 57403,
1359                        Keypad5 => 57404,
1360                        Keypad6 => 57405,
1361                        Keypad7 => 57406,
1362                        Keypad8 => 57407,
1363                        Keypad9 => 57408,
1364                        KeypadDecimal => 57409,
1365                        KeypadDivide => 57410,
1366                        KeypadMultiply => 57411,
1367                        KeypadSubtract => 57412,
1368                        KeypadAdd => 57413,
1369                        KeypadEnter => 57414,
1370                        KeypadEquals => 57415,
1371                        // KeypadSeparator => 57416,
1372                        // ApplicationLeftArrow => 57417,
1373                        // ApplicationRightArrow => 57418,
1374                        // ApplicationUpArrow => 57419,
1375                        // ApplicationDownArrow => 57420,
1376                        // KeyPadHome => 57423,
1377                        // KeyPadEnd => 57424,
1378                        // KeyPadBegin => 57427,
1379                        // KeyPadPageUp => 57421,
1380                        // KeyPadPageDown => 57422,
1381                        Insert => 57425,
1382                        // KeypadDelete => 57426,
1383                        // MediaPlayPause => 57430,
1384                        // MediaStop => 57432,
1385                        // MediaNextTrack => 57435,
1386                        // MediaPrevTrack => 57436,
1387                        VolumeDown => 57436,
1388                        VolumeUp => 57439,
1389                        VolumeMute => 57440,
1390                        LeftShift => 57441,
1391                        LeftControl => 57442,
1392                        LeftAlt => 57443,
1393                        LeftWindows => 57444,
1394                        RightShift => 57447,
1395                        RightControl => 57448,
1396                        RightAlt => 57449,
1397                        RightWindows => 57450,
1398                        _ => return None,
1399                    }
1400                }
1401                _ => return None,
1402            },
1403        })
1404    }
1405}
1406
1407#[derive(Debug, Clone, PartialEq, Eq)]
1408pub struct KeyEvent {
1409    /// Which key was pressed.
1410    /// This is the potentially processed/composed version
1411    /// of the input.
1412    pub key: KeyCode,
1413    /// Which modifiers are down
1414    pub modifiers: Modifiers,
1415
1416    pub leds: KeyboardLedStatus,
1417
1418    /// How many times this key repeats
1419    pub repeat_count: u16,
1420
1421    /// If true, this is a key down rather than a key up event
1422    pub key_is_down: bool,
1423
1424    /// If triggered from a raw key event, here it is.
1425    pub raw: Option<RawKeyEvent>,
1426
1427    #[cfg(windows)]
1428    pub win32_uni_char: Option<char>,
1429}
1430
1431fn normalize_shift(key: KeyCode, modifiers: Modifiers) -> (KeyCode, Modifiers) {
1432    if modifiers.contains(Modifiers::SHIFT) {
1433        match key {
1434            KeyCode::Char(c) if c.is_ascii_uppercase() => (key, modifiers - Modifiers::SHIFT),
1435            KeyCode::Char(c) if c.is_ascii_lowercase() => (
1436                KeyCode::Char(c.to_ascii_uppercase()),
1437                modifiers - Modifiers::SHIFT,
1438            ),
1439            _ => (key, modifiers),
1440        }
1441    } else {
1442        (key, modifiers)
1443    }
1444}
1445
1446pub fn is_ascii_control(c: char) -> Option<char> {
1447    let c = c as u32;
1448    if c < 0x20 {
1449        let de_ctrl = ((c as u8) | 0x40) as char;
1450        Some(de_ctrl.to_ascii_lowercase())
1451    } else {
1452        None
1453    }
1454}
1455
1456fn normalize_ctrl(key: KeyCode, modifiers: Modifiers) -> (KeyCode, Modifiers) {
1457    if modifiers.contains(Modifiers::CTRL) {
1458        if let KeyCode::Char(c) = key {
1459            if (c as u32) < 0x20 {
1460                let de_ctrl = ((c as u8) | 0x40) as char;
1461                return (KeyCode::Char(de_ctrl.to_ascii_lowercase()), modifiers);
1462            }
1463        }
1464    }
1465    (key, modifiers)
1466}
1467
1468impl KeyEvent {
1469    /// if SHIFT is held and we have KeyCode::Char('c') we want to normalize
1470    /// that keycode to KeyCode::Char('C'); that is what this function does.
1471    pub fn normalize_shift(mut self) -> Self {
1472        let (key, modifiers) = normalize_shift(self.key, self.modifiers);
1473        self.key = key;
1474        self.modifiers = modifiers;
1475
1476        self
1477    }
1478
1479    /// If the key code is a modifier key (Control, Alt, Shift), check
1480    /// the underlying raw event to see if we had a positional version
1481    /// of that key.
1482    /// If so, switch to the positional version.
1483    pub fn resurface_positional_modifier_key(mut self) -> Self {
1484        match self.key {
1485            KeyCode::Control
1486                if matches!(
1487                    self.raw,
1488                    Some(RawKeyEvent {
1489                        key: KeyCode::LeftControl | KeyCode::Physical(PhysKeyCode::LeftControl),
1490                        ..
1491                    })
1492                ) =>
1493            {
1494                self.key = KeyCode::LeftControl;
1495            }
1496            KeyCode::Control
1497                if matches!(
1498                    self.raw,
1499                    Some(RawKeyEvent {
1500                        key: KeyCode::RightControl | KeyCode::Physical(PhysKeyCode::RightControl),
1501                        ..
1502                    })
1503                ) =>
1504            {
1505                self.key = KeyCode::RightControl;
1506            }
1507            KeyCode::Alt
1508                if matches!(
1509                    self.raw,
1510                    Some(RawKeyEvent {
1511                        key: KeyCode::LeftAlt | KeyCode::Physical(PhysKeyCode::LeftAlt),
1512                        ..
1513                    })
1514                ) =>
1515            {
1516                self.key = KeyCode::LeftAlt;
1517            }
1518            KeyCode::Alt
1519                if matches!(
1520                    self.raw,
1521                    Some(RawKeyEvent {
1522                        key: KeyCode::RightAlt | KeyCode::Physical(PhysKeyCode::RightAlt),
1523                        ..
1524                    })
1525                ) =>
1526            {
1527                self.key = KeyCode::RightAlt;
1528            }
1529            KeyCode::Shift
1530                if matches!(
1531                    self.raw,
1532                    Some(RawKeyEvent {
1533                        key: KeyCode::LeftShift | KeyCode::Physical(PhysKeyCode::LeftShift),
1534                        ..
1535                    })
1536                ) =>
1537            {
1538                self.key = KeyCode::LeftShift;
1539            }
1540            KeyCode::Shift
1541                if matches!(
1542                    self.raw,
1543                    Some(RawKeyEvent {
1544                        key: KeyCode::RightShift | KeyCode::Physical(PhysKeyCode::RightShift),
1545                        ..
1546                    })
1547                ) =>
1548            {
1549                self.key = KeyCode::RightShift;
1550            }
1551            _ => {}
1552        }
1553
1554        self
1555    }
1556
1557    /// If CTRL is held down and we have KeyCode::Char(_) with the
1558    /// ASCII control value encoded, decode it back to the ASCII
1559    /// alpha keycode instead.
1560    pub fn normalize_ctrl(mut self) -> Self {
1561        let (key, modifiers) = normalize_ctrl(self.key, self.modifiers);
1562        self.key = key;
1563        self.modifiers = modifiers;
1564
1565        self
1566    }
1567
1568    #[cfg(not(windows))]
1569    pub fn encode_win32_input_mode(&self) -> Option<String> {
1570        None
1571    }
1572
1573    /// <https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md>
1574    #[cfg(windows)]
1575    pub fn encode_win32_input_mode(&self) -> Option<String> {
1576        let phys = self.raw.as_ref()?;
1577
1578        let vkey = phys.raw_code;
1579        let scan_code = phys.scan_code;
1580        // <https://docs.microsoft.com/en-us/windows/console/key-event-record-str>
1581        // defines the dwControlKeyState values
1582        let mut control_key_state = 0;
1583        const SHIFT_PRESSED: usize = 0x10;
1584        const ENHANCED_KEY: usize = 0x100;
1585        const RIGHT_ALT_PRESSED: usize = 0x01;
1586        const LEFT_ALT_PRESSED: usize = 0x02;
1587        const LEFT_CTRL_PRESSED: usize = 0x08;
1588        const RIGHT_CTRL_PRESSED: usize = 0x04;
1589
1590        if self
1591            .modifiers
1592            .intersects(Modifiers::SHIFT | Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT)
1593        {
1594            control_key_state |= SHIFT_PRESSED;
1595        }
1596
1597        if self.modifiers.contains(Modifiers::RIGHT_ALT) {
1598            control_key_state |= RIGHT_ALT_PRESSED;
1599        } else if self.modifiers.contains(Modifiers::ALT) {
1600            control_key_state |= LEFT_ALT_PRESSED;
1601        }
1602        if self.modifiers.contains(Modifiers::LEFT_ALT) {
1603            control_key_state |= LEFT_ALT_PRESSED;
1604        }
1605        if self.modifiers.contains(Modifiers::RIGHT_CTRL) {
1606            control_key_state |= RIGHT_CTRL_PRESSED;
1607        } else if self.modifiers.contains(Modifiers::CTRL) {
1608            control_key_state |= LEFT_CTRL_PRESSED;
1609        }
1610        if self.modifiers.contains(Modifiers::LEFT_CTRL) {
1611            control_key_state |= LEFT_CTRL_PRESSED;
1612        }
1613        if self.modifiers.contains(Modifiers::ENHANCED_KEY) {
1614            control_key_state |= ENHANCED_KEY;
1615        }
1616
1617        let key_down = if self.key_is_down { 1 } else { 0 };
1618
1619        match &self.key {
1620            KeyCode::Composed(_) => None,
1621            KeyCode::Char(c) => {
1622                let uni = self.win32_uni_char.unwrap_or(*c) as u32;
1623                Some(format!(
1624                    "\u{1b}[{};{};{};{};{};{}_",
1625                    vkey, scan_code, uni, key_down, control_key_state, self.repeat_count
1626                ))
1627            }
1628            _ => {
1629                let uni = 0;
1630                Some(format!(
1631                    "\u{1b}[{};{};{};{};{};{}_",
1632                    vkey, scan_code, uni, key_down, control_key_state, self.repeat_count
1633                ))
1634            }
1635        }
1636    }
1637
1638    pub fn encode_kitty(&self, flags: KittyKeyboardFlags) -> String {
1639        use KeyCode::*;
1640
1641        if !flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !self.key_is_down {
1642            return String::new();
1643        }
1644
1645        if self.modifiers.is_empty()
1646            && !flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES)
1647            && self.key_is_down
1648        {
1649            // Check for simple text generating keys
1650            match &self.key {
1651                Char('\x08') => return '\x7f'.to_string(),
1652                Char('\x7f') => return '\x08'.to_string(),
1653                Char(c) => return c.to_string(),
1654                _ => {}
1655            }
1656        }
1657
1658        let raw_modifiers = self
1659            .raw
1660            .as_ref()
1661            .map(|raw| raw.modifiers)
1662            .unwrap_or(self.modifiers);
1663
1664        let mut modifiers = 0;
1665        if raw_modifiers.contains(Modifiers::SHIFT) {
1666            modifiers |= 1;
1667        }
1668        if raw_modifiers.contains(Modifiers::ALT) {
1669            modifiers |= 2;
1670        }
1671        if raw_modifiers.contains(Modifiers::CTRL) {
1672            modifiers |= 4;
1673        }
1674        if raw_modifiers.contains(Modifiers::SUPER) {
1675            modifiers |= 8;
1676        }
1677        // TODO: Hyper and Meta are not handled yet.
1678        // We should somehow detect this?
1679        // See: https://github.com/wez/wezterm/pull/4605#issuecomment-1823604708
1680        if self.leds.contains(KeyboardLedStatus::CAPS_LOCK) {
1681            modifiers |= 64;
1682        }
1683        if self.leds.contains(KeyboardLedStatus::NUM_LOCK) {
1684            modifiers |= 128;
1685        }
1686        modifiers += 1;
1687
1688        let event_type =
1689            if flags.contains(KittyKeyboardFlags::REPORT_EVENT_TYPES) && !self.key_is_down {
1690                ":3"
1691            } else {
1692                ""
1693            };
1694
1695        let is_legacy_key = match &self.key {
1696            Char(c) => c.is_ascii_alphanumeric() || c.is_ascii_punctuation(),
1697            _ => false,
1698        };
1699
1700        let generated_text =
1701            if self.key_is_down && flags.contains(KittyKeyboardFlags::REPORT_ASSOCIATED_TEXT) {
1702                match &self.key {
1703                    Char(c) => format!(";{}", *c as u32),
1704                    KeyCode::Numpad(n) => format!(";{}", '0' as u32 + *n as u32),
1705                    Composed(s) => {
1706                        let mut codepoints = ";".to_string();
1707                        for c in s.chars() {
1708                            if codepoints.len() > 1 {
1709                                codepoints.push(':');
1710                            }
1711                            write!(&mut codepoints, "{}", c as u32).ok();
1712                        }
1713                        codepoints
1714                    }
1715                    _ => String::new(),
1716                }
1717            } else {
1718                String::new()
1719            };
1720
1721        let guess_phys = self
1722            .raw
1723            .as_ref()
1724            .and_then(|raw| raw.phys_code)
1725            .or_else(|| self.key.to_phys());
1726
1727        let is_numpad = guess_phys.and_then(|phys| match phys {
1728                PhysKeyCode::Keypad0
1729                | PhysKeyCode::Keypad1
1730                | PhysKeyCode::Keypad2
1731                | PhysKeyCode::Keypad3
1732                | PhysKeyCode::Keypad4
1733                | PhysKeyCode::Keypad5
1734                | PhysKeyCode::Keypad6
1735                | PhysKeyCode::Keypad7
1736                | PhysKeyCode::Keypad8
1737                | PhysKeyCode::Keypad9
1738                // | PhysKeyCode::KeypadClear not a physical numpad key?
1739                | PhysKeyCode::KeypadDecimal
1740                | PhysKeyCode::KeypadDelete
1741                | PhysKeyCode::KeypadDivide
1742                | PhysKeyCode::KeypadEnter
1743                | PhysKeyCode::KeypadEquals
1744                | PhysKeyCode::KeypadSubtract
1745                | PhysKeyCode::KeypadMultiply
1746                | PhysKeyCode::KeypadAdd
1747             => Some(phys),
1748            _ => None,
1749        });
1750
1751        if let Some(numpad) = is_numpad {
1752            let code = match (numpad, self.leds.contains(KeyboardLedStatus::NUM_LOCK)) {
1753                (PhysKeyCode::Keypad0, true) => 57399,
1754                (PhysKeyCode::Keypad0, false) => 57425,
1755                (PhysKeyCode::Keypad1, true) => 57400,
1756                (PhysKeyCode::Keypad1, false) => 57424,
1757                (PhysKeyCode::Keypad2, true) => 57401,
1758                (PhysKeyCode::Keypad2, false) => 57420,
1759                (PhysKeyCode::Keypad3, true) => 57402,
1760                (PhysKeyCode::Keypad3, false) => 57422,
1761                (PhysKeyCode::Keypad4, true) => 57403,
1762                (PhysKeyCode::Keypad4, false) => 57417,
1763                (PhysKeyCode::Keypad5, true) => 57404,
1764                (PhysKeyCode::Keypad5, false) => {
1765                    let xt_mods = self.modifiers.encode_xterm();
1766                    return if xt_mods == 0 && self.key_is_down {
1767                        "\x1b[E".to_string()
1768                    } else {
1769                        format!("\x1b[1;{}{event_type}E", 1 + xt_mods)
1770                    };
1771                }
1772                (PhysKeyCode::Keypad6, true) => 57405,
1773                (PhysKeyCode::Keypad6, false) => 57418,
1774                (PhysKeyCode::Keypad7, true) => 57406,
1775                (PhysKeyCode::Keypad7, false) => 57423,
1776                (PhysKeyCode::Keypad8, true) => 57407,
1777                (PhysKeyCode::Keypad8, false) => 57419,
1778                (PhysKeyCode::Keypad9, true) => 57408,
1779                (PhysKeyCode::Keypad9, false) => 57421,
1780                (PhysKeyCode::KeypadDecimal, _) => 57409,
1781                (PhysKeyCode::KeypadDelete, _) => 57426,
1782                (PhysKeyCode::KeypadDivide, _) => 57410,
1783                (PhysKeyCode::KeypadEnter, _) => 57414,
1784                (PhysKeyCode::KeypadEquals, _) => 57415,
1785                (PhysKeyCode::KeypadSubtract, _) => 57412,
1786                (PhysKeyCode::KeypadMultiply, _) => 57411,
1787                (PhysKeyCode::KeypadAdd, _) => 57413,
1788                _ => unreachable!(),
1789            };
1790            return format!("\x1b[{code};{modifiers}{event_type}{generated_text}u");
1791        }
1792
1793        match &self.key {
1794            PageUp | PageDown | Insert | Char('\x7f') => {
1795                let c = match &self.key {
1796                    Insert => 2,
1797                    Char('\x7f') => 3, // Delete
1798                    PageUp => 5,
1799                    PageDown => 6,
1800                    _ => unreachable!(),
1801                };
1802
1803                format!("\x1b[{c};{modifiers}{event_type}~")
1804            }
1805            Char(shifted_key) => {
1806                let shifted_key = if *shifted_key == '\x08' {
1807                    // Backspace is really VERASE -> ASCII DEL
1808                    '\x7f'
1809                } else {
1810                    *shifted_key
1811                };
1812
1813                let use_legacy = !flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
1814                    && event_type.is_empty()
1815                    && is_legacy_key
1816                    && !(flags.contains(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES)
1817                        && (self.modifiers.contains(Modifiers::CTRL)
1818                            || self.modifiers.contains(Modifiers::ALT)))
1819                    && !self.modifiers.intersects(
1820                        Modifiers::SUPER, /* TODO: Hyper and Meta should be added here. */
1821                    );
1822
1823                if use_legacy {
1824                    // Legacy text key
1825                    // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-text-keys
1826                    let mut output = String::new();
1827                    if self.modifiers.contains(Modifiers::ALT) {
1828                        output.push('\x1b');
1829                    }
1830
1831                    if self.modifiers.contains(Modifiers::CTRL) {
1832                        csi_u_encode(
1833                            &mut output,
1834                            shifted_key.to_ascii_uppercase(),
1835                            self.modifiers,
1836                        );
1837                    } else {
1838                        output.push(shifted_key);
1839                    }
1840
1841                    return output;
1842                }
1843
1844                // FIXME: ideally we'd get the correct unshifted key from
1845                // the OS based on the current keyboard layout. That needs
1846                // more plumbing, so for now, we're assuming the US layout.
1847                let c = us_layout_unshift(shifted_key);
1848
1849                let base_layout = self
1850                    .raw
1851                    .as_ref()
1852                    .and_then(|raw| raw.phys_code.as_ref())
1853                    .and_then(|phys| match phys.to_key_code() {
1854                        KeyCode::Char(base) if base != c => Some(base),
1855                        _ => None,
1856                    });
1857
1858                let mut key_code = format!("{}", (c as u32));
1859
1860                if flags.contains(KittyKeyboardFlags::REPORT_ALTERNATE_KEYS)
1861                    && (c != shifted_key || base_layout.is_some())
1862                {
1863                    key_code.push(':');
1864                    if c != shifted_key {
1865                        key_code.push_str(&format!("{}", (shifted_key as u32)));
1866                    }
1867                    if let Some(base) = base_layout {
1868                        key_code.push_str(&format!(":{}", (base as u32)));
1869                    }
1870                }
1871
1872                format!("\x1b[{key_code};{modifiers}{event_type}{generated_text}u")
1873            }
1874            LeftArrow | RightArrow | UpArrow | DownArrow | Home | End => {
1875                let c = match &self.key {
1876                    UpArrow => 'A',
1877                    DownArrow => 'B',
1878                    RightArrow => 'C',
1879                    LeftArrow => 'D',
1880                    Home => 'H',
1881                    End => 'F',
1882                    _ => unreachable!(),
1883                };
1884                format!("\x1b[1;{modifiers}{event_type}{c}")
1885            }
1886            Function(n) if *n < 25 => {
1887                // The spec says that kitty prefers an SS3 form for F1-F4,
1888                // but then has some variance in the encoding and cites a
1889                // compatibility issue with a cursor position report.
1890                // Since it allows reporting these all unambiguously with
1891                // the same general scheme, that is what we're using here.
1892                let intro = match *n {
1893                    1 => "\x1b[11",
1894                    2 => "\x1b[12",
1895                    3 => "\x1b[13",
1896                    4 => "\x1b[14",
1897                    5 => "\x1b[15",
1898                    6 => "\x1b[17",
1899                    7 => "\x1b[18",
1900                    8 => "\x1b[19",
1901                    9 => "\x1b[20",
1902                    10 => "\x1b[21",
1903                    11 => "\x1b[23",
1904                    12 => "\x1b[24",
1905                    13 => "\x1b[57376",
1906                    14 => "\x1b[57377",
1907                    15 => "\x1b[57378",
1908                    16 => "\x1b[57379",
1909                    17 => "\x1b[57380",
1910                    18 => "\x1b[57381",
1911                    19 => "\x1b[57382",
1912                    20 => "\x1b[57383",
1913                    21 => "\x1b[57384",
1914                    22 => "\x1b[57385",
1915                    23 => "\x1b[57386",
1916                    24 => "\x1b[57387",
1917                    _ => unreachable!(),
1918                };
1919                // for F1-F12 the spec says we should terminate with ~
1920                // for F13 and up the spec says we should terminate with u
1921                let end_char = if *n < 13 { '~' } else { 'u' };
1922
1923                format!("{intro};{modifiers}{event_type}{end_char}")
1924            }
1925
1926            _ => {
1927                let code = self.raw.as_ref().and_then(|raw| raw.kitty_function_code());
1928
1929                match (
1930                    code,
1931                    flags.contains(KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES),
1932                ) {
1933                    (Some(code), true) => {
1934                        format!("\x1b[{code};{modifiers}{event_type}{generated_text}u")
1935                    }
1936                    _ => String::new(),
1937                }
1938            }
1939        }
1940    }
1941}
1942
1943fn csi_u_encode(buf: &mut String, c: char, mods: Modifiers) {
1944    let c = if mods.contains(Modifiers::CTRL) && ctrl_mapping(c).is_some() {
1945        ctrl_mapping(c).unwrap()
1946    } else {
1947        c
1948    };
1949    if mods.contains(Modifiers::ALT) {
1950        buf.push(0x1b as char);
1951    }
1952    write!(buf, "{}", c).ok();
1953}
1954
1955bitflags::bitflags! {
1956pub struct KittyKeyboardFlags: u16 {
1957    const NONE = 0;
1958    const DISAMBIGUATE_ESCAPE_CODES = 1;
1959    const REPORT_EVENT_TYPES = 2;
1960    const REPORT_ALTERNATE_KEYS = 4;
1961    const REPORT_ALL_KEYS_AS_ESCAPE_CODES = 8;
1962    const REPORT_ASSOCIATED_TEXT = 16;
1963}
1964}
1965
1966bitflags! {
1967    #[derive(FromDynamic, ToDynamic)]
1968    #[cfg_attr(feature="serde", derive(Serialize, Deserialize), serde(try_from = "String"))]
1969    #[dynamic(try_from = "String", into = "String")]
1970    pub struct WindowDecorations: u8 {
1971        const TITLE = 1;
1972        const RESIZE = 2;
1973        const NONE = 0;
1974        // Reserve two bits for this enable/disable shadow,
1975        // so that we effective have Option<bool>
1976        const MACOS_FORCE_DISABLE_SHADOW = 4;
1977        const MACOS_FORCE_ENABLE_SHADOW = 4|8;
1978        const INTEGRATED_BUTTONS = 16;
1979    }
1980}
1981
1982impl Into<String> for &WindowDecorations {
1983    fn into(self) -> String {
1984        let mut s = vec![];
1985        if self.contains(WindowDecorations::TITLE) {
1986            s.push("TITLE");
1987        }
1988        if self.contains(WindowDecorations::RESIZE) {
1989            s.push("RESIZE");
1990        }
1991        if self.contains(WindowDecorations::INTEGRATED_BUTTONS) {
1992            s.push("INTEGRATED_BUTTONS");
1993        }
1994        if self.contains(WindowDecorations::MACOS_FORCE_ENABLE_SHADOW) {
1995            s.push("MACOS_FORCE_ENABLE_SHADOW");
1996        } else if self.contains(WindowDecorations::MACOS_FORCE_DISABLE_SHADOW) {
1997            s.push("MACOS_FORCE_DISABLE_SHADOW");
1998        }
1999        if s.is_empty() {
2000            "NONE".to_string()
2001        } else {
2002            s.join("|")
2003        }
2004    }
2005}
2006
2007impl TryFrom<String> for WindowDecorations {
2008    type Error = String;
2009    fn try_from(s: String) -> std::result::Result<WindowDecorations, String> {
2010        let mut flags = Self::NONE;
2011        for ele in s.split('|') {
2012            let ele = ele.trim();
2013            if ele == "TITLE" {
2014                flags |= Self::TITLE;
2015            } else if ele == "NONE" || ele == "None" {
2016                flags = Self::NONE;
2017            } else if ele == "RESIZE" {
2018                flags |= Self::RESIZE;
2019            } else if ele == "MACOS_FORCE_DISABLE_SHADOW" {
2020                flags |= Self::MACOS_FORCE_DISABLE_SHADOW;
2021            } else if ele == "MACOS_FORCE_ENABLE_SHADOW" {
2022                flags |= Self::MACOS_FORCE_ENABLE_SHADOW;
2023            } else if ele == "INTEGRATED_BUTTONS" {
2024                flags |= Self::INTEGRATED_BUTTONS;
2025            } else {
2026                return Err(format!("invalid WindowDecoration name {} in {}", ele, s));
2027            }
2028        }
2029        Ok(flags)
2030    }
2031}
2032
2033impl Default for WindowDecorations {
2034    fn default() -> Self {
2035        WindowDecorations::TITLE | WindowDecorations::RESIZE
2036    }
2037}
2038
2039#[derive(Debug, FromDynamic, ToDynamic, PartialEq, Eq, Clone, Copy)]
2040pub enum IntegratedTitleButton {
2041    Hide,
2042    Maximize,
2043    Close,
2044}
2045
2046#[derive(Debug, Default, FromDynamic, ToDynamic, PartialEq, Eq, Clone, Copy)]
2047pub enum IntegratedTitleButtonAlignment {
2048    #[default]
2049    Right,
2050    Left,
2051}
2052
2053#[derive(Debug, ToDynamic, PartialEq, Eq, Clone, Copy)]
2054pub enum IntegratedTitleButtonStyle {
2055    Windows,
2056    Gnome,
2057    MacOsNative,
2058}
2059
2060impl Default for IntegratedTitleButtonStyle {
2061    fn default() -> Self {
2062        if cfg!(target_os = "macos") {
2063            Self::MacOsNative
2064        } else {
2065            Self::Windows
2066        }
2067    }
2068}
2069
2070impl FromDynamic for IntegratedTitleButtonStyle {
2071    fn from_dynamic(
2072        value: &wezterm_dynamic::Value,
2073        _options: wezterm_dynamic::FromDynamicOptions,
2074    ) -> Result<Self, wezterm_dynamic::Error>
2075    where
2076        Self: Sized,
2077    {
2078        let type_name = "integrated_title_button_style";
2079
2080        if let wezterm_dynamic::Value::String(string) = value {
2081            let style = match string.as_str() {
2082                "Windows" => Self::Windows,
2083                "Gnome" => Self::Gnome,
2084                "MacOsNative" if cfg!(target_os = "macos") => Self::MacOsNative,
2085                _ => {
2086                    return Err(wezterm_dynamic::Error::InvalidVariantForType {
2087                        variant_name: string.to_string(),
2088                        type_name,
2089                        possible: &["Windows", "Gnome", "MacOsNative"],
2090                    });
2091                }
2092            };
2093            Ok(style)
2094        } else {
2095            Err(wezterm_dynamic::Error::InvalidVariantForType {
2096                variant_name: value.variant_name().to_string(),
2097                type_name,
2098                possible: &["String"],
2099            })
2100        }
2101    }
2102}
2103
2104/// Kitty wants us to report the un-shifted version of a key.
2105/// It's a PITA to obtain that from the OS-dependent keyboard
2106/// layout stuff. For the moment, we'll do the slightly gross
2107/// thing and make an assumption that a US ANSI layout is in
2108/// use; this function encodes that mapping.
2109fn us_layout_unshift(c: char) -> char {
2110    match c {
2111        '!' => '1',
2112        '@' => '2',
2113        '#' => '3',
2114        '$' => '4',
2115        '%' => '5',
2116        '^' => '6',
2117        '&' => '7',
2118        '*' => '8',
2119        '(' => '9',
2120        ')' => '0',
2121        '_' => '-',
2122        '+' => '=',
2123        '~' => '`',
2124        '{' => '[',
2125        '}' => ']',
2126        '|' => '\\',
2127        ':' => ';',
2128        '"' => '\'',
2129        '<' => ',',
2130        '>' => '.',
2131        '?' => '/',
2132        c => {
2133            let s: Vec<char> = c.to_lowercase().collect();
2134            if s.len() == 1 {
2135                s[0]
2136            } else {
2137                c
2138            }
2139        }
2140    }
2141}
2142
2143/// Map c to its Ctrl equivalent.
2144/// In theory, this mapping is simply translating alpha characters
2145/// to upper case and then masking them by 0x1f, but xterm inherits
2146/// some built-in translation from legacy X11 so that are some
2147/// aliased mappings and a couple that might be technically tied
2148/// to US keyboard layout (particularly the punctuation characters
2149/// produced in combination with SHIFT) that may not be 100%
2150/// the right thing to do here for users with non-US layouts.
2151pub fn ctrl_mapping(c: char) -> Option<char> {
2152    Some(match c {
2153        '@' | '`' | ' ' | '2' => '\x00',
2154        'A' | 'a' => '\x01',
2155        'B' | 'b' => '\x02',
2156        'C' | 'c' => '\x03',
2157        'D' | 'd' => '\x04',
2158        'E' | 'e' => '\x05',
2159        'F' | 'f' => '\x06',
2160        'G' | 'g' => '\x07',
2161        'H' | 'h' => '\x08',
2162        'I' | 'i' => '\x09',
2163        'J' | 'j' => '\x0a',
2164        'K' | 'k' => '\x0b',
2165        'L' | 'l' => '\x0c',
2166        'M' | 'm' => '\x0d',
2167        'N' | 'n' => '\x0e',
2168        'O' | 'o' => '\x0f',
2169        'P' | 'p' => '\x10',
2170        'Q' | 'q' => '\x11',
2171        'R' | 'r' => '\x12',
2172        'S' | 's' => '\x13',
2173        'T' | 't' => '\x14',
2174        'U' | 'u' => '\x15',
2175        'V' | 'v' => '\x16',
2176        'W' | 'w' => '\x17',
2177        'X' | 'x' => '\x18',
2178        'Y' | 'y' => '\x19',
2179        'Z' | 'z' => '\x1a',
2180        '[' | '3' | '{' => '\x1b',
2181        '\\' | '4' | '|' => '\x1c',
2182        ']' | '5' | '}' => '\x1d',
2183        '^' | '6' | '~' => '\x1e',
2184        '_' | '7' | '/' => '\x1f',
2185        '8' | '?' => '\x7f', // `Delete`
2186        _ => return None,
2187    })
2188}
2189
2190#[derive(Debug, FromDynamic, ToDynamic, Clone, Copy, PartialEq, Eq)]
2191pub enum UIKeyCapRendering {
2192    /// Super, Meta, Ctrl, Shift
2193    UnixLong,
2194    /// Super, M, C, S
2195    Emacs,
2196    /// Apple macOS style symbols
2197    AppleSymbols,
2198    /// Win, Alt, Ctrl, Shift
2199    WindowsLong,
2200    /// Like WindowsLong, but using a logo for the Win key
2201    WindowsSymbols,
2202}
2203
2204impl Default for UIKeyCapRendering {
2205    fn default() -> Self {
2206        if cfg!(target_os = "macos") {
2207            Self::AppleSymbols
2208        } else if cfg!(windows) {
2209            Self::WindowsSymbols
2210        } else {
2211            Self::UnixLong
2212        }
2213    }
2214}
2215
2216#[cfg(test)]
2217mod test {
2218    use super::*;
2219
2220    #[test]
2221    fn encode_issue_3220() {
2222        let flags =
2223            KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES | KittyKeyboardFlags::REPORT_EVENT_TYPES;
2224
2225        assert_eq!(
2226            KeyEvent {
2227                key: KeyCode::Char('o'),
2228                modifiers: Modifiers::NONE,
2229                leds: KeyboardLedStatus::empty(),
2230                repeat_count: 1,
2231                key_is_down: true,
2232                raw: None,
2233                #[cfg(windows)]
2234                win32_uni_char: None,
2235            }
2236            .encode_kitty(flags),
2237            "o".to_string()
2238        );
2239        assert_eq!(
2240            KeyEvent {
2241                key: KeyCode::Char('o'),
2242                modifiers: Modifiers::NONE,
2243                leds: KeyboardLedStatus::empty(),
2244                repeat_count: 1,
2245                key_is_down: false,
2246                raw: None,
2247                #[cfg(windows)]
2248                win32_uni_char: None,
2249            }
2250            .encode_kitty(flags),
2251            "\x1b[111;1:3u".to_string()
2252        );
2253    }
2254
2255    #[test]
2256    fn encode_issue_3473() {
2257        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2258            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2259            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2260            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
2261
2262        assert_eq!(
2263            KeyEvent {
2264                key: KeyCode::Function(1),
2265                modifiers: Modifiers::NONE,
2266                leds: KeyboardLedStatus::empty(),
2267                repeat_count: 1,
2268                key_is_down: true,
2269                raw: None,
2270                #[cfg(windows)]
2271                win32_uni_char: None,
2272            }
2273            .encode_kitty(flags),
2274            "\x1b[11;1~".to_string()
2275        );
2276        assert_eq!(
2277            KeyEvent {
2278                key: KeyCode::Function(1),
2279                modifiers: Modifiers::NONE,
2280                leds: KeyboardLedStatus::empty(),
2281                repeat_count: 1,
2282                key_is_down: false,
2283                raw: None,
2284                #[cfg(windows)]
2285                win32_uni_char: None,
2286            }
2287            .encode_kitty(flags),
2288            "\x1b[11;1:3~".to_string()
2289        );
2290    }
2291
2292    #[test]
2293    fn encode_issue_2546() {
2294        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
2295
2296        assert_eq!(
2297            KeyEvent {
2298                key: KeyCode::Char('i'),
2299                modifiers: Modifiers::ALT | Modifiers::SHIFT,
2300                leds: KeyboardLedStatus::empty(),
2301                repeat_count: 1,
2302                key_is_down: true,
2303                raw: None,
2304                #[cfg(windows)]
2305                win32_uni_char: None,
2306            }
2307            .encode_kitty(flags),
2308            "\x1b[105;4u".to_string()
2309        );
2310        assert_eq!(
2311            KeyEvent {
2312                key: KeyCode::Char('I'),
2313                modifiers: Modifiers::ALT | Modifiers::SHIFT,
2314                leds: KeyboardLedStatus::empty(),
2315                repeat_count: 1,
2316                key_is_down: true,
2317                raw: None,
2318                #[cfg(windows)]
2319                win32_uni_char: None,
2320            }
2321            .encode_kitty(flags),
2322            "\x1b[105;4u".to_string()
2323        );
2324
2325        assert_eq!(
2326            KeyEvent {
2327                key: KeyCode::Char('1'),
2328                modifiers: Modifiers::ALT | Modifiers::SHIFT,
2329                leds: KeyboardLedStatus::empty(),
2330                repeat_count: 1,
2331                key_is_down: true,
2332                raw: None,
2333                #[cfg(windows)]
2334                win32_uni_char: None,
2335            }
2336            .encode_kitty(flags),
2337            "\x1b[49;4u".to_string()
2338        );
2339
2340        assert_eq!(
2341            make_event_with_raw(
2342                KeyEvent {
2343                    key: KeyCode::Char('!'),
2344                    modifiers: Modifiers::ALT | Modifiers::SHIFT,
2345                    leds: KeyboardLedStatus::empty(),
2346                    repeat_count: 1,
2347                    key_is_down: true,
2348                    raw: None,
2349                    #[cfg(windows)]
2350                    win32_uni_char: None,
2351                },
2352                Some(PhysKeyCode::K1)
2353            )
2354            .encode_kitty(flags),
2355            "\x1b[49;4u".to_string()
2356        );
2357
2358        assert_eq!(
2359            KeyEvent {
2360                key: KeyCode::Char('i'),
2361                modifiers: Modifiers::SHIFT | Modifiers::CTRL,
2362                leds: KeyboardLedStatus::empty(),
2363                repeat_count: 1,
2364                key_is_down: true,
2365                raw: None,
2366                #[cfg(windows)]
2367                win32_uni_char: None,
2368            }
2369            .encode_kitty(flags),
2370            "\x1b[105;6u".to_string()
2371        );
2372        assert_eq!(
2373            KeyEvent {
2374                key: KeyCode::Char('I'),
2375                modifiers: Modifiers::SHIFT | Modifiers::CTRL,
2376                leds: KeyboardLedStatus::empty(),
2377                repeat_count: 1,
2378                key_is_down: true,
2379                raw: None,
2380                #[cfg(windows)]
2381                win32_uni_char: None,
2382            }
2383            .encode_kitty(flags),
2384            "\x1b[105;6u".to_string()
2385        );
2386
2387        assert_eq!(
2388            KeyEvent {
2389                key: KeyCode::Char('I'),
2390                modifiers: Modifiers::CTRL,
2391                leds: KeyboardLedStatus::empty(),
2392                repeat_count: 1,
2393                key_is_down: true,
2394                raw: Some(RawKeyEvent {
2395                    key: KeyCode::Char('I'),
2396                    modifiers: Modifiers::SHIFT | Modifiers::CTRL,
2397                    handled: Handled::new(),
2398                    key_is_down: true,
2399                    raw_code: 0,
2400                    leds: KeyboardLedStatus::empty(),
2401                    phys_code: Some(PhysKeyCode::I),
2402                    #[cfg(windows)]
2403                    scan_code: 0,
2404                    repeat_count: 1,
2405                }),
2406                #[cfg(windows)]
2407                win32_uni_char: None,
2408            }
2409            .encode_kitty(flags),
2410            "\x1b[105;6u".to_string()
2411        );
2412
2413        assert_eq!(
2414            KeyEvent {
2415                key: KeyCode::Char('i'),
2416                modifiers: Modifiers::ALT | Modifiers::SHIFT | Modifiers::CTRL,
2417                leds: KeyboardLedStatus::empty(),
2418                repeat_count: 1,
2419                key_is_down: true,
2420                raw: None,
2421                #[cfg(windows)]
2422                win32_uni_char: None,
2423            }
2424            .encode_kitty(flags),
2425            "\x1b[105;8u".to_string()
2426        );
2427        assert_eq!(
2428            KeyEvent {
2429                key: KeyCode::Char('I'),
2430                modifiers: Modifiers::ALT | Modifiers::SHIFT | Modifiers::CTRL,
2431                leds: KeyboardLedStatus::empty(),
2432                repeat_count: 1,
2433                key_is_down: true,
2434                raw: None,
2435                #[cfg(windows)]
2436                win32_uni_char: None,
2437            }
2438            .encode_kitty(flags),
2439            "\x1b[105;8u".to_string()
2440        );
2441
2442        assert_eq!(
2443            KeyEvent {
2444                key: KeyCode::Char('\x08'),
2445                modifiers: Modifiers::NONE,
2446                leds: KeyboardLedStatus::empty(),
2447                repeat_count: 1,
2448                key_is_down: true,
2449                raw: None,
2450                #[cfg(windows)]
2451                win32_uni_char: None,
2452            }
2453            .encode_kitty(flags),
2454            "\x7f".to_string()
2455        );
2456
2457        assert_eq!(
2458            KeyEvent {
2459                key: KeyCode::Char('\x08'),
2460                modifiers: Modifiers::CTRL,
2461                leds: KeyboardLedStatus::empty(),
2462                repeat_count: 1,
2463                key_is_down: true,
2464                raw: None,
2465                #[cfg(windows)]
2466                win32_uni_char: None,
2467            }
2468            .encode_kitty(flags),
2469            "\x1b[127;5u".to_string()
2470        );
2471    }
2472
2473    #[test]
2474    fn encode_issue_3474() {
2475        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2476            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2477            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2478            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
2479
2480        assert_eq!(
2481            KeyEvent {
2482                key: KeyCode::Char('A'),
2483                modifiers: Modifiers::NONE,
2484                leds: KeyboardLedStatus::empty(),
2485                repeat_count: 1,
2486                key_is_down: true,
2487                raw: None,
2488                #[cfg(windows)]
2489                win32_uni_char: None,
2490            }
2491            .encode_kitty(flags),
2492            "\u{1b}[97:65;1u".to_string()
2493        );
2494        assert_eq!(
2495            KeyEvent {
2496                key: KeyCode::Char('A'),
2497                modifiers: Modifiers::NONE,
2498                leds: KeyboardLedStatus::empty(),
2499                repeat_count: 1,
2500                key_is_down: false,
2501                raw: None,
2502                #[cfg(windows)]
2503                win32_uni_char: None,
2504            }
2505            .encode_kitty(flags),
2506            "\u{1b}[97:65;1:3u".to_string()
2507        );
2508    }
2509
2510    fn make_event_with_raw(mut event: KeyEvent, phys: Option<PhysKeyCode>) -> KeyEvent {
2511        let phys = match phys {
2512            Some(phys) => Some(phys),
2513            None => event.key.to_phys(),
2514        };
2515
2516        event.raw = Some(RawKeyEvent {
2517            key: event.key.clone(),
2518            modifiers: event.modifiers,
2519            leds: KeyboardLedStatus::empty(),
2520            phys_code: phys,
2521            raw_code: 0,
2522            #[cfg(windows)]
2523            scan_code: 0,
2524            repeat_count: 1,
2525            key_is_down: event.key_is_down,
2526            handled: Handled::new(),
2527        });
2528
2529        event
2530    }
2531
2532    #[test]
2533    fn encode_issue_3476() {
2534        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2535            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2536            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2537            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
2538
2539        assert_eq!(
2540            make_event_with_raw(
2541                KeyEvent {
2542                    key: KeyCode::LeftShift,
2543                    modifiers: Modifiers::NONE,
2544                    leds: KeyboardLedStatus::empty(),
2545                    repeat_count: 1,
2546                    key_is_down: true,
2547                    raw: None,
2548                    #[cfg(windows)]
2549                    win32_uni_char: None,
2550                },
2551                None
2552            )
2553            .encode_kitty(flags),
2554            "\u{1b}[57441;1u".to_string()
2555        );
2556        assert_eq!(
2557            make_event_with_raw(
2558                KeyEvent {
2559                    key: KeyCode::LeftShift,
2560                    modifiers: Modifiers::NONE,
2561                    leds: KeyboardLedStatus::empty(),
2562                    repeat_count: 1,
2563                    key_is_down: false,
2564                    raw: None,
2565                    #[cfg(windows)]
2566                    win32_uni_char: None,
2567                },
2568                None
2569            )
2570            .encode_kitty(flags),
2571            "\u{1b}[57441;1:3u".to_string()
2572        );
2573        assert_eq!(
2574            make_event_with_raw(
2575                KeyEvent {
2576                    key: KeyCode::LeftControl,
2577                    modifiers: Modifiers::NONE,
2578                    leds: KeyboardLedStatus::empty(),
2579                    repeat_count: 1,
2580                    key_is_down: true,
2581                    raw: None,
2582                    #[cfg(windows)]
2583                    win32_uni_char: None,
2584                },
2585                None
2586            )
2587            .encode_kitty(flags),
2588            "\u{1b}[57442;1u".to_string()
2589        );
2590        assert_eq!(
2591            make_event_with_raw(
2592                KeyEvent {
2593                    key: KeyCode::LeftControl,
2594                    modifiers: Modifiers::NONE,
2595                    leds: KeyboardLedStatus::empty(),
2596                    repeat_count: 1,
2597                    key_is_down: false,
2598                    raw: None,
2599                    #[cfg(windows)]
2600                    win32_uni_char: None,
2601                },
2602                None
2603            )
2604            .encode_kitty(flags),
2605            "\u{1b}[57442;1:3u".to_string()
2606        );
2607    }
2608
2609    #[test]
2610    fn encode_issue_3478() {
2611        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2612            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2613            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2614            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
2615
2616        assert_eq!(
2617            make_event_with_raw(
2618                KeyEvent {
2619                    key: KeyCode::Numpad(0),
2620                    modifiers: Modifiers::NONE,
2621                    leds: KeyboardLedStatus::empty(),
2622                    repeat_count: 1,
2623                    key_is_down: true,
2624                    raw: None,
2625                    #[cfg(windows)]
2626                    win32_uni_char: None,
2627                },
2628                None
2629            )
2630            .encode_kitty(flags),
2631            "\u{1b}[57425;1u".to_string()
2632        );
2633        assert_eq!(
2634            make_event_with_raw(
2635                KeyEvent {
2636                    key: KeyCode::Numpad(0),
2637                    modifiers: Modifiers::SHIFT,
2638                    leds: KeyboardLedStatus::empty(),
2639                    repeat_count: 1,
2640                    key_is_down: true,
2641                    raw: None,
2642                    #[cfg(windows)]
2643                    win32_uni_char: None,
2644                },
2645                None
2646            )
2647            .encode_kitty(flags),
2648            "\u{1b}[57425;2u".to_string()
2649        );
2650
2651        assert_eq!(
2652            make_event_with_raw(
2653                KeyEvent {
2654                    key: KeyCode::Numpad(1),
2655                    modifiers: Modifiers::NONE,
2656                    leds: KeyboardLedStatus::empty(),
2657                    repeat_count: 1,
2658                    key_is_down: true,
2659                    raw: None,
2660                    #[cfg(windows)]
2661                    win32_uni_char: None,
2662                },
2663                None
2664            )
2665            .encode_kitty(flags),
2666            "\u{1b}[57424;1u".to_string()
2667        );
2668        assert_eq!(
2669            make_event_with_raw(
2670                KeyEvent {
2671                    key: KeyCode::Numpad(1),
2672                    modifiers: Modifiers::SHIFT,
2673                    leds: KeyboardLedStatus::empty(),
2674                    repeat_count: 1,
2675                    key_is_down: true,
2676                    raw: None,
2677                    #[cfg(windows)]
2678                    win32_uni_char: None,
2679                },
2680                None
2681            )
2682            .encode_kitty(flags),
2683            "\u{1b}[57424;2u".to_string()
2684        );
2685
2686        assert_eq!(
2687            make_event_with_raw(
2688                KeyEvent {
2689                    key: KeyCode::Numpad(0),
2690                    modifiers: Modifiers::NONE,
2691                    leds: KeyboardLedStatus::NUM_LOCK,
2692                    repeat_count: 1,
2693                    key_is_down: true,
2694                    raw: None,
2695                    #[cfg(windows)]
2696                    win32_uni_char: None,
2697                },
2698                Some(PhysKeyCode::Keypad0)
2699            )
2700            .encode_kitty(flags),
2701            "\u{1b}[57399;129u".to_string()
2702        );
2703        assert_eq!(
2704            make_event_with_raw(
2705                KeyEvent {
2706                    key: KeyCode::Numpad(0),
2707                    modifiers: Modifiers::SHIFT,
2708                    leds: KeyboardLedStatus::NUM_LOCK,
2709                    repeat_count: 1,
2710                    key_is_down: true,
2711                    raw: None,
2712                    #[cfg(windows)]
2713                    win32_uni_char: None,
2714                },
2715                Some(PhysKeyCode::Keypad0)
2716            )
2717            .encode_kitty(flags),
2718            "\u{1b}[57399;130u".to_string()
2719        );
2720
2721        assert_eq!(
2722            make_event_with_raw(
2723                KeyEvent {
2724                    key: KeyCode::Numpad(5),
2725                    modifiers: Modifiers::NONE,
2726                    leds: KeyboardLedStatus::NUM_LOCK,
2727                    repeat_count: 1,
2728                    key_is_down: true,
2729                    raw: None,
2730                    #[cfg(windows)]
2731                    win32_uni_char: None,
2732                },
2733                Some(PhysKeyCode::Keypad5)
2734            )
2735            .encode_kitty(flags),
2736            "\u{1b}[57404;129u".to_string()
2737        );
2738
2739        assert_eq!(
2740            make_event_with_raw(
2741                KeyEvent {
2742                    key: KeyCode::Numpad(5),
2743                    modifiers: Modifiers::NONE,
2744                    leds: KeyboardLedStatus::empty(),
2745                    repeat_count: 1,
2746                    key_is_down: true,
2747                    raw: None,
2748                    #[cfg(windows)]
2749                    win32_uni_char: None,
2750                },
2751                Some(PhysKeyCode::Keypad5)
2752            )
2753            .encode_kitty(flags),
2754            "\u{1b}[E".to_string()
2755        );
2756    }
2757
2758    #[test]
2759    fn encode_issue_3478_extra() {
2760        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2761            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2762            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2763            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
2764            | KittyKeyboardFlags::REPORT_ASSOCIATED_TEXT;
2765
2766        assert_eq!(
2767            make_event_with_raw(
2768                KeyEvent {
2769                    key: KeyCode::Numpad(5),
2770                    modifiers: Modifiers::NONE,
2771                    leds: KeyboardLedStatus::NUM_LOCK,
2772                    repeat_count: 1,
2773                    key_is_down: true,
2774                    raw: None,
2775                    #[cfg(windows)]
2776                    win32_uni_char: None,
2777                },
2778                Some(PhysKeyCode::Keypad5)
2779            )
2780            .encode_kitty(flags),
2781            "\u{1b}[57404;129;53u".to_string()
2782        );
2783        assert_eq!(
2784            make_event_with_raw(
2785                KeyEvent {
2786                    key: KeyCode::Numpad(5),
2787                    modifiers: Modifiers::NONE,
2788                    leds: KeyboardLedStatus::NUM_LOCK,
2789                    repeat_count: 1,
2790                    key_is_down: false,
2791                    raw: None,
2792                    #[cfg(windows)]
2793                    win32_uni_char: None,
2794                },
2795                Some(PhysKeyCode::Keypad5)
2796            )
2797            .encode_kitty(flags),
2798            "\u{1b}[57404;129:3u".to_string()
2799        );
2800
2801        assert_eq!(
2802            make_event_with_raw(
2803                KeyEvent {
2804                    key: KeyCode::Numpad(5),
2805                    modifiers: Modifiers::NONE,
2806                    leds: KeyboardLedStatus::empty(),
2807                    repeat_count: 1,
2808                    key_is_down: true,
2809                    raw: None,
2810                    #[cfg(windows)]
2811                    win32_uni_char: None,
2812                },
2813                Some(PhysKeyCode::Keypad5)
2814            )
2815            .encode_kitty(flags),
2816            "\u{1b}[E".to_string()
2817        );
2818
2819        assert_eq!(
2820            make_event_with_raw(
2821                KeyEvent {
2822                    key: KeyCode::Numpad(5),
2823                    modifiers: Modifiers::NONE,
2824                    leds: KeyboardLedStatus::empty(),
2825                    repeat_count: 1,
2826                    key_is_down: false,
2827                    raw: None,
2828                    #[cfg(windows)]
2829                    win32_uni_char: None,
2830                },
2831                Some(PhysKeyCode::Keypad5)
2832            )
2833            .encode_kitty(flags),
2834            "\u{1b}[1;1:3E".to_string()
2835        );
2836    }
2837
2838    #[test]
2839    fn encode_issue_3315() {
2840        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
2841
2842        assert_eq!(
2843            KeyEvent {
2844                key: KeyCode::Char('"'),
2845                modifiers: Modifiers::NONE,
2846                leds: KeyboardLedStatus::empty(),
2847                repeat_count: 1,
2848                key_is_down: true,
2849                raw: None,
2850                #[cfg(windows)]
2851                win32_uni_char: None,
2852            }
2853            .encode_kitty(flags),
2854            "\"".to_string()
2855        );
2856
2857        assert_eq!(
2858            KeyEvent {
2859                key: KeyCode::Char('"'),
2860                modifiers: Modifiers::SHIFT,
2861                leds: KeyboardLedStatus::empty(),
2862                repeat_count: 1,
2863                key_is_down: true,
2864                raw: None,
2865                #[cfg(windows)]
2866                win32_uni_char: None,
2867            }
2868            .encode_kitty(flags),
2869            "\"".to_string()
2870        );
2871
2872        assert_eq!(
2873            KeyEvent {
2874                key: KeyCode::Char('!'),
2875                modifiers: Modifiers::SHIFT,
2876                leds: KeyboardLedStatus::empty(),
2877                repeat_count: 1,
2878                key_is_down: true,
2879                raw: None,
2880                #[cfg(windows)]
2881                win32_uni_char: None,
2882            }
2883            .encode_kitty(flags),
2884            "!".to_string()
2885        );
2886
2887        assert_eq!(
2888            KeyEvent {
2889                key: KeyCode::LeftShift,
2890                modifiers: Modifiers::NONE,
2891                leds: KeyboardLedStatus::empty(),
2892                repeat_count: 1,
2893                key_is_down: true,
2894                raw: None,
2895                #[cfg(windows)]
2896                win32_uni_char: None,
2897            }
2898            .encode_kitty(flags),
2899            "".to_string()
2900        );
2901    }
2902
2903    #[test]
2904    fn encode_issue_3479() {
2905        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2906            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2907            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2908            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
2909
2910        assert_eq!(
2911            make_event_with_raw(
2912                KeyEvent {
2913                    key: KeyCode::Char('ф'),
2914                    modifiers: Modifiers::CTRL,
2915                    leds: KeyboardLedStatus::empty(),
2916                    repeat_count: 1,
2917                    key_is_down: true,
2918                    raw: None,
2919                    #[cfg(windows)]
2920                    win32_uni_char: None,
2921                },
2922                Some(PhysKeyCode::A)
2923            )
2924            .encode_kitty(flags),
2925            "\x1b[1092::97;5u".to_string()
2926        );
2927
2928        assert_eq!(
2929            make_event_with_raw(
2930                KeyEvent {
2931                    key: KeyCode::Char('Ф'),
2932                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2933                    leds: KeyboardLedStatus::empty(),
2934                    repeat_count: 1,
2935                    key_is_down: true,
2936                    raw: None,
2937                    #[cfg(windows)]
2938                    win32_uni_char: None,
2939                },
2940                Some(PhysKeyCode::A)
2941            )
2942            .encode_kitty(flags),
2943            "\x1b[1092:1060:97;6u".to_string()
2944        );
2945    }
2946
2947    #[test]
2948    fn encode_issue_3484() {
2949        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES
2950            | KittyKeyboardFlags::REPORT_EVENT_TYPES
2951            | KittyKeyboardFlags::REPORT_ALTERNATE_KEYS
2952            | KittyKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
2953            | KittyKeyboardFlags::REPORT_ASSOCIATED_TEXT;
2954
2955        assert_eq!(
2956            make_event_with_raw(
2957                KeyEvent {
2958                    key: KeyCode::Char('ф'),
2959                    modifiers: Modifiers::CTRL,
2960                    leds: KeyboardLedStatus::empty(),
2961                    repeat_count: 1,
2962                    key_is_down: true,
2963                    raw: None,
2964                    #[cfg(windows)]
2965                    win32_uni_char: None,
2966                },
2967                Some(PhysKeyCode::A)
2968            )
2969            .encode_kitty(flags),
2970            "\x1b[1092::97;5;1092u".to_string()
2971        );
2972
2973        assert_eq!(
2974            make_event_with_raw(
2975                KeyEvent {
2976                    key: KeyCode::Char('Ф'),
2977                    modifiers: Modifiers::CTRL | Modifiers::SHIFT,
2978                    leds: KeyboardLedStatus::empty(),
2979                    repeat_count: 1,
2980                    key_is_down: true,
2981                    raw: None,
2982                    #[cfg(windows)]
2983                    win32_uni_char: None,
2984                },
2985                Some(PhysKeyCode::A)
2986            )
2987            .encode_kitty(flags),
2988            "\x1b[1092:1060:97;6;1060u".to_string()
2989        );
2990    }
2991
2992    #[test]
2993    fn encode_issue_3526() {
2994        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
2995
2996        assert_eq!(
2997            KeyEvent {
2998                key: KeyCode::Char(' '),
2999                modifiers: Modifiers::NONE,
3000                leds: KeyboardLedStatus::NUM_LOCK,
3001                repeat_count: 1,
3002                key_is_down: true,
3003                raw: None,
3004                #[cfg(windows)]
3005                win32_uni_char: None,
3006            }
3007            .encode_kitty(flags),
3008            " ".to_string()
3009        );
3010
3011        assert_eq!(
3012            KeyEvent {
3013                key: KeyCode::Char(' '),
3014                modifiers: Modifiers::NONE,
3015                leds: KeyboardLedStatus::CAPS_LOCK,
3016                repeat_count: 1,
3017                key_is_down: true,
3018                raw: None,
3019                #[cfg(windows)]
3020                win32_uni_char: None,
3021            }
3022            .encode_kitty(flags),
3023            " ".to_string()
3024        );
3025
3026        assert_eq!(
3027            make_event_with_raw(
3028                KeyEvent {
3029                    key: KeyCode::NumLock,
3030                    modifiers: Modifiers::NONE,
3031                    leds: KeyboardLedStatus::empty(),
3032                    repeat_count: 1,
3033                    key_is_down: true,
3034                    raw: None,
3035                    #[cfg(windows)]
3036                    win32_uni_char: None,
3037                },
3038                Some(PhysKeyCode::NumLock)
3039            )
3040            .encode_kitty(flags),
3041            "".to_string()
3042        );
3043
3044        assert_eq!(
3045            make_event_with_raw(
3046                KeyEvent {
3047                    key: KeyCode::CapsLock,
3048                    modifiers: Modifiers::NONE,
3049                    leds: KeyboardLedStatus::empty(),
3050                    repeat_count: 1,
3051                    key_is_down: true,
3052                    raw: None,
3053                    #[cfg(windows)]
3054                    win32_uni_char: None,
3055                },
3056                Some(PhysKeyCode::CapsLock)
3057            )
3058            .encode_kitty(flags),
3059            "".to_string()
3060        );
3061    }
3062
3063    #[test]
3064    fn encode_issue_4436() {
3065        let flags = KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES;
3066
3067        assert_eq!(
3068            KeyEvent {
3069                key: KeyCode::Char('q'),
3070                modifiers: Modifiers::NONE,
3071                leds: KeyboardLedStatus::empty(),
3072                repeat_count: 1,
3073                key_is_down: true,
3074                raw: None,
3075                #[cfg(windows)]
3076                win32_uni_char: None,
3077            }
3078            .encode_kitty(flags),
3079            "q".to_string()
3080        );
3081
3082        assert_eq!(
3083            KeyEvent {
3084                key: KeyCode::Char('f'),
3085                modifiers: Modifiers::SUPER,
3086                leds: KeyboardLedStatus::empty(),
3087                repeat_count: 1,
3088                key_is_down: true,
3089                raw: None,
3090                #[cfg(windows)]
3091                win32_uni_char: None,
3092            }
3093            .encode_kitty(flags),
3094            "\u{1b}[102;9u".to_string()
3095        );
3096
3097        assert_eq!(
3098            KeyEvent {
3099                key: KeyCode::Char('f'),
3100                modifiers: Modifiers::SUPER | Modifiers::SHIFT,
3101                leds: KeyboardLedStatus::empty(),
3102                repeat_count: 1,
3103                key_is_down: true,
3104                raw: None,
3105                #[cfg(windows)]
3106                win32_uni_char: None,
3107            }
3108            .encode_kitty(flags),
3109            "\u{1b}[102;10u".to_string()
3110        );
3111
3112        assert_eq!(
3113            KeyEvent {
3114                key: KeyCode::Char('f'),
3115                modifiers: Modifiers::SUPER | Modifiers::SHIFT | Modifiers::CTRL,
3116                leds: KeyboardLedStatus::empty(),
3117                repeat_count: 1,
3118                key_is_down: true,
3119                raw: None,
3120                #[cfg(windows)]
3121                win32_uni_char: None,
3122            }
3123            .encode_kitty(flags),
3124            "\u{1b}[102;14u".to_string()
3125        );
3126    }
3127}