1use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::utils::{Position, Size};
7use crate::{metrics, mouse_binding};
8use anyhow::{Context, Error, format_err};
9use async_trait::async_trait;
10use fidl_fuchsia_input_report::{InputDeviceProxy, InputReport};
11use fuchsia_inspect::ArrayProperty;
12use fuchsia_inspect::health::Reporter;
13use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
14use zx;
15
16use fidl::HandleBased;
17use fidl_fuchsia_input_report as fidl_input_report;
18use fidl_fuchsia_ui_input as fidl_ui_input;
19use fidl_fuchsia_ui_pointerinjector as pointerinjector;
20use maplit::hashmap;
21use metrics_registry::*;
22use std::collections::{HashMap, HashSet};
23
24#[derive(Debug, PartialEq)]
40pub struct TouchScreenEvent {
41 pub contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
47
48 pub injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
53
54 pub pressed_buttons: Vec<fidl_input_report::TouchButton>,
56
57 pub wake_lease: Option<zx::EventPair>,
59}
60
61impl Clone for TouchScreenEvent {
62 fn clone(&self) -> Self {
63 log::debug!("TouchScreenEvent cloned without wake lease.");
64 Self {
65 contacts: self.contacts.clone(),
66 injector_contacts: self.injector_contacts.clone(),
67 pressed_buttons: self.pressed_buttons.clone(),
68 wake_lease: None,
69 }
70 }
71}
72
73impl Drop for TouchScreenEvent {
74 fn drop(&mut self) {
75 log::debug!("TouchScreenEvent dropped, had_wake_lease: {:?}", self.wake_lease);
76 }
77}
78
79impl TouchScreenEvent {
80 pub fn clone_with_wake_lease(&self) -> Self {
81 log::debug!("TouchScreenEvent cloned with wake lease: {:?}", self.wake_lease);
82 Self {
83 contacts: self.contacts.clone(),
84 injector_contacts: self.injector_contacts.clone(),
85 pressed_buttons: self.pressed_buttons.clone(),
86 wake_lease: self.wake_lease.as_ref().map(|lease| {
87 lease
88 .duplicate_handle(zx::Rights::SAME_RIGHTS)
89 .expect("failed to duplicate event pair")
90 }),
91 }
92 }
93
94 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
95 let contacts_clone = self.injector_contacts.clone();
96 node.record_child("injector_contacts", move |contacts_node| {
97 for (phase, contacts) in contacts_clone.iter() {
98 let phase_str = match phase {
99 pointerinjector::EventPhase::Add => "add",
100 pointerinjector::EventPhase::Change => "change",
101 pointerinjector::EventPhase::Remove => "remove",
102 pointerinjector::EventPhase::Cancel => "cancel",
103 };
104 contacts_node.record_child(phase_str, move |phase_node| {
105 for contact in contacts.iter() {
106 phase_node.record_child(contact.id.to_string(), move |contact_node| {
107 contact_node
108 .record_double("position_x_mm", f64::from(contact.position.x));
109 contact_node
110 .record_double("position_y_mm", f64::from(contact.position.y));
111 if let Some(pressure) = contact.pressure {
112 contact_node.record_int("pressure", pressure);
113 }
114 if let Some(contact_size) = contact.contact_size {
115 contact_node.record_double(
116 "contact_width_mm",
117 f64::from(contact_size.width),
118 );
119 contact_node.record_double(
120 "contact_height_mm",
121 f64::from(contact_size.height),
122 );
123 }
124 });
125 }
126 });
127 }
128 });
129
130 let pressed_buttons_node =
131 node.create_string_array("pressed_buttons", self.pressed_buttons.len());
132 self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
133 let button_name: String = match button {
134 fidl_input_report::TouchButton::Palm => "palm".into(),
135 unknown_value => {
136 format!("unknown({:?})", unknown_value)
137 }
138 };
139 pressed_buttons_node.set(i, &button_name);
140 });
141 node.record(pressed_buttons_node);
142 }
143}
144
145#[derive(Clone, Debug, PartialEq)]
150pub struct TouchpadEvent {
151 pub injector_contacts: Vec<TouchContact>,
154
155 pub pressed_buttons: HashSet<mouse_binding::MouseButton>,
157}
158
159impl TouchpadEvent {
160 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
161 let pressed_buttons_node =
162 node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
163 self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
164 pressed_buttons_node.set(i, *button);
165 });
166 node.record(pressed_buttons_node);
167
168 let contacts_clone = self.injector_contacts.clone();
170 node.record_child("injector_contacts", move |contacts_node| {
171 for contact in contacts_clone.iter() {
172 contacts_node.record_child(contact.id.to_string(), move |contact_node| {
173 contact_node.record_double("position_x_mm", f64::from(contact.position.x));
174 contact_node.record_double("position_y_mm", f64::from(contact.position.y));
175 if let Some(pressure) = contact.pressure {
176 contact_node.record_int("pressure", pressure);
177 }
178 if let Some(contact_size) = contact.contact_size {
179 contact_node
180 .record_double("contact_width_mm", f64::from(contact_size.width));
181 contact_node
182 .record_double("contact_height_mm", f64::from(contact_size.height));
183 }
184 })
185 }
186 });
187 }
188}
189
190#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193pub enum TouchDeviceType {
194 TouchScreen,
195 WindowsPrecisionTouchpad,
196}
197
198#[derive(Clone, Copy, Debug, PartialEq)]
201pub struct TouchContact {
202 pub id: u32,
204
205 pub position: Position,
208
209 pub pressure: Option<i64>,
212
213 pub contact_size: Option<Size>,
216}
217
218impl Eq for TouchContact {}
219
220impl From<&fidl_fuchsia_input_report::ContactInputReport> for TouchContact {
221 fn from(fidl_contact: &fidl_fuchsia_input_report::ContactInputReport) -> TouchContact {
222 let contact_size =
223 if fidl_contact.contact_width.is_some() && fidl_contact.contact_height.is_some() {
224 Some(Size {
225 width: fidl_contact.contact_width.unwrap() as f32,
226 height: fidl_contact.contact_height.unwrap() as f32,
227 })
228 } else {
229 None
230 };
231
232 TouchContact {
233 id: fidl_contact.contact_id.unwrap_or_default(),
234 position: Position {
235 x: fidl_contact.position_x.unwrap_or_default() as f32,
236 y: fidl_contact.position_y.unwrap_or_default() as f32,
237 },
238 pressure: fidl_contact.pressure,
239 contact_size,
240 }
241 }
242}
243
244#[derive(Clone, Debug, Eq, PartialEq)]
245pub struct TouchScreenDeviceDescriptor {
246 pub device_id: u32,
248
249 pub contacts: Vec<ContactDeviceDescriptor>,
251}
252
253#[derive(Clone, Debug, Eq, PartialEq)]
254pub struct TouchpadDeviceDescriptor {
255 pub device_id: u32,
257
258 pub contacts: Vec<ContactDeviceDescriptor>,
260}
261
262#[derive(Clone, Debug, Eq, PartialEq)]
263enum TouchDeviceDescriptor {
264 TouchScreen(TouchScreenDeviceDescriptor),
265 Touchpad(TouchpadDeviceDescriptor),
266}
267
268#[derive(Clone, Debug, Eq, PartialEq)]
282pub struct ContactDeviceDescriptor {
283 pub x_range: fidl_input_report::Range,
285
286 pub y_range: fidl_input_report::Range,
288
289 pub x_unit: fidl_input_report::Unit,
291
292 pub y_unit: fidl_input_report::Unit,
294
295 pub pressure_range: Option<fidl_input_report::Range>,
297
298 pub width_range: Option<fidl_input_report::Range>,
300
301 pub height_range: Option<fidl_input_report::Range>,
303}
304
305pub struct TouchBinding {
312 event_sender: UnboundedSender<Vec<InputEvent>>,
314
315 device_descriptor: TouchDeviceDescriptor,
317
318 touch_device_type: TouchDeviceType,
320
321 device_proxy: InputDeviceProxy,
323}
324
325#[async_trait]
326impl input_device::InputDeviceBinding for TouchBinding {
327 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
328 self.event_sender.clone()
329 }
330
331 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
332 match self.device_descriptor.clone() {
333 TouchDeviceDescriptor::TouchScreen(desc) => {
334 input_device::InputDeviceDescriptor::TouchScreen(desc)
335 }
336 TouchDeviceDescriptor::Touchpad(desc) => {
337 input_device::InputDeviceDescriptor::Touchpad(desc)
338 }
339 }
340 }
341}
342
343impl TouchBinding {
344 pub async fn new(
359 device_proxy: InputDeviceProxy,
360 device_id: u32,
361 input_event_sender: UnboundedSender<Vec<InputEvent>>,
362 device_node: fuchsia_inspect::Node,
363 feature_flags: input_device::InputPipelineFeatureFlags,
364 metrics_logger: metrics::MetricsLogger,
365 ) -> Result<Self, Error> {
366 let (device_binding, mut inspect_status) =
367 Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
368 .await?;
369 device_binding
370 .set_touchpad_mode(true)
371 .await
372 .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
373 inspect_status.health_node.set_ok();
374 input_device::initialize_report_stream(
375 device_proxy,
376 device_binding.get_device_descriptor(),
377 device_binding.input_event_sender(),
378 inspect_status,
379 metrics_logger,
380 feature_flags,
381 Self::process_reports,
382 );
383
384 Ok(device_binding)
385 }
386
387 async fn bind_device(
399 device_proxy: InputDeviceProxy,
400 device_id: u32,
401 input_event_sender: UnboundedSender<Vec<InputEvent>>,
402 device_node: fuchsia_inspect::Node,
403 ) -> Result<(Self, InputDeviceStatus), Error> {
404 let mut input_device_status = InputDeviceStatus::new(device_node);
405 let device_descriptor: fidl_input_report::DeviceDescriptor = match device_proxy
406 .get_descriptor()
407 .await
408 {
409 Ok(descriptor) => descriptor,
410 Err(_) => {
411 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
412 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
413 }
414 };
415
416 let touch_device_type = get_device_type(&device_proxy).await;
417
418 match device_descriptor.touch {
419 Some(fidl_fuchsia_input_report::TouchDescriptor {
420 input:
421 Some(fidl_fuchsia_input_report::TouchInputDescriptor {
422 contacts: Some(contact_descriptors),
423 max_contacts: _,
424 touch_type: _,
425 buttons: _,
426 ..
427 }),
428 ..
429 }) => Ok((
430 TouchBinding {
431 event_sender: input_event_sender,
432 device_descriptor: match touch_device_type {
433 TouchDeviceType::TouchScreen => {
434 TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
435 device_id,
436 contacts: contact_descriptors
437 .iter()
438 .map(TouchBinding::parse_contact_descriptor)
439 .filter_map(Result::ok)
440 .collect(),
441 })
442 }
443 TouchDeviceType::WindowsPrecisionTouchpad => {
444 TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
445 device_id,
446 contacts: contact_descriptors
447 .iter()
448 .map(TouchBinding::parse_contact_descriptor)
449 .filter_map(Result::ok)
450 .collect(),
451 })
452 }
453 },
454 touch_device_type,
455 device_proxy,
456 },
457 input_device_status,
458 )),
459 descriptor => {
460 input_device_status
461 .health_node
462 .set_unhealthy("Touch Device Descriptor failed to parse.");
463 Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
464 }
465 }
466 }
467
468 async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
469 match self.touch_device_type {
470 TouchDeviceType::TouchScreen => Ok(()),
471 TouchDeviceType::WindowsPrecisionTouchpad => {
472 let mut report = match self.device_proxy.get_feature_report().await? {
475 Ok(report) => report,
476 Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
477 };
478 let mut touch =
479 report.touch.unwrap_or_else(fidl_input_report::TouchFeatureReport::default);
480 touch.input_mode = match enable {
481 true => Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
482 false => Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection),
483 };
484 report.touch = Some(touch);
485 match self.device_proxy.set_feature_report(&report).await? {
486 Ok(()) => {
487 log::info!("touchpad: set touchpad_enabled to {}", enable);
489 Ok(())
490 }
491 Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
492 }
493 }
494 }
495 }
496
497 fn process_reports(
519 reports: Vec<InputReport>,
520 previous_report: Option<InputReport>,
521 device_descriptor: &input_device::InputDeviceDescriptor,
522 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
523 inspect_status: &InputDeviceStatus,
524 metrics_logger: &metrics::MetricsLogger,
525 feature_flags: &input_device::InputPipelineFeatureFlags,
526 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
527 fuchsia_trace::duration!(
528 "input",
529 "touch-binding-process-report",
530 "num_reports" => reports.len(),
531 );
532 match device_descriptor {
533 input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
534 reports,
535 previous_report,
536 device_descriptor,
537 input_event_sender,
538 inspect_status,
539 metrics_logger,
540 feature_flags.enable_merge_touch_events,
541 ),
542 input_device::InputDeviceDescriptor::Touchpad(_) => process_touchpad_reports(
543 reports,
544 previous_report,
545 device_descriptor,
546 input_event_sender,
547 inspect_status,
548 metrics_logger,
549 ),
550 _ => (previous_report, None),
551 }
552 }
553
554 fn parse_contact_descriptor(
562 contact_device_descriptor: &fidl_input_report::ContactInputDescriptor,
563 ) -> Result<ContactDeviceDescriptor, Error> {
564 match contact_device_descriptor {
565 fidl_input_report::ContactInputDescriptor {
566 position_x: Some(x_axis),
567 position_y: Some(y_axis),
568 pressure: pressure_axis,
569 contact_width: width_axis,
570 contact_height: height_axis,
571 ..
572 } => Ok(ContactDeviceDescriptor {
573 x_range: x_axis.range,
574 y_range: y_axis.range,
575 x_unit: x_axis.unit,
576 y_unit: y_axis.unit,
577 pressure_range: pressure_axis.map(|axis| axis.range),
578 width_range: width_axis.map(|axis| axis.range),
579 height_range: height_axis.map(|axis| axis.range),
580 }),
581 descriptor => {
582 Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
583 }
584 }
585 }
586}
587
588fn is_move_only(event: &InputEvent) -> bool {
589 matches!(
590 &event.device_event,
591 input_device::InputDeviceEvent::TouchScreen(event)
592 if event
593 .injector_contacts
594 .get(&pointerinjector::EventPhase::Add)
595 .map_or(true, |c| c.is_empty())
596 && event
597 .injector_contacts
598 .get(&pointerinjector::EventPhase::Remove)
599 .map_or(true, |c| c.is_empty())
600 && event
601 .injector_contacts
602 .get(&pointerinjector::EventPhase::Cancel)
603 .map_or(true, |c| c.is_empty())
604 )
605}
606
607fn has_pressed_buttons(event: &InputEvent) -> bool {
608 match &event.device_event {
609 input_device::InputDeviceEvent::TouchScreen(event) => !event.pressed_buttons.is_empty(),
610 _ => false,
611 }
612}
613
614fn process_touch_screen_reports(
615 reports: Vec<InputReport>,
616 mut previous_report: Option<InputReport>,
617 device_descriptor: &input_device::InputDeviceDescriptor,
618 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
619 inspect_status: &InputDeviceStatus,
620 metrics_logger: &metrics::MetricsLogger,
621 enable_merge_touch_events: bool,
622) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
623 let num_reports = reports.len();
624 let mut batch: Vec<InputEvent> = Vec::with_capacity(num_reports);
625 for report in reports {
626 inspect_status.count_received_report(&report);
627 let (prev_report, event) = process_single_touch_screen_report(
628 report,
629 previous_report,
630 device_descriptor,
631 inspect_status,
632 );
633 previous_report = prev_report;
634 if let Some(event) = event {
635 batch.push(event);
636 }
637 }
638
639 if !batch.is_empty() {
640 if enable_merge_touch_events {
641 let mut is_event_move_only: Vec<bool> = Vec::with_capacity(batch.len());
643 let mut pressed_buttons: Vec<bool> = Vec::with_capacity(batch.len());
644 for event in &batch {
645 is_event_move_only.push(is_move_only(event));
646 pressed_buttons.push(has_pressed_buttons(event));
647 }
648 let size_of_batch = batch.len();
649
650 let mut merged_batch = Vec::with_capacity(size_of_batch);
652
653 for (i, current_event) in batch.into_iter().enumerate() {
655 let current_is_move = is_event_move_only[i];
656 let current_pressed_buttons = pressed_buttons[i];
657 let is_last_event = i == size_of_batch - 1;
658
659 let next_is_move =
661 if i + 1 < size_of_batch { is_event_move_only[i + 1] } else { false };
662
663 let next_pressed_buttons = if i + 1 < size_of_batch {
664 pressed_buttons[i + 1]
665 } else {
666 current_pressed_buttons
667 };
668
669 if !is_last_event
672 && (current_is_move && next_is_move)
674 && (current_pressed_buttons == next_pressed_buttons)
676 {
677 continue;
678 }
679
680 merged_batch.push(current_event);
681 }
682
683 batch = merged_batch;
684 }
685
686 let events_to_send: Vec<InputEvent> = batch
687 .iter()
688 .map(|e| {
689 let event = e.clone_with_wake_lease();
690 let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
693 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
694 event
695 })
696 .collect();
697 fuchsia_trace::instant!(
698 "input",
699 "events_to_input_handlers",
700 fuchsia_trace::Scope::Thread,
701 "num_reports" => num_reports,
702 "num_events_generated" => events_to_send.len()
703 );
704 match input_event_sender.unbounded_send(events_to_send) {
705 Err(e) => {
706 metrics_logger.log_error(
707 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
708 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
709 );
710 }
711 _ => {
712 inspect_status.count_generated_events(&batch);
713 }
714 }
715 }
716 (previous_report, None)
717}
718
719fn process_single_touch_screen_report(
720 mut report: InputReport,
721 previous_report: Option<InputReport>,
722 device_descriptor: &input_device::InputDeviceDescriptor,
723 inspect_status: &InputDeviceStatus,
724) -> (Option<InputReport>, Option<InputEvent>) {
725 fuchsia_trace::flow_end!("input", "input_report", report.trace_id.unwrap_or(0).into());
726
727 let wake_lease = report.wake_lease.take();
731
732 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
734 Some(touch) => touch,
735 None => {
736 inspect_status.count_filtered_report();
737 return (previous_report, None);
738 }
739 };
740
741 let (previous_contacts, previous_buttons): (
742 HashMap<u32, TouchContact>,
743 Vec<fidl_fuchsia_input_report::TouchButton>,
744 ) = previous_report
745 .as_ref()
746 .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
747 .map(touch_contacts_and_buttons_from_touch_report)
748 .unwrap_or_default();
749 let (current_contacts, current_buttons): (
750 HashMap<u32, TouchContact>,
751 Vec<fidl_fuchsia_input_report::TouchButton>,
752 ) = touch_contacts_and_buttons_from_touch_report(touch_report);
753
754 if previous_contacts.is_empty()
756 && current_contacts.is_empty()
757 && previous_buttons.is_empty()
758 && current_buttons.is_empty()
759 {
760 inspect_status.count_filtered_report();
761 return (Some(report), None);
762 }
763
764 if current_contacts.is_empty()
767 && !previous_contacts.is_empty()
768 && (!current_buttons.is_empty() || !previous_buttons.is_empty())
769 {
770 if let Some(touch_report) = report.touch.as_mut() {
771 touch_report.contacts =
772 previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
773 }
774 }
775
776 let added_contacts: Vec<TouchContact> = Vec::from_iter(
778 current_contacts
779 .values()
780 .cloned()
781 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
782 );
783 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
785 current_contacts
786 .values()
787 .cloned()
788 .filter(|contact| previous_contacts.contains_key(&contact.id)),
789 );
790 let removed_contacts: Vec<TouchContact> =
792 Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
793 current_buttons.is_empty()
794 && previous_buttons.is_empty()
795 && !current_contacts.contains_key(&contact.id)
796 }));
797
798 let trace_id = fuchsia_trace::Id::random();
799 let event = create_touch_screen_event(
800 hashmap! {
801 fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
802 fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
803 fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
804 fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
805 fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
806 },
807 hashmap! {
808 pointerinjector::EventPhase::Add => added_contacts,
809 pointerinjector::EventPhase::Change => moved_contacts,
810 pointerinjector::EventPhase::Remove => removed_contacts,
811 },
812 current_buttons,
813 device_descriptor,
814 trace_id,
815 wake_lease,
816 );
817
818 (Some(report), Some(event))
819}
820
821fn process_touchpad_reports(
822 reports: Vec<InputReport>,
823 mut previous_report: Option<InputReport>,
824 device_descriptor: &input_device::InputDeviceDescriptor,
825 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
826 inspect_status: &InputDeviceStatus,
827 metrics_logger: &metrics::MetricsLogger,
828) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
829 for report in reports {
830 inspect_status.count_received_report(&report);
831 let prev_report = process_single_touchpad_report(
832 report,
833 previous_report,
834 device_descriptor,
835 input_event_sender,
836 inspect_status,
837 metrics_logger,
838 );
839 previous_report = prev_report;
840 }
841 (previous_report, None)
842}
843
844fn process_single_touchpad_report(
845 report: InputReport,
846 previous_report: Option<InputReport>,
847 device_descriptor: &input_device::InputDeviceDescriptor,
848 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
849 inspect_status: &InputDeviceStatus,
850 metrics_logger: &metrics::MetricsLogger,
851) -> Option<InputReport> {
852 if let Some(trace_id) = report.trace_id {
853 fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
854 }
855
856 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
858 Some(touch) => touch,
859 None => {
860 inspect_status.count_filtered_report();
861 return previous_report;
862 }
863 };
864
865 let current_contacts: Vec<TouchContact> = touch_report
866 .contacts
867 .as_ref()
868 .and_then(|unwrapped_contacts| {
869 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
871 })
872 .unwrap_or_default();
873
874 let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
875 Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
876 fidl_fuchsia_input_report::TouchButton::Palm => Some(1),
877 fidl_fuchsia_input_report::TouchButton::SwipeUp
878 | fidl_fuchsia_input_report::TouchButton::SwipeLeft
879 | fidl_fuchsia_input_report::TouchButton::SwipeRight
880 | fidl_fuchsia_input_report::TouchButton::SwipeDown => {
881 log::warn!("Swipe buttons {:?} are not supported", button);
883 None
884 }
885 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal } => {
886 log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
887 None
888 }
889 })),
890 None => HashSet::new(),
891 };
892
893 let trace_id = fuchsia_trace::Id::random();
894 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
895 send_touchpad_event(
896 current_contacts,
897 buttons,
898 device_descriptor,
899 input_event_sender,
900 trace_id,
901 inspect_status,
902 metrics_logger,
903 );
904
905 Some(report)
906}
907
908fn touch_contacts_and_buttons_from_touch_report(
909 touch_report: &fidl_fuchsia_input_report::TouchInputReport,
910) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
911 let contacts: Vec<TouchContact> = touch_report
913 .contacts
914 .as_ref()
915 .and_then(|unwrapped_contacts| {
916 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
918 })
919 .unwrap_or_default();
920
921 (
922 contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
923 touch_report.pressed_buttons.clone().unwrap_or_default(),
924 )
925}
926
927fn create_touch_screen_event(
937 contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
938 injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
939 pressed_buttons: Vec<fidl_input_report::TouchButton>,
940 device_descriptor: &input_device::InputDeviceDescriptor,
941 trace_id: fuchsia_trace::Id,
942 wake_lease: Option<zx::EventPair>,
943) -> InputEvent {
944 input_device::InputEvent {
945 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
946 contacts,
947 injector_contacts,
948 pressed_buttons,
949 wake_lease,
950 }),
951 device_descriptor: device_descriptor.clone(),
952 event_time: zx::MonotonicInstant::get(),
953 handled: Handled::No,
954 trace_id: Some(trace_id),
955 }
956}
957
958fn send_touchpad_event(
966 injector_contacts: Vec<TouchContact>,
967 pressed_buttons: HashSet<mouse_binding::MouseButton>,
968 device_descriptor: &input_device::InputDeviceDescriptor,
969 input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
970 trace_id: fuchsia_trace::Id,
971 inspect_status: &InputDeviceStatus,
972 metrics_logger: &metrics::MetricsLogger,
973) {
974 let event = input_device::InputEvent {
975 device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
976 injector_contacts,
977 pressed_buttons,
978 }),
979 device_descriptor: device_descriptor.clone(),
980 event_time: zx::MonotonicInstant::get(),
981 handled: Handled::No,
982 trace_id: Some(trace_id),
983 };
984
985 match input_event_sender.unbounded_send(vec![event.clone()]) {
986 Err(e) => {
987 metrics_logger.log_error(
988 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
989 std::format!("Failed to send TouchpadEvent with error: {:?}", e),
990 );
991 }
992 _ => inspect_status.count_generated_event(event),
993 }
994}
995
996async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
1002 match input_device.get_feature_report().await {
1003 Ok(Ok(fidl_input_report::FeatureReport {
1004 touch:
1005 Some(fidl_input_report::TouchFeatureReport {
1006 input_mode:
1007 Some(
1008 fidl_input_report::TouchConfigurationInputMode::MouseCollection
1009 | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
1010 ),
1011 ..
1012 }),
1013 ..
1014 })) => TouchDeviceType::WindowsPrecisionTouchpad,
1015 _ => TouchDeviceType::TouchScreen,
1016 }
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::*;
1022 use crate::testing_utilities::{
1023 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1024 create_touch_screen_event_with_buttons, create_touchpad_event,
1025 };
1026 use crate::utils::Position;
1027 use assert_matches::assert_matches;
1028 use diagnostics_assertions::AnyProperty;
1029 use fidl_test_util::spawn_stream_handler;
1030 use fuchsia_async as fasync;
1031 use futures::StreamExt;
1032 use pretty_assertions::assert_eq;
1033 use test_case::test_case;
1034
1035 #[fasync::run_singlethreaded(test)]
1036 async fn process_empty_reports() {
1037 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1038 let previous_report = create_touch_input_report(
1039 vec![],
1040 None,
1041 previous_report_time,
1042 );
1043 let report_time = zx::MonotonicInstant::get().into_nanos();
1044 let report =
1045 create_touch_input_report(vec![], None, report_time);
1046
1047 let descriptor =
1048 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1049 device_id: 1,
1050 contacts: vec![],
1051 });
1052 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1053
1054 let inspector = fuchsia_inspect::Inspector::default();
1055 let test_node = inspector.root().create_child("TestDevice_Touch");
1056 let mut inspect_status = InputDeviceStatus::new(test_node);
1057 inspect_status.health_node.set_ok();
1058
1059 let (returned_report, _) = TouchBinding::process_reports(
1060 vec![report],
1061 Some(previous_report),
1062 &descriptor,
1063 &mut event_sender,
1064 &inspect_status,
1065 &metrics::MetricsLogger::default(),
1066 &input_device::InputPipelineFeatureFlags::default(),
1067 );
1068 assert!(returned_report.is_some());
1069 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1070
1071 let event = event_receiver.try_next();
1073 assert!(event.is_err());
1074
1075 diagnostics_assertions::assert_data_tree!(inspector, root: {
1076 "TestDevice_Touch": contains {
1077 reports_received_count: 1u64,
1078 reports_filtered_count: 1u64,
1079 events_generated: 0u64,
1080 last_received_timestamp_ns: report_time as u64,
1081 last_generated_timestamp_ns: 0u64,
1082 "fuchsia.inspect.Health": {
1083 status: "OK",
1084 start_timestamp_nanos: AnyProperty
1087 },
1088 }
1089 });
1090 }
1091
1092 #[fasync::run_singlethreaded(test)]
1094 async fn add_and_down() {
1095 const TOUCH_ID: u32 = 2;
1096
1097 let descriptor =
1098 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1099 device_id: 1,
1100 contacts: vec![],
1101 });
1102 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1103
1104 let contact = fidl_fuchsia_input_report::ContactInputReport {
1105 contact_id: Some(TOUCH_ID),
1106 position_x: Some(0),
1107 position_y: Some(0),
1108 pressure: None,
1109 contact_width: None,
1110 contact_height: None,
1111 ..Default::default()
1112 };
1113 let reports = vec![create_touch_input_report(
1114 vec![contact],
1115 None,
1116 event_time_i64,
1117 )];
1118
1119 let expected_events = vec![create_touch_screen_event(
1120 hashmap! {
1121 fidl_ui_input::PointerEventPhase::Add
1122 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1123 fidl_ui_input::PointerEventPhase::Down
1124 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1125 },
1126 event_time_u64,
1127 &descriptor,
1128 )];
1129
1130 assert_input_report_sequence_generates_events!(
1131 input_reports: reports,
1132 expected_events: expected_events,
1133 device_descriptor: descriptor,
1134 device_type: TouchBinding,
1135 );
1136 }
1137
1138 #[fasync::run_singlethreaded(test)]
1140 async fn up_and_remove() {
1141 const TOUCH_ID: u32 = 2;
1142
1143 let descriptor =
1144 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1145 device_id: 1,
1146 contacts: vec![],
1147 });
1148 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1149
1150 let contact = fidl_fuchsia_input_report::ContactInputReport {
1151 contact_id: Some(TOUCH_ID),
1152 position_x: Some(0),
1153 position_y: Some(0),
1154 pressure: None,
1155 contact_width: None,
1156 contact_height: None,
1157 ..Default::default()
1158 };
1159 let reports = vec![
1160 create_touch_input_report(
1161 vec![contact],
1162 None,
1163 event_time_i64,
1164 ),
1165 create_touch_input_report(vec![], None, event_time_i64),
1166 ];
1167
1168 let expected_events = vec![
1169 create_touch_screen_event(
1170 hashmap! {
1171 fidl_ui_input::PointerEventPhase::Add
1172 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1173 fidl_ui_input::PointerEventPhase::Down
1174 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1175 },
1176 event_time_u64,
1177 &descriptor,
1178 ),
1179 create_touch_screen_event(
1180 hashmap! {
1181 fidl_ui_input::PointerEventPhase::Up
1182 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1183 fidl_ui_input::PointerEventPhase::Remove
1184 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1185 },
1186 event_time_u64,
1187 &descriptor,
1188 ),
1189 ];
1190
1191 assert_input_report_sequence_generates_events!(
1192 input_reports: reports,
1193 expected_events: expected_events,
1194 device_descriptor: descriptor,
1195 device_type: TouchBinding,
1196 );
1197 }
1198
1199 #[fasync::run_singlethreaded(test)]
1201 async fn add_down_move() {
1202 const TOUCH_ID: u32 = 2;
1203 let first = Position { x: 10.0, y: 30.0 };
1204 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1205
1206 let descriptor =
1207 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1208 device_id: 1,
1209 contacts: vec![],
1210 });
1211 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1212
1213 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1214 contact_id: Some(TOUCH_ID),
1215 position_x: Some(first.x as i64),
1216 position_y: Some(first.y as i64),
1217 pressure: None,
1218 contact_width: None,
1219 contact_height: None,
1220 ..Default::default()
1221 };
1222 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1223 contact_id: Some(TOUCH_ID),
1224 position_x: Some(first.x as i64 * 2),
1225 position_y: Some(first.y as i64 * 2),
1226 pressure: None,
1227 contact_width: None,
1228 contact_height: None,
1229 ..Default::default()
1230 };
1231
1232 let reports = vec![
1233 create_touch_input_report(
1234 vec![first_contact],
1235 None,
1236 event_time_i64,
1237 ),
1238 create_touch_input_report(
1239 vec![second_contact],
1240 None,
1241 event_time_i64,
1242 ),
1243 ];
1244
1245 let expected_events = vec![
1246 create_touch_screen_event(
1247 hashmap! {
1248 fidl_ui_input::PointerEventPhase::Add
1249 => vec![create_touch_contact(TOUCH_ID, first)],
1250 fidl_ui_input::PointerEventPhase::Down
1251 => vec![create_touch_contact(TOUCH_ID, first)],
1252 },
1253 event_time_u64,
1254 &descriptor,
1255 ),
1256 create_touch_screen_event(
1257 hashmap! {
1258 fidl_ui_input::PointerEventPhase::Move
1259 => vec![create_touch_contact(TOUCH_ID, second)],
1260 },
1261 event_time_u64,
1262 &descriptor,
1263 ),
1264 ];
1265
1266 assert_input_report_sequence_generates_events!(
1267 input_reports: reports,
1268 expected_events: expected_events,
1269 device_descriptor: descriptor,
1270 device_type: TouchBinding,
1271 );
1272 }
1273
1274 #[fasync::run_singlethreaded(test)]
1275 async fn sent_event_has_trace_id() {
1276 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1277 let previous_report = create_touch_input_report(
1278 vec![],
1279 None,
1280 previous_report_time,
1281 );
1282
1283 let report_time = zx::MonotonicInstant::get().into_nanos();
1284 let contact = fidl_fuchsia_input_report::ContactInputReport {
1285 contact_id: Some(222),
1286 position_x: Some(333),
1287 position_y: Some(444),
1288 ..Default::default()
1289 };
1290 let report =
1291 create_touch_input_report(vec![contact], None, report_time);
1292
1293 let descriptor =
1294 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1295 device_id: 1,
1296 contacts: vec![],
1297 });
1298 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1299
1300 let inspector = fuchsia_inspect::Inspector::default();
1301 let test_node = inspector.root().create_child("TestDevice_Touch");
1302 let mut inspect_status = InputDeviceStatus::new(test_node);
1303 inspect_status.health_node.set_ok();
1304
1305 let _ = TouchBinding::process_reports(
1306 vec![report],
1307 Some(previous_report),
1308 &descriptor,
1309 &mut event_sender,
1310 &inspect_status,
1311 &metrics::MetricsLogger::default(),
1312 &input_device::InputPipelineFeatureFlags::default(),
1313 );
1314 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1315 }
1316
1317 #[fuchsia::test(allow_stalls = false)]
1318 async fn enables_touchpad_mode_automatically() {
1319 let (set_feature_report_sender, set_feature_report_receiver) =
1320 futures::channel::mpsc::unbounded();
1321 let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1322 let set_feature_report_sender = set_feature_report_sender.clone();
1323 async move {
1324 match input_device_request {
1325 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1326 let _ = responder.send(&get_touchpad_device_descriptor(
1327 true, ));
1329 }
1330 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1331 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1332 touch: Some(fidl_input_report::TouchFeatureReport {
1333 input_mode: Some(
1334 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1335 ),
1336 ..Default::default()
1337 }),
1338 ..Default::default()
1339 }));
1340 }
1341 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1342 responder,
1343 report,
1344 } => {
1345 match set_feature_report_sender.unbounded_send(report) {
1346 Ok(_) => {
1347 let _ = responder.send(Ok(()));
1348 }
1349 Err(e) => {
1350 panic!("try_send set_feature_report_request failed: {}", e);
1351 }
1352 };
1353 }
1354 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1355 }
1357 r => panic!("unsupported request {:?}", r),
1358 }
1359 }
1360 });
1361
1362 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1363
1364 let inspector = fuchsia_inspect::Inspector::default();
1366 let test_node = inspector.root().create_child("test_node");
1367
1368 TouchBinding::new(
1372 input_device_proxy,
1373 0,
1374 device_event_sender,
1375 test_node,
1376 input_device::InputPipelineFeatureFlags::default(),
1377 metrics::MetricsLogger::default(),
1378 )
1379 .await
1380 .unwrap();
1381 assert_matches!(
1382 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1383 [fidl_input_report::FeatureReport {
1384 touch: Some(fidl_input_report::TouchFeatureReport {
1385 input_mode: Some(
1386 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1387 ),
1388 ..
1389 }),
1390 ..
1391 }]
1392 );
1393 }
1394
1395 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1396 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1397 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1398 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1399 #[fuchsia::test(allow_stalls = false)]
1400 async fn identifies_correct_touch_device_type(
1401 has_mouse_descriptor: bool,
1402 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1403 expect_touch_device_type: TouchDeviceType,
1404 ) {
1405 let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1406 match input_device_request {
1407 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1408 let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1409 }
1410 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1411 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1412 touch: Some(fidl_input_report::TouchFeatureReport {
1413 input_mode: touch_input_mode,
1414 ..Default::default()
1415 }),
1416 ..Default::default()
1417 }));
1418 }
1419 fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1420 let _ = responder.send(Ok(()));
1421 }
1422 r => panic!("unsupported request {:?}", r),
1423 }
1424 });
1425
1426 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1427
1428 let inspector = fuchsia_inspect::Inspector::default();
1430 let test_node = inspector.root().create_child("test_node");
1431
1432 let binding = TouchBinding::new(
1433 input_device_proxy,
1434 0,
1435 device_event_sender,
1436 test_node,
1437 input_device::InputPipelineFeatureFlags::default(),
1438 metrics::MetricsLogger::default(),
1439 )
1440 .await
1441 .unwrap();
1442 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1443 }
1444
1445 fn get_touchpad_device_descriptor(
1448 has_mouse_descriptor: bool,
1449 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1450 fidl_input_report::DeviceDescriptor {
1451 mouse: match has_mouse_descriptor {
1452 true => Some(fidl_input_report::MouseDescriptor::default()),
1453 false => None,
1454 },
1455 touch: Some(fidl_input_report::TouchDescriptor {
1456 input: Some(fidl_input_report::TouchInputDescriptor {
1457 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1458 position_x: Some(fidl_input_report::Axis {
1459 range: fidl_input_report::Range { min: 1, max: 2 },
1460 unit: fidl_input_report::Unit {
1461 type_: fidl_input_report::UnitType::None,
1462 exponent: 0,
1463 },
1464 }),
1465 position_y: Some(fidl_input_report::Axis {
1466 range: fidl_input_report::Range { min: 2, max: 3 },
1467 unit: fidl_input_report::Unit {
1468 type_: fidl_input_report::UnitType::Other,
1469 exponent: 100000,
1470 },
1471 }),
1472 pressure: Some(fidl_input_report::Axis {
1473 range: fidl_input_report::Range { min: 3, max: 4 },
1474 unit: fidl_input_report::Unit {
1475 type_: fidl_input_report::UnitType::Grams,
1476 exponent: -991,
1477 },
1478 }),
1479 contact_width: Some(fidl_input_report::Axis {
1480 range: fidl_input_report::Range { min: 5, max: 6 },
1481 unit: fidl_input_report::Unit {
1482 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1483 exponent: 123,
1484 },
1485 }),
1486 contact_height: Some(fidl_input_report::Axis {
1487 range: fidl_input_report::Range { min: 7, max: 8 },
1488 unit: fidl_input_report::Unit {
1489 type_: fidl_input_report::UnitType::Pascals,
1490 exponent: 100,
1491 },
1492 }),
1493 ..Default::default()
1494 }]),
1495 ..Default::default()
1496 }),
1497 ..Default::default()
1498 }),
1499 ..Default::default()
1500 }
1501 }
1502
1503 #[fasync::run_singlethreaded(test)]
1504 async fn send_touchpad_event_button() {
1505 const TOUCH_ID: u32 = 1;
1506 const PRIMARY_BUTTON: u8 = 1;
1507
1508 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1509 device_id: 1,
1510 contacts: vec![],
1511 });
1512 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1513
1514 let contact = fidl_fuchsia_input_report::ContactInputReport {
1515 contact_id: Some(TOUCH_ID),
1516 position_x: Some(0),
1517 position_y: Some(0),
1518 pressure: None,
1519 contact_width: None,
1520 contact_height: None,
1521 ..Default::default()
1522 };
1523 let reports = vec![create_touch_input_report(
1524 vec![contact],
1525 Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1526 unknown_ordinal: PRIMARY_BUTTON,
1527 }]),
1528 event_time_i64,
1529 )];
1530
1531 let expected_events = vec![create_touchpad_event(
1532 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1533 vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1534 unknown_ordinal: PRIMARY_BUTTON,
1535 }]
1536 .into_iter()
1537 .collect(),
1538 event_time_u64,
1539 &descriptor,
1540 )];
1541
1542 assert_input_report_sequence_generates_events!(
1543 input_reports: reports,
1544 expected_events: expected_events,
1545 device_descriptor: descriptor,
1546 device_type: TouchBinding,
1547 );
1548 }
1549
1550 #[fasync::run_singlethreaded(test)]
1551 async fn send_touchpad_event_2_fingers_down_up() {
1552 const TOUCH_ID_1: u32 = 1;
1553 const TOUCH_ID_2: u32 = 2;
1554
1555 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1556 device_id: 1,
1557 contacts: vec![],
1558 });
1559 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1560
1561 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1562 contact_id: Some(TOUCH_ID_1),
1563 position_x: Some(0),
1564 position_y: Some(0),
1565 pressure: None,
1566 contact_width: None,
1567 contact_height: None,
1568 ..Default::default()
1569 };
1570 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1571 contact_id: Some(TOUCH_ID_2),
1572 position_x: Some(10),
1573 position_y: Some(10),
1574 pressure: None,
1575 contact_width: None,
1576 contact_height: None,
1577 ..Default::default()
1578 };
1579 let reports = vec![
1580 create_touch_input_report(
1581 vec![contact1, contact2],
1582 None,
1583 event_time_i64,
1584 ),
1585 create_touch_input_report(vec![], None, event_time_i64),
1586 ];
1587
1588 let expected_events = vec![
1589 create_touchpad_event(
1590 vec![
1591 create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1592 create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1593 ],
1594 HashSet::new(),
1595 event_time_u64,
1596 &descriptor,
1597 ),
1598 create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1599 ];
1600
1601 assert_input_report_sequence_generates_events!(
1602 input_reports: reports,
1603 expected_events: expected_events,
1604 device_descriptor: descriptor,
1605 device_type: TouchBinding,
1606 );
1607 }
1608
1609 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1610 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1611 #[fasync::run_singlethreaded(test)]
1612 async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1613 const TOUCH_ID: u32 = 1;
1614
1615 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1616 device_id: 1,
1617 contacts: vec![],
1618 });
1619 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1620
1621 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1622 contact_id: Some(TOUCH_ID),
1623 position_x: Some(p0.x as i64),
1624 position_y: Some(p0.y as i64),
1625 pressure: None,
1626 contact_width: None,
1627 contact_height: None,
1628 ..Default::default()
1629 };
1630 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1631 contact_id: Some(TOUCH_ID),
1632 position_x: Some(p1.x as i64),
1633 position_y: Some(p1.y as i64),
1634 pressure: None,
1635 contact_width: None,
1636 contact_height: None,
1637 ..Default::default()
1638 };
1639 let reports = vec![
1640 create_touch_input_report(
1641 vec![contact1],
1642 None,
1643 event_time_i64,
1644 ),
1645 create_touch_input_report(
1646 vec![contact2],
1647 None,
1648 event_time_i64,
1649 ),
1650 ];
1651
1652 let expected_events = vec![
1653 create_touchpad_event(
1654 vec![create_touch_contact(TOUCH_ID, p0)],
1655 HashSet::new(),
1656 event_time_u64,
1657 &descriptor,
1658 ),
1659 create_touchpad_event(
1660 vec![create_touch_contact(TOUCH_ID, p1)],
1661 HashSet::new(),
1662 event_time_u64,
1663 &descriptor,
1664 ),
1665 ];
1666
1667 assert_input_report_sequence_generates_events!(
1668 input_reports: reports,
1669 expected_events: expected_events,
1670 device_descriptor: descriptor,
1671 device_type: TouchBinding,
1672 );
1673 }
1674
1675 #[test_case(true; "merge touch events enabled")]
1678 #[test_case(false; "merge touch events disabled")]
1679 #[fasync::run_singlethreaded(test)]
1680 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1681 let descriptor =
1682 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1683 device_id: 1,
1684 contacts: vec![],
1685 });
1686 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1687
1688 let reports = vec![create_touch_input_report(
1689 vec![],
1690 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1691 event_time_i64,
1692 )];
1693
1694 let expected_events = vec![create_touch_screen_event_with_buttons(
1695 hashmap! {},
1696 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1697 event_time_u64,
1698 &descriptor,
1699 )];
1700
1701 assert_input_report_sequence_generates_events_with_feature_flags!(
1702 input_reports: reports,
1703 expected_events: expected_events,
1704 device_descriptor: descriptor,
1705 device_type: TouchBinding,
1706 feature_flags: input_device::InputPipelineFeatureFlags {
1707 enable_merge_touch_events,
1708 ..Default::default()
1709 },
1710 );
1711 }
1712
1713 #[test_case(true; "merge touch events enabled")]
1716 #[test_case(false; "merge touch events disabled")]
1717 #[fasync::run_singlethreaded(test)]
1718 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1719 const TOUCH_ID: u32 = 2;
1720
1721 let descriptor =
1722 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1723 device_id: 1,
1724 contacts: vec![],
1725 });
1726 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1727
1728 let contact = fidl_fuchsia_input_report::ContactInputReport {
1729 contact_id: Some(TOUCH_ID),
1730 position_x: Some(0),
1731 position_y: Some(0),
1732 pressure: None,
1733 contact_width: None,
1734 contact_height: None,
1735 ..Default::default()
1736 };
1737 let reports = vec![create_touch_input_report(
1738 vec![contact],
1739 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1740 event_time_i64,
1741 )];
1742
1743 let expected_events = vec![create_touch_screen_event_with_buttons(
1744 hashmap! {
1745 fidl_ui_input::PointerEventPhase::Add
1746 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1747 fidl_ui_input::PointerEventPhase::Down
1748 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1749 },
1750 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1751 event_time_u64,
1752 &descriptor,
1753 )];
1754
1755 assert_input_report_sequence_generates_events_with_feature_flags!(
1756 input_reports: reports,
1757 expected_events: expected_events,
1758 device_descriptor: descriptor,
1759 device_type: TouchBinding,
1760 feature_flags: input_device::InputPipelineFeatureFlags {
1761 enable_merge_touch_events,
1762 ..Default::default()
1763 },
1764 );
1765 }
1766
1767 #[test_case(true; "merge touch events enabled")]
1770 #[test_case(false; "merge touch events disabled")]
1771 #[fasync::run_singlethreaded(test)]
1772 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1773 const TOUCH_ID: u32 = 2;
1774
1775 let descriptor =
1776 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1777 device_id: 1,
1778 contacts: vec![],
1779 });
1780 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1781
1782 let contact = fidl_fuchsia_input_report::ContactInputReport {
1783 contact_id: Some(TOUCH_ID),
1784 position_x: Some(0),
1785 position_y: Some(0),
1786 pressure: None,
1787 contact_width: None,
1788 contact_height: None,
1789 ..Default::default()
1790 };
1791 let reports = vec![create_touch_input_report(
1792 vec![contact],
1793 Some(vec![
1794 fidl_fuchsia_input_report::TouchButton::Palm,
1795 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1796 ]),
1797 event_time_i64,
1798 )];
1799
1800 let expected_events = vec![create_touch_screen_event_with_buttons(
1801 hashmap! {
1802 fidl_ui_input::PointerEventPhase::Add
1803 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1804 fidl_ui_input::PointerEventPhase::Down
1805 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1806 },
1807 vec![
1808 fidl_fuchsia_input_report::TouchButton::Palm,
1809 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1810 ],
1811 event_time_u64,
1812 &descriptor,
1813 )];
1814
1815 assert_input_report_sequence_generates_events_with_feature_flags!(
1816 input_reports: reports,
1817 expected_events: expected_events,
1818 device_descriptor: descriptor,
1819 device_type: TouchBinding,
1820 feature_flags: input_device::InputPipelineFeatureFlags {
1821 enable_merge_touch_events,
1822 ..Default::default()
1823 },
1824 );
1825 }
1826
1827 #[test_case(true; "merge touch events enabled")]
1829 #[test_case(false; "merge touch events disabled")]
1830 #[fasync::run_singlethreaded(test)]
1831 async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1832 let descriptor =
1833 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1834 device_id: 1,
1835 contacts: vec![],
1836 });
1837 let (event_time_i64, _) = testing_utilities::event_times();
1838
1839 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1840
1841 let expected_events: Vec<input_device::InputEvent> = vec![];
1842
1843 assert_input_report_sequence_generates_events_with_feature_flags!(
1844 input_reports: reports,
1845 expected_events: expected_events,
1846 device_descriptor: descriptor,
1847 device_type: TouchBinding,
1848 feature_flags: input_device::InputPipelineFeatureFlags {
1849 enable_merge_touch_events,
1850 ..Default::default()
1851 },
1852 );
1853 }
1854
1855 #[test_case(true; "merge touch events enabled")]
1857 #[test_case(false; "merge touch events disabled")]
1858 #[fasync::run_singlethreaded(test)]
1859 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1860 const TOUCH_ID: u32 = 2;
1861
1862 let descriptor =
1863 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1864 device_id: 1,
1865 contacts: vec![],
1866 });
1867 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1868
1869 let contact = fidl_fuchsia_input_report::ContactInputReport {
1870 contact_id: Some(TOUCH_ID),
1871 position_x: Some(0),
1872 position_y: Some(0),
1873 pressure: None,
1874 contact_width: None,
1875 contact_height: None,
1876 ..Default::default()
1877 };
1878 let reports = vec![
1879 create_touch_input_report(vec![contact], None, event_time_i64),
1880 create_touch_input_report(
1881 vec![],
1882 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1883 event_time_i64,
1884 ),
1885 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1886 ];
1887
1888 let expected_events = vec![
1889 create_touch_screen_event_with_buttons(
1890 hashmap! {
1891 fidl_ui_input::PointerEventPhase::Add
1892 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1893 fidl_ui_input::PointerEventPhase::Down
1894 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1895 },
1896 vec![],
1897 event_time_u64,
1898 &descriptor,
1899 ),
1900 create_touch_screen_event_with_buttons(
1901 hashmap! {},
1902 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1903 event_time_u64,
1904 &descriptor,
1905 ),
1906 create_touch_screen_event_with_buttons(
1907 hashmap! {},
1908 vec![],
1909 event_time_u64,
1910 &descriptor,
1911 ),
1912 ];
1913
1914 assert_input_report_sequence_generates_events_with_feature_flags!(
1915 input_reports: reports,
1916 expected_events: expected_events,
1917 device_descriptor: descriptor,
1918 device_type: TouchBinding,
1919 feature_flags: input_device::InputPipelineFeatureFlags {
1920 enable_merge_touch_events,
1921 ..Default::default()
1922 },
1923 );
1924 }
1925
1926 #[fasync::run_singlethreaded(test)]
1927 async fn process_reports_batches_events() {
1928 const TOUCH_ID: u32 = 2;
1929
1930 let descriptor =
1931 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1932 device_id: 1,
1933 contacts: vec![],
1934 });
1935 let (event_time_i64, _) = testing_utilities::event_times();
1936
1937 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1938 contact_id: Some(TOUCH_ID),
1939 position_x: Some(0),
1940 position_y: Some(0),
1941 ..Default::default()
1942 };
1943 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1944 contact_id: Some(TOUCH_ID),
1945 position_x: Some(10),
1946 position_y: Some(10),
1947 ..Default::default()
1948 };
1949 let reports = vec![
1950 create_touch_input_report(vec![contact1], None, event_time_i64),
1951 create_touch_input_report(vec![contact2], None, event_time_i64),
1952 ];
1953
1954 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1955
1956 let inspector = fuchsia_inspect::Inspector::default();
1957 let test_node = inspector.root().create_child("TestDevice_Touch");
1958 let mut inspect_status = InputDeviceStatus::new(test_node);
1959 inspect_status.health_node.set_ok();
1960
1961 let _ = TouchBinding::process_reports(
1962 reports,
1963 None,
1964 &descriptor,
1965 &mut event_sender,
1966 &inspect_status,
1967 &metrics::MetricsLogger::default(),
1968 &input_device::InputPipelineFeatureFlags::default(),
1969 );
1970
1971 let batch = event_receiver.try_next().expect("Expected a batch of events");
1973 let events = batch.expect("Expected events in the batch");
1974 assert_eq!(events.len(), 2);
1975
1976 assert!(event_receiver.try_next().is_err());
1978 }
1979
1980 #[fasync::run_singlethreaded(test)]
1981 async fn process_reports_merges_touch_events_when_enabled() {
1982 const TOUCH_ID: u32 = 2;
1983 let descriptor =
1984 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1985 device_id: 1,
1986 contacts: vec![],
1987 });
1988 let (event_time_i64, _) = testing_utilities::event_times();
1989
1990 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1991 contact_id: Some(TOUCH_ID),
1992 position_x: Some(0),
1993 position_y: Some(0),
1994 ..Default::default()
1995 };
1996 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1997 contact_id: Some(TOUCH_ID),
1998 position_x: Some(10),
1999 position_y: Some(10),
2000 ..Default::default()
2001 };
2002 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2003 contact_id: Some(TOUCH_ID),
2004 position_x: Some(20),
2005 position_y: Some(20),
2006 ..Default::default()
2007 };
2008 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2009 contact_id: Some(TOUCH_ID),
2010 position_x: Some(30),
2011 position_y: Some(30),
2012 ..Default::default()
2013 };
2014 let reports = vec![
2015 create_touch_input_report(vec![contact_add], None, event_time_i64),
2016 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2017 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2018 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2019 create_touch_input_report(vec![], None, event_time_i64),
2020 ];
2021
2022 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2023 let inspector = fuchsia_inspect::Inspector::default();
2024 let mut inspect_status =
2025 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2026 inspect_status.health_node.set_ok();
2027
2028 let _ = TouchBinding::process_reports(
2029 reports,
2030 None,
2031 &descriptor,
2032 &mut event_sender,
2033 &inspect_status,
2034 &metrics::MetricsLogger::default(),
2035 &input_device::InputPipelineFeatureFlags {
2036 enable_merge_touch_events: true,
2037 ..Default::default()
2038 },
2039 );
2040
2041 let batch = event_receiver.try_next().unwrap().unwrap();
2042
2043 assert_eq!(batch.len(), 3);
2045
2046 assert_matches!(
2048 &batch[0].device_event,
2049 input_device::InputDeviceEvent::TouchScreen(event)
2050 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2051 );
2052 assert_matches!(
2054 &batch[1].device_event,
2055 input_device::InputDeviceEvent::TouchScreen(event)
2056 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2057 );
2058 assert_matches!(
2060 &batch[2].device_event,
2061 input_device::InputDeviceEvent::TouchScreen(event)
2062 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2063 );
2064 }
2065
2066 #[fasync::run_singlethreaded(test)]
2067 async fn process_reports_does_not_merge_touch_events_when_disabled() {
2068 const TOUCH_ID: u32 = 2;
2069 let descriptor =
2070 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2071 device_id: 1,
2072 contacts: vec![],
2073 });
2074 let (event_time_i64, _) = testing_utilities::event_times();
2075
2076 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2077 contact_id: Some(TOUCH_ID),
2078 position_x: Some(0),
2079 position_y: Some(0),
2080 ..Default::default()
2081 };
2082 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2083 contact_id: Some(TOUCH_ID),
2084 position_x: Some(10),
2085 position_y: Some(10),
2086 ..Default::default()
2087 };
2088 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2089 contact_id: Some(TOUCH_ID),
2090 position_x: Some(20),
2091 position_y: Some(20),
2092 ..Default::default()
2093 };
2094 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2095 contact_id: Some(TOUCH_ID),
2096 position_x: Some(30),
2097 position_y: Some(30),
2098 ..Default::default()
2099 };
2100 let reports = vec![
2101 create_touch_input_report(vec![contact_add], None, event_time_i64),
2102 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2103 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2104 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2105 create_touch_input_report(vec![], None, event_time_i64),
2106 ];
2107
2108 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2109 let inspector = fuchsia_inspect::Inspector::default();
2110 let mut inspect_status =
2111 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2112 inspect_status.health_node.set_ok();
2113
2114 let _ = TouchBinding::process_reports(
2115 reports,
2116 None,
2117 &descriptor,
2118 &mut event_sender,
2119 &inspect_status,
2120 &metrics::MetricsLogger::default(),
2121 &input_device::InputPipelineFeatureFlags {
2122 enable_merge_touch_events: false,
2123 ..Default::default()
2124 },
2125 );
2126
2127 let batch = event_receiver.try_next().unwrap().unwrap();
2128
2129 assert_eq!(batch.len(), 5);
2131 }
2132}