os_terminal/
keyboard.rs

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