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}