Skip to main content

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