1use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::utils::{Position, Size};
7use crate::{metrics, mouse_binding};
8use anyhow::{Context, Error, format_err};
9use async_trait::async_trait;
10use fidl_fuchsia_input_report::{InputDeviceProxy, InputReport};
11use fuchsia_inspect::ArrayProperty;
12use fuchsia_inspect::health::Reporter;
13use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
14use zx;
15
16use fidl::HandleBased;
17use maplit::hashmap;
18use metrics_registry::*;
19use std::collections::{HashMap, HashSet};
20use {
21 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
22 fidl_fuchsia_ui_pointerinjector as pointerinjector,
23};
24
25#[derive(Debug, PartialEq)]
41pub struct TouchScreenEvent {
42 pub contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
48
49 pub injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
54
55 pub pressed_buttons: Vec<fidl_input_report::TouchButton>,
57
58 pub wake_lease: Option<zx::EventPair>,
60}
61
62impl Clone for TouchScreenEvent {
63 fn clone(&self) -> Self {
64 log::debug!("TouchScreenEvent cloned without wake lease.");
65 Self {
66 contacts: self.contacts.clone(),
67 injector_contacts: self.injector_contacts.clone(),
68 pressed_buttons: self.pressed_buttons.clone(),
69 wake_lease: None,
70 }
71 }
72}
73
74impl Drop for TouchScreenEvent {
75 fn drop(&mut self) {
76 log::debug!("TouchScreenEvent dropped, had_wake_lease: {:?}", self.wake_lease);
77 }
78}
79
80impl TouchScreenEvent {
81 pub fn clone_with_wake_lease(&self) -> Self {
82 log::debug!("TouchScreenEvent cloned with wake lease: {:?}", self.wake_lease);
83 Self {
84 contacts: self.contacts.clone(),
85 injector_contacts: self.injector_contacts.clone(),
86 pressed_buttons: self.pressed_buttons.clone(),
87 wake_lease: self.wake_lease.as_ref().map(|lease| {
88 lease
89 .duplicate_handle(zx::Rights::SAME_RIGHTS)
90 .expect("failed to duplicate event pair")
91 }),
92 }
93 }
94
95 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
96 let contacts_clone = self.injector_contacts.clone();
97 node.record_child("injector_contacts", move |contacts_node| {
98 for (phase, contacts) in contacts_clone.iter() {
99 let phase_str = match phase {
100 pointerinjector::EventPhase::Add => "add",
101 pointerinjector::EventPhase::Change => "change",
102 pointerinjector::EventPhase::Remove => "remove",
103 pointerinjector::EventPhase::Cancel => "cancel",
104 };
105 contacts_node.record_child(phase_str, move |phase_node| {
106 for contact in contacts.iter() {
107 phase_node.record_child(contact.id.to_string(), move |contact_node| {
108 contact_node
109 .record_double("position_x_mm", f64::from(contact.position.x));
110 contact_node
111 .record_double("position_y_mm", f64::from(contact.position.y));
112 if let Some(pressure) = contact.pressure {
113 contact_node.record_int("pressure", pressure);
114 }
115 if let Some(contact_size) = contact.contact_size {
116 contact_node.record_double(
117 "contact_width_mm",
118 f64::from(contact_size.width),
119 );
120 contact_node.record_double(
121 "contact_height_mm",
122 f64::from(contact_size.height),
123 );
124 }
125 });
126 }
127 });
128 }
129 });
130
131 let pressed_buttons_node =
132 node.create_string_array("pressed_buttons", self.pressed_buttons.len());
133 self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
134 let button_name: String = match button {
135 fidl_input_report::TouchButton::Palm => "palm".into(),
136 unknown_value => {
137 format!("unknown({:?})", unknown_value)
138 }
139 };
140 pressed_buttons_node.set(i, &button_name);
141 });
142 node.record(pressed_buttons_node);
143 }
144}
145
146#[derive(Clone, Debug, PartialEq)]
151pub struct TouchpadEvent {
152 pub injector_contacts: Vec<TouchContact>,
155
156 pub pressed_buttons: HashSet<mouse_binding::MouseButton>,
158}
159
160impl TouchpadEvent {
161 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
162 let pressed_buttons_node =
163 node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
164 self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
165 pressed_buttons_node.set(i, *button);
166 });
167 node.record(pressed_buttons_node);
168
169 let contacts_clone = self.injector_contacts.clone();
171 node.record_child("injector_contacts", move |contacts_node| {
172 for contact in contacts_clone.iter() {
173 contacts_node.record_child(contact.id.to_string(), move |contact_node| {
174 contact_node.record_double("position_x_mm", f64::from(contact.position.x));
175 contact_node.record_double("position_y_mm", f64::from(contact.position.y));
176 if let Some(pressure) = contact.pressure {
177 contact_node.record_int("pressure", pressure);
178 }
179 if let Some(contact_size) = contact.contact_size {
180 contact_node
181 .record_double("contact_width_mm", f64::from(contact_size.width));
182 contact_node
183 .record_double("contact_height_mm", f64::from(contact_size.height));
184 }
185 })
186 }
187 });
188 }
189}
190
191#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub enum TouchDeviceType {
195 TouchScreen,
196 WindowsPrecisionTouchpad,
197}
198
199#[derive(Clone, Copy, Debug, PartialEq)]
202pub struct TouchContact {
203 pub id: u32,
205
206 pub position: Position,
209
210 pub pressure: Option<i64>,
213
214 pub contact_size: Option<Size>,
217}
218
219impl Eq for TouchContact {}
220
221impl From<&fidl_fuchsia_input_report::ContactInputReport> for TouchContact {
222 fn from(fidl_contact: &fidl_fuchsia_input_report::ContactInputReport) -> TouchContact {
223 let contact_size =
224 if fidl_contact.contact_width.is_some() && fidl_contact.contact_height.is_some() {
225 Some(Size {
226 width: fidl_contact.contact_width.unwrap() as f32,
227 height: fidl_contact.contact_height.unwrap() as f32,
228 })
229 } else {
230 None
231 };
232
233 TouchContact {
234 id: fidl_contact.contact_id.unwrap_or_default(),
235 position: Position {
236 x: fidl_contact.position_x.unwrap_or_default() as f32,
237 y: fidl_contact.position_y.unwrap_or_default() as f32,
238 },
239 pressure: fidl_contact.pressure,
240 contact_size,
241 }
242 }
243}
244
245#[derive(Clone, Debug, Eq, PartialEq)]
246pub struct TouchScreenDeviceDescriptor {
247 pub device_id: u32,
249
250 pub contacts: Vec<ContactDeviceDescriptor>,
252}
253
254#[derive(Clone, Debug, Eq, PartialEq)]
255pub struct TouchpadDeviceDescriptor {
256 pub device_id: u32,
258
259 pub contacts: Vec<ContactDeviceDescriptor>,
261}
262
263#[derive(Clone, Debug, Eq, PartialEq)]
264enum TouchDeviceDescriptor {
265 TouchScreen(TouchScreenDeviceDescriptor),
266 Touchpad(TouchpadDeviceDescriptor),
267}
268
269#[derive(Clone, Debug, Eq, PartialEq)]
283pub struct ContactDeviceDescriptor {
284 pub x_range: fidl_input_report::Range,
286
287 pub y_range: fidl_input_report::Range,
289
290 pub x_unit: fidl_input_report::Unit,
292
293 pub y_unit: fidl_input_report::Unit,
295
296 pub pressure_range: Option<fidl_input_report::Range>,
298
299 pub width_range: Option<fidl_input_report::Range>,
301
302 pub height_range: Option<fidl_input_report::Range>,
304}
305
306pub struct TouchBinding {
313 event_sender: UnboundedSender<InputEvent>,
315
316 device_descriptor: TouchDeviceDescriptor,
318
319 touch_device_type: TouchDeviceType,
321
322 device_proxy: InputDeviceProxy,
324}
325
326#[async_trait]
327impl input_device::InputDeviceBinding for TouchBinding {
328 fn input_event_sender(&self) -> UnboundedSender<InputEvent> {
329 self.event_sender.clone()
330 }
331
332 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
333 match self.device_descriptor.clone() {
334 TouchDeviceDescriptor::TouchScreen(desc) => {
335 input_device::InputDeviceDescriptor::TouchScreen(desc)
336 }
337 TouchDeviceDescriptor::Touchpad(desc) => {
338 input_device::InputDeviceDescriptor::Touchpad(desc)
339 }
340 }
341 }
342}
343
344impl TouchBinding {
345 pub async fn new(
360 device_proxy: InputDeviceProxy,
361 device_id: u32,
362 input_event_sender: UnboundedSender<input_device::InputEvent>,
363 device_node: fuchsia_inspect::Node,
364 metrics_logger: metrics::MetricsLogger,
365 ) -> Result<Self, Error> {
366 let (device_binding, mut inspect_status) =
367 Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
368 .await?;
369 device_binding
370 .set_touchpad_mode(true)
371 .await
372 .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
373 inspect_status.health_node.set_ok();
374 input_device::initialize_report_stream(
375 device_proxy,
376 device_binding.get_device_descriptor(),
377 device_binding.input_event_sender(),
378 inspect_status,
379 metrics_logger,
380 Self::process_reports,
381 );
382
383 Ok(device_binding)
384 }
385
386 async fn bind_device(
398 device_proxy: InputDeviceProxy,
399 device_id: u32,
400 input_event_sender: UnboundedSender<input_device::InputEvent>,
401 device_node: fuchsia_inspect::Node,
402 ) -> Result<(Self, InputDeviceStatus), Error> {
403 let mut input_device_status = InputDeviceStatus::new(device_node);
404 let device_descriptor: fidl_input_report::DeviceDescriptor = match device_proxy
405 .get_descriptor()
406 .await
407 {
408 Ok(descriptor) => descriptor,
409 Err(_) => {
410 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
411 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
412 }
413 };
414
415 let touch_device_type = get_device_type(&device_proxy).await;
416
417 match device_descriptor.touch {
418 Some(fidl_fuchsia_input_report::TouchDescriptor {
419 input:
420 Some(fidl_fuchsia_input_report::TouchInputDescriptor {
421 contacts: Some(contact_descriptors),
422 max_contacts: _,
423 touch_type: _,
424 buttons: _,
425 ..
426 }),
427 ..
428 }) => Ok((
429 TouchBinding {
430 event_sender: input_event_sender,
431 device_descriptor: match touch_device_type {
432 TouchDeviceType::TouchScreen => {
433 TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
434 device_id,
435 contacts: contact_descriptors
436 .iter()
437 .map(TouchBinding::parse_contact_descriptor)
438 .filter_map(Result::ok)
439 .collect(),
440 })
441 }
442 TouchDeviceType::WindowsPrecisionTouchpad => {
443 TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
444 device_id,
445 contacts: contact_descriptors
446 .iter()
447 .map(TouchBinding::parse_contact_descriptor)
448 .filter_map(Result::ok)
449 .collect(),
450 })
451 }
452 },
453 touch_device_type,
454 device_proxy,
455 },
456 input_device_status,
457 )),
458 descriptor => {
459 input_device_status
460 .health_node
461 .set_unhealthy("Touch Device Descriptor failed to parse.");
462 Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
463 }
464 }
465 }
466
467 async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
468 match self.touch_device_type {
469 TouchDeviceType::TouchScreen => Ok(()),
470 TouchDeviceType::WindowsPrecisionTouchpad => {
471 let mut report = match self.device_proxy.get_feature_report().await? {
474 Ok(report) => report,
475 Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
476 };
477 let mut touch =
478 report.touch.unwrap_or_else(fidl_input_report::TouchFeatureReport::default);
479 touch.input_mode = match enable {
480 true => Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
481 false => Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection),
482 };
483 report.touch = Some(touch);
484 match self.device_proxy.set_feature_report(&report).await? {
485 Ok(()) => {
486 log::info!("touchpad: set touchpad_enabled to {}", enable);
488 Ok(())
489 }
490 Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
491 }
492 }
493 }
494 }
495
496 fn process_reports(
516 reports: Vec<InputReport>,
517 mut previous_report: Option<InputReport>,
518 device_descriptor: &input_device::InputDeviceDescriptor,
519 input_event_sender: &mut UnboundedSender<InputEvent>,
520 inspect_status: &InputDeviceStatus,
521 metrics_logger: &metrics::MetricsLogger,
522 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
523 fuchsia_trace::duration!("input", "touch-binding-process-report", "num_reports" => reports.len());
524 for report in reports {
525 previous_report = Self::process_report(
526 report,
527 previous_report,
528 device_descriptor,
529 input_event_sender,
530 inspect_status,
531 metrics_logger,
532 );
533 }
534 (previous_report, None)
535 }
536
537 fn process_report(
538 report: InputReport,
539 previous_report: Option<InputReport>,
540 device_descriptor: &input_device::InputDeviceDescriptor,
541 input_event_sender: &mut UnboundedSender<InputEvent>,
542 inspect_status: &InputDeviceStatus,
543 metrics_logger: &metrics::MetricsLogger,
544 ) -> Option<InputReport> {
545 inspect_status.count_received_report(&report);
546 match device_descriptor {
547 input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
548 report,
549 previous_report,
550 device_descriptor,
551 input_event_sender,
552 inspect_status,
553 metrics_logger,
554 ),
555 input_device::InputDeviceDescriptor::Touchpad(_) => process_touchpad_reports(
556 report,
557 device_descriptor,
558 input_event_sender,
559 inspect_status,
560 metrics_logger,
561 ),
562 _ => previous_report,
563 }
564 }
565
566 fn parse_contact_descriptor(
574 contact_device_descriptor: &fidl_input_report::ContactInputDescriptor,
575 ) -> Result<ContactDeviceDescriptor, Error> {
576 match contact_device_descriptor {
577 fidl_input_report::ContactInputDescriptor {
578 position_x: Some(x_axis),
579 position_y: Some(y_axis),
580 pressure: pressure_axis,
581 contact_width: width_axis,
582 contact_height: height_axis,
583 ..
584 } => Ok(ContactDeviceDescriptor {
585 x_range: x_axis.range,
586 y_range: y_axis.range,
587 x_unit: x_axis.unit,
588 y_unit: y_axis.unit,
589 pressure_range: pressure_axis.map(|axis| axis.range),
590 width_range: width_axis.map(|axis| axis.range),
591 height_range: height_axis.map(|axis| axis.range),
592 }),
593 descriptor => {
594 Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
595 }
596 }
597 }
598}
599
600fn process_touch_screen_reports(
601 mut report: InputReport,
602 previous_report: Option<InputReport>,
603 device_descriptor: &input_device::InputDeviceDescriptor,
604 input_event_sender: &mut UnboundedSender<InputEvent>,
605 inspect_status: &InputDeviceStatus,
606 metrics_logger: &metrics::MetricsLogger,
607) -> Option<InputReport> {
608 fuchsia_trace::flow_end!("input", "input_report", report.trace_id.unwrap_or(0).into());
609
610 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
612 Some(touch) => touch,
613 None => {
614 inspect_status.count_filtered_report();
615 return previous_report;
616 }
617 };
618
619 let (previous_contacts, previous_buttons): (
620 HashMap<u32, TouchContact>,
621 Vec<fidl_fuchsia_input_report::TouchButton>,
622 ) = previous_report
623 .as_ref()
624 .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
625 .map(touch_contacts_and_buttons_from_touch_report)
626 .unwrap_or_default();
627 let (current_contacts, current_buttons): (
628 HashMap<u32, TouchContact>,
629 Vec<fidl_fuchsia_input_report::TouchButton>,
630 ) = touch_contacts_and_buttons_from_touch_report(touch_report);
631
632 if previous_contacts.is_empty()
634 && current_contacts.is_empty()
635 && previous_buttons.is_empty()
636 && current_buttons.is_empty()
637 {
638 inspect_status.count_filtered_report();
639 return Some(report);
640 }
641
642 if current_contacts.is_empty()
645 && !previous_contacts.is_empty()
646 && (!current_buttons.is_empty() || !previous_buttons.is_empty())
647 {
648 if let Some(touch_report) = report.touch.as_mut() {
649 touch_report.contacts =
650 previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
651 }
652 }
653
654 let added_contacts: Vec<TouchContact> = Vec::from_iter(
656 current_contacts
657 .values()
658 .cloned()
659 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
660 );
661 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
663 current_contacts
664 .values()
665 .cloned()
666 .filter(|contact| previous_contacts.contains_key(&contact.id)),
667 );
668 let removed_contacts: Vec<TouchContact> =
670 Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
671 current_buttons.is_empty()
672 && previous_buttons.is_empty()
673 && !current_contacts.contains_key(&contact.id)
674 }));
675
676 let trace_id = fuchsia_trace::Id::random();
677 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
678 send_touch_screen_event(
679 hashmap! {
680 fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
681 fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
682 fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
683 fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
684 fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
685 },
686 hashmap! {
687 pointerinjector::EventPhase::Add => added_contacts,
688 pointerinjector::EventPhase::Change => moved_contacts,
689 pointerinjector::EventPhase::Remove => removed_contacts,
690 },
691 current_buttons,
692 device_descriptor,
693 input_event_sender,
694 trace_id,
695 inspect_status,
696 metrics_logger,
697 report.wake_lease.take(),
698 );
699
700 Some(report)
701}
702
703fn process_touchpad_reports(
704 report: InputReport,
705 device_descriptor: &input_device::InputDeviceDescriptor,
706 input_event_sender: &mut UnboundedSender<InputEvent>,
707 inspect_status: &InputDeviceStatus,
708 metrics_logger: &metrics::MetricsLogger,
709) -> Option<InputReport> {
710 if let Some(trace_id) = report.trace_id {
711 fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
712 }
713
714 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
716 Some(touch) => touch,
717 None => {
718 inspect_status.count_filtered_report();
719 return None;
720 }
721 };
722
723 let current_contacts: Vec<TouchContact> = touch_report
724 .contacts
725 .as_ref()
726 .and_then(|unwrapped_contacts| {
727 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
729 })
730 .unwrap_or_default();
731
732 let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
733 Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
734 fidl_fuchsia_input_report::TouchButton::Palm => Some(1),
735 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal } => {
736 log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
737 None
738 }
739 })),
740 None => HashSet::new(),
741 };
742
743 let trace_id = fuchsia_trace::Id::random();
744 fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
745 send_touchpad_event(
746 current_contacts,
747 buttons,
748 device_descriptor,
749 input_event_sender,
750 trace_id,
751 inspect_status,
752 metrics_logger,
753 );
754
755 Some(report)
756}
757
758fn touch_contacts_and_buttons_from_touch_report(
759 touch_report: &fidl_fuchsia_input_report::TouchInputReport,
760) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
761 let contacts: Vec<TouchContact> = touch_report
763 .contacts
764 .as_ref()
765 .and_then(|unwrapped_contacts| {
766 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
768 })
769 .unwrap_or_default();
770
771 (
772 contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
773 touch_report.pressed_buttons.clone().unwrap_or_default(),
774 )
775}
776
777fn send_touch_screen_event(
787 contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
788 injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
789 pressed_buttons: Vec<fidl_input_report::TouchButton>,
790 device_descriptor: &input_device::InputDeviceDescriptor,
791 input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
792 trace_id: fuchsia_trace::Id,
793 inspect_status: &InputDeviceStatus,
794 metrics_logger: &metrics::MetricsLogger,
795 wake_lease: Option<zx::EventPair>,
796) {
797 let event = input_device::InputEvent {
798 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
799 contacts,
800 injector_contacts,
801 pressed_buttons,
802 wake_lease,
803 }),
804 device_descriptor: device_descriptor.clone(),
805 event_time: zx::MonotonicInstant::get(),
806 handled: Handled::No,
807 trace_id: Some(trace_id),
808 };
809
810 match input_event_sender.unbounded_send(event.clone_with_wake_lease()) {
811 Err(e) => {
812 metrics_logger.log_error(
813 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
814 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
815 );
816 }
817 _ => inspect_status.count_generated_event(event),
818 }
819}
820
821fn send_touchpad_event(
829 injector_contacts: Vec<TouchContact>,
830 pressed_buttons: HashSet<mouse_binding::MouseButton>,
831 device_descriptor: &input_device::InputDeviceDescriptor,
832 input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
833 trace_id: fuchsia_trace::Id,
834 inspect_status: &InputDeviceStatus,
835 metrics_logger: &metrics::MetricsLogger,
836) {
837 let event = input_device::InputEvent {
838 device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
839 injector_contacts,
840 pressed_buttons,
841 }),
842 device_descriptor: device_descriptor.clone(),
843 event_time: zx::MonotonicInstant::get(),
844 handled: Handled::No,
845 trace_id: Some(trace_id),
846 };
847
848 match input_event_sender.unbounded_send(event.clone()) {
849 Err(e) => {
850 metrics_logger.log_error(
851 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
852 std::format!("Failed to send TouchpadEvent with error: {:?}", e),
853 );
854 }
855 _ => inspect_status.count_generated_event(event),
856 }
857}
858
859async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
865 match input_device.get_feature_report().await {
866 Ok(Ok(fidl_input_report::FeatureReport {
867 touch:
868 Some(fidl_input_report::TouchFeatureReport {
869 input_mode:
870 Some(
871 fidl_input_report::TouchConfigurationInputMode::MouseCollection
872 | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
873 ),
874 ..
875 }),
876 ..
877 })) => TouchDeviceType::WindowsPrecisionTouchpad,
878 _ => TouchDeviceType::TouchScreen,
879 }
880}
881
882#[cfg(test)]
883mod tests {
884 use super::*;
885 use crate::testing_utilities::{
886 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
887 create_touch_screen_event_with_buttons, create_touchpad_event,
888 };
889 use crate::utils::Position;
890 use assert_matches::assert_matches;
891 use diagnostics_assertions::AnyProperty;
892 use fidl_test_util::spawn_stream_handler;
893 use fuchsia_async as fasync;
894 use futures::StreamExt;
895 use pretty_assertions::assert_eq;
896 use test_case::test_case;
897
898 #[fasync::run_singlethreaded(test)]
899 async fn process_empty_reports() {
900 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
901 let previous_report = create_touch_input_report(
902 vec![],
903 None,
904 previous_report_time,
905 );
906 let report_time = zx::MonotonicInstant::get().into_nanos();
907 let report =
908 create_touch_input_report(vec![], None, report_time);
909
910 let descriptor =
911 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
912 device_id: 1,
913 contacts: vec![],
914 });
915 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
916
917 let inspector = fuchsia_inspect::Inspector::default();
918 let test_node = inspector.root().create_child("TestDevice_Touch");
919 let mut inspect_status = InputDeviceStatus::new(test_node);
920 inspect_status.health_node.set_ok();
921
922 let (returned_report, _) = TouchBinding::process_reports(
923 vec![report],
924 Some(previous_report),
925 &descriptor,
926 &mut event_sender,
927 &inspect_status,
928 &metrics::MetricsLogger::default(),
929 );
930 assert!(returned_report.is_some());
931 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
932
933 let event = event_receiver.try_next();
935 assert!(event.is_err());
936
937 diagnostics_assertions::assert_data_tree!(inspector, root: {
938 "TestDevice_Touch": contains {
939 reports_received_count: 1u64,
940 reports_filtered_count: 1u64,
941 events_generated: 0u64,
942 last_received_timestamp_ns: report_time as u64,
943 last_generated_timestamp_ns: 0u64,
944 "fuchsia.inspect.Health": {
945 status: "OK",
946 start_timestamp_nanos: AnyProperty
949 },
950 }
951 });
952 }
953
954 #[fasync::run_singlethreaded(test)]
956 async fn add_and_down() {
957 const TOUCH_ID: u32 = 2;
958
959 let descriptor =
960 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
961 device_id: 1,
962 contacts: vec![],
963 });
964 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
965
966 let contact = fidl_fuchsia_input_report::ContactInputReport {
967 contact_id: Some(TOUCH_ID),
968 position_x: Some(0),
969 position_y: Some(0),
970 pressure: None,
971 contact_width: None,
972 contact_height: None,
973 ..Default::default()
974 };
975 let reports = vec![create_touch_input_report(
976 vec![contact],
977 None,
978 event_time_i64,
979 )];
980
981 let expected_events = vec![create_touch_screen_event(
982 hashmap! {
983 fidl_ui_input::PointerEventPhase::Add
984 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
985 fidl_ui_input::PointerEventPhase::Down
986 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
987 },
988 event_time_u64,
989 &descriptor,
990 )];
991
992 assert_input_report_sequence_generates_events!(
993 input_reports: reports,
994 expected_events: expected_events,
995 device_descriptor: descriptor,
996 device_type: TouchBinding,
997 );
998 }
999
1000 #[fasync::run_singlethreaded(test)]
1002 async fn up_and_remove() {
1003 const TOUCH_ID: u32 = 2;
1004
1005 let descriptor =
1006 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1007 device_id: 1,
1008 contacts: vec![],
1009 });
1010 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1011
1012 let contact = fidl_fuchsia_input_report::ContactInputReport {
1013 contact_id: Some(TOUCH_ID),
1014 position_x: Some(0),
1015 position_y: Some(0),
1016 pressure: None,
1017 contact_width: None,
1018 contact_height: None,
1019 ..Default::default()
1020 };
1021 let reports = vec![
1022 create_touch_input_report(
1023 vec![contact],
1024 None,
1025 event_time_i64,
1026 ),
1027 create_touch_input_report(vec![], None, event_time_i64),
1028 ];
1029
1030 let expected_events = vec![
1031 create_touch_screen_event(
1032 hashmap! {
1033 fidl_ui_input::PointerEventPhase::Add
1034 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1035 fidl_ui_input::PointerEventPhase::Down
1036 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1037 },
1038 event_time_u64,
1039 &descriptor,
1040 ),
1041 create_touch_screen_event(
1042 hashmap! {
1043 fidl_ui_input::PointerEventPhase::Up
1044 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1045 fidl_ui_input::PointerEventPhase::Remove
1046 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1047 },
1048 event_time_u64,
1049 &descriptor,
1050 ),
1051 ];
1052
1053 assert_input_report_sequence_generates_events!(
1054 input_reports: reports,
1055 expected_events: expected_events,
1056 device_descriptor: descriptor,
1057 device_type: TouchBinding,
1058 );
1059 }
1060
1061 #[fasync::run_singlethreaded(test)]
1063 async fn add_down_move() {
1064 const TOUCH_ID: u32 = 2;
1065 let first = Position { x: 10.0, y: 30.0 };
1066 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1067
1068 let descriptor =
1069 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1070 device_id: 1,
1071 contacts: vec![],
1072 });
1073 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1074
1075 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1076 contact_id: Some(TOUCH_ID),
1077 position_x: Some(first.x as i64),
1078 position_y: Some(first.y as i64),
1079 pressure: None,
1080 contact_width: None,
1081 contact_height: None,
1082 ..Default::default()
1083 };
1084 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1085 contact_id: Some(TOUCH_ID),
1086 position_x: Some(first.x as i64 * 2),
1087 position_y: Some(first.y as i64 * 2),
1088 pressure: None,
1089 contact_width: None,
1090 contact_height: None,
1091 ..Default::default()
1092 };
1093
1094 let reports = vec![
1095 create_touch_input_report(
1096 vec![first_contact],
1097 None,
1098 event_time_i64,
1099 ),
1100 create_touch_input_report(
1101 vec![second_contact],
1102 None,
1103 event_time_i64,
1104 ),
1105 ];
1106
1107 let expected_events = vec![
1108 create_touch_screen_event(
1109 hashmap! {
1110 fidl_ui_input::PointerEventPhase::Add
1111 => vec![create_touch_contact(TOUCH_ID, first)],
1112 fidl_ui_input::PointerEventPhase::Down
1113 => vec![create_touch_contact(TOUCH_ID, first)],
1114 },
1115 event_time_u64,
1116 &descriptor,
1117 ),
1118 create_touch_screen_event(
1119 hashmap! {
1120 fidl_ui_input::PointerEventPhase::Move
1121 => vec![create_touch_contact(TOUCH_ID, second)],
1122 },
1123 event_time_u64,
1124 &descriptor,
1125 ),
1126 ];
1127
1128 assert_input_report_sequence_generates_events!(
1129 input_reports: reports,
1130 expected_events: expected_events,
1131 device_descriptor: descriptor,
1132 device_type: TouchBinding,
1133 );
1134 }
1135
1136 #[fasync::run_singlethreaded(test)]
1137 async fn sent_event_has_trace_id() {
1138 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1139 let previous_report = create_touch_input_report(
1140 vec![],
1141 None,
1142 previous_report_time,
1143 );
1144
1145 let report_time = zx::MonotonicInstant::get().into_nanos();
1146 let contact = fidl_fuchsia_input_report::ContactInputReport {
1147 contact_id: Some(222),
1148 position_x: Some(333),
1149 position_y: Some(444),
1150 ..Default::default()
1151 };
1152 let report =
1153 create_touch_input_report(vec![contact], None, report_time);
1154
1155 let descriptor =
1156 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1157 device_id: 1,
1158 contacts: vec![],
1159 });
1160 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1161
1162 let inspector = fuchsia_inspect::Inspector::default();
1163 let test_node = inspector.root().create_child("TestDevice_Touch");
1164 let mut inspect_status = InputDeviceStatus::new(test_node);
1165 inspect_status.health_node.set_ok();
1166
1167 let _ = TouchBinding::process_reports(
1168 vec![report],
1169 Some(previous_report),
1170 &descriptor,
1171 &mut event_sender,
1172 &inspect_status,
1173 &metrics::MetricsLogger::default(),
1174 );
1175 assert_matches!(event_receiver.try_next(), Ok(Some(InputEvent { trace_id: Some(_), .. })));
1176 }
1177
1178 #[fuchsia::test(allow_stalls = false)]
1179 async fn enables_touchpad_mode_automatically() {
1180 let (set_feature_report_sender, set_feature_report_receiver) =
1181 futures::channel::mpsc::unbounded();
1182 let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1183 let set_feature_report_sender = set_feature_report_sender.clone();
1184 async move {
1185 match input_device_request {
1186 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1187 let _ = responder.send(&get_touchpad_device_descriptor(
1188 true, ));
1190 }
1191 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1192 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1193 touch: Some(fidl_input_report::TouchFeatureReport {
1194 input_mode: Some(
1195 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1196 ),
1197 ..Default::default()
1198 }),
1199 ..Default::default()
1200 }));
1201 }
1202 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1203 responder,
1204 report,
1205 } => {
1206 match set_feature_report_sender.unbounded_send(report) {
1207 Ok(_) => {
1208 let _ = responder.send(Ok(()));
1209 }
1210 Err(e) => {
1211 panic!("try_send set_feature_report_request failed: {}", e);
1212 }
1213 };
1214 }
1215 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1216 }
1218 r => panic!("unsupported request {:?}", r),
1219 }
1220 }
1221 });
1222
1223 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1224
1225 let inspector = fuchsia_inspect::Inspector::default();
1227 let test_node = inspector.root().create_child("test_node");
1228
1229 TouchBinding::new(
1233 input_device_proxy,
1234 0,
1235 device_event_sender,
1236 test_node,
1237 metrics::MetricsLogger::default(),
1238 )
1239 .await
1240 .unwrap();
1241 assert_matches!(
1242 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1243 [fidl_input_report::FeatureReport {
1244 touch: Some(fidl_input_report::TouchFeatureReport {
1245 input_mode: Some(
1246 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1247 ),
1248 ..
1249 }),
1250 ..
1251 }]
1252 );
1253 }
1254
1255 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1256 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1257 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1258 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1259 #[fuchsia::test(allow_stalls = false)]
1260 async fn identifies_correct_touch_device_type(
1261 has_mouse_descriptor: bool,
1262 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1263 expect_touch_device_type: TouchDeviceType,
1264 ) {
1265 let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1266 match input_device_request {
1267 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1268 let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1269 }
1270 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1271 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1272 touch: Some(fidl_input_report::TouchFeatureReport {
1273 input_mode: touch_input_mode,
1274 ..Default::default()
1275 }),
1276 ..Default::default()
1277 }));
1278 }
1279 fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1280 let _ = responder.send(Ok(()));
1281 }
1282 r => panic!("unsupported request {:?}", r),
1283 }
1284 });
1285
1286 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1287
1288 let inspector = fuchsia_inspect::Inspector::default();
1290 let test_node = inspector.root().create_child("test_node");
1291
1292 let binding = TouchBinding::new(
1293 input_device_proxy,
1294 0,
1295 device_event_sender,
1296 test_node,
1297 metrics::MetricsLogger::default(),
1298 )
1299 .await
1300 .unwrap();
1301 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1302 }
1303
1304 fn get_touchpad_device_descriptor(
1307 has_mouse_descriptor: bool,
1308 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1309 fidl_input_report::DeviceDescriptor {
1310 mouse: match has_mouse_descriptor {
1311 true => Some(fidl_input_report::MouseDescriptor::default()),
1312 false => None,
1313 },
1314 touch: Some(fidl_input_report::TouchDescriptor {
1315 input: Some(fidl_input_report::TouchInputDescriptor {
1316 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1317 position_x: Some(fidl_input_report::Axis {
1318 range: fidl_input_report::Range { min: 1, max: 2 },
1319 unit: fidl_input_report::Unit {
1320 type_: fidl_input_report::UnitType::None,
1321 exponent: 0,
1322 },
1323 }),
1324 position_y: Some(fidl_input_report::Axis {
1325 range: fidl_input_report::Range { min: 2, max: 3 },
1326 unit: fidl_input_report::Unit {
1327 type_: fidl_input_report::UnitType::Other,
1328 exponent: 100000,
1329 },
1330 }),
1331 pressure: Some(fidl_input_report::Axis {
1332 range: fidl_input_report::Range { min: 3, max: 4 },
1333 unit: fidl_input_report::Unit {
1334 type_: fidl_input_report::UnitType::Grams,
1335 exponent: -991,
1336 },
1337 }),
1338 contact_width: Some(fidl_input_report::Axis {
1339 range: fidl_input_report::Range { min: 5, max: 6 },
1340 unit: fidl_input_report::Unit {
1341 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1342 exponent: 123,
1343 },
1344 }),
1345 contact_height: Some(fidl_input_report::Axis {
1346 range: fidl_input_report::Range { min: 7, max: 8 },
1347 unit: fidl_input_report::Unit {
1348 type_: fidl_input_report::UnitType::Pascals,
1349 exponent: 100,
1350 },
1351 }),
1352 ..Default::default()
1353 }]),
1354 ..Default::default()
1355 }),
1356 ..Default::default()
1357 }),
1358 ..Default::default()
1359 }
1360 }
1361
1362 #[fasync::run_singlethreaded(test)]
1363 async fn send_touchpad_event_button() {
1364 const TOUCH_ID: u32 = 1;
1365 const PRIMARY_BUTTON: u8 = 1;
1366
1367 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1368 device_id: 1,
1369 contacts: vec![],
1370 });
1371 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1372
1373 let contact = fidl_fuchsia_input_report::ContactInputReport {
1374 contact_id: Some(TOUCH_ID),
1375 position_x: Some(0),
1376 position_y: Some(0),
1377 pressure: None,
1378 contact_width: None,
1379 contact_height: None,
1380 ..Default::default()
1381 };
1382 let reports = vec![create_touch_input_report(
1383 vec![contact],
1384 Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1385 unknown_ordinal: PRIMARY_BUTTON,
1386 }]),
1387 event_time_i64,
1388 )];
1389
1390 let expected_events = vec![create_touchpad_event(
1391 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1392 vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1393 unknown_ordinal: PRIMARY_BUTTON,
1394 }]
1395 .into_iter()
1396 .collect(),
1397 event_time_u64,
1398 &descriptor,
1399 )];
1400
1401 assert_input_report_sequence_generates_events!(
1402 input_reports: reports,
1403 expected_events: expected_events,
1404 device_descriptor: descriptor,
1405 device_type: TouchBinding,
1406 );
1407 }
1408
1409 #[fasync::run_singlethreaded(test)]
1410 async fn send_touchpad_event_2_fingers_down_up() {
1411 const TOUCH_ID_1: u32 = 1;
1412 const TOUCH_ID_2: u32 = 2;
1413
1414 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1415 device_id: 1,
1416 contacts: vec![],
1417 });
1418 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1419
1420 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1421 contact_id: Some(TOUCH_ID_1),
1422 position_x: Some(0),
1423 position_y: Some(0),
1424 pressure: None,
1425 contact_width: None,
1426 contact_height: None,
1427 ..Default::default()
1428 };
1429 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1430 contact_id: Some(TOUCH_ID_2),
1431 position_x: Some(10),
1432 position_y: Some(10),
1433 pressure: None,
1434 contact_width: None,
1435 contact_height: None,
1436 ..Default::default()
1437 };
1438 let reports = vec![
1439 create_touch_input_report(
1440 vec![contact1, contact2],
1441 None,
1442 event_time_i64,
1443 ),
1444 create_touch_input_report(vec![], None, event_time_i64),
1445 ];
1446
1447 let expected_events = vec![
1448 create_touchpad_event(
1449 vec![
1450 create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1451 create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1452 ],
1453 HashSet::new(),
1454 event_time_u64,
1455 &descriptor,
1456 ),
1457 create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1458 ];
1459
1460 assert_input_report_sequence_generates_events!(
1461 input_reports: reports,
1462 expected_events: expected_events,
1463 device_descriptor: descriptor,
1464 device_type: TouchBinding,
1465 );
1466 }
1467
1468 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1469 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1470 #[fasync::run_singlethreaded(test)]
1471 async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1472 const TOUCH_ID: u32 = 1;
1473
1474 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1475 device_id: 1,
1476 contacts: vec![],
1477 });
1478 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1479
1480 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1481 contact_id: Some(TOUCH_ID),
1482 position_x: Some(p0.x as i64),
1483 position_y: Some(p0.y as i64),
1484 pressure: None,
1485 contact_width: None,
1486 contact_height: None,
1487 ..Default::default()
1488 };
1489 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1490 contact_id: Some(TOUCH_ID),
1491 position_x: Some(p1.x as i64),
1492 position_y: Some(p1.y as i64),
1493 pressure: None,
1494 contact_width: None,
1495 contact_height: None,
1496 ..Default::default()
1497 };
1498 let reports = vec![
1499 create_touch_input_report(
1500 vec![contact1],
1501 None,
1502 event_time_i64,
1503 ),
1504 create_touch_input_report(
1505 vec![contact2],
1506 None,
1507 event_time_i64,
1508 ),
1509 ];
1510
1511 let expected_events = vec![
1512 create_touchpad_event(
1513 vec![create_touch_contact(TOUCH_ID, p0)],
1514 HashSet::new(),
1515 event_time_u64,
1516 &descriptor,
1517 ),
1518 create_touchpad_event(
1519 vec![create_touch_contact(TOUCH_ID, p1)],
1520 HashSet::new(),
1521 event_time_u64,
1522 &descriptor,
1523 ),
1524 ];
1525
1526 assert_input_report_sequence_generates_events!(
1527 input_reports: reports,
1528 expected_events: expected_events,
1529 device_descriptor: descriptor,
1530 device_type: TouchBinding,
1531 );
1532 }
1533
1534 #[fasync::run_singlethreaded(test)]
1537 async fn send_pressed_button_no_contact() {
1538 let descriptor =
1539 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1540 device_id: 1,
1541 contacts: vec![],
1542 });
1543 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1544
1545 let reports = vec![create_touch_input_report(
1546 vec![],
1547 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1548 event_time_i64,
1549 )];
1550
1551 let expected_events = vec![create_touch_screen_event_with_buttons(
1552 hashmap! {},
1553 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1554 event_time_u64,
1555 &descriptor,
1556 )];
1557
1558 assert_input_report_sequence_generates_events!(
1559 input_reports: reports,
1560 expected_events: expected_events,
1561 device_descriptor: descriptor,
1562 device_type: TouchBinding,
1563 );
1564 }
1565
1566 #[fasync::run_singlethreaded(test)]
1569 async fn send_pressed_button_with_contact() {
1570 const TOUCH_ID: u32 = 2;
1571
1572 let descriptor =
1573 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1574 device_id: 1,
1575 contacts: vec![],
1576 });
1577 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1578
1579 let contact = fidl_fuchsia_input_report::ContactInputReport {
1580 contact_id: Some(TOUCH_ID),
1581 position_x: Some(0),
1582 position_y: Some(0),
1583 pressure: None,
1584 contact_width: None,
1585 contact_height: None,
1586 ..Default::default()
1587 };
1588 let reports = vec![create_touch_input_report(
1589 vec![contact],
1590 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1591 event_time_i64,
1592 )];
1593
1594 let expected_events = vec![create_touch_screen_event_with_buttons(
1595 hashmap! {
1596 fidl_ui_input::PointerEventPhase::Add
1597 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1598 fidl_ui_input::PointerEventPhase::Down
1599 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1600 },
1601 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1602 event_time_u64,
1603 &descriptor,
1604 )];
1605
1606 assert_input_report_sequence_generates_events!(
1607 input_reports: reports,
1608 expected_events: expected_events,
1609 device_descriptor: descriptor,
1610 device_type: TouchBinding,
1611 );
1612 }
1613
1614 #[fasync::run_singlethreaded(test)]
1617 async fn send_multiple_pressed_buttons_with_contact() {
1618 const TOUCH_ID: u32 = 2;
1619
1620 let descriptor =
1621 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1622 device_id: 1,
1623 contacts: vec![],
1624 });
1625 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1626
1627 let contact = fidl_fuchsia_input_report::ContactInputReport {
1628 contact_id: Some(TOUCH_ID),
1629 position_x: Some(0),
1630 position_y: Some(0),
1631 pressure: None,
1632 contact_width: None,
1633 contact_height: None,
1634 ..Default::default()
1635 };
1636 let reports = vec![create_touch_input_report(
1637 vec![contact],
1638 Some(vec![
1639 fidl_fuchsia_input_report::TouchButton::Palm,
1640 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1641 ]),
1642 event_time_i64,
1643 )];
1644
1645 let expected_events = vec![create_touch_screen_event_with_buttons(
1646 hashmap! {
1647 fidl_ui_input::PointerEventPhase::Add
1648 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1649 fidl_ui_input::PointerEventPhase::Down
1650 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1651 },
1652 vec![
1653 fidl_fuchsia_input_report::TouchButton::Palm,
1654 fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1655 ],
1656 event_time_u64,
1657 &descriptor,
1658 )];
1659
1660 assert_input_report_sequence_generates_events!(
1661 input_reports: reports,
1662 expected_events: expected_events,
1663 device_descriptor: descriptor,
1664 device_type: TouchBinding,
1665 );
1666 }
1667
1668 #[fasync::run_singlethreaded(test)]
1670 async fn send_no_buttons_no_contacts() {
1671 let descriptor =
1672 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1673 device_id: 1,
1674 contacts: vec![],
1675 });
1676 let (event_time_i64, _) = testing_utilities::event_times();
1677
1678 let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1679
1680 let expected_events: Vec<input_device::InputEvent> = vec![];
1681
1682 assert_input_report_sequence_generates_events!(
1683 input_reports: reports,
1684 expected_events: expected_events,
1685 device_descriptor: descriptor,
1686 device_type: TouchBinding,
1687 );
1688 }
1689
1690 #[fasync::run_singlethreaded(test)]
1692 async fn send_button_does_not_remove_contacts() {
1693 const TOUCH_ID: u32 = 2;
1694
1695 let descriptor =
1696 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1697 device_id: 1,
1698 contacts: vec![],
1699 });
1700 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1701
1702 let contact = fidl_fuchsia_input_report::ContactInputReport {
1703 contact_id: Some(TOUCH_ID),
1704 position_x: Some(0),
1705 position_y: Some(0),
1706 pressure: None,
1707 contact_width: None,
1708 contact_height: None,
1709 ..Default::default()
1710 };
1711 let reports = vec![
1712 create_touch_input_report(vec![contact], None, event_time_i64),
1713 create_touch_input_report(
1714 vec![],
1715 Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1716 event_time_i64,
1717 ),
1718 create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1719 ];
1720
1721 let expected_events = vec![
1722 create_touch_screen_event_with_buttons(
1723 hashmap! {
1724 fidl_ui_input::PointerEventPhase::Add
1725 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1726 fidl_ui_input::PointerEventPhase::Down
1727 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1728 },
1729 vec![],
1730 event_time_u64,
1731 &descriptor,
1732 ),
1733 create_touch_screen_event_with_buttons(
1734 hashmap! {},
1735 vec![fidl_fuchsia_input_report::TouchButton::Palm],
1736 event_time_u64,
1737 &descriptor,
1738 ),
1739 create_touch_screen_event_with_buttons(
1740 hashmap! {},
1741 vec![],
1742 event_time_u64,
1743 &descriptor,
1744 ),
1745 ];
1746
1747 assert_input_report_sequence_generates_events!(
1748 input_reports: reports,
1749 expected_events: expected_events,
1750 device_descriptor: descriptor,
1751 device_type: TouchBinding,
1752 );
1753 }
1754}