1use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::utils::{Position, Size};
7use crate::{metrics, mouse_binding};
8use anyhow::{format_err, Context, Error};
9use async_trait::async_trait;
10use fidl_fuchsia_input_report::{InputDeviceProxy, InputReport};
11use fuchsia_inspect::health::Reporter;
12use fuchsia_inspect::ArrayProperty;
13use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
14use maplit::hashmap;
15use metrics_registry::*;
16use std::collections::{HashMap, HashSet};
17use {
18 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
19 fidl_fuchsia_ui_pointerinjector as pointerinjector,
20};
21
22#[derive(Clone, Debug, PartialEq)]
38pub struct TouchScreenEvent {
39 pub contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
45
46 pub injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
51}
52
53impl TouchScreenEvent {
54 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
55 let contacts_clone = self.injector_contacts.clone();
56 node.record_child("injector_contacts", move |contacts_node| {
57 for (phase, contacts) in contacts_clone.iter() {
58 let phase_str = match phase {
59 pointerinjector::EventPhase::Add => "add",
60 pointerinjector::EventPhase::Change => "change",
61 pointerinjector::EventPhase::Remove => "remove",
62 pointerinjector::EventPhase::Cancel => "cancel",
63 };
64 contacts_node.record_child(phase_str, move |phase_node| {
65 for contact in contacts.iter() {
66 phase_node.record_child(contact.id.to_string(), move |contact_node| {
67 contact_node
68 .record_double("position_x_mm", f64::from(contact.position.x));
69 contact_node
70 .record_double("position_y_mm", f64::from(contact.position.y));
71 if let Some(pressure) = contact.pressure {
72 contact_node.record_int("pressure", pressure);
73 }
74 if let Some(contact_size) = contact.contact_size {
75 contact_node.record_double(
76 "contact_width_mm",
77 f64::from(contact_size.width),
78 );
79 contact_node.record_double(
80 "contact_height_mm",
81 f64::from(contact_size.height),
82 );
83 }
84 });
85 }
86 });
87 }
88 });
89 }
90}
91
92#[derive(Clone, Debug, PartialEq)]
97pub struct TouchpadEvent {
98 pub injector_contacts: Vec<TouchContact>,
101
102 pub pressed_buttons: HashSet<mouse_binding::MouseButton>,
104}
105
106impl TouchpadEvent {
107 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
108 let pressed_buttons_node =
109 node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
110 self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
111 pressed_buttons_node.set(i, *button);
112 });
113 node.record(pressed_buttons_node);
114
115 let contacts_clone = self.injector_contacts.clone();
117 node.record_child("injector_contacts", move |contacts_node| {
118 for contact in contacts_clone.iter() {
119 contacts_node.record_child(contact.id.to_string(), move |contact_node| {
120 contact_node.record_double("position_x_mm", f64::from(contact.position.x));
121 contact_node.record_double("position_y_mm", f64::from(contact.position.y));
122 if let Some(pressure) = contact.pressure {
123 contact_node.record_int("pressure", pressure);
124 }
125 if let Some(contact_size) = contact.contact_size {
126 contact_node
127 .record_double("contact_width_mm", f64::from(contact_size.width));
128 contact_node
129 .record_double("contact_height_mm", f64::from(contact_size.height));
130 }
131 })
132 }
133 });
134 }
135}
136
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub enum TouchDeviceType {
141 TouchScreen,
142 WindowsPrecisionTouchpad,
143}
144
145#[derive(Clone, Copy, Debug, PartialEq)]
148pub struct TouchContact {
149 pub id: u32,
151
152 pub position: Position,
155
156 pub pressure: Option<i64>,
159
160 pub contact_size: Option<Size>,
163}
164
165impl Eq for TouchContact {}
166
167impl From<&fidl_fuchsia_input_report::ContactInputReport> for TouchContact {
168 fn from(fidl_contact: &fidl_fuchsia_input_report::ContactInputReport) -> TouchContact {
169 let contact_size =
170 if fidl_contact.contact_width.is_some() && fidl_contact.contact_height.is_some() {
171 Some(Size {
172 width: fidl_contact.contact_width.unwrap() as f32,
173 height: fidl_contact.contact_height.unwrap() as f32,
174 })
175 } else {
176 None
177 };
178
179 TouchContact {
180 id: fidl_contact.contact_id.unwrap_or_default(),
181 position: Position {
182 x: fidl_contact.position_x.unwrap_or_default() as f32,
183 y: fidl_contact.position_y.unwrap_or_default() as f32,
184 },
185 pressure: fidl_contact.pressure,
186 contact_size,
187 }
188 }
189}
190
191#[derive(Clone, Debug, Eq, PartialEq)]
192pub struct TouchScreenDeviceDescriptor {
193 pub device_id: u32,
195
196 pub contacts: Vec<ContactDeviceDescriptor>,
198}
199
200#[derive(Clone, Debug, Eq, PartialEq)]
201pub struct TouchpadDeviceDescriptor {
202 pub device_id: u32,
204
205 pub contacts: Vec<ContactDeviceDescriptor>,
207}
208
209#[derive(Clone, Debug, Eq, PartialEq)]
210enum TouchDeviceDescriptor {
211 TouchScreen(TouchScreenDeviceDescriptor),
212 Touchpad(TouchpadDeviceDescriptor),
213}
214
215#[derive(Clone, Debug, Eq, PartialEq)]
229pub struct ContactDeviceDescriptor {
230 pub x_range: fidl_input_report::Range,
232
233 pub y_range: fidl_input_report::Range,
235
236 pub x_unit: fidl_input_report::Unit,
238
239 pub y_unit: fidl_input_report::Unit,
241
242 pub pressure_range: Option<fidl_input_report::Range>,
244
245 pub width_range: Option<fidl_input_report::Range>,
247
248 pub height_range: Option<fidl_input_report::Range>,
250}
251
252pub struct TouchBinding {
259 event_sender: UnboundedSender<InputEvent>,
261
262 device_descriptor: TouchDeviceDescriptor,
264
265 touch_device_type: TouchDeviceType,
267
268 device_proxy: InputDeviceProxy,
270}
271
272#[async_trait]
273impl input_device::InputDeviceBinding for TouchBinding {
274 fn input_event_sender(&self) -> UnboundedSender<InputEvent> {
275 self.event_sender.clone()
276 }
277
278 fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
279 match self.device_descriptor.clone() {
280 TouchDeviceDescriptor::TouchScreen(desc) => {
281 input_device::InputDeviceDescriptor::TouchScreen(desc)
282 }
283 TouchDeviceDescriptor::Touchpad(desc) => {
284 input_device::InputDeviceDescriptor::Touchpad(desc)
285 }
286 }
287 }
288}
289
290impl TouchBinding {
291 pub async fn new(
306 device_proxy: InputDeviceProxy,
307 device_id: u32,
308 input_event_sender: UnboundedSender<input_device::InputEvent>,
309 device_node: fuchsia_inspect::Node,
310 metrics_logger: metrics::MetricsLogger,
311 ) -> Result<Self, Error> {
312 let (device_binding, mut inspect_status) =
313 Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
314 .await?;
315 device_binding
316 .set_touchpad_mode(true)
317 .await
318 .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
319 inspect_status.health_node.set_ok();
320 input_device::initialize_report_stream(
321 device_proxy,
322 device_binding.get_device_descriptor(),
323 device_binding.input_event_sender(),
324 inspect_status,
325 metrics_logger,
326 Self::process_reports,
327 );
328
329 Ok(device_binding)
330 }
331
332 async fn bind_device(
344 device_proxy: InputDeviceProxy,
345 device_id: u32,
346 input_event_sender: UnboundedSender<input_device::InputEvent>,
347 device_node: fuchsia_inspect::Node,
348 ) -> Result<(Self, InputDeviceStatus), Error> {
349 let mut input_device_status = InputDeviceStatus::new(device_node);
350 let device_descriptor: fidl_input_report::DeviceDescriptor = match device_proxy
351 .get_descriptor()
352 .await
353 {
354 Ok(descriptor) => descriptor,
355 Err(_) => {
356 input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
357 return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
358 }
359 };
360
361 let touch_device_type = get_device_type(&device_proxy).await;
362
363 match device_descriptor.touch {
364 Some(fidl_fuchsia_input_report::TouchDescriptor {
365 input:
366 Some(fidl_fuchsia_input_report::TouchInputDescriptor {
367 contacts: Some(contact_descriptors),
368 max_contacts: _,
369 touch_type: _,
370 buttons: _,
371 ..
372 }),
373 ..
374 }) => Ok((
375 TouchBinding {
376 event_sender: input_event_sender,
377 device_descriptor: match touch_device_type {
378 TouchDeviceType::TouchScreen => {
379 TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
380 device_id,
381 contacts: contact_descriptors
382 .iter()
383 .map(TouchBinding::parse_contact_descriptor)
384 .filter_map(Result::ok)
385 .collect(),
386 })
387 }
388 TouchDeviceType::WindowsPrecisionTouchpad => {
389 TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
390 device_id,
391 contacts: contact_descriptors
392 .iter()
393 .map(TouchBinding::parse_contact_descriptor)
394 .filter_map(Result::ok)
395 .collect(),
396 })
397 }
398 },
399 touch_device_type,
400 device_proxy,
401 },
402 input_device_status,
403 )),
404 descriptor => {
405 input_device_status
406 .health_node
407 .set_unhealthy("Touch Device Descriptor failed to parse.");
408 Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
409 }
410 }
411 }
412
413 async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
414 match self.touch_device_type {
415 TouchDeviceType::TouchScreen => Ok(()),
416 TouchDeviceType::WindowsPrecisionTouchpad => {
417 let mut report = match self.device_proxy.get_feature_report().await? {
420 Ok(report) => report,
421 Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
422 };
423 let mut touch =
424 report.touch.unwrap_or_else(fidl_input_report::TouchFeatureReport::default);
425 touch.input_mode = match enable {
426 true => Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
427 false => Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection),
428 };
429 report.touch = Some(touch);
430 match self.device_proxy.set_feature_report(&report).await? {
431 Ok(()) => {
432 log::info!("touchpad: set touchpad_enabled to {}", enable);
434 Ok(())
435 }
436 Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
437 }
438 }
439 }
440 }
441
442 fn process_reports(
462 report: InputReport,
463 previous_report: Option<InputReport>,
464 device_descriptor: &input_device::InputDeviceDescriptor,
465 input_event_sender: &mut UnboundedSender<InputEvent>,
466 inspect_status: &InputDeviceStatus,
467 metrics_logger: &metrics::MetricsLogger,
468 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
469 inspect_status.count_received_report(&report);
470 match device_descriptor {
471 input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
472 report,
473 previous_report,
474 device_descriptor,
475 input_event_sender,
476 inspect_status,
477 metrics_logger,
478 ),
479 input_device::InputDeviceDescriptor::Touchpad(_) => process_touchpad_reports(
480 report,
481 device_descriptor,
482 input_event_sender,
483 inspect_status,
484 metrics_logger,
485 ),
486 _ => (None, None),
487 }
488 }
489
490 fn parse_contact_descriptor(
498 contact_device_descriptor: &fidl_input_report::ContactInputDescriptor,
499 ) -> Result<ContactDeviceDescriptor, Error> {
500 match contact_device_descriptor {
501 fidl_input_report::ContactInputDescriptor {
502 position_x: Some(x_axis),
503 position_y: Some(y_axis),
504 pressure: pressure_axis,
505 contact_width: width_axis,
506 contact_height: height_axis,
507 ..
508 } => Ok(ContactDeviceDescriptor {
509 x_range: x_axis.range,
510 y_range: y_axis.range,
511 x_unit: x_axis.unit,
512 y_unit: y_axis.unit,
513 pressure_range: pressure_axis.map(|axis| axis.range),
514 width_range: width_axis.map(|axis| axis.range),
515 height_range: height_axis.map(|axis| axis.range),
516 }),
517 descriptor => {
518 Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
519 }
520 }
521 }
522}
523
524fn process_touch_screen_reports(
525 report: InputReport,
526 previous_report: Option<InputReport>,
527 device_descriptor: &input_device::InputDeviceDescriptor,
528 input_event_sender: &mut UnboundedSender<InputEvent>,
529 inspect_status: &InputDeviceStatus,
530 metrics_logger: &metrics::MetricsLogger,
531) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
532 fuchsia_trace::duration!(c"input", c"touch-binding-process-report");
533 fuchsia_trace::flow_end!(c"input", c"input_report", report.trace_id.unwrap_or(0).into());
534
535 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
537 Some(touch) => touch,
538 None => {
539 inspect_status.count_filtered_report();
540 return (previous_report, None);
541 }
542 };
543
544 let previous_contacts: HashMap<u32, TouchContact> = previous_report
545 .as_ref()
546 .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
547 .map(touch_contacts_from_touch_report)
548 .unwrap_or_default();
549 let current_contacts: HashMap<u32, TouchContact> =
550 touch_contacts_from_touch_report(touch_report);
551
552 if previous_contacts.is_empty() && current_contacts.is_empty() {
554 inspect_status.count_filtered_report();
555 return (Some(report), None);
556 }
557
558 let added_contacts: Vec<TouchContact> = Vec::from_iter(
560 current_contacts
561 .values()
562 .cloned()
563 .filter(|contact| !previous_contacts.contains_key(&contact.id)),
564 );
565 let moved_contacts: Vec<TouchContact> = Vec::from_iter(
567 current_contacts
568 .values()
569 .cloned()
570 .filter(|contact| previous_contacts.contains_key(&contact.id)),
571 );
572 let removed_contacts: Vec<TouchContact> = Vec::from_iter(
574 previous_contacts
575 .values()
576 .cloned()
577 .filter(|contact| !current_contacts.contains_key(&contact.id)),
578 );
579
580 let trace_id = fuchsia_trace::Id::new();
581 fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
582 send_touch_screen_event(
583 hashmap! {
584 fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
585 fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
586 fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
587 fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
588 fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
589 },
590 hashmap! {
591 pointerinjector::EventPhase::Add => added_contacts,
592 pointerinjector::EventPhase::Change => moved_contacts,
593 pointerinjector::EventPhase::Remove => removed_contacts,
594 },
595 device_descriptor,
596 input_event_sender,
597 trace_id,
598 inspect_status,
599 metrics_logger,
600 );
601
602 (Some(report), None)
603}
604
605fn process_touchpad_reports(
606 report: InputReport,
607 device_descriptor: &input_device::InputDeviceDescriptor,
608 input_event_sender: &mut UnboundedSender<InputEvent>,
609 inspect_status: &InputDeviceStatus,
610 metrics_logger: &metrics::MetricsLogger,
611) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
612 fuchsia_trace::duration!(c"input", c"touch-binding-process-report");
613 if let Some(trace_id) = report.trace_id {
614 fuchsia_trace::flow_end!(c"input", c"input_report", trace_id.into());
615 }
616
617 let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
619 Some(touch) => touch,
620 None => {
621 inspect_status.count_filtered_report();
622 return (None, None);
623 }
624 };
625
626 let current_contacts: Vec<TouchContact> = touch_report
627 .contacts
628 .as_ref()
629 .and_then(|unwrapped_contacts| {
630 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
632 })
633 .unwrap_or_default();
634
635 let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
636 Some(buttons) => HashSet::from_iter(buttons.iter().cloned()),
637 None => HashSet::new(),
638 };
639
640 let trace_id = fuchsia_trace::Id::new();
641 fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
642 send_touchpad_event(
643 current_contacts,
644 buttons,
645 device_descriptor,
646 input_event_sender,
647 trace_id,
648 inspect_status,
649 metrics_logger,
650 );
651
652 (Some(report), None)
653}
654
655fn touch_contacts_from_touch_report(
656 touch_report: &fidl_fuchsia_input_report::TouchInputReport,
657) -> HashMap<u32, TouchContact> {
658 let contacts: Vec<TouchContact> = touch_report
660 .contacts
661 .as_ref()
662 .and_then(|unwrapped_contacts| {
663 Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
665 })
666 .unwrap_or_default();
667
668 contacts.into_iter().map(|contact| (contact.id, contact)).collect()
669}
670
671fn send_touch_screen_event(
680 contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
681 injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
682 device_descriptor: &input_device::InputDeviceDescriptor,
683 input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
684 trace_id: fuchsia_trace::Id,
685 inspect_status: &InputDeviceStatus,
686 metrics_logger: &metrics::MetricsLogger,
687) {
688 let event = input_device::InputEvent {
689 device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
690 contacts,
691 injector_contacts,
692 }),
693 device_descriptor: device_descriptor.clone(),
694 event_time: zx::MonotonicInstant::get(),
695 handled: Handled::No,
696 trace_id: Some(trace_id),
697 };
698
699 match input_event_sender.unbounded_send(event.clone()) {
700 Err(e) => {
701 metrics_logger.log_error(
702 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
703 std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
704 );
705 }
706 _ => inspect_status.count_generated_event(event),
707 }
708}
709
710fn send_touchpad_event(
718 injector_contacts: Vec<TouchContact>,
719 pressed_buttons: HashSet<mouse_binding::MouseButton>,
720 device_descriptor: &input_device::InputDeviceDescriptor,
721 input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
722 trace_id: fuchsia_trace::Id,
723 inspect_status: &InputDeviceStatus,
724 metrics_logger: &metrics::MetricsLogger,
725) {
726 let event = input_device::InputEvent {
727 device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
728 injector_contacts,
729 pressed_buttons,
730 }),
731 device_descriptor: device_descriptor.clone(),
732 event_time: zx::MonotonicInstant::get(),
733 handled: Handled::No,
734 trace_id: Some(trace_id),
735 };
736
737 match input_event_sender.unbounded_send(event.clone()) {
738 Err(e) => {
739 metrics_logger.log_error(
740 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
741 std::format!("Failed to send TouchpadEvent with error: {:?}", e),
742 );
743 }
744 _ => inspect_status.count_generated_event(event),
745 }
746}
747
748async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
754 match input_device.get_feature_report().await {
755 Ok(Ok(fidl_input_report::FeatureReport {
756 touch:
757 Some(fidl_input_report::TouchFeatureReport {
758 input_mode:
759 Some(
760 fidl_input_report::TouchConfigurationInputMode::MouseCollection
761 | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
762 ),
763 ..
764 }),
765 ..
766 })) => TouchDeviceType::WindowsPrecisionTouchpad,
767 _ => TouchDeviceType::TouchScreen,
768 }
769}
770
771#[cfg(test)]
772mod tests {
773 use super::*;
774 use crate::testing_utilities::{
775 self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
776 create_touchpad_event,
777 };
778 use crate::utils::Position;
779 use assert_matches::assert_matches;
780 use diagnostics_assertions::AnyProperty;
781 use fidl_test_util::spawn_stream_handler;
782 use fuchsia_async as fasync;
783 use futures::StreamExt;
784 use pretty_assertions::assert_eq;
785 use test_case::test_case;
786
787 #[fasync::run_singlethreaded(test)]
788 async fn process_empty_reports() {
789 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
790 let previous_report = create_touch_input_report(
791 vec![],
792 None,
793 previous_report_time,
794 );
795 let report_time = zx::MonotonicInstant::get().into_nanos();
796 let report =
797 create_touch_input_report(vec![], None, report_time);
798
799 let descriptor =
800 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
801 device_id: 1,
802 contacts: vec![],
803 });
804 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
805
806 let inspector = fuchsia_inspect::Inspector::default();
807 let test_node = inspector.root().create_child("TestDevice_Touch");
808 let mut inspect_status = InputDeviceStatus::new(test_node);
809 inspect_status.health_node.set_ok();
810
811 let (returned_report, _) = TouchBinding::process_reports(
812 report,
813 Some(previous_report),
814 &descriptor,
815 &mut event_sender,
816 &inspect_status,
817 &metrics::MetricsLogger::default(),
818 );
819 assert!(returned_report.is_some());
820 assert_eq!(returned_report.unwrap().event_time, Some(report_time));
821
822 let event = event_receiver.try_next();
824 assert!(event.is_err());
825
826 diagnostics_assertions::assert_data_tree!(inspector, root: {
827 "TestDevice_Touch": contains {
828 reports_received_count: 1u64,
829 reports_filtered_count: 1u64,
830 events_generated: 0u64,
831 last_received_timestamp_ns: report_time as u64,
832 last_generated_timestamp_ns: 0u64,
833 "fuchsia.inspect.Health": {
834 status: "OK",
835 start_timestamp_nanos: AnyProperty
838 },
839 }
840 });
841 }
842
843 #[fasync::run_singlethreaded(test)]
845 async fn add_and_down() {
846 const TOUCH_ID: u32 = 2;
847
848 let descriptor =
849 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
850 device_id: 1,
851 contacts: vec![],
852 });
853 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
854
855 let contact = fidl_fuchsia_input_report::ContactInputReport {
856 contact_id: Some(TOUCH_ID),
857 position_x: Some(0),
858 position_y: Some(0),
859 pressure: None,
860 contact_width: None,
861 contact_height: None,
862 ..Default::default()
863 };
864 let reports = vec![create_touch_input_report(
865 vec![contact],
866 None,
867 event_time_i64,
868 )];
869
870 let expected_events = vec![create_touch_screen_event(
871 hashmap! {
872 fidl_ui_input::PointerEventPhase::Add
873 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
874 fidl_ui_input::PointerEventPhase::Down
875 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
876 },
877 event_time_u64,
878 &descriptor,
879 )];
880
881 assert_input_report_sequence_generates_events!(
882 input_reports: reports,
883 expected_events: expected_events,
884 device_descriptor: descriptor,
885 device_type: TouchBinding,
886 );
887 }
888
889 #[fasync::run_singlethreaded(test)]
891 async fn up_and_remove() {
892 const TOUCH_ID: u32 = 2;
893
894 let descriptor =
895 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
896 device_id: 1,
897 contacts: vec![],
898 });
899 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
900
901 let contact = fidl_fuchsia_input_report::ContactInputReport {
902 contact_id: Some(TOUCH_ID),
903 position_x: Some(0),
904 position_y: Some(0),
905 pressure: None,
906 contact_width: None,
907 contact_height: None,
908 ..Default::default()
909 };
910 let reports = vec![
911 create_touch_input_report(
912 vec![contact],
913 None,
914 event_time_i64,
915 ),
916 create_touch_input_report(vec![], None, event_time_i64),
917 ];
918
919 let expected_events = vec![
920 create_touch_screen_event(
921 hashmap! {
922 fidl_ui_input::PointerEventPhase::Add
923 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
924 fidl_ui_input::PointerEventPhase::Down
925 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
926 },
927 event_time_u64,
928 &descriptor,
929 ),
930 create_touch_screen_event(
931 hashmap! {
932 fidl_ui_input::PointerEventPhase::Up
933 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
934 fidl_ui_input::PointerEventPhase::Remove
935 => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
936 },
937 event_time_u64,
938 &descriptor,
939 ),
940 ];
941
942 assert_input_report_sequence_generates_events!(
943 input_reports: reports,
944 expected_events: expected_events,
945 device_descriptor: descriptor,
946 device_type: TouchBinding,
947 );
948 }
949
950 #[fasync::run_singlethreaded(test)]
952 async fn add_down_move() {
953 const TOUCH_ID: u32 = 2;
954 let first = Position { x: 10.0, y: 30.0 };
955 let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
956
957 let descriptor =
958 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
959 device_id: 1,
960 contacts: vec![],
961 });
962 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
963
964 let first_contact = fidl_fuchsia_input_report::ContactInputReport {
965 contact_id: Some(TOUCH_ID),
966 position_x: Some(first.x as i64),
967 position_y: Some(first.y as i64),
968 pressure: None,
969 contact_width: None,
970 contact_height: None,
971 ..Default::default()
972 };
973 let second_contact = fidl_fuchsia_input_report::ContactInputReport {
974 contact_id: Some(TOUCH_ID),
975 position_x: Some(first.x as i64 * 2),
976 position_y: Some(first.y as i64 * 2),
977 pressure: None,
978 contact_width: None,
979 contact_height: None,
980 ..Default::default()
981 };
982
983 let reports = vec![
984 create_touch_input_report(
985 vec![first_contact],
986 None,
987 event_time_i64,
988 ),
989 create_touch_input_report(
990 vec![second_contact],
991 None,
992 event_time_i64,
993 ),
994 ];
995
996 let expected_events = vec![
997 create_touch_screen_event(
998 hashmap! {
999 fidl_ui_input::PointerEventPhase::Add
1000 => vec![create_touch_contact(TOUCH_ID, first)],
1001 fidl_ui_input::PointerEventPhase::Down
1002 => vec![create_touch_contact(TOUCH_ID, first)],
1003 },
1004 event_time_u64,
1005 &descriptor,
1006 ),
1007 create_touch_screen_event(
1008 hashmap! {
1009 fidl_ui_input::PointerEventPhase::Move
1010 => vec![create_touch_contact(TOUCH_ID, second)],
1011 },
1012 event_time_u64,
1013 &descriptor,
1014 ),
1015 ];
1016
1017 assert_input_report_sequence_generates_events!(
1018 input_reports: reports,
1019 expected_events: expected_events,
1020 device_descriptor: descriptor,
1021 device_type: TouchBinding,
1022 );
1023 }
1024
1025 #[fasync::run_singlethreaded(test)]
1026 async fn sent_event_has_trace_id() {
1027 let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1028 let previous_report = create_touch_input_report(
1029 vec![],
1030 None,
1031 previous_report_time,
1032 );
1033
1034 let report_time = zx::MonotonicInstant::get().into_nanos();
1035 let contact = fidl_fuchsia_input_report::ContactInputReport {
1036 contact_id: Some(222),
1037 position_x: Some(333),
1038 position_y: Some(444),
1039 ..Default::default()
1040 };
1041 let report =
1042 create_touch_input_report(vec![contact], None, report_time);
1043
1044 let descriptor =
1045 input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1046 device_id: 1,
1047 contacts: vec![],
1048 });
1049 let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1050
1051 let inspector = fuchsia_inspect::Inspector::default();
1052 let test_node = inspector.root().create_child("TestDevice_Touch");
1053 let mut inspect_status = InputDeviceStatus::new(test_node);
1054 inspect_status.health_node.set_ok();
1055
1056 let _ = TouchBinding::process_reports(
1057 report,
1058 Some(previous_report),
1059 &descriptor,
1060 &mut event_sender,
1061 &inspect_status,
1062 &metrics::MetricsLogger::default(),
1063 );
1064 assert_matches!(event_receiver.try_next(), Ok(Some(InputEvent { trace_id: Some(_), .. })));
1065 }
1066
1067 #[fuchsia::test(allow_stalls = false)]
1068 async fn enables_touchpad_mode_automatically() {
1069 let (set_feature_report_sender, set_feature_report_receiver) =
1070 futures::channel::mpsc::unbounded();
1071 let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1072 let set_feature_report_sender = set_feature_report_sender.clone();
1073 async move {
1074 match input_device_request {
1075 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1076 let _ = responder.send(&get_touchpad_device_descriptor(
1077 true, ));
1079 }
1080 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1081 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1082 touch: Some(fidl_input_report::TouchFeatureReport {
1083 input_mode: Some(
1084 fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1085 ),
1086 ..Default::default()
1087 }),
1088 ..Default::default()
1089 }));
1090 }
1091 fidl_input_report::InputDeviceRequest::SetFeatureReport {
1092 responder,
1093 report,
1094 } => {
1095 match set_feature_report_sender.unbounded_send(report) {
1096 Ok(_) => {
1097 let _ = responder.send(Ok(()));
1098 }
1099 Err(e) => {
1100 panic!("try_send set_feature_report_request failed: {}", e);
1101 }
1102 };
1103 }
1104 fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1105 }
1107 r => panic!("unsupported request {:?}", r),
1108 }
1109 }
1110 });
1111
1112 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1113
1114 let inspector = fuchsia_inspect::Inspector::default();
1116 let test_node = inspector.root().create_child("test_node");
1117
1118 TouchBinding::new(
1122 input_device_proxy,
1123 0,
1124 device_event_sender,
1125 test_node,
1126 metrics::MetricsLogger::default(),
1127 )
1128 .await
1129 .unwrap();
1130 assert_matches!(
1131 set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1132 [fidl_input_report::FeatureReport {
1133 touch: Some(fidl_input_report::TouchFeatureReport {
1134 input_mode: Some(
1135 fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1136 ),
1137 ..
1138 }),
1139 ..
1140 }]
1141 );
1142 }
1143
1144 #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1145 #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1146 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1147 #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1148 #[fuchsia::test(allow_stalls = false)]
1149 async fn identifies_correct_touch_device_type(
1150 has_mouse_descriptor: bool,
1151 touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1152 expect_touch_device_type: TouchDeviceType,
1153 ) {
1154 let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1155 match input_device_request {
1156 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1157 let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1158 }
1159 fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1160 let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1161 touch: Some(fidl_input_report::TouchFeatureReport {
1162 input_mode: touch_input_mode,
1163 ..Default::default()
1164 }),
1165 ..Default::default()
1166 }));
1167 }
1168 fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1169 let _ = responder.send(Ok(()));
1170 }
1171 r => panic!("unsupported request {:?}", r),
1172 }
1173 });
1174
1175 let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1176
1177 let inspector = fuchsia_inspect::Inspector::default();
1179 let test_node = inspector.root().create_child("test_node");
1180
1181 let binding = TouchBinding::new(
1182 input_device_proxy,
1183 0,
1184 device_event_sender,
1185 test_node,
1186 metrics::MetricsLogger::default(),
1187 )
1188 .await
1189 .unwrap();
1190 pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1191 }
1192
1193 fn get_touchpad_device_descriptor(
1196 has_mouse_descriptor: bool,
1197 ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1198 fidl_input_report::DeviceDescriptor {
1199 mouse: match has_mouse_descriptor {
1200 true => Some(fidl_input_report::MouseDescriptor::default()),
1201 false => None,
1202 },
1203 touch: Some(fidl_input_report::TouchDescriptor {
1204 input: Some(fidl_input_report::TouchInputDescriptor {
1205 contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1206 position_x: Some(fidl_input_report::Axis {
1207 range: fidl_input_report::Range { min: 1, max: 2 },
1208 unit: fidl_input_report::Unit {
1209 type_: fidl_input_report::UnitType::None,
1210 exponent: 0,
1211 },
1212 }),
1213 position_y: Some(fidl_input_report::Axis {
1214 range: fidl_input_report::Range { min: 2, max: 3 },
1215 unit: fidl_input_report::Unit {
1216 type_: fidl_input_report::UnitType::Other,
1217 exponent: 100000,
1218 },
1219 }),
1220 pressure: Some(fidl_input_report::Axis {
1221 range: fidl_input_report::Range { min: 3, max: 4 },
1222 unit: fidl_input_report::Unit {
1223 type_: fidl_input_report::UnitType::Grams,
1224 exponent: -991,
1225 },
1226 }),
1227 contact_width: Some(fidl_input_report::Axis {
1228 range: fidl_input_report::Range { min: 5, max: 6 },
1229 unit: fidl_input_report::Unit {
1230 type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1231 exponent: 123,
1232 },
1233 }),
1234 contact_height: Some(fidl_input_report::Axis {
1235 range: fidl_input_report::Range { min: 7, max: 8 },
1236 unit: fidl_input_report::Unit {
1237 type_: fidl_input_report::UnitType::Pascals,
1238 exponent: 100,
1239 },
1240 }),
1241 ..Default::default()
1242 }]),
1243 ..Default::default()
1244 }),
1245 ..Default::default()
1246 }),
1247 ..Default::default()
1248 }
1249 }
1250
1251 #[fasync::run_singlethreaded(test)]
1252 async fn send_touchpad_event_button() {
1253 const TOUCH_ID: u32 = 1;
1254 const PRIMARY_BUTTON: u8 = 1;
1255
1256 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1257 device_id: 1,
1258 contacts: vec![],
1259 });
1260 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1261
1262 let contact = fidl_fuchsia_input_report::ContactInputReport {
1263 contact_id: Some(TOUCH_ID),
1264 position_x: Some(0),
1265 position_y: Some(0),
1266 pressure: None,
1267 contact_width: None,
1268 contact_height: None,
1269 ..Default::default()
1270 };
1271 let reports = vec![create_touch_input_report(
1272 vec![contact],
1273 Some(vec![PRIMARY_BUTTON]),
1274 event_time_i64,
1275 )];
1276
1277 let expected_events = vec![create_touchpad_event(
1278 vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1279 vec![PRIMARY_BUTTON].into_iter().collect(),
1280 event_time_u64,
1281 &descriptor,
1282 )];
1283
1284 assert_input_report_sequence_generates_events!(
1285 input_reports: reports,
1286 expected_events: expected_events,
1287 device_descriptor: descriptor,
1288 device_type: TouchBinding,
1289 );
1290 }
1291
1292 #[fasync::run_singlethreaded(test)]
1293 async fn send_touchpad_event_2_fingers_down_up() {
1294 const TOUCH_ID_1: u32 = 1;
1295 const TOUCH_ID_2: u32 = 2;
1296
1297 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1298 device_id: 1,
1299 contacts: vec![],
1300 });
1301 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1302
1303 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1304 contact_id: Some(TOUCH_ID_1),
1305 position_x: Some(0),
1306 position_y: Some(0),
1307 pressure: None,
1308 contact_width: None,
1309 contact_height: None,
1310 ..Default::default()
1311 };
1312 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1313 contact_id: Some(TOUCH_ID_2),
1314 position_x: Some(10),
1315 position_y: Some(10),
1316 pressure: None,
1317 contact_width: None,
1318 contact_height: None,
1319 ..Default::default()
1320 };
1321 let reports = vec![
1322 create_touch_input_report(
1323 vec![contact1, contact2],
1324 None,
1325 event_time_i64,
1326 ),
1327 create_touch_input_report(vec![], None, event_time_i64),
1328 ];
1329
1330 let expected_events = vec![
1331 create_touchpad_event(
1332 vec![
1333 create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1334 create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1335 ],
1336 HashSet::new(),
1337 event_time_u64,
1338 &descriptor,
1339 ),
1340 create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1341 ];
1342
1343 assert_input_report_sequence_generates_events!(
1344 input_reports: reports,
1345 expected_events: expected_events,
1346 device_descriptor: descriptor,
1347 device_type: TouchBinding,
1348 );
1349 }
1350
1351 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1352 #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1353 #[fasync::run_singlethreaded(test)]
1354 async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1355 const TOUCH_ID: u32 = 1;
1356
1357 let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1358 device_id: 1,
1359 contacts: vec![],
1360 });
1361 let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1362
1363 let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1364 contact_id: Some(TOUCH_ID),
1365 position_x: Some(p0.x as i64),
1366 position_y: Some(p0.y as i64),
1367 pressure: None,
1368 contact_width: None,
1369 contact_height: None,
1370 ..Default::default()
1371 };
1372 let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1373 contact_id: Some(TOUCH_ID),
1374 position_x: Some(p1.x as i64),
1375 position_y: Some(p1.y as i64),
1376 pressure: None,
1377 contact_width: None,
1378 contact_height: None,
1379 ..Default::default()
1380 };
1381 let reports = vec![
1382 create_touch_input_report(
1383 vec![contact1],
1384 None,
1385 event_time_i64,
1386 ),
1387 create_touch_input_report(
1388 vec![contact2],
1389 None,
1390 event_time_i64,
1391 ),
1392 ];
1393
1394 let expected_events = vec![
1395 create_touchpad_event(
1396 vec![create_touch_contact(TOUCH_ID, p0)],
1397 HashSet::new(),
1398 event_time_u64,
1399 &descriptor,
1400 ),
1401 create_touchpad_event(
1402 vec![create_touch_contact(TOUCH_ID, p1)],
1403 HashSet::new(),
1404 event_time_u64,
1405 &descriptor,
1406 ),
1407 ];
1408
1409 assert_input_report_sequence_generates_events!(
1410 input_reports: reports,
1411 expected_events: expected_events,
1412 device_descriptor: descriptor,
1413 device_type: TouchBinding,
1414 );
1415 }
1416}