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