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