Skip to main content

usehid_core/
keyboard.rs

1//! Virtual Keyboard implementation
2
3use crate::error::{Error, Result};
4use crate::hid::KeyboardReport;
5use crate::platform::HidBackend;
6use crate::Device;
7use bitflags::bitflags;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11bitflags! {
12    /// Keyboard modifier flags
13    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
14    pub struct Modifiers: u8 {
15        const LEFT_CTRL = 0x01;
16        const LEFT_SHIFT = 0x02;
17        const LEFT_ALT = 0x04;
18        const LEFT_GUI = 0x08;
19        const RIGHT_CTRL = 0x10;
20        const RIGHT_SHIFT = 0x20;
21        const RIGHT_ALT = 0x40;
22        const RIGHT_GUI = 0x80;
23        
24        const CTRL = Self::LEFT_CTRL.bits();
25        const SHIFT = Self::LEFT_SHIFT.bits();
26        const ALT = Self::LEFT_ALT.bits();
27        const GUI = Self::LEFT_GUI.bits();
28        const CMD = Self::LEFT_GUI.bits();
29    }
30}
31
32/// Key codes (USB HID usage IDs)
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[repr(u8)]
35pub enum Key {
36    // Letters
37    A = 0x04, B = 0x05, C = 0x06, D = 0x07, E = 0x08, F = 0x09,
38    G = 0x0A, H = 0x0B, I = 0x0C, J = 0x0D, K = 0x0E, L = 0x0F,
39    M = 0x10, N = 0x11, O = 0x12, P = 0x13, Q = 0x14, R = 0x15,
40    S = 0x16, T = 0x17, U = 0x18, V = 0x19, W = 0x1A, X = 0x1B,
41    Y = 0x1C, Z = 0x1D,
42    
43    // Numbers
44    Num1 = 0x1E, Num2 = 0x1F, Num3 = 0x20, Num4 = 0x21, Num5 = 0x22,
45    Num6 = 0x23, Num7 = 0x24, Num8 = 0x25, Num9 = 0x26, Num0 = 0x27,
46    
47    // Special keys
48    Enter = 0x28,
49    Escape = 0x29,
50    Backspace = 0x2A,
51    Tab = 0x2B,
52    Space = 0x2C,
53    Minus = 0x2D,
54    Equal = 0x2E,
55    LeftBracket = 0x2F,
56    RightBracket = 0x30,
57    Backslash = 0x31,
58    Semicolon = 0x33,
59    Quote = 0x34,
60    Grave = 0x35,
61    Comma = 0x36,
62    Period = 0x37,
63    Slash = 0x38,
64    CapsLock = 0x39,
65    
66    // Function keys
67    F1 = 0x3A, F2 = 0x3B, F3 = 0x3C, F4 = 0x3D, F5 = 0x3E, F6 = 0x3F,
68    F7 = 0x40, F8 = 0x41, F9 = 0x42, F10 = 0x43, F11 = 0x44, F12 = 0x45,
69    
70    // Navigation
71    PrintScreen = 0x46,
72    ScrollLock = 0x47,
73    Pause = 0x48,
74    Insert = 0x49,
75    Home = 0x4A,
76    PageUp = 0x4B,
77    Delete = 0x4C,
78    End = 0x4D,
79    PageDown = 0x4E,
80    Right = 0x4F,
81    Left = 0x50,
82    Down = 0x51,
83    Up = 0x52,
84}
85
86impl Key {
87    /// Parse key from string
88    pub fn from_str(s: &str) -> Result<Self> {
89        let key = match s.to_lowercase().as_str() {
90            "a" => Key::A, "b" => Key::B, "c" => Key::C, "d" => Key::D,
91            "e" => Key::E, "f" => Key::F, "g" => Key::G, "h" => Key::H,
92            "i" => Key::I, "j" => Key::J, "k" => Key::K, "l" => Key::L,
93            "m" => Key::M, "n" => Key::N, "o" => Key::O, "p" => Key::P,
94            "q" => Key::Q, "r" => Key::R, "s" => Key::S, "t" => Key::T,
95            "u" => Key::U, "v" => Key::V, "w" => Key::W, "x" => Key::X,
96            "y" => Key::Y, "z" => Key::Z,
97            "1" => Key::Num1, "2" => Key::Num2, "3" => Key::Num3,
98            "4" => Key::Num4, "5" => Key::Num5, "6" => Key::Num6,
99            "7" => Key::Num7, "8" => Key::Num8, "9" => Key::Num9, "0" => Key::Num0,
100            "enter" | "return" => Key::Enter,
101            "escape" | "esc" => Key::Escape,
102            "backspace" => Key::Backspace,
103            "tab" => Key::Tab,
104            "space" | " " => Key::Space,
105            "up" => Key::Up,
106            "down" => Key::Down,
107            "left" => Key::Left,
108            "right" => Key::Right,
109            "home" => Key::Home,
110            "end" => Key::End,
111            "pageup" => Key::PageUp,
112            "pagedown" => Key::PageDown,
113            "delete" => Key::Delete,
114            "insert" => Key::Insert,
115            "f1" => Key::F1, "f2" => Key::F2, "f3" => Key::F3, "f4" => Key::F4,
116            "f5" => Key::F5, "f6" => Key::F6, "f7" => Key::F7, "f8" => Key::F8,
117            "f9" => Key::F9, "f10" => Key::F10, "f11" => Key::F11, "f12" => Key::F12,
118            _ => return Err(Error::InvalidKey(s.to_string())),
119        };
120        Ok(key)
121    }
122}
123
124/// Virtual keyboard device
125pub struct Keyboard {
126    backend: Option<Box<dyn HidBackend>>,
127    report: KeyboardReport,
128    name: String,
129    char_map: HashMap<char, (Key, bool)>, // (key, needs_shift)
130}
131
132impl Keyboard {
133    /// Create a new virtual keyboard
134    pub fn new() -> Self {
135        Self::with_name("useHID Virtual Keyboard")
136    }
137    
138    /// Create a new virtual keyboard with custom name
139    pub fn with_name(name: &str) -> Self {
140        let mut kb = Self {
141            backend: None,
142            report: KeyboardReport::default(),
143            name: name.to_string(),
144            char_map: HashMap::new(),
145        };
146        kb.init_char_map();
147        kb
148    }
149    
150    fn init_char_map(&mut self) {
151        // Lowercase letters
152        for c in 'a'..='z' {
153            let key = match c {
154                'a' => Key::A, 'b' => Key::B, 'c' => Key::C, 'd' => Key::D,
155                'e' => Key::E, 'f' => Key::F, 'g' => Key::G, 'h' => Key::H,
156                'i' => Key::I, 'j' => Key::J, 'k' => Key::K, 'l' => Key::L,
157                'm' => Key::M, 'n' => Key::N, 'o' => Key::O, 'p' => Key::P,
158                'q' => Key::Q, 'r' => Key::R, 's' => Key::S, 't' => Key::T,
159                'u' => Key::U, 'v' => Key::V, 'w' => Key::W, 'x' => Key::X,
160                'y' => Key::Y, 'z' => Key::Z,
161                _ => unreachable!(),
162            };
163            self.char_map.insert(c, (key, false));
164            self.char_map.insert(c.to_ascii_uppercase(), (key, true));
165        }
166        
167        // Numbers
168        let numbers = [
169            ('1', Key::Num1), ('2', Key::Num2), ('3', Key::Num3),
170            ('4', Key::Num4), ('5', Key::Num5), ('6', Key::Num6),
171            ('7', Key::Num7), ('8', Key::Num8), ('9', Key::Num9), ('0', Key::Num0),
172        ];
173        for (c, key) in numbers {
174            self.char_map.insert(c, (key, false));
175        }
176        
177        // Shifted numbers (symbols)
178        let shifted = [
179            ('!', Key::Num1), ('@', Key::Num2), ('#', Key::Num3),
180            ('$', Key::Num4), ('%', Key::Num5), ('^', Key::Num6),
181            ('&', Key::Num7), ('*', Key::Num8), ('(', Key::Num9), (')', Key::Num0),
182        ];
183        for (c, key) in shifted {
184            self.char_map.insert(c, (key, true));
185        }
186        
187        // Other characters
188        self.char_map.insert(' ', (Key::Space, false));
189        self.char_map.insert('\n', (Key::Enter, false));
190        self.char_map.insert('\t', (Key::Tab, false));
191        self.char_map.insert('-', (Key::Minus, false));
192        self.char_map.insert('_', (Key::Minus, true));
193        self.char_map.insert('=', (Key::Equal, false));
194        self.char_map.insert('+', (Key::Equal, true));
195        self.char_map.insert('[', (Key::LeftBracket, false));
196        self.char_map.insert('{', (Key::LeftBracket, true));
197        self.char_map.insert(']', (Key::RightBracket, false));
198        self.char_map.insert('}', (Key::RightBracket, true));
199        self.char_map.insert('\\', (Key::Backslash, false));
200        self.char_map.insert('|', (Key::Backslash, true));
201        self.char_map.insert(';', (Key::Semicolon, false));
202        self.char_map.insert(':', (Key::Semicolon, true));
203        self.char_map.insert('\'', (Key::Quote, false));
204        self.char_map.insert('"', (Key::Quote, true));
205        self.char_map.insert('`', (Key::Grave, false));
206        self.char_map.insert('~', (Key::Grave, true));
207        self.char_map.insert(',', (Key::Comma, false));
208        self.char_map.insert('<', (Key::Comma, true));
209        self.char_map.insert('.', (Key::Period, false));
210        self.char_map.insert('>', (Key::Period, true));
211        self.char_map.insert('/', (Key::Slash, false));
212        self.char_map.insert('?', (Key::Slash, true));
213    }
214    
215    /// Press a key
216    pub fn press_key(&mut self, key: Key) -> Result<()> {
217        // Find empty slot in keys array
218        for i in 0..6 {
219            if self.report.keys[i] == 0 {
220                self.report.keys[i] = key as u8;
221                break;
222            }
223        }
224        self.send_report()
225    }
226    
227    /// Release a key
228    pub fn release_key(&mut self, key: Key) -> Result<()> {
229        for i in 0..6 {
230            if self.report.keys[i] == key as u8 {
231                self.report.keys[i] = 0;
232                break;
233            }
234        }
235        self.send_report()
236    }
237    
238    /// Press modifier(s)
239    pub fn press_modifiers(&mut self, mods: Modifiers) -> Result<()> {
240        self.report.modifiers |= mods.bits();
241        self.send_report()
242    }
243    
244    /// Release modifier(s)
245    pub fn release_modifiers(&mut self, mods: Modifiers) -> Result<()> {
246        self.report.modifiers &= !mods.bits();
247        self.send_report()
248    }
249    
250    /// Tap a key (press and release)
251    pub fn tap(&mut self, key: Key) -> Result<()> {
252        self.press_key(key)?;
253        std::thread::sleep(std::time::Duration::from_millis(10));
254        self.release_key(key)
255    }
256    
257    /// Press key combination (e.g., Ctrl+C)
258    pub fn press_combo(&mut self, mods: Modifiers, key: Key) -> Result<()> {
259        self.press_modifiers(mods)?;
260        self.tap(key)?;
261        self.release_modifiers(mods)
262    }
263    
264    /// Type a string
265    pub fn type_text(&mut self, text: &str) -> Result<()> {
266        for c in text.chars() {
267            if let Some(&(key, shift)) = self.char_map.get(&c) {
268                if shift {
269                    self.press_modifiers(Modifiers::SHIFT)?;
270                }
271                self.tap(key)?;
272                if shift {
273                    self.release_modifiers(Modifiers::SHIFT)?;
274                }
275                std::thread::sleep(std::time::Duration::from_millis(5));
276            }
277        }
278        Ok(())
279    }
280    
281    /// Release all keys
282    pub fn release_all(&mut self) -> Result<()> {
283        self.report = KeyboardReport::default();
284        self.send_report()
285    }
286    
287    fn send_report(&self) -> Result<()> {
288        if let Some(backend) = &self.backend {
289            backend.send_report(self.report.as_bytes())
290        } else {
291            Err(Error::DeviceNotCreated)
292        }
293    }
294}
295
296impl Default for Keyboard {
297    fn default() -> Self {
298        Self::new()
299    }
300}
301
302impl Device for Keyboard {
303    fn create(&mut self) -> Result<()> {
304        if self.backend.is_some() {
305            return Err(Error::DeviceAlreadyExists);
306        }
307        
308        let backend = crate::platform::create_keyboard_backend(&self.name)?;
309        self.backend = Some(backend);
310        Ok(())
311    }
312    
313    fn destroy(&mut self) -> Result<()> {
314        if let Some(backend) = self.backend.take() {
315            backend.destroy()
316        } else {
317            Err(Error::DeviceNotCreated)
318        }
319    }
320    
321    fn is_created(&self) -> bool {
322        self.backend.is_some()
323    }
324}