Skip to main content

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<Vec<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<Vec<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<Vec<InputEvent>>,
363        device_node: fuchsia_inspect::Node,
364        feature_flags: input_device::InputPipelineFeatureFlags,
365        metrics_logger: metrics::MetricsLogger,
366    ) -> Result<Self, Error> {
367        let (device_binding, mut inspect_status) =
368            Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
369                .await?;
370        device_binding
371            .set_touchpad_mode(true)
372            .await
373            .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
374        inspect_status.health_node.set_ok();
375        input_device::initialize_report_stream(
376            device_proxy,
377            device_binding.get_device_descriptor(),
378            device_binding.input_event_sender(),
379            inspect_status,
380            metrics_logger,
381            feature_flags,
382            Self::process_reports,
383        );
384
385        Ok(device_binding)
386    }
387
388    /// Binds the provided input device to a new instance of `Self`.
389    ///
390    /// # Parameters
391    /// - `device`: The device to use to initialize the binding.
392    /// - `device_id`: The id of the connected touch device.
393    /// - `input_event_sender`: The channel to send new InputEvents to.
394    /// - `device_node`: The inspect node for this device binding
395    ///
396    /// # Errors
397    /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
398    /// correctly.
399    async fn bind_device(
400        device_proxy: InputDeviceProxy,
401        device_id: u32,
402        input_event_sender: UnboundedSender<Vec<InputEvent>>,
403        device_node: fuchsia_inspect::Node,
404    ) -> Result<(Self, InputDeviceStatus), Error> {
405        let mut input_device_status = InputDeviceStatus::new(device_node);
406        let device_descriptor: fidl_input_report::DeviceDescriptor = match device_proxy
407            .get_descriptor()
408            .await
409        {
410            Ok(descriptor) => descriptor,
411            Err(_) => {
412                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
413                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
414            }
415        };
416
417        let touch_device_type = get_device_type(&device_proxy).await;
418
419        match device_descriptor.touch {
420            Some(fidl_fuchsia_input_report::TouchDescriptor {
421                input:
422                    Some(fidl_fuchsia_input_report::TouchInputDescriptor {
423                        contacts: Some(contact_descriptors),
424                        max_contacts: _,
425                        touch_type: _,
426                        buttons: _,
427                        ..
428                    }),
429                ..
430            }) => Ok((
431                TouchBinding {
432                    event_sender: input_event_sender,
433                    device_descriptor: match touch_device_type {
434                        TouchDeviceType::TouchScreen => {
435                            TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
436                                device_id,
437                                contacts: contact_descriptors
438                                    .iter()
439                                    .map(TouchBinding::parse_contact_descriptor)
440                                    .filter_map(Result::ok)
441                                    .collect(),
442                            })
443                        }
444                        TouchDeviceType::WindowsPrecisionTouchpad => {
445                            TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
446                                device_id,
447                                contacts: contact_descriptors
448                                    .iter()
449                                    .map(TouchBinding::parse_contact_descriptor)
450                                    .filter_map(Result::ok)
451                                    .collect(),
452                            })
453                        }
454                    },
455                    touch_device_type,
456                    device_proxy,
457                },
458                input_device_status,
459            )),
460            descriptor => {
461                input_device_status
462                    .health_node
463                    .set_unhealthy("Touch Device Descriptor failed to parse.");
464                Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
465            }
466        }
467    }
468
469    async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
470        match self.touch_device_type {
471            TouchDeviceType::TouchScreen => Ok(()),
472            TouchDeviceType::WindowsPrecisionTouchpad => {
473                // `get_feature_report` to only modify the input_mode and
474                // keep other feature as is.
475                let mut report = match self.device_proxy.get_feature_report().await? {
476                    Ok(report) => report,
477                    Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
478                };
479                let mut touch =
480                    report.touch.unwrap_or_else(fidl_input_report::TouchFeatureReport::default);
481                touch.input_mode = match enable {
482                            true => Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
483                            false => Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection),
484                        };
485                report.touch = Some(touch);
486                match self.device_proxy.set_feature_report(&report).await? {
487                    Ok(()) => {
488                        // TODO(https://fxbug.dev/42056283): Remove log message.
489                        log::info!("touchpad: set touchpad_enabled to {}", enable);
490                        Ok(())
491                    }
492                    Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
493                }
494            }
495        }
496    }
497
498    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
499    ///
500    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
501    ///
502    /// # Parameters
503    /// - `reports`: The incoming [`InputReport`].
504    /// - `previous_report`: The previous [`InputReport`] seen for the same device. This can be
505    ///                    used to determine, for example, which keys are no longer present in
506    ///                    a keyboard report to generate key released events. If `None`, no
507    ///                    previous report was found.
508    /// - `device_descriptor`: The descriptor for the input device generating the input reports.
509    /// - `input_event_sender`: The sender for the device binding's input event stream.
510    ///
511    /// # Returns
512    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
513    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
514    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
515    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
516    /// binding does not generate InputEvents asynchronously, this will be `None`.
517    ///
518    /// The returned [`InputReport`] is guaranteed to have no `wake_lease`.
519    fn process_reports(
520        reports: Vec<InputReport>,
521        previous_report: Option<InputReport>,
522        device_descriptor: &input_device::InputDeviceDescriptor,
523        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
524        inspect_status: &InputDeviceStatus,
525        metrics_logger: &metrics::MetricsLogger,
526        feature_flags: &input_device::InputPipelineFeatureFlags,
527    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
528        fuchsia_trace::duration!(
529            "input",
530            "touch-binding-process-report",
531            "num_reports" => reports.len(),
532        );
533        match device_descriptor {
534            input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
535                reports,
536                previous_report,
537                device_descriptor,
538                input_event_sender,
539                inspect_status,
540                metrics_logger,
541                feature_flags.enable_merge_touch_events,
542            ),
543            input_device::InputDeviceDescriptor::Touchpad(_) => process_touchpad_reports(
544                reports,
545                previous_report,
546                device_descriptor,
547                input_event_sender,
548                inspect_status,
549                metrics_logger,
550            ),
551            _ => (previous_report, None),
552        }
553    }
554
555    /// Parses a fidl_input_report contact descriptor into a [`ContactDeviceDescriptor`]
556    ///
557    /// # Parameters
558    /// - `contact_device_descriptor`: The contact descriptor to parse.
559    ///
560    /// # Errors
561    /// If the contact description fails to parse because required fields aren't present.
562    fn parse_contact_descriptor(
563        contact_device_descriptor: &fidl_input_report::ContactInputDescriptor,
564    ) -> Result<ContactDeviceDescriptor, Error> {
565        match contact_device_descriptor {
566            fidl_input_report::ContactInputDescriptor {
567                position_x: Some(x_axis),
568                position_y: Some(y_axis),
569                pressure: pressure_axis,
570                contact_width: width_axis,
571                contact_height: height_axis,
572                ..
573            } => Ok(ContactDeviceDescriptor {
574                x_range: x_axis.range,
575                y_range: y_axis.range,
576                x_unit: x_axis.unit,
577                y_unit: y_axis.unit,
578                pressure_range: pressure_axis.map(|axis| axis.range),
579                width_range: width_axis.map(|axis| axis.range),
580                height_range: height_axis.map(|axis| axis.range),
581            }),
582            descriptor => {
583                Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
584            }
585        }
586    }
587}
588
589fn is_move_only(event: &InputEvent) -> bool {
590    matches!(
591        &event.device_event,
592        input_device::InputDeviceEvent::TouchScreen(event)
593            if event
594                .injector_contacts
595                .get(&pointerinjector::EventPhase::Add)
596                .map_or(true, |c| c.is_empty())
597                && event
598                    .injector_contacts
599                    .get(&pointerinjector::EventPhase::Remove)
600                    .map_or(true, |c| c.is_empty())
601                && event
602                    .injector_contacts
603                    .get(&pointerinjector::EventPhase::Cancel)
604                    .map_or(true, |c| c.is_empty())
605    )
606}
607
608fn has_pressed_buttons(event: &InputEvent) -> bool {
609    match &event.device_event {
610        input_device::InputDeviceEvent::TouchScreen(event) => !event.pressed_buttons.is_empty(),
611        _ => false,
612    }
613}
614
615fn process_touch_screen_reports(
616    reports: Vec<InputReport>,
617    mut previous_report: Option<InputReport>,
618    device_descriptor: &input_device::InputDeviceDescriptor,
619    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
620    inspect_status: &InputDeviceStatus,
621    metrics_logger: &metrics::MetricsLogger,
622    enable_merge_touch_events: bool,
623) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
624    let num_reports = reports.len();
625    let mut batch: Vec<InputEvent> = Vec::with_capacity(num_reports);
626    for report in reports {
627        inspect_status.count_received_report(&report);
628        let (prev_report, event) = process_single_touch_screen_report(
629            report,
630            previous_report,
631            device_descriptor,
632            inspect_status,
633        );
634        previous_report = prev_report;
635        if let Some(event) = event {
636            batch.push(event);
637        }
638    }
639
640    if !batch.is_empty() {
641        if enable_merge_touch_events {
642            // Pre-calculate move-only status for all events
643            let mut is_event_move_only: Vec<bool> = Vec::with_capacity(batch.len());
644            let mut pressed_buttons: Vec<bool> = Vec::with_capacity(batch.len());
645            for event in &batch {
646                is_event_move_only.push(is_move_only(event));
647                pressed_buttons.push(has_pressed_buttons(event));
648            }
649            let size_of_batch = batch.len();
650
651            // Merge consecutive move-only events into a single event.
652            let mut merged_batch = Vec::with_capacity(size_of_batch);
653
654            // Use into_iter().enumerate() to move elements without cloning
655            for (i, current_event) in batch.into_iter().enumerate() {
656                let current_is_move = is_event_move_only[i];
657                let current_pressed_buttons = pressed_buttons[i];
658                let is_last_event = i == size_of_batch - 1;
659
660                // Check if the NEXT event is also move-only
661                let next_is_move =
662                    if i + 1 < size_of_batch { is_event_move_only[i + 1] } else { false };
663
664                let next_pressed_buttons = if i + 1 < size_of_batch {
665                    pressed_buttons[i + 1]
666                } else {
667                    current_pressed_buttons
668                };
669
670                // If both are move-only, skip the current one (it's redundant).
671                // always keep the last event
672                if !is_last_event
673                    // both are move-only
674                    && (current_is_move && next_is_move)
675                    // same pressed buttons
676                    && (current_pressed_buttons == next_pressed_buttons)
677                {
678                    continue;
679                }
680
681                merged_batch.push(current_event);
682            }
683
684            batch = merged_batch;
685        }
686
687        let events_to_send: Vec<InputEvent> = batch
688            .iter()
689            .map(|e| {
690                let event = e.clone_with_wake_lease();
691                // Unwrap is safe because trace_id is set when the event is created.
692                // This unwrap will not move the trace_id out of the event because trace_id has Copy trait.
693                let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
694                fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
695                event
696            })
697            .collect();
698        fuchsia_trace::instant!(
699            "input",
700            "events_to_input_handlers",
701            fuchsia_trace::Scope::Thread,
702            "num_reports" => num_reports,
703            "num_events_generated" => events_to_send.len()
704        );
705        match input_event_sender.unbounded_send(events_to_send) {
706            Err(e) => {
707                metrics_logger.log_error(
708                    InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
709                    std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
710                );
711            }
712            _ => {
713                inspect_status.count_generated_events(&batch);
714            }
715        }
716    }
717    (previous_report, None)
718}
719
720fn process_single_touch_screen_report(
721    mut report: InputReport,
722    previous_report: Option<InputReport>,
723    device_descriptor: &input_device::InputDeviceDescriptor,
724    inspect_status: &InputDeviceStatus,
725) -> (Option<InputReport>, Option<InputEvent>) {
726    fuchsia_trace::flow_end!("input", "input_report", report.trace_id.unwrap_or(0).into());
727
728    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
729    let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
730        Some(touch) => touch,
731        None => {
732            inspect_status.count_filtered_report();
733            return (previous_report, None);
734        }
735    };
736
737    let (previous_contacts, previous_buttons): (
738        HashMap<u32, TouchContact>,
739        Vec<fidl_fuchsia_input_report::TouchButton>,
740    ) = previous_report
741        .as_ref()
742        .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
743        .map(touch_contacts_and_buttons_from_touch_report)
744        .unwrap_or_default();
745    let (current_contacts, current_buttons): (
746        HashMap<u32, TouchContact>,
747        Vec<fidl_fuchsia_input_report::TouchButton>,
748    ) = touch_contacts_and_buttons_from_touch_report(touch_report);
749
750    // Don't send an event if there are no new contacts or pressed buttons.
751    if previous_contacts.is_empty()
752        && current_contacts.is_empty()
753        && previous_buttons.is_empty()
754        && current_buttons.is_empty()
755    {
756        inspect_status.count_filtered_report();
757        return (Some(report), None);
758    }
759
760    // A touch input report containing button state should persist prior contact state,
761    // for the sake of pointer event continuity in future reports containing contact data.
762    if current_contacts.is_empty()
763        && !previous_contacts.is_empty()
764        && (!current_buttons.is_empty() || !previous_buttons.is_empty())
765    {
766        if let Some(touch_report) = report.touch.as_mut() {
767            touch_report.contacts =
768                previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
769        }
770    }
771
772    // Contacts which exist only in current.
773    let added_contacts: Vec<TouchContact> = Vec::from_iter(
774        current_contacts
775            .values()
776            .cloned()
777            .filter(|contact| !previous_contacts.contains_key(&contact.id)),
778    );
779    // Contacts which exist in both previous and current.
780    let moved_contacts: Vec<TouchContact> = Vec::from_iter(
781        current_contacts
782            .values()
783            .cloned()
784            .filter(|contact| previous_contacts.contains_key(&contact.id)),
785    );
786    // Contacts which exist only in previous.
787    let removed_contacts: Vec<TouchContact> =
788        Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
789            current_buttons.is_empty()
790                && previous_buttons.is_empty()
791                && !current_contacts.contains_key(&contact.id)
792        }));
793
794    let trace_id = fuchsia_trace::Id::random();
795    let event = create_touch_screen_event(
796        hashmap! {
797            fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
798            fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
799            fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
800            fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
801            fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
802        },
803        hashmap! {
804            pointerinjector::EventPhase::Add => added_contacts,
805            pointerinjector::EventPhase::Change => moved_contacts,
806            pointerinjector::EventPhase::Remove => removed_contacts,
807        },
808        current_buttons,
809        device_descriptor,
810        trace_id,
811        report.wake_lease.take(),
812    );
813
814    (Some(report), Some(event))
815}
816
817fn process_touchpad_reports(
818    reports: Vec<InputReport>,
819    mut previous_report: Option<InputReport>,
820    device_descriptor: &input_device::InputDeviceDescriptor,
821    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
822    inspect_status: &InputDeviceStatus,
823    metrics_logger: &metrics::MetricsLogger,
824) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
825    for report in reports {
826        inspect_status.count_received_report(&report);
827        let prev_report = process_single_touchpad_report(
828            report,
829            previous_report,
830            device_descriptor,
831            input_event_sender,
832            inspect_status,
833            metrics_logger,
834        );
835        previous_report = prev_report;
836    }
837    (previous_report, None)
838}
839
840fn process_single_touchpad_report(
841    report: InputReport,
842    previous_report: Option<InputReport>,
843    device_descriptor: &input_device::InputDeviceDescriptor,
844    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
845    inspect_status: &InputDeviceStatus,
846    metrics_logger: &metrics::MetricsLogger,
847) -> Option<InputReport> {
848    if let Some(trace_id) = report.trace_id {
849        fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
850    }
851
852    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
853    let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
854        Some(touch) => touch,
855        None => {
856            inspect_status.count_filtered_report();
857            return previous_report;
858        }
859    };
860
861    let current_contacts: Vec<TouchContact> = touch_report
862        .contacts
863        .as_ref()
864        .and_then(|unwrapped_contacts| {
865            // Once the contacts are found, convert them into `TouchContact`s.
866            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
867        })
868        .unwrap_or_default();
869
870    let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
871        Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
872            fidl_fuchsia_input_report::TouchButton::Palm => Some(1),
873            fidl_fuchsia_input_report::TouchButton::SwipeUp
874            | fidl_fuchsia_input_report::TouchButton::SwipeLeft
875            | fidl_fuchsia_input_report::TouchButton::SwipeRight
876            | fidl_fuchsia_input_report::TouchButton::SwipeDown => {
877                // TODO(https://fxbug.dev/487728300): Support swipe buttons.
878                log::warn!("Swipe buttons {:?} are not supported", button);
879                None
880            }
881            fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal } => {
882                log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
883                None
884            }
885        })),
886        None => HashSet::new(),
887    };
888
889    let trace_id = fuchsia_trace::Id::random();
890    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
891    send_touchpad_event(
892        current_contacts,
893        buttons,
894        device_descriptor,
895        input_event_sender,
896        trace_id,
897        inspect_status,
898        metrics_logger,
899    );
900
901    Some(report)
902}
903
904fn touch_contacts_and_buttons_from_touch_report(
905    touch_report: &fidl_fuchsia_input_report::TouchInputReport,
906) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
907    // First unwrap all the optionals in the input report to get to the contacts.
908    let contacts: Vec<TouchContact> = touch_report
909        .contacts
910        .as_ref()
911        .and_then(|unwrapped_contacts| {
912            // Once the contacts are found, convert them into `TouchContact`s.
913            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
914        })
915        .unwrap_or_default();
916
917    (
918        contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
919        touch_report.pressed_buttons.clone().unwrap_or_default(),
920    )
921}
922
923/// Create a TouchScreenEvent.
924///
925/// # Parameters
926/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
927/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
928///                        pointer events into Scenic.
929/// - `device_descriptor`: The descriptor for the input device generating the input reports.
930/// - `trace_id`: The trace id to distinguish the event.
931/// - `wake_lease`: The wake lease to send with the event.
932fn create_touch_screen_event(
933    contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
934    injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
935    pressed_buttons: Vec<fidl_input_report::TouchButton>,
936    device_descriptor: &input_device::InputDeviceDescriptor,
937    trace_id: fuchsia_trace::Id,
938    wake_lease: Option<zx::EventPair>,
939) -> InputEvent {
940    input_device::InputEvent {
941        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
942            contacts,
943            injector_contacts,
944            pressed_buttons,
945            wake_lease,
946        }),
947        device_descriptor: device_descriptor.clone(),
948        event_time: zx::MonotonicInstant::get(),
949        handled: Handled::No,
950        trace_id: Some(trace_id),
951    }
952}
953
954/// Sends a TouchpadEvent over `input_event_sender`.
955///
956/// # Parameters
957/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
958/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
959/// - `device_descriptor`: The descriptor for the input device generating the input reports.
960/// - `input_event_sender`: The sender for the device binding's input event stream.
961fn send_touchpad_event(
962    injector_contacts: Vec<TouchContact>,
963    pressed_buttons: HashSet<mouse_binding::MouseButton>,
964    device_descriptor: &input_device::InputDeviceDescriptor,
965    input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
966    trace_id: fuchsia_trace::Id,
967    inspect_status: &InputDeviceStatus,
968    metrics_logger: &metrics::MetricsLogger,
969) {
970    let event = input_device::InputEvent {
971        device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
972            injector_contacts,
973            pressed_buttons,
974        }),
975        device_descriptor: device_descriptor.clone(),
976        event_time: zx::MonotonicInstant::get(),
977        handled: Handled::No,
978        trace_id: Some(trace_id),
979    };
980
981    match input_event_sender.unbounded_send(vec![event.clone()]) {
982        Err(e) => {
983            metrics_logger.log_error(
984                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
985                std::format!("Failed to send TouchpadEvent with error: {:?}", e),
986            );
987        }
988        _ => inspect_status.count_generated_event(event),
989    }
990}
991
992/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
993///
994/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
995/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
996/// because some touch screen does not report this method.
997async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
998    match input_device.get_feature_report().await {
999        Ok(Ok(fidl_input_report::FeatureReport {
1000            touch:
1001                Some(fidl_input_report::TouchFeatureReport {
1002                    input_mode:
1003                        Some(
1004                            fidl_input_report::TouchConfigurationInputMode::MouseCollection
1005                            | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
1006                        ),
1007                    ..
1008                }),
1009            ..
1010        })) => TouchDeviceType::WindowsPrecisionTouchpad,
1011        _ => TouchDeviceType::TouchScreen,
1012    }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017    use super::*;
1018    use crate::testing_utilities::{
1019        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1020        create_touch_screen_event_with_buttons, create_touchpad_event,
1021    };
1022    use crate::utils::Position;
1023    use assert_matches::assert_matches;
1024    use diagnostics_assertions::AnyProperty;
1025    use fidl_test_util::spawn_stream_handler;
1026    use fuchsia_async as fasync;
1027    use futures::StreamExt;
1028    use pretty_assertions::assert_eq;
1029    use test_case::test_case;
1030
1031    #[fasync::run_singlethreaded(test)]
1032    async fn process_empty_reports() {
1033        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1034        let previous_report = create_touch_input_report(
1035            vec![],
1036            /* pressed_buttons= */ None,
1037            previous_report_time,
1038        );
1039        let report_time = zx::MonotonicInstant::get().into_nanos();
1040        let report =
1041            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
1042
1043        let descriptor =
1044            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1045                device_id: 1,
1046                contacts: vec![],
1047            });
1048        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1049
1050        let inspector = fuchsia_inspect::Inspector::default();
1051        let test_node = inspector.root().create_child("TestDevice_Touch");
1052        let mut inspect_status = InputDeviceStatus::new(test_node);
1053        inspect_status.health_node.set_ok();
1054
1055        let (returned_report, _) = TouchBinding::process_reports(
1056            vec![report],
1057            Some(previous_report),
1058            &descriptor,
1059            &mut event_sender,
1060            &inspect_status,
1061            &metrics::MetricsLogger::default(),
1062            &input_device::InputPipelineFeatureFlags::default(),
1063        );
1064        assert!(returned_report.is_some());
1065        assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1066
1067        // Assert there are no pending events on the receiver.
1068        let event = event_receiver.try_next();
1069        assert!(event.is_err());
1070
1071        diagnostics_assertions::assert_data_tree!(inspector, root: {
1072            "TestDevice_Touch": contains {
1073                reports_received_count: 1u64,
1074                reports_filtered_count: 1u64,
1075                events_generated: 0u64,
1076                last_received_timestamp_ns: report_time as u64,
1077                last_generated_timestamp_ns: 0u64,
1078                "fuchsia.inspect.Health": {
1079                    status: "OK",
1080                    // Timestamp value is unpredictable and not relevant in this context,
1081                    // so we only assert that the property is present.
1082                    start_timestamp_nanos: AnyProperty
1083                },
1084            }
1085        });
1086    }
1087
1088    // Tests that a input report with a new contact generates an event with an add and a down.
1089    #[fasync::run_singlethreaded(test)]
1090    async fn add_and_down() {
1091        const TOUCH_ID: u32 = 2;
1092
1093        let descriptor =
1094            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1095                device_id: 1,
1096                contacts: vec![],
1097            });
1098        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1099
1100        let contact = fidl_fuchsia_input_report::ContactInputReport {
1101            contact_id: Some(TOUCH_ID),
1102            position_x: Some(0),
1103            position_y: Some(0),
1104            pressure: None,
1105            contact_width: None,
1106            contact_height: None,
1107            ..Default::default()
1108        };
1109        let reports = vec![create_touch_input_report(
1110            vec![contact],
1111            /* pressed_buttons= */ None,
1112            event_time_i64,
1113        )];
1114
1115        let expected_events = vec![create_touch_screen_event(
1116            hashmap! {
1117                fidl_ui_input::PointerEventPhase::Add
1118                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1119                fidl_ui_input::PointerEventPhase::Down
1120                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1121            },
1122            event_time_u64,
1123            &descriptor,
1124        )];
1125
1126        assert_input_report_sequence_generates_events!(
1127            input_reports: reports,
1128            expected_events: expected_events,
1129            device_descriptor: descriptor,
1130            device_type: TouchBinding,
1131        );
1132    }
1133
1134    // Tests that up and remove events are sent when a touch is released.
1135    #[fasync::run_singlethreaded(test)]
1136    async fn up_and_remove() {
1137        const TOUCH_ID: u32 = 2;
1138
1139        let descriptor =
1140            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1141                device_id: 1,
1142                contacts: vec![],
1143            });
1144        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1145
1146        let contact = fidl_fuchsia_input_report::ContactInputReport {
1147            contact_id: Some(TOUCH_ID),
1148            position_x: Some(0),
1149            position_y: Some(0),
1150            pressure: None,
1151            contact_width: None,
1152            contact_height: None,
1153            ..Default::default()
1154        };
1155        let reports = vec![
1156            create_touch_input_report(
1157                vec![contact],
1158                /* pressed_buttons= */ None,
1159                event_time_i64,
1160            ),
1161            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1162        ];
1163
1164        let expected_events = vec![
1165            create_touch_screen_event(
1166                hashmap! {
1167                    fidl_ui_input::PointerEventPhase::Add
1168                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1169                    fidl_ui_input::PointerEventPhase::Down
1170                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1171                },
1172                event_time_u64,
1173                &descriptor,
1174            ),
1175            create_touch_screen_event(
1176                hashmap! {
1177                    fidl_ui_input::PointerEventPhase::Up
1178                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1179                    fidl_ui_input::PointerEventPhase::Remove
1180                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1181                },
1182                event_time_u64,
1183                &descriptor,
1184            ),
1185        ];
1186
1187        assert_input_report_sequence_generates_events!(
1188            input_reports: reports,
1189            expected_events: expected_events,
1190            device_descriptor: descriptor,
1191            device_type: TouchBinding,
1192        );
1193    }
1194
1195    // Tests that a move generates the correct event.
1196    #[fasync::run_singlethreaded(test)]
1197    async fn add_down_move() {
1198        const TOUCH_ID: u32 = 2;
1199        let first = Position { x: 10.0, y: 30.0 };
1200        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1201
1202        let descriptor =
1203            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1204                device_id: 1,
1205                contacts: vec![],
1206            });
1207        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1208
1209        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1210            contact_id: Some(TOUCH_ID),
1211            position_x: Some(first.x as i64),
1212            position_y: Some(first.y as i64),
1213            pressure: None,
1214            contact_width: None,
1215            contact_height: None,
1216            ..Default::default()
1217        };
1218        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1219            contact_id: Some(TOUCH_ID),
1220            position_x: Some(first.x as i64 * 2),
1221            position_y: Some(first.y as i64 * 2),
1222            pressure: None,
1223            contact_width: None,
1224            contact_height: None,
1225            ..Default::default()
1226        };
1227
1228        let reports = vec![
1229            create_touch_input_report(
1230                vec![first_contact],
1231                /* pressed_buttons= */ None,
1232                event_time_i64,
1233            ),
1234            create_touch_input_report(
1235                vec![second_contact],
1236                /* pressed_buttons= */ None,
1237                event_time_i64,
1238            ),
1239        ];
1240
1241        let expected_events = vec![
1242            create_touch_screen_event(
1243                hashmap! {
1244                    fidl_ui_input::PointerEventPhase::Add
1245                        => vec![create_touch_contact(TOUCH_ID, first)],
1246                    fidl_ui_input::PointerEventPhase::Down
1247                        => vec![create_touch_contact(TOUCH_ID, first)],
1248                },
1249                event_time_u64,
1250                &descriptor,
1251            ),
1252            create_touch_screen_event(
1253                hashmap! {
1254                    fidl_ui_input::PointerEventPhase::Move
1255                        => vec![create_touch_contact(TOUCH_ID, second)],
1256                },
1257                event_time_u64,
1258                &descriptor,
1259            ),
1260        ];
1261
1262        assert_input_report_sequence_generates_events!(
1263            input_reports: reports,
1264            expected_events: expected_events,
1265            device_descriptor: descriptor,
1266            device_type: TouchBinding,
1267        );
1268    }
1269
1270    #[fasync::run_singlethreaded(test)]
1271    async fn sent_event_has_trace_id() {
1272        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1273        let previous_report = create_touch_input_report(
1274            vec![],
1275            /* pressed_buttons= */ None,
1276            previous_report_time,
1277        );
1278
1279        let report_time = zx::MonotonicInstant::get().into_nanos();
1280        let contact = fidl_fuchsia_input_report::ContactInputReport {
1281            contact_id: Some(222),
1282            position_x: Some(333),
1283            position_y: Some(444),
1284            ..Default::default()
1285        };
1286        let report =
1287            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1288
1289        let descriptor =
1290            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1291                device_id: 1,
1292                contacts: vec![],
1293            });
1294        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1295
1296        let inspector = fuchsia_inspect::Inspector::default();
1297        let test_node = inspector.root().create_child("TestDevice_Touch");
1298        let mut inspect_status = InputDeviceStatus::new(test_node);
1299        inspect_status.health_node.set_ok();
1300
1301        let _ = TouchBinding::process_reports(
1302            vec![report],
1303            Some(previous_report),
1304            &descriptor,
1305            &mut event_sender,
1306            &inspect_status,
1307            &metrics::MetricsLogger::default(),
1308            &input_device::InputPipelineFeatureFlags::default(),
1309        );
1310        assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1311    }
1312
1313    #[fuchsia::test(allow_stalls = false)]
1314    async fn enables_touchpad_mode_automatically() {
1315        let (set_feature_report_sender, set_feature_report_receiver) =
1316            futures::channel::mpsc::unbounded();
1317        let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1318            let set_feature_report_sender = set_feature_report_sender.clone();
1319            async move {
1320                match input_device_request {
1321                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1322                        let _ = responder.send(&get_touchpad_device_descriptor(
1323                            true, /* has_mouse_descriptor */
1324                        ));
1325                    }
1326                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1327                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1328                            touch: Some(fidl_input_report::TouchFeatureReport {
1329                                input_mode: Some(
1330                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1331                                ),
1332                                ..Default::default()
1333                            }),
1334                            ..Default::default()
1335                        }));
1336                    }
1337                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1338                        responder,
1339                        report,
1340                    } => {
1341                        match set_feature_report_sender.unbounded_send(report) {
1342                            Ok(_) => {
1343                                let _ = responder.send(Ok(()));
1344                            }
1345                            Err(e) => {
1346                                panic!("try_send set_feature_report_request failed: {}", e);
1347                            }
1348                        };
1349                    }
1350                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1351                        // Do not panic as `initialize_report_stream()` will call this protocol.
1352                    }
1353                    r => panic!("unsupported request {:?}", r),
1354                }
1355            }
1356        });
1357
1358        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1359
1360        // Create a test inspect node as required by TouchBinding::new()
1361        let inspector = fuchsia_inspect::Inspector::default();
1362        let test_node = inspector.root().create_child("test_node");
1363
1364        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1365        // the binding immediately, so that `set_feature_report_receiver.collect()`
1366        // does not hang.
1367        TouchBinding::new(
1368            input_device_proxy,
1369            0,
1370            device_event_sender,
1371            test_node,
1372            input_device::InputPipelineFeatureFlags::default(),
1373            metrics::MetricsLogger::default(),
1374        )
1375        .await
1376        .unwrap();
1377        assert_matches!(
1378            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1379            [fidl_input_report::FeatureReport {
1380                touch: Some(fidl_input_report::TouchFeatureReport {
1381                    input_mode: Some(
1382                        fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1383                    ),
1384                    ..
1385                }),
1386                ..
1387            }]
1388        );
1389    }
1390
1391    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1392    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1393    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1394    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1395    #[fuchsia::test(allow_stalls = false)]
1396    async fn identifies_correct_touch_device_type(
1397        has_mouse_descriptor: bool,
1398        touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1399        expect_touch_device_type: TouchDeviceType,
1400    ) {
1401        let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1402            match input_device_request {
1403                fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1404                    let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1405                }
1406                fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1407                    let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1408                        touch: Some(fidl_input_report::TouchFeatureReport {
1409                            input_mode: touch_input_mode,
1410                            ..Default::default()
1411                        }),
1412                        ..Default::default()
1413                    }));
1414                }
1415                fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1416                    let _ = responder.send(Ok(()));
1417                }
1418                r => panic!("unsupported request {:?}", r),
1419            }
1420        });
1421
1422        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1423
1424        // Create a test inspect node as required by TouchBinding::new()
1425        let inspector = fuchsia_inspect::Inspector::default();
1426        let test_node = inspector.root().create_child("test_node");
1427
1428        let binding = TouchBinding::new(
1429            input_device_proxy,
1430            0,
1431            device_event_sender,
1432            test_node,
1433            input_device::InputPipelineFeatureFlags::default(),
1434            metrics::MetricsLogger::default(),
1435        )
1436        .await
1437        .unwrap();
1438        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1439    }
1440
1441    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1442    /// touchpad related tests.
1443    fn get_touchpad_device_descriptor(
1444        has_mouse_descriptor: bool,
1445    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1446        fidl_input_report::DeviceDescriptor {
1447            mouse: match has_mouse_descriptor {
1448                true => Some(fidl_input_report::MouseDescriptor::default()),
1449                false => None,
1450            },
1451            touch: Some(fidl_input_report::TouchDescriptor {
1452                input: Some(fidl_input_report::TouchInputDescriptor {
1453                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1454                        position_x: Some(fidl_input_report::Axis {
1455                            range: fidl_input_report::Range { min: 1, max: 2 },
1456                            unit: fidl_input_report::Unit {
1457                                type_: fidl_input_report::UnitType::None,
1458                                exponent: 0,
1459                            },
1460                        }),
1461                        position_y: Some(fidl_input_report::Axis {
1462                            range: fidl_input_report::Range { min: 2, max: 3 },
1463                            unit: fidl_input_report::Unit {
1464                                type_: fidl_input_report::UnitType::Other,
1465                                exponent: 100000,
1466                            },
1467                        }),
1468                        pressure: Some(fidl_input_report::Axis {
1469                            range: fidl_input_report::Range { min: 3, max: 4 },
1470                            unit: fidl_input_report::Unit {
1471                                type_: fidl_input_report::UnitType::Grams,
1472                                exponent: -991,
1473                            },
1474                        }),
1475                        contact_width: Some(fidl_input_report::Axis {
1476                            range: fidl_input_report::Range { min: 5, max: 6 },
1477                            unit: fidl_input_report::Unit {
1478                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1479                                exponent: 123,
1480                            },
1481                        }),
1482                        contact_height: Some(fidl_input_report::Axis {
1483                            range: fidl_input_report::Range { min: 7, max: 8 },
1484                            unit: fidl_input_report::Unit {
1485                                type_: fidl_input_report::UnitType::Pascals,
1486                                exponent: 100,
1487                            },
1488                        }),
1489                        ..Default::default()
1490                    }]),
1491                    ..Default::default()
1492                }),
1493                ..Default::default()
1494            }),
1495            ..Default::default()
1496        }
1497    }
1498
1499    #[fasync::run_singlethreaded(test)]
1500    async fn send_touchpad_event_button() {
1501        const TOUCH_ID: u32 = 1;
1502        const PRIMARY_BUTTON: u8 = 1;
1503
1504        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1505            device_id: 1,
1506            contacts: vec![],
1507        });
1508        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1509
1510        let contact = fidl_fuchsia_input_report::ContactInputReport {
1511            contact_id: Some(TOUCH_ID),
1512            position_x: Some(0),
1513            position_y: Some(0),
1514            pressure: None,
1515            contact_width: None,
1516            contact_height: None,
1517            ..Default::default()
1518        };
1519        let reports = vec![create_touch_input_report(
1520            vec![contact],
1521            Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1522                unknown_ordinal: PRIMARY_BUTTON,
1523            }]),
1524            event_time_i64,
1525        )];
1526
1527        let expected_events = vec![create_touchpad_event(
1528            vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1529            vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1530                unknown_ordinal: PRIMARY_BUTTON,
1531            }]
1532            .into_iter()
1533            .collect(),
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    #[fasync::run_singlethreaded(test)]
1547    async fn send_touchpad_event_2_fingers_down_up() {
1548        const TOUCH_ID_1: u32 = 1;
1549        const TOUCH_ID_2: u32 = 2;
1550
1551        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1552            device_id: 1,
1553            contacts: vec![],
1554        });
1555        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1556
1557        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1558            contact_id: Some(TOUCH_ID_1),
1559            position_x: Some(0),
1560            position_y: Some(0),
1561            pressure: None,
1562            contact_width: None,
1563            contact_height: None,
1564            ..Default::default()
1565        };
1566        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1567            contact_id: Some(TOUCH_ID_2),
1568            position_x: Some(10),
1569            position_y: Some(10),
1570            pressure: None,
1571            contact_width: None,
1572            contact_height: None,
1573            ..Default::default()
1574        };
1575        let reports = vec![
1576            create_touch_input_report(
1577                vec![contact1, contact2],
1578                /* pressed_buttons= */ None,
1579                event_time_i64,
1580            ),
1581            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1582        ];
1583
1584        let expected_events = vec![
1585            create_touchpad_event(
1586                vec![
1587                    create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1588                    create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1589                ],
1590                HashSet::new(),
1591                event_time_u64,
1592                &descriptor,
1593            ),
1594            create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1595        ];
1596
1597        assert_input_report_sequence_generates_events!(
1598            input_reports: reports,
1599            expected_events: expected_events,
1600            device_descriptor: descriptor,
1601            device_type: TouchBinding,
1602        );
1603    }
1604
1605    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1606    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1607    #[fasync::run_singlethreaded(test)]
1608    async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1609        const TOUCH_ID: u32 = 1;
1610
1611        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1612            device_id: 1,
1613            contacts: vec![],
1614        });
1615        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1616
1617        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1618            contact_id: Some(TOUCH_ID),
1619            position_x: Some(p0.x as i64),
1620            position_y: Some(p0.y as i64),
1621            pressure: None,
1622            contact_width: None,
1623            contact_height: None,
1624            ..Default::default()
1625        };
1626        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1627            contact_id: Some(TOUCH_ID),
1628            position_x: Some(p1.x as i64),
1629            position_y: Some(p1.y as i64),
1630            pressure: None,
1631            contact_width: None,
1632            contact_height: None,
1633            ..Default::default()
1634        };
1635        let reports = vec![
1636            create_touch_input_report(
1637                vec![contact1],
1638                /* pressed_buttons= */ None,
1639                event_time_i64,
1640            ),
1641            create_touch_input_report(
1642                vec![contact2],
1643                /* pressed_buttons= */ None,
1644                event_time_i64,
1645            ),
1646        ];
1647
1648        let expected_events = vec![
1649            create_touchpad_event(
1650                vec![create_touch_contact(TOUCH_ID, p0)],
1651                HashSet::new(),
1652                event_time_u64,
1653                &descriptor,
1654            ),
1655            create_touchpad_event(
1656                vec![create_touch_contact(TOUCH_ID, p1)],
1657                HashSet::new(),
1658                event_time_u64,
1659                &descriptor,
1660            ),
1661        ];
1662
1663        assert_input_report_sequence_generates_events!(
1664            input_reports: reports,
1665            expected_events: expected_events,
1666            device_descriptor: descriptor,
1667            device_type: TouchBinding,
1668        );
1669    }
1670
1671    // Tests that a pressed button with no contacts generates an event with the
1672    // button.
1673    #[test_case(true; "merge touch events enabled")]
1674    #[test_case(false; "merge touch events disabled")]
1675    #[fasync::run_singlethreaded(test)]
1676    async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1677        let descriptor =
1678            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1679                device_id: 1,
1680                contacts: vec![],
1681            });
1682        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1683
1684        let reports = vec![create_touch_input_report(
1685            vec![],
1686            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1687            event_time_i64,
1688        )];
1689
1690        let expected_events = vec![create_touch_screen_event_with_buttons(
1691            hashmap! {},
1692            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1693            event_time_u64,
1694            &descriptor,
1695        )];
1696
1697        assert_input_report_sequence_generates_events_with_feature_flags!(
1698            input_reports: reports,
1699            expected_events: expected_events,
1700            device_descriptor: descriptor,
1701            device_type: TouchBinding,
1702            feature_flags: input_device::InputPipelineFeatureFlags {
1703                enable_merge_touch_events,
1704                ..Default::default()
1705            },
1706        );
1707    }
1708
1709    // Tests that a pressed button with a contact generates an event with
1710    // contact and button.
1711    #[test_case(true; "merge touch events enabled")]
1712    #[test_case(false; "merge touch events disabled")]
1713    #[fasync::run_singlethreaded(test)]
1714    async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1715        const TOUCH_ID: u32 = 2;
1716
1717        let descriptor =
1718            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1719                device_id: 1,
1720                contacts: vec![],
1721            });
1722        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1723
1724        let contact = fidl_fuchsia_input_report::ContactInputReport {
1725            contact_id: Some(TOUCH_ID),
1726            position_x: Some(0),
1727            position_y: Some(0),
1728            pressure: None,
1729            contact_width: None,
1730            contact_height: None,
1731            ..Default::default()
1732        };
1733        let reports = vec![create_touch_input_report(
1734            vec![contact],
1735            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1736            event_time_i64,
1737        )];
1738
1739        let expected_events = vec![create_touch_screen_event_with_buttons(
1740            hashmap! {
1741                fidl_ui_input::PointerEventPhase::Add
1742                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1743                fidl_ui_input::PointerEventPhase::Down
1744                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1745            },
1746            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1747            event_time_u64,
1748            &descriptor,
1749        )];
1750
1751        assert_input_report_sequence_generates_events_with_feature_flags!(
1752            input_reports: reports,
1753            expected_events: expected_events,
1754            device_descriptor: descriptor,
1755            device_type: TouchBinding,
1756            feature_flags: input_device::InputPipelineFeatureFlags {
1757                enable_merge_touch_events,
1758                ..Default::default()
1759            },
1760        );
1761    }
1762
1763    // Tests that multiple pressed buttons with contacts generates an event
1764    // with contact and buttons.
1765    #[test_case(true; "merge touch events enabled")]
1766    #[test_case(false; "merge touch events disabled")]
1767    #[fasync::run_singlethreaded(test)]
1768    async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1769        const TOUCH_ID: u32 = 2;
1770
1771        let descriptor =
1772            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1773                device_id: 1,
1774                contacts: vec![],
1775            });
1776        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1777
1778        let contact = fidl_fuchsia_input_report::ContactInputReport {
1779            contact_id: Some(TOUCH_ID),
1780            position_x: Some(0),
1781            position_y: Some(0),
1782            pressure: None,
1783            contact_width: None,
1784            contact_height: None,
1785            ..Default::default()
1786        };
1787        let reports = vec![create_touch_input_report(
1788            vec![contact],
1789            Some(vec![
1790                fidl_fuchsia_input_report::TouchButton::Palm,
1791                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1792            ]),
1793            event_time_i64,
1794        )];
1795
1796        let expected_events = vec![create_touch_screen_event_with_buttons(
1797            hashmap! {
1798                fidl_ui_input::PointerEventPhase::Add
1799                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1800                fidl_ui_input::PointerEventPhase::Down
1801                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1802            },
1803            vec![
1804                fidl_fuchsia_input_report::TouchButton::Palm,
1805                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1806            ],
1807            event_time_u64,
1808            &descriptor,
1809        )];
1810
1811        assert_input_report_sequence_generates_events_with_feature_flags!(
1812            input_reports: reports,
1813            expected_events: expected_events,
1814            device_descriptor: descriptor,
1815            device_type: TouchBinding,
1816            feature_flags: input_device::InputPipelineFeatureFlags {
1817                enable_merge_touch_events,
1818                ..Default::default()
1819            },
1820        );
1821    }
1822
1823    // Tests that no buttons and no contacts generates no events.
1824    #[test_case(true; "merge touch events enabled")]
1825    #[test_case(false; "merge touch events disabled")]
1826    #[fasync::run_singlethreaded(test)]
1827    async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1828        let descriptor =
1829            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1830                device_id: 1,
1831                contacts: vec![],
1832            });
1833        let (event_time_i64, _) = testing_utilities::event_times();
1834
1835        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1836
1837        let expected_events: Vec<input_device::InputEvent> = vec![];
1838
1839        assert_input_report_sequence_generates_events_with_feature_flags!(
1840            input_reports: reports,
1841            expected_events: expected_events,
1842            device_descriptor: descriptor,
1843            device_type: TouchBinding,
1844            feature_flags: input_device::InputPipelineFeatureFlags {
1845                enable_merge_touch_events,
1846                ..Default::default()
1847            },
1848        );
1849    }
1850
1851    // Tests a buttons event after a contact event does not remove contacts.
1852    #[test_case(true; "merge touch events enabled")]
1853    #[test_case(false; "merge touch events disabled")]
1854    #[fasync::run_singlethreaded(test)]
1855    async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1856        const TOUCH_ID: u32 = 2;
1857
1858        let descriptor =
1859            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1860                device_id: 1,
1861                contacts: vec![],
1862            });
1863        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1864
1865        let contact = fidl_fuchsia_input_report::ContactInputReport {
1866            contact_id: Some(TOUCH_ID),
1867            position_x: Some(0),
1868            position_y: Some(0),
1869            pressure: None,
1870            contact_width: None,
1871            contact_height: None,
1872            ..Default::default()
1873        };
1874        let reports = vec![
1875            create_touch_input_report(vec![contact], None, event_time_i64),
1876            create_touch_input_report(
1877                vec![],
1878                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1879                event_time_i64,
1880            ),
1881            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1882        ];
1883
1884        let expected_events = vec![
1885            create_touch_screen_event_with_buttons(
1886                hashmap! {
1887                    fidl_ui_input::PointerEventPhase::Add
1888                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1889                    fidl_ui_input::PointerEventPhase::Down
1890                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1891                },
1892                vec![],
1893                event_time_u64,
1894                &descriptor,
1895            ),
1896            create_touch_screen_event_with_buttons(
1897                hashmap! {},
1898                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1899                event_time_u64,
1900                &descriptor,
1901            ),
1902            create_touch_screen_event_with_buttons(
1903                hashmap! {},
1904                vec![],
1905                event_time_u64,
1906                &descriptor,
1907            ),
1908        ];
1909
1910        assert_input_report_sequence_generates_events_with_feature_flags!(
1911            input_reports: reports,
1912            expected_events: expected_events,
1913            device_descriptor: descriptor,
1914            device_type: TouchBinding,
1915            feature_flags: input_device::InputPipelineFeatureFlags {
1916                enable_merge_touch_events,
1917                ..Default::default()
1918            },
1919        );
1920    }
1921
1922    #[fasync::run_singlethreaded(test)]
1923    async fn process_reports_batches_events() {
1924        const TOUCH_ID: u32 = 2;
1925
1926        let descriptor =
1927            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1928                device_id: 1,
1929                contacts: vec![],
1930            });
1931        let (event_time_i64, _) = testing_utilities::event_times();
1932
1933        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1934            contact_id: Some(TOUCH_ID),
1935            position_x: Some(0),
1936            position_y: Some(0),
1937            ..Default::default()
1938        };
1939        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1940            contact_id: Some(TOUCH_ID),
1941            position_x: Some(10),
1942            position_y: Some(10),
1943            ..Default::default()
1944        };
1945        let reports = vec![
1946            create_touch_input_report(vec![contact1], None, event_time_i64),
1947            create_touch_input_report(vec![contact2], None, event_time_i64),
1948        ];
1949
1950        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1951
1952        let inspector = fuchsia_inspect::Inspector::default();
1953        let test_node = inspector.root().create_child("TestDevice_Touch");
1954        let mut inspect_status = InputDeviceStatus::new(test_node);
1955        inspect_status.health_node.set_ok();
1956
1957        let _ = TouchBinding::process_reports(
1958            reports,
1959            None,
1960            &descriptor,
1961            &mut event_sender,
1962            &inspect_status,
1963            &metrics::MetricsLogger::default(),
1964            &input_device::InputPipelineFeatureFlags::default(),
1965        );
1966
1967        // Expect EXACTLY one batch containing two events.
1968        let batch = event_receiver.try_next().expect("Expected a batch of events");
1969        let events = batch.expect("Expected events in the batch");
1970        assert_eq!(events.len(), 2);
1971
1972        // Verify no more batches.
1973        assert!(event_receiver.try_next().is_err());
1974    }
1975
1976    #[fasync::run_singlethreaded(test)]
1977    async fn process_reports_merges_touch_events_when_enabled() {
1978        const TOUCH_ID: u32 = 2;
1979        let descriptor =
1980            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1981                device_id: 1,
1982                contacts: vec![],
1983            });
1984        let (event_time_i64, _) = testing_utilities::event_times();
1985
1986        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1987            contact_id: Some(TOUCH_ID),
1988            position_x: Some(0),
1989            position_y: Some(0),
1990            ..Default::default()
1991        };
1992        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1993            contact_id: Some(TOUCH_ID),
1994            position_x: Some(10),
1995            position_y: Some(10),
1996            ..Default::default()
1997        };
1998        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1999            contact_id: Some(TOUCH_ID),
2000            position_x: Some(20),
2001            position_y: Some(20),
2002            ..Default::default()
2003        };
2004        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2005            contact_id: Some(TOUCH_ID),
2006            position_x: Some(30),
2007            position_y: Some(30),
2008            ..Default::default()
2009        };
2010        let reports = vec![
2011            create_touch_input_report(vec![contact_add], None, event_time_i64),
2012            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2013            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2014            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2015            create_touch_input_report(vec![], None, event_time_i64),
2016        ];
2017
2018        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2019        let inspector = fuchsia_inspect::Inspector::default();
2020        let mut inspect_status =
2021            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2022        inspect_status.health_node.set_ok();
2023
2024        let _ = TouchBinding::process_reports(
2025            reports,
2026            None,
2027            &descriptor,
2028            &mut event_sender,
2029            &inspect_status,
2030            &metrics::MetricsLogger::default(),
2031            &input_device::InputPipelineFeatureFlags {
2032                enable_merge_touch_events: true,
2033                ..Default::default()
2034            },
2035        );
2036
2037        let batch = event_receiver.try_next().unwrap().unwrap();
2038
2039        // Expected events: Add, Move(30), Remove.
2040        assert_eq!(batch.len(), 3);
2041
2042        // Verify Add event
2043        assert_matches!(
2044            &batch[0].device_event,
2045            input_device::InputDeviceEvent::TouchScreen(event)
2046                if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2047        );
2048        // Verify Move event (merged to the last one)
2049        assert_matches!(
2050            &batch[1].device_event,
2051            input_device::InputDeviceEvent::TouchScreen(event)
2052                if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2053        );
2054        // Verify Remove event
2055        assert_matches!(
2056            &batch[2].device_event,
2057            input_device::InputDeviceEvent::TouchScreen(event)
2058                if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2059        );
2060    }
2061
2062    #[fasync::run_singlethreaded(test)]
2063    async fn process_reports_does_not_merge_touch_events_when_disabled() {
2064        const TOUCH_ID: u32 = 2;
2065        let descriptor =
2066            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2067                device_id: 1,
2068                contacts: vec![],
2069            });
2070        let (event_time_i64, _) = testing_utilities::event_times();
2071
2072        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2073            contact_id: Some(TOUCH_ID),
2074            position_x: Some(0),
2075            position_y: Some(0),
2076            ..Default::default()
2077        };
2078        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2079            contact_id: Some(TOUCH_ID),
2080            position_x: Some(10),
2081            position_y: Some(10),
2082            ..Default::default()
2083        };
2084        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2085            contact_id: Some(TOUCH_ID),
2086            position_x: Some(20),
2087            position_y: Some(20),
2088            ..Default::default()
2089        };
2090        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2091            contact_id: Some(TOUCH_ID),
2092            position_x: Some(30),
2093            position_y: Some(30),
2094            ..Default::default()
2095        };
2096        let reports = vec![
2097            create_touch_input_report(vec![contact_add], None, event_time_i64),
2098            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2099            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2100            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2101            create_touch_input_report(vec![], None, event_time_i64),
2102        ];
2103
2104        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2105        let inspector = fuchsia_inspect::Inspector::default();
2106        let mut inspect_status =
2107            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2108        inspect_status.health_node.set_ok();
2109
2110        let _ = TouchBinding::process_reports(
2111            reports,
2112            None,
2113            &descriptor,
2114            &mut event_sender,
2115            &inspect_status,
2116            &metrics::MetricsLogger::default(),
2117            &input_device::InputPipelineFeatureFlags {
2118                enable_merge_touch_events: false,
2119                ..Default::default()
2120            },
2121        );
2122
2123        let batch = event_receiver.try_next().unwrap().unwrap();
2124
2125        // Expected events: Add, Move(10), Move(20), Move(30), Remove.
2126        assert_eq!(batch.len(), 5);
2127    }
2128}