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