input_pipeline/
touch_binding.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// A [`TouchScreenEvent`] represents a set of contacts and the phase those contacts are in.
26///
27/// For example, when a user touches a touch screen with two fingers, there will be two
28/// [`TouchContact`]s. When a user removes one finger, there will still be two contacts
29/// but one will be reported as removed.
30///
31/// The expected sequence for any given contact is:
32/// 1. [`fidl_fuchsia_ui_input::PointerEventPhase::Add`]
33/// 2. [`fidl_fuchsia_ui_input::PointerEventPhase::Down`]
34/// 3. 0 or more [`fidl_fuchsia_ui_input::PointerEventPhase::Move`]
35/// 4. [`fidl_fuchsia_ui_input::PointerEventPhase::Up`]
36/// 5. [`fidl_fuchsia_ui_input::PointerEventPhase::Remove`]
37///
38/// Additionally, a [`fidl_fuchsia_ui_input::PointerEventPhase::Cancel`] may be sent at any time
39/// signalling that the event is no longer directed towards the receiver.
40#[derive(Debug, PartialEq)]
41pub struct TouchScreenEvent {
42    /// Deprecated. To be removed with https://fxbug.dev/42155652.
43    /// The contacts associated with the touch event. For example, a two-finger touch would result
44    /// in one touch event with two [`TouchContact`]s.
45    ///
46    /// Contacts are grouped based on their current phase (e.g., down, move).
47    pub contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
48
49    /// The contacts associated with the touch event. For example, a two-finger touch would result
50    /// in one touch event with two [`TouchContact`]s.
51    ///
52    /// Contacts are grouped based on their current phase (e.g., add, change).
53    pub injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
54
55    /// Indicates whether any touch buttons are pressed.
56    pub pressed_buttons: Vec<fidl_input_report::TouchButton>,
57
58    /// The wake lease for this event.
59    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/// A [`TouchpadEvent`] represents a set of contacts.
147///
148/// For example, when a user touches a touch screen with two fingers, there will be two
149/// [`TouchContact`]s in the vector.
150#[derive(Clone, Debug, PartialEq)]
151pub struct TouchpadEvent {
152    /// The contacts associated with the touch event. For example, a two-finger touch would result
153    /// in one touch event with two [`TouchContact`]s.
154    pub injector_contacts: Vec<TouchContact>,
155
156    /// The complete button state including this event.
157    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        // Populate TouchpadEvent contact details.
170        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/// [`TouchDeviceType`] indicates the type of touch device. Both Touch Screen and Windows Precision
192/// Touchpad send touch event from driver but need different process inside input pipeline.
193#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub enum TouchDeviceType {
195    TouchScreen,
196    WindowsPrecisionTouchpad,
197}
198
199/// A [`TouchContact`] represents a single contact (e.g., one touch of a multi-touch gesture) related
200/// to a touch event.
201#[derive(Clone, Copy, Debug, PartialEq)]
202pub struct TouchContact {
203    /// The identifier of the contact. Unique per touch device.
204    pub id: u32,
205
206    /// The position of the touch event, in the units of the associated
207    /// [`ContactDeviceDescriptor`]'s `range`.
208    pub position: Position,
209
210    /// The pressure associated with the contact, in the units of the associated
211    /// [`ContactDeviceDescriptor`]'s `pressure_range`.
212    pub pressure: Option<i64>,
213
214    /// The size of the touch event, in the units of the associated
215    /// [`ContactDeviceDescriptor`]'s `range`.
216    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    /// The id of the connected touch screen input device.
248    pub device_id: u32,
249
250    /// The descriptors for the possible contacts associated with the device.
251    pub contacts: Vec<ContactDeviceDescriptor>,
252}
253
254#[derive(Clone, Debug, Eq, PartialEq)]
255pub struct TouchpadDeviceDescriptor {
256    /// The id of the connected touchpad input device.
257    pub device_id: u32,
258
259    /// The descriptors for the possible contacts associated with the device.
260    pub contacts: Vec<ContactDeviceDescriptor>,
261}
262
263#[derive(Clone, Debug, Eq, PartialEq)]
264enum TouchDeviceDescriptor {
265    TouchScreen(TouchScreenDeviceDescriptor),
266    Touchpad(TouchpadDeviceDescriptor),
267}
268
269/// A [`ContactDeviceDescriptor`] describes the possible values touch contact properties can take on.
270///
271/// This descriptor can be used, for example, to determine where on a screen a touch made contact.
272///
273/// # Example
274///
275/// ```
276/// // Determine the scaling factor between the display and the touch device's x range.
277/// let scaling_factor =
278///     display_width / (contact_descriptor._x_range.end - contact_descriptor._x_range.start);
279/// // Use the scaling factor to scale the contact report's x position.
280/// let hit_location =
281///     scaling_factor * (contact_report.position_x - contact_descriptor._x_range.start);
282#[derive(Clone, Debug, Eq, PartialEq)]
283pub struct ContactDeviceDescriptor {
284    /// The range of possible x values for this touch contact.
285    pub x_range: fidl_input_report::Range,
286
287    /// The range of possible y values for this touch contact.
288    pub y_range: fidl_input_report::Range,
289
290    /// The unit of measure for `x_range`.
291    pub x_unit: fidl_input_report::Unit,
292
293    /// The unit of measure for `y_range`.
294    pub y_unit: fidl_input_report::Unit,
295
296    /// The range of possible pressure values for this touch contact.
297    pub pressure_range: Option<fidl_input_report::Range>,
298
299    /// The range of possible widths for this touch contact.
300    pub width_range: Option<fidl_input_report::Range>,
301
302    /// The range of possible heights for this touch contact.
303    pub height_range: Option<fidl_input_report::Range>,
304}
305
306/// A [`TouchBinding`] represents a connection to a touch input device.
307///
308/// The [`TouchBinding`] parses and exposes touch descriptor properties (e.g., the range of
309/// possible x values for touch contacts) for the device it is associated with.
310/// It also parses [`InputReport`]s from the device, and sends them to the device binding owner over
311/// `event_sender`.
312pub struct TouchBinding {
313    /// The channel to stream InputEvents to.
314    event_sender: UnboundedSender<InputEvent>,
315
316    /// Holds information about this device.
317    device_descriptor: TouchDeviceDescriptor,
318
319    /// Touch device type of the touch device.
320    touch_device_type: TouchDeviceType,
321
322    /// Proxy to the device.
323    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    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
346    ///
347    /// The binding will start listening for input reports immediately and send new InputEvents
348    /// to the device binding owner over `input_event_sender`.
349    ///
350    /// # Parameters
351    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
352    /// - `device_id`: The id of the connected touch device.
353    /// - `input_event_sender`: The channel to send new InputEvents to.
354    /// - `device_node`: The inspect node for this device binding
355    /// - `metrics_logger`: The metrics logger.
356    ///
357    /// # Errors
358    /// If there was an error binding to the proxy.
359    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    /// Binds the provided input device to a new instance of `Self`.
387    ///
388    /// # Parameters
389    /// - `device`: The device to use to initialize the binding.
390    /// - `device_id`: The id of the connected touch device.
391    /// - `input_event_sender`: The channel to send new InputEvents to.
392    /// - `device_node`: The inspect node for this device binding
393    ///
394    /// # Errors
395    /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
396    /// correctly.
397    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                // `get_feature_report` to only modify the input_mode and
472                // keep other feature as is.
473                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                        // TODO(https://fxbug.dev/42056283): Remove log message.
487                        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    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
497    ///
498    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
499    ///
500    /// # Parameters
501    /// - `reports`: The incoming [`InputReport`].
502    /// - `previous_report`: The previous [`InputReport`] seen for the same device. This can be
503    ///                    used to determine, for example, which keys are no longer present in
504    ///                    a keyboard report to generate key released events. If `None`, no
505    ///                    previous report was found.
506    /// - `device_descriptor`: The descriptor for the input device generating the input reports.
507    /// - `input_event_sender`: The sender for the device binding's input event stream.
508    ///
509    /// # Returns
510    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
511    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
512    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
513    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
514    /// binding does not generate InputEvents asynchronously, this will be `None`.
515    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    /// Parses a fidl_input_report contact descriptor into a [`ContactDeviceDescriptor`]
567    ///
568    /// # Parameters
569    /// - `contact_device_descriptor`: The contact descriptor to parse.
570    ///
571    /// # Errors
572    /// If the contact description fails to parse because required fields aren't present.
573    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    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
611    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    // Don't send an event if there are no new contacts or pressed buttons.
633    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    // A touch input report containing button state should persist prior contact state,
643    // for the sake of pointer event continuity in future reports containing contact data.
644    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    // Contacts which exist only in current.
655    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    // Contacts which exist in both previous and current.
662    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    // Contacts which exist only in previous.
669    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    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
715    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            // Once the contacts are found, convert them into `TouchContact`s.
728            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    // First unwrap all the optionals in the input report to get to the contacts.
762    let contacts: Vec<TouchContact> = touch_report
763        .contacts
764        .as_ref()
765        .and_then(|unwrapped_contacts| {
766            // Once the contacts are found, convert them into `TouchContact`s.
767            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
777/// Sends a TouchScreenEvent over `input_event_sender`.
778///
779/// # Parameters
780/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
781/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
782///                        pointer events into Scenic.
783/// - `device_descriptor`: The descriptor for the input device generating the input reports.
784/// - `input_event_sender`: The sender for the device binding's input event stream.
785/// - `wake_lease`: The wake lease to send with the event.
786fn 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
821/// Sends a TouchpadEvent over `input_event_sender`.
822///
823/// # Parameters
824/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
825/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
826/// - `device_descriptor`: The descriptor for the input device generating the input reports.
827/// - `input_event_sender`: The sender for the device binding's input event stream.
828fn 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
859/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
860///
861/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
862/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
863/// because some touch screen does not report this method.
864async 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            /* pressed_buttons= */ None,
904            previous_report_time,
905        );
906        let report_time = zx::MonotonicInstant::get().into_nanos();
907        let report =
908            create_touch_input_report(vec![], /* pressed_buttons= */ 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        // Assert there are no pending events on the receiver.
934        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                    // Timestamp value is unpredictable and not relevant in this context,
947                    // so we only assert that the property is present.
948                    start_timestamp_nanos: AnyProperty
949                },
950            }
951        });
952    }
953
954    // Tests that a input report with a new contact generates an event with an add and a down.
955    #[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            /* pressed_buttons= */ 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    // Tests that up and remove events are sent when a touch is released.
1001    #[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                /* pressed_buttons= */ None,
1025                event_time_i64,
1026            ),
1027            create_touch_input_report(vec![], /* pressed_buttons= */ 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    // Tests that a move generates the correct event.
1062    #[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                /* pressed_buttons= */ None,
1098                event_time_i64,
1099            ),
1100            create_touch_input_report(
1101                vec![second_contact],
1102                /* pressed_buttons= */ 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            /* pressed_buttons= */ 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], /* pressed_buttons= */ 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, /* has_mouse_descriptor */
1189                        ));
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                        // Do not panic as `initialize_report_stream()` will call this protocol.
1217                    }
1218                    r => panic!("unsupported request {:?}", r),
1219                }
1220            }
1221        });
1222
1223        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1224
1225        // Create a test inspect node as required by TouchBinding::new()
1226        let inspector = fuchsia_inspect::Inspector::default();
1227        let test_node = inspector.root().create_child("test_node");
1228
1229        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1230        // the binding immediately, so that `set_feature_report_receiver.collect()`
1231        // does not hang.
1232        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        // Create a test inspect node as required by TouchBinding::new()
1289        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    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1305    /// touchpad related tests.
1306    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                /* pressed_buttons= */ None,
1442                event_time_i64,
1443            ),
1444            create_touch_input_report(vec![], /* pressed_buttons= */ 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                /* pressed_buttons= */ None,
1502                event_time_i64,
1503            ),
1504            create_touch_input_report(
1505                vec![contact2],
1506                /* pressed_buttons= */ 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    // Tests that a pressed button with no contacts generates an event with the
1535    // button.
1536    #[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    // Tests that a pressed button with a contact generates an event with
1567    // contact and button.
1568    #[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    // Tests that multiple pressed buttons with contacts generates an event
1615    // with contact and buttons.
1616    #[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    // Tests that no buttons and no contacts generates no events.
1669    #[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    // Tests a buttons event after a contact event does not remove contacts.
1691    #[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}