os_terminal/
keyboard.rs

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}