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    /// - `report`: 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        report: InputReport,
517        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        inspect_status.count_received_report(&report);
524        match device_descriptor {
525            input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
526                report,
527                previous_report,
528                device_descriptor,
529                input_event_sender,
530                inspect_status,
531                metrics_logger,
532            ),
533            input_device::InputDeviceDescriptor::Touchpad(_) => process_touchpad_reports(
534                report,
535                device_descriptor,
536                input_event_sender,
537                inspect_status,
538                metrics_logger,
539            ),
540            _ => (None, None),
541        }
542    }
543
544    /// Parses a fidl_input_report contact descriptor into a [`ContactDeviceDescriptor`]
545    ///
546    /// # Parameters
547    /// - `contact_device_descriptor`: The contact descriptor to parse.
548    ///
549    /// # Errors
550    /// If the contact description fails to parse because required fields aren't present.
551    fn parse_contact_descriptor(
552        contact_device_descriptor: &fidl_input_report::ContactInputDescriptor,
553    ) -> Result<ContactDeviceDescriptor, Error> {
554        match contact_device_descriptor {
555            fidl_input_report::ContactInputDescriptor {
556                position_x: Some(x_axis),
557                position_y: Some(y_axis),
558                pressure: pressure_axis,
559                contact_width: width_axis,
560                contact_height: height_axis,
561                ..
562            } => Ok(ContactDeviceDescriptor {
563                x_range: x_axis.range,
564                y_range: y_axis.range,
565                x_unit: x_axis.unit,
566                y_unit: y_axis.unit,
567                pressure_range: pressure_axis.map(|axis| axis.range),
568                width_range: width_axis.map(|axis| axis.range),
569                height_range: height_axis.map(|axis| axis.range),
570            }),
571            descriptor => {
572                Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
573            }
574        }
575    }
576}
577
578fn process_touch_screen_reports(
579    mut report: InputReport,
580    previous_report: Option<InputReport>,
581    device_descriptor: &input_device::InputDeviceDescriptor,
582    input_event_sender: &mut UnboundedSender<InputEvent>,
583    inspect_status: &InputDeviceStatus,
584    metrics_logger: &metrics::MetricsLogger,
585) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
586    fuchsia_trace::duration!(c"input", c"touch-binding-process-report");
587    fuchsia_trace::flow_end!(c"input", c"input_report", report.trace_id.unwrap_or(0).into());
588
589    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
590    let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
591        Some(touch) => touch,
592        None => {
593            inspect_status.count_filtered_report();
594            return (previous_report, None);
595        }
596    };
597
598    let (previous_contacts, previous_buttons): (
599        HashMap<u32, TouchContact>,
600        Vec<fidl_fuchsia_input_report::TouchButton>,
601    ) = previous_report
602        .as_ref()
603        .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
604        .map(touch_contacts_and_buttons_from_touch_report)
605        .unwrap_or_default();
606    let (current_contacts, current_buttons): (
607        HashMap<u32, TouchContact>,
608        Vec<fidl_fuchsia_input_report::TouchButton>,
609    ) = touch_contacts_and_buttons_from_touch_report(touch_report);
610
611    // Don't send an event if there are no new contacts or pressed buttons.
612    if previous_contacts.is_empty()
613        && current_contacts.is_empty()
614        && previous_buttons.is_empty()
615        && current_buttons.is_empty()
616    {
617        inspect_status.count_filtered_report();
618        return (Some(report), None);
619    }
620
621    // A touch input report containing button state should persist prior contact state,
622    // for the sake of pointer event continuity in future reports containing contact data.
623    if current_contacts.is_empty()
624        && !previous_contacts.is_empty()
625        && (!current_buttons.is_empty() || !previous_buttons.is_empty())
626    {
627        if let Some(touch_report) = report.touch.as_mut() {
628            touch_report.contacts =
629                previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
630        }
631    }
632
633    // Contacts which exist only in current.
634    let added_contacts: Vec<TouchContact> = Vec::from_iter(
635        current_contacts
636            .values()
637            .cloned()
638            .filter(|contact| !previous_contacts.contains_key(&contact.id)),
639    );
640    // Contacts which exist in both previous and current.
641    let moved_contacts: Vec<TouchContact> = Vec::from_iter(
642        current_contacts
643            .values()
644            .cloned()
645            .filter(|contact| previous_contacts.contains_key(&contact.id)),
646    );
647    // Contacts which exist only in previous.
648    let removed_contacts: Vec<TouchContact> =
649        Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
650            current_buttons.is_empty()
651                && previous_buttons.is_empty()
652                && !current_contacts.contains_key(&contact.id)
653        }));
654
655    let trace_id = fuchsia_trace::Id::random();
656    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
657    send_touch_screen_event(
658        hashmap! {
659            fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
660            fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
661            fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
662            fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
663            fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
664        },
665        hashmap! {
666            pointerinjector::EventPhase::Add => added_contacts,
667            pointerinjector::EventPhase::Change => moved_contacts,
668            pointerinjector::EventPhase::Remove => removed_contacts,
669        },
670        current_buttons,
671        device_descriptor,
672        input_event_sender,
673        trace_id,
674        inspect_status,
675        metrics_logger,
676        report.wake_lease.take(),
677    );
678
679    (Some(report), None)
680}
681
682fn process_touchpad_reports(
683    report: InputReport,
684    device_descriptor: &input_device::InputDeviceDescriptor,
685    input_event_sender: &mut UnboundedSender<InputEvent>,
686    inspect_status: &InputDeviceStatus,
687    metrics_logger: &metrics::MetricsLogger,
688) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
689    fuchsia_trace::duration!(c"input", c"touch-binding-process-report");
690    if let Some(trace_id) = report.trace_id {
691        fuchsia_trace::flow_end!(c"input", c"input_report", trace_id.into());
692    }
693
694    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
695    let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
696        Some(touch) => touch,
697        None => {
698            inspect_status.count_filtered_report();
699            return (None, None);
700        }
701    };
702
703    let current_contacts: Vec<TouchContact> = touch_report
704        .contacts
705        .as_ref()
706        .and_then(|unwrapped_contacts| {
707            // Once the contacts are found, convert them into `TouchContact`s.
708            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
709        })
710        .unwrap_or_default();
711
712    let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
713        Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
714            fidl_fuchsia_input_report::TouchButton::Palm => Some(1),
715            fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal } => {
716                log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
717                None
718            }
719        })),
720        None => HashSet::new(),
721    };
722
723    let trace_id = fuchsia_trace::Id::random();
724    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
725    send_touchpad_event(
726        current_contacts,
727        buttons,
728        device_descriptor,
729        input_event_sender,
730        trace_id,
731        inspect_status,
732        metrics_logger,
733    );
734
735    (Some(report), None)
736}
737
738fn touch_contacts_and_buttons_from_touch_report(
739    touch_report: &fidl_fuchsia_input_report::TouchInputReport,
740) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
741    // First unwrap all the optionals in the input report to get to the contacts.
742    let contacts: Vec<TouchContact> = touch_report
743        .contacts
744        .as_ref()
745        .and_then(|unwrapped_contacts| {
746            // Once the contacts are found, convert them into `TouchContact`s.
747            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
748        })
749        .unwrap_or_default();
750
751    (
752        contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
753        touch_report.pressed_buttons.clone().unwrap_or_default(),
754    )
755}
756
757/// Sends a TouchScreenEvent over `input_event_sender`.
758///
759/// # Parameters
760/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
761/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
762///                        pointer events into Scenic.
763/// - `device_descriptor`: The descriptor for the input device generating the input reports.
764/// - `input_event_sender`: The sender for the device binding's input event stream.
765/// - `wake_lease`: The wake lease to send with the event.
766fn send_touch_screen_event(
767    contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
768    injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
769    pressed_buttons: Vec<fidl_input_report::TouchButton>,
770    device_descriptor: &input_device::InputDeviceDescriptor,
771    input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
772    trace_id: fuchsia_trace::Id,
773    inspect_status: &InputDeviceStatus,
774    metrics_logger: &metrics::MetricsLogger,
775    wake_lease: Option<zx::EventPair>,
776) {
777    let event = input_device::InputEvent {
778        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
779            contacts,
780            injector_contacts,
781            pressed_buttons,
782            wake_lease,
783        }),
784        device_descriptor: device_descriptor.clone(),
785        event_time: zx::MonotonicInstant::get(),
786        handled: Handled::No,
787        trace_id: Some(trace_id),
788    };
789
790    match input_event_sender.unbounded_send(event.clone_with_wake_lease()) {
791        Err(e) => {
792            metrics_logger.log_error(
793                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
794                std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
795            );
796        }
797        _ => inspect_status.count_generated_event(event),
798    }
799}
800
801/// Sends a TouchpadEvent over `input_event_sender`.
802///
803/// # Parameters
804/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
805/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
806/// - `device_descriptor`: The descriptor for the input device generating the input reports.
807/// - `input_event_sender`: The sender for the device binding's input event stream.
808fn send_touchpad_event(
809    injector_contacts: Vec<TouchContact>,
810    pressed_buttons: HashSet<mouse_binding::MouseButton>,
811    device_descriptor: &input_device::InputDeviceDescriptor,
812    input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
813    trace_id: fuchsia_trace::Id,
814    inspect_status: &InputDeviceStatus,
815    metrics_logger: &metrics::MetricsLogger,
816) {
817    let event = input_device::InputEvent {
818        device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
819            injector_contacts,
820            pressed_buttons,
821        }),
822        device_descriptor: device_descriptor.clone(),
823        event_time: zx::MonotonicInstant::get(),
824        handled: Handled::No,
825        trace_id: Some(trace_id),
826    };
827
828    match input_event_sender.unbounded_send(event.clone()) {
829        Err(e) => {
830            metrics_logger.log_error(
831                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
832                std::format!("Failed to send TouchpadEvent with error: {:?}", e),
833            );
834        }
835        _ => inspect_status.count_generated_event(event),
836    }
837}
838
839/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
840///
841/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
842/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
843/// because some touch screen does not report this method.
844async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
845    match input_device.get_feature_report().await {
846        Ok(Ok(fidl_input_report::FeatureReport {
847            touch:
848                Some(fidl_input_report::TouchFeatureReport {
849                    input_mode:
850                        Some(
851                            fidl_input_report::TouchConfigurationInputMode::MouseCollection
852                            | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
853                        ),
854                    ..
855                }),
856            ..
857        })) => TouchDeviceType::WindowsPrecisionTouchpad,
858        _ => TouchDeviceType::TouchScreen,
859    }
860}
861
862#[cfg(test)]
863mod tests {
864    use super::*;
865    use crate::testing_utilities::{
866        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
867        create_touch_screen_event_with_buttons, create_touchpad_event,
868    };
869    use crate::utils::Position;
870    use assert_matches::assert_matches;
871    use diagnostics_assertions::AnyProperty;
872    use fidl_test_util::spawn_stream_handler;
873    use fuchsia_async as fasync;
874    use futures::StreamExt;
875    use pretty_assertions::assert_eq;
876    use test_case::test_case;
877
878    #[fasync::run_singlethreaded(test)]
879    async fn process_empty_reports() {
880        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
881        let previous_report = create_touch_input_report(
882            vec![],
883            /* pressed_buttons= */ None,
884            previous_report_time,
885        );
886        let report_time = zx::MonotonicInstant::get().into_nanos();
887        let report =
888            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
889
890        let descriptor =
891            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
892                device_id: 1,
893                contacts: vec![],
894            });
895        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
896
897        let inspector = fuchsia_inspect::Inspector::default();
898        let test_node = inspector.root().create_child("TestDevice_Touch");
899        let mut inspect_status = InputDeviceStatus::new(test_node);
900        inspect_status.health_node.set_ok();
901
902        let (returned_report, _) = TouchBinding::process_reports(
903            report,
904            Some(previous_report),
905            &descriptor,
906            &mut event_sender,
907            &inspect_status,
908            &metrics::MetricsLogger::default(),
909        );
910        assert!(returned_report.is_some());
911        assert_eq!(returned_report.unwrap().event_time, Some(report_time));
912
913        // Assert there are no pending events on the receiver.
914        let event = event_receiver.try_next();
915        assert!(event.is_err());
916
917        diagnostics_assertions::assert_data_tree!(inspector, root: {
918            "TestDevice_Touch": contains {
919                reports_received_count: 1u64,
920                reports_filtered_count: 1u64,
921                events_generated: 0u64,
922                last_received_timestamp_ns: report_time as u64,
923                last_generated_timestamp_ns: 0u64,
924                "fuchsia.inspect.Health": {
925                    status: "OK",
926                    // Timestamp value is unpredictable and not relevant in this context,
927                    // so we only assert that the property is present.
928                    start_timestamp_nanos: AnyProperty
929                },
930            }
931        });
932    }
933
934    // Tests that a input report with a new contact generates an event with an add and a down.
935    #[fasync::run_singlethreaded(test)]
936    async fn add_and_down() {
937        const TOUCH_ID: u32 = 2;
938
939        let descriptor =
940            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
941                device_id: 1,
942                contacts: vec![],
943            });
944        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
945
946        let contact = fidl_fuchsia_input_report::ContactInputReport {
947            contact_id: Some(TOUCH_ID),
948            position_x: Some(0),
949            position_y: Some(0),
950            pressure: None,
951            contact_width: None,
952            contact_height: None,
953            ..Default::default()
954        };
955        let reports = vec![create_touch_input_report(
956            vec![contact],
957            /* pressed_buttons= */ None,
958            event_time_i64,
959        )];
960
961        let expected_events = vec![create_touch_screen_event(
962            hashmap! {
963                fidl_ui_input::PointerEventPhase::Add
964                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
965                fidl_ui_input::PointerEventPhase::Down
966                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
967            },
968            event_time_u64,
969            &descriptor,
970        )];
971
972        assert_input_report_sequence_generates_events!(
973            input_reports: reports,
974            expected_events: expected_events,
975            device_descriptor: descriptor,
976            device_type: TouchBinding,
977        );
978    }
979
980    // Tests that up and remove events are sent when a touch is released.
981    #[fasync::run_singlethreaded(test)]
982    async fn up_and_remove() {
983        const TOUCH_ID: u32 = 2;
984
985        let descriptor =
986            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
987                device_id: 1,
988                contacts: vec![],
989            });
990        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
991
992        let contact = fidl_fuchsia_input_report::ContactInputReport {
993            contact_id: Some(TOUCH_ID),
994            position_x: Some(0),
995            position_y: Some(0),
996            pressure: None,
997            contact_width: None,
998            contact_height: None,
999            ..Default::default()
1000        };
1001        let reports = vec![
1002            create_touch_input_report(
1003                vec![contact],
1004                /* pressed_buttons= */ None,
1005                event_time_i64,
1006            ),
1007            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1008        ];
1009
1010        let expected_events = vec![
1011            create_touch_screen_event(
1012                hashmap! {
1013                    fidl_ui_input::PointerEventPhase::Add
1014                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1015                    fidl_ui_input::PointerEventPhase::Down
1016                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1017                },
1018                event_time_u64,
1019                &descriptor,
1020            ),
1021            create_touch_screen_event(
1022                hashmap! {
1023                    fidl_ui_input::PointerEventPhase::Up
1024                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1025                    fidl_ui_input::PointerEventPhase::Remove
1026                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1027                },
1028                event_time_u64,
1029                &descriptor,
1030            ),
1031        ];
1032
1033        assert_input_report_sequence_generates_events!(
1034            input_reports: reports,
1035            expected_events: expected_events,
1036            device_descriptor: descriptor,
1037            device_type: TouchBinding,
1038        );
1039    }
1040
1041    // Tests that a move generates the correct event.
1042    #[fasync::run_singlethreaded(test)]
1043    async fn add_down_move() {
1044        const TOUCH_ID: u32 = 2;
1045        let first = Position { x: 10.0, y: 30.0 };
1046        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1047
1048        let descriptor =
1049            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1050                device_id: 1,
1051                contacts: vec![],
1052            });
1053        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1054
1055        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1056            contact_id: Some(TOUCH_ID),
1057            position_x: Some(first.x as i64),
1058            position_y: Some(first.y as i64),
1059            pressure: None,
1060            contact_width: None,
1061            contact_height: None,
1062            ..Default::default()
1063        };
1064        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1065            contact_id: Some(TOUCH_ID),
1066            position_x: Some(first.x as i64 * 2),
1067            position_y: Some(first.y as i64 * 2),
1068            pressure: None,
1069            contact_width: None,
1070            contact_height: None,
1071            ..Default::default()
1072        };
1073
1074        let reports = vec![
1075            create_touch_input_report(
1076                vec![first_contact],
1077                /* pressed_buttons= */ None,
1078                event_time_i64,
1079            ),
1080            create_touch_input_report(
1081                vec![second_contact],
1082                /* pressed_buttons= */ None,
1083                event_time_i64,
1084            ),
1085        ];
1086
1087        let expected_events = vec![
1088            create_touch_screen_event(
1089                hashmap! {
1090                    fidl_ui_input::PointerEventPhase::Add
1091                        => vec![create_touch_contact(TOUCH_ID, first)],
1092                    fidl_ui_input::PointerEventPhase::Down
1093                        => vec![create_touch_contact(TOUCH_ID, first)],
1094                },
1095                event_time_u64,
1096                &descriptor,
1097            ),
1098            create_touch_screen_event(
1099                hashmap! {
1100                    fidl_ui_input::PointerEventPhase::Move
1101                        => vec![create_touch_contact(TOUCH_ID, second)],
1102                },
1103                event_time_u64,
1104                &descriptor,
1105            ),
1106        ];
1107
1108        assert_input_report_sequence_generates_events!(
1109            input_reports: reports,
1110            expected_events: expected_events,
1111            device_descriptor: descriptor,
1112            device_type: TouchBinding,
1113        );
1114    }
1115
1116    #[fasync::run_singlethreaded(test)]
1117    async fn sent_event_has_trace_id() {
1118        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1119        let previous_report = create_touch_input_report(
1120            vec![],
1121            /* pressed_buttons= */ None,
1122            previous_report_time,
1123        );
1124
1125        let report_time = zx::MonotonicInstant::get().into_nanos();
1126        let contact = fidl_fuchsia_input_report::ContactInputReport {
1127            contact_id: Some(222),
1128            position_x: Some(333),
1129            position_y: Some(444),
1130            ..Default::default()
1131        };
1132        let report =
1133            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1134
1135        let descriptor =
1136            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1137                device_id: 1,
1138                contacts: vec![],
1139            });
1140        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1141
1142        let inspector = fuchsia_inspect::Inspector::default();
1143        let test_node = inspector.root().create_child("TestDevice_Touch");
1144        let mut inspect_status = InputDeviceStatus::new(test_node);
1145        inspect_status.health_node.set_ok();
1146
1147        let _ = TouchBinding::process_reports(
1148            report,
1149            Some(previous_report),
1150            &descriptor,
1151            &mut event_sender,
1152            &inspect_status,
1153            &metrics::MetricsLogger::default(),
1154        );
1155        assert_matches!(event_receiver.try_next(), Ok(Some(InputEvent { trace_id: Some(_), .. })));
1156    }
1157
1158    #[fuchsia::test(allow_stalls = false)]
1159    async fn enables_touchpad_mode_automatically() {
1160        let (set_feature_report_sender, set_feature_report_receiver) =
1161            futures::channel::mpsc::unbounded();
1162        let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1163            let set_feature_report_sender = set_feature_report_sender.clone();
1164            async move {
1165                match input_device_request {
1166                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1167                        let _ = responder.send(&get_touchpad_device_descriptor(
1168                            true, /* has_mouse_descriptor */
1169                        ));
1170                    }
1171                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1172                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1173                            touch: Some(fidl_input_report::TouchFeatureReport {
1174                                input_mode: Some(
1175                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1176                                ),
1177                                ..Default::default()
1178                            }),
1179                            ..Default::default()
1180                        }));
1181                    }
1182                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1183                        responder,
1184                        report,
1185                    } => {
1186                        match set_feature_report_sender.unbounded_send(report) {
1187                            Ok(_) => {
1188                                let _ = responder.send(Ok(()));
1189                            }
1190                            Err(e) => {
1191                                panic!("try_send set_feature_report_request failed: {}", e);
1192                            }
1193                        };
1194                    }
1195                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1196                        // Do not panic as `initialize_report_stream()` will call this protocol.
1197                    }
1198                    r => panic!("unsupported request {:?}", r),
1199                }
1200            }
1201        });
1202
1203        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1204
1205        // Create a test inspect node as required by TouchBinding::new()
1206        let inspector = fuchsia_inspect::Inspector::default();
1207        let test_node = inspector.root().create_child("test_node");
1208
1209        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1210        // the binding immediately, so that `set_feature_report_receiver.collect()`
1211        // does not hang.
1212        TouchBinding::new(
1213            input_device_proxy,
1214            0,
1215            device_event_sender,
1216            test_node,
1217            metrics::MetricsLogger::default(),
1218        )
1219        .await
1220        .unwrap();
1221        assert_matches!(
1222            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1223            [fidl_input_report::FeatureReport {
1224                touch: Some(fidl_input_report::TouchFeatureReport {
1225                    input_mode: Some(
1226                        fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1227                    ),
1228                    ..
1229                }),
1230                ..
1231            }]
1232        );
1233    }
1234
1235    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1236    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1237    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1238    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1239    #[fuchsia::test(allow_stalls = false)]
1240    async fn identifies_correct_touch_device_type(
1241        has_mouse_descriptor: bool,
1242        touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1243        expect_touch_device_type: TouchDeviceType,
1244    ) {
1245        let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1246            match input_device_request {
1247                fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1248                    let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1249                }
1250                fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1251                    let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1252                        touch: Some(fidl_input_report::TouchFeatureReport {
1253                            input_mode: touch_input_mode,
1254                            ..Default::default()
1255                        }),
1256                        ..Default::default()
1257                    }));
1258                }
1259                fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1260                    let _ = responder.send(Ok(()));
1261                }
1262                r => panic!("unsupported request {:?}", r),
1263            }
1264        });
1265
1266        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1267
1268        // Create a test inspect node as required by TouchBinding::new()
1269        let inspector = fuchsia_inspect::Inspector::default();
1270        let test_node = inspector.root().create_child("test_node");
1271
1272        let binding = TouchBinding::new(
1273            input_device_proxy,
1274            0,
1275            device_event_sender,
1276            test_node,
1277            metrics::MetricsLogger::default(),
1278        )
1279        .await
1280        .unwrap();
1281        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1282    }
1283
1284    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1285    /// touchpad related tests.
1286    fn get_touchpad_device_descriptor(
1287        has_mouse_descriptor: bool,
1288    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1289        fidl_input_report::DeviceDescriptor {
1290            mouse: match has_mouse_descriptor {
1291                true => Some(fidl_input_report::MouseDescriptor::default()),
1292                false => None,
1293            },
1294            touch: Some(fidl_input_report::TouchDescriptor {
1295                input: Some(fidl_input_report::TouchInputDescriptor {
1296                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1297                        position_x: Some(fidl_input_report::Axis {
1298                            range: fidl_input_report::Range { min: 1, max: 2 },
1299                            unit: fidl_input_report::Unit {
1300                                type_: fidl_input_report::UnitType::None,
1301                                exponent: 0,
1302                            },
1303                        }),
1304                        position_y: Some(fidl_input_report::Axis {
1305                            range: fidl_input_report::Range { min: 2, max: 3 },
1306                            unit: fidl_input_report::Unit {
1307                                type_: fidl_input_report::UnitType::Other,
1308                                exponent: 100000,
1309                            },
1310                        }),
1311                        pressure: Some(fidl_input_report::Axis {
1312                            range: fidl_input_report::Range { min: 3, max: 4 },
1313                            unit: fidl_input_report::Unit {
1314                                type_: fidl_input_report::UnitType::Grams,
1315                                exponent: -991,
1316                            },
1317                        }),
1318                        contact_width: Some(fidl_input_report::Axis {
1319                            range: fidl_input_report::Range { min: 5, max: 6 },
1320                            unit: fidl_input_report::Unit {
1321                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1322                                exponent: 123,
1323                            },
1324                        }),
1325                        contact_height: Some(fidl_input_report::Axis {
1326                            range: fidl_input_report::Range { min: 7, max: 8 },
1327                            unit: fidl_input_report::Unit {
1328                                type_: fidl_input_report::UnitType::Pascals,
1329                                exponent: 100,
1330                            },
1331                        }),
1332                        ..Default::default()
1333                    }]),
1334                    ..Default::default()
1335                }),
1336                ..Default::default()
1337            }),
1338            ..Default::default()
1339        }
1340    }
1341
1342    #[fasync::run_singlethreaded(test)]
1343    async fn send_touchpad_event_button() {
1344        const TOUCH_ID: u32 = 1;
1345        const PRIMARY_BUTTON: u8 = 1;
1346
1347        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1348            device_id: 1,
1349            contacts: vec![],
1350        });
1351        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1352
1353        let contact = fidl_fuchsia_input_report::ContactInputReport {
1354            contact_id: Some(TOUCH_ID),
1355            position_x: Some(0),
1356            position_y: Some(0),
1357            pressure: None,
1358            contact_width: None,
1359            contact_height: None,
1360            ..Default::default()
1361        };
1362        let reports = vec![create_touch_input_report(
1363            vec![contact],
1364            Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1365                unknown_ordinal: PRIMARY_BUTTON,
1366            }]),
1367            event_time_i64,
1368        )];
1369
1370        let expected_events = vec![create_touchpad_event(
1371            vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1372            vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1373                unknown_ordinal: PRIMARY_BUTTON,
1374            }]
1375            .into_iter()
1376            .collect(),
1377            event_time_u64,
1378            &descriptor,
1379        )];
1380
1381        assert_input_report_sequence_generates_events!(
1382            input_reports: reports,
1383            expected_events: expected_events,
1384            device_descriptor: descriptor,
1385            device_type: TouchBinding,
1386        );
1387    }
1388
1389    #[fasync::run_singlethreaded(test)]
1390    async fn send_touchpad_event_2_fingers_down_up() {
1391        const TOUCH_ID_1: u32 = 1;
1392        const TOUCH_ID_2: u32 = 2;
1393
1394        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1395            device_id: 1,
1396            contacts: vec![],
1397        });
1398        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1399
1400        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1401            contact_id: Some(TOUCH_ID_1),
1402            position_x: Some(0),
1403            position_y: Some(0),
1404            pressure: None,
1405            contact_width: None,
1406            contact_height: None,
1407            ..Default::default()
1408        };
1409        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1410            contact_id: Some(TOUCH_ID_2),
1411            position_x: Some(10),
1412            position_y: Some(10),
1413            pressure: None,
1414            contact_width: None,
1415            contact_height: None,
1416            ..Default::default()
1417        };
1418        let reports = vec![
1419            create_touch_input_report(
1420                vec![contact1, contact2],
1421                /* pressed_buttons= */ None,
1422                event_time_i64,
1423            ),
1424            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1425        ];
1426
1427        let expected_events = vec![
1428            create_touchpad_event(
1429                vec![
1430                    create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1431                    create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1432                ],
1433                HashSet::new(),
1434                event_time_u64,
1435                &descriptor,
1436            ),
1437            create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1438        ];
1439
1440        assert_input_report_sequence_generates_events!(
1441            input_reports: reports,
1442            expected_events: expected_events,
1443            device_descriptor: descriptor,
1444            device_type: TouchBinding,
1445        );
1446    }
1447
1448    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1449    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1450    #[fasync::run_singlethreaded(test)]
1451    async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1452        const TOUCH_ID: u32 = 1;
1453
1454        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1455            device_id: 1,
1456            contacts: vec![],
1457        });
1458        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1459
1460        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1461            contact_id: Some(TOUCH_ID),
1462            position_x: Some(p0.x as i64),
1463            position_y: Some(p0.y as i64),
1464            pressure: None,
1465            contact_width: None,
1466            contact_height: None,
1467            ..Default::default()
1468        };
1469        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1470            contact_id: Some(TOUCH_ID),
1471            position_x: Some(p1.x as i64),
1472            position_y: Some(p1.y as i64),
1473            pressure: None,
1474            contact_width: None,
1475            contact_height: None,
1476            ..Default::default()
1477        };
1478        let reports = vec![
1479            create_touch_input_report(
1480                vec![contact1],
1481                /* pressed_buttons= */ None,
1482                event_time_i64,
1483            ),
1484            create_touch_input_report(
1485                vec![contact2],
1486                /* pressed_buttons= */ None,
1487                event_time_i64,
1488            ),
1489        ];
1490
1491        let expected_events = vec![
1492            create_touchpad_event(
1493                vec![create_touch_contact(TOUCH_ID, p0)],
1494                HashSet::new(),
1495                event_time_u64,
1496                &descriptor,
1497            ),
1498            create_touchpad_event(
1499                vec![create_touch_contact(TOUCH_ID, p1)],
1500                HashSet::new(),
1501                event_time_u64,
1502                &descriptor,
1503            ),
1504        ];
1505
1506        assert_input_report_sequence_generates_events!(
1507            input_reports: reports,
1508            expected_events: expected_events,
1509            device_descriptor: descriptor,
1510            device_type: TouchBinding,
1511        );
1512    }
1513
1514    // Tests that a pressed button with no contacts generates an event with the
1515    // button.
1516    #[fasync::run_singlethreaded(test)]
1517    async fn send_pressed_button_no_contact() {
1518        let descriptor =
1519            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1520                device_id: 1,
1521                contacts: vec![],
1522            });
1523        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1524
1525        let reports = vec![create_touch_input_report(
1526            vec![],
1527            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1528            event_time_i64,
1529        )];
1530
1531        let expected_events = vec![create_touch_screen_event_with_buttons(
1532            hashmap! {},
1533            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1534            event_time_u64,
1535            &descriptor,
1536        )];
1537
1538        assert_input_report_sequence_generates_events!(
1539            input_reports: reports,
1540            expected_events: expected_events,
1541            device_descriptor: descriptor,
1542            device_type: TouchBinding,
1543        );
1544    }
1545
1546    // Tests that a pressed button with a contact generates an event with
1547    // contact and button.
1548    #[fasync::run_singlethreaded(test)]
1549    async fn send_pressed_button_with_contact() {
1550        const TOUCH_ID: u32 = 2;
1551
1552        let descriptor =
1553            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1554                device_id: 1,
1555                contacts: vec![],
1556            });
1557        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1558
1559        let contact = fidl_fuchsia_input_report::ContactInputReport {
1560            contact_id: Some(TOUCH_ID),
1561            position_x: Some(0),
1562            position_y: Some(0),
1563            pressure: None,
1564            contact_width: None,
1565            contact_height: None,
1566            ..Default::default()
1567        };
1568        let reports = vec![create_touch_input_report(
1569            vec![contact],
1570            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1571            event_time_i64,
1572        )];
1573
1574        let expected_events = vec![create_touch_screen_event_with_buttons(
1575            hashmap! {
1576                fidl_ui_input::PointerEventPhase::Add
1577                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1578                fidl_ui_input::PointerEventPhase::Down
1579                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1580            },
1581            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1582            event_time_u64,
1583            &descriptor,
1584        )];
1585
1586        assert_input_report_sequence_generates_events!(
1587            input_reports: reports,
1588            expected_events: expected_events,
1589            device_descriptor: descriptor,
1590            device_type: TouchBinding,
1591        );
1592    }
1593
1594    // Tests that multiple pressed buttons with contacts generates an event
1595    // with contact and buttons.
1596    #[fasync::run_singlethreaded(test)]
1597    async fn send_multiple_pressed_buttons_with_contact() {
1598        const TOUCH_ID: u32 = 2;
1599
1600        let descriptor =
1601            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1602                device_id: 1,
1603                contacts: vec![],
1604            });
1605        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1606
1607        let contact = fidl_fuchsia_input_report::ContactInputReport {
1608            contact_id: Some(TOUCH_ID),
1609            position_x: Some(0),
1610            position_y: Some(0),
1611            pressure: None,
1612            contact_width: None,
1613            contact_height: None,
1614            ..Default::default()
1615        };
1616        let reports = vec![create_touch_input_report(
1617            vec![contact],
1618            Some(vec![
1619                fidl_fuchsia_input_report::TouchButton::Palm,
1620                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1621            ]),
1622            event_time_i64,
1623        )];
1624
1625        let expected_events = vec![create_touch_screen_event_with_buttons(
1626            hashmap! {
1627                fidl_ui_input::PointerEventPhase::Add
1628                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1629                fidl_ui_input::PointerEventPhase::Down
1630                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1631            },
1632            vec![
1633                fidl_fuchsia_input_report::TouchButton::Palm,
1634                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1635            ],
1636            event_time_u64,
1637            &descriptor,
1638        )];
1639
1640        assert_input_report_sequence_generates_events!(
1641            input_reports: reports,
1642            expected_events: expected_events,
1643            device_descriptor: descriptor,
1644            device_type: TouchBinding,
1645        );
1646    }
1647
1648    // Tests that no buttons and no contacts generates no events.
1649    #[fasync::run_singlethreaded(test)]
1650    async fn send_no_buttons_no_contacts() {
1651        let descriptor =
1652            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1653                device_id: 1,
1654                contacts: vec![],
1655            });
1656        let (event_time_i64, _) = testing_utilities::event_times();
1657
1658        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1659
1660        let expected_events: Vec<input_device::InputEvent> = vec![];
1661
1662        assert_input_report_sequence_generates_events!(
1663            input_reports: reports,
1664            expected_events: expected_events,
1665            device_descriptor: descriptor,
1666            device_type: TouchBinding,
1667        );
1668    }
1669
1670    // Tests a buttons event after a contact event does not remove contacts.
1671    #[fasync::run_singlethreaded(test)]
1672    async fn send_button_does_not_remove_contacts() {
1673        const TOUCH_ID: u32 = 2;
1674
1675        let descriptor =
1676            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1677                device_id: 1,
1678                contacts: vec![],
1679            });
1680        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1681
1682        let contact = fidl_fuchsia_input_report::ContactInputReport {
1683            contact_id: Some(TOUCH_ID),
1684            position_x: Some(0),
1685            position_y: Some(0),
1686            pressure: None,
1687            contact_width: None,
1688            contact_height: None,
1689            ..Default::default()
1690        };
1691        let reports = vec![
1692            create_touch_input_report(vec![contact], None, event_time_i64),
1693            create_touch_input_report(
1694                vec![],
1695                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1696                event_time_i64,
1697            ),
1698            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1699        ];
1700
1701        let expected_events = vec![
1702            create_touch_screen_event_with_buttons(
1703                hashmap! {
1704                    fidl_ui_input::PointerEventPhase::Add
1705                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1706                    fidl_ui_input::PointerEventPhase::Down
1707                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1708                },
1709                vec![],
1710                event_time_u64,
1711                &descriptor,
1712            ),
1713            create_touch_screen_event_with_buttons(
1714                hashmap! {},
1715                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1716                event_time_u64,
1717                &descriptor,
1718            ),
1719            create_touch_screen_event_with_buttons(
1720                hashmap! {},
1721                vec![],
1722                event_time_u64,
1723                &descriptor,
1724            ),
1725        ];
1726
1727        assert_input_report_sequence_generates_events!(
1728            input_reports: reports,
1729            expected_events: expected_events,
1730            device_descriptor: descriptor,
1731            device_type: TouchBinding,
1732        );
1733    }
1734}