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#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, FromDynamic, ToDynamic)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub enum KeyCode {
23 Char(char),
25 Composed(String),
26 RawCode(u32),
27 Physical(PhysKeyCode),
28
29 Hyper,
30 Super,
31 Meta,
32
33 Cancel,
35 Clear,
39 Shift,
41 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 Help,
68 LeftWindows,
69 RightWindows,
70 Applications,
71 Sleep,
72 Numpad(u8),
74 Multiply,
75 Add,
76 Separator,
77 Subtract,
78 Decimal,
79 Divide,
80 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 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 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 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 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 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 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 pub separator: &'a str,
540 pub want_none: bool,
542 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 const md_apple_keyboard_command: &str = "\u{f0633}"; const md_apple_keyboard_control: &str = "\u{f0634}"; const md_apple_keyboard_option: &str = "\u{f0635}"; const md_apple_keyboard_shift: &str = "\u{f0636}"; const md_microsoft_windows: &str = "\u{f05b3}"; 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 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#[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 pub coords: Point,
1204 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#[derive(Debug, Clone, Eq, PartialEq)]
1237pub struct RawKeyEvent {
1238 pub key: KeyCode,
1239 pub modifiers: Modifiers,
1240 pub leds: KeyboardLedStatus,
1241
1242 pub phys_code: Option<PhysKeyCode>,
1244 pub raw_code: u32,
1246
1247 #[cfg(windows)]
1249 pub scan_code: u32,
1250
1251 pub repeat_count: u16,
1253
1254 pub key_is_down: bool,
1256 pub handled: Handled,
1257}
1258
1259impl RawKeyEvent {
1260 pub fn set_handled(&self) {
1263 self.handled.set_handled();
1264 }
1265
1266 #[deny(warnings)]
1268 fn kitty_function_code(&self) -> Option<u32> {
1269 use KeyCode::*;
1270 Some(match self.key {
1271 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 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 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 NumLock => 57360,
1327 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 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 Insert => 57425,
1382 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 pub key: KeyCode,
1413 pub modifiers: Modifiers,
1415
1416 pub leds: KeyboardLedStatus,
1417
1418 pub repeat_count: u16,
1420
1421 pub key_is_down: bool,
1423
1424 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 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 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 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 #[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 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 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 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::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, 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 '\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, );
1822
1823 if use_legacy {
1824 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 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 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 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 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
2104fn 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
2143pub 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', _ => return None,
2187 })
2188}
2189
2190#[derive(Debug, FromDynamic, ToDynamic, Clone, Copy, PartialEq, Eq)]
2191pub enum UIKeyCapRendering {
2192 UnixLong,
2194 Emacs,
2196 AppleSymbols,
2198 WindowsLong,
2200 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}