| // 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::Id::new().into()), |
| ..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, |
| // TODO(https://fxbug.dev/102570) Use millimeters. |
| counts_per_mm: 1, |
| }); |
| |
| /// 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, |
| // TODO(https://fxbug.dev/102570) Use millimeters. |
| counts_per_mm: 1, |
| }); |
| 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); |
| } |
| } |