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