1use alloc::string::{String, ToString};
2use pc_keyboard::layouts::Us104Key;
3use pc_keyboard::KeyCode::{self, *};
4use pc_keyboard::{DecodedKey, Keyboard};
5use pc_keyboard::{HandleControl, ScancodeSet1};
6
7#[derive(Debug)]
8pub enum KeyboardEvent {
9 AnsiString(String),
10 Copy,
11 Paste,
12 SetColorScheme(usize),
13 Scroll { up: bool, page: bool },
14 None,
15}
16
17pub struct KeyboardManager {
18 pub(crate) app_cursor_mode: bool,
19 pub(crate) crnl_mapping: bool,
20 keyboard: Keyboard<Us104Key, ScancodeSet1>,
21}
22
23impl Default for KeyboardManager {
24 fn default() -> Self {
25 Self {
26 app_cursor_mode: false,
27 crnl_mapping: false,
28 keyboard: Keyboard::new(
29 ScancodeSet1::new(),
30 Us104Key,
31 HandleControl::MapLettersToUnicode,
32 ),
33 }
34 }
35}
36
37impl KeyboardManager {
38 pub fn handle_keyboard(&mut self, scancode: u8) -> KeyboardEvent {
39 self.keyboard
40 .add_byte(scancode)
41 .ok()
42 .flatten()
43 .and_then(|event| self.keyboard.process_keyevent(event))
44 .map_or(KeyboardEvent::None, |key| self.key_to_event(key))
45 }
46}
47
48impl KeyboardManager {
49 pub fn key_to_event(&self, key: DecodedKey) -> KeyboardEvent {
50 let modifiers = self.keyboard.get_modifiers();
51
52 if modifiers.is_ctrl() && modifiers.is_shifted() {
53 let raw_key = match key {
54 DecodedKey::RawKey(k) => Some(k),
55 DecodedKey::Unicode('\x03') => Some(C),
56 DecodedKey::Unicode('\x16') => Some(V),
57 _ => None,
58 };
59
60 if let Some(k) = raw_key {
61 if let Some(event) = self.handle_function(k) {
62 return event;
63 }
64 }
65 }
66
67 match key {
68 DecodedKey::RawKey(k) => self
69 .generate_ansi_sequence(k)
70 .map(|s| KeyboardEvent::AnsiString(s.to_string()))
71 .unwrap_or(KeyboardEvent::None),
72 DecodedKey::Unicode(c) => match c {
73 '\x08' => KeyboardEvent::AnsiString("\x7f".to_string()),
74 '\x7f' => KeyboardEvent::AnsiString("\x1b[3~".to_string()),
75 '\n' if !self.crnl_mapping => KeyboardEvent::AnsiString("\r".to_string()),
76 _ => KeyboardEvent::AnsiString(c.to_string()),
77 },
78 }
79 }
80
81 fn handle_function(&self, key: KeyCode) -> Option<KeyboardEvent> {
82 if let Some(index) = match key {
83 F1 => Some(0),
84 F2 => Some(1),
85 F3 => Some(2),
86 F4 => Some(3),
87 F5 => Some(4),
88 F6 => Some(5),
89 F7 => Some(6),
90 F8 => Some(7),
91 _ => None,
92 } {
93 return Some(KeyboardEvent::SetColorScheme(index));
94 }
95
96 match key {
97 C => Some(KeyboardEvent::Copy),
98 V => Some(KeyboardEvent::Paste),
99 ArrowUp | PageUp => Some(KeyboardEvent::Scroll {
100 up: true,
101 page: matches!(key, PageUp),
102 }),
103 ArrowDown | PageDown => Some(KeyboardEvent::Scroll {
104 up: false,
105 page: matches!(key, PageDown),
106 }),
107 _ => None,
108 }
109 }
110
111 #[rustfmt::skip]
112 fn generate_ansi_sequence(&self, key: KeyCode) -> Option<&'static str> {
113 let sequence = match key {
114 F1 => "\x1bOP",
115 F2 => "\x1bOQ",
116 F3 => "\x1bOR",
117 F4 => "\x1bOS",
118 F5 => "\x1b[15~",
119 F6 => "\x1b[17~",
120 F7 => "\x1b[18~",
121 F8 => "\x1b[19~",
122 F9 => "\x1b[20~",
123 F10 => "\x1b[21~",
124 F11 => "\x1b[23~",
125 F12 => "\x1b[24~",
126 ArrowUp => if self.app_cursor_mode { "\x1bOA" } else { "\x1b[A" },
127 ArrowDown => if self.app_cursor_mode { "\x1bOB" } else { "\x1b[B" },
128 ArrowRight => if self.app_cursor_mode { "\x1bOC" } else { "\x1b[C" },
129 ArrowLeft => if self.app_cursor_mode { "\x1bOD" } else { "\x1b[D" },
130 Home => "\x1b[H",
131 End => "\x1b[F",
132 PageUp => "\x1b[5~",
133 PageDown => "\x1b[6~",
134 _ => return None,
135 };
136 Some(sequence)
137 }
138}