logitech_cve/
keyboard.rs

1use crate::device::Device;
2use core::time::Duration;
3use std::thread;
4
5#[repr(u8)]
6#[derive(Copy, Clone)]
7pub enum Key {
8    A = 0x4,
9    B = 0x5,
10    C = 0x6,
11    D = 0x7,
12    E = 0x8,
13    F = 0x9,
14    G = 0xA,
15    H = 0xB,
16    I = 0xC,
17    J = 0xD,
18    K = 0xE,
19    L = 0xF,
20    M = 0x10,
21    N = 0x11,
22    O = 0x12,
23    P = 0x13,
24    Q = 0x14,
25    R = 0x15,
26    S = 0x16,
27    T = 0x17,
28    U = 0x18,
29    V = 0x19,
30    W = 0x1A,
31    X = 0x1B,
32    Y = 0x1C,
33    Z = 0x1D,
34    N1 = 0x1E,
35    N2 = 0x1F,
36    N3 = 0x20,
37    N4 = 0x21,
38    N5 = 0x22,
39    N6 = 0x23,
40    N7 = 0x24,
41    N8 = 0x25,
42    N9 = 0x26,
43    N0 = 0x27,
44    Enter = 0x28,
45    Esc = 0x29,
46    BackSpace = 0x2A,
47    Tab = 0x2B,
48    Space = 0x2C,
49    Minus = 0x2D,
50    Equal = 0x2E,
51    SquareBracketLeft = 0x2F,
52    SquareBracketRight = 0x30,
53    BackSlash = 0x31,
54    BackSlash_ = 0x32,
55    Column = 0x33,
56    Quote = 0x34,
57    BackTick = 0x35,
58    Comma = 0x36,
59    Period = 0x37,
60    Slash = 0x38,
61    Cap = 0x39,
62    F1 = 0x3A,
63    F2 = 0x3B,
64    F3 = 0x3C,
65    F4 = 0x3D,
66    F5 = 0x3E,
67    F6 = 0x3F,
68    F7 = 0x40,
69    F8 = 0x41,
70    F9 = 0x42,
71    F10 = 0x43,
72    F11 = 0x44,
73    F12 = 0x45,
74    Snapshot = 0x46,
75    ScrollLock = 0x47,
76    Pause = 0x48,
77    Insert = 0x49,
78    Home = 0x4A,
79    PageUp = 0x4B,
80    Del = 0x4C,
81    End = 0x4D,
82    PageDown = 0x4E,
83    Right = 0x4F,
84    Left = 0x50,
85    Down = 0x51,
86    Up = 0x52,
87    Numlock = 0x53,
88    NumpadDiv = 0x54,
89    NumpadMul = 0x55,
90    NumpadMinus = 0x56,
91    NumpadPlus = 0x57,
92    NumpadEnter = 0x58,
93    Numpad1 = 0x59,
94    Numpad2 = 0x5A,
95    Numpad3 = 0x5B,
96    Numpad4 = 0x5C,
97    Numpad5 = 0x5D,
98    Numpad6 = 0x5E,
99    Numpad7 = 0x5F,
100    Numpad8 = 0x60,
101    Numpad9 = 0x61,
102    Numpad0 = 0x62,
103    NumpadDec = 0x63,
104    Apps = 0x65,
105    F13 = 0x68,
106    F14 = 0x69,
107    F15 = 0x6A,
108    F16 = 0x6B,
109    F17 = 0x6C,
110    F18 = 0x6D,
111    F19 = 0x6E,
112    F20 = 0x6F,
113    F21 = 0x70,
114    F22 = 0x71,
115    F23 = 0x72,
116    F24 = 0x73,
117    Rwin = 0x8C,
118    F24_ = 0x94,
119    Lctrl = 0xE0,
120    Lshift = 0xE1,
121    Lalt = 0xE2,
122    Lwin = 0xE3,
123    Rctrl = 0xE4,
124    Rshift = 0xE5,
125    Ralt = 0xE6,
126    Rwin_ = 0xE7,
127    NONE = 0x0,
128}
129
130impl From<Key> for u8 {
131    #[inline]
132    fn from(button: Key) -> Self {
133        button as Self
134    }
135}
136
137impl TryFrom<char> for Key {
138    type Error = String;
139
140    fn try_from(c: char) -> Result<Self, Self::Error> {
141        match c.to_ascii_uppercase() {
142            'A' => Ok(Self::A),
143            'B' => Ok(Self::B),
144            'C' => Ok(Self::C),
145            'D' => Ok(Self::D),
146            'E' => Ok(Self::E),
147            'F' => Ok(Self::F),
148            'G' => Ok(Self::G),
149            'H' => Ok(Self::H),
150            'I' => Ok(Self::I),
151            'J' => Ok(Self::J),
152            'K' => Ok(Self::K),
153            'L' => Ok(Self::L),
154            'M' => Ok(Self::M),
155            'N' => Ok(Self::N),
156            'O' => Ok(Self::O),
157            'P' => Ok(Self::P),
158            'Q' => Ok(Self::Q),
159            'R' => Ok(Self::R),
160            'S' => Ok(Self::S),
161            'T' => Ok(Self::T),
162            'U' => Ok(Self::U),
163            'V' => Ok(Self::V),
164            'W' => Ok(Self::W),
165            'X' => Ok(Self::X),
166            'Y' => Ok(Self::Y),
167            'Z' => Ok(Self::Z),
168            '\n' => Ok(Self::Enter),
169            '\t' => Ok(Self::Tab),
170            ' ' => Ok(Self::Space),
171            '1' | '!' => Ok(Self::N1),                 // ! for Shift + 1
172            '2' | '@' => Ok(Self::N2),                 // @ for Shift + 2
173            '3' | '#' => Ok(Self::N3),                 // # for Shift + 3
174            '4' | '$' => Ok(Self::N4),                 // $ for Shift + 4
175            '5' | '%' => Ok(Self::N5),                 // % for Shift + 5
176            '6' | '^' => Ok(Self::N6),                 // ^ for Shift + 6
177            '7' | '&' => Ok(Self::N7),                 // & for Shift + 7
178            '8' | '*' => Ok(Self::N8),                 // * for Shift + 8
179            '9' | '(' => Ok(Self::N9),                 // ( for Shift + 9
180            '0' | ')' => Ok(Self::N0),                 // ) for Shift + 0
181            '-' | '_' => Ok(Self::Minus),              // _ for Shift + -
182            '=' => Ok(Self::Equal),                    // + for Shift + =
183            '[' | '{' => Ok(Self::SquareBracketLeft),  // { for Shift + [
184            ']' | '}' => Ok(Self::SquareBracketRight), // } for Shift + ]
185            ';' | ':' => Ok(Self::Column),             // : for Shift + ;
186            '\'' | '"' => Ok(Self::Quote),             // " for Shift + '
187            '\\' | '|' => Ok(Self::BackSlash),         // | for Shift + \
188            ',' | '<' => Ok(Self::Comma),              // < for Shift + ,
189            '.' | '>' => Ok(Self::Period),             // > for Shift + .
190            '/' | '?' => Ok(Self::Slash),              // ? for Shift + /
191            '`' | '~' => Ok(Self::BackTick),           // ~ for Shift + `
192            _ => Err(format!("Unsupported character: {c}")),
193        }
194    }
195}
196
197/// A struct for controlling a virtual keyboard.
198///
199/// It holds a reference to a `Device` which is used to send the keyboard commands.
200pub struct Keyboard<'a> {
201    /// A reference to the device used to send keyboard commands.
202    device: &'a Device,
203}
204
205impl<'a> Keyboard<'a> {
206    /// Creates a new [`Keyboard`].
207    #[must_use]
208    pub const fn new(device: &'a Device) -> Self {
209        Self { device }
210    }
211
212    /// Presses a single keyboard button.
213    ///
214    /// The button is held down until a `release()` or `multi_press()` with `Key::NONE` is called.
215    ///
216    /// # Arguments
217    ///
218    /// * `button` - The `Key` to press.
219    #[inline]
220    pub fn press(&self, button: Key) {
221        self.device
222            .call_keyboard(button, Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE);
223    }
224
225    /// Releases all currently pressed keyboard buttons.
226    ///
227    /// This effectively sends a "no keys pressed" command to the device.
228    #[inline]
229    pub fn release(&self) {
230        self.device
231            .call_keyboard(Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE);
232    }
233
234    /// Presses and releases a single keyboard button.
235    ///
236    /// The button is pressed down, held for the specified duration, then released.
237    ///
238    /// # Arguments
239    ///
240    /// * `button` - The `Key` to press and release.
241    /// * `millis` - The duration in milliseconds to hold the button down before releasing it.
242    pub fn press_and_release(&self, button: Key, millis: u64) {
243        self.device
244            .call_keyboard(button, Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE);
245        thread::sleep(Duration::from_millis(millis));
246        self.device
247            .call_keyboard(Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE, Key::NONE);
248    }
249
250    /// Presses up to six keyboard buttons simultaneously.
251    ///
252    /// This can be used for pressing modifier keys and other keys at the same time.
253    ///
254    /// # Arguments
255    ///
256    /// * `button1` - The first `Key` to press.
257    /// * `button2` - The second `Key` to press.
258    /// * `button3` - The third `Key` to press.
259    /// * `button4` - The fourth `Key` to press.
260    /// * `button5` - The fifth `Key` to press.
261    /// * `button6` - The sixth `Key` to press.
262    #[inline]
263    pub fn multi_press(&self, button1: Key, button2: Key, button3: Key, button4: Key, button5: Key, button6: Key) {
264        self.device
265            .call_keyboard(button1, button2, button3, button4, button5, button6);
266    }
267
268    /// Types a string by simulating individual key presses for each character.
269    ///
270    /// # Arguments
271    ///
272    /// * `string` - The string to be typed.
273    /// * `millis` - The duration in milliseconds to hold the button down before releasing it.
274    ///
275    /// # Errors
276    ///
277    /// This function will return an error if a character in the input string
278    /// cannot be converted into a valid `Key` enum variant.
279    pub fn type_string(&self, string: &str, millis: u64) -> Result<(), String> {
280        for c in string.chars() {
281            let key = Key::try_from(c)?;
282            match c {
283                'a'..='z'
284                | '0'..='9'
285                | '\n'
286                | '\t'
287                | ' '
288                | '-'
289                | '='
290                | '['
291                | ']'
292                | '\\'
293                | ';'
294                | '\''
295                | '`'
296                | ','
297                | '.'
298                | '/' => self.press_and_release(key, millis),
299
300                'A'..='Z'
301                | '!'
302                | '@'
303                | '#'
304                | '$'
305                | '%'
306                | '^'
307                | '&'
308                | '*'
309                | '('
310                | ')'
311                | '_'
312                | '+'
313                | '{'
314                | '}'
315                | ':'
316                | '"'
317                | '|'
318                | '<'
319                | '>'
320                | '?'
321                | '~' => {
322                    self.multi_press(Key::Lshift, key, Key::NONE, Key::NONE, Key::NONE, Key::NONE);
323                    thread::sleep(Duration::from_millis(millis));
324                    self.release();
325                }
326                _ => {}
327            }
328        }
329
330        Ok(())
331    }
332}