keycode/
lib.rs

1//! A Rust crate for translating keycodes based on Chrome's mapping of keys.
2//!
3//! Easily convert, generate, listen for, or map keycodes for Linux, Windows, Mac, USB, and
4//! browsers! Includes a `struct` to manage the state of pressed keys and generate USB HID reports.
5//! Can be used for `#![no_std]` crates.
6//!
7//! Source of keycodes data:
8//! *   Repo: <https://chromium.googlesource.com/chromium/src.git>
9//! *   File: <https://chromium.googlesource.com/chromium/src.git/+/master/ui/events/keycodes/dom/keycode_converter_data.inc>
10//! *   Git commit: `2b6022954b9fb600f15e08002a148187f4f986da`
11//!
12//! # Example: get a key mapping
13//!
14//! ```
15//! use keycode::{KeyMap, KeyMappingId};
16//!
17//! // Check the USB HID value of the "a" key
18//! let a = KeyMap::from(KeyMappingId::UsA);
19//! assert_eq!(a.usb, 0x0004);
20//! assert_eq!(a.evdev, 0x001e);
21//! assert_eq!(a.xkb, 0x0026);
22//! assert_eq!(a.win, 0x001e);
23//! assert_eq!(a.mac, 0x0000);
24//! ```
25//!
26//! # Example: generate a USB HID report
27//!
28//! ```
29//! use keycode::{KeyboardState, KeyMap, KeyMappingId, KeyState};
30//!
31//! // Press and release the "A" key
32//! // Generate a keyboard state with n-key rollover
33//! let mut keyboard_state = KeyboardState::new(None);
34//!
35//! // Get key mappings
36//! let a = KeyMap::from(KeyMappingId::UsA);
37//! let shift = KeyMap::from(KeyMappingId::ShiftLeft);
38//!
39//! // USB HID report for "no keys pressed"
40//! assert_eq!(keyboard_state.usb_input_report(), &[0; 8]);
41//!
42//! // Press "shift" and "a" keys
43//! keyboard_state.update_key(a, KeyState::Pressed);
44//! keyboard_state.update_key(shift, KeyState::Pressed);
45//!
46//! // USB HID report for "'A' is pressed"
47//! assert_eq!(
48//!     keyboard_state.usb_input_report(),
49//!     &[shift.modifier.unwrap().bits(), 0, a.usb as u8, 0, 0, 0, 0, 0]
50//! );
51//!
52//! // Release "shift" and "a" keys
53//! keyboard_state.update_key(a, KeyState::Released);
54//! keyboard_state.update_key(shift, KeyState::Released);
55//!
56//! // USB HID report for "no keys pressed"
57//! assert_eq!(keyboard_state.usb_input_report(), &[0; 8]);
58//! ```
59
60#![no_std]
61#![deny(missing_docs)]
62
63use arraydeque::ArrayDeque;
64use arrayvec::ArrayVec;
65
66use keycode_macro::parse_keycode_converter_data;
67
68parse_keycode_converter_data!();
69
70/// State of any key, whether it is pressed or not
71#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub enum KeyState {
74    /// Pressed key
75    Pressed,
76    /// Released key
77    Released,
78}
79
80/// Max keys is 235, but this is the size of array used to manage state
81pub const NUM_KEYS: usize = 256;
82
83/// Keyboard state that helps manage pressed keys, rollover, and generating USB HID reports
84#[derive(Debug, Clone, PartialEq, Eq, Hash)]
85pub struct KeyboardState {
86    key_rollover: Option<usize>,
87    key_state: ArrayDeque<[Option<KeyMap>; NUM_KEYS]>,
88    modifier_state: KeyModifiers,
89    input_report: ArrayVec<[u8; NUM_KEYS]>,
90}
91
92impl<'a> KeyboardState {
93    /// Create a new keyboard state
94    pub fn new(key_rollover: Option<usize>) -> KeyboardState {
95        KeyboardState {
96            key_rollover,
97            key_state: ArrayDeque::new(),
98            modifier_state: KeyModifiers::empty(),
99            input_report: ArrayVec::new(),
100        }
101    }
102
103    /// Update the keyboard state with a key's new state
104    pub fn update_key(self: &mut Self, key: KeyMap, state: KeyState) {
105        match state {
106            KeyState::Pressed => {
107                if let Some(key_modifier) = key.modifier {
108                    self.modifier_state.insert(key_modifier);
109                    return;
110                }
111
112                // Already contains key
113                if self.key_state.contains(&Some(key)) {
114                    return;
115                }
116
117                // Key state can't store anymore keys
118                if self.key_state.is_full() {
119                    return;
120                }
121
122                // Key rollover limit is met
123                if let Some(key_rollover) = self.key_rollover {
124                    if self.key_state.len() >= key_rollover {
125                        return;
126                    }
127                }
128
129                // We check if the `key_state` is full above, so this should be safe.
130                self.key_state.push_back(Some(key)).unwrap();
131            }
132            KeyState::Released => {
133                if let Some(key_modifier) = key.modifier {
134                    self.modifier_state.remove(key_modifier);
135                    return;
136                }
137
138                if self.key_state.is_empty() {
139                    return;
140                }
141
142                self.key_state.retain(|k| *k != Some(key));
143            }
144        }
145    }
146
147    /// Generate a USB HID report
148    pub fn usb_input_report(self: &mut Self) -> &[u8] {
149        let mut input_report: ArrayVec<[u8; NUM_KEYS]> = ArrayVec::new();
150
151        // Key modifiers
152        input_report.push(self.modifier_state.bits());
153        input_report.push(0);
154
155        // Normal keys
156        for possible_key in self.key_state.iter() {
157            if let Some(key) = possible_key {
158                input_report.push(key.usb as u8);
159            }
160        }
161
162        // Default (not pressed)
163        let min_input_report_size = self
164            .key_rollover
165            .map(|key_rollover_without_modifiers| key_rollover_without_modifiers + 2)
166            .unwrap_or(8);
167        if input_report.len() < min_input_report_size {
168            for _ in input_report.len()..min_input_report_size {
169                input_report.push(0);
170            }
171        }
172
173        self.input_report = input_report;
174        self.input_report.as_slice()
175    }
176}