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;
20
21use metrics_registry::*;
22use sorted_vec_map::{SortedVecMap, SortedVecSet};
23
24#[derive(Debug, PartialEq)]
40pub struct TouchScreenEvent {
41 pub contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
47
48 pub injector_contacts: SortedVecMap<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: SortedVecSet<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> = {
689 fuchsia_trace::duration!("input", "clone_with_wake_lease_batch");
690 batch
691 .into_iter()
692 .map(|event| {
693 let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
697 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
698 event
699 })
700 .collect()
701 };
702 fuchsia_trace::instant!(
703 "input",
704 "events_to_input_handlers",
705 fuchsia_trace::Scope::Thread,
706 "num_reports" => num_reports,
707 "num_events_generated" => events_to_send.len()
708 );
709
710 inspect_status.count_generated_events(&events_to_send);
712
713 if let Err(e) = input_event_sender.unbounded_send(events_to_send) {
714 metrics_logger.log_error(
715 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
716 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
717 );
718 }
719 }
720 (previous_report, None)
721}
722
723fn process_single_touch_screen_report(
724 mut report: InputReport,
725 previous_report: Option<InputReport>,
726 device_descriptor: &input_device::InputDeviceDescriptor,
727 inspect_status: &InputDeviceStatus,
728) -> (Option<InputReport>, Option<InputEvent>) {
729 fuchsia_trace::flow_end!("input", "input_report", report.trace_id.unwrap_or(0).into());
730
731 let wake_lease = report.wake_lease.take();
735
736 let touch_report: &fidl_next_fuchsia_input_report::TouchInputReport = match &report.touch {
738 Some(touch) => touch,
739 None => {
740 inspect_status.count_filtered_report();
741 return (previous_report, None);
742 }
743 };
744
745 let (previous_contacts, previous_buttons): (
746 SortedVecMap<u32, TouchContact>,
747 Vec<fidl_next_fuchsia_input_report::TouchButton>,
748 ) = previous_report
749 .as_ref()
750 .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
751 .map(touch_contacts_and_buttons_from_touch_report)
752 .unwrap_or_default();
753 let (current_contacts, current_buttons): (
754 SortedVecMap<u32, TouchContact>,
755 Vec<fidl_next_fuchsia_input_report::TouchButton>,
756 ) = touch_contacts_and_buttons_from_touch_report(touch_report);
757
758 if previous_contacts.is_empty()
760 && current_contacts.is_empty()
761 && previous_buttons.is_empty()
762 && current_buttons.is_empty()
763 {
764 inspect_status.count_filtered_report();
765 return (Some(report), None);
766 }
767
768 if current_contacts.is_empty()
771 && !previous_contacts.is_empty()
772 && (!current_buttons.is_empty() || !previous_buttons.is_empty())
773 {
774 if let Some(touch_report) = report.touch.as_mut() {
775 touch_report.contacts =
776 previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
777 }
778 }
779
780 let added_contacts: Vec<TouchContact> = Vec::from_iter(
782 current_contacts
783 .iter()
784 .map(|(_, v)| v.clone())
785 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
786 );
787 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
789 current_contacts
790 .iter()
791 .map(|(_, v)| v.clone())
792 .filter(|contact| previous_contacts.contains_key(&contact.id)),
793 );
794 let removed_contacts: Vec<TouchContact> =
796 Vec::from_iter(previous_contacts.iter().map(|(_, v)| v.clone()).filter(|contact| {
797 current_buttons.is_empty()
798 && previous_buttons.is_empty()
799 && !current_contacts.contains_key(&contact.id)
800 }));
801
802 let trace_id = fuchsia_trace::Id::random();
803 let event = create_touch_screen_event(
804 SortedVecMap::from_iter(vec![
805 (fidl_ui_input::PointerEventPhase::Add, added_contacts.clone()),
806 (fidl_ui_input::PointerEventPhase::Down, added_contacts.clone()),
807 (fidl_ui_input::PointerEventPhase::Move, moved_contacts.clone()),
808 (fidl_ui_input::PointerEventPhase::Up, removed_contacts.clone()),
809 (fidl_ui_input::PointerEventPhase::Remove, removed_contacts.clone()),
810 ]),
811 SortedVecMap::from_iter(vec![
812 (pointerinjector::EventPhase::Add, added_contacts),
813 (pointerinjector::EventPhase::Change, moved_contacts),
814 (pointerinjector::EventPhase::Remove, removed_contacts),
815 ]),
816 current_buttons,
817 device_descriptor,
818 trace_id,
819 wake_lease,
820 );
821
822 (Some(report), Some(event))
823}
824
825fn process_touchpad_reports(
826 reports: Vec<InputReport>,
827 mut previous_report: Option<InputReport>,
828 device_descriptor: &input_device::InputDeviceDescriptor,
829 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
830 inspect_status: &InputDeviceStatus,
831 metrics_logger: &metrics::MetricsLogger,
832) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
833 for report in reports {
834 inspect_status.count_received_report(&report);
835 let prev_report = process_single_touchpad_report(
836 report,
837 previous_report,
838 device_descriptor,
839 input_event_sender,
840 inspect_status,
841 metrics_logger,
842 );
843 previous_report = prev_report;
844 }
845 (previous_report, None)
846}
847
848fn process_single_touchpad_report(
849 report: InputReport,
850 previous_report: Option<InputReport>,
851 device_descriptor: &input_device::InputDeviceDescriptor,
852 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
853 inspect_status: &InputDeviceStatus,
854 metrics_logger: &metrics::MetricsLogger,
855) -> Option<InputReport> {
856 if let Some(trace_id) = report.trace_id {
857 fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
858 }
859
860 let touch_report: &fidl_next_fuchsia_input_report::TouchInputReport = match &report.touch {
862 Some(touch) => touch,
863 None => {
864 inspect_status.count_filtered_report();
865 return previous_report;
866 }
867 };
868
869 let current_contacts: Vec<TouchContact> = touch_report
870 .contacts
871 .as_ref()
872 .and_then(|unwrapped_contacts| {
873 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
875 })
876 .unwrap_or_default();
877
878 let buttons: SortedVecSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
879 Some(buttons) => {
880 SortedVecSet::from_iter(buttons.iter().filter_map(|button| match button {
881 fidl_next_fuchsia_input_report::TouchButton::Palm => Some(1),
882 fidl_next_fuchsia_input_report::TouchButton::SwipeUp
883 | fidl_next_fuchsia_input_report::TouchButton::SwipeLeft
884 | fidl_next_fuchsia_input_report::TouchButton::SwipeRight
885 | fidl_next_fuchsia_input_report::TouchButton::SwipeDown => {
886 log::warn!("Swipe buttons {:?} are not supported", button);
888 None
889 }
890 fidl_next_fuchsia_input_report::TouchButton::UnknownOrdinal_(unknown_ordinal) => {
891 log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
892 None
893 }
894 }))
895 }
896 None => SortedVecSet::new(),
897 };
898
899 let trace_id = fuchsia_trace::Id::random();
900 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
901 send_touchpad_event(
902 current_contacts,
903 buttons,
904 device_descriptor,
905 input_event_sender,
906 trace_id,
907 inspect_status,
908 metrics_logger,
909 );
910
911 Some(report)
912}
913
914fn touch_contacts_and_buttons_from_touch_report(
915 touch_report: &fidl_next_fuchsia_input_report::TouchInputReport,
916) -> (SortedVecMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
917 let contacts: Vec<TouchContact> = touch_report
919 .contacts
920 .as_ref()
921 .and_then(|unwrapped_contacts| {
922 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
924 })
925 .unwrap_or_default();
926
927 (
928 SortedVecMap::from_iter(contacts.into_iter().map(|contact| (contact.id, contact))),
929 touch_report.pressed_buttons.clone().unwrap_or_default(),
930 )
931}
932
933fn create_touch_screen_event(
943 contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
944 injector_contacts: SortedVecMap<pointerinjector::EventPhase, Vec<TouchContact>>,
945 pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
946 device_descriptor: &input_device::InputDeviceDescriptor,
947 trace_id: fuchsia_trace::Id,
948 wake_lease: Option<zx::EventPair>,
949) -> InputEvent {
950 input_device::InputEvent {
951 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
952 contacts,
953 injector_contacts,
954 pressed_buttons,
955 wake_lease,
956 }),
957 device_descriptor: device_descriptor.clone(),
958 event_time: zx::MonotonicInstant::get(),
959 handled: Handled::No,
960 trace_id: Some(trace_id),
961 }
962}
963
964fn send_touchpad_event(
972 injector_contacts: Vec<TouchContact>,
973 pressed_buttons: SortedVecSet<mouse_binding::MouseButton>,
974 device_descriptor: &input_device::InputDeviceDescriptor,
975 input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
976 trace_id: fuchsia_trace::Id,
977 inspect_status: &InputDeviceStatus,
978 metrics_logger: &metrics::MetricsLogger,
979) {
980 let event = input_device::InputEvent {
981 device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
982 injector_contacts,
983 pressed_buttons,
984 }),
985 device_descriptor: device_descriptor.clone(),
986 event_time: zx::MonotonicInstant::get(),
987 handled: Handled::No,
988 trace_id: Some(trace_id),
989 };
990
991 let events = vec![event];
992 inspect_status.count_generated_events(&events);
993
994 if let Err(e) = input_event_sender.unbounded_send(events) {
995 metrics_logger.log_error(
996 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
997 std::format!("Failed to send TouchpadEvent with error: {:?}", e),
998 );
999 }
1000}
1001
1002async fn get_device_type(
1008 input_device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
1009) -> TouchDeviceType {
1010 match input_device.get_feature_report().await {
1011 Ok(Ok(fidl_next_fuchsia_input_report::InputDeviceGetFeatureReportResponse {
1012 report: fidl_next_fuchsia_input_report::FeatureReport {
1013 touch:
1014 Some(fidl_next_fuchsia_input_report::TouchFeatureReport {
1015 input_mode:
1016 Some(
1017 fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection
1018 | fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
1019 ),
1020 ..
1021 }),
1022 ..
1023 }
1024 })) => TouchDeviceType::WindowsPrecisionTouchpad,
1025 _ => TouchDeviceType::TouchScreen,
1026 }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032 use crate::testing_utilities::{
1033 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1034 create_touch_screen_event_with_buttons, create_touchpad_event, spawn_input_stream_handler,
1035 };
1036 use crate::utils::Position;
1037 use assert_matches::assert_matches;
1038 use diagnostics_assertions::AnyProperty;
1039 use fuchsia_async as fasync;
1040 use futures::StreamExt;
1041 use pretty_assertions::assert_eq;
1042 use test_case::test_case;
1043
1044 #[fasync::run_singlethreaded(test)]
1045 async fn process_empty_reports() {
1046 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1047 let previous_report = create_touch_input_report(
1048 vec![],
1049 None,
1050 previous_report_time,
1051 );
1052 let report_time = zx::MonotonicInstant::get().into_nanos();
1053 let report =
1054 create_touch_input_report(vec![], None, report_time);
1055
1056 let descriptor =
1057 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1058 device_id: 1,
1059 contacts: vec![],
1060 });
1061 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1062
1063 let inspector = fuchsia_inspect::Inspector::default();
1064 let test_node = inspector.root().create_child("TestDevice_Touch");
1065 let mut inspect_status = InputDeviceStatus::new(test_node);
1066 inspect_status.health_node.set_ok();
1067
1068 let (returned_report, _) = TouchBinding::process_reports(
1069 vec![report],
1070 Some(previous_report),
1071 &descriptor,
1072 &mut event_sender,
1073 &inspect_status,
1074 &metrics::MetricsLogger::default(),
1075 &input_device::InputPipelineFeatureFlags::default(),
1076 );
1077 assert!(returned_report.is_some());
1078 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1079
1080 let event = event_receiver.try_next();
1082 assert!(event.is_err());
1083
1084 diagnostics_assertions::assert_data_tree!(inspector, root: {
1085 "TestDevice_Touch": contains {
1086 reports_received_count: 1u64,
1087 reports_filtered_count: 1u64,
1088 events_generated: 0u64,
1089 last_received_timestamp_ns: report_time as u64,
1090 last_generated_timestamp_ns: 0u64,
1091 "fuchsia.inspect.Health": {
1092 status: "OK",
1093 start_timestamp_nanos: AnyProperty
1096 },
1097 }
1098 });
1099 }
1100
1101 #[fasync::run_singlethreaded(test)]
1103 async fn add_and_down() {
1104 const TOUCH_ID: u32 = 2;
1105
1106 let descriptor =
1107 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1108 device_id: 1,
1109 contacts: vec![],
1110 });
1111 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1112
1113 let contact = fidl_fuchsia_input_report::ContactInputReport {
1114 contact_id: Some(TOUCH_ID),
1115 position_x: Some(0),
1116 position_y: Some(0),
1117 pressure: None,
1118 contact_width: None,
1119 contact_height: None,
1120 ..Default::default()
1121 };
1122 let reports = vec![create_touch_input_report(
1123 vec![contact],
1124 None,
1125 event_time_i64,
1126 )];
1127
1128 let expected_events = vec![create_touch_screen_event(
1129 SortedVecMap::from_iter(vec![
1130 (
1131 fidl_ui_input::PointerEventPhase::Add,
1132 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1133 ),
1134 (
1135 fidl_ui_input::PointerEventPhase::Down,
1136 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1137 ),
1138 ]),
1139 event_time_u64,
1140 &descriptor,
1141 )];
1142
1143 assert_input_report_sequence_generates_events!(
1144 input_reports: reports,
1145 expected_events: expected_events,
1146 device_descriptor: descriptor,
1147 device_type: TouchBinding,
1148 );
1149 }
1150
1151 #[fasync::run_singlethreaded(test)]
1153 async fn up_and_remove() {
1154 const TOUCH_ID: u32 = 2;
1155
1156 let descriptor =
1157 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1158 device_id: 1,
1159 contacts: vec![],
1160 });
1161 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1162
1163 let contact = fidl_fuchsia_input_report::ContactInputReport {
1164 contact_id: Some(TOUCH_ID),
1165 position_x: Some(0),
1166 position_y: Some(0),
1167 pressure: None,
1168 contact_width: None,
1169 contact_height: None,
1170 ..Default::default()
1171 };
1172 let reports = vec![
1173 create_touch_input_report(
1174 vec![contact],
1175 None,
1176 event_time_i64,
1177 ),
1178 create_touch_input_report(vec![], None, event_time_i64),
1179 ];
1180
1181 let expected_events = vec![
1182 create_touch_screen_event(
1183 SortedVecMap::from_iter(vec![
1184 (
1185 fidl_ui_input::PointerEventPhase::Add,
1186 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1187 ),
1188 (
1189 fidl_ui_input::PointerEventPhase::Down,
1190 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1191 ),
1192 ]),
1193 event_time_u64,
1194 &descriptor,
1195 ),
1196 create_touch_screen_event(
1197 SortedVecMap::from_iter(vec![
1198 (
1199 fidl_ui_input::PointerEventPhase::Up,
1200 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1201 ),
1202 (
1203 fidl_ui_input::PointerEventPhase::Remove,
1204 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1205 ),
1206 ]),
1207 event_time_u64,
1208 &descriptor,
1209 ),
1210 ];
1211
1212 assert_input_report_sequence_generates_events!(
1213 input_reports: reports,
1214 expected_events: expected_events,
1215 device_descriptor: descriptor,
1216 device_type: TouchBinding,
1217 );
1218 }
1219
1220 #[fasync::run_singlethreaded(test)]
1222 async fn add_down_move() {
1223 const TOUCH_ID: u32 = 2;
1224 let first = Position { x: 10.0, y: 30.0 };
1225 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1226
1227 let descriptor =
1228 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1229 device_id: 1,
1230 contacts: vec![],
1231 });
1232 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1233
1234 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1235 contact_id: Some(TOUCH_ID),
1236 position_x: Some(first.x as i64),
1237 position_y: Some(first.y as i64),
1238 pressure: None,
1239 contact_width: None,
1240 contact_height: None,
1241 ..Default::default()
1242 };
1243 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1244 contact_id: Some(TOUCH_ID),
1245 position_x: Some(first.x as i64 * 2),
1246 position_y: Some(first.y as i64 * 2),
1247 pressure: None,
1248 contact_width: None,
1249 contact_height: None,
1250 ..Default::default()
1251 };
1252
1253 let reports = vec![
1254 create_touch_input_report(
1255 vec![first_contact],
1256 None,
1257 event_time_i64,
1258 ),
1259 create_touch_input_report(
1260 vec![second_contact],
1261 None,
1262 event_time_i64,
1263 ),
1264 ];
1265
1266 let expected_events = vec![
1267 create_touch_screen_event(
1268 SortedVecMap::from_iter(vec![
1269 (
1270 fidl_ui_input::PointerEventPhase::Add,
1271 vec![create_touch_contact(TOUCH_ID, first)],
1272 ),
1273 (
1274 fidl_ui_input::PointerEventPhase::Down,
1275 vec![create_touch_contact(TOUCH_ID, first)],
1276 ),
1277 ]),
1278 event_time_u64,
1279 &descriptor,
1280 ),
1281 create_touch_screen_event(
1282 SortedVecMap::from_iter(vec![(
1283 fidl_ui_input::PointerEventPhase::Move,
1284 vec![create_touch_contact(TOUCH_ID, second)],
1285 )]),
1286 event_time_u64,
1287 &descriptor,
1288 ),
1289 ];
1290
1291 assert_input_report_sequence_generates_events!(
1292 input_reports: reports,
1293 expected_events: expected_events,
1294 device_descriptor: descriptor,
1295 device_type: TouchBinding,
1296 );
1297 }
1298
1299 #[fasync::run_singlethreaded(test)]
1300 async fn sent_event_has_trace_id() {
1301 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1302 let previous_report = create_touch_input_report(
1303 vec![],
1304 None,
1305 previous_report_time,
1306 );
1307
1308 let report_time = zx::MonotonicInstant::get().into_nanos();
1309 let contact = fidl_fuchsia_input_report::ContactInputReport {
1310 contact_id: Some(222),
1311 position_x: Some(333),
1312 position_y: Some(444),
1313 ..Default::default()
1314 };
1315 let report =
1316 create_touch_input_report(vec![contact], None, report_time);
1317
1318 let descriptor =
1319 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1320 device_id: 1,
1321 contacts: vec![],
1322 });
1323 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1324
1325 let inspector = fuchsia_inspect::Inspector::default();
1326 let test_node = inspector.root().create_child("TestDevice_Touch");
1327 let mut inspect_status = InputDeviceStatus::new(test_node);
1328 inspect_status.health_node.set_ok();
1329
1330 let _ = TouchBinding::process_reports(
1331 vec![report],
1332 Some(previous_report),
1333 &descriptor,
1334 &mut event_sender,
1335 &inspect_status,
1336 &metrics::MetricsLogger::default(),
1337 &input_device::InputPipelineFeatureFlags::default(),
1338 );
1339 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1340 }
1341
1342 #[fuchsia::test(allow_stalls = false)]
1343 async fn enables_touchpad_mode_automatically() {
1344 let (set_feature_report_sender, set_feature_report_receiver) =
1345 futures::channel::mpsc::unbounded();
1346 let input_device_proxy = spawn_input_stream_handler(move |input_device_request| {
1347 let set_feature_report_sender = set_feature_report_sender.clone();
1348 async move {
1349 match input_device_request {
1350 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1351 let _ = responder.send(&get_touchpad_device_descriptor(
1352 true, ));
1354 }
1355 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1356 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1357 touch: Some(fidl_input_report::TouchFeatureReport {
1358 input_mode: Some(
1359 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1360 ),
1361 ..Default::default()
1362 }),
1363 ..Default::default()
1364 }));
1365 }
1366 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1367 responder,
1368 report,
1369 } => {
1370 match set_feature_report_sender.unbounded_send(report) {
1371 Ok(_) => {
1372 let _ = responder.send(Ok(()));
1373 }
1374 Err(e) => {
1375 panic!("try_send set_feature_report_request failed: {}", e);
1376 }
1377 };
1378 }
1379 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1380 }
1382 r => panic!("unsupported request {:?}", r),
1383 }
1384 }
1385 });
1386
1387 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1388
1389 let inspector = fuchsia_inspect::Inspector::default();
1391 let test_node = inspector.root().create_child("test_node");
1392
1393 TouchBinding::new(
1397 input_device_proxy,
1398 0,
1399 device_event_sender,
1400 test_node,
1401 input_device::InputPipelineFeatureFlags::default(),
1402 metrics::MetricsLogger::default(),
1403 )
1404 .await
1405 .unwrap();
1406 assert_matches!(
1407 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1408 [fidl_input_report::FeatureReport {
1409 touch: Some(fidl_input_report::TouchFeatureReport {
1410 input_mode: Some(
1411 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1412 ),
1413 ..
1414 }),
1415 ..
1416 }]
1417 );
1418 }
1419
1420 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1421 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1422 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1423 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1424 #[fuchsia::test(allow_stalls = false)]
1425 async fn identifies_correct_touch_device_type(
1426 has_mouse_descriptor: bool,
1427 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1428 expect_touch_device_type: TouchDeviceType,
1429 ) {
1430 let input_device_proxy =
1431 spawn_input_stream_handler(move |input_device_request| async move {
1432 match input_device_request {
1433 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1434 let _ =
1435 responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1436 }
1437 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1438 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1439 touch: Some(fidl_input_report::TouchFeatureReport {
1440 input_mode: touch_input_mode,
1441 ..Default::default()
1442 }),
1443 ..Default::default()
1444 }));
1445 }
1446 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1447 responder, ..
1448 } => {
1449 let _ = responder.send(Ok(()));
1450 }
1451 r => panic!("unsupported request {:?}", r),
1452 }
1453 });
1454
1455 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1456
1457 let inspector = fuchsia_inspect::Inspector::default();
1459 let test_node = inspector.root().create_child("test_node");
1460
1461 let binding = TouchBinding::new(
1462 input_device_proxy,
1463 0,
1464 device_event_sender,
1465 test_node,
1466 input_device::InputPipelineFeatureFlags::default(),
1467 metrics::MetricsLogger::default(),
1468 )
1469 .await
1470 .unwrap();
1471 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1472 }
1473
1474 fn get_touchpad_device_descriptor(
1477 has_mouse_descriptor: bool,
1478 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1479 fidl_input_report::DeviceDescriptor {
1480 mouse: match has_mouse_descriptor {
1481 true => Some(fidl_input_report::MouseDescriptor::default()),
1482 false => None,
1483 },
1484 touch: Some(fidl_input_report::TouchDescriptor {
1485 input: Some(fidl_input_report::TouchInputDescriptor {
1486 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1487 position_x: Some(fidl_input_report::Axis {
1488 range: fidl_input_report::Range { min: 1, max: 2 },
1489 unit: fidl_input_report::Unit {
1490 type_: fidl_input_report::UnitType::None,
1491 exponent: 0,
1492 },
1493 }),
1494 position_y: Some(fidl_input_report::Axis {
1495 range: fidl_input_report::Range { min: 2, max: 3 },
1496 unit: fidl_input_report::Unit {
1497 type_: fidl_input_report::UnitType::Other,
1498 exponent: 100000,
1499 },
1500 }),
1501 pressure: Some(fidl_input_report::Axis {
1502 range: fidl_input_report::Range { min: 3, max: 4 },
1503 unit: fidl_input_report::Unit {
1504 type_: fidl_input_report::UnitType::Grams,
1505 exponent: -991,
1506 },
1507 }),
1508 contact_width: Some(fidl_input_report::Axis {
1509 range: fidl_input_report::Range { min: 5, max: 6 },
1510 unit: fidl_input_report::Unit {
1511 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1512 exponent: 123,
1513 },
1514 }),
1515 contact_height: Some(fidl_input_report::Axis {
1516 range: fidl_input_report::Range { min: 7, max: 8 },
1517 unit: fidl_input_report::Unit {
1518 type_: fidl_input_report::UnitType::Pascals,
1519 exponent: 100,
1520 },
1521 }),
1522 ..Default::default()
1523 }]),
1524 ..Default::default()
1525 }),
1526 ..Default::default()
1527 }),
1528 ..Default::default()
1529 }
1530 }
1531
1532 #[fasync::run_singlethreaded(test)]
1533 async fn send_touchpad_event_button() {
1534 const TOUCH_ID: u32 = 1;
1535 const PRIMARY_BUTTON: u8 = 255;
1536
1537 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1538 device_id: 1,
1539 contacts: vec![],
1540 });
1541 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1542
1543 let contact = fidl_fuchsia_input_report::ContactInputReport {
1544 contact_id: Some(TOUCH_ID),
1545 position_x: Some(0),
1546 position_y: Some(0),
1547 pressure: None,
1548 contact_width: None,
1549 contact_height: None,
1550 ..Default::default()
1551 };
1552 let reports = vec![create_touch_input_report(
1553 vec![contact],
1554 Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1555 unknown_ordinal: PRIMARY_BUTTON,
1556 }]),
1557 event_time_i64,
1558 )];
1559
1560 let expected_events = vec![create_touchpad_event(
1561 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1562 vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1563 unknown_ordinal: PRIMARY_BUTTON,
1564 }]
1565 .into_iter()
1566 .collect(),
1567 event_time_u64,
1568 &descriptor,
1569 )];
1570
1571 assert_input_report_sequence_generates_events!(
1572 input_reports: reports,
1573 expected_events: expected_events,
1574 device_descriptor: descriptor,
1575 device_type: TouchBinding,
1576 );
1577 }
1578
1579 #[fasync::run_singlethreaded(test)]
1580 async fn send_touchpad_event_2_fingers_down_up() {
1581 const TOUCH_ID_1: u32 = 1;
1582 const TOUCH_ID_2: u32 = 2;
1583
1584 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1585 device_id: 1,
1586 contacts: vec![],
1587 });
1588 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1589
1590 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1591 contact_id: Some(TOUCH_ID_1),
1592 position_x: Some(0),
1593 position_y: Some(0),
1594 pressure: None,
1595 contact_width: None,
1596 contact_height: None,
1597 ..Default::default()
1598 };
1599 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1600 contact_id: Some(TOUCH_ID_2),
1601 position_x: Some(10),
1602 position_y: Some(10),
1603 pressure: None,
1604 contact_width: None,
1605 contact_height: None,
1606 ..Default::default()
1607 };
1608 let reports = vec![
1609 create_touch_input_report(
1610 vec![contact1, contact2],
1611 None,
1612 event_time_i64,
1613 ),
1614 create_touch_input_report(vec![], None, event_time_i64),
1615 ];
1616
1617 let expected_events = vec![
1618 create_touchpad_event(
1619 vec![
1620 create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1621 create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1622 ],
1623 SortedVecSet::new(),
1624 event_time_u64,
1625 &descriptor,
1626 ),
1627 create_touchpad_event(vec![], SortedVecSet::new(), event_time_u64, &descriptor),
1628 ];
1629
1630 assert_input_report_sequence_generates_events!(
1631 input_reports: reports,
1632 expected_events: expected_events,
1633 device_descriptor: descriptor,
1634 device_type: TouchBinding,
1635 );
1636 }
1637
1638 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1639 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1640 #[fasync::run_singlethreaded(test)]
1641 async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1642 const TOUCH_ID: u32 = 1;
1643
1644 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1645 device_id: 1,
1646 contacts: vec![],
1647 });
1648 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1649
1650 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1651 contact_id: Some(TOUCH_ID),
1652 position_x: Some(p0.x as i64),
1653 position_y: Some(p0.y as i64),
1654 pressure: None,
1655 contact_width: None,
1656 contact_height: None,
1657 ..Default::default()
1658 };
1659 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1660 contact_id: Some(TOUCH_ID),
1661 position_x: Some(p1.x as i64),
1662 position_y: Some(p1.y as i64),
1663 pressure: None,
1664 contact_width: None,
1665 contact_height: None,
1666 ..Default::default()
1667 };
1668 let reports = vec![
1669 create_touch_input_report(
1670 vec![contact1],
1671 None,
1672 event_time_i64,
1673 ),
1674 create_touch_input_report(
1675 vec![contact2],
1676 None,
1677 event_time_i64,
1678 ),
1679 ];
1680
1681 let expected_events = vec![
1682 create_touchpad_event(
1683 vec![create_touch_contact(TOUCH_ID, p0)],
1684 SortedVecSet::new(),
1685 event_time_u64,
1686 &descriptor,
1687 ),
1688 create_touchpad_event(
1689 vec![create_touch_contact(TOUCH_ID, p1)],
1690 SortedVecSet::new(),
1691 event_time_u64,
1692 &descriptor,
1693 ),
1694 ];
1695
1696 assert_input_report_sequence_generates_events!(
1697 input_reports: reports,
1698 expected_events: expected_events,
1699 device_descriptor: descriptor,
1700 device_type: TouchBinding,
1701 );
1702 }
1703
1704 #[test_case(true; "merge touch events enabled")]
1707 #[test_case(false; "merge touch events disabled")]
1708 #[fasync::run_singlethreaded(test)]
1709 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1710 let descriptor =
1711 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1712 device_id: 1,
1713 contacts: vec![],
1714 });
1715 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1716
1717 let reports = vec![create_touch_input_report(
1718 vec![],
1719 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1720 event_time_i64,
1721 )];
1722
1723 let expected_events = vec![create_touch_screen_event_with_buttons(
1724 SortedVecMap::new(),
1725 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1726 event_time_u64,
1727 &descriptor,
1728 )];
1729
1730 assert_input_report_sequence_generates_events_with_feature_flags!(
1731 input_reports: reports,
1732 expected_events: expected_events,
1733 device_descriptor: descriptor,
1734 device_type: TouchBinding,
1735 feature_flags: input_device::InputPipelineFeatureFlags {
1736 enable_merge_touch_events,
1737 ..Default::default()
1738 },
1739 );
1740 }
1741
1742 #[test_case(true; "merge touch events enabled")]
1745 #[test_case(false; "merge touch events disabled")]
1746 #[fasync::run_singlethreaded(test)]
1747 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1748 const TOUCH_ID: u32 = 2;
1749
1750 let descriptor =
1751 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1752 device_id: 1,
1753 contacts: vec![],
1754 });
1755 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1756
1757 let contact = fidl_fuchsia_input_report::ContactInputReport {
1758 contact_id: Some(TOUCH_ID),
1759 position_x: Some(0),
1760 position_y: Some(0),
1761 pressure: None,
1762 contact_width: None,
1763 contact_height: None,
1764 ..Default::default()
1765 };
1766 let reports = vec![create_touch_input_report(
1767 vec![contact],
1768 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1769 event_time_i64,
1770 )];
1771
1772 let expected_events = vec![create_touch_screen_event_with_buttons(
1773 SortedVecMap::from_iter(vec![
1774 (
1775 fidl_ui_input::PointerEventPhase::Add,
1776 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1777 ),
1778 (
1779 fidl_ui_input::PointerEventPhase::Down,
1780 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1781 ),
1782 ]),
1783 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1784 event_time_u64,
1785 &descriptor,
1786 )];
1787
1788 assert_input_report_sequence_generates_events_with_feature_flags!(
1789 input_reports: reports,
1790 expected_events: expected_events,
1791 device_descriptor: descriptor,
1792 device_type: TouchBinding,
1793 feature_flags: input_device::InputPipelineFeatureFlags {
1794 enable_merge_touch_events,
1795 ..Default::default()
1796 },
1797 );
1798 }
1799
1800 #[test_case(true; "merge touch events enabled")]
1803 #[test_case(false; "merge touch events disabled")]
1804 #[fasync::run_singlethreaded(test)]
1805 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1806 const TOUCH_ID: u32 = 2;
1807
1808 let descriptor =
1809 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1810 device_id: 1,
1811 contacts: vec![],
1812 });
1813 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1814
1815 let contact = fidl_fuchsia_input_report::ContactInputReport {
1816 contact_id: Some(TOUCH_ID),
1817 position_x: Some(0),
1818 position_y: Some(0),
1819 pressure: None,
1820 contact_width: None,
1821 contact_height: None,
1822 ..Default::default()
1823 };
1824 let reports = vec![create_touch_input_report(
1825 vec![contact],
1826 Some(vec![
1827 fidl_fuchsia_input_report::TouchButton::Palm,
1828 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1829 ]),
1830 event_time_i64,
1831 )];
1832
1833 let expected_events = vec![create_touch_screen_event_with_buttons(
1834 SortedVecMap::from_iter(vec![
1835 (
1836 fidl_ui_input::PointerEventPhase::Add,
1837 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1838 ),
1839 (
1840 fidl_ui_input::PointerEventPhase::Down,
1841 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1842 ),
1843 ]),
1844 vec![
1845 fidl_fuchsia_input_report::TouchButton::Palm,
1846 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1847 ],
1848 event_time_u64,
1849 &descriptor,
1850 )];
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_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1869 let descriptor =
1870 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1871 device_id: 1,
1872 contacts: vec![],
1873 });
1874 let (event_time_i64, _) = testing_utilities::event_times();
1875
1876 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1877
1878 let expected_events: Vec<input_device::InputEvent> = vec![];
1879
1880 assert_input_report_sequence_generates_events_with_feature_flags!(
1881 input_reports: reports,
1882 expected_events: expected_events,
1883 device_descriptor: descriptor,
1884 device_type: TouchBinding,
1885 feature_flags: input_device::InputPipelineFeatureFlags {
1886 enable_merge_touch_events,
1887 ..Default::default()
1888 },
1889 );
1890 }
1891
1892 #[test_case(true; "merge touch events enabled")]
1894 #[test_case(false; "merge touch events disabled")]
1895 #[fasync::run_singlethreaded(test)]
1896 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1897 const TOUCH_ID: u32 = 2;
1898
1899 let descriptor =
1900 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1901 device_id: 1,
1902 contacts: vec![],
1903 });
1904 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1905
1906 let contact = fidl_fuchsia_input_report::ContactInputReport {
1907 contact_id: Some(TOUCH_ID),
1908 position_x: Some(0),
1909 position_y: Some(0),
1910 pressure: None,
1911 contact_width: None,
1912 contact_height: None,
1913 ..Default::default()
1914 };
1915 let reports = vec![
1916 create_touch_input_report(vec![contact], None, event_time_i64),
1917 create_touch_input_report(
1918 vec![],
1919 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1920 event_time_i64,
1921 ),
1922 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1923 ];
1924
1925 let expected_events = vec![
1926 create_touch_screen_event_with_buttons(
1927 SortedVecMap::from_iter(vec![
1928 (
1929 fidl_ui_input::PointerEventPhase::Add,
1930 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1931 ),
1932 (
1933 fidl_ui_input::PointerEventPhase::Down,
1934 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1935 ),
1936 ]),
1937 vec![],
1938 event_time_u64,
1939 &descriptor,
1940 ),
1941 create_touch_screen_event_with_buttons(
1942 SortedVecMap::new(),
1943 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1944 event_time_u64,
1945 &descriptor,
1946 ),
1947 create_touch_screen_event_with_buttons(
1948 SortedVecMap::new(),
1949 vec![],
1950 event_time_u64,
1951 &descriptor,
1952 ),
1953 ];
1954
1955 assert_input_report_sequence_generates_events_with_feature_flags!(
1956 input_reports: reports,
1957 expected_events: expected_events,
1958 device_descriptor: descriptor,
1959 device_type: TouchBinding,
1960 feature_flags: input_device::InputPipelineFeatureFlags {
1961 enable_merge_touch_events,
1962 ..Default::default()
1963 },
1964 );
1965 }
1966
1967 #[fasync::run_singlethreaded(test)]
1968 async fn process_reports_batches_events() {
1969 const TOUCH_ID: u32 = 2;
1970
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 contact1 = 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 contact2 = 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 reports = vec![
1991 create_touch_input_report(vec![contact1], None, event_time_i64),
1992 create_touch_input_report(vec![contact2], None, event_time_i64),
1993 ];
1994
1995 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1996
1997 let inspector = fuchsia_inspect::Inspector::default();
1998 let test_node = inspector.root().create_child("TestDevice_Touch");
1999 let mut inspect_status = InputDeviceStatus::new(test_node);
2000 inspect_status.health_node.set_ok();
2001
2002 let _ = TouchBinding::process_reports(
2003 reports,
2004 None,
2005 &descriptor,
2006 &mut event_sender,
2007 &inspect_status,
2008 &metrics::MetricsLogger::default(),
2009 &input_device::InputPipelineFeatureFlags::default(),
2010 );
2011
2012 let batch = event_receiver.try_next().expect("Expected a batch of events");
2014 let events = batch.expect("Expected events in the batch");
2015 assert_eq!(events.len(), 2);
2016
2017 assert!(event_receiver.try_next().is_err());
2019 }
2020
2021 #[fasync::run_singlethreaded(test)]
2022 async fn process_reports_merges_touch_events_when_enabled() {
2023 const TOUCH_ID: u32 = 2;
2024 let descriptor =
2025 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2026 device_id: 1,
2027 contacts: vec![],
2028 });
2029 let (event_time_i64, _) = testing_utilities::event_times();
2030
2031 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2032 contact_id: Some(TOUCH_ID),
2033 position_x: Some(0),
2034 position_y: Some(0),
2035 ..Default::default()
2036 };
2037 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2038 contact_id: Some(TOUCH_ID),
2039 position_x: Some(10),
2040 position_y: Some(10),
2041 ..Default::default()
2042 };
2043 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2044 contact_id: Some(TOUCH_ID),
2045 position_x: Some(20),
2046 position_y: Some(20),
2047 ..Default::default()
2048 };
2049 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2050 contact_id: Some(TOUCH_ID),
2051 position_x: Some(30),
2052 position_y: Some(30),
2053 ..Default::default()
2054 };
2055 let reports = vec![
2056 create_touch_input_report(vec![contact_add], None, event_time_i64),
2057 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2058 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2059 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2060 create_touch_input_report(vec![], None, event_time_i64),
2061 ];
2062
2063 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2064 let inspector = fuchsia_inspect::Inspector::default();
2065 let mut inspect_status =
2066 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2067 inspect_status.health_node.set_ok();
2068
2069 let _ = TouchBinding::process_reports(
2070 reports,
2071 None,
2072 &descriptor,
2073 &mut event_sender,
2074 &inspect_status,
2075 &metrics::MetricsLogger::default(),
2076 &input_device::InputPipelineFeatureFlags {
2077 enable_merge_touch_events: true,
2078 ..Default::default()
2079 },
2080 );
2081
2082 let batch = event_receiver.try_next().unwrap().unwrap();
2083
2084 assert_eq!(batch.len(), 3);
2086
2087 assert_matches!(
2089 &batch[0].device_event,
2090 input_device::InputDeviceEvent::TouchScreen(event)
2091 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2092 );
2093 assert_matches!(
2095 &batch[1].device_event,
2096 input_device::InputDeviceEvent::TouchScreen(event)
2097 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2098 );
2099 assert_matches!(
2101 &batch[2].device_event,
2102 input_device::InputDeviceEvent::TouchScreen(event)
2103 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2104 );
2105 }
2106
2107 #[fasync::run_singlethreaded(test)]
2108 async fn process_reports_does_not_merge_touch_events_when_disabled() {
2109 const TOUCH_ID: u32 = 2;
2110 let descriptor =
2111 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2112 device_id: 1,
2113 contacts: vec![],
2114 });
2115 let (event_time_i64, _) = testing_utilities::event_times();
2116
2117 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2118 contact_id: Some(TOUCH_ID),
2119 position_x: Some(0),
2120 position_y: Some(0),
2121 ..Default::default()
2122 };
2123 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2124 contact_id: Some(TOUCH_ID),
2125 position_x: Some(10),
2126 position_y: Some(10),
2127 ..Default::default()
2128 };
2129 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2130 contact_id: Some(TOUCH_ID),
2131 position_x: Some(20),
2132 position_y: Some(20),
2133 ..Default::default()
2134 };
2135 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2136 contact_id: Some(TOUCH_ID),
2137 position_x: Some(30),
2138 position_y: Some(30),
2139 ..Default::default()
2140 };
2141 let reports = vec![
2142 create_touch_input_report(vec![contact_add], None, event_time_i64),
2143 create_touch_input_report(vec![contact_move1], None, event_time_i64),
2144 create_touch_input_report(vec![contact_move2], None, event_time_i64),
2145 create_touch_input_report(vec![contact_move3], None, event_time_i64),
2146 create_touch_input_report(vec![], None, event_time_i64),
2147 ];
2148
2149 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2150 let inspector = fuchsia_inspect::Inspector::default();
2151 let mut inspect_status =
2152 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2153 inspect_status.health_node.set_ok();
2154
2155 let _ = TouchBinding::process_reports(
2156 reports,
2157 None,
2158 &descriptor,
2159 &mut event_sender,
2160 &inspect_status,
2161 &metrics::MetricsLogger::default(),
2162 &input_device::InputPipelineFeatureFlags {
2163 enable_merge_touch_events: false,
2164 ..Default::default()
2165 },
2166 );
2167
2168 let batch = event_receiver.try_next().unwrap().unwrap();
2169
2170 assert_eq!(batch.len(), 5);
2172 }
2173}