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 = fuchsia_trace::Id::random();
452        fuchsia_trace::flow_begin!(c"input", c"key_event_thread", tracing_id);
453
454        inspect_status.count_received_report(&report);
455        // Input devices can have multiple types so ensure `report` is a KeyboardInputReport.
456        match &report.keyboard {
457            None => {
458                inspect_status.count_filtered_report();
459                return (previous_report, None);
460            }
461            _ => (),
462        };
463
464        let new_keys = match KeyboardBinding::parse_pressed_keys(&report) {
465            Some(keys) => keys,
466            None => {
467                // It's OK for the report to contain an empty vector of keys, but it's not OK for
468                // the report to not have the appropriate fields set.
469                //
470                // In this case the report is treated as malformed, and the previous report is not
471                // updated.
472                metrics_logger.log_error(
473                    InputPipelineErrorMetricDimensionEvent::KeyboardFailedToParse,
474                    std::format!("Failed to parse keyboard keys: {:?}", report),
475                );
476                inspect_status.count_filtered_report();
477                return (previous_report, None);
478            }
479        };
480
481        let previous_keys: Vec<fidl_fuchsia_input::Key> = previous_report
482            .as_ref()
483            .and_then(|unwrapped_report| KeyboardBinding::parse_pressed_keys(&unwrapped_report))
484            .unwrap_or_default();
485
486        let (inspect_sender, inspect_receiver) = futures::channel::mpsc::unbounded();
487
488        KeyboardBinding::send_key_events(
489            &new_keys,
490            &previous_keys,
491            device_descriptor.clone(),
492            zx::MonotonicInstant::get(),
493            input_event_sender.clone(),
494            inspect_sender,
495            metrics_logger,
496            tracing_id,
497        );
498
499        (Some(report), Some(inspect_receiver))
500    }
501
502    /// Parses the currently pressed [`fidl_fuchsia_input3::Key`]s from an input report.
503    ///
504    /// # Parameters
505    /// - `input_report`: The input report to parse the keyboard keys from.
506    ///
507    /// # Returns
508    /// Returns `None` if any of the required input report fields are `None`. If all the
509    /// required report fields are present, but there are no pressed keys, an empty vector
510    /// is returned.
511    fn parse_pressed_keys(input_report: &InputReport) -> Option<Vec<fidl_fuchsia_input::Key>> {
512        input_report
513            .keyboard
514            .as_ref()
515            .and_then(|unwrapped_keyboard| unwrapped_keyboard.pressed_keys3.as_ref())
516            .and_then(|unwrapped_keys| Some(unwrapped_keys.iter().cloned().collect()))
517    }
518
519    /// Sends key events to clients based on the new and previously pressed keys.
520    ///
521    /// # Parameters
522    /// - `new_keys`: The input3 keys which are currently pressed, as reported by the bound device.
523    /// - `previous_keys`: The input3 keys which were pressed in the previous input report.
524    /// - `device_descriptor`: The descriptor for the input device generating the input reports.
525    /// - `event_time`: The time in nanoseconds when the event was first recorded.
526    /// - `input_event_sender`: The sender for the device binding's input event stream.
527    fn send_key_events(
528        new_keys: &Vec<fidl_fuchsia_input::Key>,
529        previous_keys: &Vec<fidl_fuchsia_input::Key>,
530        device_descriptor: input_device::InputDeviceDescriptor,
531        event_time: zx::MonotonicInstant,
532        input_event_sender: UnboundedSender<input_device::InputEvent>,
533        inspect_sender: UnboundedSender<input_device::InputEvent>,
534        metrics_logger: &metrics::MetricsLogger,
535        tracing_id: fuchsia_trace::Id,
536    ) {
537        // Dispatches all key events individually in a separate task.  This is done in a separate
538        // function so that the lifetime of `new_keys` above could be detached from that of the
539        // spawned task.
540        fn dispatch_events(
541            key_events: Vec<(fidl_fuchsia_input::Key, fidl_fuchsia_ui_input3::KeyEventType)>,
542            device_descriptor: input_device::InputDeviceDescriptor,
543            event_time: zx::MonotonicInstant,
544            input_event_sender: UnboundedSender<input_device::InputEvent>,
545            inspect_sender: UnboundedSender<input_device::InputEvent>,
546            metrics_logger: metrics::MetricsLogger,
547            tracing_id: fuchsia_trace::Id,
548        ) {
549            fasync::Task::local(async move {
550                fuchsia_trace::duration!(c"input", c"key_event_thread");
551                fuchsia_trace::flow_end!(c"input", c"key_event_thread", tracing_id);
552
553                let mut event_time = event_time;
554                for (key, event_type) in key_events.into_iter() {
555                    let trace_id = fuchsia_trace::Id::random();
556                    fuchsia_trace::duration!(c"input", c"keyboard_event_in_binding");
557                    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
558
559                    let event = input_device::InputEvent {
560                        device_event: input_device::InputDeviceEvent::Keyboard(
561                            KeyboardEvent::new(key, event_type),
562                        ),
563                        device_descriptor: device_descriptor.clone(),
564                        event_time,
565                        handled: Handled::No,
566                        trace_id: Some(trace_id),
567                    };
568                    match input_event_sender.unbounded_send(event.clone()) {
569                        Err(error) => {
570                            metrics_logger.log_error(
571                                InputPipelineErrorMetricDimensionEvent::KeyboardFailedToSendKeyboardEvent,
572                                std::format!(
573                                    "Failed to send KeyboardEvent for key: {:?}, event_type: {:?}: {:?}",
574                                    &key,
575                                    &event_type,
576                                    error));
577                        }
578                        _ => { let _ = inspect_sender.unbounded_send(event).expect("Failed to count generated KeyboardEvent in Input Pipeline Inspect tree."); },
579                    }
580                    // If key events happen to have been reported at the same time,
581                    // we pull them apart artificially. A 1ns increment will likely
582                    // be enough of a difference that it is recognizable but that it
583                    // does not introduce confusion.
584                    event_time = event_time + zx::MonotonicDuration::from_nanos(1);
585                }
586            })
587            .detach();
588        }
589
590        // Filter out the keys which were present in the previous keyboard report to avoid sending
591        // multiple `KeyEventType::Pressed` events for a key.
592        let pressed_keys = new_keys
593            .iter()
594            .cloned()
595            .filter(|key| !previous_keys.contains(key))
596            .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Pressed));
597
598        // Any key which is not present in the new keys, but was present in the previous report
599        // is considered to be released.
600        let released_keys = previous_keys
601            .iter()
602            .cloned()
603            .filter(|key| !new_keys.contains(key))
604            .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Released));
605
606        // It is important that key releases are dispatched before key presses,
607        // so that modifier tracking would work correctly.  We collect the result
608        // into a vector since an iterator is not Send and can not be moved into
609        // a closure.
610        let all_keys = released_keys.chain(pressed_keys).collect::<Vec<_>>();
611
612        dispatch_events(
613            all_keys,
614            device_descriptor,
615            event_time,
616            input_event_sender,
617            inspect_sender,
618            metrics_logger.clone(),
619            tracing_id,
620        );
621    }
622}
623
624#[cfg(test)]
625mod tests {
626    use super::*;
627    use crate::testing_utilities;
628    use fuchsia_async as fasync;
629    use futures::StreamExt;
630
631    /// Tests that a key that is present in the new report, but was not present in the previous report
632    /// is propagated as pressed.
633    #[fasync::run_singlethreaded(test)]
634    async fn pressed_key() {
635        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
636            keys: vec![fidl_fuchsia_input::Key::A],
637            ..Default::default()
638        });
639        let (event_time_i64, _) = testing_utilities::event_times();
640
641        let reports = vec![testing_utilities::create_keyboard_input_report(
642            vec![fidl_fuchsia_input::Key::A],
643            event_time_i64,
644        )];
645        let expected_events = vec![testing_utilities::create_keyboard_event(
646            fidl_fuchsia_input::Key::A,
647            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
648            None,
649            &descriptor,
650            /* keymap= */ None,
651        )];
652
653        assert_input_report_sequence_generates_events!(
654            input_reports: reports,
655            expected_events: expected_events,
656            device_descriptor: descriptor,
657            device_type: KeyboardBinding,
658        );
659    }
660
661    /// Tests that a key that is not present in the new report, but was present in the previous report
662    /// is propagated as released.
663    #[fasync::run_singlethreaded(test)]
664    async fn released_key() {
665        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
666            keys: vec![fidl_fuchsia_input::Key::A],
667            ..Default::default()
668        });
669        let (event_time_i64, _) = testing_utilities::event_times();
670
671        let reports = vec![
672            testing_utilities::create_keyboard_input_report(
673                vec![fidl_fuchsia_input::Key::A],
674                event_time_i64,
675            ),
676            testing_utilities::create_keyboard_input_report(vec![], event_time_i64),
677        ];
678
679        let expected_events = vec![
680            testing_utilities::create_keyboard_event(
681                fidl_fuchsia_input::Key::A,
682                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
683                None,
684                &descriptor,
685                /* keymap= */ None,
686            ),
687            testing_utilities::create_keyboard_event(
688                fidl_fuchsia_input::Key::A,
689                fidl_fuchsia_ui_input3::KeyEventType::Released,
690                None,
691                &descriptor,
692                /* keymap= */ None,
693            ),
694        ];
695
696        assert_input_report_sequence_generates_events!(
697            input_reports: reports,
698            expected_events: expected_events,
699            device_descriptor: descriptor.clone(),
700            device_type: KeyboardBinding,
701        );
702    }
703
704    /// Tests that a key that is present in multiple consecutive input reports is not propagated
705    /// as a pressed event more than once.
706    #[fasync::run_singlethreaded(test)]
707    async fn multiple_pressed_event_filtering() {
708        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
709            keys: vec![fidl_fuchsia_input::Key::A],
710            ..Default::default()
711        });
712        let (event_time_i64, _) = testing_utilities::event_times();
713
714        let reports = vec![
715            testing_utilities::create_keyboard_input_report(
716                vec![fidl_fuchsia_input::Key::A],
717                event_time_i64,
718            ),
719            testing_utilities::create_keyboard_input_report(
720                vec![fidl_fuchsia_input::Key::A],
721                event_time_i64,
722            ),
723        ];
724
725        let expected_events = vec![testing_utilities::create_keyboard_event(
726            fidl_fuchsia_input::Key::A,
727            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
728            None,
729            &descriptor,
730            /* keymap= */ None,
731        )];
732
733        assert_input_report_sequence_generates_events!(
734            input_reports: reports,
735            expected_events: expected_events,
736            device_descriptor: descriptor,
737            device_type: KeyboardBinding,
738        );
739    }
740
741    /// Tests that both pressed and released keys are sent at once.
742    #[fasync::run_singlethreaded(test)]
743    async fn pressed_and_released_keys() {
744        let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
745            keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
746            ..Default::default()
747        });
748        let (event_time_i64, _) = testing_utilities::event_times();
749
750        let reports = vec![
751            testing_utilities::create_keyboard_input_report(
752                vec![fidl_fuchsia_input::Key::A],
753                event_time_i64,
754            ),
755            testing_utilities::create_keyboard_input_report(
756                vec![fidl_fuchsia_input::Key::B],
757                event_time_i64,
758            ),
759        ];
760
761        let expected_events = vec![
762            testing_utilities::create_keyboard_event(
763                fidl_fuchsia_input::Key::A,
764                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
765                None,
766                &descriptor,
767                /* keymap= */ None,
768            ),
769            testing_utilities::create_keyboard_event(
770                fidl_fuchsia_input::Key::A,
771                fidl_fuchsia_ui_input3::KeyEventType::Released,
772                None,
773                &descriptor,
774                /* keymap= */ None,
775            ),
776            testing_utilities::create_keyboard_event(
777                fidl_fuchsia_input::Key::B,
778                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
779                None,
780                &descriptor,
781                /* keymap= */ None,
782            ),
783        ];
784
785        assert_input_report_sequence_generates_events!(
786            input_reports: reports,
787            expected_events: expected_events,
788            device_descriptor: descriptor,
789            device_type: KeyboardBinding,
790        );
791    }
792
793    #[fuchsia::test]
794    fn get_unsided_modifiers() {
795        use fidl_ui_input3::Modifiers;
796        let event = KeyboardEvent::new(fidl_fuchsia_input::Key::A, KeyEventType::Pressed)
797            .into_with_modifiers(Some(Modifiers::all()));
798        assert_eq!(
799            event.get_unsided_modifiers(),
800            Modifiers::CAPS_LOCK
801                | Modifiers::NUM_LOCK
802                | Modifiers::SCROLL_LOCK
803                | Modifiers::FUNCTION
804                | Modifiers::SYMBOL
805                | Modifiers::SHIFT
806                | Modifiers::ALT
807                | Modifiers::ALT_GRAPH
808                | Modifiers::META
809                | Modifiers::CTRL
810        )
811    }
812
813    #[fuchsia::test]
814    fn conversion_fills_out_all_fields() {
815        use fidl_fuchsia_input::Key;
816        use fidl_ui_input3::{KeyMeaning, LockState, Modifiers, NonPrintableKey};
817        let event = KeyboardEvent::new(Key::A, KeyEventType::Pressed)
818            .into_with_modifiers(Some(Modifiers::all()))
819            .into_with_lock_state(Some(LockState::all()))
820            .into_with_repeat_sequence(42)
821            .into_with_key_meaning(Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)));
822
823        let actual = event.from_key_event_at_time(zx::MonotonicInstant::from_nanos(42));
824        assert_eq!(
825            actual,
826            fidl_fuchsia_ui_input3::KeyEvent {
827                timestamp: Some(42),
828                type_: Some(KeyEventType::Pressed),
829                key: Some(Key::A),
830                modifiers: Some(Modifiers::all()),
831                key_meaning: Some(KeyMeaning::NonPrintableKey(NonPrintableKey::Tab)),
832                repeat_sequence: Some(42),
833                lock_state: Some(LockState::all()),
834                ..Default::default()
835            }
836        );
837    }
838}