input_pipeline/
keyboard_binding.rs

1// Copyright 2019 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
5use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::{autorepeater, metrics};
7use anyhow::{format_err, Error, Result};
8use async_trait::async_trait;
9use fidl_fuchsia_input_report::{InputDeviceProxy, InputReport};
10use fidl_fuchsia_ui_input3::KeyEventType;
11use fuchsia_inspect::health::Reporter;
12use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
13use metrics_registry::*;
14use {fidl_fuchsia_ui_input3 as fidl_ui_input3, fuchsia_async as fasync};
15
16/// A [`KeyboardEvent`] represents an input event from a keyboard device.
17///
18/// The keyboard event contains information about a key event.  A key event represents a change in
19/// the key state. Clients can expect the following sequence of events for a given key:
20///
21/// 1. [`KeyEventType::Pressed`]: the key has transitioned to being pressed.
22/// 2. [`KeyEventType::Released`]: the key has transitioned to being released.
23///
24/// No duplicate [`KeyEventType::Pressed`] events will be sent for keys, even if the
25/// key is present in a subsequent [`InputReport`]. Clients can assume that
26/// a key is pressed for all received input events until the key is present in
27/// the [`KeyEventType::Released`] entry of [`keys`].
28///
29/// Use `new` to create.  Use `get_*` methods to read fields.  Use `into_with_*`
30/// methods to add optional information.
31#[derive(Clone, Debug, PartialEq)]
32pub struct KeyboardEvent {
33    /// The key that changed state in this [KeyboardEvent].
34    key: fidl_fuchsia_input::Key,
35
36    /// A description of what happened to `key`.
37    event_type: KeyEventType,
38
39    /// The [`fidl_ui_input3::Modifiers`] associated with the pressed keys.
40    modifiers: Option<fidl_ui_input3::Modifiers>,
41
42    /// The [`fidl_ui_input3::LockState`] currently computed.
43    lock_state: Option<fidl_ui_input3::LockState>,
44
45    /// If set, contains the unique identifier of the keymap to be used when or
46    /// if remapping the keypresses.
47    keymap: Option<String>,
48
49    /// If set, denotes the meaning of `key` in terms of the key effect.
50    /// A `KeyboardEvent` starts off with `key_meaning` unset, and the key
51    /// meaning is added in the input pipeline by the appropriate
52    /// keymap-aware input handlers.
53    key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>,
54
55    /// If this keyboard event has been generated as a result of a repeated
56    /// generation of the same key, then this will be a nonzero. A nonzero
57    /// value N here means that this is Nth generated autorepeat for this
58    /// keyboard event.  The counter is reset for each new autorepeat key
59    /// span.
60    repeat_sequence: u32,
61
62    /// The currently active autorepeater settings.
63    autorepeat_settings: Option<autorepeater::Settings>,
64}
65
66impl KeyboardEvent {
67    /// Creates a new KeyboardEvent, with required fields filled out.  Use the
68    /// `into_with_*` methods to add optional information.
69    pub fn new(key: fidl_fuchsia_input::Key, event_type: KeyEventType) -> Self {
70        KeyboardEvent {
71            key,
72            event_type,
73            modifiers: None,
74            lock_state: None,
75            keymap: None,
76            key_meaning: None,
77            repeat_sequence: 0,
78            autorepeat_settings: Default::default(),
79        }
80    }
81
82    /// Converts [KeyboardEvent] into the same one, but with the specified settings.
83    pub fn into_with_autorepeat_settings(
84        self,
85        autorepeat_settings: Option<autorepeater::Settings>,
86    ) -> Self {
87        Self { autorepeat_settings, ..self }
88    }
89
90    pub fn get_autorepeat_settings(&self) -> autorepeater::Settings {
91        self.autorepeat_settings.unwrap_or(Default::default())
92    }
93
94    pub fn get_key(&self) -> fidl_fuchsia_input::Key {
95        self.key
96    }
97
98    /// Converts [KeyboardEvent] into the same one, but with specified key.
99    pub fn into_with_key(self, key: fidl_fuchsia_input::Key) -> Self {
100        Self { key, ..self }
101    }
102
103    pub fn get_event_type(&self) -> KeyEventType {
104        self.event_type
105    }
106
107    /// Converts [KeyboardEvent] into the same one, but with specified event type.
108    pub fn into_with_event_type(self, event_type: KeyEventType) -> Self {
109        Self { event_type, ..self }
110    }
111
112    /// Folds the key event type into an active event (Pressed, Released).
113    pub fn into_with_folded_event(self) -> Self {
114        Self { event_type: self.get_event_type_folded(), ..self }
115    }
116
117    /// Gets [KeyEventType], folding `SYNC` into `PRESSED` and `CANCEL` into `RELEASED`.
118    pub fn get_event_type_folded(&self) -> KeyEventType {
119        match self.event_type {
120            KeyEventType::Pressed | KeyEventType::Sync => KeyEventType::Pressed,
121            KeyEventType::Released | KeyEventType::Cancel => KeyEventType::Released,
122        }
123    }
124
125    /// Converts [KeyboardEvent] into the same one, but with specified modifiers.
126    pub fn into_with_modifiers(self, modifiers: Option<fidl_ui_input3::Modifiers>) -> Self {
127        Self { modifiers, ..self }
128    }
129
130    /// Returns the currently applicable modifiers.
131    pub fn get_modifiers(&self) -> Option<fidl_ui_input3::Modifiers> {
132        self.modifiers
133    }
134
135    /// Returns the currently applicable modifiers, with the sided modifiers removed.
136    ///
137    /// For example, if LEFT_SHIFT is pressed, returns SHIFT, rather than SHIFT | LEFT_SHIFT
138    pub fn get_unsided_modifiers(&self) -> fidl_fuchsia_ui_input3::Modifiers {
139        use fidl_fuchsia_ui_input3::Modifiers;
140        let mut modifiers = self.modifiers.unwrap_or(Modifiers::empty());
141        modifiers.set(
142            Modifiers::LEFT_ALT
143                | Modifiers::LEFT_CTRL
144                | Modifiers::LEFT_SHIFT
145                | Modifiers::LEFT_META
146                | Modifiers::RIGHT_ALT
147                | Modifiers::RIGHT_CTRL
148                | Modifiers::RIGHT_SHIFT
149                | Modifiers::RIGHT_META,
150            false,
151        );
152        modifiers
153    }
154
155    /// Converts [KeyboardEvent] into the same one, but with the specified lock state.
156    pub fn into_with_lock_state(self, lock_state: Option<fidl_ui_input3::LockState>) -> Self {
157        Self { lock_state, ..self }
158    }
159
160    /// Returns the currently applicable lock state.
161    pub fn get_lock_state(&self) -> Option<fidl_ui_input3::LockState> {
162        self.lock_state
163    }
164
165    /// Converts [KeyboardEvent] into the same one, but with the specified keymap
166    /// applied.
167    pub fn into_with_keymap(self, keymap: Option<String>) -> Self {
168        Self { keymap, ..self }
169    }
170
171    /// Returns the currently applied keymap.
172    pub fn get_keymap(&self) -> Option<String> {
173        self.keymap.clone()
174    }
175
176    /// Converts [KeyboardEvent] into the same one, but with the key meaning applied.
177    pub fn into_with_key_meaning(
178        self,
179        key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>,
180    ) -> Self {
181        Self { key_meaning, ..self }
182    }
183
184    /// Returns the currently valid key meaning.
185    pub fn get_key_meaning(&self) -> Option<fidl_fuchsia_ui_input3::KeyMeaning> {
186        self.key_meaning
187    }
188
189    /// Returns the repeat sequence number.  If a nonzero number N is returned,
190    /// that means this [KeyboardEvent] is the N-th generated autorepeat event.
191    /// A zero means this is an event that came from the keyboard driver.
192    pub fn get_repeat_sequence(&self) -> u32 {
193        self.repeat_sequence
194    }
195
196    /// Converts [KeyboardEvent] into the same one, but with the repeat sequence
197    /// changed.
198    pub fn into_with_repeat_sequence(self, repeat_sequence: u32) -> Self {
199        Self { repeat_sequence, ..self }
200    }
201
202    /// Centralizes the conversion from [KeyboardEvent] to `KeyEvent`.
203    #[cfg(test)]
204    pub(crate) fn from_key_event_at_time(
205        &self,
206        event_time: zx::MonotonicInstant,
207    ) -> fidl_ui_input3::KeyEvent {
208        fidl_ui_input3::KeyEvent {
209            timestamp: Some(event_time.into_nanos()),
210            type_: Some(self.event_type),
211            key: Some(self.key),
212            modifiers: self.modifiers,
213            lock_state: self.lock_state,
214            repeat_sequence: Some(self.repeat_sequence),
215            key_meaning: self.key_meaning,
216            ..Default::default()
217        }
218    }
219}
220
221impl KeyboardEvent {
222    /// Returns true if the two keyboard events are about the same key.
223    pub fn same_key(this: &KeyboardEvent, that: &KeyboardEvent) -> bool {
224        this.get_key() == that.get_key()
225    }
226}
227
228/// A [`KeyboardDeviceDescriptor`] contains information about a specific keyboard device.
229#[derive(Clone, Debug, PartialEq)]
230pub struct KeyboardDeviceDescriptor {
231    /// All the [`fidl_fuchsia_input::Key`]s available on the keyboard device.
232    pub keys: Vec<fidl_fuchsia_input::Key>,
233
234    /// The vendor ID, product ID and version.
235    pub device_information: fidl_fuchsia_input_report::DeviceInformation,
236
237    /// The unique identifier of this device.
238    pub device_id: u32,
239}
240
241#[cfg(test)]
242impl Default for KeyboardDeviceDescriptor {
243    fn default() -> Self {
244        KeyboardDeviceDescriptor {
245            keys: vec![],
246            device_information: fidl_fuchsia_input_report::DeviceInformation {
247                vendor_id: Some(0),
248                product_id: Some(0),
249                version: Some(0),
250                polling_rate: Some(0),
251                ..Default::default()
252            },
253            device_id: 0,
254        }
255    }
256}
257
258/// A [`KeyboardBinding`] represents a connection to a keyboard input device.
259///
260/// The [`KeyboardBinding`] parses and exposes keyboard device descriptor properties (e.g., the
261/// available keyboard keys) for the device it is associated with. It also parses [`InputReport`]s
262/// from the device, and sends them to the device binding owner over `event_sender`.
263pub struct KeyboardBinding {
264    /// The channel to stream InputEvents to.
265    event_sender: UnboundedSender<input_device::InputEvent>,
266
267    /// Holds information about this device.
268    device_descriptor: KeyboardDeviceDescriptor,
269}
270
271#[async_trait]
272impl input_device::InputDeviceBinding for KeyboardBinding {
273    fn input_event_sender(&self) -> UnboundedSender<input_device::InputEvent> {
274        self.event_sender.clone()
275    }
276
277    fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
278        input_device::InputDeviceDescriptor::Keyboard(self.device_descriptor.clone())
279    }
280}
281
282impl KeyboardBinding {
283    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
284    ///
285    /// The binding will start listening for input reports immediately and send new InputEvents
286    /// to the device binding owner over `input_event_sender`.
287    ///
288    /// # Parameters
289    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
290    /// - `device_id`: The unique identifier of this device.
291    /// - `input_event_sender`: The channel to send new InputEvents to.
292    /// - `device_node`: The inspect node for this device binding
293    /// - `metrics_logger`: The metrics logger.
294    ///
295    /// # Errors
296    /// If there was an error binding to the proxy.
297    pub async fn new(
298        device_proxy: InputDeviceProxy,
299        device_id: u32,
300        input_event_sender: UnboundedSender<input_device::InputEvent>,
301        device_node: fuchsia_inspect::Node,
302        metrics_logger: metrics::MetricsLogger,
303    ) -> Result<Self, Error> {
304        let (device_binding, mut inspect_status) = Self::bind_device(
305            &device_proxy,
306            input_event_sender,
307            device_id,
308            device_node,
309            metrics_logger.clone(),
310        )
311        .await?;
312        inspect_status.health_node.set_ok();
313        input_device::initialize_report_stream(
314            device_proxy,
315            device_binding.get_device_descriptor(),
316            device_binding.input_event_sender(),
317            inspect_status,
318            metrics_logger,
319            Self::process_reports,
320        );
321
322        Ok(device_binding)
323    }
324
325    /// Converts a vector of keyboard keys to the appropriate [`fidl_ui_input3::Modifiers`] bitflags.
326    ///
327    /// For example, if `keys` contains `Key::CapsLock`, the bitflags will contain the corresponding
328    /// flags for `CapsLock`.
329    ///
330    /// # Parameters
331    /// - `keys`: The keys to check for modifiers.
332    ///
333    /// # Returns
334    /// Returns `None` if there are no modifier keys present.
335    pub fn to_modifiers(keys: &[&fidl_fuchsia_input::Key]) -> Option<fidl_ui_input3::Modifiers> {
336        let mut modifiers = fidl_ui_input3::Modifiers::empty();
337        for key in keys {
338            let modifier = match key {
339                fidl_fuchsia_input::Key::CapsLock => Some(fidl_ui_input3::Modifiers::CAPS_LOCK),
340                fidl_fuchsia_input::Key::NumLock => Some(fidl_ui_input3::Modifiers::NUM_LOCK),
341                fidl_fuchsia_input::Key::ScrollLock => Some(fidl_ui_input3::Modifiers::SCROLL_LOCK),
342                _ => None,
343            };
344            if let Some(modifier) = modifier {
345                modifiers.insert(modifier);
346            };
347        }
348        if modifiers.is_empty() {
349            return None;
350        }
351        Some(modifiers)
352    }
353
354    /// Binds the provided input device to a new instance of `Self`.
355    ///
356    /// # Parameters
357    /// - `device`: The device to use to initialize the binding.
358    /// - `input_event_sender`: The channel to send new InputEvents to.
359    /// - `device_id`: The device ID being bound.
360    /// - `device_node`: The inspect node for this device binding
361    ///
362    /// # Errors
363    /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
364    /// correctly.
365    async fn bind_device(
366        device: &InputDeviceProxy,
367        input_event_sender: UnboundedSender<input_device::InputEvent>,
368        device_id: u32,
369        device_node: fuchsia_inspect::Node,
370        metrics_logger: metrics::MetricsLogger,
371    ) -> Result<(Self, InputDeviceStatus), Error> {
372        let mut input_device_status = InputDeviceStatus::new(device_node);
373        let descriptor = match device.get_descriptor().await {
374            Ok(descriptor) => descriptor,
375            Err(_) => {
376                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
377                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
378            }
379        };
380
381        let device_info = descriptor.device_information.ok_or_else(|| {
382            input_device_status.health_node.set_unhealthy("Empty device_information in descriptor");
383            // Logging in addition to returning an error, as in some test
384            // setups the error may never be displayed to the user.
385            metrics_logger.log_error(
386                InputPipelineErrorMetricDimensionEvent::KeyboardEmptyDeviceInfo,
387                std::format!("DRIVER BUG: empty device_information for device_id: {}", device_id),
388            );
389            format_err!("empty device info for device_id: {}", device_id)
390        })?;
391        match descriptor.keyboard {
392            Some(fidl_fuchsia_input_report::KeyboardDescriptor {
393                input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor { keys3, .. }),
394                output: _,
395                ..
396            }) => Ok((
397                KeyboardBinding {
398                    event_sender: input_event_sender,
399                    device_descriptor: KeyboardDeviceDescriptor {
400                        keys: keys3.unwrap_or_default(),
401                        device_information: device_info,
402                        device_id,
403                    },
404                },
405                input_device_status,
406            )),
407            device_descriptor => {
408                input_device_status
409                    .health_node
410                    .set_unhealthy("Keyboard Device Descriptor failed to parse.");
411                Err(format_err!(
412                    "Keyboard Device Descriptor failed to parse: \n {:?}",
413                    device_descriptor
414                ))
415            }
416        }
417    }
418
419    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
420    ///
421    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
422    ///
423    /// # Parameters
424    /// `report`: The incoming [`InputReport`].
425    /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be
426    ///                    used to determine, for example, which keys are no longer present in
427    ///                    a keyboard report to generate key released events. If `None`, no
428    ///                    previous report was found.
429    /// `device_descriptor`: The descriptor for the input device generating the input reports.
430    /// `input_event_sender`: The sender for the device binding's input event stream.
431    ///
432    /// # Returns
433    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
434    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
435    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
436    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
437    /// binding does not generate InputEvents asynchronously, this will be `None`.
438    fn process_reports(
439        report: InputReport,
440        previous_report: Option<InputReport>,
441        device_descriptor: &input_device::InputDeviceDescriptor,
442        input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
443        inspect_status: &InputDeviceStatus,
444        metrics_logger: &metrics::MetricsLogger,
445    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
446        fuchsia_trace::duration!(c"input", c"keyboard-binding-process-report");
447        if let Some(trace_id) = report.trace_id {
448            fuchsia_trace::flow_end!(c"input", c"input_report", trace_id.into());
449        }
450
451        let tracing_id = match report.trace_id {
452            Some(id) => id.into(),
453            None => fuchsia_trace::Id::new(),
454        };
455        fuchsia_trace::flow_begin!(c"input", c"key_event_thread", tracing_id);
456
457        inspect_status.count_received_report(&report);
458        // Input devices can have multiple types so ensure `report` is a KeyboardInputReport.
459        match &report.keyboard {
460            None => {
461                inspect_status.count_filtered_report();
462                return (previous_report, None);
463            }
464            _ => (),
465        };
466
467        let new_keys = match KeyboardBinding::parse_pressed_keys(&report) {
468            Some(keys) => keys,
469            None => {
470                // It's OK for the report to contain an empty vector of keys, but it's not OK for
471                // the report to not have the appropriate fields set.
472                //
473                // In this case the report is treated as malformed, and the previous report is not
474                // updated.
475                metrics_logger.log_error(
476                    InputPipelineErrorMetricDimensionEvent::KeyboardFailedToParse,
477                    std::format!("Failed to parse keyboard keys: {:?}", report),
478                );
479                inspect_status.count_filtered_report();
480                return (previous_report, None);
481            }
482        };
483
484        let previous_keys: Vec<fidl_fuchsia_input::Key> = previous_report
485            .as_ref()
486            .and_then(|unwrapped_report| KeyboardBinding::parse_pressed_keys(&unwrapped_report))
487            .unwrap_or_default();
488
489        let (inspect_sender, inspect_receiver) = futures::channel::mpsc::unbounded();
490
491        KeyboardBinding::send_key_events(
492            &new_keys,
493            &previous_keys,
494            device_descriptor.clone(),
495            zx::MonotonicInstant::get(),
496            input_event_sender.clone(),
497            inspect_sender,
498            metrics_logger,
499            tracing_id,
500        );
501
502        (Some(report), Some(inspect_receiver))
503    }
504
505    /// Parses the currently pressed [`fidl_fuchsia_input3::Key`]s from an input report.
506    ///
507    /// # Parameters
508    /// - `input_report`: The input report to parse the keyboard keys from.
509    ///
510    /// # Returns
511    /// Returns `None` if any of the required input report fields are `None`. If all the
512    /// required report fields are present, but there are no pressed keys, an empty vector
513    /// is returned.
514    fn parse_pressed_keys(input_report: &InputReport) -> Option<Vec<fidl_fuchsia_input::Key>> {
515        input_report
516            .keyboard
517            .as_ref()
518            .and_then(|unwrapped_keyboard| unwrapped_keyboard.pressed_keys3.as_ref())
519            .and_then(|unwrapped_keys| Some(unwrapped_keys.iter().cloned().collect()))
520    }
521
522    /// Sends key events to clients based on the new and previously pressed keys.
523    ///
524    /// # Parameters
525    /// - `new_keys`: The input3 keys which are currently pressed, as reported by the bound device.
526    /// - `previous_keys`: The input3 keys which were pressed in the previous input report.
527    /// - `device_descriptor`: The descriptor for the input device generating the input reports.
528    /// - `event_time`: The time in nanoseconds when the event was first recorded.
529    /// - `input_event_sender`: The sender for the device binding's input event stream.
530    fn send_key_events(
531        new_keys: &Vec<fidl_fuchsia_input::Key>,
532        previous_keys: &Vec<fidl_fuchsia_input::Key>,
533        device_descriptor: input_device::InputDeviceDescriptor,
534        event_time: zx::MonotonicInstant,
535        input_event_sender: UnboundedSender<input_device::InputEvent>,
536        inspect_sender: UnboundedSender<input_device::InputEvent>,
537        metrics_logger: &metrics::MetricsLogger,
538        tracing_id: fuchsia_trace::Id,
539    ) {
540        // Dispatches all key events individually in a separate task.  This is done in a separate
541        // function so that the lifetime of `new_keys` above could be detached from that of the
542        // spawned task.
543        fn dispatch_events(
544            key_events: Vec<(fidl_fuchsia_input::Key, fidl_fuchsia_ui_input3::KeyEventType)>,
545            device_descriptor: input_device::InputDeviceDescriptor,
546            event_time: zx::MonotonicInstant,
547            input_event_sender: UnboundedSender<input_device::InputEvent>,
548            inspect_sender: UnboundedSender<input_device::InputEvent>,
549            metrics_logger: metrics::MetricsLogger,
550            tracing_id: fuchsia_trace::Id,
551        ) {
552            fasync::Task::local(async move {
553                fuchsia_trace::duration!(c"input", c"key_event_thread");
554                fuchsia_trace::flow_end!(c"input", c"key_event_thread", tracing_id);
555
556                let mut event_time = event_time;
557                for (key, event_type) in key_events.into_iter() {
558                    let trace_id = fuchsia_trace::Id::new();
559                    fuchsia_trace::duration!(c"input", c"keyboard_event_in_binding");
560                    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
561
562                    let event = input_device::InputEvent {
563                        device_event: input_device::InputDeviceEvent::Keyboard(
564                            KeyboardEvent::new(key, event_type),
565                        ),
566                        device_descriptor: device_descriptor.clone(),
567                        event_time,
568                        handled: Handled::No,
569                        trace_id: Some(trace_id),
570                    };
571                    match input_event_sender.unbounded_send(event.clone()) {
572                        Err(error) => {
573                            metrics_logger.log_error(
574                                InputPipelineErrorMetricDimensionEvent::KeyboardFailedToSendKeyboardEvent,
575                                std::format!(
576                                    "Failed to send KeyboardEvent for key: {:?}, event_type: {:?}: {:?}",
577                                    &key,
578                                    &event_type,
579                                    error));
580                        }
581                        _ => { let _ = inspect_sender.unbounded_send(event).expect("Failed to count generated KeyboardEvent in Input Pipeline Inspect tree."); },
582                    }
583                    // If key events happen to have been reported at the same time,
584                    // we pull them apart artificially. A 1ns increment will likely
585                    // be enough of a difference that it is recognizable but that it
586                    // does not introduce confusion.
587                    event_time = event_time + zx::MonotonicDuration::from_nanos(1);
588                }
589            })
590            .detach();
591        }
592
593        // Filter out the keys which were present in the previous keyboard report to avoid sending
594        // multiple `KeyEventType::Pressed` events for a key.
595        let pressed_keys = new_keys
596            .iter()
597            .cloned()
598            .filter(|key| !previous_keys.contains(key))
599            .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Pressed));
600
601        // Any key which is not present in the new keys, but was present in the previous report
602        // is considered to be released.
603        let released_keys = previous_keys
604            .iter()
605            .cloned()
606            .filter(|key| !new_keys.contains(key))
607            .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Released));
608
609        // It is important that key releases are dispatched before key presses,
610        // so that modifier tracking would work correctly.  We collect the result
611        // into a vector since an iterator is not Send and can not be moved into
612        // a closure.
613        let all_keys = released_keys.chain(pressed_keys).collect::<Vec<_>>();
614
615        dispatch_events(
616            all_keys,
617            device_descriptor,
618            event_time,
619            input_event_sender,
620            inspect_sender,
621            metrics_logger.clone(),
622            tracing_id,
623        );
624    }
625}
626
627#[cfg(test)]
628mod tests {
629    use super::*;
630    use crate::testing_utilities;
631    use fuchsia_async as fasync;
632    use futures::StreamExt;
633
634    /// Tests that a key that is present in the new report, but was not present in the previous report
635    /// is propagated as pressed.
636    #[fasync::run_singlethreaded(test)]
637    async fn pressed_key() {
638        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
639            keys: vec![fidl_fuchsia_input::Key::A],
640            ..Default::default()
641        });
642        let (event_time_i64, _) = testing_utilities::event_times();
643
644        let reports = vec![testing_utilities::create_keyboard_input_report(
645            vec![fidl_fuchsia_input::Key::A],
646            event_time_i64,
647        )];
648        let expected_events = vec![testing_utilities::create_keyboard_event(
649            fidl_fuchsia_input::Key::A,
650            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
651            None,
652            &descriptor,
653            /* keymap= */ None,
654        )];
655
656        assert_input_report_sequence_generates_events!(
657            input_reports: reports,
658            expected_events: expected_events,
659            device_descriptor: descriptor,
660            device_type: KeyboardBinding,
661        );
662    }
663
664    /// Tests that a key that is not present in the new report, but was present in the previous report
665    /// is propagated as released.
666    #[fasync::run_singlethreaded(test)]
667    async fn released_key() {
668        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
669            keys: vec![fidl_fuchsia_input::Key::A],
670            ..Default::default()
671        });
672        let (event_time_i64, _) = testing_utilities::event_times();
673
674        let reports = vec![
675            testing_utilities::create_keyboard_input_report(
676                vec![fidl_fuchsia_input::Key::A],
677                event_time_i64,
678            ),
679            testing_utilities::create_keyboard_input_report(vec![], event_time_i64),
680        ];
681
682        let expected_events = vec![
683            testing_utilities::create_keyboard_event(
684                fidl_fuchsia_input::Key::A,
685                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
686                None,
687                &descriptor,
688                /* keymap= */ None,
689            ),
690            testing_utilities::create_keyboard_event(
691                fidl_fuchsia_input::Key::A,
692                fidl_fuchsia_ui_input3::KeyEventType::Released,
693                None,
694                &descriptor,
695                /* keymap= */ None,
696            ),
697        ];
698
699        assert_input_report_sequence_generates_events!(
700            input_reports: reports,
701            expected_events: expected_events,
702            device_descriptor: descriptor.clone(),
703            device_type: KeyboardBinding,
704        );
705    }
706
707    /// Tests that a key that is present in multiple consecutive input reports is not propagated
708    /// as a pressed event more than once.
709    #[fasync::run_singlethreaded(test)]
710    async fn multiple_pressed_event_filtering() {
711        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
712            keys: vec![fidl_fuchsia_input::Key::A],
713            ..Default::default()
714        });
715        let (event_time_i64, _) = testing_utilities::event_times();
716
717        let reports = vec![
718            testing_utilities::create_keyboard_input_report(
719                vec![fidl_fuchsia_input::Key::A],
720                event_time_i64,
721            ),
722            testing_utilities::create_keyboard_input_report(
723                vec![fidl_fuchsia_input::Key::A],
724                event_time_i64,
725            ),
726        ];
727
728        let expected_events = vec![testing_utilities::create_keyboard_event(
729            fidl_fuchsia_input::Key::A,
730            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
731            None,
732            &descriptor,
733            /* keymap= */ None,
734        )];
735
736        assert_input_report_sequence_generates_events!(
737            input_reports: reports,
738            expected_events: expected_events,
739            device_descriptor: descriptor,
740            device_type: KeyboardBinding,
741        );
742    }
743
744    /// Tests that both pressed and released keys are sent at once.
745    #[fasync::run_singlethreaded(test)]
746    async fn pressed_and_released_keys() {
747        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
748            keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
749            ..Default::default()
750        });
751        let (event_time_i64, _) = testing_utilities::event_times();
752
753        let reports = vec![
754            testing_utilities::create_keyboard_input_report(
755                vec![fidl_fuchsia_input::Key::A],
756                event_time_i64,
757            ),
758            testing_utilities::create_keyboard_input_report(
759                vec![fidl_fuchsia_input::Key::B],
760                event_time_i64,
761            ),
762        ];
763
764        let expected_events = vec![
765            testing_utilities::create_keyboard_event(
766                fidl_fuchsia_input::Key::A,
767                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
768                None,
769                &descriptor,
770                /* keymap= */ None,
771            ),
772            testing_utilities::create_keyboard_event(
773                fidl_fuchsia_input::Key::A,
774                fidl_fuchsia_ui_input3::KeyEventType::Released,
775                None,
776                &descriptor,
777                /* keymap= */ None,
778            ),
779            testing_utilities::create_keyboard_event(
780                fidl_fuchsia_input::Key::B,
781                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
782                None,
783                &descriptor,
784                /* keymap= */ None,
785            ),
786        ];
787
788        assert_input_report_sequence_generates_events!(
789            input_reports: reports,
790            expected_events: expected_events,
791            device_descriptor: descriptor,
792            device_type: KeyboardBinding,
793        );
794    }
795
796    #[fuchsia::test]
797    fn get_unsided_modifiers() {
798        use fidl_ui_input3::Modifiers;
799        let event = KeyboardEvent::new(fidl_fuchsia_input::Key::A, KeyEventType::Pressed)
800            .into_with_modifiers(Some(Modifiers::all()));
801        assert_eq!(
802            event.get_unsided_modifiers(),
803            Modifiers::CAPS_LOCK
804                | Modifiers::NUM_LOCK
805                | Modifiers::SCROLL_LOCK
806                | Modifiers::FUNCTION
807                | Modifiers::SYMBOL
808                | Modifiers::SHIFT
809                | Modifiers::ALT
810                | Modifiers::ALT_GRAPH
811                | Modifiers::META
812                | Modifiers::CTRL
813        )
814    }
815
816    #[fuchsia::test]
817    fn conversion_fills_out_all_fields() {
818        use fidl_fuchsia_input::Key;
819        use fidl_ui_input3::{KeyMeaning, LockState, Modifiers, NonPrintableKey};
820        let event = KeyboardEvent::new(Key::A, KeyEventType::Pressed)
821            .into_with_modifiers(Some(Modifiers::all()))
822            .into_with_lock_state(Some(LockState::all()))
823            .into_with_repeat_sequence(42)
824            .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)));
825
826        let actual = event.from_key_event_at_time(zx::MonotonicInstant::from_nanos(42));
827        assert_eq!(
828            actual,
829            fidl_fuchsia_ui_input3::KeyEvent {
830                timestamp: Some(42),
831                type_: Some(KeyEventType::Pressed),
832                key: Some(Key::A),
833                modifiers: Some(Modifiers::all()),
834                key_meaning: Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)),
835                repeat_sequence: Some(42),
836                lock_state: Some(LockState::all()),
837                ..Default::default()
838            }
839        );
840    }
841}