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