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