input_pipeline/dead_keys_handler.rs
1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implements dead key handling.
6//!
7//! Dead key is a character composition approach where an accented character,
8//! typically from a Western European alphabet, is composed by actuating two
9//! keys on the keyboard:
10//!
11//! 1. A "dead key" which determines which diacritic is to be placed on the
12//! character, and which produces no immediate output; and
13//! 2. The character onto which the diacritic is to be placed.
14//!
15//! The resulting two successive key actuations produce an effect of single
16//! accented character being emitted.
17//!
18//! The dead key handler relies on keymap already having been applied, and the
19//! use of key meanings.
20//!
21//! This means that the dead key handler must be added to the input pipeline
22//! after the keymap handler in the input pipeline.
23//!
24//! The dead key handler can delay or modify the key meanings, but it never delays nor
25//! modifies key events. This ensures that clients which require key events see the
26//! key events as they come in. The key meanings may be delayed because of the delayed
27//! effect of composition.
28//!
29//! The state machine of the dead key handler is watching for dead key and "live" key
30//! combinations, and handles all their possible interleaving. The event sequences
31//! vary from the "obvious" ones such as "dead key press and release followed
32//! by a live key press and release", to not so obvious ones such as: "dead key
33//! press and hold, shift press, live key press and hold followed by another
34//! live key press, followed by arbitrary sequence of key releases".
35//!
36//! See the documentation for [Handler] for some more detail.
37
38use crate::input_device::{
39 Handled, InputDeviceDescriptor, InputDeviceEvent, InputEvent, UnhandledInputEvent,
40};
41use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
42use crate::keyboard_binding::KeyboardEvent;
43use async_trait::async_trait;
44use core::fmt;
45use fidl_fuchsia_ui_input3::{KeyEventType, KeyMeaning};
46use fuchsia_inspect::health::Reporter;
47use std::cell::RefCell;
48use std::rc::Rc;
49use {rust_icu_sys as usys, rust_icu_unorm2 as unorm};
50
51// There probably is a more general method of determining whether the characters
52// are combining characters. But somehow it escapes me now.
53const GRAVE: u32 = 0x300;
54const ACUTE: u32 = 0x301;
55const CIRCUMFLEX: u32 = 0x302;
56const TILDE: u32 = 0x303;
57
58/// Returns true if `c` is one of the dead keys we support.
59///
60/// This should likely be some ICU library function, but I'm not sure which one.
61fn is_dead_key(c: u32) -> bool {
62 match c {
63 GRAVE | ACUTE | CIRCUMFLEX | TILDE => true,
64 _ => false,
65 }
66}
67
68/// Removes the combining effect from a combining code point, leaving only
69/// the diacritic.
70///
71/// This should likely be some ICU library function, but I'm not sure which one.
72fn remove_combination(c: u32) -> u32 {
73 match c {
74 GRAVE => '`' as u32,
75 ACUTE => '\'' as u32,
76 CIRCUMFLEX => '^' as u32,
77 TILDE => '~' as u32,
78 _ => c,
79 }
80}
81
82/// StoredEvent is an InputEvent which is known to be a keyboard event.
83#[derive(Debug, Clone)]
84struct StoredEvent {
85 event: KeyboardEvent,
86 device_descriptor: InputDeviceDescriptor,
87 event_time: zx::MonotonicInstant,
88 trace_id: Option<fuchsia_trace::Id>,
89}
90
91impl fmt::Display for StoredEvent {
92 // Implement a compact [Display], as the device descriptor is not
93 // normally very interesting to see.
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "event: {:?}, event_time: {:?}", &self.event, &self.event_time)
96 }
97}
98
99impl Into<InputEvent> for StoredEvent {
100 /// Converts [StoredEvent] into [InputEvent].
101 fn into(self) -> InputEvent {
102 InputEvent {
103 device_event: InputDeviceEvent::Keyboard(self.event),
104 device_descriptor: self.device_descriptor,
105 event_time: self.event_time,
106 handled: Handled::No,
107 trace_id: self.trace_id,
108 }
109 }
110}
111
112impl Into<Vec<InputEvent>> for StoredEvent {
113 fn into(self) -> Vec<InputEvent> {
114 vec![self.into()]
115 }
116}
117
118/// Whether a [StoredEvent] corresponds to a live key or a dead key.
119enum Liveness {
120 /// The key is dead.
121 Dead,
122 /// The key is live.
123 Live,
124}
125
126/// Whether two events are the same or different by key.
127enum Sameness {
128 /// Two events are the same by key.
129 Same,
130 /// Two events are different.
131 Other,
132}
133
134impl StoredEvent {
135 /// Repackages self into a new [StoredEvent], with `event` replaced as supplied.
136 fn into_with_event(self, event: KeyboardEvent) -> Self {
137 StoredEvent {
138 event,
139 device_descriptor: self.device_descriptor,
140 event_time: self.event_time,
141 trace_id: self.trace_id,
142 }
143 }
144
145 /// Returns the code point contained in this [StoredEvent].
146 fn code_point(&self) -> u32 {
147 match self.event.get_key_meaning() {
148 Some(KeyMeaning::Codepoint(c)) => c,
149 _ => panic!("programming error: requested code point for an event that has none"),
150 }
151 }
152
153 /// Modifies this [StoredEvent] to contain a new code point instead of whatever was there.
154 fn into_with_code_point(self, code_point: u32) -> Self {
155 let new_event =
156 self.event.clone().into_with_key_meaning(Some(KeyMeaning::Codepoint(code_point)));
157 self.into_with_event(new_event)
158 }
159
160 /// Returns true if [StoredEvent] contains a valid code point.
161 fn is_code_point(&self) -> bool {
162 match self.event.get_key_meaning() {
163 // Some nonprintable keys have the code point value set to 0.
164 Some(KeyMeaning::Codepoint(c)) => c != 0,
165 _ => false,
166 }
167 }
168
169 /// Returns whether the key is a dead key or not. The return value is an enum
170 /// to make the state machine match arms more readable.
171 fn key_liveness(&self) -> Liveness {
172 match self.event.get_key_meaning() {
173 Some(KeyMeaning::Codepoint(c)) if is_dead_key(c) => Liveness::Dead,
174 _ => Liveness::Live,
175 }
176 }
177
178 /// Returns the key event type (pressed, released, or something else)
179 fn e_type(&self) -> KeyEventType {
180 self.event.get_event_type_folded()
181 }
182
183 /// Returns a new [StoredEvent] based on `Self`, but with the combining effect removed.
184 fn into_base_character(self) -> Self {
185 let key_meaning = self.event.get_key_meaning();
186 match key_meaning {
187 Some(KeyMeaning::Codepoint(c)) => {
188 let new_event = self
189 .event
190 .clone()
191 .into_with_key_meaning(Some(KeyMeaning::Codepoint(remove_combination(c))));
192 self.into_with_event(new_event)
193 }
194 _ => self,
195 }
196 }
197
198 /// Returns a new [StoredEvent], but with key meaning removed.
199 fn remove_key_meaning(self) -> Self {
200 let mut event = self.event.clone();
201 // A zero code point means a KeyEvent for which its edit effect should
202 // be ignored. In contrast, an event with an unset code point has by
203 // definition the same effect as if the US QWERTY keymap were applied.
204 // See discussion at:
205 // https://groups.google.com/a/fuchsia.dev/g/ui-input-dev/c/ITYKvbJS6_o/m/8kK0DRccDAAJ
206 event = event.into_with_key_meaning(Some(KeyMeaning::Codepoint(0)));
207 self.into_with_event(event)
208 }
209
210 /// Returns whether the two keys `this` and `that` are in fact the same key
211 /// as per the USB HID usage reported. The return value is an enum to make
212 /// the state machine match arms more readable.
213 fn key_sameness(this: &StoredEvent, that: &StoredEvent) -> Sameness {
214 match this.event.get_key() == that.event.get_key() {
215 true => Sameness::Same,
216 false => Sameness::Other,
217 }
218 }
219}
220
221#[allow(clippy::large_enum_variant)] // TODO(https://fxbug.dev/401086995)
222/// State contains the current observed state of the dead key state machine.
223///
224/// The dead key composition is started by observing a key press that amounts
225/// to a dead key. The first non-dead key that gets actuated thereafter becomes
226/// the "live" key that we will attempt to add a diacritic to. When such a live
227/// key is actuated, we will emit a key meaning equivalent to producing an
228/// accented character.
229///
230/// A complication here is that composition can unfold in any number of ways.
231/// The user could press and release the dead key, then press and release
232/// the live key. The user could, also, press and hold the dead key, then
233/// press any number of live or dead keys in an arbitrary order.
234///
235/// Another complication is that the user could press the dead key twice, which
236/// should also be handled correctly. In this case, "correct" handling implies
237/// emitting the dead key as an accented character. Similarly, two different
238/// dead keys pressed in succession are handled by (1) emitting the first as
239/// an accented character, and restarting composition with the second. It is
240/// worth noting that the key press and key release events could be arbitrarily
241/// interleaved for the two dead keys, and that should be handled correctly too.
242///
243/// A third complication is that, while all the composition is taking place,
244/// the pipeline must emit the `KeyEvent`s consistent with the key event protocol,
245/// but keep key meanings suppressed until the time that the key meanings have
246/// been resolved by the combination.
247///
248/// The elements of state are as follows:
249///
250/// * Did we see a dead key press event? (bit `a`)
251/// * Did we see a dead key release event? (bit `b`)
252/// * Did we see a live key press event? (bit `c`)
253/// * Did we see a live key release event? (bit `d`)
254///
255/// Almost any variation of the above elements is possible and allowed. Even
256/// the states that ostensibly shouldn't be possible (e.g. observed a release
257/// event before a press) should be accounted for in order to implement
258/// self-correcting behavior if needed. The [State] enum below encodes each
259/// state as a name `Sdcba`, where each of `a..d` are booleans, encoded
260/// as characters `0` and `1` as conventional. So for example, `S0101`
261/// is a state where we observed a dead key press event, and a live key press
262/// event. I made an experiment where I tried to use more illustrative state
263/// names, but the number of variations didn't make the resulting names any more
264/// meaningful compared to the current state name encoding scheme. So compact
265/// naming it is.
266#[derive(Debug, Clone)]
267enum State {
268 /// We have yet to see a key to act on.
269 S0000,
270
271 /// We saw an actuation of a dead key.
272 S0001 { dead_key_down: StoredEvent },
273
274 /// A dead key was pressed and released.
275 S0011 { dead_key_down: StoredEvent, dead_key_up: StoredEvent },
276
277 /// A dead key was pressed and released, followed by a live key press.
278 S0111 { dead_key_down: StoredEvent, dead_key_up: StoredEvent, live_key_down: StoredEvent },
279
280 /// A dead key was pressed, followed by a live key press.
281 S0101 { dead_key_down: StoredEvent, live_key_down: StoredEvent },
282
283 /// A dead key was pressed, then a live key was pressed and released.
284 S1101 { dead_key_down: StoredEvent },
285}
286
287#[derive(Debug)]
288pub struct DeadKeysHandler {
289 /// Tracks the current state of the dead key composition.
290 state: RefCell<State>,
291
292 /// The unicode normalizer used for composition.
293 normalizer: unorm::UNormalizer,
294
295 /// This handler requires ICU data to be live. This is ensured by holding
296 /// a reference to an ICU data loader.
297 _data: icu_data::Loader,
298
299 /// The inventory of this handler's Inspect status.
300 pub inspect_status: InputHandlerStatus,
301}
302
303/// This trait implementation allows the [Handler] to be hooked up into the input
304/// pipeline.
305#[async_trait(?Send)]
306impl UnhandledInputHandler for DeadKeysHandler {
307 async fn handle_unhandled_input_event(
308 self: Rc<Self>,
309 unhandled_input_event: UnhandledInputEvent,
310 ) -> Vec<InputEvent> {
311 self.handle_unhandled_input_event_internal(unhandled_input_event)
312 }
313
314 fn set_handler_healthy(self: std::rc::Rc<Self>) {
315 self.inspect_status.health_node.borrow_mut().set_ok();
316 }
317
318 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
319 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
320 }
321}
322
323impl DeadKeysHandler {
324 /// Creates a new instance of the dead keys handler.
325 pub fn new(
326 icu_data: icu_data::Loader,
327 input_handlers_node: &fuchsia_inspect::Node,
328 ) -> Rc<Self> {
329 let inspect_status = InputHandlerStatus::new(
330 input_handlers_node,
331 "dead_keys_handler",
332 /* generates_events */ false,
333 );
334 let handler = DeadKeysHandler {
335 state: RefCell::new(State::S0000),
336 // The NFC normalizer performs the needed composition and is not
337 // lossy.
338 normalizer: unorm::UNormalizer::new_nfc().unwrap(),
339 _data: icu_data,
340 inspect_status,
341 };
342 Rc::new(handler)
343 }
344
345 fn handle_unhandled_input_event_internal(
346 self: Rc<Self>,
347 unhandled_input_event: UnhandledInputEvent,
348 ) -> Vec<InputEvent> {
349 fuchsia_trace::duration!(c"input", c"dead_keys_handler");
350 match unhandled_input_event.clone() {
351 UnhandledInputEvent {
352 device_event: InputDeviceEvent::Keyboard(event),
353 device_descriptor,
354 event_time,
355 trace_id,
356 } => {
357 fuchsia_trace::duration!(c"input", c"dead_keys_handler[processing]");
358 if let Some(trace_id) = trace_id {
359 fuchsia_trace::flow_step!(
360 c"input",
361 c"event_in_input_pipeline",
362 trace_id.into()
363 );
364 }
365
366 self.inspect_status.count_received_event(&event_time);
367 let event = StoredEvent { event, device_descriptor, event_time, trace_id };
368 // Separated into two statements to ensure the logs are not truncated.
369 log::debug!("state: {:?}", self.state.borrow());
370 log::debug!("event: {}", &event);
371 let result = self.process_keyboard_event(event);
372 log::debug!("result: {:?}", &result);
373 result
374 }
375
376 // Pass other events unchanged.
377 _ => vec![InputEvent::from(unhandled_input_event)],
378 }
379 }
380
381 /// Sets the internal handler state to `new_state`.
382 fn set_state(self: &Rc<Self>, new_state: State) {
383 *(self.state.borrow_mut()) = new_state;
384 }
385
386 /// Attaches a key meaning to each passing keyboard event.
387 ///
388 /// Underlying this function is a state machine which registers the flow of dead and live keys
389 /// after each reported event, and modifies the input event stream accordingly. For example,
390 /// a sequence of events where a dead key is pressed and released, followed by a live key
391 /// press and release, results in a composed character being emitted. The state machine
392 /// takese care of this sequence, but also of other less obvious sequences and their effects.
393 fn process_keyboard_event(self: &Rc<Self>, event: StoredEvent) -> Vec<InputEvent> {
394 if !event.is_code_point() {
395 // Pass through any non-codepoint events.
396 return event.into();
397 }
398 let old_state = self.state.borrow().clone();
399 match old_state {
400 // We are waiting for the composition to begin.
401 State::S0000 => match (event.key_liveness(), event.e_type()) {
402 // A dead key press starts composition. We advance to the next
403 // state machine state, and eliminate any key meaning from the
404 // key event, since we anticipate its use in composition.
405 (Liveness::Dead, KeyEventType::Pressed) => {
406 self.set_state(State::S0001 { dead_key_down: event.clone() });
407 event.remove_key_meaning().into()
408 }
409
410 // A dead key release while we're waiting for a dead key press,
411 // this is probably a remnant of an earlier double press, remove the
412 // combining from it and forward. Keep waiting for composition
413 // to begin.
414 (Liveness::Dead, KeyEventType::Released) => event.into_base_character().into(),
415
416 // Any other events can be forwarded unmodified.
417 _ => event.into(),
418 },
419
420 // We have seen a dead key press, but not release.
421 State::S0001 { dead_key_down } => {
422 match (
423 event.key_liveness(),
424 StoredEvent::key_sameness(&event, &dead_key_down),
425 event.e_type(),
426 ) {
427 // The same dead key that was pressed the other time was released.
428 // Emit a stripped version, and start waiting for a live key.
429 (Liveness::Dead, Sameness::Same, KeyEventType::Released) => {
430 self.set_state(State::S0011 { dead_key_down, dead_key_up: event.clone() });
431 event.remove_key_meaning().into()
432 }
433
434 // Another dead key was released at this point. Since
435 // we can not start a new combination here, we must forward
436 // it with meaning stripped.
437 (Liveness::Dead, Sameness::Other, KeyEventType::Released) => {
438 event.remove_key_meaning().into()
439 }
440
441 // The same dead key was pressed again, while we have seen
442 // it pressed before. This can happen when autorepeat kicks
443 // in. We treat this the same as two successive actuations
444 // i.e. we send a stripped version of the character, and
445 // go back to waiting.
446 (Liveness::Dead, Sameness::Same, KeyEventType::Pressed) => {
447 self.set_state(State::S0000);
448 event.into_base_character().into()
449 }
450
451 // A different dead key was pressed. This stops the ongoing
452 // composition, and starts a new one with a new dead key. However,
453 // what we emit is a bit subtle: we emit a key press event
454 // for the *new* key, but with a key meaning of the stripped
455 // version of the current key.
456 (Liveness::Dead, Sameness::Other, KeyEventType::Pressed) => {
457 let current_removed = dead_key_down.clone().into_base_character();
458 self.set_state(State::S0001 { dead_key_down: event.clone() });
459 event.into_with_code_point(current_removed.code_point()).into()
460 }
461
462 // A live key was pressed while the dead key is held down. Yay!
463 //
464 // Compose and ship out the live key with attached new meaning.
465 //
466 // A very similar piece of code happens in the state `State::S0011`,
467 // except we get there through a different sequence of events.
468 // Please refer to that code for the details about composition.
469 (Liveness::Live, _, KeyEventType::Pressed) => {
470 let maybe_composed = self.normalizer.compose_pair(
471 event.code_point() as usys::UChar32,
472 dead_key_down.code_point() as usys::UChar32,
473 );
474
475 if maybe_composed >= 0 {
476 // Composition was a success.
477 let composed_event = event.into_with_code_point(maybe_composed as u32);
478 self.set_state(State::S0101 {
479 dead_key_down,
480 live_key_down: composed_event.clone(),
481 });
482 return composed_event.into();
483 } else {
484 // FAIL!
485 self.set_state(State::S0101 {
486 dead_key_down,
487 live_key_down: event.clone(),
488 });
489 return event.into();
490 }
491 }
492 // All other key events are forwarded unmodified.
493 _ => event.into(),
494 }
495 }
496
497 // The dead key was pressed and released, the first live key that
498 // gets pressed after that now will be used for the composition.
499 State::S0011 { dead_key_down, dead_key_up } => {
500 match (event.key_liveness(), event.e_type()) {
501 // We observed a dead key actuation.
502 (Liveness::Dead, KeyEventType::Pressed) => {
503 match StoredEvent::key_sameness(&dead_key_down, &event) {
504 // The user pressed the same dead key again. Let's "compose" it by
505 // stripping its diacritic and making that a compose key.
506 Sameness::Same => {
507 let event = event.into_base_character();
508 self.set_state(State::S0111 {
509 dead_key_down,
510 dead_key_up,
511 live_key_down: event.clone(),
512 });
513 event.into()
514 }
515 // The user pressed a different dead key. It would have been nice
516 // to start a new composition, but we can not express that with the
517 // KeyEvent API, since that would require emitting spurious press and
518 // release key events for the dead key press and release.
519 //
520 // Instead, forward the key unmodified and cancel
521 // the composition. We may revisit this if the KeyEvent API is
522 // changed to allow decoupling key events from key meanings.
523 Sameness::Other => {
524 self.set_state(State::S0000);
525 event.into_base_character().into()
526 }
527 }
528 }
529
530 // We observed a dead key release. This is likely a dead key
531 // from the *previous* composition attempt. Nothing to do here,
532 // except forward it stripped of key meaning.
533 (Liveness::Dead, KeyEventType::Released) => event.remove_key_meaning().into(),
534
535 // Oh, frabjous day! Someone pressed a live key that may be
536 // possible to combine! Let's try it out! If composition is
537 // a success, emit the current key with the meaning set to
538 // the composed character.
539 (Liveness::Live, KeyEventType::Pressed) => {
540 let maybe_composed = self.normalizer.compose_pair(
541 event.code_point() as usys::UChar32,
542 dead_key_down.code_point() as usys::UChar32,
543 );
544
545 if maybe_composed >= 0 {
546 // Composition was a success.
547 // Emit the composed event, remember it also when
548 // transitioning to S0111, so we can recover the key meaning
549 // when the live key is released.
550 let composed_event = event.into_with_code_point(maybe_composed as u32);
551 self.set_state(State::S0111 {
552 dead_key_down,
553 dead_key_up,
554 live_key_down: composed_event.clone(),
555 });
556 return composed_event.into();
557 } else {
558 log::debug!("compose failed for: {}\n", &event);
559 // FAIL!
560 // Composition failed, what now? We would need to
561 // emit TWO characters - one for the now-defunct
562 // dead key, and another for the current live key.
563 // But this is not possible, since we may not emit
564 // more combining key events, but must always emit
565 // both the key and the key meaning since that is
566 // how our protocol works. Well, we reached the
567 // limit of what key event composition may do, so
568 // let's simply agree to emit the current event
569 // unmodified and forget we had the dead key.
570 self.set_state(State::S0111 {
571 dead_key_down,
572 dead_key_up,
573 live_key_down: event.clone(),
574 });
575 return event.into();
576 }
577 }
578
579 // All other key events are forwarded unmodified.
580 _ => event.into(),
581 }
582 }
583
584 // We already combined the live key with the dead key, and are
585 // now waiting for the live key to be released.
586 State::S0111 { dead_key_down, dead_key_up, live_key_down } => {
587 match (
588 event.key_liveness(),
589 // Here we compare the current key with the live key down,
590 // unlike in prior states.
591 StoredEvent::key_sameness(&event, &live_key_down),
592 event.e_type(),
593 ) {
594 // This is what we've been waiting for: the live key is now
595 // lifted. Emit the live key release using the same code point
596 // as we used when the key went down, and we're done.
597 (Liveness::Live, Sameness::Same, KeyEventType::Released) => {
598 self.set_state(State::S0000);
599 event.into_with_code_point(live_key_down.code_point()).into()
600 }
601
602 // A second press of the live key we're combining. This is
603 // probably a consequence of autorepeat. The effect should
604 // be to complete the composition and continue emitting the
605 // "base" key meaning for any further repeats; but also
606 // continue waiting for a key release.
607 (Liveness::Live, Sameness::Same, KeyEventType::Pressed) => {
608 let base_codepoint = event.code_point();
609 let combined_event =
610 event.clone().into_with_code_point(live_key_down.code_point());
611 // We emit a combined key, but further repeats will use the
612 // base code point and not combine.
613 self.set_state(State::S0111 {
614 dead_key_down,
615 dead_key_up,
616 live_key_down: event.into_with_code_point(base_codepoint),
617 });
618 combined_event.into()
619 }
620
621 // If another live key event comes in, just forward it, and
622 // continue waiting for the last live key release.
623 (Liveness::Live, Sameness::Other, _) => event.into(),
624
625 // Another dead key has been pressed in addition to what
626 // had been pressed before. So now, we are waiting for the
627 // user to release the live key we already composed, but the
628 // user is again pressing a compose key instead.
629 //
630 // Ideally, we'd want to start new composition with the
631 // new dead key. But, there's still the issue with the
632 // live key that is still being pressed: when it is eventually
633 // released, we want to have it have exactly the same key
634 // meaning as what we emitted for when it was pressed. But,
635 // that may happen arbitrarily late afterwards, and we'd
636 // prefer not to keep any composition state for that long.
637 //
638 // That suggests that we must not honor this new dead key
639 // as composition. But, also, we must not drop the key
640 // event on the floor, since the clients that read key
641 // events must receive it. So, we just *turn* off
642 // the combining effect on this key, forward it like that,
643 // and continue waiting for the key release.
644 (Liveness::Dead, _, KeyEventType::Pressed) => event.remove_key_meaning().into(),
645
646 (Liveness::Dead, _, KeyEventType::Released) => {
647 match StoredEvent::key_sameness(&event, &live_key_down) {
648 // Special: if the released key a dead key and the same as the
649 // "live" composing key, then we're seeing a release of a doubly-
650 // pressed dead key. This one needs to be emitted as a diacritic.
651 Sameness::Same => {
652 self.set_state(State::S0000);
653 event.into_base_character().into()
654 }
655
656 // All other dead keys are forwarded with stripped key meanings.
657 // We have no way to handle them further.
658 Sameness::Other => event.remove_key_meaning().into(),
659 }
660 }
661
662 // Forward any other events unmodified.
663 _ => event.into(),
664 }
665 }
666
667 // The user pressed and is holding the dead key; and pressed and
668 // is holding a live key.
669 State::S0101 { dead_key_down, live_key_down } => {
670 match (event.key_liveness(), event.e_type()) {
671 // The same dead key we're already holding is pressed. Just forward
672 // the key event, but not meaning.
673 (Liveness::Dead, KeyEventType::Pressed) => event.remove_key_meaning().into(),
674
675 (Liveness::Dead, KeyEventType::Released) => {
676 // The dead key that we are using for combining is released.
677 // Emit its release event without a key meaning and go to a
678 // state that expects a release of the live key.
679 match StoredEvent::key_sameness(&dead_key_down, &event) {
680 Sameness::Same => {
681 self.set_state(State::S0111 {
682 dead_key_down,
683 dead_key_up: event.clone(),
684 live_key_down,
685 });
686 event.remove_key_meaning().into()
687 }
688
689 // Other dead key is released. Remove its key meaning, but forward.
690 Sameness::Other => event.remove_key_meaning().into(),
691 }
692 }
693 (Liveness::Live, KeyEventType::Pressed) => {
694 match StoredEvent::key_sameness(&live_key_down, &event) {
695 // The currently pressed live key is pressed again.
696 // This is autorepeat. We emit one composed key, but any
697 // further emitted keys will not compose. This
698 // should be similar to `State::S0111`, except the
699 // transition is back to *this* state.
700 Sameness::Same => {
701 let base_codepoint = event.code_point();
702 let combined_event =
703 event.clone().into_with_code_point(live_key_down.code_point());
704 self.set_state(State::S0101 {
705 dead_key_down,
706 live_key_down: event.into_with_code_point(base_codepoint),
707 });
708 combined_event.into()
709 }
710 Sameness::Other => event.into(),
711 }
712 }
713 (Liveness::Live, KeyEventType::Released) => {
714 match StoredEvent::key_sameness(&live_key_down, &event) {
715 Sameness::Same => {
716 self.set_state(State::S1101 { dead_key_down });
717 event.into_with_code_point(live_key_down.code_point()).into()
718 }
719
720 // Any other release just gets forwarded.
721 Sameness::Other => event.into(),
722 }
723 }
724
725 // Forward any other events unmodified
726 _ => event.into(),
727 }
728 }
729
730 // The dead key is still actuated, but we already sent out the
731 // combined versions of the live key.
732 State::S1101 { dead_key_down } => {
733 match (event.key_liveness(), event.e_type()) {
734 (Liveness::Dead, KeyEventType::Pressed) => {
735 // Two possible cases here, but the outcome is the
736 // same:
737 //
738 // The same dead key is pressed again. Let's not
739 // do any more compositions here.
740 //
741 // A different dead key has been pressed. We can
742 // not start a new composition while we have not
743 // closed out the current composition. For this
744 // reason we ignore the other key.
745 //
746 // A real compositioning API would perhaps allow us
747 // to stack compositions on top of each other, but
748 // we will require any such consumers to go talk to
749 // the text editing API instead.
750 event.remove_key_meaning().into()
751 }
752
753 (Liveness::Dead, KeyEventType::Released) => {
754 match StoredEvent::key_sameness(&dead_key_down, &event) {
755 // The dead key is released, the composition is
756 // done, let's close up shop.
757 Sameness::Same => {
758 self.set_state(State::S0000);
759 event.remove_key_meaning().into()
760 }
761 // A dead key was released, but not the one that we
762 // are combining by. Forward with the combining
763 // effect stripped.
764 Sameness::Other => event.remove_key_meaning().into(),
765 }
766 }
767
768 // Any additional live keys, no matter if they are the same
769 // as the one currently being composed, will *not* be composed,
770 // we forward them unmodified as we wait to close off this
771 // composition.
772 //
773 // Forward any other events unmodified.
774 _ => event.into(),
775 }
776 }
777 }
778 }
779}
780
781#[cfg(test)]
782mod tests {
783 use super::*;
784 use crate::testing_utilities;
785 use fidl_fuchsia_input::Key;
786 use fidl_fuchsia_input_report::ConsumerControlButton;
787
788 use pretty_assertions::assert_eq;
789 use std::convert::TryFrom as _;
790
791 // Creates a new keyboard event for testing.
792 fn new_event(
793 key: Key,
794 event_type: KeyEventType,
795 key_meaning: Option<KeyMeaning>,
796 ) -> UnhandledInputEvent {
797 UnhandledInputEvent::try_from(testing_utilities::create_keyboard_event_with_handled(
798 key,
799 event_type,
800 /*modifiers=*/ None,
801 /*event_time*/ zx::MonotonicInstant::ZERO,
802 &InputDeviceDescriptor::Fake,
803 /*keymap=*/ None,
804 key_meaning,
805 /*handled=*/ Handled::No,
806 ))
807 .unwrap()
808 }
809
810 // Tests some common keyboard input use cases with dead keys actuation.
811 #[test]
812 fn test_input_processing() {
813 // A zero codepoint is a way to let the consumers know that this key
814 // event should have no effect on the edited text; even though its
815 // key event may have other effects, such as moving the hero across
816 // the screen in a game.
817 const ZERO_CP: Option<KeyMeaning> = Some(KeyMeaning::Codepoint(0));
818
819 #[derive(Debug)]
820 struct TestCase {
821 name: &'static str,
822 // The sequence of input events at the input of the dead keys
823 // handler.
824 inputs: Vec<UnhandledInputEvent>,
825 // The expected sequence of input events, after being transformed
826 // by the dead keys handler.
827 expected: Vec<UnhandledInputEvent>,
828 }
829 let tests: Vec<TestCase> = vec![
830 TestCase {
831 name: "passthrough",
832 inputs: vec![
833 new_event(
834 Key::A,
835 KeyEventType::Pressed,
836 Some(KeyMeaning::Codepoint('A' as u32)),
837 ),
838 new_event(
839 Key::A,
840 KeyEventType::Released,
841 Some(KeyMeaning::Codepoint('A' as u32)),
842 ),
843 ],
844 expected: vec![
845 new_event(
846 Key::A,
847 KeyEventType::Pressed,
848 Some(KeyMeaning::Codepoint('A' as u32)),
849 ),
850 new_event(
851 Key::A,
852 KeyEventType::Released,
853 Some(KeyMeaning::Codepoint('A' as u32)),
854 ),
855 ],
856 },
857 TestCase {
858 name: "A circumflex - dead key first, then live key",
859 inputs: vec![
860 new_event(
861 Key::Key5,
862 KeyEventType::Pressed,
863 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
864 ),
865 new_event(
866 Key::Key5,
867 KeyEventType::Released,
868 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
869 ),
870 new_event(
871 Key::A,
872 KeyEventType::Pressed,
873 Some(KeyMeaning::Codepoint('A' as u32)),
874 ),
875 new_event(
876 Key::A,
877 KeyEventType::Released,
878 Some(KeyMeaning::Codepoint('A' as u32)),
879 ),
880 ],
881 expected: vec![
882 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
883 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
884 new_event(
885 Key::A,
886 KeyEventType::Pressed,
887 Some(KeyMeaning::Codepoint('Â' as u32)),
888 ),
889 new_event(
890 Key::A,
891 KeyEventType::Released,
892 Some(KeyMeaning::Codepoint('Â' as u32)),
893 ),
894 ],
895 },
896 TestCase {
897 name: "A circumflex - dead key held all the way through composition",
898 inputs: vec![
899 new_event(
900 Key::Key5,
901 KeyEventType::Pressed,
902 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
903 ),
904 new_event(
905 Key::A,
906 KeyEventType::Pressed,
907 Some(KeyMeaning::Codepoint('A' as u32)),
908 ),
909 new_event(
910 Key::A,
911 KeyEventType::Released,
912 Some(KeyMeaning::Codepoint('A' as u32)),
913 ),
914 new_event(
915 Key::Key5,
916 KeyEventType::Released,
917 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
918 ),
919 ],
920 expected: vec![
921 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
922 new_event(
923 Key::A,
924 KeyEventType::Pressed,
925 Some(KeyMeaning::Codepoint('Â' as u32)),
926 ),
927 new_event(
928 Key::A,
929 KeyEventType::Released,
930 Some(KeyMeaning::Codepoint('Â' as u32)),
931 ),
932 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
933 ],
934 },
935 TestCase {
936 name: "A circumflex - dead key held until the live key was down",
937 inputs: vec![
938 new_event(
939 Key::Key5,
940 KeyEventType::Pressed,
941 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
942 ),
943 new_event(
944 Key::A,
945 KeyEventType::Pressed,
946 Some(KeyMeaning::Codepoint('A' as u32)),
947 ),
948 new_event(
949 Key::Key5,
950 KeyEventType::Released,
951 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
952 ),
953 new_event(
954 Key::A,
955 KeyEventType::Released,
956 Some(KeyMeaning::Codepoint('A' as u32)),
957 ),
958 ],
959 expected: vec![
960 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
961 new_event(
962 Key::A,
963 KeyEventType::Pressed,
964 Some(KeyMeaning::Codepoint('Â' as u32)),
965 ),
966 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
967 new_event(
968 Key::A,
969 KeyEventType::Released,
970 Some(KeyMeaning::Codepoint('Â' as u32)),
971 ),
972 ],
973 },
974 TestCase {
975 name: "Combining character pressed twice - results in a single diacritic",
976 inputs: vec![
977 new_event(
978 Key::Key5,
979 KeyEventType::Pressed,
980 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
981 ),
982 new_event(
983 Key::Key5,
984 KeyEventType::Released,
985 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
986 ),
987 new_event(
988 Key::Key5,
989 KeyEventType::Pressed,
990 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
991 ),
992 new_event(
993 Key::Key5,
994 KeyEventType::Released,
995 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
996 ),
997 ],
998 expected: vec![
999 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1000 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1001 new_event(
1002 Key::Key5,
1003 KeyEventType::Pressed,
1004 Some(KeyMeaning::Codepoint('^' as u32)),
1005 ),
1006 new_event(
1007 Key::Key5,
1008 KeyEventType::Released,
1009 Some(KeyMeaning::Codepoint('^' as u32)),
1010 ),
1011 ],
1012 },
1013 TestCase {
1014 name: "A circumflex - dead key spans live key",
1015 inputs: vec![
1016 new_event(
1017 Key::Key5,
1018 KeyEventType::Pressed,
1019 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1020 ),
1021 new_event(
1022 Key::A,
1023 KeyEventType::Pressed,
1024 Some(KeyMeaning::Codepoint('A' as u32)),
1025 ),
1026 new_event(
1027 Key::A,
1028 KeyEventType::Released,
1029 Some(KeyMeaning::Codepoint('A' as u32)),
1030 ),
1031 new_event(
1032 Key::Key5,
1033 KeyEventType::Released,
1034 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1035 ),
1036 ],
1037 expected: vec![
1038 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1039 new_event(
1040 Key::A,
1041 KeyEventType::Pressed,
1042 Some(KeyMeaning::Codepoint('Â' as u32)),
1043 ),
1044 new_event(
1045 Key::A,
1046 KeyEventType::Released,
1047 Some(KeyMeaning::Codepoint('Â' as u32)),
1048 ),
1049 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1050 ],
1051 },
1052 TestCase {
1053 name: "Only the first key after the dead key actuation is composed",
1054 inputs: vec![
1055 new_event(
1056 Key::Key5,
1057 KeyEventType::Pressed,
1058 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1059 ),
1060 new_event(
1061 Key::Key5,
1062 KeyEventType::Released,
1063 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1064 ),
1065 new_event(
1066 Key::A,
1067 KeyEventType::Pressed,
1068 Some(KeyMeaning::Codepoint('A' as u32)),
1069 ),
1070 new_event(
1071 Key::E,
1072 KeyEventType::Pressed,
1073 Some(KeyMeaning::Codepoint('E' as u32)),
1074 ),
1075 new_event(
1076 Key::A,
1077 KeyEventType::Released,
1078 Some(KeyMeaning::Codepoint('A' as u32)),
1079 ),
1080 new_event(
1081 Key::E,
1082 KeyEventType::Released,
1083 Some(KeyMeaning::Codepoint('E' as u32)),
1084 ),
1085 ],
1086 expected: vec![
1087 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1088 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1089 new_event(
1090 Key::A,
1091 KeyEventType::Pressed,
1092 Some(KeyMeaning::Codepoint('Â' as u32)),
1093 ),
1094 new_event(
1095 Key::E,
1096 KeyEventType::Pressed,
1097 Some(KeyMeaning::Codepoint('E' as u32)),
1098 ),
1099 new_event(
1100 Key::A,
1101 KeyEventType::Released,
1102 Some(KeyMeaning::Codepoint('Â' as u32)),
1103 ),
1104 new_event(
1105 Key::E,
1106 KeyEventType::Released,
1107 Some(KeyMeaning::Codepoint('E' as u32)),
1108 ),
1109 ],
1110 },
1111 TestCase {
1112 name: "Modifier keys are not affected",
1113 inputs: vec![
1114 new_event(
1115 Key::Key5,
1116 KeyEventType::Pressed,
1117 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1118 ),
1119 new_event(
1120 Key::Key5,
1121 KeyEventType::Released,
1122 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1123 ),
1124 new_event(Key::LeftShift, KeyEventType::Pressed, ZERO_CP),
1125 new_event(
1126 Key::A,
1127 KeyEventType::Pressed,
1128 Some(KeyMeaning::Codepoint('A' as u32)),
1129 ),
1130 new_event(
1131 Key::A,
1132 KeyEventType::Released,
1133 Some(KeyMeaning::Codepoint('A' as u32)),
1134 ),
1135 new_event(Key::LeftShift, KeyEventType::Released, ZERO_CP),
1136 ],
1137 expected: vec![
1138 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1139 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1140 new_event(Key::LeftShift, KeyEventType::Pressed, ZERO_CP),
1141 new_event(
1142 Key::A,
1143 KeyEventType::Pressed,
1144 Some(KeyMeaning::Codepoint('Â' as u32)),
1145 ),
1146 new_event(
1147 Key::A,
1148 KeyEventType::Released,
1149 Some(KeyMeaning::Codepoint('Â' as u32)),
1150 ),
1151 new_event(Key::LeftShift, KeyEventType::Released, ZERO_CP),
1152 ],
1153 },
1154 TestCase {
1155 name: "Two dead keys in succession - no compose",
1156 inputs: vec![
1157 new_event(
1158 Key::Key5,
1159 KeyEventType::Pressed,
1160 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1161 ),
1162 new_event(
1163 Key::Key5,
1164 KeyEventType::Released,
1165 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1166 ),
1167 new_event(
1168 Key::A,
1169 KeyEventType::Pressed,
1170 Some(KeyMeaning::Codepoint(GRAVE as u32)),
1171 ),
1172 new_event(
1173 Key::A,
1174 KeyEventType::Released,
1175 Some(KeyMeaning::Codepoint(GRAVE as u32)),
1176 ),
1177 ],
1178 expected: vec![
1179 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1180 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1181 new_event(
1182 Key::A,
1183 KeyEventType::Pressed,
1184 Some(KeyMeaning::Codepoint('`' as u32)),
1185 ),
1186 new_event(
1187 Key::A,
1188 KeyEventType::Released,
1189 Some(KeyMeaning::Codepoint('`' as u32)),
1190 ),
1191 ],
1192 },
1193 TestCase {
1194 name: "Compose with capital letter",
1195 inputs: vec![
1196 new_event(
1197 Key::Key5,
1198 KeyEventType::Pressed,
1199 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1200 ),
1201 new_event(
1202 Key::Key5,
1203 KeyEventType::Released,
1204 Some(KeyMeaning::Codepoint(CIRCUMFLEX as u32)),
1205 ),
1206 new_event(
1207 Key::LeftShift,
1208 KeyEventType::Pressed,
1209 Some(KeyMeaning::Codepoint(0)),
1210 ),
1211 new_event(
1212 Key::A,
1213 KeyEventType::Pressed,
1214 Some(KeyMeaning::Codepoint('A' as u32)),
1215 ),
1216 new_event(
1217 Key::A,
1218 KeyEventType::Released,
1219 Some(KeyMeaning::Codepoint('A' as u32)),
1220 ),
1221 new_event(
1222 Key::LeftShift,
1223 KeyEventType::Released,
1224 Some(KeyMeaning::Codepoint(0)),
1225 ),
1226 ],
1227 expected: vec![
1228 new_event(Key::Key5, KeyEventType::Pressed, ZERO_CP),
1229 new_event(Key::Key5, KeyEventType::Released, ZERO_CP),
1230 new_event(
1231 Key::LeftShift,
1232 KeyEventType::Pressed,
1233 Some(KeyMeaning::Codepoint(0)),
1234 ),
1235 new_event(
1236 Key::A,
1237 KeyEventType::Pressed,
1238 Some(KeyMeaning::Codepoint('Â' as u32)),
1239 ),
1240 new_event(
1241 Key::A,
1242 KeyEventType::Released,
1243 Some(KeyMeaning::Codepoint('Â' as u32)),
1244 ),
1245 new_event(
1246 Key::LeftShift,
1247 KeyEventType::Released,
1248 Some(KeyMeaning::Codepoint(0)),
1249 ),
1250 ],
1251 },
1252 ];
1253 let inspector = fuchsia_inspect::Inspector::default();
1254 let test_node = inspector.root().create_child("test_node");
1255 let loader = icu_data::Loader::new().unwrap();
1256 let handler = super::DeadKeysHandler::new(loader, &test_node);
1257 for test in tests {
1258 let actuals: Vec<InputEvent> = test
1259 .inputs
1260 .into_iter()
1261 .map(|event| handler.clone().handle_unhandled_input_event_internal(event))
1262 .flatten()
1263 .collect();
1264 assert_eq!(
1265 test.expected.into_iter().map(InputEvent::from).collect::<Vec<_>>(),
1266 actuals,
1267 "in test: {}",
1268 test.name
1269 );
1270 }
1271 }
1272
1273 #[fuchsia::test]
1274 async fn dead_keys_handler_initialized_with_inspect_node() {
1275 let loader = icu_data::Loader::new().unwrap();
1276 let inspector = fuchsia_inspect::Inspector::default();
1277 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1278 let _handler = DeadKeysHandler::new(loader, &fake_handlers_node);
1279 diagnostics_assertions::assert_data_tree!(inspector, root: {
1280 input_handlers_node: {
1281 dead_keys_handler: {
1282 events_received_count: 0u64,
1283 events_handled_count: 0u64,
1284 last_received_timestamp_ns: 0u64,
1285 "fuchsia.inspect.Health": {
1286 status: "STARTING_UP",
1287 // Timestamp value is unpredictable and not relevant in this context,
1288 // so we only assert that the property is present.
1289 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1290 },
1291 }
1292 }
1293 });
1294 }
1295
1296 #[fuchsia::test]
1297 async fn dead_keys_handler_inspect_counts_events() {
1298 let loader = icu_data::Loader::new().unwrap();
1299 let inspector = fuchsia_inspect::Inspector::default();
1300 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
1301 let handler = DeadKeysHandler::new(loader, &fake_handlers_node);
1302
1303 // Inspect should count unhandled key events and ignore irrelevent InputEvent types.
1304 let events = vec![
1305 new_event(Key::A, KeyEventType::Pressed, Some(KeyMeaning::Codepoint('A' as u32))),
1306 UnhandledInputEvent::try_from(testing_utilities::create_consumer_controls_event(
1307 vec![ConsumerControlButton::VolumeUp],
1308 zx::MonotonicInstant::ZERO,
1309 &testing_utilities::consumer_controls_device_descriptor(),
1310 ))
1311 .unwrap(),
1312 new_event(Key::A, KeyEventType::Released, Some(KeyMeaning::Codepoint('A' as u32))),
1313 ];
1314 let _res: Vec<InputEvent> = events
1315 .into_iter()
1316 .map(|event| handler.clone().handle_unhandled_input_event_internal(event))
1317 .flatten()
1318 .collect();
1319 diagnostics_assertions::assert_data_tree!(inspector, root: {
1320 input_handlers_node: {
1321 dead_keys_handler: {
1322 events_received_count: 2u64,
1323 events_handled_count: 0u64,
1324 last_received_timestamp_ns: 0u64,
1325 "fuchsia.inspect.Health": {
1326 status: "STARTING_UP",
1327 // Timestamp value is unpredictable and not relevant in this context,
1328 // so we only assert that the property is present.
1329 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1330 },
1331 }
1332 }
1333 });
1334 }
1335}