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