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 fuchsia_async as fasync;
951 use futures::StreamExt;
952 use pretty_assertions::assert_eq;
953 use test_case::test_case;
954
955 #[fasync::run_singlethreaded(test)]
956 async fn process_empty_reports() {
957 let report_time = zx::MonotonicInstant::get().into_nanos();
958 let report =
959 create_touch_input_report(vec![], None, report_time);
960
961 let descriptor =
962 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
963 device_id: 1,
964 contacts: vec![],
965 });
966 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
967
968 let inspector = fuchsia_inspect::Inspector::default();
969 let test_node = inspector.root().create_child("TestDevice_Touch");
970 let mut inspect_status = InputDeviceStatus::new(test_node);
971 inspect_status.health_node.set_ok();
972
973 let previous_state = input_device::PreviousDeviceState::TouchScreen {
974 active_contacts: vec![],
975 pressed_buttons: vec![],
976 };
977
978 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
979 let (returned_state, _) = TouchBinding::process_reports(
980 &reports_wire,
981 Some(previous_state),
982 &descriptor,
983 &mut event_sender,
984 &inspect_status,
985 &metrics::MetricsLogger::default(),
986 &input_device::InputPipelineFeatureFlags::default(),
987 );
988 assert!(returned_state.is_some());
989 assert_eq!(
990 returned_state.unwrap(),
991 input_device::PreviousDeviceState::TouchScreen {
992 active_contacts: vec![],
993 pressed_buttons: vec![]
994 }
995 );
996
997 let event = event_receiver.try_next();
999 assert!(event.is_err());
1000
1001 diagnostics_assertions::assert_data_tree!(inspector, root: {
1002 "TestDevice_Touch": contains {
1003 reports_received_count: 1u64,
1004 reports_filtered_count: 1u64,
1005 events_generated: 0u64,
1006 last_received_timestamp_ns: report_time as u64,
1007 last_generated_timestamp_ns: 0u64,
1008 "fuchsia.inspect.Health": {
1009 status: "OK",
1010 start_timestamp_nanos: AnyProperty
1013 },
1014 }
1015 });
1016 }
1017
1018 #[fasync::run_singlethreaded(test)]
1020 async fn add_and_down() {
1021 const TOUCH_ID: u32 = 2;
1022
1023 let descriptor =
1024 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1025 device_id: 1,
1026 contacts: vec![],
1027 });
1028 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1029
1030 let contact = fidl_fuchsia_input_report::ContactInputReport {
1031 contact_id: Some(TOUCH_ID),
1032 position_x: Some(0),
1033 position_y: Some(0),
1034 pressure: None,
1035 contact_width: None,
1036 contact_height: None,
1037 ..Default::default()
1038 };
1039 let reports = vec![create_touch_input_report(
1040 vec![contact],
1041 None,
1042 event_time_i64,
1043 )];
1044
1045 let expected_events = vec![create_touch_screen_event(
1046 SortedVecMap::from_iter(vec![
1047 (
1048 fidl_ui_input::PointerEventPhase::Add,
1049 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1050 ),
1051 (
1052 fidl_ui_input::PointerEventPhase::Down,
1053 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1054 ),
1055 ]),
1056 event_time_u64,
1057 &descriptor,
1058 )];
1059
1060 assert_input_report_sequence_generates_events!(
1061 input_reports: reports,
1062 expected_events: expected_events,
1063 device_descriptor: descriptor,
1064 device_type: TouchBinding,
1065 );
1066 }
1067
1068 #[fasync::run_singlethreaded(test)]
1070 async fn up_and_remove() {
1071 const TOUCH_ID: u32 = 2;
1072
1073 let descriptor =
1074 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1075 device_id: 1,
1076 contacts: vec![],
1077 });
1078 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1079
1080 let contact = fidl_fuchsia_input_report::ContactInputReport {
1081 contact_id: Some(TOUCH_ID),
1082 position_x: Some(0),
1083 position_y: Some(0),
1084 pressure: None,
1085 contact_width: None,
1086 contact_height: None,
1087 ..Default::default()
1088 };
1089 let reports = vec![
1090 create_touch_input_report(
1091 vec![contact],
1092 None,
1093 event_time_i64,
1094 ),
1095 create_touch_input_report(vec![], None, event_time_i64),
1096 ];
1097
1098 let expected_events = vec![
1099 create_touch_screen_event(
1100 SortedVecMap::from_iter(vec![
1101 (
1102 fidl_ui_input::PointerEventPhase::Add,
1103 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1104 ),
1105 (
1106 fidl_ui_input::PointerEventPhase::Down,
1107 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1108 ),
1109 ]),
1110 event_time_u64,
1111 &descriptor,
1112 ),
1113 create_touch_screen_event(
1114 SortedVecMap::from_iter(vec![
1115 (
1116 fidl_ui_input::PointerEventPhase::Up,
1117 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1118 ),
1119 (
1120 fidl_ui_input::PointerEventPhase::Remove,
1121 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1122 ),
1123 ]),
1124 event_time_u64,
1125 &descriptor,
1126 ),
1127 ];
1128
1129 assert_input_report_sequence_generates_events!(
1130 input_reports: reports,
1131 expected_events: expected_events,
1132 device_descriptor: descriptor,
1133 device_type: TouchBinding,
1134 );
1135 }
1136
1137 #[fasync::run_singlethreaded(test)]
1139 async fn add_down_move() {
1140 const TOUCH_ID: u32 = 2;
1141 let first = Position { x: 10.0, y: 30.0 };
1142 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1143
1144 let descriptor =
1145 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1146 device_id: 1,
1147 contacts: vec![],
1148 });
1149 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1150
1151 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1152 contact_id: Some(TOUCH_ID),
1153 position_x: Some(first.x as i64),
1154 position_y: Some(first.y as i64),
1155 pressure: None,
1156 contact_width: None,
1157 contact_height: None,
1158 ..Default::default()
1159 };
1160 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1161 contact_id: Some(TOUCH_ID),
1162 position_x: Some(first.x as i64 * 2),
1163 position_y: Some(first.y as i64 * 2),
1164 pressure: None,
1165 contact_width: None,
1166 contact_height: None,
1167 ..Default::default()
1168 };
1169
1170 let reports = vec![
1171 create_touch_input_report(
1172 vec![first_contact],
1173 None,
1174 event_time_i64,
1175 ),
1176 create_touch_input_report(
1177 vec![second_contact],
1178 None,
1179 event_time_i64,
1180 ),
1181 ];
1182
1183 let expected_events = vec![
1184 create_touch_screen_event(
1185 SortedVecMap::from_iter(vec![
1186 (
1187 fidl_ui_input::PointerEventPhase::Add,
1188 vec![create_touch_contact(TOUCH_ID, first)],
1189 ),
1190 (
1191 fidl_ui_input::PointerEventPhase::Down,
1192 vec![create_touch_contact(TOUCH_ID, first)],
1193 ),
1194 ]),
1195 event_time_u64,
1196 &descriptor,
1197 ),
1198 create_touch_screen_event(
1199 SortedVecMap::from_iter(vec![(
1200 fidl_ui_input::PointerEventPhase::Move,
1201 vec![create_touch_contact(TOUCH_ID, second)],
1202 )]),
1203 event_time_u64,
1204 &descriptor,
1205 ),
1206 ];
1207
1208 assert_input_report_sequence_generates_events!(
1209 input_reports: reports,
1210 expected_events: expected_events,
1211 device_descriptor: descriptor,
1212 device_type: TouchBinding,
1213 );
1214 }
1215
1216 #[fasync::run_singlethreaded(test)]
1217 async fn sent_event_has_trace_id() {
1218 let report_time = zx::MonotonicInstant::get().into_nanos();
1219 let contact = fidl_fuchsia_input_report::ContactInputReport {
1220 contact_id: Some(222),
1221 position_x: Some(333),
1222 position_y: Some(444),
1223 ..Default::default()
1224 };
1225 let report =
1226 create_touch_input_report(vec![contact], None, report_time);
1227
1228 let descriptor =
1229 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1230 device_id: 1,
1231 contacts: vec![],
1232 });
1233 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1234
1235 let inspector = fuchsia_inspect::Inspector::default();
1236 let test_node = inspector.root().create_child("TestDevice_Touch");
1237 let mut inspect_status = InputDeviceStatus::new(test_node);
1238 inspect_status.health_node.set_ok();
1239
1240 let previous_state = input_device::PreviousDeviceState::TouchScreen {
1241 active_contacts: vec![],
1242 pressed_buttons: vec![],
1243 };
1244
1245 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
1246 let _ = TouchBinding::process_reports(
1247 &reports_wire,
1248 Some(previous_state),
1249 &descriptor,
1250 &mut event_sender,
1251 &inspect_status,
1252 &metrics::MetricsLogger::default(),
1253 &input_device::InputPipelineFeatureFlags::default(),
1254 );
1255 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1256 }
1257
1258 #[fuchsia::test(allow_stalls = false)]
1259 async fn enables_touchpad_mode_automatically() {
1260 let (set_feature_report_sender, set_feature_report_receiver) =
1261 futures::channel::mpsc::unbounded();
1262 let (input_device_proxy, _task) = spawn_input_stream_handler(move |input_device_request| {
1263 let set_feature_report_sender = set_feature_report_sender.clone();
1264 async move {
1265 match input_device_request {
1266 fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1267 let _ = responder.send(&get_touchpad_device_descriptor(
1268 true, ));
1270 }
1271 fidl_fuchsia_input_report::InputDeviceRequest::GetFeatureReport {
1272 responder,
1273 } => {
1274 let _ = responder.send(Ok(&fidl_fuchsia_input_report::FeatureReport {
1275 touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1276 input_mode: Some(
1277 fidl_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection,
1278 ),
1279 ..Default::default()
1280 }),
1281 ..Default::default()
1282 }));
1283 }
1284 fidl_fuchsia_input_report::InputDeviceRequest::SetFeatureReport {
1285 responder,
1286 report,
1287 } => {
1288 match set_feature_report_sender.unbounded_send(report) {
1289 Ok(_) => {
1290 let _ = responder.send(Ok(()));
1291 }
1292 Err(e) => {
1293 panic!("try_send set_feature_report_request failed: {}", e);
1294 }
1295 };
1296 }
1297 fidl_fuchsia_input_report::InputDeviceRequest::GetInputReportsReader {
1298 ..
1299 } => {
1300 }
1302 r => panic!("unsupported request {:?}", r),
1303 }
1304 }
1305 });
1306
1307 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1308
1309 let inspector = fuchsia_inspect::Inspector::default();
1311 let test_node = inspector.root().create_child("test_node");
1312
1313 TouchBinding::new(
1317 input_device_proxy,
1318 0,
1319 device_event_sender,
1320 test_node,
1321 input_device::InputPipelineFeatureFlags::default(),
1322 metrics::MetricsLogger::default(),
1323 )
1324 .await
1325 .unwrap();
1326 assert_matches!(
1327 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1328 [fidl_fuchsia_input_report::FeatureReport {
1329 touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1330 input_mode: Some(
1331 fidl_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1332 ),
1333 ..
1334 }),
1335 ..
1336 }]
1337 );
1338 }
1339
1340 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1341 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1342 #[test_case(true, Some(fidl_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1343 #[test_case(true, Some(fidl_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1344 #[fuchsia::test(allow_stalls = false)]
1345 async fn identifies_correct_touch_device_type(
1346 has_mouse_descriptor: bool,
1347 touch_input_mode: Option<fidl_fuchsia_input_report::TouchConfigurationInputMode>,
1348 expect_touch_device_type: TouchDeviceType,
1349 ) {
1350 let (input_device_proxy, _task) =
1351 spawn_input_stream_handler(move |input_device_request| async move {
1352 match input_device_request {
1353 fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1354 let _ =
1355 responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1356 }
1357 fidl_fuchsia_input_report::InputDeviceRequest::GetFeatureReport {
1358 responder,
1359 } => {
1360 let _ = responder.send(Ok(&fidl_fuchsia_input_report::FeatureReport {
1361 touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1362 input_mode: touch_input_mode,
1363 ..Default::default()
1364 }),
1365 ..Default::default()
1366 }));
1367 }
1368 fidl_fuchsia_input_report::InputDeviceRequest::SetFeatureReport {
1369 responder,
1370 ..
1371 } => {
1372 let _ = responder.send(Ok(()));
1373 }
1374 r => panic!("unsupported request {:?}", r),
1375 }
1376 });
1377
1378 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1379
1380 let inspector = fuchsia_inspect::Inspector::default();
1382 let test_node = inspector.root().create_child("test_node");
1383
1384 let binding = TouchBinding::new(
1385 input_device_proxy,
1386 0,
1387 device_event_sender,
1388 test_node,
1389 input_device::InputPipelineFeatureFlags::default(),
1390 metrics::MetricsLogger::default(),
1391 )
1392 .await
1393 .unwrap();
1394 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1395 }
1396
1397 fn get_touchpad_device_descriptor(
1400 has_mouse_descriptor: bool,
1401 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1402 fidl_fuchsia_input_report::DeviceDescriptor {
1403 mouse: match has_mouse_descriptor {
1404 true => Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
1405 false => None,
1406 },
1407 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1408 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1409 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1410 position_x: Some(fidl_fuchsia_input::Axis {
1411 range: fidl_fuchsia_input::Range { min: 1, max: 2 },
1412 unit: fidl_fuchsia_input::Unit {
1413 type_: fidl_fuchsia_input::UnitType::None,
1414 exponent: 0,
1415 },
1416 }),
1417 position_y: Some(fidl_fuchsia_input::Axis {
1418 range: fidl_fuchsia_input::Range { min: 2, max: 3 },
1419 unit: fidl_fuchsia_input::Unit {
1420 type_: fidl_fuchsia_input::UnitType::Other,
1421 exponent: 100000,
1422 },
1423 }),
1424 pressure: Some(fidl_fuchsia_input::Axis {
1425 range: fidl_fuchsia_input::Range { min: 3, max: 4 },
1426 unit: fidl_fuchsia_input::Unit {
1427 type_: fidl_fuchsia_input::UnitType::Grams,
1428 exponent: -991,
1429 },
1430 }),
1431 contact_width: Some(fidl_fuchsia_input::Axis {
1432 range: fidl_fuchsia_input::Range { min: 5, max: 6 },
1433 unit: fidl_fuchsia_input::Unit {
1434 type_: fidl_fuchsia_input::UnitType::EnglishAngularVelocity,
1435 exponent: 123,
1436 },
1437 }),
1438 contact_height: Some(fidl_fuchsia_input::Axis {
1439 range: fidl_fuchsia_input::Range { min: 7, max: 8 },
1440 unit: fidl_fuchsia_input::Unit {
1441 type_: fidl_fuchsia_input::UnitType::Pascals,
1442 exponent: 100,
1443 },
1444 }),
1445 ..Default::default()
1446 }]),
1447 ..Default::default()
1448 }),
1449 ..Default::default()
1450 }),
1451 ..Default::default()
1452 }
1453 }
1454
1455 #[test_case(true; "merge touch events enabled")]
1458 #[test_case(false; "merge touch events disabled")]
1459 #[fasync::run_singlethreaded(test)]
1460 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1461 let descriptor =
1462 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1463 device_id: 1,
1464 contacts: vec![],
1465 });
1466 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1467
1468 let reports = vec![create_touch_input_report(
1469 vec![],
1470 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1471 event_time_i64,
1472 )];
1473
1474 let expected_events = vec![create_touch_screen_event_with_buttons(
1475 SortedVecMap::new(),
1476 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1477 event_time_u64,
1478 &descriptor,
1479 )];
1480
1481 assert_input_report_sequence_generates_events_with_feature_flags!(
1482 input_reports: reports,
1483 expected_events: expected_events,
1484 device_descriptor: descriptor,
1485 device_type: TouchBinding,
1486 feature_flags: input_device::InputPipelineFeatureFlags {
1487 enable_merge_touch_events,
1488 ..Default::default()
1489 },
1490 );
1491 }
1492
1493 #[test_case(true; "merge touch events enabled")]
1496 #[test_case(false; "merge touch events disabled")]
1497 #[fasync::run_singlethreaded(test)]
1498 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1499 const TOUCH_ID: u32 = 2;
1500
1501 let descriptor =
1502 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1503 device_id: 1,
1504 contacts: vec![],
1505 });
1506 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1507
1508 let contact = fidl_fuchsia_input_report::ContactInputReport {
1509 contact_id: Some(TOUCH_ID),
1510 position_x: Some(0),
1511 position_y: Some(0),
1512 pressure: None,
1513 contact_width: None,
1514 contact_height: None,
1515 ..Default::default()
1516 };
1517 let reports = vec![create_touch_input_report(
1518 vec![contact],
1519 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1520 event_time_i64,
1521 )];
1522
1523 let expected_events = vec![create_touch_screen_event_with_buttons(
1524 SortedVecMap::from_iter(vec![
1525 (
1526 fidl_ui_input::PointerEventPhase::Add,
1527 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1528 ),
1529 (
1530 fidl_ui_input::PointerEventPhase::Down,
1531 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1532 ),
1533 ]),
1534 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1535 event_time_u64,
1536 &descriptor,
1537 )];
1538
1539 assert_input_report_sequence_generates_events_with_feature_flags!(
1540 input_reports: reports,
1541 expected_events: expected_events,
1542 device_descriptor: descriptor,
1543 device_type: TouchBinding,
1544 feature_flags: input_device::InputPipelineFeatureFlags {
1545 enable_merge_touch_events,
1546 ..Default::default()
1547 },
1548 );
1549 }
1550
1551 #[test_case(true; "merge touch events enabled")]
1554 #[test_case(false; "merge touch events disabled")]
1555 #[fasync::run_singlethreaded(test)]
1556 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1557 const TOUCH_ID: u32 = 2;
1558
1559 let descriptor =
1560 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1561 device_id: 1,
1562 contacts: vec![],
1563 });
1564 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1565
1566 let contact = fidl_fuchsia_input_report::ContactInputReport {
1567 contact_id: Some(TOUCH_ID),
1568 position_x: Some(0),
1569 position_y: Some(0),
1570 pressure: None,
1571 contact_width: None,
1572 contact_height: None,
1573 ..Default::default()
1574 };
1575 let reports = vec![create_touch_input_report(
1576 vec![contact],
1577 Some(vec![
1578 fidl_fuchsia_input_report::TouchButton::Palm,
1579 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1580 ]),
1581 event_time_i64,
1582 )];
1583
1584 let expected_events = vec![create_touch_screen_event_with_buttons(
1585 SortedVecMap::from_iter(vec![
1586 (
1587 fidl_ui_input::PointerEventPhase::Add,
1588 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1589 ),
1590 (
1591 fidl_ui_input::PointerEventPhase::Down,
1592 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1593 ),
1594 ]),
1595 vec![
1596 fidl_fuchsia_input_report::TouchButton::Palm,
1597 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1598 ],
1599 event_time_u64,
1600 &descriptor,
1601 )];
1602
1603 assert_input_report_sequence_generates_events_with_feature_flags!(
1604 input_reports: reports,
1605 expected_events: expected_events,
1606 device_descriptor: descriptor,
1607 device_type: TouchBinding,
1608 feature_flags: input_device::InputPipelineFeatureFlags {
1609 enable_merge_touch_events,
1610 ..Default::default()
1611 },
1612 );
1613 }
1614
1615 #[test_case(true; "merge touch events enabled")]
1617 #[test_case(false; "merge touch events disabled")]
1618 #[fasync::run_singlethreaded(test)]
1619 async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1620 let descriptor =
1621 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1622 device_id: 1,
1623 contacts: vec![],
1624 });
1625 let (event_time_i64, _) = testing_utilities::event_times();
1626
1627 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1628
1629 let expected_events: Vec<input_device::InputEvent> = vec![];
1630
1631 assert_input_report_sequence_generates_events_with_feature_flags!(
1632 input_reports: reports,
1633 expected_events: expected_events,
1634 device_descriptor: descriptor,
1635 device_type: TouchBinding,
1636 feature_flags: input_device::InputPipelineFeatureFlags {
1637 enable_merge_touch_events,
1638 ..Default::default()
1639 },
1640 );
1641 }
1642
1643 #[test_case(true; "merge touch events enabled")]
1645 #[test_case(false; "merge touch events disabled")]
1646 #[fasync::run_singlethreaded(test)]
1647 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1648 const TOUCH_ID: u32 = 2;
1649
1650 let descriptor =
1651 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1652 device_id: 1,
1653 contacts: vec![],
1654 });
1655 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1656
1657 let contact = fidl_fuchsia_input_report::ContactInputReport {
1658 contact_id: Some(TOUCH_ID),
1659 position_x: Some(0),
1660 position_y: Some(0),
1661 pressure: None,
1662 contact_width: None,
1663 contact_height: None,
1664 ..Default::default()
1665 };
1666 let reports = vec![
1667 create_touch_input_report(vec![contact], None, event_time_i64),
1668 create_touch_input_report(
1669 vec![],
1670 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1671 event_time_i64,
1672 ),
1673 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1674 ];
1675
1676 let expected_events = vec![
1677 create_touch_screen_event_with_buttons(
1678 SortedVecMap::from_iter(vec![
1679 (
1680 fidl_ui_input::PointerEventPhase::Add,
1681 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1682 ),
1683 (
1684 fidl_ui_input::PointerEventPhase::Down,
1685 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1686 ),
1687 ]),
1688 vec![],
1689 event_time_u64,
1690 &descriptor,
1691 ),
1692 create_touch_screen_event_with_buttons(
1693 SortedVecMap::new(),
1694 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1695 event_time_u64,
1696 &descriptor,
1697 ),
1698 create_touch_screen_event_with_buttons(
1699 SortedVecMap::new(),
1700 vec![],
1701 event_time_u64,
1702 &descriptor,
1703 ),
1704 ];
1705
1706 assert_input_report_sequence_generates_events_with_feature_flags!(
1707 input_reports: reports,
1708 expected_events: expected_events,
1709 device_descriptor: descriptor,
1710 device_type: TouchBinding,
1711 feature_flags: input_device::InputPipelineFeatureFlags {
1712 enable_merge_touch_events,
1713 ..Default::default()
1714 },
1715 );
1716 }
1717
1718 #[fasync::run_singlethreaded(test)]
1719 async fn process_reports_batches_events() {
1720 const TOUCH_ID: u32 = 2;
1721
1722 let descriptor =
1723 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1724 device_id: 1,
1725 contacts: vec![],
1726 });
1727 let (event_time_i64, _) = testing_utilities::event_times();
1728
1729 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1730 contact_id: Some(TOUCH_ID),
1731 position_x: Some(0),
1732 position_y: Some(0),
1733 ..Default::default()
1734 };
1735 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1736 contact_id: Some(TOUCH_ID),
1737 position_x: Some(10),
1738 position_y: Some(10),
1739 ..Default::default()
1740 };
1741 let reports = vec![
1742 create_touch_input_report(vec![contact1], None, event_time_i64),
1743 create_touch_input_report(vec![contact2], None, event_time_i64),
1744 ];
1745
1746 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1747
1748 let inspector = fuchsia_inspect::Inspector::default();
1749 let test_node = inspector.root().create_child("TestDevice_Touch");
1750 let mut inspect_status = InputDeviceStatus::new(test_node);
1751 inspect_status.health_node.set_ok();
1752
1753 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1754 let _ = TouchBinding::process_reports(
1755 &reports_wire,
1756 None,
1757 &descriptor,
1758 &mut event_sender,
1759 &inspect_status,
1760 &metrics::MetricsLogger::default(),
1761 &input_device::InputPipelineFeatureFlags::default(),
1762 );
1763
1764 let batch = event_receiver.try_next().expect("Expected a batch of events");
1766 let events = batch.expect("Expected events in the batch");
1767 assert_eq!(events.len(), 2);
1768
1769 assert!(event_receiver.try_next().is_err());
1771 }
1772
1773 #[fasync::run_singlethreaded(test)]
1774 async fn process_reports_merges_touch_events_when_enabled() {
1775 const TOUCH_ID: u32 = 2;
1776 let descriptor =
1777 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1778 device_id: 1,
1779 contacts: vec![],
1780 });
1781 let (event_time_i64, _) = testing_utilities::event_times();
1782
1783 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1784 contact_id: Some(TOUCH_ID),
1785 position_x: Some(0),
1786 position_y: Some(0),
1787 ..Default::default()
1788 };
1789 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1790 contact_id: Some(TOUCH_ID),
1791 position_x: Some(10),
1792 position_y: Some(10),
1793 ..Default::default()
1794 };
1795 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1796 contact_id: Some(TOUCH_ID),
1797 position_x: Some(20),
1798 position_y: Some(20),
1799 ..Default::default()
1800 };
1801 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1802 contact_id: Some(TOUCH_ID),
1803 position_x: Some(30),
1804 position_y: Some(30),
1805 ..Default::default()
1806 };
1807 let reports = vec![
1808 create_touch_input_report(vec![contact_add], None, event_time_i64),
1809 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1810 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1811 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1812 create_touch_input_report(vec![], None, event_time_i64),
1813 ];
1814
1815 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1816 let inspector = fuchsia_inspect::Inspector::default();
1817 let mut inspect_status =
1818 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1819 inspect_status.health_node.set_ok();
1820
1821 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1822 let _ = TouchBinding::process_reports(
1823 &reports_wire,
1824 None,
1825 &descriptor,
1826 &mut event_sender,
1827 &inspect_status,
1828 &metrics::MetricsLogger::default(),
1829 &input_device::InputPipelineFeatureFlags {
1830 enable_merge_touch_events: true,
1831 ..Default::default()
1832 },
1833 );
1834
1835 let batch = event_receiver.try_next().unwrap().unwrap();
1836
1837 assert_eq!(batch.len(), 3);
1839
1840 assert_matches!(
1842 &batch[0].device_event,
1843 input_device::InputDeviceEvent::TouchScreen(event)
1844 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
1845 );
1846 assert_matches!(
1848 &batch[1].device_event,
1849 input_device::InputDeviceEvent::TouchScreen(event)
1850 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
1851 );
1852 assert_matches!(
1854 &batch[2].device_event,
1855 input_device::InputDeviceEvent::TouchScreen(event)
1856 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
1857 );
1858 }
1859
1860 #[fasync::run_singlethreaded(test)]
1861 async fn process_reports_does_not_merge_touch_events_when_disabled() {
1862 const TOUCH_ID: u32 = 2;
1863 let descriptor =
1864 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1865 device_id: 1,
1866 contacts: vec![],
1867 });
1868 let (event_time_i64, _) = testing_utilities::event_times();
1869
1870 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1871 contact_id: Some(TOUCH_ID),
1872 position_x: Some(0),
1873 position_y: Some(0),
1874 ..Default::default()
1875 };
1876 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1877 contact_id: Some(TOUCH_ID),
1878 position_x: Some(10),
1879 position_y: Some(10),
1880 ..Default::default()
1881 };
1882 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1883 contact_id: Some(TOUCH_ID),
1884 position_x: Some(20),
1885 position_y: Some(20),
1886 ..Default::default()
1887 };
1888 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1889 contact_id: Some(TOUCH_ID),
1890 position_x: Some(30),
1891 position_y: Some(30),
1892 ..Default::default()
1893 };
1894 let reports = vec![
1895 create_touch_input_report(vec![contact_add], None, event_time_i64),
1896 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1897 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1898 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1899 create_touch_input_report(vec![], None, event_time_i64),
1900 ];
1901
1902 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1903 let inspector = fuchsia_inspect::Inspector::default();
1904 let mut inspect_status =
1905 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1906 inspect_status.health_node.set_ok();
1907
1908 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1909 let _ = TouchBinding::process_reports(
1910 &reports_wire,
1911 None,
1912 &descriptor,
1913 &mut event_sender,
1914 &inspect_status,
1915 &metrics::MetricsLogger::default(),
1916 &input_device::InputPipelineFeatureFlags {
1917 enable_merge_touch_events: false,
1918 ..Default::default()
1919 },
1920 );
1921
1922 let batch = event_receiver.try_next().unwrap().unwrap();
1923
1924 assert_eq!(batch.len(), 5);
1926 }
1927}