diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c6520..2ff0155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix 102/105-key German layout: Add missing Alt-Gr combinations * Added tool to print keyboard layouts as ASCII-art * Cleaned up how layouts are implemented +* Added support for decoding USB HID Boot Keyboard reports ## v0.8.0 (13 Sep 2024) diff --git a/src/layouts/azerty.rs b/src/layouts/azerty.rs index 35fc007..59f1577 100644 --- a/src/layouts/azerty.rs +++ b/src/layouts/azerty.rs @@ -193,11 +193,11 @@ impl KeyboardLayout for Azerty { #[cfg(test)] mod test { use super::*; - use crate::{KeyCode, KeyEvent, KeyState, Keyboard, ScancodeSet2}; + use crate::{KeyCode, KeyEvent, KeyState, PS2Keyboard, ScancodeSet2}; #[test] fn test_frazert() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), Azerty, HandleControl::MapLettersToUnicode, diff --git a/src/layouts/uk105.rs b/src/layouts/uk105.rs index 88812ab..ea954b5 100644 --- a/src/layouts/uk105.rs +++ b/src/layouts/uk105.rs @@ -171,7 +171,9 @@ impl KeyboardLayout for Uk105Key { #[cfg(test)] mod test { use super::*; - use crate::{EventDecoder, HandleControl, Keyboard, ScancodeSet, ScancodeSet1, ScancodeSet2}; + use crate::{ + EventDecoder, HandleControl, PS2Keyboard, ScancodeSet, ScancodeSet1, ScancodeSet2, + }; #[test] fn layout() { @@ -238,7 +240,7 @@ mod test { #[test] fn test_hash() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), Uk105Key, HandleControl::MapLettersToUnicode, @@ -251,7 +253,7 @@ mod test { #[test] fn test_backslash() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), Uk105Key, HandleControl::MapLettersToUnicode, @@ -264,7 +266,7 @@ mod test { #[test] fn test_tilde() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), Uk105Key, HandleControl::MapLettersToUnicode, @@ -279,7 +281,7 @@ mod test { #[test] fn test_pipe() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), Uk105Key, HandleControl::MapLettersToUnicode, diff --git a/src/lib.rs b/src/lib.rs index 3f61270..f62ed55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ //! Unicode characters (where possible) according to the currently selected //! `KeyboardLayout`. //! -//! There is also `Keyboard` which combines the above three functions into a +//! There is also `PS2Keyboard` which combines the above three functions into a //! single object. //! //! See the [`examples`](./examples) folder for more details. @@ -152,147 +152,146 @@ //! any MS-DOS applications that read raw scancodes from the keyboard. //! //! This table shows the correspondence between our symbolic KeyCode, Scancode Set 1 -//! and Scancode Set 2. We may extend this in the future to also handle USB HID -//! Scancodes. Any codes prefixed `0xE0` or `0xE1` are *extended* multi-byte +//! and Scancode Set 2. Any codes prefixed `0xE0` or `0xE1` are *extended* multi-byte //! scancodes. Typically these are keys that were not on the IBM PC and PC/XT //! keyboards so they they were added in such a way that if you ignored the 0xE0, //! you got a reasonable result anyway. For example `ArrowLeft` is `0xE04B` in //! Scancode Set 1 because `Numpad4` is `0x4B` and that was the left-arrow key on an //! IBM PC or PC/XT. //! -//! | Symbolic Key | Scancode Set 1 | Scancode Set 2 | -//! | -------------- | -------------- | -------------- | -//! | Escape | 0x01 | 0x76 | -//! | F1 | 0x3B | 0x05 | -//! | F2 | 0x3C | 0x06 | -//! | F3 | 0x3D | 0x04 | -//! | F4 | 0x3E | 0x0C | -//! | F5 | 0x3F | 0x03 | -//! | F6 | 0x40 | 0x0B | -//! | F7 | 0x41 | 0x83 | -//! | F8 | 0x42 | 0x0A | -//! | F9 | 0x43 | 0x01 | -//! | F10 | 0x44 | 0x09 | -//! | F11 | 0x57 | 0x78 | -//! | F12 | 0x58 | 0x07 | -//! | PrintScreen | 0xE037 | 0xE07C | -//! | SysRq | 0x54 | 0x7F | -//! | ScrollLock | 0x46 | 0x7E | -//! | PauseBreak | -- | -- | -//! | - | -- | -- | -//! | Oem8 | 0x29 | 0x0E | -//! | Key1 | 0x02 | 0x16 | -//! | Key2 | 0x03 | 0x1E | -//! | Key3 | 0x04 | 0x26 | -//! | Key4 | 0x05 | 0x25 | -//! | Key5 | 0x06 | 0x2E | -//! | Key6 | 0x07 | 0x36 | -//! | Key7 | 0x08 | 0x3D | -//! | Key8 | 0x09 | 0x3E | -//! | Key9 | 0x0A | 0x46 | -//! | Key0 | 0x0B | 0x45 | -//! | OemMinus | 0x0C | 0x4E | -//! | OemPlus | 0x0D | 0x55 | -//! | Backspace | 0x0E | 0x66 | -//! | Insert | 0xE052 | 0xE070 | -//! | Home | 0xE047 | 0xE06C | -//! | PageUp | 0xE049 | 0xE07D | -//! | NumpadLock | 0x45 | 0x77 | -//! | NumpadDivide | 0xE035 | 0xE04A | -//! | NumpadMultiply | 0x37 | 0x7C | -//! | NumpadSubtract | 0x4A | 0x7B | -//! | - | -- | -- | -//! | Tab | 0x0F | 0x0D | -//! | Q | 0x10 | 0x15 | -//! | W | 0x11 | 0x1D | -//! | E | 0x12 | 0x24 | -//! | R | 0x13 | 0x2D | -//! | T | 0x14 | 0x2C | -//! | Y | 0x15 | 0x35 | -//! | U | 0x16 | 0x3C | -//! | I | 0x17 | 0x43 | -//! | O | 0x18 | 0x44 | -//! | P | 0x19 | 0x4D | -//! | Oem4 | 0x1A | 0x54 | -//! | Oem6 | 0x1B | 0x5B | -//! | Oem5 | 0x56 | 0x61 | -//! | Oem7 | 0x2B | 0x5D | -//! | Delete | 0xE053 | 0xE071 | -//! | End | 0xE04F | 0xE069 | -//! | PageDown | 0xE051 | 0xE07A | -//! | Numpad7 | 0x47 | 0x6C | -//! | Numpad8 | 0x48 | 0x75 | -//! | Numpad9 | 0x49 | 0x7D | -//! | NumpadAdd | 0x4E | 0x79 | -//! | - | -- | -- | -//! | CapsLock | 0x3A | 0x58 | -//! | A | 0x1E | 0x1C | -//! | S | 0x1F | 0x1B | -//! | D | 0x20 | 0x23 | -//! | F | 0x21 | 0x2B | -//! | G | 0x22 | 0x34 | -//! | H | 0x23 | 0x33 | -//! | J | 0x24 | 0x3B | -//! | K | 0x25 | 0x42 | -//! | L | 0x26 | 0x4B | -//! | Oem1 | 0x27 | 0x4C | -//! | Oem3 | 0x28 | 0x52 | -//! | Return | 0x1C | 0x5A | -//! | Numpad4 | 0x4B | 0x6B | -//! | Numpad5 | 0x4C | 0x73 | -//! | Numpad6 | 0x4D | 0x74 | -//! | - | -- | -- | -//! | LShift | 0x2A | 0x12 | -//! | Z | 0x2C | 0x1A | -//! | X | 0x2D | 0x22 | -//! | C | 0x2E | 0x21 | -//! | V | 0x2F | 0x2A | -//! | B | 0x30 | 0x32 | -//! | N | 0x31 | 0x31 | -//! | M | 0x32 | 0x3A | -//! | OemComma | 0x33 | 0x41 | -//! | OemPeriod | 0x34 | 0x49 | -//! | Oem2 | 0x35 | 0x4A | -//! | RShift | 0x36 | 0x59 | -//! | ArrowUp | 0xE048 | 0xE075 | -//! | Numpad1 | 0x4F | 0x69 | -//! | Numpad2 | 0x50 | 0x72 | -//! | Numpad3 | 0x51 | 0x7A | -//! | NumpadEnter | 0xE01C | 0xE075 | -//! | - | -- | -- | -//! | LControl | 0x1D | 0x14 | -//! | LWin | 0xE05B | 0xE01F | -//! | LAlt | 0x38 | 0x11 | -//! | Spacebar | 0x39 | 0x29 | -//! | RAltGr | 0xE038 | 0xE011 | -//! | RWin | 0xE05C | 0xE027 | -//! | Apps | 0xE05C | 0xE02F | -//! | RControl | 0xE01D | 0xE014 | -//! | ArrowLeft | 0xE04B | 0xE06B | -//! | ArrowDown | 0xE050 | 0xE072 | -//! | ArrowRight | 0xE04D | 0xE074 | -//! | Numpad0 | 0x52 | 0x70 | -//! | NumpadPeriod | 0x53 | 0x71 | -//! | - | -- | -- | -//! | Oem9 | 0x7B | 0x67 | -//! | Oem10 | 0x79 | 0x64 | -//! | Oem11 | 0x70 | 0x13 | -//! | Oem12 | 0x73 | 0x51 | -//! | Oem13 | 0x7D | 0x6A | -//! | - | -- | -- | -//! | PrevTrack | 0xE010 | 0xE015 | -//! | NextTrack | 0xE019 | 0xE04D | -//! | Mute | 0xE020 | 0xE023 | -//! | Calculator | 0xE021 | 0xE02B | -//! | Play | 0xE022 | 0xE034 | -//! | Stop | 0xE024 | 0xE03B | -//! | VolumeDown | 0xE02E | 0xE021 | -//! | VolumeUp | 0xE030 | 0xE032 | -//! | WWWHome | 0xE032 | 0xE03A | -//! | TooManyKeys | -- | 0x00 | -//! | PowerOnTestOk | -- | 0xAA | -//! | RControl2 | 0xE11D | 0xE114 | -//! | RAlt2 | 0xE02A | 0xE012 | +//! | Symbolic Key | Scancode Set 1 | Scancode Set 2 | USB HID | +//! | -------------- | -------------- | -------------- | ------- | +//! | Escape | 0x01 | 0x76 | 0x29 | +//! | F1 | 0x3B | 0x05 | 0x3A | +//! | F2 | 0x3C | 0x06 | 0x3B | +//! | F3 | 0x3D | 0x04 | 0x3C | +//! | F4 | 0x3E | 0x0C | 0x3D | +//! | F5 | 0x3F | 0x03 | 0x3E | +//! | F6 | 0x40 | 0x0B | 0x3F | +//! | F7 | 0x41 | 0x83 | 0x40 | +//! | F8 | 0x42 | 0x0A | 0x41 | +//! | F9 | 0x43 | 0x01 | 0x42 | +//! | F10 | 0x44 | 0x09 | 0x43 | +//! | F11 | 0x57 | 0x78 | 0x44 | +//! | F12 | 0x58 | 0x07 | 0x45 | +//! | PrintScreen | 0xE037 | 0xE07C | 0x46 | +//! | SysRq | 0x54 | 0x7F | -- | +//! | ScrollLock | 0x46 | 0x7E | 0x47 | +//! | PauseBreak | -- | -- | 0x48 | +//! | - | -- | -- | -- | +//! | Oem8 | 0x29 | 0x0E | 0x35 | +//! | Key1 | 0x02 | 0x16 | 0x1E | +//! | Key2 | 0x03 | 0x1E | 0x1F | +//! | Key3 | 0x04 | 0x26 | 0x20 | +//! | Key4 | 0x05 | 0x25 | 0x21 | +//! | Key5 | 0x06 | 0x2E | 0x22 | +//! | Key6 | 0x07 | 0x36 | 0x23 | +//! | Key7 | 0x08 | 0x3D | 0x24 | +//! | Key8 | 0x09 | 0x3E | 0x25 | +//! | Key9 | 0x0A | 0x46 | 0x26 | +//! | Key0 | 0x0B | 0x45 | 0x27 | +//! | OemMinus | 0x0C | 0x4E | 0x2D | +//! | OemPlus | 0x0D | 0x55 | 0x2E | +//! | Backspace | 0x0E | 0x66 | 0x2A | +//! | Insert | 0xE052 | 0xE070 | 0x49 | +//! | Home | 0xE047 | 0xE06C | 0x4A | +//! | PageUp | 0xE049 | 0xE07D | 0x4B | +//! | NumpadLock | 0x45 | 0x77 | 0x53 | +//! | NumpadDivide | 0xE035 | 0xE04A | 0x54 | +//! | NumpadMultiply | 0x37 | 0x7C | 0x55 | +//! | NumpadSubtract | 0x4A | 0x7B | 0x56 | +//! | - | -- | -- | -- | +//! | Tab | 0x0F | 0x0D | 0x2B | +//! | Q | 0x10 | 0x15 | 0x14 | +//! | W | 0x11 | 0x1D | 0x1A | +//! | E | 0x12 | 0x24 | 0x08 | +//! | R | 0x13 | 0x2D | 0x15 | +//! | T | 0x14 | 0x2C | 0x17 | +//! | Y | 0x15 | 0x35 | 0x1C | +//! | U | 0x16 | 0x3C | 0x18 | +//! | I | 0x17 | 0x43 | 0x0C | +//! | O | 0x18 | 0x44 | 0x12 | +//! | P | 0x19 | 0x4D | 0x13 | +//! | Oem4 | 0x1A | 0x54 | 0x2F | +//! | Oem6 | 0x1B | 0x5B | 0x30 | +//! | Oem5 | 0x56 | 0x61 | 0x64 | +//! | Oem7 | 0x2B | 0x5D | 0x31 | +//! | Delete | 0xE053 | 0xE071 | 0x4C | +//! | End | 0xE04F | 0xE069 | 0x4D | +//! | PageDown | 0xE051 | 0xE07A | 0x4E | +//! | Numpad7 | 0x47 | 0x6C | 0x5F | +//! | Numpad8 | 0x48 | 0x75 | 0x60 | +//! | Numpad9 | 0x49 | 0x7D | 0x61 | +//! | NumpadAdd | 0x4E | 0x79 | 0x57 | +//! | - | -- | -- | -- | +//! | CapsLock | 0x3A | 0x58 | 0x39 | +//! | A | 0x1E | 0x1C | 0x04 | +//! | S | 0x1F | 0x1B | 0x16 | +//! | D | 0x20 | 0x23 | 0x07 | +//! | F | 0x21 | 0x2B | 0x09 | +//! | G | 0x22 | 0x34 | 0x0A | +//! | H | 0x23 | 0x33 | 0x0B | +//! | J | 0x24 | 0x3B | 0x0D | +//! | K | 0x25 | 0x42 | 0x0E | +//! | L | 0x26 | 0x4B | 0x0F | +//! | Oem1 | 0x27 | 0x4C | 0x33 | +//! | Oem3 | 0x28 | 0x52 | 0x34 | +//! | Return | 0x1C | 0x5A | 0x28 | +//! | Numpad4 | 0x4B | 0x6B | 0x5C | +//! | Numpad5 | 0x4C | 0x73 | 0x5D | +//! | Numpad6 | 0x4D | 0x74 | 0x5E | +//! | - | -- | -- | -- | +//! | LShift | 0x2A | 0x12 | 0xE1 | +//! | Z | 0x2C | 0x1A | 0x1D | +//! | X | 0x2D | 0x22 | 0x1B | +//! | C | 0x2E | 0x21 | 0x06 | +//! | V | 0x2F | 0x2A | 0x19 | +//! | B | 0x30 | 0x32 | 0x05 | +//! | N | 0x31 | 0x31 | 0x11 | +//! | M | 0x32 | 0x3A | 0x10 | +//! | OemComma | 0x33 | 0x41 | 0x36 | +//! | OemPeriod | 0x34 | 0x49 | 0x37 | +//! | Oem2 | 0x35 | 0x4A | 0x38 | +//! | RShift | 0x36 | 0x59 | 0xE5 | +//! | ArrowUp | 0xE048 | 0xE075 | 0x52 | +//! | Numpad1 | 0x4F | 0x69 | 0x59 | +//! | Numpad2 | 0x50 | 0x72 | 0x5A | +//! | Numpad3 | 0x51 | 0x7A | 0x5B | +//! | NumpadEnter | 0xE01C | 0xE075 | 0x58 | +//! | - | -- | -- | -- | +//! | LControl | 0x1D | 0x14 | 0xE0 | +//! | LWin | 0xE05B | 0xE01F | 0xE3 | +//! | LAlt | 0x38 | 0x11 | 0xE2 | +//! | Spacebar | 0x39 | 0x29 | 0x2C | +//! | RAltGr | 0xE038 | 0xE011 | 0xE6 | +//! | RWin | 0xE05C | 0xE027 | 0xE7 | +//! | Apps | 0xE05C | 0xE02F | 0x65 | +//! | RControl | 0xE01D | 0xE014 | 0xE4 | +//! | ArrowLeft | 0xE04B | 0xE06B | 0x50 | +//! | ArrowDown | 0xE050 | 0xE072 | 0x51 | +//! | ArrowRight | 0xE04D | 0xE074 | 0x52 | +//! | Numpad0 | 0x52 | 0x70 | 0x62 | +//! | NumpadPeriod | 0x53 | 0x71 | 0x63 | +//! | - | -- | -- | -- | +//! | Oem9 | 0x7B | 0x67 | ?? | +//! | Oem10 | 0x79 | 0x64 | ?? | +//! | Oem11 | 0x70 | 0x13 | ?? | +//! | Oem12 | 0x73 | 0x51 | ?? | +//! | Oem13 | 0x7D | 0x6A | ?? | +//! | - | -- | -- | ?? | +//! | PrevTrack | 0xE010 | 0xE015 | ?? | +//! | NextTrack | 0xE019 | 0xE04D | ?? | +//! | Mute | 0xE020 | 0xE023 | ?? | +//! | Calculator | 0xE021 | 0xE02B | ?? | +//! | Play | 0xE022 | 0xE034 | ?? | +//! | Stop | 0xE024 | 0xE03B | ?? | +//! | VolumeDown | 0xE02E | 0xE021 | ?? | +//! | VolumeUp | 0xE030 | 0xE032 | ?? | +//! | WWWHome | 0xE032 | 0xE03A | ?? | +//! | TooManyKeys | -- | 0x00 | ?? | +//! | PowerOnTestOk | -- | 0xAA | ?? | +//! | RControl2 | 0xE11D | 0xE114 | ?? | +//! | RAlt2 | 0xE02A | 0xE012 | ?? | //! //! __Note 1:__ `PauseBreak` does not have a scancode because it's something we infer from a //! sequence of other keypresses (`NumLock` with `RControl2` held). @@ -311,7 +310,7 @@ pub mod layouts; mod scancodes; -pub use crate::scancodes::{ScancodeSet1, ScancodeSet2}; +pub use crate::scancodes::{ScancodeSet1, ScancodeSet2, UsbModifiers}; // **************************************************************************** // @@ -319,9 +318,9 @@ pub use crate::scancodes::{ScancodeSet1, ScancodeSet2}; // // **************************************************************************** -/// Encapsulates decode/sampling logic, and handles state transitions and key events. +/// Encapsulates decode/sampling logic, and handles state transitions and key events for PS/2 Keyboards #[derive(Debug)] -pub struct Keyboard +pub struct PS2Keyboard where S: ScancodeSet, L: KeyboardLayout, @@ -338,6 +337,42 @@ pub struct Ps2Decoder { num_bits: u8, } +/// Encapsulates HID frame handling, and handles state transitions and key events for USB HID Keyboards +#[derive(Debug)] +pub struct UsbKeyboard +where + L: KeyboardLayout, +{ + event_decoder: EventDecoder, + last_report: UsbBootKeyboardReport, +} + +/// A USB HID report as received from a keyboard in Boot Mode +#[derive(Debug, Clone, Default)] +pub struct UsbBootKeyboardReport { + /// Modifier Keys - the first byte in the report + pub modifiers: u8, + /// Keycodes - the last six bytes in the report + pub keys: [u8; 6], +} + +/// An Iterator that produces ['KeyEvent'] values +pub struct UsbKeyEventIter<'parent, L> +where + L: KeyboardLayout, +{ + parent: &'parent mut UsbKeyboard, + new_report: UsbBootKeyboardReport, +} + +/// An Iterator that produces ['DecodedKey'] values, using an [`EventDecoder`] +pub struct UsbDecodedKeyIter<'parent, L> +where + L: KeyboardLayout, +{ + inner: UsbKeyEventIter<'parent, L>, +} + /// Converts KeyEvents into Unicode, according to the current Keyboard Layout #[derive(Debug)] pub struct EventDecoder @@ -641,6 +676,8 @@ pub enum KeyCode { RControl2, /// Used as a 'hidden' Right Alt Key (Print Screen = RAlt2 + PrntScr) RAlt2, + /// Represents a key we don't know about + Unknown, } /// The new state for a key, as part of a key event. @@ -789,14 +826,14 @@ const SLS: char = '\\'; // // **************************************************************************** -impl Keyboard +impl PS2Keyboard where L: KeyboardLayout, S: ScancodeSet, { /// Make a new Keyboard object with the given layout. - pub const fn new(scancode_set: S, layout: L, handle_ctrl: HandleControl) -> Keyboard { - Keyboard { + pub const fn new(scancode_set: S, layout: L, handle_ctrl: HandleControl) -> PS2Keyboard { + PS2Keyboard { ps2_decoder: Ps2Decoder::new(), scancode_set, event_decoder: EventDecoder::new(layout, handle_ctrl), @@ -950,6 +987,193 @@ impl Default for Ps2Decoder { } } +impl UsbKeyboard +where + L: KeyboardLayout, +{ + /// Construct USB HID keyboard handler + pub fn new(layout: L, handle_ctrl: HandleControl) -> UsbKeyboard { + UsbKeyboard { + event_decoder: EventDecoder::new(layout, handle_ctrl), + last_report: Default::default(), + } + } + + /// Reset the USB HID state + /// + /// We only get changes in key state from the keyboard, so this function + /// resets our internal state. + pub fn reset_state(&mut self) { + self.last_report = Default::default(); + } + + /// Process a new USB HID Frame into KeyEvents + /// + /// You are given an iterator, which will process the incoming report. Do + /// not drop the iterator until it starts returning None. + pub fn handle_report_raw<'kb>( + &'kb mut self, + report: &UsbBootKeyboardReport, + ) -> UsbKeyEventIter<'kb, L> { + UsbKeyEventIter { + parent: self, + new_report: report.clone(), + } + } + + /// Process a new USB HID Frame into Decoded keys + /// + /// You are given an iterator, which will process the incoming report. Do + /// not drop the iterator until it starts returning None. + pub fn handle_report<'kb>( + &'kb mut self, + report: &UsbBootKeyboardReport, + ) -> UsbDecodedKeyIter<'kb, L> { + UsbDecodedKeyIter { + inner: self.handle_report_raw(report), + } + } +} + +impl<'kb, L> UsbKeyEventIter<'kb, L> +where + L: KeyboardLayout, +{ + const MODIFIERS: [(KeyCode, scancodes::UsbModifiers); 8] = [ + (KeyCode::LControl, scancodes::UsbModifiers::LCtrl), + (KeyCode::LShift, scancodes::UsbModifiers::LShift), + (KeyCode::LAlt, scancodes::UsbModifiers::LAlt), + (KeyCode::LWin, scancodes::UsbModifiers::LGui), + (KeyCode::RControl, scancodes::UsbModifiers::RCtrl), + (KeyCode::RShift, scancodes::UsbModifiers::RShift), + (KeyCode::RAltGr, scancodes::UsbModifiers::RAlt), + (KeyCode::RWin, scancodes::UsbModifiers::RGui), + ]; + + /// Returns true (and updates the old modifiers) if a modifier was pressed but has now been released + fn modifier_was_released( + old_modifiers: &mut u8, + new_modifiers: u8, + key: scancodes::UsbModifiers, + ) -> bool { + let key = key as u8; + if ((*old_modifiers & key) != 0) && ((new_modifiers & key) == 0) { + // was released - clear it + *old_modifiers &= !key; + true + } else { + false + } + } + + /// Returns true (and updates the old modifiers) if a modifier was released but has now been pressed + fn modifier_was_pressed( + old_modifiers: &mut u8, + new_modifiers: u8, + key: scancodes::UsbModifiers, + ) -> bool { + let key = key as u8; + if ((*old_modifiers & key) == 0) && ((new_modifiers & key) != 0) { + // was pressed - set it + *old_modifiers |= key; + true + } else { + false + } + } +} + +impl<'kb, L> Iterator for UsbKeyEventIter<'kb, L> +where + L: KeyboardLayout, +{ + type Item = KeyEvent; + + fn next(&mut self) -> Option { + // for every modifier that was on and is now off, send a key release + for (keycode, modifier) in Self::MODIFIERS { + if Self::modifier_was_released( + &mut self.parent.last_report.modifiers, + self.new_report.modifiers, + modifier, + ) { + return Some(KeyEvent { + code: keycode, + state: KeyState::Up, + }); + } + } + + // for every modifier that was off and is now on, send a key down + for (keycode, modifier) in Self::MODIFIERS { + if Self::modifier_was_pressed( + &mut self.parent.last_report.modifiers, + self.new_report.modifiers, + modifier, + ) { + return Some(KeyEvent { + code: keycode, + state: KeyState::Down, + }); + } + } + + // for every keycode that was on and is now off, send a key release + for old_place in self.parent.last_report.keys.iter_mut().filter(|x| **x != 0) { + if !self.new_report.keys.contains(old_place) { + // cannot find old key in new report => it has been released + let output = Some(KeyEvent { + code: scancodes::usb_convert(*old_place), + state: KeyState::Up, + }); + *old_place = 0; + return output; + } + } + + // for every keycode that was on and is now off, send a key down + for new_place in self.new_report.keys.iter().filter(|x| **x != 0) { + if !self.parent.last_report.keys.contains(new_place) { + // cannot find new key in old report => it has been pressed + let output = Some(KeyEvent { + code: scancodes::usb_convert(*new_place), + state: KeyState::Down, + }); + // we know we must have space in the old report because if all + // the codes in the new report are new, then all the old + // codes will have been expunged already. So the unwrap is + // fine. + let old_gap = self + .parent + .last_report + .keys + .iter_mut() + .find(|x| **x == 0) + .unwrap(); + *old_gap = *new_place; + return output; + } + } + None + } +} + +impl<'kb, L> Iterator for UsbDecodedKeyIter<'kb, L> +where + L: KeyboardLayout, +{ + type Item = DecodedKey; + + fn next(&mut self) -> Option { + while let Some(ev) = self.inner.next() { + if let Some(decoded) = self.inner.parent.event_decoder.process_keyevent(ev) { + return Some(decoded); + } + } + None + } +} + impl EventDecoder where L: KeyboardLayout, @@ -1329,7 +1553,7 @@ impl Modifiers { mod test { use super::*; - fn add_bytes(keyboard: &mut Keyboard, test_sequence: &[(u8, Option)]) + fn add_bytes(keyboard: &mut PS2Keyboard, test_sequence: &[(u8, Option)]) where L: KeyboardLayout, S: ScancodeSet, @@ -1348,7 +1572,7 @@ mod test { } fn process_keyevents( - keyboard: &mut Keyboard, + keyboard: &mut PS2Keyboard, test_sequence: &[(KeyEvent, Option)], ) where L: KeyboardLayout, @@ -1370,7 +1594,7 @@ mod test { #[test] fn test_f9() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1397,7 +1621,7 @@ mod test { #[test] fn test_f9_word() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1410,7 +1634,7 @@ mod test { #[test] fn test_f9_byte() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1422,7 +1646,7 @@ mod test { #[test] fn test_keyup_keydown() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1438,7 +1662,7 @@ mod test { #[test] fn test_f5() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1465,7 +1689,7 @@ mod test { #[test] fn test_f5_up() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1512,7 +1736,7 @@ mod test { #[test] fn test_shift() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1582,7 +1806,7 @@ mod test { #[test] fn test_ctrl() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1628,7 +1852,7 @@ mod test { #[test] fn test_numlock() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1659,7 +1883,7 @@ mod test { #[test] fn test_set_1_down_up_down() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet1::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1675,7 +1899,7 @@ mod test { #[test] fn test_set_1_ext_down_up_down() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet1::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1697,7 +1921,7 @@ mod test { #[test] fn test_set_2_poweron() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1711,7 +1935,7 @@ mod test { #[test] fn test_set_2_toomanykeys() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1725,7 +1949,7 @@ mod test { #[test] fn test_set_2_down_up() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1746,7 +1970,7 @@ mod test { #[test] fn test_set_2_ext_down_up() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Us104Key, HandleControl::MapLettersToUnicode, @@ -1763,7 +1987,7 @@ mod test { #[test] fn test_pause_set1() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet1::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1813,7 +2037,7 @@ mod test { #[test] fn test_pause_set2() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1864,7 +2088,7 @@ mod test { #[test] fn test_pause_events() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1911,7 +2135,7 @@ mod test { #[test] fn test_print_screen_set1() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet1::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -1961,7 +2185,7 @@ mod test { #[test] fn test_print_screen_set2() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -2014,7 +2238,7 @@ mod test { #[test] fn test_print_screen_events() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -2061,7 +2285,7 @@ mod test { #[test] fn test_modifier_state_shift() { - let mut k = Keyboard::new( + let mut k = PS2Keyboard::new( ScancodeSet2::new(), layouts::Uk105Key, HandleControl::MapLettersToUnicode, @@ -2080,6 +2304,580 @@ mod test { }); assert!(!k.get_modifiers().lshift); } + + #[test] + fn usb_left_control() { + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x01, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::LControl, + state: KeyState::Down + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::LControl, + state: KeyState::Up + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert!(events.is_empty()); + } + + #[test] + fn usb_right_gui() { + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x80, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::RWin, + state: KeyState::Down + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::RWin, + state: KeyState::Up + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert!(events.is_empty()); + } + + #[test] + fn usb_two_modifiers() { + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x82, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::LShift, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::RWin, + state: KeyState::Down + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x18, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::LShift, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::RWin, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::LWin, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::RControl, + state: KeyState::Down + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::LWin, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::RControl, + state: KeyState::Up + }, + ] + ); + } + + #[test] + fn usb_change_all_letters() { + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x04, 0x05, 0x06, 0x07, 0x08, 0x09], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::A, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::B, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::C, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::D, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::E, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::F, + state: KeyState::Down + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0A, 0x0B, 0x0C, 0xD, 0xE, 0xF], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::A, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::B, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::C, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::D, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::E, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::F, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::G, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::H, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::I, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::J, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::K, + state: KeyState::Down + }, + KeyEvent { + code: KeyCode::L, + state: KeyState::Down + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::G, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::H, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::I, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::J, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::K, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::L, + state: KeyState::Up + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert!(events.is_empty()); + } + + #[test] + fn usb_letters() { + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x04, 0x00, 0x00, 0x00, 0x00, 0x00], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::A, + state: KeyState::Down + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x05, 0x04, 0x00, 0x00, 0x00, 0x00], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::B, + state: KeyState::Down + }] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x05, 0x06, 0x00, 0x00, 0x00, 0x00], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[ + KeyEvent { + code: KeyCode::A, + state: KeyState::Up + }, + KeyEvent { + code: KeyCode::C, + state: KeyState::Down + }, + ] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x05, 0x00, 0x00, 0x00, 0x00, 0x00], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::C, + state: KeyState::Up + },] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert_eq!( + &events, + &[KeyEvent { + code: KeyCode::B, + state: KeyState::Up + },] + ); + + let iter = keyboard.handle_report_raw(&UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0; 6], + }); + let events: Vec = iter.collect(); + assert!(events.is_empty()); + } + + #[test] + fn usb_reports() { + // Reports, as captured with `sudo usbhid-dump -a 1:4 -es | tee capture.log` + let reports = [ + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x0B, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x08, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0F, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0F, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x12, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x2C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x17, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0B, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x16, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x2C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x0C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x16, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x2C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x04, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x2C, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x17, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x08, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x16, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x17, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x1E, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x02, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x28, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + UsbBootKeyboardReport { + modifiers: 0x00, + keys: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + }, + ]; + let mut output = String::new(); + let mut keyboard = UsbKeyboard::new(layouts::Us104Key, HandleControl::MapLettersToUnicode); + for report in reports.iter() { + for ev in keyboard.handle_report(report) { + if let DecodedKey::Unicode(ch) = ev { + output.push(ch); + } + } + } + assert_eq!("Hello this is a test!\n", output); + } } // **************************************************************************** diff --git a/src/scancodes/mod.rs b/src/scancodes/mod.rs index 0ec2969..b08b112 100644 --- a/src/scancodes/mod.rs +++ b/src/scancodes/mod.rs @@ -2,6 +2,8 @@ mod set1; mod set2; +mod usbhid; pub use self::set1::ScancodeSet1; pub use self::set2::ScancodeSet2; +pub use self::usbhid::{convert as usb_convert, Modifiers as UsbModifiers}; diff --git a/src/scancodes/usbhid.rs b/src/scancodes/usbhid.rs new file mode 100644 index 0000000..e4184c4 --- /dev/null +++ b/src/scancodes/usbhid.rs @@ -0,0 +1,233 @@ +//! USB Scancode conversions + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum Modifiers { + LCtrl = 0x01, + LShift = 0x02, + LAlt = 0x04, + LGui = 0x08, + RCtrl = 0x10, + RShift = 0x20, + RAlt = 0x40, + RGui = 0x80, +} + +const HID_A: u8 = 0x04; +const HID_B: u8 = 0x05; +const HID_C: u8 = 0x06; +const HID_D: u8 = 0x07; +const HID_E: u8 = 0x08; +const HID_F: u8 = 0x09; +const HID_G: u8 = 0x0A; +const HID_H: u8 = 0x0B; +const HID_I: u8 = 0x0C; +const HID_J: u8 = 0x0D; +const HID_K: u8 = 0x0E; +const HID_L: u8 = 0x0F; +const HID_M: u8 = 0x10; +const HID_N: u8 = 0x11; +const HID_O: u8 = 0x12; +const HID_P: u8 = 0x13; +const HID_Q: u8 = 0x14; +const HID_R: u8 = 0x15; +const HID_S: u8 = 0x16; +const HID_T: u8 = 0x17; +const HID_U: u8 = 0x18; +const HID_V: u8 = 0x19; +const HID_W: u8 = 0x1A; +const HID_X: u8 = 0x1B; +const HID_Y: u8 = 0x1C; +const HID_Z: u8 = 0x1D; +const HID_1: u8 = 0x1E; +const HID_2: u8 = 0x1F; +const HID_3: u8 = 0x20; +const HID_4: u8 = 0x21; +const HID_5: u8 = 0x22; +const HID_6: u8 = 0x23; +const HID_7: u8 = 0x24; +const HID_8: u8 = 0x25; +const HID_9: u8 = 0x26; +const HID_0: u8 = 0x27; +const HID_RETURN: u8 = 0x28; +const HID_ESCAPE: u8 = 0x29; +const HID_BACKSPACE: u8 = 0x2A; +const HID_TAB: u8 = 0x2B; +const HID_SPACEBAR: u8 = 0x2C; +const HID_UNDERSCORE: u8 = 0x2D; +const HID_EQUAL: u8 = 0x2E; +const HID_OPEN_BRACE: u8 = 0x2F; +const HID_CLOSE_BRACE: u8 = 0x30; +const HID_BACKSLASH: u8 = 0x31; +const HID_HASH_TILDE: u8 = 0x32; +const HID_SEMICOLON: u8 = 0x33; +const HID_APOSTROPHE: u8 = 0x34; +const HID_GRAVE_TILDE: u8 = 0x35; +const HID_COMMA: u8 = 0x36; +const HID_DOT: u8 = 0x37; +const HID_SLASH: u8 = 0x38; +const HID_CAPS_LOCK: u8 = 0x39; +const HID_F1: u8 = 0x3A; +const HID_F2: u8 = 0x3B; +const HID_F3: u8 = 0x3C; +const HID_F4: u8 = 0x3D; +const HID_F5: u8 = 0x3E; +const HID_F6: u8 = 0x3F; +const HID_F7: u8 = 0x40; +const HID_F8: u8 = 0x41; +const HID_F9: u8 = 0x42; +const HID_F10: u8 = 0x43; +const HID_F11: u8 = 0x44; +const HID_F12: u8 = 0x45; +const HID_PRINTSCREEN: u8 = 0x46; +const HID_SCROLL_LOCK: u8 = 0x47; +const HID_PAUSE: u8 = 0x48; +const HID_INSERT: u8 = 0x49; +const HID_HOME: u8 = 0x4A; +const HID_PAGEUP: u8 = 0x4B; +const HID_DELETE: u8 = 0x4C; +const HID_END: u8 = 0x4D; +const HID_PAGEDOWN: u8 = 0x4E; +const HID_RIGHT: u8 = 0x4F; +const HID_LEFT: u8 = 0x50; +const HID_DOWN: u8 = 0x51; +const HID_UP: u8 = 0x52; +const HID_KEYPAD_NUM_LOCK: u8 = 0x53; +const HID_KEYPAD_SLASH: u8 = 0x54; +const HID_KEYPAD_ASTERISK: u8 = 0x55; +const HID_KEYPAD_MINUS: u8 = 0x56; +const HID_KEYPAD_PLUS: u8 = 0x57; +const HID_KEYPAD_ENTER: u8 = 0x58; +const HID_KEYPAD_1: u8 = 0x59; +const HID_KEYPAD_2: u8 = 0x5A; +const HID_KEYPAD_3: u8 = 0x5B; +const HID_KEYPAD_4: u8 = 0x5C; +const HID_KEYPAD_5: u8 = 0x5D; +const HID_KEYPAD_6: u8 = 0x5E; +const HID_KEYPAD_7: u8 = 0x5F; +const HID_KEYPAD_8: u8 = 0x60; +const HID_KEYPAD_9: u8 = 0x61; +const HID_KEYPAD_0: u8 = 0x62; +const HID_KEYPAD_DOT: u8 = 0x63; +const HID_LEFT_CTRL: u8 = 0xE0; +const HID_LEFT_SHIFT: u8 = 0xE1; +const HID_LEFT_ALT: u8 = 0xE2; +const HID_LEFT_GUI: u8 = 0xE3; +const HID_RIGHT_CTRL: u8 = 0xE4; +const HID_RIGHT_SHIFT: u8 = 0xE5; +const HID_RIGHT_ALT: u8 = 0xE6; +const HID_RIGHT_GUI: u8 = 0xE7; +const HID_AT102: u8 = 0x64; +const HID_APPLICATION: u8 = 0x65; + +pub fn convert(usb_keycode: u8) -> crate::KeyCode { + match usb_keycode { + HID_A => crate::KeyCode::A, + HID_B => crate::KeyCode::B, + HID_C => crate::KeyCode::C, + HID_D => crate::KeyCode::D, + HID_E => crate::KeyCode::E, + HID_F => crate::KeyCode::F, + HID_G => crate::KeyCode::G, + HID_H => crate::KeyCode::H, + HID_I => crate::KeyCode::I, + HID_J => crate::KeyCode::J, + HID_K => crate::KeyCode::K, + HID_L => crate::KeyCode::L, + HID_M => crate::KeyCode::M, + HID_N => crate::KeyCode::N, + HID_O => crate::KeyCode::O, + HID_P => crate::KeyCode::P, + HID_Q => crate::KeyCode::Q, + HID_R => crate::KeyCode::R, + HID_S => crate::KeyCode::S, + HID_T => crate::KeyCode::T, + HID_U => crate::KeyCode::U, + HID_V => crate::KeyCode::V, + HID_W => crate::KeyCode::W, + HID_X => crate::KeyCode::X, + HID_Y => crate::KeyCode::Y, + HID_Z => crate::KeyCode::Z, + HID_1 => crate::KeyCode::Key1, + HID_2 => crate::KeyCode::Key2, + HID_3 => crate::KeyCode::Key3, + HID_4 => crate::KeyCode::Key4, + HID_5 => crate::KeyCode::Key5, + HID_6 => crate::KeyCode::Key6, + HID_7 => crate::KeyCode::Key7, + HID_8 => crate::KeyCode::Key8, + HID_9 => crate::KeyCode::Key9, + HID_0 => crate::KeyCode::Key0, + HID_RETURN => crate::KeyCode::Return, + HID_ESCAPE => crate::KeyCode::Escape, + HID_BACKSPACE => crate::KeyCode::Backspace, + HID_TAB => crate::KeyCode::Tab, + HID_SPACEBAR => crate::KeyCode::Spacebar, + HID_UNDERSCORE => crate::KeyCode::OemMinus, + HID_EQUAL => crate::KeyCode::OemPlus, + HID_OPEN_BRACE => crate::KeyCode::Oem4, + HID_CLOSE_BRACE => crate::KeyCode::Oem6, + HID_BACKSLASH => crate::KeyCode::Oem7, + HID_HASH_TILDE => crate::KeyCode::Oem7, + HID_SEMICOLON => crate::KeyCode::Oem1, + HID_APOSTROPHE => crate::KeyCode::Oem3, + HID_GRAVE_TILDE => crate::KeyCode::Oem8, + HID_AT102 => crate::KeyCode::Oem5, + HID_COMMA => crate::KeyCode::OemComma, + HID_DOT => crate::KeyCode::OemPeriod, + HID_SLASH => crate::KeyCode::Oem2, + HID_CAPS_LOCK => crate::KeyCode::CapsLock, + HID_F1 => crate::KeyCode::F1, + HID_F2 => crate::KeyCode::F2, + HID_F3 => crate::KeyCode::F3, + HID_F4 => crate::KeyCode::F4, + HID_F5 => crate::KeyCode::F5, + HID_F6 => crate::KeyCode::F6, + HID_F7 => crate::KeyCode::F7, + HID_F8 => crate::KeyCode::F8, + HID_F9 => crate::KeyCode::F9, + HID_F10 => crate::KeyCode::F10, + HID_F11 => crate::KeyCode::F11, + HID_F12 => crate::KeyCode::F12, + HID_PRINTSCREEN => crate::KeyCode::PrintScreen, + HID_SCROLL_LOCK => crate::KeyCode::ScrollLock, + HID_PAUSE => crate::KeyCode::PauseBreak, + HID_INSERT => crate::KeyCode::Insert, + HID_HOME => crate::KeyCode::Home, + HID_PAGEUP => crate::KeyCode::PageUp, + HID_DELETE => crate::KeyCode::Delete, + HID_END => crate::KeyCode::End, + HID_PAGEDOWN => crate::KeyCode::PageDown, + HID_RIGHT => crate::KeyCode::ArrowRight, + HID_LEFT => crate::KeyCode::ArrowLeft, + HID_DOWN => crate::KeyCode::ArrowDown, + HID_UP => crate::KeyCode::ArrowUp, + HID_KEYPAD_NUM_LOCK => crate::KeyCode::NumpadLock, + HID_KEYPAD_SLASH => crate::KeyCode::NumpadDivide, + HID_KEYPAD_ASTERISK => crate::KeyCode::NumpadMultiply, + HID_KEYPAD_MINUS => crate::KeyCode::NumpadSubtract, + HID_KEYPAD_PLUS => crate::KeyCode::NumpadAdd, + HID_KEYPAD_ENTER => crate::KeyCode::NumpadEnter, + HID_KEYPAD_1 => crate::KeyCode::Numpad1, + HID_KEYPAD_2 => crate::KeyCode::Numpad2, + HID_KEYPAD_3 => crate::KeyCode::Numpad3, + HID_KEYPAD_4 => crate::KeyCode::Numpad4, + HID_KEYPAD_5 => crate::KeyCode::Numpad5, + HID_KEYPAD_6 => crate::KeyCode::Numpad6, + HID_KEYPAD_7 => crate::KeyCode::Numpad7, + HID_KEYPAD_8 => crate::KeyCode::Numpad8, + HID_KEYPAD_9 => crate::KeyCode::Numpad9, + HID_KEYPAD_0 => crate::KeyCode::Numpad0, + HID_KEYPAD_DOT => crate::KeyCode::NumpadPeriod, + HID_LEFT_CTRL => crate::KeyCode::LControl, + HID_LEFT_SHIFT => crate::KeyCode::LShift, + HID_LEFT_ALT => crate::KeyCode::LAlt, + HID_LEFT_GUI => crate::KeyCode::LWin, + HID_APPLICATION => crate::KeyCode::Apps, + HID_RIGHT_CTRL => crate::KeyCode::RControl, + HID_RIGHT_SHIFT => crate::KeyCode::RShift, + HID_RIGHT_ALT => crate::KeyCode::RAltGr, + HID_RIGHT_GUI => crate::KeyCode::RWin, + _ => crate::KeyCode::Unknown, + } +}