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