1use alloc::borrow::ToOwned;
45use alloc::string::{String, ToString};
46use alloc::vec::Vec;
47use std::collections::HashSet;
48
49use unicode_segmentation::UnicodeSegmentation;
50
51use crate::{first_char, NamedKey};
52use crate::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
53use crate::{CompositionEvent, CompositionState};
54
55fn normalised_key_value(raw_key: char) -> Key {
58 match raw_key {
59 '\u{E000}' => Key::Named(NamedKey::Unidentified),
60 '\u{E001}' => Key::Named(NamedKey::Cancel),
61 '\u{E002}' => Key::Named(NamedKey::Help),
62 '\u{E003}' => Key::Named(NamedKey::Backspace),
63 '\u{E004}' => Key::Named(NamedKey::Tab),
64 '\u{E005}' => Key::Named(NamedKey::Clear),
65 '\u{E006}' => Key::Named(NamedKey::Enter),
67 '\u{E007}' => Key::Named(NamedKey::Enter),
68 '\u{E008}' => Key::Named(NamedKey::Shift),
69 '\u{E009}' => Key::Named(NamedKey::Control),
70 '\u{E00A}' => Key::Named(NamedKey::Alt),
71 '\u{E00B}' => Key::Named(NamedKey::Pause),
72 '\u{E00C}' => Key::Named(NamedKey::Escape),
73 '\u{E00D}' => Key::Character(" ".to_string()),
74 '\u{E00E}' => Key::Named(NamedKey::PageUp),
75 '\u{E00F}' => Key::Named(NamedKey::PageDown),
76 '\u{E010}' => Key::Named(NamedKey::End),
77 '\u{E011}' => Key::Named(NamedKey::Home),
78 '\u{E012}' => Key::Named(NamedKey::ArrowLeft),
79 '\u{E013}' => Key::Named(NamedKey::ArrowUp),
80 '\u{E014}' => Key::Named(NamedKey::ArrowRight),
81 '\u{E015}' => Key::Named(NamedKey::ArrowDown),
82 '\u{E016}' => Key::Named(NamedKey::Insert),
83 '\u{E017}' => Key::Named(NamedKey::Delete),
84 '\u{E018}' => Key::Character(";".to_string()),
85 '\u{E019}' => Key::Character("=".to_string()),
86 '\u{E01A}' => Key::Character("0".to_string()),
87 '\u{E01B}' => Key::Character("1".to_string()),
88 '\u{E01C}' => Key::Character("2".to_string()),
89 '\u{E01D}' => Key::Character("3".to_string()),
90 '\u{E01E}' => Key::Character("4".to_string()),
91 '\u{E01F}' => Key::Character("5".to_string()),
92 '\u{E020}' => Key::Character("6".to_string()),
93 '\u{E021}' => Key::Character("7".to_string()),
94 '\u{E022}' => Key::Character("8".to_string()),
95 '\u{E023}' => Key::Character("9".to_string()),
96 '\u{E024}' => Key::Character("*".to_string()),
97 '\u{E025}' => Key::Character("+".to_string()),
98 '\u{E026}' => Key::Character(",".to_string()),
99 '\u{E027}' => Key::Character("-".to_string()),
100 '\u{E028}' => Key::Character(".".to_string()),
101 '\u{E029}' => Key::Character("/".to_string()),
102 '\u{E031}' => Key::Named(NamedKey::F1),
103 '\u{E032}' => Key::Named(NamedKey::F2),
104 '\u{E033}' => Key::Named(NamedKey::F3),
105 '\u{E034}' => Key::Named(NamedKey::F4),
106 '\u{E035}' => Key::Named(NamedKey::F5),
107 '\u{E036}' => Key::Named(NamedKey::F6),
108 '\u{E037}' => Key::Named(NamedKey::F7),
109 '\u{E038}' => Key::Named(NamedKey::F8),
110 '\u{E039}' => Key::Named(NamedKey::F9),
111 '\u{E03A}' => Key::Named(NamedKey::F10),
112 '\u{E03B}' => Key::Named(NamedKey::F11),
113 '\u{E03C}' => Key::Named(NamedKey::F12),
114 '\u{E03D}' => Key::Named(NamedKey::Meta),
115 '\u{E040}' => Key::Named(NamedKey::ZenkakuHankaku),
116 '\u{E050}' => Key::Named(NamedKey::Shift),
117 '\u{E051}' => Key::Named(NamedKey::Control),
118 '\u{E052}' => Key::Named(NamedKey::Alt),
119 '\u{E053}' => Key::Named(NamedKey::Meta),
120 '\u{E054}' => Key::Named(NamedKey::PageUp),
121 '\u{E055}' => Key::Named(NamedKey::PageDown),
122 '\u{E056}' => Key::Named(NamedKey::End),
123 '\u{E057}' => Key::Named(NamedKey::Home),
124 '\u{E058}' => Key::Named(NamedKey::ArrowLeft),
125 '\u{E059}' => Key::Named(NamedKey::ArrowUp),
126 '\u{E05A}' => Key::Named(NamedKey::ArrowRight),
127 '\u{E05B}' => Key::Named(NamedKey::ArrowDown),
128 '\u{E05C}' => Key::Named(NamedKey::Insert),
129 '\u{E05D}' => Key::Named(NamedKey::Delete),
130 _ => Key::Character(raw_key.to_string()),
131 }
132}
133
134fn code(raw_key: char) -> Code {
136 match raw_key {
137 '`' | '~' => Code::Backquote,
138 '\\' | '|' => Code::Backslash,
139 '\u{E003}' => Code::Backspace,
140 '[' | '{' => Code::BracketLeft,
141 ']' | '}' => Code::BracketRight,
142 ',' | '<' => Code::Comma,
143 '0' | ')' => Code::Digit0,
144 '1' | '!' => Code::Digit1,
145 '2' | '@' => Code::Digit2,
146 '3' | '#' => Code::Digit3,
147 '4' | '$' => Code::Digit4,
148 '5' | '%' => Code::Digit5,
149 '6' | '^' => Code::Digit6,
150 '7' | '&' => Code::Digit7,
151 '8' | '*' => Code::Digit8,
152 '9' | '(' => Code::Digit9,
153 '=' | '+' => Code::Equal,
154 'a' | 'A' => Code::KeyA,
156 'b' | 'B' => Code::KeyB,
157 'c' | 'C' => Code::KeyC,
158 'd' | 'D' => Code::KeyD,
159 'e' | 'E' => Code::KeyE,
160 'f' | 'F' => Code::KeyF,
161 'g' | 'G' => Code::KeyG,
162 'h' | 'H' => Code::KeyH,
163 'i' | 'I' => Code::KeyI,
164 'j' | 'J' => Code::KeyJ,
165 'k' | 'K' => Code::KeyK,
166 'l' | 'L' => Code::KeyL,
167 'm' | 'M' => Code::KeyM,
168 'n' | 'N' => Code::KeyN,
169 'o' | 'O' => Code::KeyO,
170 'p' | 'P' => Code::KeyP,
171 'q' | 'Q' => Code::KeyQ,
172 'r' | 'R' => Code::KeyR,
173 's' | 'S' => Code::KeyS,
174 't' | 'T' => Code::KeyT,
175 'u' | 'U' => Code::KeyU,
176 'v' | 'V' => Code::KeyV,
177 'w' | 'W' => Code::KeyW,
178 'x' | 'X' => Code::KeyX,
179 'y' | 'Y' => Code::KeyY,
180 'z' | 'Z' => Code::KeyZ,
181 '-' | '_' => Code::Minus,
182 '.' | '>' => Code::Period,
183 '\'' | '"' => Code::Quote,
184 ';' | ':' => Code::Semicolon,
185 '/' | '?' => Code::Slash,
186 '\u{E00A}' => Code::AltLeft,
187 '\u{E052}' => Code::AltRight,
188 '\u{E009}' => Code::ControlLeft,
189 '\u{E051}' => Code::ControlRight,
190 '\u{E006}' => Code::Enter,
191 '\u{E00B}' => Code::Pause,
192 '\u{E03D}' => Code::MetaLeft,
194 '\u{E053}' => Code::MetaRight,
196 '\u{E008}' => Code::ShiftLeft,
197 '\u{E050}' => Code::ShiftRight,
198 ' ' | '\u{E00D}' => Code::Space,
199 '\u{E004}' => Code::Tab,
200 '\u{E017}' => Code::Delete,
201 '\u{E010}' => Code::End,
202 '\u{E002}' => Code::Help,
203 '\u{E011}' => Code::Home,
204 '\u{E016}' => Code::Insert,
205 '\u{E00F}' => Code::PageDown,
207 '\u{E00E}' => Code::PageUp,
209 '\u{E015}' => Code::ArrowDown,
210 '\u{E012}' => Code::ArrowLeft,
211 '\u{E014}' => Code::ArrowRight,
212 '\u{E013}' => Code::ArrowUp,
213 '\u{E00C}' => Code::Escape,
214 '\u{E031}' => Code::F1,
215 '\u{E032}' => Code::F2,
216 '\u{E033}' => Code::F3,
217 '\u{E034}' => Code::F4,
218 '\u{E035}' => Code::F5,
219 '\u{E036}' => Code::F6,
220 '\u{E037}' => Code::F7,
221 '\u{E038}' => Code::F8,
222 '\u{E039}' => Code::F9,
223 '\u{E03A}' => Code::F10,
224 '\u{E03B}' => Code::F11,
225 '\u{E03C}' => Code::F12,
226 '\u{E019}' => Code::NumpadEqual,
227 '\u{E01A}' | '\u{E05C}' => Code::Numpad0,
228 '\u{E01B}' | '\u{E056}' => Code::Numpad1,
229 '\u{E01C}' | '\u{E05B}' => Code::Numpad2,
230 '\u{E01D}' | '\u{E055}' => Code::Numpad3,
231 '\u{E01E}' | '\u{E058}' => Code::Numpad4,
232 '\u{E01F}' => Code::Numpad5,
233 '\u{E020}' | '\u{E05A}' => Code::Numpad6,
234 '\u{E021}' | '\u{E057}' => Code::Numpad7,
235 '\u{E022}' | '\u{E059}' => Code::Numpad8,
236 '\u{E023}' | '\u{E054}' => Code::Numpad9,
237 '\u{E025}' => Code::NumpadAdd,
239 '\u{E026}' => Code::NumpadComma,
240 '\u{E028}' | '\u{E05D}' => Code::NumpadDecimal,
241 '\u{E029}' => Code::NumpadDivide,
242 '\u{E007}' => Code::NumpadEnter,
243 '\u{E024}' => Code::NumpadMultiply,
244 '\u{E027}' => Code::NumpadSubtract,
246 _ => Code::Unidentified,
247 }
248}
249
250fn is_shifted_character(raw_key: char) -> bool {
251 matches!(
252 raw_key,
253 '~' | '|'
254 | '{'
255 | '}'
256 | '<'
257 | ')'
258 | '!'
259 | '@'
260 | '#'
261 | '$'
262 | '%'
263 | '^'
264 | '&'
265 | '*'
266 | '('
267 | '+'
268 | '>'
269 | '_'
270 | '\"'
271 | ':'
272 | '?'
273 | '\u{E00D}'
274 | '\u{E05C}'
275 | '\u{E056}'
276 | '\u{E05B}'
277 | '\u{E055}'
278 | '\u{E058}'
279 | '\u{E05A}'
280 | '\u{E057}'
281 | '\u{E059}'
282 | '\u{E054}'
283 | '\u{E05D}'
284 | 'A'..='Z'
285 )
286}
287
288fn key_location(raw_key: char) -> Location {
289 match raw_key {
290 '\u{E007}'..='\u{E00A}' => Location::Left,
291 '\u{E01A}'..='\u{E029}' => Location::Numpad,
292 '\u{E03D}' => Location::Left,
293 '\u{E050}'..='\u{E053}' => Location::Right,
294 '\u{E054}'..='\u{E05D}' => Location::Numpad,
295 '\u{E019}' => Location::Numpad,
296 _ => Location::Standard,
297 }
298}
299
300fn get_modifier(key: &Key) -> Modifiers {
301 match key {
302 Key::Named(NamedKey::Alt) => Modifiers::ALT,
303 Key::Named(NamedKey::Shift) => Modifiers::SHIFT,
304 Key::Named(NamedKey::Control) => Modifiers::CONTROL,
305 Key::Named(NamedKey::Meta) => Modifiers::META,
306 _ => Modifiers::empty(),
307 }
308}
309
310#[derive(Clone, Debug, Default)]
314#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
315pub struct KeyInputState {
316 pressed: HashSet<Key>,
317 modifiers: Modifiers,
318}
319
320impl KeyInputState {
321 pub fn new() -> KeyInputState {
325 KeyInputState::default()
326 }
327
328 pub fn dispatch_keydown(&mut self, raw_key: char) -> KeyboardEvent {
338 let key = normalised_key_value(raw_key);
339 let repeat = self.pressed.contains(&key);
340 let code = code(raw_key);
341 let location = key_location(raw_key);
342 self.modifiers.insert(get_modifier(&key));
343 self.pressed.insert(key.clone());
344 KeyboardEvent {
345 state: KeyState::Down,
346 key,
347 code,
348 location,
349 modifiers: self.modifiers,
350 repeat,
351 is_composing: false,
352 }
353 }
354
355 pub fn dispatch_keyup(&mut self, raw_key: char) -> Option<KeyboardEvent> {
361 let key = normalised_key_value(raw_key);
362 if !self.pressed.contains(&key) {
363 return None;
364 }
365 let code = code(raw_key);
366 let location = key_location(raw_key);
367 self.modifiers.remove(get_modifier(&key));
368 self.pressed.remove(&key);
369 Some(KeyboardEvent {
370 state: KeyState::Up,
371 key,
372 code,
373 location,
374 modifiers: self.modifiers,
375 repeat: false,
376 is_composing: false,
377 })
378 }
379
380 fn clear(&mut self, undo_actions: &mut HashSet<char>, result: &mut Vec<Event>) {
381 let mut actions: Vec<_> = undo_actions.drain().collect();
382 actions.sort_unstable();
383 for action in actions {
384 result.push(self.dispatch_keyup(action).unwrap().into());
385 }
386 assert!(undo_actions.is_empty());
387 }
388
389 fn dispatch_typeable(&mut self, text: &mut String, result: &mut Vec<Event>) {
390 for character in text.chars() {
391 let shifted = self.modifiers.contains(Modifiers::SHIFT);
392 if is_shifted_character(character) && !shifted {
393 result.push(self.dispatch_keydown('\u{E008}').into());
395 }
396 if !is_shifted_character(character) && shifted {
397 result.push(self.dispatch_keyup('\u{E008}').unwrap().into());
399 }
400 result.push(self.dispatch_keydown(character).into());
401 result.push(self.dispatch_keyup(character).unwrap().into());
402 }
403 text.clear();
404 }
405}
406
407#[derive(Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
411#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
412pub enum Event {
413 Keyboard(KeyboardEvent),
414 Composition(CompositionEvent),
415}
416
417impl From<KeyboardEvent> for Event {
418 fn from(v: KeyboardEvent) -> Event {
419 Event::Keyboard(v)
420 }
421}
422
423impl From<CompositionEvent> for Event {
424 fn from(v: CompositionEvent) -> Event {
425 Event::Composition(v)
426 }
427}
428
429pub fn send_keys(text: &str) -> Vec<Event> {
433 #[allow(deprecated)]
434 fn is_modifier(text: &str) -> bool {
435 if text.chars().count() != 1 {
436 return false;
437 }
438 matches!(
440 normalised_key_value(first_char(text)),
441 Key::Named(
442 NamedKey::Alt
443 | NamedKey::AltGraph
444 | NamedKey::CapsLock
445 | NamedKey::Control
446 | NamedKey::Fn
447 | NamedKey::FnLock
448 | NamedKey::Meta
449 | NamedKey::NumLock
450 | NamedKey::ScrollLock
451 | NamedKey::Shift
452 | NamedKey::Symbol
453 | NamedKey::SymbolLock
454 | NamedKey::Hyper
455 | NamedKey::Super
456 )
457 )
458 }
459
460 fn is_typeable(text: &str) -> bool {
462 text.chars().count() == 1
463 }
464
465 let mut result = Vec::new();
466 let mut typeable_text = String::new();
467 let mut state = KeyInputState::new();
468 let mut undo_actions = HashSet::new();
469 for cluster in UnicodeSegmentation::graphemes(text, true) {
470 match cluster {
471 "\u{E000}" => {
472 state.dispatch_typeable(&mut typeable_text, &mut result);
473 state.clear(&mut undo_actions, &mut result);
474 }
475 s if is_modifier(s) => {
476 state.dispatch_typeable(&mut typeable_text, &mut result);
477 let raw_modifier = first_char(s);
478 result.push(state.dispatch_keydown(raw_modifier).into());
479 undo_actions.insert(raw_modifier);
480 }
481 s if is_typeable(s) => typeable_text.push_str(s),
482 s => {
483 state.dispatch_typeable(&mut typeable_text, &mut result);
484 result.push(
486 CompositionEvent {
487 state: CompositionState::Start,
488 data: String::new(),
489 }
490 .into(),
491 );
492 result.push(
493 CompositionEvent {
494 state: CompositionState::Update,
495 data: s.to_owned(),
496 }
497 .into(),
498 );
499 result.push(
500 CompositionEvent {
501 state: CompositionState::End,
502 data: s.to_owned(),
503 }
504 .into(),
505 );
506 }
507 }
508 }
509 state.dispatch_typeable(&mut typeable_text, &mut result);
510 state.clear(&mut undo_actions, &mut result);
511 result
512}