Skip to main content

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