blob: d38e752850d356c9903303c1e1cbf74f243f27fc [file] [log] [blame]
// Copyright 2020 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::input::types::{
ActionResult, MultiFingerSwipeRequest, MultiFingerTapRequest, SwipeRequest, TapRequest,
};
use anyhow::{Context, Error};
use fuchsia_syslog::macros::fx_log_info;
use input_synthesis as input;
use serde_json::{from_value, Value};
use std::{convert::TryFrom, time::Duration};
const DEFAULT_DIMENSION: u32 = 1000;
const DEFAULT_DURATION: u64 = 300;
macro_rules! validate_fingers {
( $fingers:expr, $field:ident $comparator:tt $limit:expr ) => {
match $fingers.iter().enumerate().find(|(_, finger)| !(finger.$field $comparator $limit)) {
None => Ok(()),
Some((finger_num, finger)) => Err(anyhow!(
"finger {}: expected {} {} {}, but {} is not {} {}",
finger_num,
stringify!($field),
stringify!($comparator),
stringify!($limit),
finger.$field,
stringify!($comparator),
$limit
)),
}
};
}
/// Perform Input fidl operations.
///
/// Note this object is shared among all threads created by server.
///
#[derive(Debug)]
pub struct InputFacade {}
impl InputFacade {
pub fn new() -> InputFacade {
InputFacade {}
}
/// Tap at coordinates (x, y) for a touchscreen with default or custom
/// width, height, duration, and tap event counts
///
/// # Arguments
/// * `value`: will be parsed to TapRequest
/// * must include:
/// * `x`: X axis coordinate
/// * `y`: Y axis coordinate
/// * optionally includes any of:
/// * `width`: Horizontal resolution of the touch panel, defaults to 1000
/// * `height`: Vertical resolution of the touch panel, defaults to 1000
/// * `tap_event_count`: Number of tap events to send (`duration` is divided over the tap
/// events), defaults to 1
/// * `duration`: Duration of the event(s) in milliseconds, defaults to 300
pub async fn tap(&self, args: Value) -> Result<ActionResult, Error> {
fx_log_info!("Executing Tap in Input Facade.");
let req: TapRequest = from_value(args)?;
const DEFAULT_TAP_EVENT_COUNT: usize = 1;
let width = match req.width {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let height = match req.height {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let tap_event_count = match req.tap_event_count {
Some(x) => x,
None => DEFAULT_TAP_EVENT_COUNT,
};
let duration = match req.duration {
Some(x) => Duration::from_millis(x),
None => Duration::from_millis(DEFAULT_DURATION),
};
input::tap_event_command(req.x, req.y, width, height, tap_event_count, duration).await?;
Ok(ActionResult::Success)
}
/// Multi-Finger Taps for a touchscreen with default or custom
/// width, height, duration, and tap event counts.
///
/// # Arguments
/// * `value`: will be parsed by MultiFingerTapRequest
/// * must include:
/// * `fingers`: List of FIDL struct `Touch` defined at
/// sdk/fidl/fuchsia.ui.input/input_reports.fidl.
/// * optionally includes any of:
/// * `width`: Horizontal resolution of the touch panel, defaults to 1000
/// * `height`: Vertical resolution of the touch panel, defaults to 1000
/// * `tap_event_count`: Number of multi-finger tap events to send
/// (`duration` is divided over the events), defaults to 1
/// * `duration`: Duration of the event(s) in milliseconds, defaults to 0
///
/// Example:
/// To send a 2-finger triple tap over 3s.
/// multi_finger_tap(MultiFingerTap {
/// tap_event_count: 3,
/// duration: 3000,
/// fingers: [
/// Touch { finger_id: 1, x: 0, y: 0, width: 0, height: 0 },
/// Touch { finger_id: 2, x: 20, y: 20, width: 0, height: 0 },
/// ]
/// });
///
pub async fn multi_finger_tap(&self, args: Value) -> Result<ActionResult, Error> {
fx_log_info!("Executing MultiFingerTap in Input Facade.");
let req: MultiFingerTapRequest = from_value(args)?;
const DEFAULT_TAP_EVENT_COUNT: usize = 1;
let width = match req.width {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let height = match req.height {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let tap_event_count = match req.tap_event_count {
Some(x) => x,
None => DEFAULT_TAP_EVENT_COUNT,
};
let duration = match req.duration {
Some(x) => Duration::from_millis(x),
None => Duration::from_millis(DEFAULT_DURATION),
};
input::multi_finger_tap_event_command(
req.fingers,
width,
height,
tap_event_count,
duration,
)
.await?;
Ok(ActionResult::Success)
}
/// Swipe from coordinates (x0, y0) to (x1, y1) for a touchscreen with default
/// or custom width, height, duration, and tap event counts
///
/// # Arguments
/// * `value`: will be parsed to SwipeRequest
/// * must include:
/// * `x0`: X axis start coordinate
/// * `y0`: Y axis start coordinate
/// * `x1`: X axis end coordinate
/// * `y1`: Y axis end coordinate
/// * optionally includes any of:
/// * `width`: Horizontal resolution of the touch panel, defaults to 1000
/// * `height`: Vertical resolution of the touch panel, defaults to 1000
/// * `tap_event_count`: Number of move events to send in between the down and up events of
/// the swipe, defaults to `duration / 17` (to emulate a 60 HZ sensor)
/// * `duration`: Duration of the event(s) in milliseconds, default to 300
pub async fn swipe(&self, args: Value) -> Result<ActionResult, Error> {
fx_log_info!("Executing Swipe in Input Facade.");
let req: SwipeRequest = from_value(args)?;
let width = match req.width {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let height = match req.height {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let duration = match req.duration {
Some(x) => Duration::from_millis(x),
None => Duration::from_millis(DEFAULT_DURATION),
};
let tap_event_count = match req.tap_event_count {
Some(x) => x,
// 17 msec per move event, to emulate a ~60Hz sensor.
None => duration.as_millis() as usize / 17,
};
input::swipe_command(
req.x0,
req.y0,
req.x1,
req.y1,
height,
width,
tap_event_count,
duration,
)
.await?;
Ok(ActionResult::Success)
}
/// Swipes multiple fingers from start positions to end positions for a touchscreen.
///
/// # Arguments
/// * `value`: will be parsed to `MultiFingerSwipeRequest`
/// * must include:
/// * `fingers`: List of `FingerSwipe`s.
/// * All `x0` and `x1` values must be in the range (0, width), regardless of
/// whether the width is defaulted or explicitly specified.
/// * All `y0` and `y1` values must be in the range (0, height), regardless of
/// whether the height is defaulted or explicitly specified.
/// * optionally includes any of:
/// * `width`: Horizontal resolution of the touch panel, defaults to 1000
/// * `height`: Vertical resolution of the touch panel, defaults to 1000
/// * `move_event_count`: Number of move events to send in between the down and up events of
/// the swipe.
/// * Defaults to `duration / 17` (to emulate a 60 HZ sensor).
/// * If 0, only the down and up events will be sent.
/// * `duration`: Duration of the event(s) in milliseconds
/// * Defaults to 300 milliseconds.
/// * Must be large enough to allow for at least one nanosecond per move event.
///
/// # Returns
/// * `Ok(ActionResult::Success)` if the arguments were successfully parsed and events
/// successfully injected.
/// * `Err(Error)` otherwise.
///
/// # Example
/// To send a two-finger swipe, with four events over two seconds:
///
/// ```
/// multi_finger_swipe(MultiFingerSwipeRequest {
/// fingers: [
/// FingerSwipe { x0: 0, y0: 0, x1: 100, y1: 0 },
/// FingerSwipe { x0: 0, y0: 100, x1: 100, y1: 100 },
/// ],
/// move_event_count: 4
/// duration: 2000,
/// });
/// ```
pub async fn multi_finger_swipe(&self, args: Value) -> Result<ActionResult, Error> {
fx_log_info!("Executing MultiFingerSwipe in Input Facade.");
let req: MultiFingerSwipeRequest = from_value(args)?;
let width = match req.width {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let height = match req.height {
Some(x) => x,
None => DEFAULT_DIMENSION,
};
let duration = match req.duration {
Some(x) => Duration::from_millis(x),
None => Duration::from_millis(DEFAULT_DURATION),
};
let move_event_count = match req.move_event_count {
Some(x) => x,
// 17 msec per move event, to emulate a ~60Hz sensor.
None => duration.as_millis() as usize / 17,
};
ensure!(
duration.as_nanos()
>= u128::try_from(move_event_count)
.context("internal error while validating `duration`")?,
"`duration` of {} nsec is too short for `move_event_count` of {}; \
all events would have same timestamp",
duration.as_nanos(),
move_event_count
);
validate_fingers!(req.fingers, x0 <= width)?;
validate_fingers!(req.fingers, x1 <= width)?;
validate_fingers!(req.fingers, y0 <= height)?;
validate_fingers!(req.fingers, y1 <= height)?;
let start_fingers =
req.fingers.iter().map(|finger| (finger.x0, finger.y0)).collect::<Vec<_>>();
let end_fingers =
req.fingers.iter().map(|finger| (finger.x1, finger.y1)).collect::<Vec<_>>();
input::multi_finger_swipe_command(
start_fingers,
end_fingers,
width,
height,
move_event_count,
duration,
)
.await?;
Ok(ActionResult::Success)
}
}