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