1#![warn(clippy::await_holding_refcell_ref)]
6use crate::input_handler::{BatchInputHandler, Handler, InputHandlerStatus};
7use crate::utils::{Position, Size};
8use crate::{input_device, metrics, touch_binding};
9use anyhow::{Context, Error, Result};
10use async_trait::async_trait;
11use async_utils::hanging_get::client::HangingGetStream;
12use fidl::endpoints::{Proxy, create_proxy};
13use fidl::{AsHandleRef, HandleBased};
14use fuchsia_component::client::connect_to_protocol;
15use fuchsia_inspect::health::Reporter;
16use futures::channel::mpsc;
17use futures::stream::StreamExt;
18use metrics_registry::*;
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::rc::Rc;
22use {
23 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
24 fidl_fuchsia_ui_pointerinjector as pointerinjector,
25 fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config,
26 fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
27};
28
29pub struct TouchInjectorHandler {
32 mutable_state: RefCell<MutableState>,
34
35 context_view_ref: fidl_fuchsia_ui_views::ViewRef,
38
39 target_view_ref: fidl_fuchsia_ui_views::ViewRef,
42
43 display_size: Size,
47
48 injector_registry_proxy: pointerinjector::RegistryProxy,
50
51 configuration_proxy: pointerinjector_config::SetupProxy,
53
54 pub inspect_status: InputHandlerStatus,
56
57 metrics_logger: metrics::MetricsLogger,
59}
60
61#[derive(Debug)]
62struct MutableState {
63 viewport: Option<pointerinjector::Viewport>,
66
67 injectors: HashMap<u32, pointerinjector::DeviceProxy>,
69
70 pub listeners: HashMap<u32, fidl_ui_policy::TouchButtonsListenerProxy>,
72
73 pub last_button_event: Option<fidl_ui_input::TouchButtonsEvent>,
76
77 pub send_event_task_tracker: LocalTaskTracker,
78}
79
80impl Handler for TouchInjectorHandler {
81 fn set_handler_healthy(self: std::rc::Rc<Self>) {
82 self.inspect_status.health_node.borrow_mut().set_ok();
83 }
84
85 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
86 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
87 }
88
89 fn get_name(&self) -> &'static str {
90 "TouchInjectorHandler"
91 }
92
93 fn interest(&self) -> Vec<input_device::InputEventType> {
94 vec![input_device::InputEventType::TouchScreen]
95 }
96}
97
98#[async_trait(?Send)]
99impl BatchInputHandler for TouchInjectorHandler {
100 async fn handle_input_events(
101 self: Rc<Self>,
102 events: Vec<input_device::InputEvent>,
103 ) -> Vec<input_device::InputEvent> {
104 if events.is_empty() {
105 return events;
106 }
107
108 fuchsia_trace::duration!("input", "touch_injector_handler");
109
110 let mut result: Vec<input_device::InputEvent> = Vec::new();
111 let mut pending_scenic_events: Vec<pointerinjector::Event> = Vec::new();
112
113 let device_id = events[0].device_descriptor.device_id();
114 let has_different_device_events =
115 events.iter().any(|e| e.device_descriptor.device_id() != device_id);
116 if has_different_device_events {
117 log::warn!("TouchInjectorHandler: Received events from different devices");
119 return events;
120 }
121
122 for event in events {
123 let (out_events, scenic_events) = self.clone().handle_single_input_event(event).await;
124 result.extend(out_events);
125 pending_scenic_events.extend(scenic_events);
126 }
127
128 if !pending_scenic_events.is_empty() {
129 if let input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor) =
130 result[0].device_descriptor
131 {
132 if let Err(e) =
133 self.inject_pointer_events(pending_scenic_events, touch_device_descriptor)
134 {
135 self.metrics_logger.log_error(
136 InputPipelineErrorMetricDimensionEvent::TouchInjectorSendEventToScenicFailed,
137 std::format!("inject_pointer_events failed: {}", e),
138 );
139 }
140 }
141 }
142
143 result
144 }
145}
146
147impl TouchInjectorHandler {
148 pub async fn new(
160 display_size: Size,
161 input_handlers_node: &fuchsia_inspect::Node,
162 metrics_logger: metrics::MetricsLogger,
163 ) -> Result<Rc<Self>, Error> {
164 let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
165 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
166
167 Self::new_handler(
168 configuration_proxy,
169 injector_registry_proxy,
170 display_size,
171 input_handlers_node,
172 metrics_logger,
173 )
174 .await
175 }
176
177 pub async fn new_with_config_proxy(
192 configuration_proxy: pointerinjector_config::SetupProxy,
193 display_size: Size,
194 input_handlers_node: &fuchsia_inspect::Node,
195 metrics_logger: metrics::MetricsLogger,
196 ) -> Result<Rc<Self>, Error> {
197 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
198 Self::new_handler(
199 configuration_proxy,
200 injector_registry_proxy,
201 display_size,
202 input_handlers_node,
203 metrics_logger,
204 )
205 .await
206 }
207
208 async fn new_handler(
224 configuration_proxy: pointerinjector_config::SetupProxy,
225 injector_registry_proxy: pointerinjector::RegistryProxy,
226 display_size: Size,
227 input_handlers_node: &fuchsia_inspect::Node,
228 metrics_logger: metrics::MetricsLogger,
229 ) -> Result<Rc<Self>, Error> {
230 let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
232
233 let inspect_status = InputHandlerStatus::new(
234 input_handlers_node,
235 "touch_injector_handler",
236 false,
237 );
238 let handler = Rc::new(Self {
239 mutable_state: RefCell::new(MutableState {
240 viewport: None,
241 injectors: HashMap::new(),
242 listeners: HashMap::new(),
243 last_button_event: None,
244 send_event_task_tracker: LocalTaskTracker::new(),
245 }),
246 context_view_ref,
247 target_view_ref,
248 display_size,
249 injector_registry_proxy,
250 configuration_proxy,
251 inspect_status,
252 metrics_logger,
253 });
254
255 Ok(handler)
256 }
257
258 fn clone_event(event: &fidl_ui_input::TouchButtonsEvent) -> fidl_ui_input::TouchButtonsEvent {
259 fidl_ui_input::TouchButtonsEvent {
260 event_time: event.event_time,
261 device_info: event.device_info.clone(),
262 pressed_buttons: event.pressed_buttons.clone(),
263 wake_lease: event.wake_lease.as_ref().map(|lease| {
264 fidl::EventPair::from_handle(
265 lease
266 .as_handle_ref()
267 .duplicate(zx::Rights::SAME_RIGHTS)
268 .expect("failed to duplicate event pair")
269 .into_handle(),
270 )
271 }),
272 ..Default::default()
273 }
274 }
275
276 async fn handle_single_input_event(
277 self: Rc<Self>,
278 mut input_event: input_device::InputEvent,
279 ) -> (Vec<input_device::InputEvent>, Vec<pointerinjector::Event>) {
280 match input_event {
281 input_device::InputEvent {
282 device_event: input_device::InputDeviceEvent::TouchScreen(ref mut touch_event),
283 device_descriptor:
284 input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor),
285 event_time,
286 handled: input_device::Handled::No,
287 trace_id,
288 } => {
289 self.inspect_status.count_received_event(&event_time);
290 fuchsia_trace::duration!("input", "touch_injector_handler[processing]");
291 if let Some(trace_id) = trace_id {
292 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", trace_id.into());
293 }
294
295 let mut scenic_events = vec![];
296 if touch_event.injector_contacts.values().all(|vec| vec.is_empty()) {
297 let mut touch_buttons_event = Self::create_touch_buttons_event(
298 touch_event,
299 event_time,
300 &touch_device_descriptor,
301 );
302
303 self.send_event_to_listeners(&touch_buttons_event).await;
305
306 std::mem::drop(touch_buttons_event.wake_lease.take());
308 self.mutable_state.borrow_mut().last_button_event = Some(touch_buttons_event);
309 } else if touch_event.pressed_buttons.is_empty() {
310 if let Err(e) = self.ensure_injector_registered(&touch_device_descriptor).await
312 {
313 self.metrics_logger.log_error(
314 InputPipelineErrorMetricDimensionEvent::TouchInjectorEnsureInjectorRegisteredFailed,
315 std::format!("ensure_injector_registered failed: {}", e));
316 }
317
318 scenic_events = self.create_pointer_events(
320 touch_event,
321 &touch_device_descriptor,
322 event_time,
323 );
324 }
325
326 self.inspect_status.count_handled_event();
328 (vec![input_event.into_handled()], scenic_events)
329 }
330 input_device::InputEvent {
331 device_event: input_device::InputDeviceEvent::TouchScreen(_),
332 handled: input_device::Handled::Yes,
333 ..
334 } => {
335 (vec![input_event], vec![])
337 }
338 _ => {
339 log::warn!("Unhandled input event: {:?}", input_event.get_event_type());
340 (vec![input_event], vec![])
341 }
342 }
343 }
344
345 async fn ensure_injector_registered(
351 self: &Rc<Self>,
352 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
353 ) -> Result<(), anyhow::Error> {
354 if self.mutable_state.borrow().injectors.contains_key(&touch_descriptor.device_id) {
355 return Ok(());
356 }
357
358 let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>();
360 let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
361 .context("Failed to duplicate context view ref.")?;
362 let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
363 .context("Failed to duplicate target view ref.")?;
364 let viewport = self.mutable_state.borrow().viewport.clone();
365 if viewport.is_none() {
366 return Err(anyhow::format_err!(
369 "Received a touch event without a viewport to inject into."
370 ));
371 }
372 let config = pointerinjector::Config {
373 device_id: Some(touch_descriptor.device_id),
374 device_type: Some(pointerinjector::DeviceType::Touch),
375 context: Some(pointerinjector::Context::View(context)),
376 target: Some(pointerinjector::Target::View(target)),
377 viewport,
378 dispatch_policy: Some(pointerinjector::DispatchPolicy::TopHitAndAncestorsInTarget),
379 scroll_v_range: None,
380 scroll_h_range: None,
381 buttons: None,
382 ..Default::default()
383 };
384
385 self.mutable_state.borrow_mut().injectors.insert(touch_descriptor.device_id, device_proxy);
387
388 self.injector_registry_proxy
390 .register(config, device_server)
391 .await
392 .context("Failed to register injector.")?;
393 log::info!("Registered injector with device id {:?}", touch_descriptor.device_id);
394
395 Ok(())
396 }
397
398 fn create_pointer_events(
405 &self,
406 touch_event: &mut touch_binding::TouchScreenEvent,
407 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
408 event_time: zx::MonotonicInstant,
409 ) -> Vec<pointerinjector::Event> {
410 let ordered_phases = vec![
411 pointerinjector::EventPhase::Add,
412 pointerinjector::EventPhase::Change,
413 pointerinjector::EventPhase::Remove,
414 ];
415
416 let mut events: Vec<pointerinjector::Event> = vec![];
417 for phase in ordered_phases {
418 let contacts: Vec<touch_binding::TouchContact> = touch_event
419 .injector_contacts
420 .get(&phase)
421 .map_or(vec![], |contacts| contacts.to_owned());
422 let new_events = contacts.into_iter().map(|contact| {
423 Self::create_pointer_sample_event(
424 phase,
425 &contact,
426 touch_descriptor,
427 &self.display_size,
428 event_time,
429 touch_event.wake_lease.take(),
430 )
431 });
432 events.extend(new_events);
433 }
434
435 events
436 }
437
438 fn inject_pointer_events(
444 &self,
445 events: Vec<pointerinjector::Event>,
446 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
447 ) -> Result<(), anyhow::Error> {
448 fuchsia_trace::duration!("input", "touch-inject-into-scenic");
449
450 let injector =
451 self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
452 if let Some(injector) = injector {
453 let _ = injector.inject_events(events);
454 Ok(())
455 } else {
456 Err(anyhow::format_err!(
457 "No injector found for touch device {}.",
458 touch_descriptor.device_id
459 ))
460 }
461 }
462
463 fn create_pointer_sample_event(
473 phase: pointerinjector::EventPhase,
474 contact: &touch_binding::TouchContact,
475 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
476 display_size: &Size,
477 event_time: zx::MonotonicInstant,
478 wake_lease: Option<zx::EventPair>,
479 ) -> pointerinjector::Event {
480 let position =
481 Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
482 let pointer_sample = pointerinjector::PointerSample {
483 pointer_id: Some(contact.id),
484 phase: Some(phase),
485 position_in_viewport: Some([position.x, position.y]),
486 scroll_v: None,
487 scroll_h: None,
488 pressed_buttons: None,
489 ..Default::default()
490 };
491 let data = pointerinjector::Data::PointerSample(pointer_sample);
492
493 let trace_flow_id = fuchsia_trace::Id::random();
494 let event = pointerinjector::Event {
495 timestamp: Some(event_time.into_nanos()),
496 data: Some(data),
497 trace_flow_id: Some(trace_flow_id.into()),
498 wake_lease,
499 ..Default::default()
500 };
501
502 fuchsia_trace::flow_begin!("input", "dispatch_event_to_scenic", trace_flow_id);
503
504 event
505 }
506
507 fn display_coordinate_from_contact(
521 contact: &touch_binding::TouchContact,
522 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
523 display_size: &Size,
524 ) -> Position {
525 if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
526 let x_range: f32 =
528 contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
529 let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
530 let x: f32 = (display_size.width * x_wrt_range) / x_range;
531
532 let y_range: f32 =
534 contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
535 let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
536 let y: f32 = (display_size.height * y_wrt_range) / y_range;
537
538 Position { x, y }
539 } else {
540 return contact.position;
541 }
542 }
543
544 pub async fn watch_viewport(self: Rc<Self>) {
546 let configuration_proxy = self.configuration_proxy.clone();
547 let mut viewport_stream = HangingGetStream::new(
548 configuration_proxy,
549 pointerinjector_config::SetupProxy::watch_viewport,
550 );
551 loop {
552 match viewport_stream.next().await {
553 Some(Ok(new_viewport)) => {
554 self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
556
557 let injectors: Vec<pointerinjector::DeviceProxy> =
559 self.mutable_state.borrow_mut().injectors.values().cloned().collect();
560 for injector in injectors {
561 let events = vec![pointerinjector::Event {
562 timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
563 data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
564 trace_flow_id: Some(fuchsia_trace::Id::random().into()),
565 ..Default::default()
566 }];
567 injector.inject_events(events).expect("Failed to inject updated viewport.");
568 }
569 }
570 Some(Err(e)) => {
571 self.metrics_logger.log_error(
572 InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
573 std::format!("Error while reading viewport update: {}", e));
574 return;
575 }
576 None => {
577 self.metrics_logger.log_error(
578 InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
579 "Viewport update stream terminated unexpectedly");
580 return;
581 }
582 }
583 }
584 }
585
586 fn create_touch_buttons_event(
593 event: &mut touch_binding::TouchScreenEvent,
594 event_time: zx::MonotonicInstant,
595 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
596 ) -> fidl_ui_input::TouchButtonsEvent {
597 let pressed_buttons = match event.pressed_buttons.len() {
598 0 => None,
599 _ => Some(
600 event
601 .pressed_buttons
602 .clone()
603 .into_iter()
604 .map(|button| match button {
605 fidl_input_report::TouchButton::Palm => fidl_ui_input::TouchButton::Palm,
606 fidl_input_report::TouchButton::__SourceBreaking { unknown_ordinal: n } => {
607 fidl_ui_input::TouchButton::__SourceBreaking {
608 unknown_ordinal: n as u32,
609 }
610 }
611 })
612 .collect::<Vec<_>>(),
613 ),
614 };
615 fidl_ui_input::TouchButtonsEvent {
616 event_time: Some(event_time),
617 device_info: Some(fidl_ui_input::TouchDeviceInfo {
618 id: Some(touch_descriptor.device_id),
619 ..Default::default()
620 }),
621 pressed_buttons,
622 wake_lease: event.wake_lease.take(),
623 ..Default::default()
624 }
625 }
626
627 async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::TouchButtonsEvent) {
632 let tracker = &self.mutable_state.borrow().send_event_task_tracker;
633
634 for (handle, listener) in &self.mutable_state.borrow().listeners {
635 let weak_handler = Rc::downgrade(&self);
636 let listener_clone = listener.clone();
637 let handle_clone = handle.clone();
638 let event_to_send = Self::clone_event(event);
639 let fut = async move {
640 match listener_clone.on_event(event_to_send).await {
641 Ok(_) => {}
642 Err(e) => {
643 if let Some(handler) = weak_handler.upgrade() {
644 handler.mutable_state.borrow_mut().listeners.remove(&handle_clone);
645 log::info!(
646 "Unregistering listener; unable to send TouchButtonsEvent: {:?}",
647 e
648 )
649 }
650 }
651 }
652 };
653
654 let metrics_logger_clone = self.metrics_logger.clone();
655 tracker.track(metrics_logger_clone, fasync::Task::local(fut));
656 }
657 }
658
659 pub async fn register_listener_proxy(
664 self: &Rc<Self>,
665 proxy: fidl_ui_policy::TouchButtonsListenerProxy,
666 ) {
667 self.mutable_state
668 .borrow_mut()
669 .listeners
670 .insert(proxy.as_channel().as_handle_ref().raw_handle(), proxy.clone());
671
672 if let Some(event) = &self.mutable_state.borrow().last_button_event {
674 let event_to_send = Self::clone_event(event);
675 let fut = async move {
676 match proxy.on_event(event_to_send).await {
677 Ok(_) => {}
678 Err(e) => {
679 log::info!("Failed to send touch buttons event to listener {:?}", e)
680 }
681 }
682 };
683 let metrics_logger_clone = self.metrics_logger.clone();
684 self.mutable_state
685 .borrow()
686 .send_event_task_tracker
687 .track(metrics_logger_clone, fasync::Task::local(fut));
688 }
689 }
690}
691
692#[derive(Debug)]
695pub struct LocalTaskTracker {
696 sender: mpsc::UnboundedSender<fasync::Task<()>>,
697 _receiver_task: fasync::Task<()>,
698}
699
700impl LocalTaskTracker {
701 pub fn new() -> Self {
702 let (sender, receiver) = mpsc::unbounded();
703 let receiver_task = fasync::Task::local(async move {
704 receiver.for_each_concurrent(None, |task: fasync::Task<()>| task).await
706 });
707
708 Self { sender, _receiver_task: receiver_task }
709 }
710
711 pub fn track(&self, metrics_logger: metrics::MetricsLogger, task: fasync::Task<()>) {
713 match self.sender.unbounded_send(task) {
714 Ok(_) => {}
715 Err(e) => {
719 metrics_logger.log_error(
720 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
721 std::format!("Unexpected {e:?} while pushing task"),
722 );
723 }
724 };
725 }
726}
727
728#[cfg(test)]
729mod tests {
730 use super::*;
731 use crate::input_handler::BatchInputHandler;
732 use crate::testing_utilities::{
733 create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
734 create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
735 get_touch_screen_device_descriptor,
736 };
737 use assert_matches::assert_matches;
738 use futures::{FutureExt, TryStreamExt};
739 use maplit::hashmap;
740 use pretty_assertions::assert_eq;
741 use std::collections::HashSet;
742 use std::convert::TryFrom as _;
743 use std::ops::Add;
744 use {
745 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
746 fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
747 };
748
749 const TOUCH_ID: u32 = 1;
750 const DISPLAY_WIDTH: f32 = 100.0;
751 const DISPLAY_HEIGHT: f32 = 100.0;
752
753 struct TestFixtures {
754 touch_handler: Rc<TouchInjectorHandler>,
755 device_listener_proxy: fidl_ui_policy::DeviceListenerRegistryProxy,
756 injector_registry_request_stream: pointerinjector::RegistryRequestStream,
757 configuration_request_stream: pointerinjector_config::SetupRequestStream,
758 inspector: fuchsia_inspect::Inspector,
759 _test_node: fuchsia_inspect::Node,
760 }
761
762 fn spawn_device_listener_registry_server(
763 handler: Rc<TouchInjectorHandler>,
764 ) -> fidl_ui_policy::DeviceListenerRegistryProxy {
765 let (device_listener_proxy, mut device_listener_stream) =
766 fidl::endpoints::create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>(
767 );
768
769 fasync::Task::local(async move {
770 loop {
771 match device_listener_stream.try_next().await {
772 Ok(Some(
773 fidl_ui_policy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
774 listener,
775 responder,
776 },
777 )) => {
778 handler.register_listener_proxy(listener.into_proxy()).await;
779 let _ = responder.send();
780 }
781 Ok(Some(_)) => {
782 panic!("Unexpected registration");
783 }
784 Ok(None) => {
785 break;
786 }
787 Err(e) => {
788 panic!("Error handling device listener registry request stream: {}", e);
789 }
790 }
791 }
792 })
793 .detach();
794
795 device_listener_proxy
796 }
797
798 impl TestFixtures {
799 async fn new() -> Self {
800 let inspector = fuchsia_inspect::Inspector::default();
801 let test_node = inspector.root().create_child("test_node");
802 let (configuration_proxy, mut configuration_request_stream) =
803 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
804 let (injector_registry_proxy, injector_registry_request_stream) =
805 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
806
807 let touch_handler_fut = TouchInjectorHandler::new_handler(
808 configuration_proxy,
809 injector_registry_proxy,
810 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
811 &test_node,
812 metrics::MetricsLogger::default(),
813 );
814
815 let handle_initial_request_fut = async {
816 match configuration_request_stream.next().await {
817 Some(Ok(pointerinjector_config::SetupRequest::GetViewRefs {
818 responder,
819 ..
820 })) => {
821 let context = fuchsia_scenic::ViewRefPair::new()
822 .expect("Failed to create viewrefpair.")
823 .view_ref;
824 let target = fuchsia_scenic::ViewRefPair::new()
825 .expect("Failed to create viewrefpair.")
826 .view_ref;
827 let _ = responder.send(context, target);
828 }
829 other => panic!("Expected GetViewRefs request, got {:?}", other),
830 }
831 };
832
833 let (touch_handler_res, _) =
834 futures::future::join(touch_handler_fut, handle_initial_request_fut).await;
835
836 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
837 let device_listener_proxy =
838 spawn_device_listener_registry_server(touch_handler.clone());
839
840 TestFixtures {
841 touch_handler,
842 device_listener_proxy,
843 injector_registry_request_stream,
844 configuration_request_stream,
845 inspector,
846 _test_node: test_node,
847 }
848 }
849 }
850
851 fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
853 input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
854 device_id: 1,
855 contacts: vec![touch_binding::ContactDeviceDescriptor {
856 x_range: fidl_input_report::Range { min: 0, max: 100 },
857 y_range: fidl_input_report::Range { min: 0, max: 100 },
858 x_unit: fidl_input_report::Unit {
859 type_: fidl_input_report::UnitType::Meters,
860 exponent: -6,
861 },
862 y_unit: fidl_input_report::Unit {
863 type_: fidl_input_report::UnitType::Meters,
864 exponent: -6,
865 },
866 pressure_range: None,
867 width_range: None,
868 height_range: None,
869 }],
870 })
871 }
872
873 async fn handle_device_request_stream(
876 mut injector_stream: pointerinjector::DeviceRequestStream,
877 expected_event: pointerinjector::Event,
878 ) {
879 match injector_stream.next().await {
880 Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
881 panic!("DeviceRequest::Inject is deprecated.");
882 }
883 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
884 assert_eq!(events.len(), 1);
885 assert_eq!(events[0].timestamp, expected_event.timestamp);
886 assert_eq!(events[0].data, expected_event.data);
887 }
888 Some(Err(e)) => panic!("FIDL error {}", e),
889 None => panic!("Expected another event."),
890 }
891 }
892
893 fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
895 pointerinjector::Viewport {
896 extents: Some([[min, min], [max, max]]),
897 viewport_to_context_transform: None,
898 ..Default::default()
899 }
900 }
901
902 #[fuchsia::test]
903 async fn events_with_pressed_buttons_are_sent_to_listener() {
904 let fixtures = TestFixtures::new().await;
905 let (listener, mut listener_stream) =
906 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
907 fixtures
908 .device_listener_proxy
909 .register_touch_buttons_listener(listener)
910 .await
911 .expect("Failed to register listener.");
912
913 let descriptor = get_touch_screen_device_descriptor();
914 let event_time = zx::MonotonicInstant::get();
915 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
916
917 let _ = fixtures.touch_handler.clone().handle_input_events(vec![input_event]).await;
918
919 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
920 event_time: Some(event_time),
921 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
922 ..Default::default()
923 };
924
925 assert_matches!(
926 listener_stream.next().await,
927 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
928 event,
929 responder,
930 })) => {
931 assert_eq!(event, expected_touch_buttons_event);
932 let _ = responder.send();
933 }
934 );
935 }
936
937 #[fuchsia::test]
938 async fn events_with_contacts_are_not_sent_to_listener() {
939 let fixtures = TestFixtures::new().await;
940 let (listener, mut listener_stream) =
941 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
942 fixtures
943 .device_listener_proxy
944 .register_touch_buttons_listener(listener)
945 .await
946 .expect("Failed to register listener.");
947
948 let descriptor = get_touch_screen_device_descriptor();
949 let event_time = zx::MonotonicInstant::get();
950 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
951 let input_event = create_touch_screen_event(
952 hashmap! {
953 fidl_ui_input::PointerEventPhase::Add
954 => vec![contact.clone()],
955 },
956 event_time,
957 &descriptor,
958 );
959
960 let _ = fixtures.touch_handler.clone().handle_input_events(vec![input_event]).await;
961
962 assert!(listener_stream.next().now_or_never().is_none());
963 }
964
965 #[fuchsia::test]
966 async fn multiple_listeners_receive_pressed_button_events() {
967 let fixtures = TestFixtures::new().await;
968 let (first_listener, mut first_listener_stream) =
969 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
970 let (second_listener, mut second_listener_stream) =
971 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
972 fixtures
973 .device_listener_proxy
974 .register_touch_buttons_listener(first_listener)
975 .await
976 .expect("Failed to register listener.");
977 fixtures
978 .device_listener_proxy
979 .register_touch_buttons_listener(second_listener)
980 .await
981 .expect("Failed to register listener.");
982
983 let descriptor = get_touch_screen_device_descriptor();
984 let event_time = zx::MonotonicInstant::get();
985 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
986
987 let _ = fixtures.touch_handler.clone().handle_input_events(vec![input_event]).await;
988
989 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
990 event_time: Some(event_time),
991 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
992 ..Default::default()
993 };
994
995 assert_matches!(
996 first_listener_stream.next().await,
997 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
998 event,
999 responder,
1000 })) => {
1001 assert_eq!(event, expected_touch_buttons_event);
1002 let _ = responder.send();
1003 }
1004 );
1005 assert_matches!(
1006 second_listener_stream.next().await,
1007 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
1008 event,
1009 responder,
1010 })) => {
1011 assert_eq!(event, expected_touch_buttons_event);
1012 let _ = responder.send();
1013 }
1014 );
1015 }
1016
1017 #[fuchsia::test]
1020 async fn receives_viewport_updates() {
1021 let mut fixtures = TestFixtures::new().await;
1022
1023 let (injector_device_proxy, mut injector_device_request_stream) =
1025 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1026 fixtures
1027 .touch_handler
1028 .mutable_state
1029 .borrow_mut()
1030 .injectors
1031 .insert(1, injector_device_proxy);
1032
1033 {
1035 let _watch_viewport_task =
1037 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1038
1039 match fixtures.configuration_request_stream.next().await {
1041 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
1042 responder, ..
1043 })) => {
1044 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1045 }
1046 other => panic!("Received unexpected value: {:?}", other),
1047 };
1048
1049 match injector_device_request_stream.next().await {
1051 Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1052 panic!("DeviceRequest::Inject is deprecated.");
1053 }
1054 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1055 assert_eq!(events.len(), 1);
1056 assert!(events[0].data.is_some());
1057 assert_eq!(
1058 events[0].data,
1059 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1060 );
1061 }
1062 other => panic!("Received unexpected value: {:?}", other),
1063 }
1064
1065 match fixtures.configuration_request_stream.next().await {
1068 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
1069 responder, ..
1070 })) => {
1071 responder
1072 .send(&create_viewport(100.0, 200.0))
1073 .expect("Failed to send viewport.");
1074 }
1075 other => panic!("Received unexpected value: {:?}", other),
1076 };
1077
1078 match injector_device_request_stream.next().await {
1080 Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1081 panic!("DeviceRequest::Inject is deprecated.");
1082 }
1083 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1084 assert_eq!(events.len(), 1);
1085 assert!(events[0].data.is_some());
1086 assert_eq!(
1087 events[0].data,
1088 Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
1089 );
1090 }
1091 other => panic!("Received unexpected value: {:?}", other),
1092 }
1093 }
1094
1095 let expected_viewport = create_viewport(100.0, 200.0);
1097 assert_eq!(fixtures.touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
1098 }
1099
1100 #[fuchsia::test]
1102 async fn add_contact_drops_without_viewport() {
1103 let mut fixtures = TestFixtures::new().await;
1104
1105 let event_time = zx::MonotonicInstant::get();
1107 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1108 let descriptor = get_touch_screen_device_descriptor();
1109 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1110 hashmap! {
1111 fidl_ui_input::PointerEventPhase::Add
1112 => vec![contact.clone()],
1113 },
1114 event_time,
1115 &descriptor,
1116 ))
1117 .unwrap();
1118
1119 fixtures.touch_handler.mutable_state.borrow_mut().viewport = None;
1121
1122 let _ = fixtures.touch_handler.clone().handle_input_events(vec![input_event.into()]).await;
1124
1125 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1127 }
1128
1129 #[fuchsia::test]
1131 async fn add_contact_succeeds_with_viewport() {
1132 let mut fixtures = TestFixtures::new().await;
1133
1134 let (injector_device_proxy, mut injector_device_request_stream) =
1136 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1137 fixtures
1138 .touch_handler
1139 .mutable_state
1140 .borrow_mut()
1141 .injectors
1142 .insert(1, injector_device_proxy);
1143
1144 let _watch_viewport_task =
1146 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1147
1148 match fixtures.configuration_request_stream.next().await {
1150 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1151 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1152 }
1153 other => panic!("Received unexpected value: {:?}", other),
1154 };
1155
1156 match injector_device_request_stream.next().await {
1158 Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1159 panic!("DeviceRequest::Inject is deprecated.");
1160 }
1161 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1162 assert_eq!(events.len(), 1);
1163 assert!(events[0].data.is_some());
1164 assert_eq!(
1165 events[0].data,
1166 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1167 );
1168 }
1169 other => panic!("Received unexpected value: {:?}", other),
1170 }
1171
1172 let event_time = zx::MonotonicInstant::get();
1174 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1175 let descriptor = get_touch_screen_device_descriptor();
1176 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1177 hashmap! {
1178 fidl_ui_input::PointerEventPhase::Add
1179 => vec![contact.clone()],
1180 },
1181 event_time,
1182 &descriptor,
1183 ))
1184 .unwrap();
1185
1186 let handle_event_fut =
1188 fixtures.touch_handler.clone().handle_input_events(vec![input_event.into()]);
1189
1190 let expected_event = create_touch_pointer_sample_event(
1192 pointerinjector::EventPhase::Add,
1193 &contact,
1194 Position { x: 20.0, y: 40.0 },
1195 event_time,
1196 );
1197
1198 let device_fut =
1201 handle_device_request_stream(injector_device_request_stream, expected_event);
1202 let (handle_result, _) = futures::future::join(handle_event_fut, device_fut).await;
1203
1204 assert_matches!(
1206 handle_result.as_slice(),
1207 [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1208 );
1209 }
1210
1211 #[fuchsia::test]
1213 async fn add_touchpad_contact_with_viewport() {
1214 let mut fixtures = TestFixtures::new().await;
1215
1216 let (injector_device_proxy, mut injector_device_request_stream) =
1218 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1219 fixtures
1220 .touch_handler
1221 .mutable_state
1222 .borrow_mut()
1223 .injectors
1224 .insert(1, injector_device_proxy);
1225
1226 let _watch_viewport_task =
1228 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1229
1230 match fixtures.configuration_request_stream.next().await {
1232 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1233 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1234 }
1235 other => panic!("Received unexpected value: {:?}", other),
1236 };
1237
1238 match injector_device_request_stream.next().await {
1240 Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1241 panic!("DeviceRequest::Inject is deprecated.");
1242 }
1243 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1244 assert_eq!(events.len(), 1);
1245 assert!(events[0].data.is_some());
1246 assert_eq!(
1247 events[0].data,
1248 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1249 );
1250 }
1251 other => panic!("Received unexpected value: {:?}", other),
1252 }
1253
1254 let event_time = zx::MonotonicInstant::get();
1256 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1257 let descriptor = get_touchpad_device_descriptor();
1258 let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
1259 vec![contact.clone()],
1260 HashSet::new(),
1261 event_time,
1262 &descriptor,
1263 ))
1264 .unwrap();
1265
1266 let handle_event_fut =
1268 fixtures.touch_handler.clone().handle_input_events(vec![input_event.into()]);
1269
1270 let handle_result = handle_event_fut.await;
1271
1272 assert_matches!(
1274 handle_result.as_slice(),
1275 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
1276 );
1277
1278 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1280 }
1281
1282 #[fuchsia::test(allow_stalls = false)]
1283 async fn touch_injector_handler_initialized_with_inspect_node() {
1284 let fixtures = TestFixtures::new().await;
1285 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1286 test_node: {
1287 touch_injector_handler: {
1288 events_received_count: 0u64,
1289 events_handled_count: 0u64,
1290 last_received_timestamp_ns: 0u64,
1291 "fuchsia.inspect.Health": {
1292 status: "STARTING_UP",
1293 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1296 },
1297 }
1298 }
1299 });
1300 }
1301
1302 #[fuchsia::test(allow_stalls = false)]
1303 async fn touch_injector_handler_inspect_counts_events() {
1304 let fixtures = TestFixtures::new().await;
1305
1306 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1307 let descriptor = get_touch_screen_device_descriptor();
1308 let event_time1 = zx::MonotonicInstant::get();
1309 let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1310 let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1311
1312 let input_events = vec![
1313 create_touch_screen_event(
1314 hashmap! {
1315 fidl_ui_input::PointerEventPhase::Add
1316 => vec![contact.clone()],
1317 },
1318 event_time1,
1319 &descriptor,
1320 ),
1321 create_touch_screen_event(
1322 hashmap! {
1323 fidl_ui_input::PointerEventPhase::Move
1324 => vec![contact.clone()],
1325 },
1326 event_time2,
1327 &descriptor,
1328 ),
1329 create_fake_input_event(event_time2),
1331 create_touch_screen_event_with_handled(
1333 hashmap! {
1334 fidl_ui_input::PointerEventPhase::Move
1335 => vec![contact.clone()],
1336 },
1337 event_time2,
1338 &descriptor,
1339 input_device::Handled::Yes,
1340 ),
1341 create_touch_screen_event(
1342 hashmap! {
1343 fidl_ui_input::PointerEventPhase::Remove
1344 => vec![contact.clone()],
1345 },
1346 event_time3,
1347 &descriptor,
1348 ),
1349 ];
1350
1351 for input_event in input_events {
1352 fixtures.touch_handler.clone().handle_input_events(vec![input_event]).await;
1353 }
1354
1355 let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1356
1357 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1358 test_node: {
1359 touch_injector_handler: {
1360 events_received_count: 3u64,
1361 events_handled_count: 3u64,
1362 last_received_timestamp_ns: last_received_event_time,
1363 "fuchsia.inspect.Health": {
1364 status: "STARTING_UP",
1365 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1368 },
1369 }
1370 }
1371 });
1372 }
1373
1374 #[fuchsia::test]
1375 async fn clone_event_with_lease_duplicates_lease() {
1376 let (event_pair, _) = fidl::EventPair::create();
1377 let event = fidl_ui_input::TouchButtonsEvent {
1378 event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1379 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1380 pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1381 wake_lease: Some(event_pair),
1382 ..Default::default()
1383 };
1384 let cloned_event = TouchInjectorHandler::clone_event(&event);
1385 assert_eq!(event.event_time, cloned_event.event_time);
1386 assert_eq!(event.device_info, cloned_event.device_info);
1387 assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1388 assert!(event.wake_lease.is_some());
1389 assert!(cloned_event.wake_lease.is_some());
1390 assert_ne!(
1391 event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle(),
1392 cloned_event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle()
1393 );
1394 }
1395
1396 #[fuchsia::test]
1397 async fn clone_event_without_lease_has_no_lease() {
1398 let event = fidl_ui_input::TouchButtonsEvent {
1399 event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1400 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1401 pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1402 wake_lease: None,
1403 ..Default::default()
1404 };
1405 let cloned_event = TouchInjectorHandler::clone_event(&event);
1406 assert_eq!(event.event_time, cloned_event.event_time);
1407 assert_eq!(event.device_info, cloned_event.device_info);
1408 assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1409 assert!(event.wake_lease.is_none());
1410 assert!(cloned_event.wake_lease.is_none());
1411 }
1412
1413 #[fuchsia::test]
1414 async fn handle_input_events_batches_events() {
1415 let mut fixtures = TestFixtures::new().await;
1416
1417 let (injector_device_proxy, mut injector_device_request_stream) =
1419 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1420 fixtures
1421 .touch_handler
1422 .mutable_state
1423 .borrow_mut()
1424 .injectors
1425 .insert(1, injector_device_proxy);
1426
1427 let _watch_viewport_task =
1429 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1430
1431 match fixtures.configuration_request_stream.next().await {
1433 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1434 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1435 }
1436 other => panic!("Received unexpected value: {:?}", other),
1437 };
1438
1439 match injector_device_request_stream.next().await {
1441 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1442 assert_eq!(events.len(), 1);
1443 assert!(events[0].data.is_some());
1444 assert_eq!(
1445 events[0].data,
1446 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1447 );
1448 }
1449 other => panic!("Received unexpected value: {:?}", other),
1450 }
1451
1452 let event_time1 = zx::MonotonicInstant::get();
1454 let contact1 = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1455 let descriptor = get_touch_screen_device_descriptor();
1456 let input_event1 = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1457 hashmap! {
1458 fidl_ui_input::PointerEventPhase::Add
1459 => vec![contact1.clone()],
1460 },
1461 event_time1,
1462 &descriptor,
1463 ))
1464 .unwrap();
1465
1466 let event_time2 = event_time1 + zx::MonotonicDuration::from_millis(10);
1467 let contact2 = create_touch_contact(TOUCH_ID, Position { x: 25.0, y: 45.0 });
1468 let input_event2 = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1469 hashmap! {
1470 fidl_ui_input::PointerEventPhase::Move
1471 => vec![contact2.clone()],
1472 },
1473 event_time2,
1474 &descriptor,
1475 ))
1476 .unwrap();
1477
1478 let handle_event_fut = fixtures
1480 .touch_handler
1481 .clone()
1482 .handle_input_events(vec![input_event1.into(), input_event2.into()]);
1483
1484 let expected_event1 = create_touch_pointer_sample_event(
1486 pointerinjector::EventPhase::Add,
1487 &contact1,
1488 Position { x: 20.0, y: 40.0 },
1489 event_time1,
1490 );
1491 let expected_event2 = create_touch_pointer_sample_event(
1492 pointerinjector::EventPhase::Change,
1493 &contact2,
1494 Position { x: 25.0, y: 45.0 },
1495 event_time2,
1496 );
1497
1498 let device_fut = async move {
1499 match injector_device_request_stream.next().await {
1500 Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1501 assert_eq!(events.len(), 2);
1502 assert_eq!(events[0].timestamp, expected_event1.timestamp);
1503 assert_eq!(events[0].data, expected_event1.data);
1504 assert_eq!(events[1].timestamp, expected_event2.timestamp);
1505 assert_eq!(events[1].data, expected_event2.data);
1506 }
1507 other => panic!("Received unexpected value: {:?}", other),
1508 }
1509 };
1510
1511 let (handle_result, _) = futures::future::join(handle_event_fut, device_fut).await;
1512
1513 assert_matches!(
1515 handle_result.as_slice(),
1516 [
1517 input_device::InputEvent { handled: input_device::Handled::Yes, .. },
1518 input_device::InputEvent { handled: input_device::Handled::Yes, .. }
1519 ]
1520 );
1521 }
1522}