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