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