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