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_fuchsia_input_report as fidl_input_report;
17use fidl_fuchsia_ui_input as fidl_ui_input;
18use fidl_next_fuchsia_ui_pointerinjector as pointerinjector;
19
20use metrics_registry::*;
21use sorted_vec_map::{SortedVecMap, SortedVecSet};
22
23#[derive(Debug, PartialEq)]
39pub struct TouchScreenEvent {
40 pub contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
46
47 pub injector_contacts: SortedVecMap<pointerinjector::EventPhase, Vec<TouchContact>>,
52
53 pub pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
55
56 pub wake_lease: Option<zx::EventPair>,
58}
59
60impl Clone for TouchScreenEvent {
61 fn clone(&self) -> Self {
62 log::debug!("TouchScreenEvent cloned without wake lease.");
63 Self {
64 contacts: self.contacts.clone(),
65 injector_contacts: self.injector_contacts.clone(),
66 pressed_buttons: self.pressed_buttons.clone(),
67 wake_lease: None,
68 }
69 }
70}
71
72impl Drop for TouchScreenEvent {
73 fn drop(&mut self) {
74 log::debug!("TouchScreenEvent dropped, had_wake_lease: {:?}", self.wake_lease);
75 }
76}
77
78impl TouchScreenEvent {
79 pub fn clone_with_wake_lease(&self) -> Self {
80 log::debug!("TouchScreenEvent cloned with wake lease: {:?}", self.wake_lease);
81 Self {
82 contacts: self.contacts.clone(),
83 injector_contacts: self.injector_contacts.clone(),
84 pressed_buttons: self.pressed_buttons.clone(),
85 wake_lease: self.wake_lease.as_ref().map(|lease| {
86 lease
87 .duplicate_handle(zx::Rights::SAME_RIGHTS)
88 .expect("failed to duplicate event pair")
89 }),
90 }
91 }
92
93 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
94 let contacts_clone = self.injector_contacts.clone();
95 node.record_child("injector_contacts", move |contacts_node| {
96 for (phase, contacts) in contacts_clone.iter() {
97 let phase_str = match pointerinjector::EventPhase::try_from(*phase) {
98 Ok(pointerinjector::EventPhase::Add) => "add",
99 Ok(pointerinjector::EventPhase::Change) => "change",
100 Ok(pointerinjector::EventPhase::Remove) => "remove",
101 Ok(pointerinjector::EventPhase::Cancel) => "cancel",
102 Err(_) => unreachable!("invalid phase"),
103 };
104 contacts_node.record_child(phase_str, move |phase_node| {
105 for contact in contacts.iter() {
106 phase_node.record_child(contact.id.to_string(), move |contact_node| {
107 contact_node
108 .record_double("position_x_mm", f64::from(contact.position.x));
109 contact_node
110 .record_double("position_y_mm", f64::from(contact.position.y));
111 if let Some(pressure) = contact.pressure {
112 contact_node.record_int("pressure", pressure);
113 }
114 if let Some(contact_size) = contact.contact_size {
115 contact_node.record_double(
116 "contact_width_mm",
117 f64::from(contact_size.width),
118 );
119 contact_node.record_double(
120 "contact_height_mm",
121 f64::from(contact_size.height),
122 );
123 }
124 });
125 }
126 });
127 }
128 });
129
130 let pressed_buttons_node =
131 node.create_string_array("pressed_buttons", self.pressed_buttons.len());
132 self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
133 let button_name: String = match button {
134 fidl_next_fuchsia_input_report::TouchButton::Palm => "palm".into(),
135 unknown_value => {
136 format!("unknown({:?})", unknown_value)
137 }
138 };
139 pressed_buttons_node.set(i, &button_name);
140 });
141 node.record(pressed_buttons_node);
142 }
143}
144
145#[derive(Clone, Debug, PartialEq)]
150pub struct TouchpadEvent {
151 pub injector_contacts: Vec<TouchContact>,
154
155 pub pressed_buttons: SortedVecSet<mouse_binding::MouseButton>,
157}
158
159impl TouchpadEvent {
160 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
161 let pressed_buttons_node =
162 node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
163 self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
164 pressed_buttons_node.set(i, *button);
165 });
166 node.record(pressed_buttons_node);
167
168 let contacts_clone = self.injector_contacts.clone();
170 node.record_child("injector_contacts", move |contacts_node| {
171 for contact in contacts_clone.iter() {
172 contacts_node.record_child(contact.id.to_string(), move |contact_node| {
173 contact_node.record_double("position_x_mm", f64::from(contact.position.x));
174 contact_node.record_double("position_y_mm", f64::from(contact.position.y));
175 if let Some(pressure) = contact.pressure {
176 contact_node.record_int("pressure", pressure);
177 }
178 if let Some(contact_size) = contact.contact_size {
179 contact_node
180 .record_double("contact_width_mm", f64::from(contact_size.width));
181 contact_node
182 .record_double("contact_height_mm", f64::from(contact_size.height));
183 }
184 })
185 }
186 });
187 }
188}
189
190#[derive(Clone, Copy, Debug, Eq, PartialEq)]
193pub enum TouchDeviceType {
194 TouchScreen,
195 WindowsPrecisionTouchpad,
196}
197
198#[derive(Clone, Copy, Debug, PartialEq)]
201pub struct TouchContact {
202 pub id: u32,
204
205 pub position: Position,
208
209 pub pressure: Option<i64>,
212
213 pub contact_size: Option<Size>,
216}
217
218impl Eq for TouchContact {}
219
220impl TryFrom<&fidl_next_fuchsia_input_report::ContactInputReport> for TouchContact {
221 type Error = anyhow::Error;
222
223 fn try_from(
224 fidl_contact: &fidl_next_fuchsia_input_report::ContactInputReport,
225 ) -> anyhow::Result<TouchContact> {
226 let contact_size =
227 if fidl_contact.contact_width.is_some() && fidl_contact.contact_height.is_some() {
228 Some(Size {
229 width: fidl_contact.contact_width.unwrap() as f32,
230 height: fidl_contact.contact_height.unwrap() as f32,
231 })
232 } else {
233 None
234 };
235
236 let id = fidl_contact.contact_id.context("contact_id is required")?;
237 let position_x = fidl_contact.position_x.context("position_x is required")?;
238 let position_y = fidl_contact.position_y.context("position_y is required")?;
239
240 Ok(TouchContact {
241 id,
242 position: Position { x: position_x as f32, y: position_y as f32 },
243 pressure: fidl_contact.pressure,
244 contact_size,
245 })
246 }
247}
248
249impl TryFrom<&fidl_next_fuchsia_input_report::wire::ContactInputReport<'_>> for TouchContact {
250 type Error = anyhow::Error;
251
252 fn try_from(
253 fidl_contact: &fidl_next_fuchsia_input_report::wire::ContactInputReport<'_>,
254 ) -> Result<Self, Self::Error> {
255 let contact_size =
256 if fidl_contact.contact_width().is_some() && fidl_contact.contact_height().is_some() {
257 Some(Size {
258 width: fidl_contact.contact_width().map(|w| w.0).unwrap() as f32,
259 height: fidl_contact.contact_height().map(|h| h.0).unwrap() as f32,
260 })
261 } else {
262 None
263 };
264
265 let id = fidl_contact.contact_id().map(|id| id.0).context("contact_id is required")?;
266 let position_x =
267 fidl_contact.position_x().map(|x| x.0).context("position_x is required")?;
268 let position_y =
269 fidl_contact.position_y().map(|y| y.0).context("position_y is required")?;
270
271 Ok(TouchContact {
272 id,
273 position: Position { x: position_x as f32, y: position_y as f32 },
274 pressure: fidl_contact.pressure().map(|p| p.0),
275 contact_size,
276 })
277 }
278}
279
280#[derive(Clone, Debug, Eq, PartialEq)]
281pub struct TouchScreenDeviceDescriptor {
282 pub device_id: u32,
284
285 pub contacts: Vec<ContactDeviceDescriptor>,
287}
288
289#[derive(Clone, Debug, Eq, PartialEq)]
290pub struct TouchpadDeviceDescriptor {
291 pub device_id: u32,
293
294 pub contacts: Vec<ContactDeviceDescriptor>,
296}
297
298#[derive(Clone, Debug, Eq, PartialEq)]
299enum TouchDeviceDescriptor {
300 TouchScreen(TouchScreenDeviceDescriptor),
301 Touchpad(TouchpadDeviceDescriptor),
302}
303
304#[derive(Clone, Debug, Eq, PartialEq)]
318pub struct ContactDeviceDescriptor {
319 pub x_range: fidl_input_report::Range,
321
322 pub y_range: fidl_input_report::Range,
324
325 pub x_unit: fidl_input_report::Unit,
327
328 pub y_unit: fidl_input_report::Unit,
330
331 pub pressure_range: Option<fidl_input_report::Range>,
333
334 pub width_range: Option<fidl_input_report::Range>,
336
337 pub height_range: Option<fidl_input_report::Range>,
339}
340
341pub struct TouchBinding {
348 event_sender: UnboundedSender<Vec<InputEvent>>,
350
351 device_descriptor: TouchDeviceDescriptor,
353
354 touch_device_type: TouchDeviceType,
356
357 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
359}
360
361#[async_trait]
362impl input_device::InputDeviceBinding for TouchBinding {
363 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
364 self.event_sender.clone()
365 }
366
367 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
368 match self.device_descriptor.clone() {
369 TouchDeviceDescriptor::TouchScreen(desc) => {
370 input_device::InputDeviceDescriptor::TouchScreen(desc)
371 }
372 TouchDeviceDescriptor::Touchpad(desc) => {
373 input_device::InputDeviceDescriptor::Touchpad(desc)
374 }
375 }
376 }
377}
378
379impl TouchBinding {
380 pub async fn new(
395 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
396 device_id: u32,
397 input_event_sender: UnboundedSender<Vec<InputEvent>>,
398 device_node: fuchsia_inspect::Node,
399 feature_flags: input_device::InputPipelineFeatureFlags,
400 metrics_logger: metrics::MetricsLogger,
401 ) -> Result<Self, Error> {
402 let (device_binding, mut inspect_status) =
403 Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
404 .await?;
405 device_binding
406 .set_touchpad_mode(true)
407 .await
408 .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
409 inspect_status.health_node.set_ok();
410 input_device::initialize_report_stream(
411 device_proxy,
412 device_binding.get_device_descriptor(),
413 device_binding.input_event_sender(),
414 inspect_status,
415 metrics_logger,
416 feature_flags,
417 Self::process_reports,
418 );
419
420 Ok(device_binding)
421 }
422
423 async fn bind_device(
435 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
436 device_id: u32,
437 input_event_sender: UnboundedSender<Vec<InputEvent>>,
438 device_node: fuchsia_inspect::Node,
439 ) -> Result<(Self, InputDeviceStatus), Error> {
440 let mut input_device_status = InputDeviceStatus::new(device_node);
441 let device_descriptor: fidl_next_fuchsia_input_report::DeviceDescriptor = match device_proxy
442 .get_descriptor()
443 .await
444 {
445 Ok(res) => res.descriptor,
446 Err(_) => {
447 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
448 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
449 }
450 };
451
452 let touch_device_type = get_device_type(&device_proxy).await;
453
454 match device_descriptor.touch {
455 Some(fidl_next_fuchsia_input_report::TouchDescriptor {
456 input:
457 Some(fidl_next_fuchsia_input_report::TouchInputDescriptor {
458 contacts: Some(contact_descriptors),
459 max_contacts: _,
460 touch_type: _,
461 buttons: _,
462 ..
463 }),
464 ..
465 }) => Ok((
466 TouchBinding {
467 event_sender: input_event_sender,
468 device_descriptor: match touch_device_type {
469 TouchDeviceType::TouchScreen => {
470 TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
471 device_id,
472 contacts: contact_descriptors
473 .iter()
474 .map(TouchBinding::parse_contact_descriptor)
475 .filter_map(Result::ok)
476 .collect(),
477 })
478 }
479 TouchDeviceType::WindowsPrecisionTouchpad => {
480 TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
481 device_id,
482 contacts: contact_descriptors
483 .iter()
484 .map(TouchBinding::parse_contact_descriptor)
485 .filter_map(Result::ok)
486 .collect(),
487 })
488 }
489 },
490 touch_device_type,
491 device_proxy,
492 },
493 input_device_status,
494 )),
495 descriptor => {
496 input_device_status
497 .health_node
498 .set_unhealthy("Touch Device Descriptor failed to parse.");
499 Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
500 }
501 }
502 }
503
504 async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
505 match self.touch_device_type {
506 TouchDeviceType::TouchScreen => Ok(()),
507 TouchDeviceType::WindowsPrecisionTouchpad => {
508 let mut report = match self.device_proxy.get_feature_report().await? {
511 Ok(res) => res.report,
512 Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
513 };
514 let mut touch = report
515 .touch
516 .unwrap_or_else(fidl_next_fuchsia_input_report::TouchFeatureReport::default);
517 touch.input_mode = match enable {
518 true => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
519 false => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection),
520 };
521 report.touch = Some(touch);
522 match self.device_proxy.set_feature_report(&report).await? {
523 Ok(_) => {
524 log::info!("touchpad: set touchpad_enabled to {}", enable);
526 Ok(())
527 }
528 Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
529 }
530 }
531 }
532 }
533
534 fn process_reports(
556 reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
557 previous_report: Option<InputReport>,
558 device_descriptor: &input_device::InputDeviceDescriptor,
559 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
560 inspect_status: &InputDeviceStatus,
561 metrics_logger: &metrics::MetricsLogger,
562 feature_flags: &input_device::InputPipelineFeatureFlags,
563 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
564 fuchsia_trace::duration!(
565 "input",
566 "touch-binding-process-report",
567 "num_reports" => reports.len(),
568 );
569 match device_descriptor {
570 input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
571 reports,
572 previous_report,
573 device_descriptor,
574 input_event_sender,
575 inspect_status,
576 metrics_logger,
577 feature_flags.enable_merge_touch_events,
578 ),
579 input_device::InputDeviceDescriptor::Touchpad(_) => {
580 (previous_report, None)
582 }
583 _ => (previous_report, None),
584 }
585 }
586
587 fn parse_contact_descriptor(
595 contact_device_descriptor: &fidl_next_fuchsia_input_report::ContactInputDescriptor,
596 ) -> Result<ContactDeviceDescriptor, Error> {
597 match contact_device_descriptor {
598 fidl_next_fuchsia_input_report::ContactInputDescriptor {
599 position_x: Some(x_axis),
600 position_y: Some(y_axis),
601 pressure: pressure_axis,
602 contact_width: width_axis,
603 contact_height: height_axis,
604 ..
605 } => Ok(ContactDeviceDescriptor {
606 x_range: utils::range_to_old(&x_axis.range),
607 y_range: utils::range_to_old(&y_axis.range),
608 x_unit: utils::unit_to_old(&x_axis.unit),
609 y_unit: utils::unit_to_old(&y_axis.unit),
610 pressure_range: pressure_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
611 width_range: width_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
612 height_range: height_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
613 }),
614 descriptor => {
615 Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
616 }
617 }
618 }
619}
620
621fn is_move_only(event: &InputEvent) -> bool {
622 matches!(
623 &event.device_event,
624 input_device::InputDeviceEvent::TouchScreen(event)
625 if event
626 .injector_contacts
627 .get(&pointerinjector::EventPhase::Add)
628 .map_or(true, |c| c.is_empty())
629 && event
630 .injector_contacts
631 .get(&pointerinjector::EventPhase::Remove)
632 .map_or(true, |c| c.is_empty())
633 && event
634 .injector_contacts
635 .get(&pointerinjector::EventPhase::Cancel)
636 .map_or(true, |c| c.is_empty())
637 )
638}
639
640fn has_pressed_buttons(event: &InputEvent) -> bool {
641 match &event.device_event {
642 input_device::InputDeviceEvent::TouchScreen(event) => !event.pressed_buttons.is_empty(),
643 _ => false,
644 }
645}
646
647fn process_touch_screen_reports(
648 reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
649 mut previous_report: Option<InputReport>,
650 device_descriptor: &input_device::InputDeviceDescriptor,
651 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
652 inspect_status: &InputDeviceStatus,
653 metrics_logger: &metrics::MetricsLogger,
654 enable_merge_touch_events: bool,
655) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
656 let num_reports = reports.len();
657 let mut batch: Vec<InputEvent> = Vec::with_capacity(num_reports);
658 for report in reports {
659 inspect_status.count_received_report_wire(report);
660 let (prev_report, event) = process_single_touch_screen_report(
661 report,
662 previous_report,
663 device_descriptor,
664 inspect_status,
665 metrics_logger,
666 );
667 previous_report = prev_report;
668 if let Some(event) = event {
669 batch.push(event);
670 }
671 }
672
673 if !batch.is_empty() {
674 if enable_merge_touch_events {
675 let mut is_event_move_only: Vec<bool> = Vec::with_capacity(batch.len());
677 let mut pressed_buttons: Vec<bool> = Vec::with_capacity(batch.len());
678 for event in &batch {
679 is_event_move_only.push(is_move_only(event));
680 pressed_buttons.push(has_pressed_buttons(event));
681 }
682 let size_of_batch = batch.len();
683
684 let mut merged_batch = Vec::with_capacity(size_of_batch);
686
687 for (i, current_event) in batch.into_iter().enumerate() {
689 let current_is_move = is_event_move_only[i];
690 let current_pressed_buttons = pressed_buttons[i];
691 let is_last_event = i == size_of_batch - 1;
692
693 let next_is_move =
695 if i + 1 < size_of_batch { is_event_move_only[i + 1] } else { false };
696
697 let next_pressed_buttons = if i + 1 < size_of_batch {
698 pressed_buttons[i + 1]
699 } else {
700 current_pressed_buttons
701 };
702
703 if !is_last_event
706 && (current_is_move && next_is_move)
708 && (current_pressed_buttons == next_pressed_buttons)
710 {
711 continue;
712 }
713
714 merged_batch.push(current_event);
715 }
716
717 batch = merged_batch;
718 }
719
720 let events_to_send: Vec<InputEvent> = {
721 fuchsia_trace::duration!("input", "clone_with_wake_lease_batch");
722 batch
723 .into_iter()
724 .map(|event| {
725 let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
729 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
730 event
731 })
732 .collect()
733 };
734 fuchsia_trace::instant!(
735 "input",
736 "events_to_input_handlers",
737 fuchsia_trace::Scope::Thread,
738 "num_reports" => num_reports,
739 "num_events_generated" => events_to_send.len()
740 );
741
742 inspect_status.count_generated_events(&events_to_send);
744
745 if let Err(e) = input_event_sender.unbounded_send(events_to_send) {
746 metrics_logger.log_error(
747 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
748 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
749 );
750 }
751 }
752 (previous_report, None)
753}
754
755fn process_single_touch_screen_report(
756 report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
757 previous_report: Option<InputReport>,
758 device_descriptor: &input_device::InputDeviceDescriptor,
759 inspect_status: &InputDeviceStatus,
760 metrics_logger: &metrics::MetricsLogger,
761) -> (Option<InputReport>, Option<InputEvent>) {
762 fuchsia_trace::flow_end!(
763 "input",
764 "input_report",
765 report.trace_id().map(|x| x.0).unwrap_or(0).into()
766 );
767
768 let wake_lease = utils::duplicate_wake_lease(report.wake_lease());
772
773 let touch_report = match report.touch() {
775 Some(touch) => touch,
776 None => {
777 inspect_status.count_filtered_report();
778 return (previous_report, None);
779 }
780 };
781
782 let (previous_contacts, previous_buttons): (
783 SortedVecMap<u32, TouchContact>,
784 Vec<fidl_next_fuchsia_input_report::TouchButton>,
785 ) = previous_report
786 .as_ref()
787 .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
788 .map(|touch_report| {
789 touch_contacts_and_buttons_from_touch_report(touch_report, metrics_logger)
790 })
791 .unwrap_or_default();
792 let (current_contacts, current_buttons): (
793 SortedVecMap<u32, TouchContact>,
794 Vec<fidl_next_fuchsia_input_report::TouchButton>,
795 ) = touch_contacts_and_buttons_from_touch_report_wire(touch_report, metrics_logger);
796
797 if previous_contacts.is_empty()
799 && current_contacts.is_empty()
800 && previous_buttons.is_empty()
801 && current_buttons.is_empty()
802 {
803 inspect_status.count_filtered_report();
804 let natural_report = utils::input_report_to_natural(report);
806 return (Some(natural_report), None);
807 }
808
809 let mut natural_report = utils::input_report_to_natural(report);
810
811 if current_contacts.is_empty()
814 && !previous_contacts.is_empty()
815 && (!current_buttons.is_empty() || !previous_buttons.is_empty())
816 {
817 if let Some(touch_report) = natural_report.touch.as_mut() {
818 touch_report.contacts =
819 previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
820 }
821 }
822
823 let added_contacts: Vec<TouchContact> = Vec::from_iter(
825 current_contacts
826 .iter()
827 .map(|(_, v)| v.clone())
828 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
829 );
830 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
832 current_contacts
833 .iter()
834 .map(|(_, v)| v.clone())
835 .filter(|contact| previous_contacts.contains_key(&contact.id)),
836 );
837 let removed_contacts: Vec<TouchContact> =
839 Vec::from_iter(previous_contacts.iter().map(|(_, v)| v.clone()).filter(|contact| {
840 current_buttons.is_empty()
841 && previous_buttons.is_empty()
842 && !current_contacts.contains_key(&contact.id)
843 }));
844
845 let trace_id = fuchsia_trace::Id::new();
846 let event = create_touch_screen_event(
847 SortedVecMap::from_iter(vec![
848 (fidl_ui_input::PointerEventPhase::Add, added_contacts.clone()),
849 (fidl_ui_input::PointerEventPhase::Down, added_contacts.clone()),
850 (fidl_ui_input::PointerEventPhase::Move, moved_contacts.clone()),
851 (fidl_ui_input::PointerEventPhase::Up, removed_contacts.clone()),
852 (fidl_ui_input::PointerEventPhase::Remove, removed_contacts.clone()),
853 ]),
854 SortedVecMap::from_iter(vec![
855 (pointerinjector::EventPhase::Add, added_contacts),
856 (pointerinjector::EventPhase::Change, moved_contacts),
857 (pointerinjector::EventPhase::Remove, removed_contacts),
858 ]),
859 current_buttons,
860 device_descriptor,
861 trace_id,
862 wake_lease,
863 );
864
865 (Some(natural_report), Some(event))
866}
867
868fn touch_contacts_and_buttons_from_touch_report(
869 touch_report: &fidl_next_fuchsia_input_report::TouchInputReport,
870 metrics_logger: &metrics::MetricsLogger,
871) -> (SortedVecMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
872 let mut contacts = Vec::with_capacity(touch_report.contacts.as_ref().map_or(0, |v| v.len()));
873 if let Some(unwrapped_contacts) = &touch_report.contacts {
874 for contact in unwrapped_contacts {
875 match TouchContact::try_from(contact) {
876 Ok(c) => contacts.push(c),
877 Err(e) => {
878 metrics_logger.log_warn(
879 InputPipelineErrorMetricDimensionEvent::TouchReportContactMissingField,
880 std::format!("failed to convert touch contact: {:?}", e),
881 );
882 }
883 }
884 }
885 } else {
886 metrics_logger.log_warn(
887 InputPipelineErrorMetricDimensionEvent::TouchReportMissingContact,
888 "contacts missing in touch input report",
889 );
890 }
891
892 (
893 SortedVecMap::from_iter(contacts.into_iter().map(|contact| (contact.id, contact))),
894 touch_report.pressed_buttons.clone().unwrap_or_default(),
895 )
896}
897
898fn touch_contacts_and_buttons_from_touch_report_wire(
899 touch_report: &fidl_next_fuchsia_input_report::wire::TouchInputReport<'_>,
900 metrics_logger: &metrics::MetricsLogger,
901) -> (SortedVecMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
902 let mut contacts = Vec::new();
903 if let Some(unwrapped_contacts) = touch_report.contacts() {
904 for contact in unwrapped_contacts.iter() {
905 match TouchContact::try_from(contact) {
906 Ok(c) => contacts.push(c),
907 Err(e) => {
908 metrics_logger.log_warn(
909 InputPipelineErrorMetricDimensionEvent::TouchReportContactMissingField,
910 std::format!("failed to convert touch contact: {:?}", e),
911 );
912 }
913 }
914 }
915 } else {
916 metrics_logger.log_warn(
917 InputPipelineErrorMetricDimensionEvent::TouchReportMissingContact,
918 "contacts missing in touch input report",
919 );
920 }
921
922 let pressed_buttons = touch_report
923 .pressed_buttons()
924 .map(|buttons| buttons.iter().map(|&b| fidl_next::FromWire::from_wire(b)).collect())
925 .unwrap_or_default();
926
927 (
928 SortedVecMap::from_iter(contacts.into_iter().map(|contact| (contact.id, contact))),
929 pressed_buttons,
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
964async fn get_device_type(
970 input_device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
971) -> TouchDeviceType {
972 match input_device.get_feature_report().await {
973 Ok(Ok(fidl_next_fuchsia_input_report::InputDeviceGetFeatureReportResponse {
974 report: fidl_next_fuchsia_input_report::FeatureReport {
975 touch:
976 Some(fidl_next_fuchsia_input_report::TouchFeatureReport {
977 input_mode:
978 Some(
979 fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection
980 | fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
981 ),
982 ..
983 }),
984 ..
985 }
986 })) => TouchDeviceType::WindowsPrecisionTouchpad,
987 _ => TouchDeviceType::TouchScreen,
988 }
989}
990
991#[cfg(test)]
992mod tests {
993 use super::*;
994 use crate::testing_utilities::{
995 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
996 create_touch_screen_event_with_buttons, spawn_input_stream_handler,
997 };
998 use crate::utils::Position;
999 use assert_matches::assert_matches;
1000 use diagnostics_assertions::AnyProperty;
1001 use fuchsia_async as fasync;
1002 use futures::StreamExt;
1003 use pretty_assertions::assert_eq;
1004 use test_case::test_case;
1005
1006 #[fasync::run_singlethreaded(test)]
1007 async fn process_empty_reports() {
1008 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1009 let previous_report = create_touch_input_report(
1010 vec![],
1011 None,
1012 previous_report_time,
1013 );
1014 let report_time = zx::MonotonicInstant::get().into_nanos();
1015 let report =
1016 create_touch_input_report(vec![], None, report_time);
1017
1018 let descriptor =
1019 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1020 device_id: 1,
1021 contacts: vec![],
1022 });
1023 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1024
1025 let inspector = fuchsia_inspect::Inspector::default();
1026 let test_node = inspector.root().create_child("TestDevice_Touch");
1027 let mut inspect_status = InputDeviceStatus::new(test_node);
1028 inspect_status.health_node.set_ok();
1029
1030 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
1031 let (returned_report, _) = TouchBinding::process_reports(
1032 &reports_wire,
1033 Some(previous_report),
1034 &descriptor,
1035 &mut event_sender,
1036 &inspect_status,
1037 &metrics::MetricsLogger::default(),
1038 &input_device::InputPipelineFeatureFlags::default(),
1039 );
1040 assert!(returned_report.is_some());
1041 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1042
1043 let event = event_receiver.try_next();
1045 assert!(event.is_err());
1046
1047 diagnostics_assertions::assert_data_tree!(inspector, root: {
1048 "TestDevice_Touch": contains {
1049 reports_received_count: 1u64,
1050 reports_filtered_count: 1u64,
1051 events_generated: 0u64,
1052 last_received_timestamp_ns: report_time as u64,
1053 last_generated_timestamp_ns: 0u64,
1054 "fuchsia.inspect.Health": {
1055 status: "OK",
1056 start_timestamp_nanos: AnyProperty
1059 },
1060 }
1061 });
1062 }
1063
1064 #[fasync::run_singlethreaded(test)]
1066 async fn add_and_down() {
1067 const TOUCH_ID: u32 = 2;
1068
1069 let descriptor =
1070 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1071 device_id: 1,
1072 contacts: vec![],
1073 });
1074 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1075
1076 let contact = fidl_fuchsia_input_report::ContactInputReport {
1077 contact_id: Some(TOUCH_ID),
1078 position_x: Some(0),
1079 position_y: Some(0),
1080 pressure: None,
1081 contact_width: None,
1082 contact_height: None,
1083 ..Default::default()
1084 };
1085 let reports = vec![create_touch_input_report(
1086 vec![contact],
1087 None,
1088 event_time_i64,
1089 )];
1090
1091 let expected_events = vec![create_touch_screen_event(
1092 SortedVecMap::from_iter(vec![
1093 (
1094 fidl_ui_input::PointerEventPhase::Add,
1095 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1096 ),
1097 (
1098 fidl_ui_input::PointerEventPhase::Down,
1099 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1100 ),
1101 ]),
1102 event_time_u64,
1103 &descriptor,
1104 )];
1105
1106 assert_input_report_sequence_generates_events!(
1107 input_reports: reports,
1108 expected_events: expected_events,
1109 device_descriptor: descriptor,
1110 device_type: TouchBinding,
1111 );
1112 }
1113
1114 #[fasync::run_singlethreaded(test)]
1116 async fn up_and_remove() {
1117 const TOUCH_ID: u32 = 2;
1118
1119 let descriptor =
1120 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1121 device_id: 1,
1122 contacts: vec![],
1123 });
1124 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1125
1126 let contact = fidl_fuchsia_input_report::ContactInputReport {
1127 contact_id: Some(TOUCH_ID),
1128 position_x: Some(0),
1129 position_y: Some(0),
1130 pressure: None,
1131 contact_width: None,
1132 contact_height: None,
1133 ..Default::default()
1134 };
1135 let reports = vec![
1136 create_touch_input_report(
1137 vec![contact],
1138 None,
1139 event_time_i64,
1140 ),
1141 create_touch_input_report(vec![], None, event_time_i64),
1142 ];
1143
1144 let expected_events = vec![
1145 create_touch_screen_event(
1146 SortedVecMap::from_iter(vec![
1147 (
1148 fidl_ui_input::PointerEventPhase::Add,
1149 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1150 ),
1151 (
1152 fidl_ui_input::PointerEventPhase::Down,
1153 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1154 ),
1155 ]),
1156 event_time_u64,
1157 &descriptor,
1158 ),
1159 create_touch_screen_event(
1160 SortedVecMap::from_iter(vec![
1161 (
1162 fidl_ui_input::PointerEventPhase::Up,
1163 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1164 ),
1165 (
1166 fidl_ui_input::PointerEventPhase::Remove,
1167 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1168 ),
1169 ]),
1170 event_time_u64,
1171 &descriptor,
1172 ),
1173 ];
1174
1175 assert_input_report_sequence_generates_events!(
1176 input_reports: reports,
1177 expected_events: expected_events,
1178 device_descriptor: descriptor,
1179 device_type: TouchBinding,
1180 );
1181 }
1182
1183 #[fasync::run_singlethreaded(test)]
1185 async fn add_down_move() {
1186 const TOUCH_ID: u32 = 2;
1187 let first = Position { x: 10.0, y: 30.0 };
1188 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1189
1190 let descriptor =
1191 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1192 device_id: 1,
1193 contacts: vec![],
1194 });
1195 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1196
1197 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1198 contact_id: Some(TOUCH_ID),
1199 position_x: Some(first.x as i64),
1200 position_y: Some(first.y as i64),
1201 pressure: None,
1202 contact_width: None,
1203 contact_height: None,
1204 ..Default::default()
1205 };
1206 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1207 contact_id: Some(TOUCH_ID),
1208 position_x: Some(first.x as i64 * 2),
1209 position_y: Some(first.y as i64 * 2),
1210 pressure: None,
1211 contact_width: None,
1212 contact_height: None,
1213 ..Default::default()
1214 };
1215
1216 let reports = vec![
1217 create_touch_input_report(
1218 vec![first_contact],
1219 None,
1220 event_time_i64,
1221 ),
1222 create_touch_input_report(
1223 vec![second_contact],
1224 None,
1225 event_time_i64,
1226 ),
1227 ];
1228
1229 let expected_events = vec![
1230 create_touch_screen_event(
1231 SortedVecMap::from_iter(vec![
1232 (
1233 fidl_ui_input::PointerEventPhase::Add,
1234 vec![create_touch_contact(TOUCH_ID, first)],
1235 ),
1236 (
1237 fidl_ui_input::PointerEventPhase::Down,
1238 vec![create_touch_contact(TOUCH_ID, first)],
1239 ),
1240 ]),
1241 event_time_u64,
1242 &descriptor,
1243 ),
1244 create_touch_screen_event(
1245 SortedVecMap::from_iter(vec![(
1246 fidl_ui_input::PointerEventPhase::Move,
1247 vec![create_touch_contact(TOUCH_ID, second)],
1248 )]),
1249 event_time_u64,
1250 &descriptor,
1251 ),
1252 ];
1253
1254 assert_input_report_sequence_generates_events!(
1255 input_reports: reports,
1256 expected_events: expected_events,
1257 device_descriptor: descriptor,
1258 device_type: TouchBinding,
1259 );
1260 }
1261
1262 #[fasync::run_singlethreaded(test)]
1263 async fn sent_event_has_trace_id() {
1264 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1265 let previous_report = create_touch_input_report(
1266 vec![],
1267 None,
1268 previous_report_time,
1269 );
1270
1271 let report_time = zx::MonotonicInstant::get().into_nanos();
1272 let contact = fidl_fuchsia_input_report::ContactInputReport {
1273 contact_id: Some(222),
1274 position_x: Some(333),
1275 position_y: Some(444),
1276 ..Default::default()
1277 };
1278 let report =
1279 create_touch_input_report(vec![contact], None, report_time);
1280
1281 let descriptor =
1282 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1283 device_id: 1,
1284 contacts: vec![],
1285 });
1286 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1287
1288 let inspector = fuchsia_inspect::Inspector::default();
1289 let test_node = inspector.root().create_child("TestDevice_Touch");
1290 let mut inspect_status = InputDeviceStatus::new(test_node);
1291 inspect_status.health_node.set_ok();
1292
1293 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
1294 let _ = TouchBinding::process_reports(
1295 &reports_wire,
1296 Some(previous_report),
1297 &descriptor,
1298 &mut event_sender,
1299 &inspect_status,
1300 &metrics::MetricsLogger::default(),
1301 &input_device::InputPipelineFeatureFlags::default(),
1302 );
1303 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1304 }
1305
1306 #[fuchsia::test(allow_stalls = false)]
1307 async fn enables_touchpad_mode_automatically() {
1308 let (set_feature_report_sender, set_feature_report_receiver) =
1309 futures::channel::mpsc::unbounded();
1310 let input_device_proxy = spawn_input_stream_handler(move |input_device_request| {
1311 let set_feature_report_sender = set_feature_report_sender.clone();
1312 async move {
1313 match input_device_request {
1314 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1315 let _ = responder.send(&get_touchpad_device_descriptor(
1316 true, ));
1318 }
1319 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1320 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1321 touch: Some(fidl_input_report::TouchFeatureReport {
1322 input_mode: Some(
1323 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1324 ),
1325 ..Default::default()
1326 }),
1327 ..Default::default()
1328 }));
1329 }
1330 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1331 responder,
1332 report,
1333 } => {
1334 match set_feature_report_sender.unbounded_send(report) {
1335 Ok(_) => {
1336 let _ = responder.send(Ok(()));
1337 }
1338 Err(e) => {
1339 panic!("try_send set_feature_report_request failed: {}", e);
1340 }
1341 };
1342 }
1343 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1344 }
1346 r => panic!("unsupported request {:?}", r),
1347 }
1348 }
1349 });
1350
1351 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1352
1353 let inspector = fuchsia_inspect::Inspector::default();
1355 let test_node = inspector.root().create_child("test_node");
1356
1357 TouchBinding::new(
1361 input_device_proxy,
1362 0,
1363 device_event_sender,
1364 test_node,
1365 input_device::InputPipelineFeatureFlags::default(),
1366 metrics::MetricsLogger::default(),
1367 )
1368 .await
1369 .unwrap();
1370 assert_matches!(
1371 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1372 [fidl_input_report::FeatureReport {
1373 touch: Some(fidl_input_report::TouchFeatureReport {
1374 input_mode: Some(
1375 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1376 ),
1377 ..
1378 }),
1379 ..
1380 }]
1381 );
1382 }
1383
1384 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1385 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1386 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1387 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1388 #[fuchsia::test(allow_stalls = false)]
1389 async fn identifies_correct_touch_device_type(
1390 has_mouse_descriptor: bool,
1391 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1392 expect_touch_device_type: TouchDeviceType,
1393 ) {
1394 let input_device_proxy =
1395 spawn_input_stream_handler(move |input_device_request| async move {
1396 match input_device_request {
1397 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1398 let _ =
1399 responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1400 }
1401 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1402 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1403 touch: Some(fidl_input_report::TouchFeatureReport {
1404 input_mode: touch_input_mode,
1405 ..Default::default()
1406 }),
1407 ..Default::default()
1408 }));
1409 }
1410 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1411 responder, ..
1412 } => {
1413 let _ = responder.send(Ok(()));
1414 }
1415 r => panic!("unsupported request {:?}", r),
1416 }
1417 });
1418
1419 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1420
1421 let inspector = fuchsia_inspect::Inspector::default();
1423 let test_node = inspector.root().create_child("test_node");
1424
1425 let binding = TouchBinding::new(
1426 input_device_proxy,
1427 0,
1428 device_event_sender,
1429 test_node,
1430 input_device::InputPipelineFeatureFlags::default(),
1431 metrics::MetricsLogger::default(),
1432 )
1433 .await
1434 .unwrap();
1435 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1436 }
1437
1438 fn get_touchpad_device_descriptor(
1441 has_mouse_descriptor: bool,
1442 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1443 fidl_input_report::DeviceDescriptor {
1444 mouse: match has_mouse_descriptor {
1445 true => Some(fidl_input_report::MouseDescriptor::default()),
1446 false => None,
1447 },
1448 touch: Some(fidl_input_report::TouchDescriptor {
1449 input: Some(fidl_input_report::TouchInputDescriptor {
1450 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1451 position_x: Some(fidl_input_report::Axis {
1452 range: fidl_input_report::Range { min: 1, max: 2 },
1453 unit: fidl_input_report::Unit {
1454 type_: fidl_input_report::UnitType::None,
1455 exponent: 0,
1456 },
1457 }),
1458 position_y: Some(fidl_input_report::Axis {
1459 range: fidl_input_report::Range { min: 2, max: 3 },
1460 unit: fidl_input_report::Unit {
1461 type_: fidl_input_report::UnitType::Other,
1462 exponent: 100000,
1463 },
1464 }),
1465 pressure: Some(fidl_input_report::Axis {
1466 range: fidl_input_report::Range { min: 3, max: 4 },
1467 unit: fidl_input_report::Unit {
1468 type_: fidl_input_report::UnitType::Grams,
1469 exponent: -991,
1470 },
1471 }),
1472 contact_width: Some(fidl_input_report::Axis {
1473 range: fidl_input_report::Range { min: 5, max: 6 },
1474 unit: fidl_input_report::Unit {
1475 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1476 exponent: 123,
1477 },
1478 }),
1479 contact_height: Some(fidl_input_report::Axis {
1480 range: fidl_input_report::Range { min: 7, max: 8 },
1481 unit: fidl_input_report::Unit {
1482 type_: fidl_input_report::UnitType::Pascals,
1483 exponent: 100,
1484 },
1485 }),
1486 ..Default::default()
1487 }]),
1488 ..Default::default()
1489 }),
1490 ..Default::default()
1491 }),
1492 ..Default::default()
1493 }
1494 }
1495
1496 #[test_case(true; "merge touch events enabled")]
1499 #[test_case(false; "merge touch events disabled")]
1500 #[fasync::run_singlethreaded(test)]
1501 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1502 let descriptor =
1503 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1504 device_id: 1,
1505 contacts: vec![],
1506 });
1507 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1508
1509 let reports = vec![create_touch_input_report(
1510 vec![],
1511 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1512 event_time_i64,
1513 )];
1514
1515 let expected_events = vec![create_touch_screen_event_with_buttons(
1516 SortedVecMap::new(),
1517 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1518 event_time_u64,
1519 &descriptor,
1520 )];
1521
1522 assert_input_report_sequence_generates_events_with_feature_flags!(
1523 input_reports: reports,
1524 expected_events: expected_events,
1525 device_descriptor: descriptor,
1526 device_type: TouchBinding,
1527 feature_flags: input_device::InputPipelineFeatureFlags {
1528 enable_merge_touch_events,
1529 ..Default::default()
1530 },
1531 );
1532 }
1533
1534 #[test_case(true; "merge touch events enabled")]
1537 #[test_case(false; "merge touch events disabled")]
1538 #[fasync::run_singlethreaded(test)]
1539 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1540 const TOUCH_ID: u32 = 2;
1541
1542 let descriptor =
1543 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1544 device_id: 1,
1545 contacts: vec![],
1546 });
1547 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1548
1549 let contact = fidl_fuchsia_input_report::ContactInputReport {
1550 contact_id: Some(TOUCH_ID),
1551 position_x: Some(0),
1552 position_y: Some(0),
1553 pressure: None,
1554 contact_width: None,
1555 contact_height: None,
1556 ..Default::default()
1557 };
1558 let reports = vec![create_touch_input_report(
1559 vec![contact],
1560 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1561 event_time_i64,
1562 )];
1563
1564 let expected_events = vec![create_touch_screen_event_with_buttons(
1565 SortedVecMap::from_iter(vec![
1566 (
1567 fidl_ui_input::PointerEventPhase::Add,
1568 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1569 ),
1570 (
1571 fidl_ui_input::PointerEventPhase::Down,
1572 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1573 ),
1574 ]),
1575 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1576 event_time_u64,
1577 &descriptor,
1578 )];
1579
1580 assert_input_report_sequence_generates_events_with_feature_flags!(
1581 input_reports: reports,
1582 expected_events: expected_events,
1583 device_descriptor: descriptor,
1584 device_type: TouchBinding,
1585 feature_flags: input_device::InputPipelineFeatureFlags {
1586 enable_merge_touch_events,
1587 ..Default::default()
1588 },
1589 );
1590 }
1591
1592 #[test_case(true; "merge touch events enabled")]
1595 #[test_case(false; "merge touch events disabled")]
1596 #[fasync::run_singlethreaded(test)]
1597 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1598 const TOUCH_ID: u32 = 2;
1599
1600 let descriptor =
1601 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1602 device_id: 1,
1603 contacts: vec![],
1604 });
1605 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1606
1607 let contact = fidl_fuchsia_input_report::ContactInputReport {
1608 contact_id: Some(TOUCH_ID),
1609 position_x: Some(0),
1610 position_y: Some(0),
1611 pressure: None,
1612 contact_width: None,
1613 contact_height: None,
1614 ..Default::default()
1615 };
1616 let reports = vec![create_touch_input_report(
1617 vec![contact],
1618 Some(vec![
1619 fidl_fuchsia_input_report::TouchButton::Palm,
1620 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1621 ]),
1622 event_time_i64,
1623 )];
1624
1625 let expected_events = vec![create_touch_screen_event_with_buttons(
1626 SortedVecMap::from_iter(vec![
1627 (
1628 fidl_ui_input::PointerEventPhase::Add,
1629 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1630 ),
1631 (
1632 fidl_ui_input::PointerEventPhase::Down,
1633 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1634 ),
1635 ]),
1636 vec![
1637 fidl_fuchsia_input_report::TouchButton::Palm,
1638 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1639 ],
1640 event_time_u64,
1641 &descriptor,
1642 )];
1643
1644 assert_input_report_sequence_generates_events_with_feature_flags!(
1645 input_reports: reports,
1646 expected_events: expected_events,
1647 device_descriptor: descriptor,
1648 device_type: TouchBinding,
1649 feature_flags: input_device::InputPipelineFeatureFlags {
1650 enable_merge_touch_events,
1651 ..Default::default()
1652 },
1653 );
1654 }
1655
1656 #[test_case(true; "merge touch events enabled")]
1658 #[test_case(false; "merge touch events disabled")]
1659 #[fasync::run_singlethreaded(test)]
1660 async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1661 let descriptor =
1662 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1663 device_id: 1,
1664 contacts: vec![],
1665 });
1666 let (event_time_i64, _) = testing_utilities::event_times();
1667
1668 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1669
1670 let expected_events: Vec<input_device::InputEvent> = vec![];
1671
1672 assert_input_report_sequence_generates_events_with_feature_flags!(
1673 input_reports: reports,
1674 expected_events: expected_events,
1675 device_descriptor: descriptor,
1676 device_type: TouchBinding,
1677 feature_flags: input_device::InputPipelineFeatureFlags {
1678 enable_merge_touch_events,
1679 ..Default::default()
1680 },
1681 );
1682 }
1683
1684 #[test_case(true; "merge touch events enabled")]
1686 #[test_case(false; "merge touch events disabled")]
1687 #[fasync::run_singlethreaded(test)]
1688 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1689 const TOUCH_ID: u32 = 2;
1690
1691 let descriptor =
1692 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1693 device_id: 1,
1694 contacts: vec![],
1695 });
1696 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1697
1698 let contact = fidl_fuchsia_input_report::ContactInputReport {
1699 contact_id: Some(TOUCH_ID),
1700 position_x: Some(0),
1701 position_y: Some(0),
1702 pressure: None,
1703 contact_width: None,
1704 contact_height: None,
1705 ..Default::default()
1706 };
1707 let reports = vec![
1708 create_touch_input_report(vec![contact], None, event_time_i64),
1709 create_touch_input_report(
1710 vec![],
1711 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1712 event_time_i64,
1713 ),
1714 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1715 ];
1716
1717 let expected_events = vec![
1718 create_touch_screen_event_with_buttons(
1719 SortedVecMap::from_iter(vec![
1720 (
1721 fidl_ui_input::PointerEventPhase::Add,
1722 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1723 ),
1724 (
1725 fidl_ui_input::PointerEventPhase::Down,
1726 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1727 ),
1728 ]),
1729 vec![],
1730 event_time_u64,
1731 &descriptor,
1732 ),
1733 create_touch_screen_event_with_buttons(
1734 SortedVecMap::new(),
1735 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1736 event_time_u64,
1737 &descriptor,
1738 ),
1739 create_touch_screen_event_with_buttons(
1740 SortedVecMap::new(),
1741 vec![],
1742 event_time_u64,
1743 &descriptor,
1744 ),
1745 ];
1746
1747 assert_input_report_sequence_generates_events_with_feature_flags!(
1748 input_reports: reports,
1749 expected_events: expected_events,
1750 device_descriptor: descriptor,
1751 device_type: TouchBinding,
1752 feature_flags: input_device::InputPipelineFeatureFlags {
1753 enable_merge_touch_events,
1754 ..Default::default()
1755 },
1756 );
1757 }
1758
1759 #[fasync::run_singlethreaded(test)]
1760 async fn process_reports_batches_events() {
1761 const TOUCH_ID: u32 = 2;
1762
1763 let descriptor =
1764 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1765 device_id: 1,
1766 contacts: vec![],
1767 });
1768 let (event_time_i64, _) = testing_utilities::event_times();
1769
1770 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1771 contact_id: Some(TOUCH_ID),
1772 position_x: Some(0),
1773 position_y: Some(0),
1774 ..Default::default()
1775 };
1776 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1777 contact_id: Some(TOUCH_ID),
1778 position_x: Some(10),
1779 position_y: Some(10),
1780 ..Default::default()
1781 };
1782 let reports = vec![
1783 create_touch_input_report(vec![contact1], None, event_time_i64),
1784 create_touch_input_report(vec![contact2], None, event_time_i64),
1785 ];
1786
1787 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1788
1789 let inspector = fuchsia_inspect::Inspector::default();
1790 let test_node = inspector.root().create_child("TestDevice_Touch");
1791 let mut inspect_status = InputDeviceStatus::new(test_node);
1792 inspect_status.health_node.set_ok();
1793
1794 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1795 let _ = TouchBinding::process_reports(
1796 &reports_wire,
1797 None,
1798 &descriptor,
1799 &mut event_sender,
1800 &inspect_status,
1801 &metrics::MetricsLogger::default(),
1802 &input_device::InputPipelineFeatureFlags::default(),
1803 );
1804
1805 let batch = event_receiver.try_next().expect("Expected a batch of events");
1807 let events = batch.expect("Expected events in the batch");
1808 assert_eq!(events.len(), 2);
1809
1810 assert!(event_receiver.try_next().is_err());
1812 }
1813
1814 #[fasync::run_singlethreaded(test)]
1815 async fn process_reports_merges_touch_events_when_enabled() {
1816 const TOUCH_ID: u32 = 2;
1817 let descriptor =
1818 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1819 device_id: 1,
1820 contacts: vec![],
1821 });
1822 let (event_time_i64, _) = testing_utilities::event_times();
1823
1824 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1825 contact_id: Some(TOUCH_ID),
1826 position_x: Some(0),
1827 position_y: Some(0),
1828 ..Default::default()
1829 };
1830 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1831 contact_id: Some(TOUCH_ID),
1832 position_x: Some(10),
1833 position_y: Some(10),
1834 ..Default::default()
1835 };
1836 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1837 contact_id: Some(TOUCH_ID),
1838 position_x: Some(20),
1839 position_y: Some(20),
1840 ..Default::default()
1841 };
1842 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1843 contact_id: Some(TOUCH_ID),
1844 position_x: Some(30),
1845 position_y: Some(30),
1846 ..Default::default()
1847 };
1848 let reports = vec![
1849 create_touch_input_report(vec![contact_add], None, event_time_i64),
1850 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1851 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1852 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1853 create_touch_input_report(vec![], None, event_time_i64),
1854 ];
1855
1856 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1857 let inspector = fuchsia_inspect::Inspector::default();
1858 let mut inspect_status =
1859 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1860 inspect_status.health_node.set_ok();
1861
1862 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1863 let _ = TouchBinding::process_reports(
1864 &reports_wire,
1865 None,
1866 &descriptor,
1867 &mut event_sender,
1868 &inspect_status,
1869 &metrics::MetricsLogger::default(),
1870 &input_device::InputPipelineFeatureFlags {
1871 enable_merge_touch_events: true,
1872 ..Default::default()
1873 },
1874 );
1875
1876 let batch = event_receiver.try_next().unwrap().unwrap();
1877
1878 assert_eq!(batch.len(), 3);
1880
1881 assert_matches!(
1883 &batch[0].device_event,
1884 input_device::InputDeviceEvent::TouchScreen(event)
1885 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
1886 );
1887 assert_matches!(
1889 &batch[1].device_event,
1890 input_device::InputDeviceEvent::TouchScreen(event)
1891 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
1892 );
1893 assert_matches!(
1895 &batch[2].device_event,
1896 input_device::InputDeviceEvent::TouchScreen(event)
1897 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
1898 );
1899 }
1900
1901 #[fasync::run_singlethreaded(test)]
1902 async fn process_reports_does_not_merge_touch_events_when_disabled() {
1903 const TOUCH_ID: u32 = 2;
1904 let descriptor =
1905 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1906 device_id: 1,
1907 contacts: vec![],
1908 });
1909 let (event_time_i64, _) = testing_utilities::event_times();
1910
1911 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1912 contact_id: Some(TOUCH_ID),
1913 position_x: Some(0),
1914 position_y: Some(0),
1915 ..Default::default()
1916 };
1917 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1918 contact_id: Some(TOUCH_ID),
1919 position_x: Some(10),
1920 position_y: Some(10),
1921 ..Default::default()
1922 };
1923 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1924 contact_id: Some(TOUCH_ID),
1925 position_x: Some(20),
1926 position_y: Some(20),
1927 ..Default::default()
1928 };
1929 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1930 contact_id: Some(TOUCH_ID),
1931 position_x: Some(30),
1932 position_y: Some(30),
1933 ..Default::default()
1934 };
1935 let reports = vec![
1936 create_touch_input_report(vec![contact_add], None, event_time_i64),
1937 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1938 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1939 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1940 create_touch_input_report(vec![], None, event_time_i64),
1941 ];
1942
1943 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1944 let inspector = fuchsia_inspect::Inspector::default();
1945 let mut inspect_status =
1946 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1947 inspect_status.health_node.set_ok();
1948
1949 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1950 let _ = TouchBinding::process_reports(
1951 &reports_wire,
1952 None,
1953 &descriptor,
1954 &mut event_sender,
1955 &inspect_status,
1956 &metrics::MetricsLogger::default(),
1957 &input_device::InputPipelineFeatureFlags {
1958 enable_merge_touch_events: false,
1959 ..Default::default()
1960 },
1961 );
1962
1963 let batch = event_receiver.try_next().unwrap().unwrap();
1964
1965 assert_eq!(batch.len(), 5);
1967 }
1968}