| // 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::synthesizer::*, |
| anyhow::{format_err, Error}, |
| fidl_fuchsia_ui_input::{self, Touch}, |
| fuchsia_component::client::new_service_connector, |
| std::time::Duration, |
| }; |
| |
| pub mod inverse_keymap; |
| pub mod keymaps; |
| pub mod legacy_backend; |
| pub mod synthesizer; |
| pub mod usages; |
| |
| mod modern_backend; |
| |
| /// Simulates a media button event. |
| pub async fn media_button_event_command( |
| volume_up: bool, |
| volume_down: bool, |
| mic_mute: bool, |
| reset: bool, |
| pause: bool, |
| camera_disable: bool, |
| ) -> Result<(), Error> { |
| media_button_event( |
| volume_up, |
| volume_down, |
| mic_mute, |
| reset, |
| pause, |
| camera_disable, |
| get_backend().await?.as_mut(), |
| ) |
| .await |
| } |
| |
| /// Simulates a key press of specified `usage`. |
| /// |
| /// `key_event_duration` is the time spent between key-press and key-release events. |
| /// |
| /// # Resolves to |
| /// * `Ok(())` if the events were successfully injected. |
| /// * `Err(Error)` otherwise. |
| /// |
| /// # Corner case handling |
| /// * `key_event_duration` of zero is permitted, and will result in events being generated as |
| /// quickly as possible. |
| /// |
| /// # Future directions |
| /// Per fxbug.dev/63532, this method will be replaced with a method that deals in |
| /// `fuchsia.input.Key`s, instead of HID Usage IDs. |
| pub async fn keyboard_event_command(usage: u32, key_event_duration: Duration) -> Result<(), Error> { |
| keyboard_event(usage, key_event_duration, get_backend().await?.as_mut()).await |
| } |
| |
| /// Simulates `input` being typed on a keyboard, with `key_event_duration` between key events. |
| /// |
| /// # Requirements |
| /// * `input` must be non-empty |
| /// * `input` must only contain characters representable using the current keyboard layout |
| /// and locale. (At present, it is assumed that the current layout and locale are |
| /// `US-QWERTY` and `en-US`, respectively.) |
| /// |
| /// # Resolves to |
| /// * `Ok(())` if the arguments met the requirements above, and the events were successfully |
| /// injected. |
| /// * `Err(Error)` otherwise. |
| /// |
| /// # Corner case handling |
| /// * `key_event_duration` of zero is permitted, and will result in events being generated as |
| /// quickly as possible. |
| pub async fn text_command(input: String, key_event_duration: Duration) -> Result<(), Error> { |
| text(input, key_event_duration, get_backend().await?.as_mut()).await |
| } |
| |
| /// Simulates `tap_event_count` taps at coordinates `(x, y)` for a touchscreen with horizontal |
| /// resolution `width` and vertical resolution `height`. `(x, y)` _should_ be specified in absolute |
| /// coordinations, with `x` normally in the range (0, `width`), `y` normally in the range |
| /// (0, `height`). |
| /// |
| /// `duration` is divided equally between touch-down and touch-up event pairs, while the |
| /// transition between these pairs is immediate. |
| pub async fn tap_event_command( |
| x: u32, |
| y: u32, |
| width: u32, |
| height: u32, |
| tap_event_count: usize, |
| duration: Duration, |
| ) -> Result<(), Error> { |
| tap_event(x, y, width, height, tap_event_count, duration, get_backend().await?.as_mut()).await |
| } |
| |
| /// Simulates `tap_event_count` times to repeat the multi-finger-taps, for touchscreen with |
| /// horizontal resolution `width` and vertical resolution `height`. Finger positions _should_ |
| /// be specified in absolute coordinations, with `x` values normally in the range (0, `width`), |
| /// and `y` values normally in the range (0, `height`). |
| /// |
| /// `duration` is divided equally between multi-touch-down and multi-touch-up |
| /// pairs, while the transition between these is immediate. |
| pub async fn multi_finger_tap_event_command( |
| fingers: Vec<Touch>, |
| width: u32, |
| height: u32, |
| tap_event_count: usize, |
| duration: Duration, |
| ) -> Result<(), Error> { |
| multi_finger_tap_event( |
| fingers, |
| width, |
| height, |
| tap_event_count, |
| duration, |
| get_backend().await?.as_mut(), |
| ) |
| .await |
| } |
| |
| /// Simulates swipe from coordinates `(x0, y0)` to `(x1, y1)` for a touchscreen with |
| /// horizontal resolution `width` and vertical resolution `height`, with `move_event_count` |
| /// touch-move events in between. Positions for move events are linearly interpolated. |
| /// |
| /// Finger positions _should_ be specified in absolute coordinations, with `x` values normally in the |
| /// range (0, `width`), and `y` values normally in the range (0, `height`). |
| /// |
| /// `duration` is the total time from the touch-down event to the touch-up event, inclusive |
| /// of all move events in between. |
| pub async fn swipe_command( |
| x0: u32, |
| y0: u32, |
| x1: u32, |
| y1: u32, |
| width: u32, |
| height: u32, |
| move_event_count: usize, |
| duration: Duration, |
| ) -> Result<(), Error> { |
| swipe(x0, y0, x1, y1, width, height, move_event_count, duration, get_backend().await?.as_mut()) |
| .await |
| } |
| |
| /// Simulates swipe with fingers starting at `start_fingers`, and moving to `end_fingers`, |
| /// for a touchscreen for a touchscreen with horizontal resolution `width` and vertical resolution |
| /// `height`. Finger positions _should_ be specified in absolute coordinations, with `x` values |
| /// normally in the range (0, `width`), and `y` values normally in the range (0, `height`). |
| /// |
| /// Linearly interpolates `move_event_count` touch-move events between the start positions |
| /// and end positions, over `duration` time. (`duration` is the total time from the touch-down |
| /// event to the touch-up event, inclusive of all move events in between.) |
| /// |
| /// # Requirements |
| /// * `start_fingers` and `end_fingers` must have the same length |
| /// * `start_fingers.len()` and `end_finger.len()` must be representable within a `u32` |
| /// |
| /// # Resolves to |
| /// * `Ok(())` if the arguments met the requirements above, and the events were successfully |
| /// injected. |
| /// * `Err(Error)` otherwise. |
| /// |
| /// # Corner case handling |
| /// * `move_event_count` of zero is permitted, and will result in just the DOWN and UP events |
| /// being generated. |
| /// * `duration.as_nanos() < move_event_count` is allowed, and will result in all events having |
| /// the same timestamp. |
| /// * `width` and `height` are permitted to be zero; such values are left to the interpretation |
| /// of the system under test. |
| /// * finger positions _may_ exceed the expected bounds; such values are left to the interpretation |
| /// of the sytem under test. |
| pub async fn multi_finger_swipe_command( |
| start_fingers: Vec<(u32, u32)>, |
| end_fingers: Vec<(u32, u32)>, |
| width: u32, |
| height: u32, |
| move_event_count: usize, |
| duration: Duration, |
| ) -> Result<(), Error> { |
| multi_finger_swipe( |
| start_fingers, |
| end_fingers, |
| width, |
| height, |
| move_event_count, |
| duration, |
| get_backend().await?.as_mut(), |
| ) |
| .await |
| } |
| |
| /// Selects an injection protocol, and returns the corresponding implementation |
| /// of `synthesizer::InputDeviceRegistry`. |
| /// |
| /// # Returns |
| /// * Ok(`legacy_backend::InputDeviceRegistry`) if `fuchsia.ui.input.InputDeviceRegistry` |
| /// is available (even if `fuchsia.input.injection.InputDeviceRegistry` is also available). |
| /// * Ok(`modern_backend::InputDeviceRegistry`) if only `fuchsia.input.injection.InputDeviceRegistry` |
| /// is available. |
| /// * Err otherwise. E.g., |
| /// * Neither protocol was available. |
| /// * Access to `/svc` was denied. |
| async fn get_backend() -> Result<Box<dyn InputDeviceRegistry>, Error> { |
| let legacy_registry = |
| new_service_connector::<fidl_fuchsia_ui_input::InputDeviceRegistryMarker>()?; |
| if legacy_registry.exists().await? { |
| return Ok(Box::new(legacy_backend::InputDeviceRegistry::new())); |
| } |
| |
| let modern_registry = |
| new_service_connector::<fidl_fuchsia_input_injection::InputDeviceRegistryMarker>()?; |
| if modern_registry.exists().await? { |
| return Ok(Box::new(modern_backend::InputDeviceRegistry::new(modern_registry.connect()?))); |
| } |
| |
| Err(format_err!("no available InputDeviceRegistry")) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| // The functions in this file need to bind to FIDL services in this component's environment to |
| // do their work, but a component can't modify its own environment. Hence, we can't validate |
| // this module with unit tests. |
| } |