os_terminal/
keyboard.rs

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}