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::__SourceBreaking { unknown_ordinal } => {
874 log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
875 None
876 }
877 })),
878 None => HashSet::new(),
879 };
880
881 let trace_id = fuchsia_trace::Id::random();
882 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
883 send_touchpad_event(
884 current_contacts,
885 buttons,
886 device_descriptor,
887 input_event_sender,
888 trace_id,
889 inspect_status,
890 metrics_logger,
891 );
892
893 Some(report)
894}
895
896fn touch_contacts_and_buttons_from_touch_report(
897 touch_report: &fidl_fuchsia_input_report::TouchInputReport,
898) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
899 let contacts: Vec<TouchContact> = touch_report
901 .contacts
902 .as_ref()
903 .and_then(|unwrapped_contacts| {
904 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
906 })
907 .unwrap_or_default();
908
909 (
910 contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
911 touch_report.pressed_buttons.clone().unwrap_or_default(),
912 )
913}
914
915fn create_touch_screen_event(
925 contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
926 injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
927 pressed_buttons: Vec<fidl_input_report::TouchButton>,
928 device_descriptor: &input_device::InputDeviceDescriptor,
929 trace_id: fuchsia_trace::Id,
930 wake_lease: Option<zx::EventPair>,
931) -> InputEvent {
932 input_device::InputEvent {
933 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
934 contacts,
935 injector_contacts,
936 pressed_buttons,
937 wake_lease,
938 }),
939 device_descriptor: device_descriptor.clone(),
940 event_time: zx::MonotonicInstant::get(),
941 handled: Handled::No,
942 trace_id: Some(trace_id),
943 }
944}
945
946fn send_touchpad_event(
954 injector_contacts: Vec<TouchContact>,
955 pressed_buttons: HashSet<mouse_binding::MouseButton>,
956 device_descriptor: &input_device::InputDeviceDescriptor,
957 input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
958 trace_id: fuchsia_trace::Id,
959 inspect_status: &InputDeviceStatus,
960 metrics_logger: &metrics::MetricsLogger,
961) {
962 let event = input_device::InputEvent {
963 device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
964 injector_contacts,
965 pressed_buttons,
966 }),
967 device_descriptor: device_descriptor.clone(),
968 event_time: zx::MonotonicInstant::get(),
969 handled: Handled::No,
970 trace_id: Some(trace_id),
971 };
972
973 match input_event_sender.unbounded_send(vec![event.clone()]) {
974 Err(e) => {
975 metrics_logger.log_error(
976 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
977 std::format!("Failed to send TouchpadEvent with error: {:?}", e),
978 );
979 }
980 _ => inspect_status.count_generated_event(event),
981 }
982}
983
984async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
990 match input_device.get_feature_report().await {
991 Ok(Ok(fidl_input_report::FeatureReport {
992 touch:
993 Some(fidl_input_report::TouchFeatureReport {
994 input_mode:
995 Some(
996 fidl_input_report::TouchConfigurationInputMode::MouseCollection
997 | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
998 ),
999 ..
1000 }),
1001 ..
1002 })) => TouchDeviceType::WindowsPrecisionTouchpad,
1003 _ => TouchDeviceType::TouchScreen,
1004 }
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009 use super::*;
1010 use crate::testing_utilities::{
1011 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1012 create_touch_screen_event_with_buttons, create_touchpad_event,
1013 };
1014 use crate::utils::Position;
1015 use assert_matches::assert_matches;
1016 use diagnostics_assertions::AnyProperty;
1017 use fidl_test_util::spawn_stream_handler;
1018 use fuchsia_async as fasync;
1019 use futures::StreamExt;
1020 use pretty_assertions::assert_eq;
1021 use test_case::test_case;
1022
1023 #[fasync::run_singlethreaded(test)]
1024 async fn process_empty_reports() {
1025 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1026 let previous_report = create_touch_input_report(
1027 vec![],
1028 None,
1029 previous_report_time,
1030 );
1031 let report_time = zx::MonotonicInstant::get().into_nanos();
1032 let report =
1033 create_touch_input_report(vec![], None, report_time);
1034
1035 let descriptor =
1036 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1037 device_id: 1,
1038 contacts: vec![],
1039 });
1040 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1041
1042 let inspector = fuchsia_inspect::Inspector::default();
1043 let test_node = inspector.root().create_child("TestDevice_Touch");
1044 let mut inspect_status = InputDeviceStatus::new(test_node);
1045 inspect_status.health_node.set_ok();
1046
1047 let (returned_report, _) = TouchBinding::process_reports(
1048 vec![report],
1049 Some(previous_report),
1050 &descriptor,
1051 &mut event_sender,
1052 &inspect_status,
1053 &metrics::MetricsLogger::default(),
1054 &input_device::InputPipelineFeatureFlags::default(),
1055 );
1056 assert!(returned_report.is_some());
1057 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1058
1059 let event = event_receiver.try_next();
1061 assert!(event.is_err());
1062
1063 diagnostics_assertions::assert_data_tree!(inspector, root: {
1064 "TestDevice_Touch": contains {
1065 reports_received_count: 1u64,
1066 reports_filtered_count: 1u64,
1067 events_generated: 0u64,
1068 last_received_timestamp_ns: report_time as u64,
1069 last_generated_timestamp_ns: 0u64,
1070 "fuchsia.inspect.Health": {
1071 status: "OK",
1072 start_timestamp_nanos: AnyProperty
1075 },
1076 }
1077 });
1078 }
1079
1080 #[fasync::run_singlethreaded(test)]
1082 async fn add_and_down() {
1083 const TOUCH_ID: u32 = 2;
1084
1085 let descriptor =
1086 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1087 device_id: 1,
1088 contacts: vec![],
1089 });
1090 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1091
1092 let contact = fidl_fuchsia_input_report::ContactInputReport {
1093 contact_id: Some(TOUCH_ID),
1094 position_x: Some(0),
1095 position_y: Some(0),
1096 pressure: None,
1097 contact_width: None,
1098 contact_height: None,
1099 ..Default::default()
1100 };
1101 let reports = vec![create_touch_input_report(
1102 vec![contact],
1103 None,
1104 event_time_i64,
1105 )];
1106
1107 let expected_events = vec![create_touch_screen_event(
1108 hashmap! {
1109 fidl_ui_input::PointerEventPhase::Add
1110 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1111 fidl_ui_input::PointerEventPhase::Down
1112 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1113 },
1114 event_time_u64,
1115 &descriptor,
1116 )];
1117
1118 assert_input_report_sequence_generates_events!(
1119 input_reports: reports,
1120 expected_events: expected_events,
1121 device_descriptor: descriptor,
1122 device_type: TouchBinding,
1123 );
1124 }
1125
1126 #[fasync::run_singlethreaded(test)]
1128 async fn up_and_remove() {
1129 const TOUCH_ID: u32 = 2;
1130
1131 let descriptor =
1132 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1133 device_id: 1,
1134 contacts: vec![],
1135 });
1136 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1137
1138 let contact = fidl_fuchsia_input_report::ContactInputReport {
1139 contact_id: Some(TOUCH_ID),
1140 position_x: Some(0),
1141 position_y: Some(0),
1142 pressure: None,
1143 contact_width: None,
1144 contact_height: None,
1145 ..Default::default()
1146 };
1147 let reports = vec![
1148 create_touch_input_report(
1149 vec![contact],
1150 None,
1151 event_time_i64,
1152 ),
1153 create_touch_input_report(vec![], None, event_time_i64),
1154 ];
1155
1156 let expected_events = vec![
1157 create_touch_screen_event(
1158 hashmap! {
1159 fidl_ui_input::PointerEventPhase::Add
1160 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1161 fidl_ui_input::PointerEventPhase::Down
1162 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1163 },
1164 event_time_u64,
1165 &descriptor,
1166 ),
1167 create_touch_screen_event(
1168 hashmap! {
1169 fidl_ui_input::PointerEventPhase::Up
1170 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1171 fidl_ui_input::PointerEventPhase::Remove
1172 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1173 },
1174 event_time_u64,
1175 &descriptor,
1176 ),
1177 ];
1178
1179 assert_input_report_sequence_generates_events!(
1180 input_reports: reports,
1181 expected_events: expected_events,
1182 device_descriptor: descriptor,
1183 device_type: TouchBinding,
1184 );
1185 }
1186
1187 #[fasync::run_singlethreaded(test)]
1189 async fn add_down_move() {
1190 const TOUCH_ID: u32 = 2;
1191 let first = Position { x: 10.0, y: 30.0 };
1192 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1193
1194 let descriptor =
1195 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1196 device_id: 1,
1197 contacts: vec![],
1198 });
1199 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1200
1201 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1202 contact_id: Some(TOUCH_ID),
1203 position_x: Some(first.x as i64),
1204 position_y: Some(first.y as i64),
1205 pressure: None,
1206 contact_width: None,
1207 contact_height: None,
1208 ..Default::default()
1209 };
1210 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1211 contact_id: Some(TOUCH_ID),
1212 position_x: Some(first.x as i64 * 2),
1213 position_y: Some(first.y as i64 * 2),
1214 pressure: None,
1215 contact_width: None,
1216 contact_height: None,
1217 ..Default::default()
1218 };
1219
1220 let reports = vec![
1221 create_touch_input_report(
1222 vec![first_contact],
1223 None,
1224 event_time_i64,
1225 ),
1226 create_touch_input_report(
1227 vec![second_contact],
1228 None,
1229 event_time_i64,
1230 ),
1231 ];
1232
1233 let expected_events = vec![
1234 create_touch_screen_event(
1235 hashmap! {
1236 fidl_ui_input::PointerEventPhase::Add
1237 => vec![create_touch_contact(TOUCH_ID, first)],
1238 fidl_ui_input::PointerEventPhase::Down
1239 => vec![create_touch_contact(TOUCH_ID, first)],
1240 },
1241 event_time_u64,
1242 &descriptor,
1243 ),
1244 create_touch_screen_event(
1245 hashmap! {
1246 fidl_ui_input::PointerEventPhase::Move
1247 => vec![create_touch_contact(TOUCH_ID, second)],
1248 },
1249 event_time_u64,
1250 &descriptor,
1251 ),
1252 ];
1253
1254 assert_input_report_sequence_generates_events!(
1255 input_reports: reports,
1256 expected_events: expected_events,
1257 device_descriptor: descriptor,
1258 device_type: TouchBinding,
1259 );
1260 }
1261
1262 #[fasync::run_singlethreaded(test)]
1263 async fn sent_event_has_trace_id() {
1264 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1265 let previous_report = create_touch_input_report(
1266 vec![],
1267 None,
1268 previous_report_time,
1269 );
1270
1271 let report_time = zx::MonotonicInstant::get().into_nanos();
1272 let contact = fidl_fuchsia_input_report::ContactInputReport {
1273 contact_id: Some(222),
1274 position_x: Some(333),
1275 position_y: Some(444),
1276 ..Default::default()
1277 };
1278 let report =
1279 create_touch_input_report(vec![contact], None, report_time);
1280
1281 let descriptor =
1282 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1283 device_id: 1,
1284 contacts: vec![],
1285 });
1286 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1287
1288 let inspector = fuchsia_inspect::Inspector::default();
1289 let test_node = inspector.root().create_child("TestDevice_Touch");
1290 let mut inspect_status = InputDeviceStatus::new(test_node);
1291 inspect_status.health_node.set_ok();
1292
1293 let _ = TouchBinding::process_reports(
1294 vec![report],
1295 Some(previous_report),
1296 &descriptor,
1297 &mut event_sender,
1298 &inspect_status,
1299 &metrics::MetricsLogger::default(),
1300 &input_device::InputPipelineFeatureFlags::default(),
1301 );
1302 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1303 }
1304
1305 #[fuchsia::test(allow_stalls = false)]
1306 async fn enables_touchpad_mode_automatically() {
1307 let (set_feature_report_sender, set_feature_report_receiver) =
1308 futures::channel::mpsc::unbounded();
1309 let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1310 let set_feature_report_sender = set_feature_report_sender.clone();
1311 async move {
1312 match input_device_request {
1313 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1314 let _ = responder.send(&get_touchpad_device_descriptor(
1315 true, ));
1317 }
1318 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1319 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1320 touch: Some(fidl_input_report::TouchFeatureReport {
1321 input_mode: Some(
1322 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1323 ),
1324 ..Default::default()
1325 }),
1326 ..Default::default()
1327 }));
1328 }
1329 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1330 responder,
1331 report,
1332 } => {
1333 match set_feature_report_sender.unbounded_send(report) {
1334 Ok(_) => {
1335 let _ = responder.send(Ok(()));
1336 }
1337 Err(e) => {
1338 panic!("try_send set_feature_report_request failed: {}", e);
1339 }
1340 };
1341 }
1342 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1343 }
1345 r => panic!("unsupported request {:?}", r),
1346 }
1347 }
1348 });
1349
1350 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1351
1352 let inspector = fuchsia_inspect::Inspector::default();
1354 let test_node = inspector.root().create_child("test_node");
1355
1356 TouchBinding::new(
1360 input_device_proxy,
1361 0,
1362 device_event_sender,
1363 test_node,
1364 input_device::InputPipelineFeatureFlags::default(),
1365 metrics::MetricsLogger::default(),
1366 )
1367 .await
1368 .unwrap();
1369 assert_matches!(
1370 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1371 [fidl_input_report::FeatureReport {
1372 touch: Some(fidl_input_report::TouchFeatureReport {
1373 input_mode: Some(
1374 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1375 ),
1376 ..
1377 }),
1378 ..
1379 }]
1380 );
1381 }
1382
1383 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1384 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1385 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1386 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1387 #[fuchsia::test(allow_stalls = false)]
1388 async fn identifies_correct_touch_device_type(
1389 has_mouse_descriptor: bool,
1390 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1391 expect_touch_device_type: TouchDeviceType,
1392 ) {
1393 let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1394 match input_device_request {
1395 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1396 let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1397 }
1398 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1399 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1400 touch: Some(fidl_input_report::TouchFeatureReport {
1401 input_mode: touch_input_mode,
1402 ..Default::default()
1403 }),
1404 ..Default::default()
1405 }));
1406 }
1407 fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1408 let _ = responder.send(Ok(()));
1409 }
1410 r => panic!("unsupported request {:?}", r),
1411 }
1412 });
1413
1414 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1415
1416 let inspector = fuchsia_inspect::Inspector::default();
1418 let test_node = inspector.root().create_child("test_node");
1419
1420 let binding = TouchBinding::new(
1421 input_device_proxy,
1422 0,
1423 device_event_sender,
1424 test_node,
1425 input_device::InputPipelineFeatureFlags::default(),
1426 metrics::MetricsLogger::default(),
1427 )
1428 .await
1429 .unwrap();
1430 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1431 }
1432
1433 fn get_touchpad_device_descriptor(
1436 has_mouse_descriptor: bool,
1437 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1438 fidl_input_report::DeviceDescriptor {
1439 mouse: match has_mouse_descriptor {
1440 true => Some(fidl_input_report::MouseDescriptor::default()),
1441 false => None,
1442 },
1443 touch: Some(fidl_input_report::TouchDescriptor {
1444 input: Some(fidl_input_report::TouchInputDescriptor {
1445 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1446 position_x: Some(fidl_input_report::Axis {
1447 range: fidl_input_report::Range { min: 1, max: 2 },
1448 unit: fidl_input_report::Unit {
1449 type_: fidl_input_report::UnitType::None,
1450 exponent: 0,
1451 },
1452 }),
1453 position_y: Some(fidl_input_report::Axis {
1454 range: fidl_input_report::Range { min: 2, max: 3 },
1455 unit: fidl_input_report::Unit {
1456 type_: fidl_input_report::UnitType::Other,
1457 exponent: 100000,
1458 },
1459 }),
1460 pressure: Some(fidl_input_report::Axis {
1461 range: fidl_input_report::Range { min: 3, max: 4 },
1462 unit: fidl_input_report::Unit {
1463 type_: fidl_input_report::UnitType::Grams,
1464 exponent: -991,
1465 },
1466 }),
1467 contact_width: Some(fidl_input_report::Axis {
1468 range: fidl_input_report::Range { min: 5, max: 6 },
1469 unit: fidl_input_report::Unit {
1470 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1471 exponent: 123,
1472 },
1473 }),
1474 contact_height: Some(fidl_input_report::Axis {
1475 range: fidl_input_report::Range { min: 7, max: 8 },
1476 unit: fidl_input_report::Unit {
1477 type_: fidl_input_report::UnitType::Pascals,
1478 exponent: 100,
1479 },
1480 }),
1481 ..Default::default()
1482 }]),
1483 ..Default::default()
1484 }),
1485 ..Default::default()
1486 }),
1487 ..Default::default()
1488 }
1489 }
1490
1491 #[fasync::run_singlethreaded(test)]
1492 async fn send_touchpad_event_button() {
1493 const TOUCH_ID: u32 = 1;
1494 const PRIMARY_BUTTON: u8 = 1;
1495
1496 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1497 device_id: 1,
1498 contacts: vec![],
1499 });
1500 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1501
1502 let contact = fidl_fuchsia_input_report::ContactInputReport {
1503 contact_id: Some(TOUCH_ID),
1504 position_x: Some(0),
1505 position_y: Some(0),
1506 pressure: None,
1507 contact_width: None,
1508 contact_height: None,
1509 ..Default::default()
1510 };
1511 let reports = vec![create_touch_input_report(
1512 vec![contact],
1513 Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1514 unknown_ordinal: PRIMARY_BUTTON,
1515 }]),
1516 event_time_i64,
1517 )];
1518
1519 let expected_events = vec![create_touchpad_event(
1520 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1521 vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1522 unknown_ordinal: PRIMARY_BUTTON,
1523 }]
1524 .into_iter()
1525 .collect(),
1526 event_time_u64,
1527 &descriptor,
1528 )];
1529
1530 assert_input_report_sequence_generates_events!(
1531 input_reports: reports,
1532 expected_events: expected_events,
1533 device_descriptor: descriptor,
1534 device_type: TouchBinding,
1535 );
1536 }
1537
1538 #[fasync::run_singlethreaded(test)]
1539 async fn send_touchpad_event_2_fingers_down_up() {
1540 const TOUCH_ID_1: u32 = 1;
1541 const TOUCH_ID_2: u32 = 2;
1542
1543 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1544 device_id: 1,
1545 contacts: vec![],
1546 });
1547 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1548
1549 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1550 contact_id: Some(TOUCH_ID_1),
1551 position_x: Some(0),
1552 position_y: Some(0),
1553 pressure: None,
1554 contact_width: None,
1555 contact_height: None,
1556 ..Default::default()
1557 };
1558 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1559 contact_id: Some(TOUCH_ID_2),
1560 position_x: Some(10),
1561 position_y: Some(10),
1562 pressure: None,
1563 contact_width: None,
1564 contact_height: None,
1565 ..Default::default()
1566 };
1567 let reports = vec![
1568 create_touch_input_report(
1569 vec![contact1, contact2],
1570 None,
1571 event_time_i64,
1572 ),
1573 create_touch_input_report(vec![], None, event_time_i64),
1574 ];
1575
1576 let expected_events = vec![
1577 create_touchpad_event(
1578 vec![
1579 create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1580 create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1581 ],
1582 HashSet::new(),
1583 event_time_u64,
1584 &descriptor,
1585 ),
1586 create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1587 ];
1588
1589 assert_input_report_sequence_generates_events!(
1590 input_reports: reports,
1591 expected_events: expected_events,
1592 device_descriptor: descriptor,
1593 device_type: TouchBinding,
1594 );
1595 }
1596
1597 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1598 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1599 #[fasync::run_singlethreaded(test)]
1600 async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1601 const TOUCH_ID: u32 = 1;
1602
1603 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1604 device_id: 1,
1605 contacts: vec![],
1606 });
1607 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1608
1609 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1610 contact_id: Some(TOUCH_ID),
1611 position_x: Some(p0.x as i64),
1612 position_y: Some(p0.y as i64),
1613 pressure: None,
1614 contact_width: None,
1615 contact_height: None,
1616 ..Default::default()
1617 };
1618 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1619 contact_id: Some(TOUCH_ID),
1620 position_x: Some(p1.x as i64),
1621 position_y: Some(p1.y as i64),
1622 pressure: None,
1623 contact_width: None,
1624 contact_height: None,
1625 ..Default::default()
1626 };
1627 let reports = vec![
1628 create_touch_input_report(
1629 vec![contact1],
1630 None,
1631 event_time_i64,
1632 ),
1633 create_touch_input_report(
1634 vec![contact2],
1635 None,
1636 event_time_i64,
1637 ),
1638 ];
1639
1640 let expected_events = vec![
1641 create_touchpad_event(
1642 vec![create_touch_contact(TOUCH_ID, p0)],
1643 HashSet::new(),
1644 event_time_u64,
1645 &descriptor,
1646 ),
1647 create_touchpad_event(
1648 vec![create_touch_contact(TOUCH_ID, p1)],
1649 HashSet::new(),
1650 event_time_u64,
1651 &descriptor,
1652 ),
1653 ];
1654
1655 assert_input_report_sequence_generates_events!(
1656 input_reports: reports,
1657 expected_events: expected_events,
1658 device_descriptor: descriptor,
1659 device_type: TouchBinding,
1660 );
1661 }
1662
1663 #[test_case(true; "merge touch events enabled")]
1666 #[test_case(false; "merge touch events disabled")]
1667 #[fasync::run_singlethreaded(test)]
1668 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1669 let descriptor =
1670 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1671 device_id: 1,
1672 contacts: vec![],
1673 });
1674 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1675
1676 let reports = vec![create_touch_input_report(
1677 vec![],
1678 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1679 event_time_i64,
1680 )];
1681
1682 let expected_events = vec![create_touch_screen_event_with_buttons(
1683 hashmap! {},
1684 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1685 event_time_u64,
1686 &descriptor,
1687 )];
1688
1689 assert_input_report_sequence_generates_events_with_feature_flags!(
1690 input_reports: reports,
1691 expected_events: expected_events,
1692 device_descriptor: descriptor,
1693 device_type: TouchBinding,
1694 feature_flags: input_device::InputPipelineFeatureFlags {
1695 enable_merge_touch_events,
1696 ..Default::default()
1697 },
1698 );
1699 }
1700
1701 #[test_case(true; "merge touch events enabled")]
1704 #[test_case(false; "merge touch events disabled")]
1705 #[fasync::run_singlethreaded(test)]
1706 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1707 const TOUCH_ID: u32 = 2;
1708
1709 let descriptor =
1710 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1711 device_id: 1,
1712 contacts: vec![],
1713 });
1714 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1715
1716 let contact = fidl_fuchsia_input_report::ContactInputReport {
1717 contact_id: Some(TOUCH_ID),
1718 position_x: Some(0),
1719 position_y: Some(0),
1720 pressure: None,
1721 contact_width: None,
1722 contact_height: None,
1723 ..Default::default()
1724 };
1725 let reports = vec![create_touch_input_report(
1726 vec![contact],
1727 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1728 event_time_i64,
1729 )];
1730
1731 let expected_events = vec![create_touch_screen_event_with_buttons(
1732 hashmap! {
1733 fidl_ui_input::PointerEventPhase::Add
1734 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1735 fidl_ui_input::PointerEventPhase::Down
1736 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1737 },
1738 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1739 event_time_u64,
1740 &descriptor,
1741 )];
1742
1743 assert_input_report_sequence_generates_events_with_feature_flags!(
1744 input_reports: reports,
1745 expected_events: expected_events,
1746 device_descriptor: descriptor,
1747 device_type: TouchBinding,
1748 feature_flags: input_device::InputPipelineFeatureFlags {
1749 enable_merge_touch_events,
1750 ..Default::default()
1751 },
1752 );
1753 }
1754
1755 #[test_case(true; "merge touch events enabled")]
1758 #[test_case(false; "merge touch events disabled")]
1759 #[fasync::run_singlethreaded(test)]
1760 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1761 const TOUCH_ID: u32 = 2;
1762
1763 let descriptor =
1764 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1765 device_id: 1,
1766 contacts: vec![],
1767 });
1768 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1769
1770 let contact = fidl_fuchsia_input_report::ContactInputReport {
1771 contact_id: Some(TOUCH_ID),
1772 position_x: Some(0),
1773 position_y: Some(0),
1774 pressure: None,
1775 contact_width: None,
1776 contact_height: None,
1777 ..Default::default()
1778 };
1779 let reports = vec![create_touch_input_report(
1780 vec![contact],
1781 Some(vec![
1782 fidl_fuchsia_input_report::TouchButton::Palm,
1783 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1784 ]),
1785 event_time_i64,
1786 )];
1787
1788 let expected_events = vec![create_touch_screen_event_with_buttons(
1789 hashmap! {
1790 fidl_ui_input::PointerEventPhase::Add
1791 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1792 fidl_ui_input::PointerEventPhase::Down
1793 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1794 },
1795 vec![
1796 fidl_fuchsia_input_report::TouchButton::Palm,
1797 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1798 ],
1799 event_time_u64,
1800 &descriptor,
1801 )];
1802
1803 assert_input_report_sequence_generates_events_with_feature_flags!(
1804 input_reports: reports,
1805 expected_events: expected_events,
1806 device_descriptor: descriptor,
1807 device_type: TouchBinding,
1808 feature_flags: input_device::InputPipelineFeatureFlags {
1809 enable_merge_touch_events,
1810 ..Default::default()
1811 },
1812 );
1813 }
1814
1815 #[test_case(true; "merge touch events enabled")]
1817 #[test_case(false; "merge touch events disabled")]
1818 #[fasync::run_singlethreaded(test)]
1819 async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1820 let descriptor =
1821 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1822 device_id: 1,
1823 contacts: vec![],
1824 });
1825 let (event_time_i64, _) = testing_utilities::event_times();
1826
1827 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1828
1829 let expected_events: Vec<input_device::InputEvent> = vec![];
1830
1831 assert_input_report_sequence_generates_events_with_feature_flags!(
1832 input_reports: reports,
1833 expected_events: expected_events,
1834 device_descriptor: descriptor,
1835 device_type: TouchBinding,
1836 feature_flags: input_device::InputPipelineFeatureFlags {
1837 enable_merge_touch_events,
1838 ..Default::default()
1839 },
1840 );
1841 }
1842
1843 #[test_case(true; "merge touch events enabled")]
1845 #[test_case(false; "merge touch events disabled")]
1846 #[fasync::run_singlethreaded(test)]
1847 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1848 const TOUCH_ID: u32 = 2;
1849
1850 let descriptor =
1851 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1852 device_id: 1,
1853 contacts: vec![],
1854 });
1855 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1856
1857 let contact = fidl_fuchsia_input_report::ContactInputReport {
1858 contact_id: Some(TOUCH_ID),
1859 position_x: Some(0),
1860 position_y: Some(0),
1861 pressure: None,
1862 contact_width: None,
1863 contact_height: None,
1864 ..Default::default()
1865 };
1866 let reports = vec![
1867 create_touch_input_report(vec![contact], None, event_time_i64),
1868 create_touch_input_report(
1869 vec![],
1870 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1871 event_time_i64,
1872 ),
1873 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1874 ];
1875
1876 let expected_events = vec![
1877 create_touch_screen_event_with_buttons(
1878 hashmap! {
1879 fidl_ui_input::PointerEventPhase::Add
1880 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1881 fidl_ui_input::PointerEventPhase::Down
1882 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1883 },
1884 vec![],
1885 event_time_u64,
1886 &descriptor,
1887 ),
1888 create_touch_screen_event_with_buttons(
1889 hashmap! {},
1890 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1891 event_time_u64,
1892 &descriptor,
1893 ),
1894 create_touch_screen_event_with_buttons(
1895 hashmap! {},
1896 vec![],
1897 event_time_u64,
1898 &descriptor,
1899 ),
1900 ];
1901
1902 assert_input_report_sequence_generates_events_with_feature_flags!(
1903 input_reports: reports,
1904 expected_events: expected_events,
1905 device_descriptor: descriptor,
1906 device_type: TouchBinding,
1907 feature_flags: input_device::InputPipelineFeatureFlags {
1908 enable_merge_touch_events,
1909 ..Default::default()
1910 },
1911 );
1912 }
1913
1914 #[fasync::run_singlethreaded(test)]
1915 async fn process_reports_batches_events() {
1916 const TOUCH_ID: u32 = 2;
1917
1918 let descriptor =
1919 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1920 device_id: 1,
1921 contacts: vec![],
1922 });
1923 let (event_time_i64, _) = testing_utilities::event_times();
1924
1925 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1926 contact_id: Some(TOUCH_ID),
1927 position_x: Some(0),
1928 position_y: Some(0),
1929 ..Default::default()
1930 };
1931 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1932 contact_id: Some(TOUCH_ID),
1933 position_x: Some(10),
1934 position_y: Some(10),
1935 ..Default::default()
1936 };
1937 let reports = vec![
1938 create_touch_input_report(vec![contact1], None, event_time_i64),
1939 create_touch_input_report(vec![contact2], None, event_time_i64),
1940 ];
1941
1942 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1943
1944 let inspector = fuchsia_inspect::Inspector::default();
1945 let test_node = inspector.root().create_child("TestDevice_Touch");
1946 let mut inspect_status = InputDeviceStatus::new(test_node);
1947 inspect_status.health_node.set_ok();
1948
1949 let _ = TouchBinding::process_reports(
1950 reports,
1951 None,
1952 &descriptor,
1953 &mut event_sender,
1954 &inspect_status,
1955 &metrics::MetricsLogger::default(),
1956 &input_device::InputPipelineFeatureFlags::default(),
1957 );
1958
1959 let batch = event_receiver.try_next().expect("Expected a batch of events");
1961 let events = batch.expect("Expected events in the batch");
1962 assert_eq!(events.len(), 2);
1963
1964 assert!(event_receiver.try_next().is_err());
1966 }
1967
1968 #[fasync::run_singlethreaded(test)]
1969 async fn process_reports_merges_touch_events_when_enabled() {
1970 const TOUCH_ID: u32 = 2;
1971 let descriptor =
1972 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1973 device_id: 1,
1974 contacts: vec![],
1975 });
1976 let (event_time_i64, _) = testing_utilities::event_times();
1977
1978 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1979 contact_id: Some(TOUCH_ID),
1980 position_x: Some(0),
1981 position_y: Some(0),
1982 ..Default::default()
1983 };
1984 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1985 contact_id: Some(TOUCH_ID),
1986 position_x: Some(10),
1987 position_y: Some(10),
1988 ..Default::default()
1989 };
1990 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1991 contact_id: Some(TOUCH_ID),
1992 position_x: Some(20),
1993 position_y: Some(20),
1994 ..Default::default()
1995 };
1996 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1997 contact_id: Some(TOUCH_ID),
1998 position_x: Some(30),
1999 position_y: Some(30),
2000 ..Default::default()
2001 };
2002 let reports = vec![
2003 create_touch_input_report(vec![contact_add], None, event_time_i64),
2004 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2005 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2006 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2007 create_touch_input_report(vec![], None, event_time_i64),
2008 ];
2009
2010 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2011 let inspector = fuchsia_inspect::Inspector::default();
2012 let mut inspect_status =
2013 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2014 inspect_status.health_node.set_ok();
2015
2016 let _ = TouchBinding::process_reports(
2017 reports,
2018 None,
2019 &descriptor,
2020 &mut event_sender,
2021 &inspect_status,
2022 &metrics::MetricsLogger::default(),
2023 &input_device::InputPipelineFeatureFlags {
2024 enable_merge_touch_events: true,
2025 ..Default::default()
2026 },
2027 );
2028
2029 let batch = event_receiver.try_next().unwrap().unwrap();
2030
2031 assert_eq!(batch.len(), 3);
2033
2034 assert_matches!(
2036 &batch[0].device_event,
2037 input_device::InputDeviceEvent::TouchScreen(event)
2038 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2039 );
2040 assert_matches!(
2042 &batch[1].device_event,
2043 input_device::InputDeviceEvent::TouchScreen(event)
2044 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2045 );
2046 assert_matches!(
2048 &batch[2].device_event,
2049 input_device::InputDeviceEvent::TouchScreen(event)
2050 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2051 );
2052 }
2053
2054 #[fasync::run_singlethreaded(test)]
2055 async fn process_reports_does_not_merge_touch_events_when_disabled() {
2056 const TOUCH_ID: u32 = 2;
2057 let descriptor =
2058 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2059 device_id: 1,
2060 contacts: vec![],
2061 });
2062 let (event_time_i64, _) = testing_utilities::event_times();
2063
2064 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2065 contact_id: Some(TOUCH_ID),
2066 position_x: Some(0),
2067 position_y: Some(0),
2068 ..Default::default()
2069 };
2070 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2071 contact_id: Some(TOUCH_ID),
2072 position_x: Some(10),
2073 position_y: Some(10),
2074 ..Default::default()
2075 };
2076 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2077 contact_id: Some(TOUCH_ID),
2078 position_x: Some(20),
2079 position_y: Some(20),
2080 ..Default::default()
2081 };
2082 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2083 contact_id: Some(TOUCH_ID),
2084 position_x: Some(30),
2085 position_y: Some(30),
2086 ..Default::default()
2087 };
2088 let reports = vec![
2089 create_touch_input_report(vec![contact_add], None, event_time_i64),
2090 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2091 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2092 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2093 create_touch_input_report(vec![], None, event_time_i64),
2094 ];
2095
2096 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2097 let inspector = fuchsia_inspect::Inspector::default();
2098 let mut inspect_status =
2099 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2100 inspect_status.health_node.set_ok();
2101
2102 let _ = TouchBinding::process_reports(
2103 reports,
2104 None,
2105 &descriptor,
2106 &mut event_sender,
2107 &inspect_status,
2108 &metrics::MetricsLogger::default(),
2109 &input_device::InputPipelineFeatureFlags {
2110 enable_merge_touch_events: false,
2111 ..Default::default()
2112 },
2113 );
2114
2115 let batch = event_receiver.try_next().unwrap().unwrap();
2116
2117 assert_eq!(batch.len(), 5);
2119 }
2120}