blob: 93ffca91993bf8c2b43abc438b309ec69748559c [file] [log] [blame]
// Copyright 2021 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_device,
crate::input_handler::InputHandler,
crate::mouse_binding,
crate::mouse_config,
crate::utils::{CursorMessage, Position, Size},
anyhow::{anyhow, Context, Error, Result},
async_trait::async_trait,
async_utils::hanging_get::client::HangingGetStream,
fidl::endpoints::create_proxy,
fidl_fuchsia_input_report::Range,
fidl_fuchsia_ui_pointerinjector as pointerinjector,
fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config,
fuchsia_component::client::connect_to_protocol,
fuchsia_syslog::{fx_log_err, fx_log_info},
fuchsia_zircon as zx,
futures::{channel::mpsc::Sender, stream::StreamExt, SinkExt},
std::iter::FromIterator,
std::{cell::RefCell, collections::HashMap, option::Option, rc::Rc},
};
/// A [`MouseInjectorHandler`] parses mouse events and forwards them to Scenic through the
/// fidl_fuchsia_pointerinjector protocols.
pub struct MouseInjectorHandler {
/// The mutable fields of this handler.
mutable_state: RefCell<MutableState>,
/// The scope and coordinate system of injection.
/// See [`fidl_fuchsia_pointerinjector::Context`] for more details.
context_view_ref: fidl_fuchsia_ui_views::ViewRef,
/// The region where dispatch is attempted for injected events.
/// See [`fidl_fuchsia_pointerinjector::Target`] for more details.
target_view_ref: fidl_fuchsia_ui_views::ViewRef,
/// The maximum position sent to clients, used to bound relative movements
/// and scale absolute positions from device coordinates.
max_position: Position,
/// The FIDL proxy to register new injectors.
injector_registry_proxy: pointerinjector::RegistryProxy,
/// The FIDL proxy used to get configuration details for pointer injection.
configuration_proxy: pointerinjector_config::SetupProxy,
}
struct MutableState {
/// A rectangular region that directs injected events into a target.
/// See fidl_fuchsia_pointerinjector::Viewport for more details.
viewport: Option<pointerinjector::Viewport>,
/// The injectors registered with Scenic, indexed by their device ids.
injectors: HashMap<u32, pointerinjector::DeviceProxy>,
/// The current position.
current_position: Position,
/// A [`Sender`] used to communicate the current cursor state.
cursor_message_sender: Sender<CursorMessage>,
/// Set to true when in immersive mode.
immersive_mode: bool,
/// The current visibility for the cursor.
is_cursor_visible: bool,
}
#[async_trait(?Send)]
impl InputHandler for MouseInjectorHandler {
async fn handle_input_event(
self: Rc<Self>,
mut input_event: input_device::InputEvent,
) -> Vec<input_device::InputEvent> {
match input_event {
input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Mouse(ref mouse_event),
device_descriptor:
input_device::InputDeviceDescriptor::Mouse(ref mouse_device_descriptor),
event_time,
handled: input_device::Handled::No,
trace_id: _,
} => {
// TODO(fxbug.dev/90317): Investigate latency introduced by waiting for update_cursor_renderer
if let Err(e) =
self.update_cursor_renderer(mouse_event, &mouse_device_descriptor).await
{
fx_log_err!("update_cursor_renderer failed: {}", e);
}
let immersive_mode = self.mutable_state.borrow().immersive_mode;
if let Err(e) = self.update_cursor_visibility(!immersive_mode).await {
fx_log_err!("update_cursor_visibility failed: {}", e);
}
// Create a new injector if this is the first time seeing device_id.
if let Err(e) = self
.ensure_injector_registered(&mouse_event, &mouse_device_descriptor, event_time)
.await
{
fx_log_err!("ensure_injector_registered failed: {}", e);
}
// Handle the event.
if let Err(e) = self
.send_event_to_scenic(&mouse_event, &mouse_device_descriptor, event_time)
.await
{
fx_log_err!("send_event_to_scenic failed: {}", e);
}
// Consume the input event.
input_event.handled = input_device::Handled::Yes;
}
input_device::InputEvent {
device_event: input_device::InputDeviceEvent::Touch(ref _touch_event),
device_descriptor:
input_device::InputDeviceDescriptor::Touch(ref _touch_device_descriptor),
event_time: _,
handled: _,
trace_id: _,
} => {
// Hide the cursor on touch input.
// TODO(fxbug.dev/90290): Remove this workaround when we have a
// proper cursor API.
let visible = false;
if let Err(e) = self.update_cursor_visibility(visible).await {
fx_log_err!("update_cursor_visibility failed: {}", e);
}
}
input_device::InputEvent {
device_event:
input_device::InputDeviceEvent::MouseConfig(
mouse_config::MouseConfigEvent::ToggleImmersiveMode,
),
device_descriptor: input_device::InputDeviceDescriptor::MouseConfig,
event_time: _,
handled: _,
trace_id: _,
} => {
// Immersive mode hides the cursor and is a temporary workaround
// until we have a cursor API that makes it possible for UI
// components to control the appearance of the cursor.
//
// TODO(fxbug.dev/90290): Remove this workaround when we have a
// proper cursor API.
if let Err(e) = self.toggle_immersive_mode().await {
fx_log_err!("update_cursor_visibility failed: {}", e);
}
input_event.handled = input_device::Handled::Yes
}
_ => {}
}
vec![input_event]
}
}
impl MouseInjectorHandler {
/// Creates a new mouse handler that holds mouse pointer injectors.
/// The caller is expected to spawn a task to continually watch for updates to the viewport.
/// Example:
/// let handler = MouseInjectorHandler::new(display_size).await?;
/// fasync::Task::local(handler.clone().watch_viewport()).detach();
///
/// # Parameters
/// - `display_size`: The size of the associated display.
/// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
///
/// # Errors
/// If unable to connect to pointerinjector protocols.
pub async fn new(
display_size: Size,
cursor_message_sender: Sender<CursorMessage>,
) -> Result<Rc<Self>, Error> {
let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
Self::new_handler(
configuration_proxy,
injector_registry_proxy,
display_size,
cursor_message_sender,
)
.await
}
/// Creates a new mouse handler that holds mouse pointer injectors.
/// The caller is expected to spawn a task to continually watch for updates to the viewport.
/// Example:
/// let handler = MouseInjectorHandler::new_with_config_proxy(config_proxy, display_size).await?;
/// fasync::Task::local(handler.clone().watch_viewport()).detach();
///
/// # Parameters
/// - `configuration_proxy`: A proxy used to get configuration details for pointer
/// injection.
/// - `display_size`: The size of the associated display.
/// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
///
/// # Errors
/// If unable to get injection view refs from `configuration_proxy`.
/// If unable to connect to pointerinjector Registry protocol.
pub async fn new_with_config_proxy(
configuration_proxy: pointerinjector_config::SetupProxy,
display_size: Size,
cursor_message_sender: Sender<CursorMessage>,
) -> Result<Rc<Self>, Error> {
let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
Self::new_handler(
configuration_proxy,
injector_registry_proxy,
display_size,
cursor_message_sender,
)
.await
}
/// Creates a new mouse handler that holds mouse pointer injectors.
/// The caller is expected to spawn a task to continually watch for updates to the viewport.
/// Example:
/// let handler = MouseInjectorHandler::new_handler(None, None, display_size).await?;
/// fasync::Task::local(handler.clone().watch_viewport()).detach();
///
/// # Parameters
/// - `configuration_proxy`: A proxy used to get configuration details for pointer
/// injection.
/// - `injector_registry_proxy`: A proxy used to register new pointer injectors.
/// - `display_size`: The size of the associated display.
/// - `cursor_message_sender`: A [`Sender`] used to communicate the current cursor state.
///
/// # Errors
/// If unable to get injection view refs from `configuration_proxy`.
async fn new_handler(
configuration_proxy: pointerinjector_config::SetupProxy,
injector_registry_proxy: pointerinjector::RegistryProxy,
display_size: Size,
cursor_message_sender: Sender<CursorMessage>,
) -> Result<Rc<Self>, Error> {
// Get the context and target views to inject into.
let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
let handler = Rc::new(Self {
mutable_state: RefCell::new(MutableState {
viewport: None,
injectors: HashMap::new(),
// Initially centered.
current_position: Position {
x: display_size.width / 2.0,
y: display_size.height / 2.0,
},
cursor_message_sender,
immersive_mode: false,
is_cursor_visible: true,
}),
context_view_ref,
target_view_ref,
max_position: Position { x: display_size.width, y: display_size.height },
injector_registry_proxy,
configuration_proxy,
});
Ok(handler)
}
/// Adds a new pointer injector and tracks it in `self.injectors` if one doesn't exist at
/// `mouse_descriptor.device_id`.
///
/// # Parameters
/// - `mouse_event`: The mouse event to send to Scenic.
/// - `mouse_descriptor`: The descriptor for the device that sent the mouse event.
/// - `event_time`: The time in nanoseconds when the event was first recorded.
async fn ensure_injector_registered(
self: &Rc<Self>,
mouse_event: &mouse_binding::MouseEvent,
mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
event_time: zx::Time,
) -> Result<(), anyhow::Error> {
let mut inner = self.mutable_state.borrow_mut();
if inner.injectors.contains_key(&mouse_descriptor.device_id) {
return Ok(());
}
// Create a new injector.
let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>()
.context("Failed to create DeviceProxy.")?;
let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
.context("Failed to duplicate context view ref.")?;
let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
.context("Failed to duplicate target view ref.")?;
let viewport = inner.viewport.clone();
let config = pointerinjector::Config {
device_id: Some(mouse_descriptor.device_id),
device_type: Some(pointerinjector::DeviceType::Mouse),
context: Some(pointerinjector::Context::View(context)),
target: Some(pointerinjector::Target::View(target)),
viewport,
dispatch_policy: Some(pointerinjector::DispatchPolicy::MouseHoverAndLatchInTarget),
scroll_v_range: mouse_descriptor.wheel_v_range.clone(),
scroll_h_range: mouse_descriptor.wheel_h_range.clone(),
buttons: mouse_descriptor.buttons.clone(),
..pointerinjector::Config::EMPTY
};
// Register the new injector.
self.injector_registry_proxy
.register(config, device_server)
.await
.context("Failed to register injector.")?;
fx_log_info!("Registered injector with device id {:?}", mouse_descriptor.device_id);
// Keep track of the injector.
inner.injectors.insert(mouse_descriptor.device_id, device_proxy.clone());
// Inject ADD event the first time a MouseDevice is seen.
let events_to_send = vec![self.create_pointer_sample_event(
mouse_event,
event_time,
pointerinjector::EventPhase::Add,
inner.current_position,
None,
)];
device_proxy
.inject(&mut events_to_send.into_iter())
.await
.context("Failed to ADD new MouseDevice.")?;
Ok(())
}
/// Updates the current cursor position according to the received mouse event.
///
/// The updated cursor state is sent via `self.inner.cursor_message_sender` to a client
/// that renders the cursor on-screen.
///
/// If there is no movement, the location is not sent.
///
/// # Parameters
/// - `mouse_event`: The mouse event to use to update the cursor location.
/// - `mouse_descriptor`: The descriptor for the input device generating the input reports.
async fn update_cursor_renderer(
&self,
mouse_event: &mouse_binding::MouseEvent,
mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
) -> Result<(), anyhow::Error> {
let mut inner = self.mutable_state.borrow_mut();
let new_position = match (mouse_event.location, mouse_descriptor) {
(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: offset,
millimeters: _,
}),
_,
) => inner.current_position + offset,
(
mouse_binding::MouseLocation::Absolute(position),
mouse_binding::MouseDeviceDescriptor {
absolute_x_range: Some(x_range),
absolute_y_range: Some(y_range),
..
},
) => self.scale_absolute_position(&position, &x_range, &y_range),
(mouse_binding::MouseLocation::Absolute(_), _) => {
return Err(anyhow!(
"Received an Absolute mouse location without absolute device ranges."
))
}
};
inner.current_position = new_position;
Position::clamp(&mut inner.current_position, Position::zero(), self.max_position);
// Only send a position-update message if the cursor is visible. If it isn't, then when it
// eventually becomes visible, the position will be updated first.
if inner.is_cursor_visible {
let msg = CursorMessage::SetPosition(inner.current_position);
inner
.cursor_message_sender
.send(msg)
.await
.context("Failed to send current mouse position to cursor renderer")?;
}
Ok(())
}
/// Updates the current cursor's visibility.
///
/// The updated visibility is sent to a client via `self.inner.cursor_message_sender`.
///
/// If there is no change to visibility, the state is not sent.
///
/// # Parameters
/// - `visible`: The new visibility of the cursor.
async fn update_cursor_visibility(&self, visible: bool) -> Result<(), anyhow::Error> {
let mut inner = self.mutable_state.borrow_mut();
// No change to visibility needed.
if visible == inner.is_cursor_visible {
return Ok(());
}
inner.is_cursor_visible = visible;
// If we just became visible, update the cursor position. We update the position before
// making it visible, because doing it the other order can result in the cursor briefly
// appearing in the wrong position before jumping to the correct position.
if inner.is_cursor_visible {
let pos = inner.current_position;
inner
.cursor_message_sender
.send(CursorMessage::SetPosition(pos))
.await
.context("Failed to send current mouse position to cursor renderer")?;
}
inner
.cursor_message_sender
.send(CursorMessage::SetVisibility(visible))
.await
.context("Failed to send current visibility to cursor renderer")
}
/// Toggle "immersive mode", which means that the cursor is not shown.
///
/// When leaving immersive mode, the cursor becomes visible. Returns a bool that says whether
/// the handler is now in immersive mode.
async fn toggle_immersive_mode(&self) -> Result<bool, anyhow::Error> {
let immersive_mode = {
let mut inner = self.mutable_state.borrow_mut();
inner.immersive_mode = !inner.immersive_mode;
fx_log_info!("Toggled immersive mode: {}", inner.immersive_mode);
inner.immersive_mode
};
if let Err(e) = self.update_cursor_visibility(!immersive_mode).await {
return Err(e);
}
Ok(immersive_mode)
}
/// Returns an absolute cursor position scaled from device coordinates to the handler's
/// max position.
///
/// # Parameters
/// - `position`: Absolute cursor position in device coordinates.
/// - `x_range`: The range of possible x values of absolute mouse positions.
/// - `y_range`: The range of possible y values of absolute mouse positions.
fn scale_absolute_position(
&self,
position: &Position,
x_range: &Range,
y_range: &Range,
) -> Position {
let range_min = Position { x: x_range.min as f32, y: y_range.min as f32 };
let range_max = Position { x: x_range.max as f32, y: y_range.max as f32 };
self.max_position * ((*position - range_min) / (range_max - range_min))
}
/// Sends the given event to Scenic.
///
/// # Parameters
/// - `mouse_event`: The mouse event to send to Scenic.
/// - `mouse_descriptor`: The descriptor for the device that sent the mouse event.
/// - `event_time`: The time in nanoseconds when the event was first recorded.
async fn send_event_to_scenic(
&self,
mouse_event: &mouse_binding::MouseEvent,
mouse_descriptor: &mouse_binding::MouseDeviceDescriptor,
event_time: zx::Time,
) -> Result<(), anyhow::Error> {
let inner = self.mutable_state.borrow();
if let Some(injector) = inner.injectors.get(&mouse_descriptor.device_id) {
let relative_motion = match mouse_event.location {
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: offset,
millimeters: _,
}) if mouse_event.phase == mouse_binding::MousePhase::Move => {
Some([offset.x, offset.y])
}
_ => None,
};
let events_to_send = vec![self.create_pointer_sample_event(
mouse_event,
event_time,
pointerinjector::EventPhase::Change,
inner.current_position,
relative_motion,
)];
let _ = injector.inject(&mut events_to_send.into_iter()).await;
Ok(())
} else {
Err(anyhow::format_err!(
"No injector found for mouse device {}.",
mouse_descriptor.device_id
))
}
}
/// Creates a [`fidl_fuchsia_ui_pointerinjector::Event`] representing the given MouseEvent.
///
/// # Parameters
/// - `mouse_event`: The mouse event to send to Scenic.
/// - `event_time`: The time in nanoseconds when the event was first recorded.
/// - `phase`: The EventPhase to send to Scenic.
/// - `current_position`: The current cursor position.
/// - `relative_motion`: The relative motion to send to Scenic.
fn create_pointer_sample_event(
&self,
mouse_event: &mouse_binding::MouseEvent,
event_time: zx::Time,
phase: pointerinjector::EventPhase,
current_position: Position,
relative_motion: Option<[f32; 2]>,
) -> pointerinjector::Event {
let pointer_sample = pointerinjector::PointerSample {
pointer_id: Some(0),
phase: Some(phase),
position_in_viewport: Some([current_position.x, current_position.y]),
scroll_v: mouse_event.wheel_delta_v,
scroll_h: mouse_event.wheel_delta_h,
pressed_buttons: Some(Vec::from_iter(mouse_event.pressed_buttons.iter().cloned())),
relative_motion,
..pointerinjector::PointerSample::EMPTY
};
pointerinjector::Event {
timestamp: Some(event_time.into_nanos()),
data: Some(pointerinjector::Data::PointerSample(pointer_sample)),
trace_flow_id: None,
..pointerinjector::Event::EMPTY
}
}
/// Watches for viewport updates from the scene manager.
pub async fn watch_viewport(self: Rc<Self>) {
let configuration_proxy = self.configuration_proxy.clone();
let mut viewport_stream = HangingGetStream::new(
configuration_proxy,
pointerinjector_config::SetupProxy::watch_viewport,
);
loop {
match viewport_stream.next().await {
Some(Ok(new_viewport)) => {
// Update the viewport tracked by this handler.
let mut inner = self.mutable_state.borrow_mut();
inner.viewport = Some(new_viewport.clone());
// Update Scenic with the latest viewport.
for (_device_id, injector) in inner.injectors.iter() {
let events = &mut vec![pointerinjector::Event {
timestamp: Some(fuchsia_async::Time::now().into_nanos()),
data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
trace_flow_id: Some(fuchsia_trace::generate_nonce()),
..pointerinjector::Event::EMPTY
}]
.into_iter();
injector.inject(events).await.expect("Failed to inject updated viewport.");
}
}
Some(Err(e)) => {
fx_log_err!("Error while reading viewport update: {}", e);
return;
}
None => {
fx_log_err!("Viewport update stream terminated unexpectedly");
return;
}
}
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::testing_utilities::{
assert_handler_ignores_input_event_sequence, create_mouse_event,
create_mouse_event_with_handled, create_mouse_pointer_sample_event,
create_touch_contact, create_touch_event_with_handled,
},
crate::touch_binding,
assert_matches::assert_matches,
fidl_fuchsia_input_report as fidl_input_report, fuchsia_async as fasync,
fuchsia_zircon as zx,
futures::{channel::mpsc, StreamExt},
maplit::hashmap,
pretty_assertions::assert_eq,
std::collections::HashSet,
test_case::test_case,
};
// const MOUSE_ID: u32 = 1;
const DISPLAY_WIDTH: f32 = 100.0;
const DISPLAY_HEIGHT: f32 = 100.0;
/// Returns an |input_device::InputDeviceDescriptor::MouseDescriptor|.
const DESCRIPTOR: input_device::InputDeviceDescriptor =
input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
device_id: 1,
absolute_x_range: Some(fidl_input_report::Range { min: 0, max: 100 }),
absolute_y_range: Some(fidl_input_report::Range { min: 0, max: 100 }),
wheel_v_range: Some(fidl_input_report::Axis {
range: fidl_input_report::Range { min: -1, max: 1 },
unit: fidl_input_report::Unit {
type_: fidl_input_report::UnitType::Other,
exponent: 0,
},
}),
wheel_h_range: Some(fidl_input_report::Axis {
range: fidl_input_report::Range { min: -1, max: 1 },
unit: fidl_input_report::Unit {
type_: fidl_input_report::UnitType::Other,
exponent: 0,
},
}),
buttons: None,
});
/// Returns an TouchDescriptor.
fn get_touch_device_descriptor() -> input_device::InputDeviceDescriptor {
input_device::InputDeviceDescriptor::Touch(touch_binding::TouchDeviceDescriptor {
device_id: 1,
contacts: vec![touch_binding::ContactDeviceDescriptor {
x_range: fidl_input_report::Range { min: 0, max: 100 },
y_range: fidl_input_report::Range { min: 0, max: 100 },
pressure_range: None,
width_range: None,
height_range: None,
}],
})
}
/// Handles |fidl_fuchsia_pointerinjector_configuration::SetupRequest::GetViewRefs|.
async fn handle_configuration_request_stream(
stream: &mut pointerinjector_config::SetupRequestStream,
) {
if let Some(Ok(request)) = stream.next().await {
match request {
pointerinjector_config::SetupRequest::GetViewRefs { responder, .. } => {
let mut context = fuchsia_scenic::ViewRefPair::new()
.expect("Failed to create viewrefpair.")
.view_ref;
let mut target = fuchsia_scenic::ViewRefPair::new()
.expect("Failed to create viewrefpair.")
.view_ref;
let _ = responder.send(&mut context, &mut target);
}
_ => {}
};
}
}
/// Handles |fidl_fuchsia_pointerinjector::RegistryRequest|s by forwarding the registered device
/// over `injector_sender` to be handled by handle_device_request_stream().
async fn handle_registry_request_stream(
mut stream: pointerinjector::RegistryRequestStream,
injector_sender: futures::channel::oneshot::Sender<pointerinjector::DeviceRequestStream>,
) {
if let Some(request) = stream.next().await {
match request {
Ok(pointerinjector::RegistryRequest::Register {
config: _,
injector,
responder,
..
}) => {
let injector_stream =
injector.into_stream().expect("Failed to get stream from server end.");
let _ = injector_sender.send(injector_stream);
responder.send().expect("failed to respond");
}
_ => {}
};
} else {
panic!("RegistryRequestStream failed.");
}
}
// Handles |fidl_fuchsia_pointerinjector::RegistryRequest|s
async fn handle_registry_request_stream2(
mut stream: pointerinjector::RegistryRequestStream,
injector_sender: mpsc::UnboundedSender<Vec<pointerinjector::Event>>,
) {
let (injector, responder) = match stream.next().await {
Some(Ok(pointerinjector::RegistryRequest::Register {
config: _,
injector,
responder,
..
})) => (injector, responder),
other => panic!("expected register request, but got {:?}", other),
};
let injector_stream: pointerinjector::DeviceRequestStream =
injector.into_stream().expect("Failed to get stream from server end.");
responder.send().expect("failed to respond");
injector_stream
.for_each(|request| {
futures::future::ready({
match request {
Ok(pointerinjector::DeviceRequest::Inject {
events,
responder: device_injector_responder,
}) => {
let _ = injector_sender.unbounded_send(events);
device_injector_responder.send().expect("failed to respond")
}
Err(e) => panic!("FIDL error {}", e),
}
})
})
.await;
}
/// Handles |fidl_fuchsia_pointerinjector::DeviceRequest|s by asserting the injector stream
/// received on `injector_stream_receiver` gets `expected_events`.
async fn handle_device_request_stream(
injector_stream_receiver: futures::channel::oneshot::Receiver<
pointerinjector::DeviceRequestStream,
>,
expected_events: Vec<pointerinjector::Event>,
) {
let mut injector_stream =
injector_stream_receiver.await.expect("Failed to get DeviceRequestStream.");
for expected_event in expected_events {
match injector_stream.next().await {
Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
assert_eq!(events, vec![expected_event]);
responder.send().expect("failed to respond");
}
Some(Err(e)) => panic!("FIDL error {}", e),
None => panic!("Expected another event."),
}
}
}
// Creates a |pointerinjector::Viewport|.
fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
pointerinjector::Viewport {
extents: Some([[min, min], [max, max]]),
viewport_to_context_transform: None,
..pointerinjector::Viewport::EMPTY
}
}
// Synthesize a "hardware" input event to be processed by the tested MouseInjectorHandler.
fn create_relative_mouse_move_event(
position: Position,
time: fuchsia_zircon::Time,
) -> input_device::InputEvent {
create_mouse_event(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: position,
// TODO(https://fxbug.dev/102570): Implement millimeters.
millimeters: Position::zero(),
}),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Move,
HashSet::new(),
HashSet::new(),
time,
&DESCRIPTOR,
)
}
// Create a pointerinjector event which will be compared with one produced by the tested
// MouseInjectorHandler.
fn create_expected_pointerinjector_mouse_change_sample_event(
position: Position,
relative_motion: Option<[f32; 2]>,
time: fuchsia_zircon::Time,
) -> pointerinjector::Event {
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
position,
relative_motion,
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
time,
)
}
// Expect that |msg| is a SetPosition message with the expected position, otherwise panic.
fn expect_cursor_position_message(msg: Option<CursorMessage>, expected_position: Position) {
assert_eq!(Some(CursorMessage::SetPosition(expected_position)), msg);
}
// Expect that |msg| is a SetVisibility message with the expected boolean, otherwise panic.
fn expect_cursor_visibility_message(msg: Option<CursorMessage>, expected_bool: bool) {
assert_eq!(Some(CursorMessage::SetVisibility(expected_bool)), msg);
}
// Tests that MouseInjectorHandler::receives_viewport_updates() tracks viewport updates
// and notifies injectors about said updates.
#[fuchsia::test]
fn receives_viewport_updates() {
let mut exec = fasync::TestExecutor::new().expect("executor needed");
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, _) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(0);
// Create mouse handler.
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
let (mouse_handler_res, _) = exec.run_singlethreaded(futures::future::join(
mouse_handler_fut,
config_request_stream_fut,
));
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
// Add an injector.
let (injector_device_proxy, mut injector_device_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
mouse_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
// This nested block is used to bound the lifetime of `watch_viewport_fut`.
{
// Request a viewport update.
let watch_viewport_fut = mouse_handler.clone().watch_viewport();
futures::pin_mut!(watch_viewport_fut);
assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
// Send a viewport update.
match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
responder, ..
})) => {
responder.send(create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
}
other => panic!("Received unexpected value: {:?}", other),
};
assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
// Check that the injector received an updated viewport
exec.run_singlethreaded(async {
match injector_device_request_stream.next().await {
Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
assert_eq!(events.len(), 1);
assert!(events[0].data.is_some());
assert_eq!(
events[0].data,
Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
);
responder.send().expect("injector stream failed to respond.");
}
other => panic!("Received unexpected value: {:?}", other),
}
});
// Request viewport update.
assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
// Send viewport update.
match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
responder, ..
})) => {
responder
.send(create_viewport(100.0, 200.0))
.expect("Failed to send viewport.");
}
other => panic!("Received unexpected value: {:?}", other),
};
// Process viewport update.
assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
}
// Check that the injector received an updated viewport
exec.run_singlethreaded(async {
match injector_device_request_stream.next().await {
Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
assert_eq!(events.len(), 1);
assert!(events[0].data.is_some());
assert_eq!(
events[0].data,
Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
);
responder.send().expect("injector stream failed to respond.");
}
other => panic!("Received unexpected value: {:?}", other),
}
});
// Check the viewport on the handler is accurate.
let expected_viewport = create_viewport(100.0, 200.0);
assert_eq!(mouse_handler.mutable_state.borrow().viewport, Some(expected_viewport));
}
// Tests that a mouse move event both sends an update to scenic and sends the current cursor
// location via the cursor location sender.
#[test_case(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {counts:Position { x: 10.0, y: 15.0 }, millimeters: Position { x: 0.0, y: 0.0 }}),
Position { x: DISPLAY_WIDTH / 2.0 + 10.0, y: DISPLAY_HEIGHT / 2.0 + 15.0 },
[10.0, 15.0]; "Valid move event."
)]
#[test_case(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {counts:Position {
x: DISPLAY_WIDTH + 2.0,
y: DISPLAY_HEIGHT + 2.0,
}, millimeters: Position { x: 0.0, y: 0.0 }}),
Position {
x: DISPLAY_WIDTH ,
y: DISPLAY_HEIGHT,
},
[DISPLAY_WIDTH + 2.0, DISPLAY_HEIGHT + 2.0]; "Move event exceeds max bounds."
)]
#[test_case(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {counts:Position { x: -(DISPLAY_WIDTH + 20.0), y: -(DISPLAY_HEIGHT + 15.0) }, millimeters: Position { x: 0.0, y: 0.0 }}),
Position { x: 0.0, y: 0.0 },
[-(DISPLAY_WIDTH + 20.0), -(DISPLAY_HEIGHT + 15.0)]; "Move event exceeds min bounds."
)]
#[fuchsia::test(allow_stalls = false)]
async fn move_event(
move_location: mouse_binding::MouseLocation,
expected_position: Position,
expected_relative_motion: [f32; 2],
) {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let event_time = zx::Time::get_monotonic();
let input_event = create_mouse_event(
move_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Move,
HashSet::new(),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
// Handle event.
let handle_event_fut = mouse_handler.handle_input_event(input_event);
let expected_events = vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
Some(expected_relative_motion),
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
];
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
let registry_fut = handle_registry_request_stream(
injector_registry_request_stream,
injector_stream_sender,
);
let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
// Await all futures concurrently. If this completes, then the mouse event was handled and
// matches `expected_events`.
let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
pretty_assertions::assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
// No unhandled events.
assert_matches!(
handle_result.as_slice(),
[input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
);
}
// Tests that an absolute mouse move event scales the location from device coordinates to
// between {0, 0} and the handler's maximum position.
#[fuchsia::test(allow_stalls = false)]
async fn move_absolute_event() {
const DEVICE_ID: u32 = 1;
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
// The location is rescaled from the device coordinate system defined
// by `absolute_x_range` and `absolute_y_range`, to the display coordinate
// system defined by `max_position`.
//
// -50 y 0 +------------------ w
// | | .
// | | .
// | | .
// -50 x -----o----- 50 -> | . . . . . . . . .
// | | .
// * { x: -25, y: 25 } | * { x: w * 0.25, y: h * 0.75 }
// | | .
// 50 h | .
//
// Where w = DISPLAY_WIDTH, h = DISPLAY_HEIGHT
let cursor_location =
mouse_binding::MouseLocation::Absolute(Position { x: -25.0, y: 25.0 });
let event_time = zx::Time::get_monotonic();
let descriptor =
input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
device_id: DEVICE_ID,
absolute_x_range: Some(fidl_input_report::Range { min: -50, max: 50 }),
absolute_y_range: Some(fidl_input_report::Range { min: -50, max: 50 }),
wheel_v_range: None,
wheel_h_range: None,
buttons: None,
});
let input_event = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Move,
HashSet::new(),
HashSet::new(),
event_time,
&descriptor,
);
// Handle event.
let handle_event_fut = mouse_handler.handle_input_event(input_event);
let expected_position = Position { x: DISPLAY_WIDTH * 0.25, y: DISPLAY_HEIGHT * 0.75 };
let expected_events = vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
];
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
let registry_fut = handle_registry_request_stream(
injector_registry_request_stream,
injector_stream_sender,
);
let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
// Await all futures concurrently. If this completes, then the mouse event was handled and
// matches `expected_events`.
let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
// No unhandled events.
assert_matches!(
handle_result.as_slice(),
[input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
);
}
// Tests that mouse down and up events inject button press state.
#[test_case(
mouse_binding::MousePhase::Down,
vec![1], vec![1]; "Down event injects button press state."
)]
#[test_case(
mouse_binding::MousePhase::Up,
vec![1], vec![]; "Up event injects button press state."
)]
#[fuchsia::test(allow_stalls = false)]
async fn button_state_event(
phase: mouse_binding::MousePhase,
affected_buttons: Vec<mouse_binding::MouseButton>,
pressed_buttons: Vec<mouse_binding::MouseButton>,
) {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
let event_time = zx::Time::get_monotonic();
let input_event = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
phase,
HashSet::from_iter(affected_buttons.clone()),
HashSet::from_iter(pressed_buttons.clone()),
event_time,
&DESCRIPTOR,
);
// Handle event.
let handle_event_fut = mouse_handler.handle_input_event(input_event);
let expected_position = Position { x: 0.0, y: 0.0 };
let expected_events = vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
pressed_buttons.clone(),
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
pressed_buttons.clone(),
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
];
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
futures::channel::oneshot::channel::<pointerinjector::DeviceRequestStream>();
let registry_fut = handle_registry_request_stream(
injector_registry_request_stream,
injector_stream_sender,
);
let device_fut = handle_device_request_stream(injector_stream_receiver, expected_events);
// Await all futures concurrently. If this completes, then the mouse event was handled and
// matches `expected_events`.
let (handle_result, _, _) = futures::join!(handle_event_fut, registry_fut, device_fut);
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
pretty_assertions::assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
// No unhandled events.
assert_matches!(
handle_result.as_slice(),
[input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
);
}
// Tests that mouse down followed by mouse up events inject button press state.
#[fuchsia::test(allow_stalls = false)]
async fn down_up_event() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
// Note: The size of the CursorMessage channel's buffer is 2 to allow for one cursor
// update for every input event being sent.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(2);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
let event_time = zx::Time::get_monotonic();
let event1 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Down,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let event2 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Up,
HashSet::from_iter(vec![1]),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
let expected_position = Position { x: 0.0, y: 0.0 };
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
mouse_handler.clone().handle_input_event(event1).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)
])
);
// Send another input event.
mouse_handler.clone().handle_input_event(event2).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Wait until validation is complete.
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
}
/// Tests that two staggered button presses followed by stagged releases generate four mouse
/// events with distinct `affected_button` and `pressed_button`.
/// Specifically, we test and expect the following in order:
/// | Action | MousePhase | Injected Phase | `pressed_buttons` |
/// | ---------------- | ---------- | -------------- | ----------------- |
/// | Press button 1 | Down | Change | [1] |
/// | Press button 2 | Down | Change | [1, 2] |
/// | Release button 1 | Up | Change | [2] |
/// | Release button 2 | Up | Change | [] |
#[fuchsia::test(allow_stalls = false)]
async fn down_down_up_up_event() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
// Note: The size of the CursorMessage channel's buffer is 4 to allow for one cursor
// update for every input event being sent.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(4);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let cursor_location = mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 });
let event_time = zx::Time::get_monotonic();
let event1 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Down,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let event2 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Down,
HashSet::from_iter(vec![2]),
HashSet::from_iter(vec![1, 2]),
event_time,
&DESCRIPTOR,
);
let event3 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Up,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![2]),
event_time,
&DESCRIPTOR,
);
let event4 = create_mouse_event(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Up,
HashSet::from_iter(vec![2]),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
let expected_position = Position { x: 0.0, y: 0.0 };
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
mouse_handler.clone().handle_input_event(event1).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)
])
);
// Send another down event.
mouse_handler.clone().handle_input_event(event2).await;
let pointer_sample_event2 = injector_stream_receiver
.next()
.await
.map(|events| events.concat())
.expect("Failed to receive pointer sample event.");
let expected_event_time: i64 = event_time.into_nanos();
assert_eq!(pointer_sample_event2.len(), 1);
// We must break this event result apart for assertions since the
// `pressed_buttons` can be given with elements in any order.
match &pointer_sample_event2[0] {
pointerinjector::Event {
timestamp: Some(actual_event_time),
data:
Some(pointerinjector::Data::PointerSample(pointerinjector::PointerSample {
pointer_id: Some(0),
phase: Some(pointerinjector::EventPhase::Change),
position_in_viewport: Some(actual_position),
scroll_v: None,
scroll_h: None,
pressed_buttons: Some(actual_buttons),
relative_motion: None,
..
})),
..
} => {
assert_eq!(actual_event_time, &expected_event_time);
assert_eq!(actual_position[0], expected_position.x);
assert_eq!(actual_position[1], expected_position.y);
assert_eq!(
HashSet::<mouse_binding::MouseButton>::from_iter(actual_buttons.clone()),
HashSet::from_iter(vec![1, 2])
);
}
_ => panic!("Unexpected pointer sample event: {:?}", pointer_sample_event2[0]),
}
// Send another up event.
mouse_handler.clone().handle_input_event(event3).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![2],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Send another up event.
mouse_handler.clone().handle_input_event(event4).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Wait until validation is complete.
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
}
/// Tests that button press, mouse move, and button release inject changes accordingly.
#[fuchsia::test(allow_stalls = false)]
async fn down_move_up_event() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
// Note: The size of the CursorMessage channel's buffer is 3 to allow for one cursor
// update for every input event being sent.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(3);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let event_time = zx::Time::get_monotonic();
let zero_position = Position { x: 0.0, y: 0.0 };
let expected_position = Position { x: 10.0, y: 15.0 };
let expected_relative_motion = [10.0, 15.0];
let event1 = create_mouse_event(
mouse_binding::MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Down,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let event2 = create_mouse_event(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: Position { x: 10.0, y: 15.0 },
millimeters: Position { x: 0.0, y: 0.0 },
}),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Move,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let event3 = create_mouse_event(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: Position { x: 0.0, y: 0.0 },
millimeters: Position { x: 0.0, y: 0.0 },
}),
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Up,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![]),
event_time,
&DESCRIPTOR,
);
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
mouse_handler.clone().handle_input_event(event1).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![1],
zero_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
zero_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)
])
);
// Wait until cursor position validation is complete.
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, zero_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
// Send a move event.
mouse_handler.clone().handle_input_event(event2).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
expected_position,
Some(expected_relative_motion),
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Wait until cursor position validation is complete.
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
// Send an up event.
mouse_handler.clone().handle_input_event(event3).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Wait until cursor position validation is complete.
match receiver.next().await {
Some(CursorMessage::SetPosition(position)) => {
assert_eq!(position, expected_position);
}
Some(CursorMessage::SetVisibility(_)) => {
panic!("Received unexpected cursor visibility update.")
}
None => panic!("Did not receive cursor update."),
}
}
// Tests that a mouse move event that has already been handled is not forwarded to scenic.
#[fuchsia::test(allow_stalls = false)]
async fn handler_ignores_handled_events() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
let cursor_relative_position = Position { x: 50.0, y: 75.0 };
let cursor_location =
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: cursor_relative_position,
millimeters: Position { x: 0.0, y: 0.0 },
});
let event_time = zx::Time::get_monotonic();
let input_events = vec![create_mouse_event_with_handled(
cursor_location,
None, /* wheel_delta_v */
None, /* wheel_delta_h */
mouse_binding::MousePhase::Move,
HashSet::new(),
HashSet::new(),
event_time,
&DESCRIPTOR,
input_device::Handled::Yes,
)];
assert_handler_ignores_input_event_sequence(
mouse_handler,
input_events,
injector_registry_request_stream,
)
.await;
// The cursor location stream should not receive any position.
assert!(receiver.next().await.is_none());
}
/// Test simple scroll in vertical and horizontal.
#[fuchsia::test(allow_stalls = false)]
async fn scroll() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
let zero_location =
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: Position { x: 0.0, y: 0.0 },
millimeters: Position { x: 0.0, y: 0.0 },
});
let expected_position = Position { x: 50.0, y: 50.0 };
let event_time = zx::Time::get_monotonic();
let wheel_v_event = create_mouse_event(
zero_location,
Some(1),
None,
mouse_binding::MousePhase::Wheel,
HashSet::new(),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
let wheel_h_event = create_mouse_event(
zero_location,
None,
Some(1),
mouse_binding::MousePhase::Wheel,
HashSet::new(),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
// Handle event 1 vertical scroll.
mouse_handler.clone().handle_input_event(wheel_v_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![],
expected_position,
None, /*relative_motion*/
Some(1), /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
Some(1), /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
])
);
// Handle event 1 horizontal scroll.
mouse_handler.clone().handle_input_event(wheel_h_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
Some(1), /*wheel_delta_h*/
event_time,
)])
);
}
/// Test button down -> scroll -> button up -> continue scroll.
#[fuchsia::test(allow_stalls = false)]
async fn down_scroll_up_scroll() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, _) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
let zero_location =
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
counts: Position { x: 0.0, y: 0.0 },
millimeters: Position { x: 0.0, y: 0.0 },
});
let expected_position = Position { x: 50.0, y: 50.0 };
let event_time = zx::Time::get_monotonic();
let down_event = create_mouse_event(
zero_location,
None,
None,
mouse_binding::MousePhase::Down,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let wheel_event = create_mouse_event(
zero_location,
Some(1),
None,
mouse_binding::MousePhase::Wheel,
HashSet::from_iter(vec![1]),
HashSet::from_iter(vec![1]),
event_time,
&DESCRIPTOR,
);
let up_event = create_mouse_event(
zero_location,
None,
None,
mouse_binding::MousePhase::Up,
HashSet::from_iter(vec![1]),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
let continue_wheel_event = create_mouse_event(
zero_location,
Some(1),
None,
mouse_binding::MousePhase::Wheel,
HashSet::new(),
HashSet::new(),
event_time,
&DESCRIPTOR,
);
// Handle button down event.
mouse_handler.clone().handle_input_event(down_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
),
])
);
// Handle wheel event with button pressing.
mouse_handler.clone().handle_input_event(wheel_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![1],
expected_position,
None, /*relative_motion*/
Some(1), /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Handle button up event.
mouse_handler.clone().handle_input_event(up_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
// Handle wheel event after button released.
mouse_handler.clone().handle_input_event(continue_wheel_event).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Change,
vec![],
expected_position,
None, /*relative_motion*/
Some(1), /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time,
)])
);
}
// Tests that a mouse move event that has already been handled is not forwarded to scenic.
#[fuchsia::test(allow_stalls = false)]
async fn touch_hides_cursor() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (sender, mut receiver) = futures::channel::mpsc::channel::<CursorMessage>(1);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
const TOUCH_ID: u32 = 1;
let touch_descriptor = get_touch_device_descriptor();
let event_time = zx::Time::get_monotonic();
let input_events = vec![create_touch_event_with_handled(
hashmap! {
fidl_fuchsia_ui_input::PointerEventPhase::Add
=> vec![create_touch_contact(TOUCH_ID, Position{ x: 20.0, y: 40.0 })],
fidl_fuchsia_ui_input::PointerEventPhase::Down
=> vec![create_touch_contact(TOUCH_ID, Position{ x: 20.0, y: 40.0 })],
},
event_time,
&touch_descriptor,
input_device::Handled::Yes,
)];
assert_handler_ignores_input_event_sequence(
mouse_handler,
input_events,
injector_registry_request_stream,
)
.await;
// Touch event should hide the cursor.
match receiver.next().await {
Some(CursorMessage::SetVisibility(visible)) => assert_eq!(visible, false),
_ => panic!("Touch event did not hide the cursor."),
};
}
// Tests that mouse cursor position events are suppressed when the mouse cursor is invisible.
#[fuchsia::test(allow_stalls = false)]
async fn invisibility_suppresses_cursor_positions_updates() {
// Set up fidl streams.
let (configuration_proxy, mut configuration_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>()
.expect("Failed to create pointerinjector Setup proxy and stream.");
let (injector_registry_proxy, injector_registry_request_stream) =
fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>()
.expect("Failed to create pointerinjector Registry proxy and stream.");
let config_request_stream_fut =
handle_configuration_request_stream(&mut configuration_request_stream);
// Create MouseInjectorHandler.
let (cursor_message_sender, mut cursor_message_receiver) =
futures::channel::mpsc::channel::<CursorMessage>(20);
let mouse_handler_fut = MouseInjectorHandler::new_handler(
configuration_proxy,
injector_registry_proxy,
Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
cursor_message_sender,
);
let (mouse_handler_res, _) = futures::join!(mouse_handler_fut, config_request_stream_fut);
let mouse_handler = mouse_handler_res.expect("Failed to create mouse handler");
// Create a channel for the the registered device's handle to be forwarded to the
// DeviceRequestStream handler. This allows the registry_fut to complete and allows
// handle_input_event() to continue.
let (injector_stream_sender, injector_stream_receiver) =
mpsc::unbounded::<Vec<pointerinjector::Event>>();
// Up to 2 events per handle_input_event() call.
let mut injector_stream_receiver = injector_stream_receiver.ready_chunks(2);
let registry_fut = handle_registry_request_stream2(
injector_registry_request_stream,
injector_stream_sender,
);
// Run all futures until the handler future completes.
let _registry_task = fasync::Task::local(registry_fut);
// The first event is to initially add the pointer so that we can more directly compare
// event2/event3 with event4/event5.
let zero_position = Position { x: 0.0, y: 0.0 };
let event_time1 = zx::Time::get_monotonic();
let event1 = create_mouse_event(
mouse_binding::MouseLocation::Absolute(zero_position),
None,
None,
mouse_binding::MousePhase::Down,
HashSet::new(),
HashSet::new(),
event_time1,
&DESCRIPTOR,
);
mouse_handler.clone().handle_input_event(event1).await;
expect_cursor_position_message(cursor_message_receiver.next().await, zero_position);
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_mouse_pointer_sample_event(
pointerinjector::EventPhase::Add,
vec![],
zero_position,
None, /*relative_motion*/
None, /*wheel_delta_v*/
None, /*wheel_delta_h*/
event_time1,
),
create_expected_pointerinjector_mouse_change_sample_event(
zero_position,
None,
event_time1
),
]),
"after event1"
);
use std::ops::Add; // used to add Durations to Times below.
// event2/event3 cause pointerinjector events to be sent, and since the cursor is visible,
// also causes cursor position messages to be sent.
let event_time2 = event_time1.add(fuchsia_zircon::Duration::from_micros(1));
let event2 = create_relative_mouse_move_event(Position { x: 3.0, y: 5.0 }, event_time2);
let event_time3 = event_time2.add(fuchsia_zircon::Duration::from_micros(1));
let event3 = create_relative_mouse_move_event(Position { x: 4.0, y: 6.0 }, event_time3);
mouse_handler.clone().handle_input_event(event2).await;
mouse_handler.clone().handle_input_event(event3).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_expected_pointerinjector_mouse_change_sample_event(
Position { x: 3.0, y: 5.0 },
Some([3.0, 5.0]),
event_time2
),
create_expected_pointerinjector_mouse_change_sample_event(
Position { x: 7.0, y: 11.0 },
Some([4.0, 6.0]),
event_time3
),
]),
"after event2 and event3"
);
expect_cursor_position_message(
cursor_message_receiver.next().await,
Position { x: 3.0, y: 5.0 },
);
expect_cursor_position_message(
cursor_message_receiver.next().await,
Position { x: 7.0, y: 11.0 },
);
// Make the cursor invisible. The MouseInjectorHandler will continue to generate
// pointerinjector events, but it will not generate any SetPosition cursor messages until
// the cursor becomes visible again.
assert_eq!(true, mouse_handler.toggle_immersive_mode().await.unwrap());
expect_cursor_visibility_message(cursor_message_receiver.next().await, false);
// event4/event5 cause pointerinjector events to be sent. However, now the handler is in
// immersive mode; since the cursor is NOT visible, no cursor position messages are sent.
let event_time4 = event_time3.add(fuchsia_zircon::Duration::from_micros(1));
let event4 = create_relative_mouse_move_event(Position { x: -2.0, y: -2.0 }, event_time4);
let event_time5 = event_time4.add(fuchsia_zircon::Duration::from_micros(1));
let event5 = create_relative_mouse_move_event(Position { x: -1.0, y: -3.0 }, event_time5);
mouse_handler.clone().handle_input_event(event4).await;
mouse_handler.clone().handle_input_event(event5).await;
assert_eq!(
injector_stream_receiver.next().await.map(|events| events.concat()),
Some(vec![
create_expected_pointerinjector_mouse_change_sample_event(
Position { x: 5.0, y: 9.0 },
Some([-2.0, -2.0]),
event_time4
),
create_expected_pointerinjector_mouse_change_sample_event(
Position { x: 4.0, y: 6.0 },
Some([-1.0, -3.0]),
event_time5
),
]),
"after event4 and event5"
);
// Make the cursor visible again. This will result in a cursor position update, followed
// by a cursor visibility update.
//
// NOTE: above, we didn't verify directly that no cursor position messages were generated.
// However, if they were, there would have been 2: one for each of event4/event5, similar to
// how there was one for each of event2/event3 while the cursor was visible. Instead, by
// toggling off immersive mode, we make the cursor visible, which sends:
// - a cursor position message with the latest position, then:
// - a cursor visibility message
assert_eq!(false, mouse_handler.toggle_immersive_mode().await.unwrap());
expect_cursor_position_message(
cursor_message_receiver.next().await,
Position { x: 4.0, y: 6.0 },
);
expect_cursor_visibility_message(cursor_message_receiver.next().await, true);
}
}