| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::autorepeater, |
| crate::input_device::{self, Handled, InputDeviceBinding}, |
| anyhow::{format_err, Error, Result}, |
| async_trait::async_trait, |
| fidl_fuchsia_input, |
| fidl_fuchsia_input_report::{InputDeviceProxy, InputReport}, |
| fidl_fuchsia_ui_input3 as fidl_ui_input3, |
| fidl_fuchsia_ui_input3::KeyEventType, |
| fidl_fuchsia_ui_input_config::FeaturesRequest as InputConfigFeaturesRequest, |
| fuchsia_async as fasync, |
| fuchsia_syslog::fx_log_err, |
| fuchsia_zircon as zx, |
| futures::{channel::mpsc::Sender, SinkExt}, |
| }; |
| |
| /// A [`KeyboardEvent`] represents an input event from a keyboard device. |
| /// |
| /// The keyboard event contains information about a key event. A key event represents a change in |
| /// the key state. Clients can expect the following sequence of events for a given key: |
| /// |
| /// 1. [`KeyEventType::Pressed`]: the key has transitioned to being pressed. |
| /// 2. [`KeyEventType::Released`]: the key has transitioned to being released. |
| /// |
| /// No duplicate [`KeyEventType::Pressed`] events will be sent for keys, even if the |
| /// key is present in a subsequent [`InputReport`]. Clients can assume that |
| /// a key is pressed for all received input events until the key is present in |
| /// the [`KeyEventType::Released`] entry of [`keys`]. |
| /// |
| /// Use `new` to create. Use `get_*` methods to read fields. Use `into_with_*` |
| /// methods to add optional information. |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct KeyboardEvent { |
| /// The key that changed state in this [KeyboardEvent]. |
| key: fidl_fuchsia_input::Key, |
| |
| /// A description of what happened to `key`. |
| event_type: KeyEventType, |
| |
| /// The [`fidl_ui_input3::Modifiers`] associated with the pressed keys. |
| modifiers: Option<fidl_ui_input3::Modifiers>, |
| |
| /// The [`fidl_ui_input3::LockState`] currently computed. |
| lock_state: Option<fidl_ui_input3::LockState>, |
| |
| /// If set, contains the unique identifier of the keymap to be used when or |
| /// if remapping the keypresses. |
| keymap: Option<String>, |
| |
| /// If set, denotes the meaning of `key` in terms of the key effect. |
| /// A `KeyboardEvent` starts off with `key_meaning` unset, and the key |
| /// meaning is added in the input pipeline by the appropriate |
| /// keymap-aware input handlers. |
| key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>, |
| |
| /// If this keyboard event has been generated as a result of a repeated |
| /// generation of the same key, then this will be a nonzero. A nonzero |
| /// value N here means that this is Nth generated autorepeat for this |
| /// keyboard event. The counter is reset for each new autorepeat key |
| /// span. |
| repeat_sequence: u32, |
| |
| /// The currently active autorepeater settings. |
| autorepeat_settings: Option<autorepeater::Settings>, |
| } |
| |
| impl KeyboardEvent { |
| /// Creates a new KeyboardEvent, with required fields filled out. Use the |
| /// `into_with_*` methods to add optional information. |
| pub fn new(key: fidl_fuchsia_input::Key, event_type: KeyEventType) -> Self { |
| KeyboardEvent { |
| key, |
| event_type, |
| modifiers: None, |
| lock_state: None, |
| keymap: None, |
| key_meaning: None, |
| repeat_sequence: 0, |
| autorepeat_settings: Default::default(), |
| } |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with the specified settings. |
| pub fn into_with_autorepeat_settings( |
| self, |
| autorepeat_settings: Option<autorepeater::Settings>, |
| ) -> Self { |
| Self { autorepeat_settings, ..self } |
| } |
| |
| pub fn get_autorepeat_settings(&self) -> autorepeater::Settings { |
| self.autorepeat_settings.unwrap_or(Default::default()) |
| } |
| |
| pub fn get_key(&self) -> fidl_fuchsia_input::Key { |
| self.key |
| } |
| |
| pub fn get_event_type(&self) -> KeyEventType { |
| self.event_type |
| } |
| |
| /// Folds the key event type into an active event (Pressed, Released). |
| pub fn into_with_folded_event(self) -> Self { |
| Self { event_type: self.get_event_type_folded(), ..self } |
| } |
| |
| /// Gets [KeyEventType], folding `SYNC` into `PRESSED` and `CANCEL` into `RELEASED`. |
| pub fn get_event_type_folded(&self) -> KeyEventType { |
| match self.event_type { |
| KeyEventType::Pressed | KeyEventType::Sync => KeyEventType::Pressed, |
| KeyEventType::Released | KeyEventType::Cancel => KeyEventType::Released, |
| } |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with specified modifiers. |
| pub fn into_with_modifiers(self, modifiers: Option<fidl_ui_input3::Modifiers>) -> Self { |
| Self { modifiers, ..self } |
| } |
| |
| /// Returns the currently applicable modifiers. |
| pub fn get_modifiers(&self) -> Option<fidl_ui_input3::Modifiers> { |
| self.modifiers |
| } |
| |
| /// Returns the currently applicable modifiers, with the sided modifiers removed. |
| /// |
| /// For example, if LEFT_SHIFT is pressed, returns SHIFT, rather than SHIFT | LEFT_SHIFT |
| pub fn get_unsided_modifiers(&self) -> fidl_fuchsia_ui_input3::Modifiers { |
| use fidl_fuchsia_ui_input3::Modifiers; |
| let mut modifiers = self.modifiers.unwrap_or(Modifiers::empty()); |
| modifiers.set( |
| Modifiers::LEFT_ALT |
| | Modifiers::LEFT_CTRL |
| | Modifiers::LEFT_SHIFT |
| | Modifiers::LEFT_META |
| | Modifiers::RIGHT_ALT |
| | Modifiers::RIGHT_CTRL |
| | Modifiers::RIGHT_SHIFT |
| | Modifiers::RIGHT_META, |
| false, |
| ); |
| modifiers |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with the specified lock state. |
| pub fn into_with_lock_state(self, lock_state: Option<fidl_ui_input3::LockState>) -> Self { |
| Self { lock_state, ..self } |
| } |
| |
| /// Returns the currently applicable lock state. |
| pub fn get_lock_state(&self) -> Option<fidl_ui_input3::LockState> { |
| self.lock_state |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with the specified keymap |
| /// applied. |
| pub fn into_with_keymap(self, keymap: Option<String>) -> Self { |
| Self { keymap, ..self } |
| } |
| |
| /// Returns the currently applied keymap. |
| pub fn get_keymap(&self) -> Option<String> { |
| self.keymap.clone() |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with the key meaning applied. |
| pub fn into_with_key_meaning( |
| self, |
| key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>, |
| ) -> Self { |
| Self { key_meaning, ..self } |
| } |
| |
| /// Returns the currently valid key meaning. |
| pub fn get_key_meaning(&self) -> Option<fidl_fuchsia_ui_input3::KeyMeaning> { |
| self.key_meaning |
| } |
| |
| /// Returns the repeat sequence number. If a nonzero number N is returned, |
| /// that means this [KeyboardEvent] is the N-th generated autorepeat event. |
| /// A zero means this is an event that came from the keyboard driver. |
| pub fn get_repeat_sequence(&self) -> u32 { |
| self.repeat_sequence |
| } |
| |
| /// Converts [KeyboardEvent] into the same one, but with the repeat sequence |
| /// changed. |
| pub fn into_with_repeat_sequence(self, repeat_sequence: u32) -> Self { |
| Self { repeat_sequence, ..self } |
| } |
| } |
| |
| impl KeyboardEvent { |
| /// Returns true if the two keyboard events are about the same key. |
| pub fn same_key(this: &KeyboardEvent, that: &KeyboardEvent) -> bool { |
| this.get_key() == that.get_key() |
| } |
| } |
| |
| /// A [`KeyboardDeviceDescriptor`] contains information about a specific keyboard device. |
| #[derive(Clone, Debug, Eq, PartialEq)] |
| pub struct KeyboardDeviceDescriptor { |
| /// All the [`fidl_fuchsia_input::Key`]s available on the keyboard device. |
| pub keys: Vec<fidl_fuchsia_input::Key>, |
| } |
| |
| /// A [`KeyboardBinding`] represents a connection to a keyboard input device. |
| /// |
| /// The [`KeyboardBinding`] parses and exposes keyboard device descriptor properties (e.g., the |
| /// available keyboard keys) for the device it is associated with. It also parses [`InputReport`]s |
| /// from the device, and sends them to the device binding owner over `event_sender`. |
| pub struct KeyboardBinding { |
| /// The channel to stream InputEvents to. |
| event_sender: Sender<input_device::InputEvent>, |
| |
| /// Holds information about this device. |
| device_descriptor: KeyboardDeviceDescriptor, |
| } |
| |
| #[async_trait] |
| impl input_device::InputDeviceBinding for KeyboardBinding { |
| fn input_event_sender(&self) -> Sender<input_device::InputEvent> { |
| self.event_sender.clone() |
| } |
| |
| fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor { |
| input_device::InputDeviceDescriptor::Keyboard(self.device_descriptor.clone()) |
| } |
| |
| async fn handle_input_config_request( |
| &self, |
| _request: &InputConfigFeaturesRequest, |
| ) -> Result<(), Error> { |
| Ok(()) |
| } |
| } |
| |
| impl KeyboardBinding { |
| /// Creates a new [`InputDeviceBinding`] from the `device_proxy`. |
| /// |
| /// The binding will start listening for input reports immediately and send new InputEvents |
| /// to the device binding owner over `input_event_sender`. |
| /// |
| /// # Parameters |
| /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to. |
| /// - `input_event_sender`: The channel to send new InputEvents to. |
| /// |
| /// # Errors |
| /// If there was an error binding to the proxy. |
| pub async fn new( |
| device_proxy: InputDeviceProxy, |
| input_event_sender: Sender<input_device::InputEvent>, |
| ) -> Result<Self, Error> { |
| let device_binding = Self::bind_device(&device_proxy, input_event_sender).await?; |
| input_device::initialize_report_stream( |
| device_proxy, |
| device_binding.get_device_descriptor(), |
| device_binding.input_event_sender(), |
| Self::process_reports, |
| ); |
| |
| Ok(device_binding) |
| } |
| |
| /// Converts a vector of keyboard keys to the appropriate [`fidl_ui_input3::Modifiers`] bitflags. |
| /// |
| /// For example, if `keys` contains `Key::CapsLock`, the bitflags will contain the corresponding |
| /// flags for `CapsLock`. |
| /// |
| /// # Parameters |
| /// - `keys`: The keys to check for modifiers. |
| /// |
| /// # Returns |
| /// Returns `None` if there are no modifier keys present. |
| pub fn to_modifiers(keys: &[&fidl_fuchsia_input::Key]) -> Option<fidl_ui_input3::Modifiers> { |
| let mut modifiers = fidl_ui_input3::Modifiers::empty(); |
| for key in keys { |
| let modifier = match key { |
| fidl_fuchsia_input::Key::CapsLock => Some(fidl_ui_input3::Modifiers::CAPS_LOCK), |
| fidl_fuchsia_input::Key::NumLock => Some(fidl_ui_input3::Modifiers::NUM_LOCK), |
| fidl_fuchsia_input::Key::ScrollLock => Some(fidl_ui_input3::Modifiers::SCROLL_LOCK), |
| _ => None, |
| }; |
| if let Some(modifier) = modifier { |
| modifiers.insert(modifier); |
| }; |
| } |
| if modifiers.is_empty() { |
| return None; |
| } |
| Some(modifiers) |
| } |
| |
| /// Binds the provided input device to a new instance of `Self`. |
| /// |
| /// # Parameters |
| /// - `device`: The device to use to initialize the binding. |
| /// - `input_event_sender`: The channel to send new InputEvents to. |
| /// |
| /// # Errors |
| /// If the device descriptor could not be retrieved, or the descriptor could not be parsed |
| /// correctly. |
| async fn bind_device( |
| device: &InputDeviceProxy, |
| input_event_sender: Sender<input_device::InputEvent>, |
| ) -> Result<Self, Error> { |
| match device.get_descriptor().await?.keyboard { |
| Some(fidl_fuchsia_input_report::KeyboardDescriptor { |
| input: Some(fidl_fuchsia_input_report::KeyboardInputDescriptor { keys3, .. }), |
| output: _, |
| .. |
| }) => Ok(KeyboardBinding { |
| event_sender: input_event_sender, |
| device_descriptor: KeyboardDeviceDescriptor { keys: keys3.unwrap_or_default() }, |
| }), |
| device_descriptor => Err(format_err!( |
| "Keyboard Device Descriptor failed to parse: \n {:?}", |
| device_descriptor |
| )), |
| } |
| } |
| |
| /// Parses an [`InputReport`] into one or more [`InputEvent`]s. |
| /// |
| /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`]. |
| /// |
| /// # Parameters |
| /// `report`: The incoming [`InputReport`]. |
| /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be |
| /// used to determine, for example, which keys are no longer present in |
| /// a keyboard report to generate key released events. If `None`, no |
| /// previous report was found. |
| /// `device_descriptor`: The descriptor for the input device generating the input reports. |
| /// `input_event_sender`: The sender for the device binding's input event stream. |
| /// |
| /// # Returns |
| /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as |
| /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`. |
| fn process_reports( |
| report: InputReport, |
| previous_report: Option<InputReport>, |
| device_descriptor: &input_device::InputDeviceDescriptor, |
| input_event_sender: &mut Sender<input_device::InputEvent>, |
| ) -> Option<InputReport> { |
| // Input devices can have multiple types so ensure `report` is a KeyboardInputReport. |
| match &report.keyboard { |
| None => return previous_report, |
| _ => (), |
| }; |
| |
| let new_keys = match KeyboardBinding::parse_pressed_keys(&report) { |
| Some(keys) => keys, |
| None => { |
| // It's OK for the report to contain an empty vector of keys, but it's not OK for |
| // the report to not have the appropriate fields set. |
| // |
| // In this case the report is treated as malformed, and the previous report is not |
| // updated. |
| fx_log_err!("Failed to parse keyboard keys: {:?}", report); |
| return previous_report; |
| } |
| }; |
| |
| let previous_keys: Vec<fidl_fuchsia_input::Key> = previous_report |
| .as_ref() |
| .and_then(|unwrapped_report| KeyboardBinding::parse_pressed_keys(&unwrapped_report)) |
| .unwrap_or_default(); |
| |
| let event_time: zx::Time = input_device::event_time_or_now(report.event_time); |
| |
| KeyboardBinding::send_key_events( |
| &new_keys, |
| &previous_keys, |
| device_descriptor.clone(), |
| event_time, |
| input_event_sender.clone(), |
| ); |
| |
| Some(report) |
| } |
| |
| /// Parses the currently pressed [`fidl_fuchsia_input3::Key`]s from an input report. |
| /// |
| /// # Parameters |
| /// - `input_report`: The input report to parse the keyboard keys from. |
| /// |
| /// # Returns |
| /// Returns `None` if any of the required input report fields are `None`. If all the |
| /// required report fields are present, but there are no pressed keys, an empty vector |
| /// is returned. |
| fn parse_pressed_keys(input_report: &InputReport) -> Option<Vec<fidl_fuchsia_input::Key>> { |
| input_report |
| .keyboard |
| .as_ref() |
| .and_then(|unwrapped_keyboard| unwrapped_keyboard.pressed_keys3.as_ref()) |
| .and_then(|unwrapped_keys| Some(unwrapped_keys.iter().cloned().collect())) |
| } |
| |
| /// Sends key events to clients based on the new and previously pressed keys. |
| /// |
| /// # Parameters |
| /// - `new_keys`: The input3 keys which are currently pressed, as reported by the bound device. |
| /// - `previous_keys`: The input3 keys which were pressed in the previous input report. |
| /// - `device_descriptor`: The descriptor for the input device generating the input reports. |
| /// - `event_time`: The time in nanoseconds when the event was first recorded. |
| /// - `input_event_sender`: The sender for the device binding's input event stream. |
| fn send_key_events( |
| new_keys: &Vec<fidl_fuchsia_input::Key>, |
| previous_keys: &Vec<fidl_fuchsia_input::Key>, |
| device_descriptor: input_device::InputDeviceDescriptor, |
| event_time: zx::Time, |
| input_event_sender: Sender<input_device::InputEvent>, |
| ) { |
| // Dispatches all key events individually in a separate task. This is done in a separate |
| // function so that the lifetime of `new_keys` above could be detached from that of the |
| // spawned task. |
| fn dispatch_events( |
| key_events: Vec<(fidl_fuchsia_input::Key, fidl_fuchsia_ui_input3::KeyEventType)>, |
| device_descriptor: input_device::InputDeviceDescriptor, |
| event_time: zx::Time, |
| mut input_event_sender: Sender<input_device::InputEvent>, |
| ) { |
| fasync::Task::local(async move { |
| let mut event_time = event_time; |
| for (key, event_type) in key_events.into_iter() { |
| match input_event_sender |
| .send(input_device::InputEvent { |
| device_event: input_device::InputDeviceEvent::Keyboard( |
| KeyboardEvent::new(key, event_type), |
| ), |
| device_descriptor: device_descriptor.clone(), |
| event_time, |
| handled: Handled::No, |
| trace_id: None, |
| }) |
| .await |
| { |
| Err(error) => { |
| fx_log_err!( |
| "Failed to send KeyboardEvent for key: {:?}, event_type: {:?}: {:?}", |
| &key, |
| &event_type, |
| error |
| ); |
| } |
| _ => (), |
| } |
| // If key events happen to have been reported at the same time, |
| // we pull them apart artificially. A 1ns increment will likely |
| // be enough of a difference that it is recognizable but that it |
| // does not introduce confusion. |
| event_time = event_time + zx::Duration::from_nanos(1); |
| } |
| }) |
| .detach(); |
| } |
| |
| // Filter out the keys which were present in the previous keyboard report to avoid sending |
| // multiple `KeyEventType::Pressed` events for a key. |
| let pressed_keys = new_keys |
| .iter() |
| .cloned() |
| .filter(|key| !previous_keys.contains(key)) |
| .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Pressed)); |
| |
| // Any key which is not present in the new keys, but was present in the previous report |
| // is considered to be released. |
| let released_keys = previous_keys |
| .iter() |
| .cloned() |
| .filter(|key| !new_keys.contains(key)) |
| .map(|k| (k, fidl_fuchsia_ui_input3::KeyEventType::Released)); |
| |
| // It is important that key releases are dispatched before key presses, |
| // so that modifier tracking would work correctly. We collect the result |
| // into a vector since an iterator is not Send and can not be moved into |
| // a closure. |
| let all_keys = released_keys.chain(pressed_keys).collect::<Vec<_>>(); |
| |
| dispatch_events(all_keys, device_descriptor, event_time, input_event_sender); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, crate::testing_utilities, fuchsia_async as fasync, fuchsia_zircon as zx, |
| futures::StreamExt, |
| }; |
| |
| /// Tests that a key that is present in the new report, but was not present in the previous report |
| /// is propagated as pressed. |
| #[fasync::run_singlethreaded(test)] |
| async fn pressed_key() { |
| let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor { |
| keys: vec![fidl_fuchsia_input::Key::A], |
| }); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| |
| let reports = vec![testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::A], |
| event_time_i64, |
| )]; |
| let expected_events = vec![testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &descriptor, |
| /* keymap= */ None, |
| )]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: KeyboardBinding, |
| ); |
| } |
| |
| /// Tests that a key that is not present in the new report, but was present in the previous report |
| /// is propagated as released. |
| #[fasync::run_singlethreaded(test)] |
| async fn released_key() { |
| let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor { |
| keys: vec![fidl_fuchsia_input::Key::A], |
| }); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| |
| let reports = vec![ |
| testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::A], |
| event_time_i64, |
| ), |
| testing_utilities::create_keyboard_input_report(vec![], event_time_i64), |
| ]; |
| |
| let expected_events = vec![ |
| testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &descriptor, |
| /* keymap= */ None, |
| ), |
| testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| None, |
| event_time_u64, |
| &descriptor, |
| /* keymap= */ None, |
| ), |
| ]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor.clone(), |
| device_type: KeyboardBinding, |
| ); |
| } |
| |
| /// Tests that a key that is present in multiple consecutive input reports is not propagated |
| /// as a pressed event more than once. |
| #[fasync::run_singlethreaded(test)] |
| async fn multiple_pressed_event_filtering() { |
| let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor { |
| keys: vec![fidl_fuchsia_input::Key::A], |
| }); |
| let (event_time_i64, event_time_u64) = testing_utilities::event_times(); |
| |
| let reports = vec![ |
| testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::A], |
| event_time_i64, |
| ), |
| testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::A], |
| event_time_i64, |
| ), |
| ]; |
| |
| let expected_events = vec![testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time_u64, |
| &descriptor, |
| /* keymap= */ None, |
| )]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: KeyboardBinding, |
| ); |
| } |
| |
| /// Tests that both pressed and released keys are sent at once. |
| #[fasync::run_singlethreaded(test)] |
| async fn pressed_and_released_keys() { |
| let descriptor = input_device::InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor { |
| keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B], |
| }); |
| let (event_time_i64, event_time) = testing_utilities::event_times(); |
| |
| let reports = vec![ |
| testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::A], |
| event_time_i64, |
| ), |
| testing_utilities::create_keyboard_input_report( |
| vec![fidl_fuchsia_input::Key::B], |
| event_time_i64, |
| ), |
| ]; |
| |
| let expected_events = vec![ |
| testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| event_time, |
| &descriptor, |
| /* keymap= */ None, |
| ), |
| testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::A, |
| fidl_fuchsia_ui_input3::KeyEventType::Released, |
| None, |
| event_time, |
| &descriptor, |
| /* keymap= */ None, |
| ), |
| testing_utilities::create_keyboard_event( |
| fidl_fuchsia_input::Key::B, |
| fidl_fuchsia_ui_input3::KeyEventType::Pressed, |
| None, |
| // Simultaneous key events are artificially separated by 1ns |
| // on purpose. |
| event_time + zx::Duration::from_nanos(1), |
| &descriptor, |
| /* keymap= */ None, |
| ), |
| ]; |
| |
| assert_input_report_sequence_generates_events!( |
| input_reports: reports, |
| expected_events: expected_events, |
| device_descriptor: descriptor, |
| device_type: KeyboardBinding, |
| ); |
| } |
| |
| #[fuchsia::test] |
| fn get_unsided_modifiers() { |
| use fidl_ui_input3::Modifiers; |
| let event = KeyboardEvent::new(fidl_fuchsia_input::Key::A, KeyEventType::Pressed) |
| .into_with_modifiers(Some(Modifiers::all())); |
| assert_eq!( |
| event.get_unsided_modifiers(), |
| Modifiers::CAPS_LOCK |
| | Modifiers::NUM_LOCK |
| | Modifiers::SCROLL_LOCK |
| | Modifiers::FUNCTION |
| | Modifiers::SYMBOL |
| | Modifiers::SHIFT |
| | Modifiers::ALT |
| | Modifiers::ALT_GRAPH |
| | Modifiers::META |
| | Modifiers::CTRL |
| ) |
| } |
| } |