1use crate::input_device::{self, Handled, 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
333#[async_trait]
334impl input_device::InputDeviceBinding for TouchBinding {
335 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
336 self.event_sender.clone()
337 }
338
339 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
340 match self.device_descriptor.clone() {
341 TouchDeviceDescriptor::TouchScreen(desc) => {
342 input_device::InputDeviceDescriptor::TouchScreen(desc)
343 }
344 TouchDeviceDescriptor::Touchpad(desc) => {
345 input_device::InputDeviceDescriptor::Touchpad(desc)
346 }
347 }
348 }
349}
350
351impl TouchBinding {
352 pub async fn new(
367 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
368 device_id: u32,
369 input_event_sender: UnboundedSender<Vec<InputEvent>>,
370 device_node: fuchsia_inspect::Node,
371 feature_flags: input_device::InputPipelineFeatureFlags,
372 metrics_logger: metrics::MetricsLogger,
373 ) -> Result<(Self, crate::dispatcher::TaskHandle<()>), Error> {
374 let (device_descriptor, touch_device_type, mut inspect_status) =
375 Self::bind_device(&device_proxy, device_id, device_node).await?;
376 Self::set_touchpad_mode(&device_proxy, touch_device_type, true)
377 .await
378 .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
379 inspect_status.health_node.set_ok();
380 let task = input_device::initialize_report_stream(
381 device_proxy.clone(),
382 match device_descriptor.clone() {
383 TouchDeviceDescriptor::TouchScreen(desc) => {
384 input_device::InputDeviceDescriptor::TouchScreen(desc)
385 }
386 TouchDeviceDescriptor::Touchpad(desc) => {
387 input_device::InputDeviceDescriptor::Touchpad(desc)
388 }
389 },
390 input_event_sender.clone(),
391 inspect_status,
392 metrics_logger,
393 feature_flags,
394 Self::process_reports,
395 );
396
397 Ok((TouchBinding { event_sender: input_event_sender, device_descriptor }, task))
398 }
399
400 async fn bind_device(
412 device_proxy: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
413 device_id: u32,
414 device_node: fuchsia_inspect::Node,
415 ) -> Result<(TouchDeviceDescriptor, TouchDeviceType, InputDeviceStatus), Error> {
416 let mut input_device_status = InputDeviceStatus::new(device_node);
417 let device_descriptor: fidl_next_fuchsia_input_report::DeviceDescriptor = match device_proxy
418 .get_descriptor()
419 .await
420 {
421 Ok(res) => res.descriptor,
422 Err(_) => {
423 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
424 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
425 }
426 };
427
428 let touch_device_type = get_device_type(device_proxy).await;
429
430 match device_descriptor.touch {
431 Some(fidl_next_fuchsia_input_report::TouchDescriptor {
432 input:
433 Some(fidl_next_fuchsia_input_report::TouchInputDescriptor {
434 contacts: Some(contact_descriptors),
435 max_contacts: _,
436 touch_type: _,
437 buttons: _,
438 ..
439 }),
440 ..
441 }) => Ok((
442 match touch_device_type {
443 TouchDeviceType::TouchScreen => {
444 TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
445 device_id,
446 contacts: contact_descriptors
447 .iter()
448 .map(TouchBinding::parse_contact_descriptor)
449 .filter_map(Result::ok)
450 .collect(),
451 })
452 }
453 TouchDeviceType::WindowsPrecisionTouchpad => {
454 TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
455 device_id,
456 contacts: contact_descriptors
457 .iter()
458 .map(TouchBinding::parse_contact_descriptor)
459 .filter_map(Result::ok)
460 .collect(),
461 })
462 }
463 },
464 touch_device_type,
465 input_device_status,
466 )),
467 descriptor => {
468 input_device_status
469 .health_node
470 .set_unhealthy("Touch Device Descriptor failed to parse.");
471 Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
472 }
473 }
474 }
475
476 async fn set_touchpad_mode(
477 device_proxy: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
478 touch_device_type: TouchDeviceType,
479 enable: bool,
480 ) -> Result<(), Error> {
481 match touch_device_type {
482 TouchDeviceType::TouchScreen => Ok(()),
483 TouchDeviceType::WindowsPrecisionTouchpad => {
484 let mut report = match device_proxy.get_feature_report().await? {
487 Ok(res) => res.report,
488 Err(e) => return Err(format_err!("get_feature_report failed: {e:?}")),
489 };
490 let mut touch = report
491 .touch
492 .unwrap_or_else(fidl_next_fuchsia_input_report::TouchFeatureReport::default);
493 touch.input_mode = match enable {
494 true => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
495 false => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection),
496 };
497 report.touch = Some(touch);
498 match device_proxy.set_feature_report(&report).await? {
499 Ok(_) => {
500 log::info!("touchpad: set touchpad_enabled to {}", enable);
502 Ok(())
503 }
504 Err(e) => Err(format_err!("set_feature_report failed: {e:?}")),
505 }
506 }
507 }
508 }
509
510 fn process_reports(
532 reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
533 previous_state: Option<input_device::PreviousDeviceState>,
534 device_descriptor: &input_device::InputDeviceDescriptor,
535 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
536 inspect_status: &InputDeviceStatus,
537 metrics_logger: &metrics::MetricsLogger,
538 feature_flags: &input_device::InputPipelineFeatureFlags,
539 ) -> (Option<input_device::PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>) {
540 fuchsia_trace::duration!(
541 "input",
542 "touch-binding-process-report",
543 "num_reports" => reports.len(),
544 );
545 match device_descriptor {
546 input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
547 reports,
548 previous_state,
549 device_descriptor,
550 input_event_sender,
551 inspect_status,
552 metrics_logger,
553 feature_flags.enable_merge_touch_events,
554 ),
555 input_device::InputDeviceDescriptor::Touchpad(_) => {
556 (previous_state, None)
558 }
559 _ => (previous_state, None),
560 }
561 }
562
563 fn parse_contact_descriptor(
571 contact_device_descriptor: &fidl_next_fuchsia_input_report::ContactInputDescriptor,
572 ) -> Result<ContactDeviceDescriptor, Error> {
573 match contact_device_descriptor {
574 fidl_next_fuchsia_input_report::ContactInputDescriptor {
575 position_x: Some(x_axis),
576 position_y: Some(y_axis),
577 pressure: pressure_axis,
578 contact_width: width_axis,
579 contact_height: height_axis,
580 ..
581 } => Ok(ContactDeviceDescriptor {
582 x_range: utils::range_to_old(&x_axis.range),
583 y_range: utils::range_to_old(&y_axis.range),
584 x_unit: utils::unit_to_old(&x_axis.unit),
585 y_unit: utils::unit_to_old(&y_axis.unit),
586 pressure_range: pressure_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
587 width_range: width_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
588 height_range: height_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
589 }),
590 descriptor => {
591 Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
592 }
593 }
594 }
595}
596
597fn is_move_only(event: &InputEvent) -> bool {
598 matches!(
599 &event.device_event,
600 input_device::InputDeviceEvent::TouchScreen(event)
601 if event
602 .injector_contacts
603 .get(&pointerinjector::EventPhase::Add)
604 .map_or(true, |c| c.is_empty())
605 && event
606 .injector_contacts
607 .get(&pointerinjector::EventPhase::Remove)
608 .map_or(true, |c| c.is_empty())
609 && event
610 .injector_contacts
611 .get(&pointerinjector::EventPhase::Cancel)
612 .map_or(true, |c| c.is_empty())
613 )
614}
615
616fn has_pressed_buttons(event: &InputEvent) -> bool {
617 match &event.device_event {
618 input_device::InputDeviceEvent::TouchScreen(event) => !event.pressed_buttons.is_empty(),
619 _ => false,
620 }
621}
622
623fn process_touch_screen_reports(
624 reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
625 mut previous_state: Option<input_device::PreviousDeviceState>,
626 device_descriptor: &input_device::InputDeviceDescriptor,
627 input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
628 inspect_status: &InputDeviceStatus,
629 metrics_logger: &metrics::MetricsLogger,
630 enable_merge_touch_events: bool,
631) -> (Option<input_device::PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>) {
632 let num_reports = reports.len();
633 let mut batch: Vec<InputEvent> = Vec::with_capacity(num_reports);
634 for report in reports {
635 inspect_status.count_received_report_wire(report);
636 let (prev_state, event) = process_single_touch_screen_report(
637 report,
638 previous_state,
639 device_descriptor,
640 inspect_status,
641 metrics_logger,
642 );
643 previous_state = prev_state;
644 if let Some(event) = event {
645 batch.push(event);
646 }
647 }
648
649 if !batch.is_empty() {
650 if enable_merge_touch_events {
651 let mut is_event_move_only: Vec<bool> = Vec::with_capacity(batch.len());
653 let mut pressed_buttons: Vec<bool> = Vec::with_capacity(batch.len());
654 for event in &batch {
655 is_event_move_only.push(is_move_only(event));
656 pressed_buttons.push(has_pressed_buttons(event));
657 }
658 let size_of_batch = batch.len();
659
660 let mut merged_batch = Vec::with_capacity(size_of_batch);
662
663 for (i, current_event) in batch.into_iter().enumerate() {
665 let current_is_move = is_event_move_only[i];
666 let current_pressed_buttons = pressed_buttons[i];
667 let is_last_event = i == size_of_batch - 1;
668
669 let next_is_move =
671 if i + 1 < size_of_batch { is_event_move_only[i + 1] } else { false };
672
673 let next_pressed_buttons = if i + 1 < size_of_batch {
674 pressed_buttons[i + 1]
675 } else {
676 current_pressed_buttons
677 };
678
679 if !is_last_event
682 && (current_is_move && next_is_move)
684 && (current_pressed_buttons == next_pressed_buttons)
686 {
687 continue;
688 }
689
690 merged_batch.push(current_event);
691 }
692
693 batch = merged_batch;
694 }
695
696 let events_to_send: Vec<InputEvent> = {
697 fuchsia_trace::duration!("input", "prepare_events_to_send");
698 batch
699 .into_iter()
700 .map(|event| {
701 let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
705 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
706 event
707 })
708 .collect()
709 };
710 fuchsia_trace::instant!(
711 "input",
712 "events_to_input_handlers",
713 fuchsia_trace::Scope::Thread,
714 "num_reports" => num_reports,
715 "num_events_generated" => events_to_send.len()
716 );
717
718 inspect_status.count_generated_events(&events_to_send);
720
721 if let Err(e) = input_event_sender.unbounded_send(events_to_send) {
722 metrics_logger.log_error(
723 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
724 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
725 );
726 }
727 }
728 (previous_state, None)
729}
730
731fn process_single_touch_screen_report(
732 report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
733 previous_state: Option<input_device::PreviousDeviceState>,
734 device_descriptor: &input_device::InputDeviceDescriptor,
735 inspect_status: &InputDeviceStatus,
736 metrics_logger: &metrics::MetricsLogger,
737) -> (Option<input_device::PreviousDeviceState>, Option<InputEvent>) {
738 fuchsia_trace::flow_end!(
739 "input",
740 "input_report",
741 report.trace_id().map(|x| x.0).unwrap_or(0).into()
742 );
743
744 let wake_lease = utils::duplicate_wake_lease(report.wake_lease());
748
749 let touch_report = match report.touch() {
751 Some(touch) => touch,
752 None => {
753 inspect_status.count_filtered_report();
754 return (previous_state, None);
755 }
756 };
757
758 let (previous_contacts, previous_buttons): (
759 SortedVecMap<u32, TouchContact>,
760 Vec<fidl_next_fuchsia_input_report::TouchButton>,
761 ) = match &previous_state {
762 Some(input_device::PreviousDeviceState::TouchScreen {
763 active_contacts,
764 pressed_buttons,
765 }) => {
766 let contacts =
767 SortedVecMap::from_iter(active_contacts.iter().map(|c| (c.id, c.clone())));
768 (contacts, pressed_buttons.clone())
769 }
770 _ => (SortedVecMap::new(), vec![]),
771 };
772 let (current_contacts, current_buttons): (
773 SortedVecMap<u32, TouchContact>,
774 Vec<fidl_next_fuchsia_input_report::TouchButton>,
775 ) = touch_contacts_and_buttons_from_touch_report_wire(touch_report, metrics_logger);
776
777 if previous_contacts.is_empty()
778 && current_contacts.is_empty()
779 && previous_buttons.is_empty()
780 && current_buttons.is_empty()
781 {
782 inspect_status.count_filtered_report();
783 return (previous_state, None);
784 }
785
786 let added_contacts: Vec<TouchContact> = Vec::from_iter(
788 current_contacts
789 .iter()
790 .map(|(_, v)| v.clone())
791 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
792 );
793 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
795 current_contacts
796 .iter()
797 .map(|(_, v)| v.clone())
798 .filter(|contact| previous_contacts.contains_key(&contact.id)),
799 );
800 let removed_contacts: Vec<TouchContact> =
802 Vec::from_iter(previous_contacts.iter().map(|(_, v)| v.clone()).filter(|contact| {
803 current_buttons.is_empty()
804 && previous_buttons.is_empty()
805 && !current_contacts.contains_key(&contact.id)
806 }));
807
808 let active_contacts: Vec<TouchContact> = if current_contacts.is_empty()
809 && !previous_contacts.is_empty()
810 && (!current_buttons.is_empty() || !previous_buttons.is_empty())
811 {
812 previous_contacts.values().cloned().collect()
813 } else {
814 added_contacts.iter().chain(moved_contacts.iter()).cloned().collect()
815 };
816
817 let trace_id = fuchsia_trace::Id::new();
818 let event = create_touch_screen_event(
819 SortedVecMap::from_iter(vec![
820 (fidl_ui_input::PointerEventPhase::Add, added_contacts.clone()),
821 (fidl_ui_input::PointerEventPhase::Down, added_contacts.clone()),
822 (fidl_ui_input::PointerEventPhase::Move, moved_contacts.clone()),
823 (fidl_ui_input::PointerEventPhase::Up, removed_contacts.clone()),
824 (fidl_ui_input::PointerEventPhase::Remove, removed_contacts.clone()),
825 ]),
826 SortedVecMap::from_iter(vec![
827 (pointerinjector::EventPhase::Add, added_contacts),
828 (pointerinjector::EventPhase::Change, moved_contacts),
829 (pointerinjector::EventPhase::Remove, removed_contacts),
830 ]),
831 current_buttons.clone(),
832 device_descriptor,
833 trace_id,
834 wake_lease,
835 );
836
837 let next_previous_state = input_device::PreviousDeviceState::TouchScreen {
838 active_contacts,
839 pressed_buttons: current_buttons,
840 };
841
842 (Some(next_previous_state), Some(event))
843}
844
845fn touch_contacts_and_buttons_from_touch_report_wire(
846 touch_report: &fidl_next_fuchsia_input_report::wire::TouchInputReport<'_>,
847 metrics_logger: &metrics::MetricsLogger,
848) -> (SortedVecMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
849 let mut contacts = Vec::new();
850 if let Some(unwrapped_contacts) = touch_report.contacts() {
851 for contact in unwrapped_contacts.iter() {
852 match TouchContact::try_from(contact) {
853 Ok(c) => contacts.push(c),
854 Err(e) => {
855 metrics_logger.log_warn(
856 InputPipelineErrorMetricDimensionEvent::TouchReportContactMissingField,
857 std::format!("failed to convert touch contact: {:?}", e),
858 );
859 }
860 }
861 }
862 } else {
863 metrics_logger.log_warn(
864 InputPipelineErrorMetricDimensionEvent::TouchReportMissingContact,
865 "contacts missing in touch input report",
866 );
867 }
868
869 let pressed_buttons = touch_report
870 .pressed_buttons()
871 .map(|buttons| buttons.iter().map(|&b| fidl_next::FromWire::from_wire(b)).collect())
872 .unwrap_or_default();
873
874 (
875 SortedVecMap::from_iter(contacts.into_iter().map(|contact| (contact.id, contact))),
876 pressed_buttons,
877 )
878}
879
880fn create_touch_screen_event(
890 contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
891 injector_contacts: SortedVecMap<pointerinjector::EventPhase, Vec<TouchContact>>,
892 pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
893 device_descriptor: &input_device::InputDeviceDescriptor,
894 trace_id: fuchsia_trace::Id,
895 wake_lease: Option<zx::EventPair>,
896) -> InputEvent {
897 input_device::InputEvent {
898 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
899 contacts,
900 injector_contacts,
901 pressed_buttons,
902 wake_lease,
903 }),
904 device_descriptor: device_descriptor.clone(),
905 event_time: zx::MonotonicInstant::get(),
906 handled: Handled::No,
907 trace_id: Some(trace_id),
908 }
909}
910
911async fn get_device_type(
917 input_device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
918) -> TouchDeviceType {
919 match input_device.get_feature_report().await {
920 Ok(Ok(fidl_next_fuchsia_input_report::InputDeviceGetFeatureReportResponse {
921 report: fidl_next_fuchsia_input_report::FeatureReport {
922 touch:
923 Some(fidl_next_fuchsia_input_report::TouchFeatureReport {
924 input_mode:
925 Some(
926 fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection
927 | fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
928 ),
929 ..
930 }),
931 ..
932 }
933 })) => TouchDeviceType::WindowsPrecisionTouchpad,
934 _ => TouchDeviceType::TouchScreen,
935 }
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use crate::testing_utilities::{
942 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
943 create_touch_screen_event_with_buttons, spawn_input_stream_handler,
944 };
945 use crate::utils::Position;
946 use assert_matches::assert_matches;
947 use diagnostics_assertions::AnyProperty;
948 use futures::StreamExt;
949 use pretty_assertions::assert_eq;
950 use test_case::test_case;
951
952 #[fuchsia::test]
953 async fn process_empty_reports() {
954 let report_time = zx::MonotonicInstant::get().into_nanos();
955 let report =
956 create_touch_input_report(vec![], None, report_time);
957
958 let descriptor =
959 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
960 device_id: 1,
961 contacts: vec![],
962 });
963 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
964
965 let inspector = fuchsia_inspect::Inspector::default();
966 let test_node = inspector.root().create_child("TestDevice_Touch");
967 let mut inspect_status = InputDeviceStatus::new(test_node);
968 inspect_status.health_node.set_ok();
969
970 let previous_state = input_device::PreviousDeviceState::TouchScreen {
971 active_contacts: vec![],
972 pressed_buttons: vec![],
973 };
974
975 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
976 let (returned_state, _) = TouchBinding::process_reports(
977 &reports_wire,
978 Some(previous_state),
979 &descriptor,
980 &mut event_sender,
981 &inspect_status,
982 &metrics::MetricsLogger::default(),
983 &input_device::InputPipelineFeatureFlags::default(),
984 );
985 assert!(returned_state.is_some());
986 assert_eq!(
987 returned_state.unwrap(),
988 input_device::PreviousDeviceState::TouchScreen {
989 active_contacts: vec![],
990 pressed_buttons: vec![]
991 }
992 );
993
994 let event = event_receiver.try_next();
996 assert!(event.is_err());
997
998 diagnostics_assertions::assert_data_tree!(inspector, root: {
999 "TestDevice_Touch": contains {
1000 reports_received_count: 1u64,
1001 reports_filtered_count: 1u64,
1002 events_generated: 0u64,
1003 last_received_timestamp_ns: report_time as u64,
1004 last_generated_timestamp_ns: 0u64,
1005 "fuchsia.inspect.Health": {
1006 status: "OK",
1007 start_timestamp_nanos: AnyProperty
1010 },
1011 }
1012 });
1013 }
1014
1015 #[fuchsia::test]
1017 async fn add_and_down() {
1018 const TOUCH_ID: u32 = 2;
1019
1020 let descriptor =
1021 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1022 device_id: 1,
1023 contacts: vec![],
1024 });
1025 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1026
1027 let contact = fidl_fuchsia_input_report::ContactInputReport {
1028 contact_id: Some(TOUCH_ID),
1029 position_x: Some(0),
1030 position_y: Some(0),
1031 pressure: None,
1032 contact_width: None,
1033 contact_height: None,
1034 ..Default::default()
1035 };
1036 let reports = vec![create_touch_input_report(
1037 vec![contact],
1038 None,
1039 event_time_i64,
1040 )];
1041
1042 let expected_events = vec![create_touch_screen_event(
1043 SortedVecMap::from_iter(vec![
1044 (
1045 fidl_ui_input::PointerEventPhase::Add,
1046 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1047 ),
1048 (
1049 fidl_ui_input::PointerEventPhase::Down,
1050 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1051 ),
1052 ]),
1053 event_time_u64,
1054 &descriptor,
1055 )];
1056
1057 assert_input_report_sequence_generates_events!(
1058 input_reports: reports,
1059 expected_events: expected_events,
1060 device_descriptor: descriptor,
1061 device_type: TouchBinding,
1062 );
1063 }
1064
1065 #[fuchsia::test]
1067 async fn up_and_remove() {
1068 const TOUCH_ID: u32 = 2;
1069
1070 let descriptor =
1071 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1072 device_id: 1,
1073 contacts: vec![],
1074 });
1075 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1076
1077 let contact = fidl_fuchsia_input_report::ContactInputReport {
1078 contact_id: Some(TOUCH_ID),
1079 position_x: Some(0),
1080 position_y: Some(0),
1081 pressure: None,
1082 contact_width: None,
1083 contact_height: None,
1084 ..Default::default()
1085 };
1086 let reports = vec![
1087 create_touch_input_report(
1088 vec![contact],
1089 None,
1090 event_time_i64,
1091 ),
1092 create_touch_input_report(vec![], None, event_time_i64),
1093 ];
1094
1095 let expected_events = vec![
1096 create_touch_screen_event(
1097 SortedVecMap::from_iter(vec![
1098 (
1099 fidl_ui_input::PointerEventPhase::Add,
1100 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1101 ),
1102 (
1103 fidl_ui_input::PointerEventPhase::Down,
1104 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1105 ),
1106 ]),
1107 event_time_u64,
1108 &descriptor,
1109 ),
1110 create_touch_screen_event(
1111 SortedVecMap::from_iter(vec![
1112 (
1113 fidl_ui_input::PointerEventPhase::Up,
1114 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1115 ),
1116 (
1117 fidl_ui_input::PointerEventPhase::Remove,
1118 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1119 ),
1120 ]),
1121 event_time_u64,
1122 &descriptor,
1123 ),
1124 ];
1125
1126 assert_input_report_sequence_generates_events!(
1127 input_reports: reports,
1128 expected_events: expected_events,
1129 device_descriptor: descriptor,
1130 device_type: TouchBinding,
1131 );
1132 }
1133
1134 #[fuchsia::test]
1136 async fn add_down_move() {
1137 const TOUCH_ID: u32 = 2;
1138 let first = Position { x: 10.0, y: 30.0 };
1139 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1140
1141 let descriptor =
1142 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1143 device_id: 1,
1144 contacts: vec![],
1145 });
1146 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1147
1148 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1149 contact_id: Some(TOUCH_ID),
1150 position_x: Some(first.x as i64),
1151 position_y: Some(first.y as i64),
1152 pressure: None,
1153 contact_width: None,
1154 contact_height: None,
1155 ..Default::default()
1156 };
1157 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1158 contact_id: Some(TOUCH_ID),
1159 position_x: Some(first.x as i64 * 2),
1160 position_y: Some(first.y as i64 * 2),
1161 pressure: None,
1162 contact_width: None,
1163 contact_height: None,
1164 ..Default::default()
1165 };
1166
1167 let reports = vec![
1168 create_touch_input_report(
1169 vec![first_contact],
1170 None,
1171 event_time_i64,
1172 ),
1173 create_touch_input_report(
1174 vec![second_contact],
1175 None,
1176 event_time_i64,
1177 ),
1178 ];
1179
1180 let expected_events = vec![
1181 create_touch_screen_event(
1182 SortedVecMap::from_iter(vec![
1183 (
1184 fidl_ui_input::PointerEventPhase::Add,
1185 vec![create_touch_contact(TOUCH_ID, first)],
1186 ),
1187 (
1188 fidl_ui_input::PointerEventPhase::Down,
1189 vec![create_touch_contact(TOUCH_ID, first)],
1190 ),
1191 ]),
1192 event_time_u64,
1193 &descriptor,
1194 ),
1195 create_touch_screen_event(
1196 SortedVecMap::from_iter(vec![(
1197 fidl_ui_input::PointerEventPhase::Move,
1198 vec![create_touch_contact(TOUCH_ID, second)],
1199 )]),
1200 event_time_u64,
1201 &descriptor,
1202 ),
1203 ];
1204
1205 assert_input_report_sequence_generates_events!(
1206 input_reports: reports,
1207 expected_events: expected_events,
1208 device_descriptor: descriptor,
1209 device_type: TouchBinding,
1210 );
1211 }
1212
1213 #[fuchsia::test]
1214 async fn sent_event_has_trace_id() {
1215 let report_time = zx::MonotonicInstant::get().into_nanos();
1216 let contact = fidl_fuchsia_input_report::ContactInputReport {
1217 contact_id: Some(222),
1218 position_x: Some(333),
1219 position_y: Some(444),
1220 ..Default::default()
1221 };
1222 let report =
1223 create_touch_input_report(vec![contact], None, report_time);
1224
1225 let descriptor =
1226 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1227 device_id: 1,
1228 contacts: vec![],
1229 });
1230 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1231
1232 let inspector = fuchsia_inspect::Inspector::default();
1233 let test_node = inspector.root().create_child("TestDevice_Touch");
1234 let mut inspect_status = InputDeviceStatus::new(test_node);
1235 inspect_status.health_node.set_ok();
1236
1237 let previous_state = input_device::PreviousDeviceState::TouchScreen {
1238 active_contacts: vec![],
1239 pressed_buttons: vec![],
1240 };
1241
1242 let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
1243 let _ = TouchBinding::process_reports(
1244 &reports_wire,
1245 Some(previous_state),
1246 &descriptor,
1247 &mut event_sender,
1248 &inspect_status,
1249 &metrics::MetricsLogger::default(),
1250 &input_device::InputPipelineFeatureFlags::default(),
1251 );
1252 assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1253 }
1254
1255 #[fuchsia::test(allow_stalls = false)]
1256 async fn enables_touchpad_mode_automatically() {
1257 let (set_feature_report_sender, set_feature_report_receiver) =
1258 futures::channel::mpsc::unbounded();
1259 let (input_device_proxy, _task) = spawn_input_stream_handler(move |input_device_request| {
1260 let set_feature_report_sender = set_feature_report_sender.clone();
1261 async move {
1262 match input_device_request {
1263 fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1264 let _ = responder.send(&get_touchpad_device_descriptor(
1265 true, ));
1267 }
1268 fidl_fuchsia_input_report::InputDeviceRequest::GetFeatureReport {
1269 responder,
1270 } => {
1271 let _ = responder.send(Ok(&fidl_fuchsia_input_report::FeatureReport {
1272 touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1273 input_mode: Some(
1274 fidl_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection,
1275 ),
1276 ..Default::default()
1277 }),
1278 ..Default::default()
1279 }));
1280 }
1281 fidl_fuchsia_input_report::InputDeviceRequest::SetFeatureReport {
1282 responder,
1283 report,
1284 } => {
1285 match set_feature_report_sender.unbounded_send(report) {
1286 Ok(_) => {
1287 let _ = responder.send(Ok(()));
1288 }
1289 Err(e) => {
1290 panic!("try_send set_feature_report_request failed: {}", e);
1291 }
1292 };
1293 }
1294 fidl_fuchsia_input_report::InputDeviceRequest::GetInputReportsReader {
1295 ..
1296 }
1297 | fidl_fuchsia_input_report::InputDeviceRequest::GetInputReportsReaderV2 {
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 let (_binding, _task) = 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, _task) = 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 let actual_type = match binding.device_descriptor {
1395 TouchDeviceDescriptor::TouchScreen(_) => TouchDeviceType::TouchScreen,
1396 TouchDeviceDescriptor::Touchpad(_) => TouchDeviceType::WindowsPrecisionTouchpad,
1397 };
1398 pretty_assertions::assert_eq!(actual_type, expect_touch_device_type);
1399 }
1400
1401 fn get_touchpad_device_descriptor(
1404 has_mouse_descriptor: bool,
1405 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1406 fidl_fuchsia_input_report::DeviceDescriptor {
1407 mouse: match has_mouse_descriptor {
1408 true => Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
1409 false => None,
1410 },
1411 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1412 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1413 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1414 position_x: Some(fidl_fuchsia_input::Axis {
1415 range: fidl_fuchsia_input::Range { min: 1, max: 2 },
1416 unit: fidl_fuchsia_input::Unit {
1417 type_: fidl_fuchsia_input::UnitType::None,
1418 exponent: 0,
1419 },
1420 }),
1421 position_y: Some(fidl_fuchsia_input::Axis {
1422 range: fidl_fuchsia_input::Range { min: 2, max: 3 },
1423 unit: fidl_fuchsia_input::Unit {
1424 type_: fidl_fuchsia_input::UnitType::Other,
1425 exponent: 100000,
1426 },
1427 }),
1428 pressure: Some(fidl_fuchsia_input::Axis {
1429 range: fidl_fuchsia_input::Range { min: 3, max: 4 },
1430 unit: fidl_fuchsia_input::Unit {
1431 type_: fidl_fuchsia_input::UnitType::Grams,
1432 exponent: -991,
1433 },
1434 }),
1435 contact_width: Some(fidl_fuchsia_input::Axis {
1436 range: fidl_fuchsia_input::Range { min: 5, max: 6 },
1437 unit: fidl_fuchsia_input::Unit {
1438 type_: fidl_fuchsia_input::UnitType::EnglishAngularVelocity,
1439 exponent: 123,
1440 },
1441 }),
1442 contact_height: Some(fidl_fuchsia_input::Axis {
1443 range: fidl_fuchsia_input::Range { min: 7, max: 8 },
1444 unit: fidl_fuchsia_input::Unit {
1445 type_: fidl_fuchsia_input::UnitType::Pascals,
1446 exponent: 100,
1447 },
1448 }),
1449 ..Default::default()
1450 }]),
1451 ..Default::default()
1452 }),
1453 ..Default::default()
1454 }),
1455 ..Default::default()
1456 }
1457 }
1458
1459 #[test_case(true; "merge touch events enabled")]
1462 #[test_case(false; "merge touch events disabled")]
1463 #[fuchsia::test]
1464 async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1465 let descriptor =
1466 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1467 device_id: 1,
1468 contacts: vec![],
1469 });
1470 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1471
1472 let reports = vec![create_touch_input_report(
1473 vec![],
1474 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1475 event_time_i64,
1476 )];
1477
1478 let expected_events = vec![create_touch_screen_event_with_buttons(
1479 SortedVecMap::new(),
1480 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1481 event_time_u64,
1482 &descriptor,
1483 )];
1484
1485 assert_input_report_sequence_generates_events_with_feature_flags!(
1486 input_reports: reports,
1487 expected_events: expected_events,
1488 device_descriptor: descriptor,
1489 device_type: TouchBinding,
1490 feature_flags: input_device::InputPipelineFeatureFlags {
1491 enable_merge_touch_events,
1492 ..Default::default()
1493 },
1494 );
1495 }
1496
1497 #[test_case(true; "merge touch events enabled")]
1500 #[test_case(false; "merge touch events disabled")]
1501 #[fuchsia::test]
1502 async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1503 const TOUCH_ID: u32 = 2;
1504
1505 let descriptor =
1506 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1507 device_id: 1,
1508 contacts: vec![],
1509 });
1510 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1511
1512 let contact = fidl_fuchsia_input_report::ContactInputReport {
1513 contact_id: Some(TOUCH_ID),
1514 position_x: Some(0),
1515 position_y: Some(0),
1516 pressure: None,
1517 contact_width: None,
1518 contact_height: None,
1519 ..Default::default()
1520 };
1521 let reports = vec![create_touch_input_report(
1522 vec![contact],
1523 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1524 event_time_i64,
1525 )];
1526
1527 let expected_events = vec![create_touch_screen_event_with_buttons(
1528 SortedVecMap::from_iter(vec![
1529 (
1530 fidl_ui_input::PointerEventPhase::Add,
1531 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1532 ),
1533 (
1534 fidl_ui_input::PointerEventPhase::Down,
1535 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1536 ),
1537 ]),
1538 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1539 event_time_u64,
1540 &descriptor,
1541 )];
1542
1543 assert_input_report_sequence_generates_events_with_feature_flags!(
1544 input_reports: reports,
1545 expected_events: expected_events,
1546 device_descriptor: descriptor,
1547 device_type: TouchBinding,
1548 feature_flags: input_device::InputPipelineFeatureFlags {
1549 enable_merge_touch_events,
1550 ..Default::default()
1551 },
1552 );
1553 }
1554
1555 #[test_case(true; "merge touch events enabled")]
1558 #[test_case(false; "merge touch events disabled")]
1559 #[fuchsia::test]
1560 async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1561 const TOUCH_ID: u32 = 2;
1562
1563 let descriptor =
1564 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1565 device_id: 1,
1566 contacts: vec![],
1567 });
1568 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1569
1570 let contact = fidl_fuchsia_input_report::ContactInputReport {
1571 contact_id: Some(TOUCH_ID),
1572 position_x: Some(0),
1573 position_y: Some(0),
1574 pressure: None,
1575 contact_width: None,
1576 contact_height: None,
1577 ..Default::default()
1578 };
1579 let reports = vec![create_touch_input_report(
1580 vec![contact],
1581 Some(vec![
1582 fidl_fuchsia_input_report::TouchButton::Palm,
1583 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1584 ]),
1585 event_time_i64,
1586 )];
1587
1588 let expected_events = vec![create_touch_screen_event_with_buttons(
1589 SortedVecMap::from_iter(vec![
1590 (
1591 fidl_ui_input::PointerEventPhase::Add,
1592 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1593 ),
1594 (
1595 fidl_ui_input::PointerEventPhase::Down,
1596 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1597 ),
1598 ]),
1599 vec![
1600 fidl_fuchsia_input_report::TouchButton::Palm,
1601 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1602 ],
1603 event_time_u64,
1604 &descriptor,
1605 )];
1606
1607 assert_input_report_sequence_generates_events_with_feature_flags!(
1608 input_reports: reports,
1609 expected_events: expected_events,
1610 device_descriptor: descriptor,
1611 device_type: TouchBinding,
1612 feature_flags: input_device::InputPipelineFeatureFlags {
1613 enable_merge_touch_events,
1614 ..Default::default()
1615 },
1616 );
1617 }
1618
1619 #[test_case(true; "merge touch events enabled")]
1621 #[test_case(false; "merge touch events disabled")]
1622 #[fuchsia::test]
1623 async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1624 let descriptor =
1625 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1626 device_id: 1,
1627 contacts: vec![],
1628 });
1629 let (event_time_i64, _) = testing_utilities::event_times();
1630
1631 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1632
1633 let expected_events: Vec<input_device::InputEvent> = vec![];
1634
1635 assert_input_report_sequence_generates_events_with_feature_flags!(
1636 input_reports: reports,
1637 expected_events: expected_events,
1638 device_descriptor: descriptor,
1639 device_type: TouchBinding,
1640 feature_flags: input_device::InputPipelineFeatureFlags {
1641 enable_merge_touch_events,
1642 ..Default::default()
1643 },
1644 );
1645 }
1646
1647 #[test_case(true; "merge touch events enabled")]
1649 #[test_case(false; "merge touch events disabled")]
1650 #[fuchsia::test]
1651 async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1652 const TOUCH_ID: u32 = 2;
1653
1654 let descriptor =
1655 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1656 device_id: 1,
1657 contacts: vec![],
1658 });
1659 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1660
1661 let contact = fidl_fuchsia_input_report::ContactInputReport {
1662 contact_id: Some(TOUCH_ID),
1663 position_x: Some(0),
1664 position_y: Some(0),
1665 pressure: None,
1666 contact_width: None,
1667 contact_height: None,
1668 ..Default::default()
1669 };
1670 let reports = vec![
1671 create_touch_input_report(vec![contact], None, event_time_i64),
1672 create_touch_input_report(
1673 vec![],
1674 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1675 event_time_i64,
1676 ),
1677 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1678 ];
1679
1680 let expected_events = vec![
1681 create_touch_screen_event_with_buttons(
1682 SortedVecMap::from_iter(vec![
1683 (
1684 fidl_ui_input::PointerEventPhase::Add,
1685 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1686 ),
1687 (
1688 fidl_ui_input::PointerEventPhase::Down,
1689 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1690 ),
1691 ]),
1692 vec![],
1693 event_time_u64,
1694 &descriptor,
1695 ),
1696 create_touch_screen_event_with_buttons(
1697 SortedVecMap::new(),
1698 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1699 event_time_u64,
1700 &descriptor,
1701 ),
1702 create_touch_screen_event_with_buttons(
1703 SortedVecMap::new(),
1704 vec![],
1705 event_time_u64,
1706 &descriptor,
1707 ),
1708 ];
1709
1710 assert_input_report_sequence_generates_events_with_feature_flags!(
1711 input_reports: reports,
1712 expected_events: expected_events,
1713 device_descriptor: descriptor,
1714 device_type: TouchBinding,
1715 feature_flags: input_device::InputPipelineFeatureFlags {
1716 enable_merge_touch_events,
1717 ..Default::default()
1718 },
1719 );
1720 }
1721
1722 #[fuchsia::test]
1723 async fn process_reports_batches_events() {
1724 const TOUCH_ID: u32 = 2;
1725
1726 let descriptor =
1727 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1728 device_id: 1,
1729 contacts: vec![],
1730 });
1731 let (event_time_i64, _) = testing_utilities::event_times();
1732
1733 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1734 contact_id: Some(TOUCH_ID),
1735 position_x: Some(0),
1736 position_y: Some(0),
1737 ..Default::default()
1738 };
1739 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1740 contact_id: Some(TOUCH_ID),
1741 position_x: Some(10),
1742 position_y: Some(10),
1743 ..Default::default()
1744 };
1745 let reports = vec![
1746 create_touch_input_report(vec![contact1], None, event_time_i64),
1747 create_touch_input_report(vec![contact2], None, event_time_i64),
1748 ];
1749
1750 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1751
1752 let inspector = fuchsia_inspect::Inspector::default();
1753 let test_node = inspector.root().create_child("TestDevice_Touch");
1754 let mut inspect_status = InputDeviceStatus::new(test_node);
1755 inspect_status.health_node.set_ok();
1756
1757 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1758 let _ = TouchBinding::process_reports(
1759 &reports_wire,
1760 None,
1761 &descriptor,
1762 &mut event_sender,
1763 &inspect_status,
1764 &metrics::MetricsLogger::default(),
1765 &input_device::InputPipelineFeatureFlags::default(),
1766 );
1767
1768 let batch = event_receiver.try_next().expect("Expected a batch of events");
1770 let events = batch.expect("Expected events in the batch");
1771 assert_eq!(events.len(), 2);
1772
1773 assert!(event_receiver.try_next().is_err());
1775 }
1776
1777 #[fuchsia::test]
1778 async fn process_reports_merges_touch_events_when_enabled() {
1779 const TOUCH_ID: u32 = 2;
1780 let descriptor =
1781 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1782 device_id: 1,
1783 contacts: vec![],
1784 });
1785 let (event_time_i64, _) = testing_utilities::event_times();
1786
1787 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1788 contact_id: Some(TOUCH_ID),
1789 position_x: Some(0),
1790 position_y: Some(0),
1791 ..Default::default()
1792 };
1793 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1794 contact_id: Some(TOUCH_ID),
1795 position_x: Some(10),
1796 position_y: Some(10),
1797 ..Default::default()
1798 };
1799 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1800 contact_id: Some(TOUCH_ID),
1801 position_x: Some(20),
1802 position_y: Some(20),
1803 ..Default::default()
1804 };
1805 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1806 contact_id: Some(TOUCH_ID),
1807 position_x: Some(30),
1808 position_y: Some(30),
1809 ..Default::default()
1810 };
1811 let reports = vec![
1812 create_touch_input_report(vec![contact_add], None, event_time_i64),
1813 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1814 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1815 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1816 create_touch_input_report(vec![], None, event_time_i64),
1817 ];
1818
1819 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1820 let inspector = fuchsia_inspect::Inspector::default();
1821 let mut inspect_status =
1822 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1823 inspect_status.health_node.set_ok();
1824
1825 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1826 let _ = TouchBinding::process_reports(
1827 &reports_wire,
1828 None,
1829 &descriptor,
1830 &mut event_sender,
1831 &inspect_status,
1832 &metrics::MetricsLogger::default(),
1833 &input_device::InputPipelineFeatureFlags {
1834 enable_merge_touch_events: true,
1835 ..Default::default()
1836 },
1837 );
1838
1839 let batch = event_receiver.try_next().unwrap().unwrap();
1840
1841 assert_eq!(batch.len(), 3);
1843
1844 assert_matches!(
1846 &batch[0].device_event,
1847 input_device::InputDeviceEvent::TouchScreen(event)
1848 if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
1849 );
1850 assert_matches!(
1852 &batch[1].device_event,
1853 input_device::InputDeviceEvent::TouchScreen(event)
1854 if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
1855 );
1856 assert_matches!(
1858 &batch[2].device_event,
1859 input_device::InputDeviceEvent::TouchScreen(event)
1860 if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
1861 );
1862 }
1863
1864 #[fuchsia::test]
1865 async fn process_reports_does_not_merge_touch_events_when_disabled() {
1866 const TOUCH_ID: u32 = 2;
1867 let descriptor =
1868 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1869 device_id: 1,
1870 contacts: vec![],
1871 });
1872 let (event_time_i64, _) = testing_utilities::event_times();
1873
1874 let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1875 contact_id: Some(TOUCH_ID),
1876 position_x: Some(0),
1877 position_y: Some(0),
1878 ..Default::default()
1879 };
1880 let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1881 contact_id: Some(TOUCH_ID),
1882 position_x: Some(10),
1883 position_y: Some(10),
1884 ..Default::default()
1885 };
1886 let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1887 contact_id: Some(TOUCH_ID),
1888 position_x: Some(20),
1889 position_y: Some(20),
1890 ..Default::default()
1891 };
1892 let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1893 contact_id: Some(TOUCH_ID),
1894 position_x: Some(30),
1895 position_y: Some(30),
1896 ..Default::default()
1897 };
1898 let reports = vec![
1899 create_touch_input_report(vec![contact_add], None, event_time_i64),
1900 create_touch_input_report(vec![contact_move1], None, event_time_i64),
1901 create_touch_input_report(vec![contact_move2], None, event_time_i64),
1902 create_touch_input_report(vec![contact_move3], None, event_time_i64),
1903 create_touch_input_report(vec![], None, event_time_i64),
1904 ];
1905
1906 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1907 let inspector = fuchsia_inspect::Inspector::default();
1908 let mut inspect_status =
1909 InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1910 inspect_status.health_node.set_ok();
1911
1912 let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1913 let _ = TouchBinding::process_reports(
1914 &reports_wire,
1915 None,
1916 &descriptor,
1917 &mut event_sender,
1918 &inspect_status,
1919 &metrics::MetricsLogger::default(),
1920 &input_device::InputPipelineFeatureFlags {
1921 enable_merge_touch_events: false,
1922 ..Default::default()
1923 },
1924 );
1925
1926 let batch = event_receiver.try_next().unwrap().unwrap();
1927
1928 assert_eq!(batch.len(), 5);
1930 }
1931}