1use std::fmt::{self, Display};
4use std::time::Duration;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum KeyEventType {
10 Down,
12
13 Up,
15
16 Repeat,
18}
19
20impl KeyEventType {
21 pub fn label(&self) -> &'static str {
23 match self {
24 KeyEventType::Down => "Key Down",
25 KeyEventType::Up => "Key Up",
26 KeyEventType::Repeat => "Key Repeat",
27 }
28 }
29}
30
31#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub enum KeyEventDeviceType {
37 #[default]
39 Keyboard,
40
41 DirectionalPad,
44
45 Gamepad,
47
48 Joystick,
50
51 Hdmi,
53}
54
55impl KeyEventDeviceType {
56 pub fn label(&self) -> &'static str {
58 match self {
59 KeyEventDeviceType::Keyboard => "Keyboard",
60 KeyEventDeviceType::DirectionalPad => "Directional Pad",
61 KeyEventDeviceType::Gamepad => "Gamepad",
62 KeyEventDeviceType::Joystick => "Joystick",
63 KeyEventDeviceType::Hdmi => "HDMI",
64 }
65 }
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71pub struct KeyData {
72 pub time_stamp: Duration,
77
78 pub event_type: KeyEventType,
80
81 pub device_type: KeyEventDeviceType,
84
85 pub physical: u64,
87
88 pub logical: u64,
90
91 pub character: Option<String>,
95
96 pub synthesized: bool,
125}
126
127impl Default for KeyData {
128 fn default() -> KeyData {
129 KeyData {
130 time_stamp: Duration::ZERO,
131 event_type: KeyEventType::Down,
132 device_type: KeyEventDeviceType::Keyboard,
133 physical: 0,
134 logical: 0,
135 character: None,
136 synthesized: false,
137 }
138 }
139}
140
141impl KeyData {
142 fn non_value_bits(n: u64) -> u64 {
147 const VALUE_MASK_WIDTH: u32 = 32;
148 const MAX_SAFE_INTEGER_WIDTH: u32 = 52;
150 const NON_VALUE_MASK: u64 = (1 << (MAX_SAFE_INTEGER_WIDTH - VALUE_MASK_WIDTH)) - 1;
151 (n >> VALUE_MASK_WIDTH) & NON_VALUE_MASK
152 }
153
154 fn logical_to_string(&self) -> String {
155 let plane_description = match KeyData::non_value_bits(self.logical) & 0x0ff {
156 0x000 => " (Unicode)",
157 0x001 => " (Unprintable)",
158 0x002 => " (Flutter)",
159 0x011 => " (Android)",
160 0x012 => " (Fuchsia)",
161 0x013 => " (iOS)",
162 0x014 => " (macOS)",
163 0x015 => " (GTK)",
164 0x016 => " (Windows)",
165 0x017 => " (Web)",
166 0x018 => " (GLFW)",
167 _ => "",
168 };
169 format!("0x{:x}{}", self.logical, plane_description)
170 }
171
172 fn escape_character(&self) -> String {
173 let Some(character) = &self.character else {
174 return "<none>".to_owned();
175 };
176 match character.as_str() {
177 "\n" => r#""\n""#.to_owned(),
178 "\t" => r#""\t""#.to_owned(),
179 "\r" => r#""\r""#.to_owned(),
180 "\u{8}" => r#""\b""#.to_owned(),
181 "\u{c}" => r#""\f""#.to_owned(),
182 _ => format!("\"{character}\""),
183 }
184 }
185
186 fn quoted_char_code(&self) -> String {
187 let Some(character) = &self.character else {
188 return String::new();
189 };
190 let hex_chars: Vec<String> = character
191 .encode_utf16()
192 .map(|code| format!("{code:02x}"))
193 .collect();
194 format!(" (0x{})", hex_chars.join(" "))
195 }
196
197 pub fn to_string_full(&self) -> String {
199 format!(
200 "KeyData(type: {}, \
201 deviceType: {}, \
202 timeStamp: {:?}, \
203 physical: 0x{:x}, \
204 logical: 0x{:x}, \
205 character: {}, \
206 synthesized: {}\
207 )",
208 self.event_type.label(),
209 self.device_type.label(),
210 self.time_stamp,
211 self.physical,
212 self.logical,
213 self.escape_character(),
214 self.synthesized,
215 )
216 }
217}
218
219impl Display for KeyData {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(
222 f,
223 "KeyData({}, physical: 0x{:x}, logical: {}, character: {}{}{}",
224 self.event_type.label(),
225 self.physical,
226 self.logical_to_string(),
227 self.escape_character(),
228 self.quoted_char_code(),
229 if self.synthesized {
230 ", synthesized"
231 } else {
232 ""
233 },
234 )
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use std::time::Duration;
241
242 use super::{KeyData, KeyEventDeviceType, KeyEventType};
243
244 #[test]
245 fn to_string_names_the_plane_and_quotes_the_character() {
246 let data = KeyData {
247 time_stamp: Duration::from_millis(5),
248 event_type: KeyEventType::Down,
249 device_type: KeyEventDeviceType::Keyboard,
250 physical: 0x00070004,
251 logical: 0x00000000061,
252 character: Some("a".to_owned()),
253 synthesized: false,
254 };
255 assert_eq!(
256 data.to_string(),
257 "KeyData(Key Down, physical: 0x70004, logical: 0x61 (Unicode), character: \"a\" (0x61)"
258 );
259 }
260
261 #[test]
262 fn to_string_full_reports_every_field() {
263 let data = KeyData {
264 time_stamp: Duration::from_millis(5),
265 event_type: KeyEventType::Up,
266 device_type: KeyEventDeviceType::Gamepad,
267 physical: 0x00070004,
268 logical: 0x00100000301,
269 character: None,
270 synthesized: true,
271 };
272 assert_eq!(
273 data.to_string_full(),
274 "KeyData(type: Key Up, deviceType: Gamepad, timeStamp: 5ms, physical: 0x70004, \
275 logical: 0x100000301, character: <none>, synthesized: true)"
276 );
277 }
278}