Skip to main content

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}