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