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