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::__SourceBreaking { unknown_ordinal } => {
874                log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
875                None
876            }
877        })),
878        None => HashSet::new(),
879    };
880
881    let trace_id = fuchsia_trace::Id::random();
882    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
883    send_touchpad_event(
884        current_contacts,
885        buttons,
886        device_descriptor,
887        input_event_sender,
888        trace_id,
889        inspect_status,
890        metrics_logger,
891    );
892
893    Some(report)
894}
895
896fn touch_contacts_and_buttons_from_touch_report(
897    touch_report: &fidl_fuchsia_input_report::TouchInputReport,
898) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
899    // First unwrap all the optionals in the input report to get to the contacts.
900    let contacts: Vec<TouchContact> = touch_report
901        .contacts
902        .as_ref()
903        .and_then(|unwrapped_contacts| {
904            // Once the contacts are found, convert them into `TouchContact`s.
905            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
906        })
907        .unwrap_or_default();
908
909    (
910        contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
911        touch_report.pressed_buttons.clone().unwrap_or_default(),
912    )
913}
914
915/// Create a TouchScreenEvent.
916///
917/// # Parameters
918/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
919/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
920///                        pointer events into Scenic.
921/// - `device_descriptor`: The descriptor for the input device generating the input reports.
922/// - `trace_id`: The trace id to distinguish the event.
923/// - `wake_lease`: The wake lease to send with the event.
924fn create_touch_screen_event(
925    contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
926    injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
927    pressed_buttons: Vec<fidl_input_report::TouchButton>,
928    device_descriptor: &input_device::InputDeviceDescriptor,
929    trace_id: fuchsia_trace::Id,
930    wake_lease: Option<zx::EventPair>,
931) -> InputEvent {
932    input_device::InputEvent {
933        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
934            contacts,
935            injector_contacts,
936            pressed_buttons,
937            wake_lease,
938        }),
939        device_descriptor: device_descriptor.clone(),
940        event_time: zx::MonotonicInstant::get(),
941        handled: Handled::No,
942        trace_id: Some(trace_id),
943    }
944}
945
946/// Sends a TouchpadEvent over `input_event_sender`.
947///
948/// # Parameters
949/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
950/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
951/// - `device_descriptor`: The descriptor for the input device generating the input reports.
952/// - `input_event_sender`: The sender for the device binding's input event stream.
953fn send_touchpad_event(
954    injector_contacts: Vec<TouchContact>,
955    pressed_buttons: HashSet<mouse_binding::MouseButton>,
956    device_descriptor: &input_device::InputDeviceDescriptor,
957    input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
958    trace_id: fuchsia_trace::Id,
959    inspect_status: &InputDeviceStatus,
960    metrics_logger: &metrics::MetricsLogger,
961) {
962    let event = input_device::InputEvent {
963        device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
964            injector_contacts,
965            pressed_buttons,
966        }),
967        device_descriptor: device_descriptor.clone(),
968        event_time: zx::MonotonicInstant::get(),
969        handled: Handled::No,
970        trace_id: Some(trace_id),
971    };
972
973    match input_event_sender.unbounded_send(vec![event.clone()]) {
974        Err(e) => {
975            metrics_logger.log_error(
976                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
977                std::format!("Failed to send TouchpadEvent with error: {:?}", e),
978            );
979        }
980        _ => inspect_status.count_generated_event(event),
981    }
982}
983
984/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
985///
986/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
987/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
988/// because some touch screen does not report this method.
989async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
990    match input_device.get_feature_report().await {
991        Ok(Ok(fidl_input_report::FeatureReport {
992            touch:
993                Some(fidl_input_report::TouchFeatureReport {
994                    input_mode:
995                        Some(
996                            fidl_input_report::TouchConfigurationInputMode::MouseCollection
997                            | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
998                        ),
999                    ..
1000                }),
1001            ..
1002        })) => TouchDeviceType::WindowsPrecisionTouchpad,
1003        _ => TouchDeviceType::TouchScreen,
1004    }
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009    use super::*;
1010    use crate::testing_utilities::{
1011        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1012        create_touch_screen_event_with_buttons, create_touchpad_event,
1013    };
1014    use crate::utils::Position;
1015    use assert_matches::assert_matches;
1016    use diagnostics_assertions::AnyProperty;
1017    use fidl_test_util::spawn_stream_handler;
1018    use fuchsia_async as fasync;
1019    use futures::StreamExt;
1020    use pretty_assertions::assert_eq;
1021    use test_case::test_case;
1022
1023    #[fasync::run_singlethreaded(test)]
1024    async fn process_empty_reports() {
1025        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1026        let previous_report = create_touch_input_report(
1027            vec![],
1028            /* pressed_buttons= */ None,
1029            previous_report_time,
1030        );
1031        let report_time = zx::MonotonicInstant::get().into_nanos();
1032        let report =
1033            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
1034
1035        let descriptor =
1036            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1037                device_id: 1,
1038                contacts: vec![],
1039            });
1040        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1041
1042        let inspector = fuchsia_inspect::Inspector::default();
1043        let test_node = inspector.root().create_child("TestDevice_Touch");
1044        let mut inspect_status = InputDeviceStatus::new(test_node);
1045        inspect_status.health_node.set_ok();
1046
1047        let (returned_report, _) = TouchBinding::process_reports(
1048            vec![report],
1049            Some(previous_report),
1050            &descriptor,
1051            &mut event_sender,
1052            &inspect_status,
1053            &metrics::MetricsLogger::default(),
1054            &input_device::InputPipelineFeatureFlags::default(),
1055        );
1056        assert!(returned_report.is_some());
1057        assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1058
1059        // Assert there are no pending events on the receiver.
1060        let event = event_receiver.try_next();
1061        assert!(event.is_err());
1062
1063        diagnostics_assertions::assert_data_tree!(inspector, root: {
1064            "TestDevice_Touch": contains {
1065                reports_received_count: 1u64,
1066                reports_filtered_count: 1u64,
1067                events_generated: 0u64,
1068                last_received_timestamp_ns: report_time as u64,
1069                last_generated_timestamp_ns: 0u64,
1070                "fuchsia.inspect.Health": {
1071                    status: "OK",
1072                    // Timestamp value is unpredictable and not relevant in this context,
1073                    // so we only assert that the property is present.
1074                    start_timestamp_nanos: AnyProperty
1075                },
1076            }
1077        });
1078    }
1079
1080    // Tests that a input report with a new contact generates an event with an add and a down.
1081    #[fasync::run_singlethreaded(test)]
1082    async fn add_and_down() {
1083        const TOUCH_ID: u32 = 2;
1084
1085        let descriptor =
1086            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1087                device_id: 1,
1088                contacts: vec![],
1089            });
1090        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1091
1092        let contact = fidl_fuchsia_input_report::ContactInputReport {
1093            contact_id: Some(TOUCH_ID),
1094            position_x: Some(0),
1095            position_y: Some(0),
1096            pressure: None,
1097            contact_width: None,
1098            contact_height: None,
1099            ..Default::default()
1100        };
1101        let reports = vec![create_touch_input_report(
1102            vec![contact],
1103            /* pressed_buttons= */ None,
1104            event_time_i64,
1105        )];
1106
1107        let expected_events = vec![create_touch_screen_event(
1108            hashmap! {
1109                fidl_ui_input::PointerEventPhase::Add
1110                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1111                fidl_ui_input::PointerEventPhase::Down
1112                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1113            },
1114            event_time_u64,
1115            &descriptor,
1116        )];
1117
1118        assert_input_report_sequence_generates_events!(
1119            input_reports: reports,
1120            expected_events: expected_events,
1121            device_descriptor: descriptor,
1122            device_type: TouchBinding,
1123        );
1124    }
1125
1126    // Tests that up and remove events are sent when a touch is released.
1127    #[fasync::run_singlethreaded(test)]
1128    async fn up_and_remove() {
1129        const TOUCH_ID: u32 = 2;
1130
1131        let descriptor =
1132            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1133                device_id: 1,
1134                contacts: vec![],
1135            });
1136        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1137
1138        let contact = fidl_fuchsia_input_report::ContactInputReport {
1139            contact_id: Some(TOUCH_ID),
1140            position_x: Some(0),
1141            position_y: Some(0),
1142            pressure: None,
1143            contact_width: None,
1144            contact_height: None,
1145            ..Default::default()
1146        };
1147        let reports = vec![
1148            create_touch_input_report(
1149                vec![contact],
1150                /* pressed_buttons= */ None,
1151                event_time_i64,
1152            ),
1153            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1154        ];
1155
1156        let expected_events = vec![
1157            create_touch_screen_event(
1158                hashmap! {
1159                    fidl_ui_input::PointerEventPhase::Add
1160                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1161                    fidl_ui_input::PointerEventPhase::Down
1162                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1163                },
1164                event_time_u64,
1165                &descriptor,
1166            ),
1167            create_touch_screen_event(
1168                hashmap! {
1169                    fidl_ui_input::PointerEventPhase::Up
1170                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1171                    fidl_ui_input::PointerEventPhase::Remove
1172                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1173                },
1174                event_time_u64,
1175                &descriptor,
1176            ),
1177        ];
1178
1179        assert_input_report_sequence_generates_events!(
1180            input_reports: reports,
1181            expected_events: expected_events,
1182            device_descriptor: descriptor,
1183            device_type: TouchBinding,
1184        );
1185    }
1186
1187    // Tests that a move generates the correct event.
1188    #[fasync::run_singlethreaded(test)]
1189    async fn add_down_move() {
1190        const TOUCH_ID: u32 = 2;
1191        let first = Position { x: 10.0, y: 30.0 };
1192        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1193
1194        let descriptor =
1195            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1196                device_id: 1,
1197                contacts: vec![],
1198            });
1199        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1200
1201        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1202            contact_id: Some(TOUCH_ID),
1203            position_x: Some(first.x as i64),
1204            position_y: Some(first.y as i64),
1205            pressure: None,
1206            contact_width: None,
1207            contact_height: None,
1208            ..Default::default()
1209        };
1210        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1211            contact_id: Some(TOUCH_ID),
1212            position_x: Some(first.x as i64 * 2),
1213            position_y: Some(first.y as i64 * 2),
1214            pressure: None,
1215            contact_width: None,
1216            contact_height: None,
1217            ..Default::default()
1218        };
1219
1220        let reports = vec![
1221            create_touch_input_report(
1222                vec![first_contact],
1223                /* pressed_buttons= */ None,
1224                event_time_i64,
1225            ),
1226            create_touch_input_report(
1227                vec![second_contact],
1228                /* pressed_buttons= */ None,
1229                event_time_i64,
1230            ),
1231        ];
1232
1233        let expected_events = vec![
1234            create_touch_screen_event(
1235                hashmap! {
1236                    fidl_ui_input::PointerEventPhase::Add
1237                        => vec![create_touch_contact(TOUCH_ID, first)],
1238                    fidl_ui_input::PointerEventPhase::Down
1239                        => vec![create_touch_contact(TOUCH_ID, first)],
1240                },
1241                event_time_u64,
1242                &descriptor,
1243            ),
1244            create_touch_screen_event(
1245                hashmap! {
1246                    fidl_ui_input::PointerEventPhase::Move
1247                        => vec![create_touch_contact(TOUCH_ID, second)],
1248                },
1249                event_time_u64,
1250                &descriptor,
1251            ),
1252        ];
1253
1254        assert_input_report_sequence_generates_events!(
1255            input_reports: reports,
1256            expected_events: expected_events,
1257            device_descriptor: descriptor,
1258            device_type: TouchBinding,
1259        );
1260    }
1261
1262    #[fasync::run_singlethreaded(test)]
1263    async fn sent_event_has_trace_id() {
1264        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1265        let previous_report = create_touch_input_report(
1266            vec![],
1267            /* pressed_buttons= */ None,
1268            previous_report_time,
1269        );
1270
1271        let report_time = zx::MonotonicInstant::get().into_nanos();
1272        let contact = fidl_fuchsia_input_report::ContactInputReport {
1273            contact_id: Some(222),
1274            position_x: Some(333),
1275            position_y: Some(444),
1276            ..Default::default()
1277        };
1278        let report =
1279            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1280
1281        let descriptor =
1282            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1283                device_id: 1,
1284                contacts: vec![],
1285            });
1286        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1287
1288        let inspector = fuchsia_inspect::Inspector::default();
1289        let test_node = inspector.root().create_child("TestDevice_Touch");
1290        let mut inspect_status = InputDeviceStatus::new(test_node);
1291        inspect_status.health_node.set_ok();
1292
1293        let _ = TouchBinding::process_reports(
1294            vec![report],
1295            Some(previous_report),
1296            &descriptor,
1297            &mut event_sender,
1298            &inspect_status,
1299            &metrics::MetricsLogger::default(),
1300            &input_device::InputPipelineFeatureFlags::default(),
1301        );
1302        assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1303    }
1304
1305    #[fuchsia::test(allow_stalls = false)]
1306    async fn enables_touchpad_mode_automatically() {
1307        let (set_feature_report_sender, set_feature_report_receiver) =
1308            futures::channel::mpsc::unbounded();
1309        let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1310            let set_feature_report_sender = set_feature_report_sender.clone();
1311            async move {
1312                match input_device_request {
1313                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1314                        let _ = responder.send(&get_touchpad_device_descriptor(
1315                            true, /* has_mouse_descriptor */
1316                        ));
1317                    }
1318                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1319                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1320                            touch: Some(fidl_input_report::TouchFeatureReport {
1321                                input_mode: Some(
1322                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1323                                ),
1324                                ..Default::default()
1325                            }),
1326                            ..Default::default()
1327                        }));
1328                    }
1329                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1330                        responder,
1331                        report,
1332                    } => {
1333                        match set_feature_report_sender.unbounded_send(report) {
1334                            Ok(_) => {
1335                                let _ = responder.send(Ok(()));
1336                            }
1337                            Err(e) => {
1338                                panic!("try_send set_feature_report_request failed: {}", e);
1339                            }
1340                        };
1341                    }
1342                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1343                        // Do not panic as `initialize_report_stream()` will call this protocol.
1344                    }
1345                    r => panic!("unsupported request {:?}", r),
1346                }
1347            }
1348        });
1349
1350        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1351
1352        // Create a test inspect node as required by TouchBinding::new()
1353        let inspector = fuchsia_inspect::Inspector::default();
1354        let test_node = inspector.root().create_child("test_node");
1355
1356        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1357        // the binding immediately, so that `set_feature_report_receiver.collect()`
1358        // does not hang.
1359        TouchBinding::new(
1360            input_device_proxy,
1361            0,
1362            device_event_sender,
1363            test_node,
1364            input_device::InputPipelineFeatureFlags::default(),
1365            metrics::MetricsLogger::default(),
1366        )
1367        .await
1368        .unwrap();
1369        assert_matches!(
1370            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1371            [fidl_input_report::FeatureReport {
1372                touch: Some(fidl_input_report::TouchFeatureReport {
1373                    input_mode: Some(
1374                        fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1375                    ),
1376                    ..
1377                }),
1378                ..
1379            }]
1380        );
1381    }
1382
1383    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1384    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1385    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1386    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1387    #[fuchsia::test(allow_stalls = false)]
1388    async fn identifies_correct_touch_device_type(
1389        has_mouse_descriptor: bool,
1390        touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1391        expect_touch_device_type: TouchDeviceType,
1392    ) {
1393        let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1394            match input_device_request {
1395                fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1396                    let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1397                }
1398                fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1399                    let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1400                        touch: Some(fidl_input_report::TouchFeatureReport {
1401                            input_mode: touch_input_mode,
1402                            ..Default::default()
1403                        }),
1404                        ..Default::default()
1405                    }));
1406                }
1407                fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1408                    let _ = responder.send(Ok(()));
1409                }
1410                r => panic!("unsupported request {:?}", r),
1411            }
1412        });
1413
1414        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1415
1416        // Create a test inspect node as required by TouchBinding::new()
1417        let inspector = fuchsia_inspect::Inspector::default();
1418        let test_node = inspector.root().create_child("test_node");
1419
1420        let binding = TouchBinding::new(
1421            input_device_proxy,
1422            0,
1423            device_event_sender,
1424            test_node,
1425            input_device::InputPipelineFeatureFlags::default(),
1426            metrics::MetricsLogger::default(),
1427        )
1428        .await
1429        .unwrap();
1430        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1431    }
1432
1433    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1434    /// touchpad related tests.
1435    fn get_touchpad_device_descriptor(
1436        has_mouse_descriptor: bool,
1437    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1438        fidl_input_report::DeviceDescriptor {
1439            mouse: match has_mouse_descriptor {
1440                true => Some(fidl_input_report::MouseDescriptor::default()),
1441                false => None,
1442            },
1443            touch: Some(fidl_input_report::TouchDescriptor {
1444                input: Some(fidl_input_report::TouchInputDescriptor {
1445                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1446                        position_x: Some(fidl_input_report::Axis {
1447                            range: fidl_input_report::Range { min: 1, max: 2 },
1448                            unit: fidl_input_report::Unit {
1449                                type_: fidl_input_report::UnitType::None,
1450                                exponent: 0,
1451                            },
1452                        }),
1453                        position_y: Some(fidl_input_report::Axis {
1454                            range: fidl_input_report::Range { min: 2, max: 3 },
1455                            unit: fidl_input_report::Unit {
1456                                type_: fidl_input_report::UnitType::Other,
1457                                exponent: 100000,
1458                            },
1459                        }),
1460                        pressure: Some(fidl_input_report::Axis {
1461                            range: fidl_input_report::Range { min: 3, max: 4 },
1462                            unit: fidl_input_report::Unit {
1463                                type_: fidl_input_report::UnitType::Grams,
1464                                exponent: -991,
1465                            },
1466                        }),
1467                        contact_width: Some(fidl_input_report::Axis {
1468                            range: fidl_input_report::Range { min: 5, max: 6 },
1469                            unit: fidl_input_report::Unit {
1470                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1471                                exponent: 123,
1472                            },
1473                        }),
1474                        contact_height: Some(fidl_input_report::Axis {
1475                            range: fidl_input_report::Range { min: 7, max: 8 },
1476                            unit: fidl_input_report::Unit {
1477                                type_: fidl_input_report::UnitType::Pascals,
1478                                exponent: 100,
1479                            },
1480                        }),
1481                        ..Default::default()
1482                    }]),
1483                    ..Default::default()
1484                }),
1485                ..Default::default()
1486            }),
1487            ..Default::default()
1488        }
1489    }
1490
1491    #[fasync::run_singlethreaded(test)]
1492    async fn send_touchpad_event_button() {
1493        const TOUCH_ID: u32 = 1;
1494        const PRIMARY_BUTTON: u8 = 1;
1495
1496        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1497            device_id: 1,
1498            contacts: vec![],
1499        });
1500        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1501
1502        let contact = fidl_fuchsia_input_report::ContactInputReport {
1503            contact_id: Some(TOUCH_ID),
1504            position_x: Some(0),
1505            position_y: Some(0),
1506            pressure: None,
1507            contact_width: None,
1508            contact_height: None,
1509            ..Default::default()
1510        };
1511        let reports = vec![create_touch_input_report(
1512            vec![contact],
1513            Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1514                unknown_ordinal: PRIMARY_BUTTON,
1515            }]),
1516            event_time_i64,
1517        )];
1518
1519        let expected_events = vec![create_touchpad_event(
1520            vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1521            vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1522                unknown_ordinal: PRIMARY_BUTTON,
1523            }]
1524            .into_iter()
1525            .collect(),
1526            event_time_u64,
1527            &descriptor,
1528        )];
1529
1530        assert_input_report_sequence_generates_events!(
1531            input_reports: reports,
1532            expected_events: expected_events,
1533            device_descriptor: descriptor,
1534            device_type: TouchBinding,
1535        );
1536    }
1537
1538    #[fasync::run_singlethreaded(test)]
1539    async fn send_touchpad_event_2_fingers_down_up() {
1540        const TOUCH_ID_1: u32 = 1;
1541        const TOUCH_ID_2: u32 = 2;
1542
1543        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1544            device_id: 1,
1545            contacts: vec![],
1546        });
1547        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1548
1549        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1550            contact_id: Some(TOUCH_ID_1),
1551            position_x: Some(0),
1552            position_y: Some(0),
1553            pressure: None,
1554            contact_width: None,
1555            contact_height: None,
1556            ..Default::default()
1557        };
1558        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1559            contact_id: Some(TOUCH_ID_2),
1560            position_x: Some(10),
1561            position_y: Some(10),
1562            pressure: None,
1563            contact_width: None,
1564            contact_height: None,
1565            ..Default::default()
1566        };
1567        let reports = vec![
1568            create_touch_input_report(
1569                vec![contact1, contact2],
1570                /* pressed_buttons= */ None,
1571                event_time_i64,
1572            ),
1573            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1574        ];
1575
1576        let expected_events = vec![
1577            create_touchpad_event(
1578                vec![
1579                    create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1580                    create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1581                ],
1582                HashSet::new(),
1583                event_time_u64,
1584                &descriptor,
1585            ),
1586            create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1587        ];
1588
1589        assert_input_report_sequence_generates_events!(
1590            input_reports: reports,
1591            expected_events: expected_events,
1592            device_descriptor: descriptor,
1593            device_type: TouchBinding,
1594        );
1595    }
1596
1597    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1598    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1599    #[fasync::run_singlethreaded(test)]
1600    async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1601        const TOUCH_ID: u32 = 1;
1602
1603        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1604            device_id: 1,
1605            contacts: vec![],
1606        });
1607        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1608
1609        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1610            contact_id: Some(TOUCH_ID),
1611            position_x: Some(p0.x as i64),
1612            position_y: Some(p0.y as i64),
1613            pressure: None,
1614            contact_width: None,
1615            contact_height: None,
1616            ..Default::default()
1617        };
1618        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1619            contact_id: Some(TOUCH_ID),
1620            position_x: Some(p1.x as i64),
1621            position_y: Some(p1.y as i64),
1622            pressure: None,
1623            contact_width: None,
1624            contact_height: None,
1625            ..Default::default()
1626        };
1627        let reports = vec![
1628            create_touch_input_report(
1629                vec![contact1],
1630                /* pressed_buttons= */ None,
1631                event_time_i64,
1632            ),
1633            create_touch_input_report(
1634                vec![contact2],
1635                /* pressed_buttons= */ None,
1636                event_time_i64,
1637            ),
1638        ];
1639
1640        let expected_events = vec![
1641            create_touchpad_event(
1642                vec![create_touch_contact(TOUCH_ID, p0)],
1643                HashSet::new(),
1644                event_time_u64,
1645                &descriptor,
1646            ),
1647            create_touchpad_event(
1648                vec![create_touch_contact(TOUCH_ID, p1)],
1649                HashSet::new(),
1650                event_time_u64,
1651                &descriptor,
1652            ),
1653        ];
1654
1655        assert_input_report_sequence_generates_events!(
1656            input_reports: reports,
1657            expected_events: expected_events,
1658            device_descriptor: descriptor,
1659            device_type: TouchBinding,
1660        );
1661    }
1662
1663    // Tests that a pressed button with no contacts generates an event with the
1664    // button.
1665    #[test_case(true; "merge touch events enabled")]
1666    #[test_case(false; "merge touch events disabled")]
1667    #[fasync::run_singlethreaded(test)]
1668    async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1669        let descriptor =
1670            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1671                device_id: 1,
1672                contacts: vec![],
1673            });
1674        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1675
1676        let reports = vec![create_touch_input_report(
1677            vec![],
1678            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1679            event_time_i64,
1680        )];
1681
1682        let expected_events = vec![create_touch_screen_event_with_buttons(
1683            hashmap! {},
1684            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1685            event_time_u64,
1686            &descriptor,
1687        )];
1688
1689        assert_input_report_sequence_generates_events_with_feature_flags!(
1690            input_reports: reports,
1691            expected_events: expected_events,
1692            device_descriptor: descriptor,
1693            device_type: TouchBinding,
1694            feature_flags: input_device::InputPipelineFeatureFlags {
1695                enable_merge_touch_events,
1696                ..Default::default()
1697            },
1698        );
1699    }
1700
1701    // Tests that a pressed button with a contact generates an event with
1702    // contact and button.
1703    #[test_case(true; "merge touch events enabled")]
1704    #[test_case(false; "merge touch events disabled")]
1705    #[fasync::run_singlethreaded(test)]
1706    async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1707        const TOUCH_ID: u32 = 2;
1708
1709        let descriptor =
1710            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1711                device_id: 1,
1712                contacts: vec![],
1713            });
1714        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1715
1716        let contact = fidl_fuchsia_input_report::ContactInputReport {
1717            contact_id: Some(TOUCH_ID),
1718            position_x: Some(0),
1719            position_y: Some(0),
1720            pressure: None,
1721            contact_width: None,
1722            contact_height: None,
1723            ..Default::default()
1724        };
1725        let reports = vec![create_touch_input_report(
1726            vec![contact],
1727            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1728            event_time_i64,
1729        )];
1730
1731        let expected_events = vec![create_touch_screen_event_with_buttons(
1732            hashmap! {
1733                fidl_ui_input::PointerEventPhase::Add
1734                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1735                fidl_ui_input::PointerEventPhase::Down
1736                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1737            },
1738            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1739            event_time_u64,
1740            &descriptor,
1741        )];
1742
1743        assert_input_report_sequence_generates_events_with_feature_flags!(
1744            input_reports: reports,
1745            expected_events: expected_events,
1746            device_descriptor: descriptor,
1747            device_type: TouchBinding,
1748            feature_flags: input_device::InputPipelineFeatureFlags {
1749                enable_merge_touch_events,
1750                ..Default::default()
1751            },
1752        );
1753    }
1754
1755    // Tests that multiple pressed buttons with contacts generates an event
1756    // with contact and buttons.
1757    #[test_case(true; "merge touch events enabled")]
1758    #[test_case(false; "merge touch events disabled")]
1759    #[fasync::run_singlethreaded(test)]
1760    async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1761        const TOUCH_ID: u32 = 2;
1762
1763        let descriptor =
1764            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1765                device_id: 1,
1766                contacts: vec![],
1767            });
1768        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1769
1770        let contact = fidl_fuchsia_input_report::ContactInputReport {
1771            contact_id: Some(TOUCH_ID),
1772            position_x: Some(0),
1773            position_y: Some(0),
1774            pressure: None,
1775            contact_width: None,
1776            contact_height: None,
1777            ..Default::default()
1778        };
1779        let reports = vec![create_touch_input_report(
1780            vec![contact],
1781            Some(vec![
1782                fidl_fuchsia_input_report::TouchButton::Palm,
1783                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1784            ]),
1785            event_time_i64,
1786        )];
1787
1788        let expected_events = vec![create_touch_screen_event_with_buttons(
1789            hashmap! {
1790                fidl_ui_input::PointerEventPhase::Add
1791                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1792                fidl_ui_input::PointerEventPhase::Down
1793                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1794            },
1795            vec![
1796                fidl_fuchsia_input_report::TouchButton::Palm,
1797                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1798            ],
1799            event_time_u64,
1800            &descriptor,
1801        )];
1802
1803        assert_input_report_sequence_generates_events_with_feature_flags!(
1804            input_reports: reports,
1805            expected_events: expected_events,
1806            device_descriptor: descriptor,
1807            device_type: TouchBinding,
1808            feature_flags: input_device::InputPipelineFeatureFlags {
1809                enable_merge_touch_events,
1810                ..Default::default()
1811            },
1812        );
1813    }
1814
1815    // Tests that no buttons and no contacts generates no events.
1816    #[test_case(true; "merge touch events enabled")]
1817    #[test_case(false; "merge touch events disabled")]
1818    #[fasync::run_singlethreaded(test)]
1819    async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1820        let descriptor =
1821            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1822                device_id: 1,
1823                contacts: vec![],
1824            });
1825        let (event_time_i64, _) = testing_utilities::event_times();
1826
1827        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1828
1829        let expected_events: Vec<input_device::InputEvent> = vec![];
1830
1831        assert_input_report_sequence_generates_events_with_feature_flags!(
1832            input_reports: reports,
1833            expected_events: expected_events,
1834            device_descriptor: descriptor,
1835            device_type: TouchBinding,
1836            feature_flags: input_device::InputPipelineFeatureFlags {
1837                enable_merge_touch_events,
1838                ..Default::default()
1839            },
1840        );
1841    }
1842
1843    // Tests a buttons event after a contact event does not remove contacts.
1844    #[test_case(true; "merge touch events enabled")]
1845    #[test_case(false; "merge touch events disabled")]
1846    #[fasync::run_singlethreaded(test)]
1847    async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1848        const TOUCH_ID: u32 = 2;
1849
1850        let descriptor =
1851            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1852                device_id: 1,
1853                contacts: vec![],
1854            });
1855        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1856
1857        let contact = fidl_fuchsia_input_report::ContactInputReport {
1858            contact_id: Some(TOUCH_ID),
1859            position_x: Some(0),
1860            position_y: Some(0),
1861            pressure: None,
1862            contact_width: None,
1863            contact_height: None,
1864            ..Default::default()
1865        };
1866        let reports = vec![
1867            create_touch_input_report(vec![contact], None, event_time_i64),
1868            create_touch_input_report(
1869                vec![],
1870                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1871                event_time_i64,
1872            ),
1873            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1874        ];
1875
1876        let expected_events = vec![
1877            create_touch_screen_event_with_buttons(
1878                hashmap! {
1879                    fidl_ui_input::PointerEventPhase::Add
1880                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1881                    fidl_ui_input::PointerEventPhase::Down
1882                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1883                },
1884                vec![],
1885                event_time_u64,
1886                &descriptor,
1887            ),
1888            create_touch_screen_event_with_buttons(
1889                hashmap! {},
1890                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1891                event_time_u64,
1892                &descriptor,
1893            ),
1894            create_touch_screen_event_with_buttons(
1895                hashmap! {},
1896                vec![],
1897                event_time_u64,
1898                &descriptor,
1899            ),
1900        ];
1901
1902        assert_input_report_sequence_generates_events_with_feature_flags!(
1903            input_reports: reports,
1904            expected_events: expected_events,
1905            device_descriptor: descriptor,
1906            device_type: TouchBinding,
1907            feature_flags: input_device::InputPipelineFeatureFlags {
1908                enable_merge_touch_events,
1909                ..Default::default()
1910            },
1911        );
1912    }
1913
1914    #[fasync::run_singlethreaded(test)]
1915    async fn process_reports_batches_events() {
1916        const TOUCH_ID: u32 = 2;
1917
1918        let descriptor =
1919            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1920                device_id: 1,
1921                contacts: vec![],
1922            });
1923        let (event_time_i64, _) = testing_utilities::event_times();
1924
1925        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1926            contact_id: Some(TOUCH_ID),
1927            position_x: Some(0),
1928            position_y: Some(0),
1929            ..Default::default()
1930        };
1931        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1932            contact_id: Some(TOUCH_ID),
1933            position_x: Some(10),
1934            position_y: Some(10),
1935            ..Default::default()
1936        };
1937        let reports = vec![
1938            create_touch_input_report(vec![contact1], None, event_time_i64),
1939            create_touch_input_report(vec![contact2], None, event_time_i64),
1940        ];
1941
1942        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1943
1944        let inspector = fuchsia_inspect::Inspector::default();
1945        let test_node = inspector.root().create_child("TestDevice_Touch");
1946        let mut inspect_status = InputDeviceStatus::new(test_node);
1947        inspect_status.health_node.set_ok();
1948
1949        let _ = TouchBinding::process_reports(
1950            reports,
1951            None,
1952            &descriptor,
1953            &mut event_sender,
1954            &inspect_status,
1955            &metrics::MetricsLogger::default(),
1956            &input_device::InputPipelineFeatureFlags::default(),
1957        );
1958
1959        // Expect EXACTLY one batch containing two events.
1960        let batch = event_receiver.try_next().expect("Expected a batch of events");
1961        let events = batch.expect("Expected events in the batch");
1962        assert_eq!(events.len(), 2);
1963
1964        // Verify no more batches.
1965        assert!(event_receiver.try_next().is_err());
1966    }
1967
1968    #[fasync::run_singlethreaded(test)]
1969    async fn process_reports_merges_touch_events_when_enabled() {
1970        const TOUCH_ID: u32 = 2;
1971        let descriptor =
1972            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1973                device_id: 1,
1974                contacts: vec![],
1975            });
1976        let (event_time_i64, _) = testing_utilities::event_times();
1977
1978        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1979            contact_id: Some(TOUCH_ID),
1980            position_x: Some(0),
1981            position_y: Some(0),
1982            ..Default::default()
1983        };
1984        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1985            contact_id: Some(TOUCH_ID),
1986            position_x: Some(10),
1987            position_y: Some(10),
1988            ..Default::default()
1989        };
1990        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1991            contact_id: Some(TOUCH_ID),
1992            position_x: Some(20),
1993            position_y: Some(20),
1994            ..Default::default()
1995        };
1996        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1997            contact_id: Some(TOUCH_ID),
1998            position_x: Some(30),
1999            position_y: Some(30),
2000            ..Default::default()
2001        };
2002        let reports = vec![
2003            create_touch_input_report(vec![contact_add], None, event_time_i64),
2004            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2005            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2006            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2007            create_touch_input_report(vec![], None, event_time_i64),
2008        ];
2009
2010        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2011        let inspector = fuchsia_inspect::Inspector::default();
2012        let mut inspect_status =
2013            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2014        inspect_status.health_node.set_ok();
2015
2016        let _ = TouchBinding::process_reports(
2017            reports,
2018            None,
2019            &descriptor,
2020            &mut event_sender,
2021            &inspect_status,
2022            &metrics::MetricsLogger::default(),
2023            &input_device::InputPipelineFeatureFlags {
2024                enable_merge_touch_events: true,
2025                ..Default::default()
2026            },
2027        );
2028
2029        let batch = event_receiver.try_next().unwrap().unwrap();
2030
2031        // Expected events: Add, Move(30), Remove.
2032        assert_eq!(batch.len(), 3);
2033
2034        // Verify Add event
2035        assert_matches!(
2036            &batch[0].device_event,
2037            input_device::InputDeviceEvent::TouchScreen(event)
2038                if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2039        );
2040        // Verify Move event (merged to the last one)
2041        assert_matches!(
2042            &batch[1].device_event,
2043            input_device::InputDeviceEvent::TouchScreen(event)
2044                if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2045        );
2046        // Verify Remove event
2047        assert_matches!(
2048            &batch[2].device_event,
2049            input_device::InputDeviceEvent::TouchScreen(event)
2050                if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2051        );
2052    }
2053
2054    #[fasync::run_singlethreaded(test)]
2055    async fn process_reports_does_not_merge_touch_events_when_disabled() {
2056        const TOUCH_ID: u32 = 2;
2057        let descriptor =
2058            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2059                device_id: 1,
2060                contacts: vec![],
2061            });
2062        let (event_time_i64, _) = testing_utilities::event_times();
2063
2064        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2065            contact_id: Some(TOUCH_ID),
2066            position_x: Some(0),
2067            position_y: Some(0),
2068            ..Default::default()
2069        };
2070        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2071            contact_id: Some(TOUCH_ID),
2072            position_x: Some(10),
2073            position_y: Some(10),
2074            ..Default::default()
2075        };
2076        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2077            contact_id: Some(TOUCH_ID),
2078            position_x: Some(20),
2079            position_y: Some(20),
2080            ..Default::default()
2081        };
2082        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2083            contact_id: Some(TOUCH_ID),
2084            position_x: Some(30),
2085            position_y: Some(30),
2086            ..Default::default()
2087        };
2088        let reports = vec![
2089            create_touch_input_report(vec![contact_add], None, event_time_i64),
2090            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2091            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2092            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2093            create_touch_input_report(vec![], None, event_time_i64),
2094        ];
2095
2096        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2097        let inspector = fuchsia_inspect::Inspector::default();
2098        let mut inspect_status =
2099            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2100        inspect_status.health_node.set_ok();
2101
2102        let _ = TouchBinding::process_reports(
2103            reports,
2104            None,
2105            &descriptor,
2106            &mut event_sender,
2107            &inspect_status,
2108            &metrics::MetricsLogger::default(),
2109            &input_device::InputPipelineFeatureFlags {
2110                enable_merge_touch_events: false,
2111                ..Default::default()
2112            },
2113        );
2114
2115        let batch = event_receiver.try_next().unwrap().unwrap();
2116
2117        // Expected events: Add, Move(10), Move(20), Move(30), Remove.
2118        assert_eq!(batch.len(), 5);
2119    }
2120}