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;
20use maplit::hashmap;
21use metrics_registry::*;
22use std::collections::{HashMap, HashSet};
23
24/// A [`TouchScreenEvent`] represents a set of contacts and the phase those contacts are in.
25///
26/// For example, when a user touches a touch screen with two fingers, there will be two
27/// [`TouchContact`]s. When a user removes one finger, there will still be two contacts
28/// but one will be reported as removed.
29///
30/// The expected sequence for any given contact is:
31/// 1. [`fidl_fuchsia_ui_input::PointerEventPhase::Add`]
32/// 2. [`fidl_fuchsia_ui_input::PointerEventPhase::Down`]
33/// 3. 0 or more [`fidl_fuchsia_ui_input::PointerEventPhase::Move`]
34/// 4. [`fidl_fuchsia_ui_input::PointerEventPhase::Up`]
35/// 5. [`fidl_fuchsia_ui_input::PointerEventPhase::Remove`]
36///
37/// Additionally, a [`fidl_fuchsia_ui_input::PointerEventPhase::Cancel`] may be sent at any time
38/// signalling that the event is no longer directed towards the receiver.
39#[derive(Debug, PartialEq)]
40pub struct TouchScreenEvent {
41    /// Deprecated. To be removed with https://fxbug.dev/42155652.
42    /// The contacts associated with the touch event. For example, a two-finger touch would result
43    /// in one touch event with two [`TouchContact`]s.
44    ///
45    /// Contacts are grouped based on their current phase (e.g., down, move).
46    pub contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
47
48    /// The contacts associated with the touch event. For example, a two-finger touch would result
49    /// in one touch event with two [`TouchContact`]s.
50    ///
51    /// Contacts are grouped based on their current phase (e.g., add, change).
52    pub injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
53
54    /// Indicates whether any touch buttons are pressed.
55    pub pressed_buttons: Vec<fidl_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: HashSet<mouse_binding::MouseButton>,
158}
159
160impl TouchpadEvent {
161    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
162        let pressed_buttons_node =
163            node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
164        self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
165            pressed_buttons_node.set(i, *button);
166        });
167        node.record(pressed_buttons_node);
168
169        // Populate TouchpadEvent contact details.
170        let contacts_clone = self.injector_contacts.clone();
171        node.record_child("injector_contacts", move |contacts_node| {
172            for contact in contacts_clone.iter() {
173                contacts_node.record_child(contact.id.to_string(), move |contact_node| {
174                    contact_node.record_double("position_x_mm", f64::from(contact.position.x));
175                    contact_node.record_double("position_y_mm", f64::from(contact.position.y));
176                    if let Some(pressure) = contact.pressure {
177                        contact_node.record_int("pressure", pressure);
178                    }
179                    if let Some(contact_size) = contact.contact_size {
180                        contact_node
181                            .record_double("contact_width_mm", f64::from(contact_size.width));
182                        contact_node
183                            .record_double("contact_height_mm", f64::from(contact_size.height));
184                    }
185                })
186            }
187        });
188    }
189}
190
191/// [`TouchDeviceType`] indicates the type of touch device. Both Touch Screen and Windows Precision
192/// Touchpad send touch event from driver but need different process inside input pipeline.
193#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub enum TouchDeviceType {
195    TouchScreen,
196    WindowsPrecisionTouchpad,
197}
198
199/// A [`TouchContact`] represents a single contact (e.g., one touch of a multi-touch gesture) related
200/// to a touch event.
201#[derive(Clone, Copy, Debug, PartialEq)]
202pub struct TouchContact {
203    /// The identifier of the contact. Unique per touch device.
204    pub id: u32,
205
206    /// The position of the touch event, in the units of the associated
207    /// [`ContactDeviceDescriptor`]'s `range`.
208    pub position: Position,
209
210    /// The pressure associated with the contact, in the units of the associated
211    /// [`ContactDeviceDescriptor`]'s `pressure_range`.
212    pub pressure: Option<i64>,
213
214    /// The size of the touch event, in the units of the associated
215    /// [`ContactDeviceDescriptor`]'s `range`.
216    pub contact_size: Option<Size>,
217}
218
219impl Eq for TouchContact {}
220
221impl From<&fidl_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> = batch
689            .iter()
690            .map(|e| {
691                let event = e.clone_with_wake_lease();
692                // Unwrap is safe because trace_id is set when the event is created.
693                // This unwrap will not move the trace_id out of the event because trace_id has Copy trait.
694                let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
695                fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
696                event
697            })
698            .collect();
699        fuchsia_trace::instant!(
700            "input",
701            "events_to_input_handlers",
702            fuchsia_trace::Scope::Thread,
703            "num_reports" => num_reports,
704            "num_events_generated" => events_to_send.len()
705        );
706        match input_event_sender.unbounded_send(events_to_send) {
707            Err(e) => {
708                metrics_logger.log_error(
709                    InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
710                    std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
711                );
712            }
713            _ => {
714                inspect_status.count_generated_events(&batch);
715            }
716        }
717    }
718    (previous_report, None)
719}
720
721fn process_single_touch_screen_report(
722    mut report: InputReport,
723    previous_report: Option<InputReport>,
724    device_descriptor: &input_device::InputDeviceDescriptor,
725    inspect_status: &InputDeviceStatus,
726) -> (Option<InputReport>, Option<InputEvent>) {
727    fuchsia_trace::flow_end!("input", "input_report", report.trace_id.unwrap_or(0).into());
728
729    // Extract the wake_lease early to prevent it from leaking. If this is moved
730    // below an early return, the lease could accidentally be stored inside
731    // `previous_report`, which would prevent the system from suspending.
732    let wake_lease = report.wake_lease.take();
733
734    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
735    let touch_report: &fidl_next_fuchsia_input_report::TouchInputReport = match &report.touch {
736        Some(touch) => touch,
737        None => {
738            inspect_status.count_filtered_report();
739            return (previous_report, None);
740        }
741    };
742
743    let (previous_contacts, previous_buttons): (
744        HashMap<u32, TouchContact>,
745        Vec<fidl_next_fuchsia_input_report::TouchButton>,
746    ) = previous_report
747        .as_ref()
748        .and_then(|unwrapped_report| unwrapped_report.touch.as_ref())
749        .map(touch_contacts_and_buttons_from_touch_report)
750        .unwrap_or_default();
751    let (current_contacts, current_buttons): (
752        HashMap<u32, TouchContact>,
753        Vec<fidl_next_fuchsia_input_report::TouchButton>,
754    ) = touch_contacts_and_buttons_from_touch_report(touch_report);
755
756    // Don't send an event if there are no new contacts or pressed buttons.
757    if previous_contacts.is_empty()
758        && current_contacts.is_empty()
759        && previous_buttons.is_empty()
760        && current_buttons.is_empty()
761    {
762        inspect_status.count_filtered_report();
763        return (Some(report), None);
764    }
765
766    // A touch input report containing button state should persist prior contact state,
767    // for the sake of pointer event continuity in future reports containing contact data.
768    if current_contacts.is_empty()
769        && !previous_contacts.is_empty()
770        && (!current_buttons.is_empty() || !previous_buttons.is_empty())
771    {
772        if let Some(touch_report) = report.touch.as_mut() {
773            touch_report.contacts =
774                previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
775        }
776    }
777
778    // Contacts which exist only in current.
779    let added_contacts: Vec<TouchContact> = Vec::from_iter(
780        current_contacts
781            .values()
782            .cloned()
783            .filter(|contact| !previous_contacts.contains_key(&contact.id)),
784    );
785    // Contacts which exist in both previous and current.
786    let moved_contacts: Vec<TouchContact> = Vec::from_iter(
787        current_contacts
788            .values()
789            .cloned()
790            .filter(|contact| previous_contacts.contains_key(&contact.id)),
791    );
792    // Contacts which exist only in previous.
793    let removed_contacts: Vec<TouchContact> =
794        Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
795            current_buttons.is_empty()
796                && previous_buttons.is_empty()
797                && !current_contacts.contains_key(&contact.id)
798        }));
799
800    let trace_id = fuchsia_trace::Id::random();
801    let event = create_touch_screen_event(
802        hashmap! {
803            fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
804            fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
805            fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
806            fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
807            fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
808        },
809        hashmap! {
810            pointerinjector::EventPhase::Add => added_contacts,
811            pointerinjector::EventPhase::Change => moved_contacts,
812            pointerinjector::EventPhase::Remove => removed_contacts,
813        },
814        current_buttons,
815        device_descriptor,
816        trace_id,
817        wake_lease,
818    );
819
820    (Some(report), Some(event))
821}
822
823fn process_touchpad_reports(
824    reports: Vec<InputReport>,
825    mut previous_report: Option<InputReport>,
826    device_descriptor: &input_device::InputDeviceDescriptor,
827    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
828    inspect_status: &InputDeviceStatus,
829    metrics_logger: &metrics::MetricsLogger,
830) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
831    for report in reports {
832        inspect_status.count_received_report(&report);
833        let prev_report = process_single_touchpad_report(
834            report,
835            previous_report,
836            device_descriptor,
837            input_event_sender,
838            inspect_status,
839            metrics_logger,
840        );
841        previous_report = prev_report;
842    }
843    (previous_report, None)
844}
845
846fn process_single_touchpad_report(
847    report: InputReport,
848    previous_report: Option<InputReport>,
849    device_descriptor: &input_device::InputDeviceDescriptor,
850    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
851    inspect_status: &InputDeviceStatus,
852    metrics_logger: &metrics::MetricsLogger,
853) -> Option<InputReport> {
854    if let Some(trace_id) = report.trace_id {
855        fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
856    }
857
858    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
859    let touch_report: &fidl_next_fuchsia_input_report::TouchInputReport = match &report.touch {
860        Some(touch) => touch,
861        None => {
862            inspect_status.count_filtered_report();
863            return previous_report;
864        }
865    };
866
867    let current_contacts: Vec<TouchContact> = touch_report
868        .contacts
869        .as_ref()
870        .and_then(|unwrapped_contacts| {
871            // Once the contacts are found, convert them into `TouchContact`s.
872            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
873        })
874        .unwrap_or_default();
875
876    let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
877        Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
878            fidl_next_fuchsia_input_report::TouchButton::Palm => Some(1),
879            fidl_next_fuchsia_input_report::TouchButton::SwipeUp
880            | fidl_next_fuchsia_input_report::TouchButton::SwipeLeft
881            | fidl_next_fuchsia_input_report::TouchButton::SwipeRight
882            | fidl_next_fuchsia_input_report::TouchButton::SwipeDown => {
883                // TODO(https://fxbug.dev/487728300): Support swipe buttons.
884                log::warn!("Swipe buttons {:?} are not supported", button);
885                None
886            }
887            fidl_next_fuchsia_input_report::TouchButton::UnknownOrdinal_(unknown_ordinal) => {
888                log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
889                None
890            }
891        })),
892        None => HashSet::new(),
893    };
894
895    let trace_id = fuchsia_trace::Id::random();
896    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
897    send_touchpad_event(
898        current_contacts,
899        buttons,
900        device_descriptor,
901        input_event_sender,
902        trace_id,
903        inspect_status,
904        metrics_logger,
905    );
906
907    Some(report)
908}
909
910fn touch_contacts_and_buttons_from_touch_report(
911    touch_report: &fidl_next_fuchsia_input_report::TouchInputReport,
912) -> (HashMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
913    // First unwrap all the optionals in the input report to get to the contacts.
914    let contacts: Vec<TouchContact> = touch_report
915        .contacts
916        .as_ref()
917        .and_then(|unwrapped_contacts| {
918            // Once the contacts are found, convert them into `TouchContact`s.
919            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
920        })
921        .unwrap_or_default();
922
923    (
924        contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
925        touch_report.pressed_buttons.clone().unwrap_or_default(),
926    )
927}
928
929/// Create a TouchScreenEvent.
930///
931/// # Parameters
932/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
933/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
934///                        pointer events into Scenic.
935/// - `device_descriptor`: The descriptor for the input device generating the input reports.
936/// - `trace_id`: The trace id to distinguish the event.
937/// - `wake_lease`: The wake lease to send with the event.
938fn create_touch_screen_event(
939    contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
940    injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
941    pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
942    device_descriptor: &input_device::InputDeviceDescriptor,
943    trace_id: fuchsia_trace::Id,
944    wake_lease: Option<zx::EventPair>,
945) -> InputEvent {
946    input_device::InputEvent {
947        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
948            contacts,
949            injector_contacts,
950            pressed_buttons,
951            wake_lease,
952        }),
953        device_descriptor: device_descriptor.clone(),
954        event_time: zx::MonotonicInstant::get(),
955        handled: Handled::No,
956        trace_id: Some(trace_id),
957    }
958}
959
960/// Sends a TouchpadEvent over `input_event_sender`.
961///
962/// # Parameters
963/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
964/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
965/// - `device_descriptor`: The descriptor for the input device generating the input reports.
966/// - `input_event_sender`: The sender for the device binding's input event stream.
967fn send_touchpad_event(
968    injector_contacts: Vec<TouchContact>,
969    pressed_buttons: HashSet<mouse_binding::MouseButton>,
970    device_descriptor: &input_device::InputDeviceDescriptor,
971    input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
972    trace_id: fuchsia_trace::Id,
973    inspect_status: &InputDeviceStatus,
974    metrics_logger: &metrics::MetricsLogger,
975) {
976    let event = input_device::InputEvent {
977        device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
978            injector_contacts,
979            pressed_buttons,
980        }),
981        device_descriptor: device_descriptor.clone(),
982        event_time: zx::MonotonicInstant::get(),
983        handled: Handled::No,
984        trace_id: Some(trace_id),
985    };
986
987    match input_event_sender.unbounded_send(vec![event.clone()]) {
988        Err(e) => {
989            metrics_logger.log_error(
990                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
991                std::format!("Failed to send TouchpadEvent with error: {:?}", e),
992            );
993        }
994        _ => inspect_status.count_generated_event(event),
995    }
996}
997
998/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
999///
1000/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
1001/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
1002/// because some touch screen does not report this method.
1003async fn get_device_type(
1004    input_device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
1005) -> TouchDeviceType {
1006    match input_device.get_feature_report().await {
1007        Ok(Ok(fidl_next_fuchsia_input_report::InputDeviceGetFeatureReportResponse {
1008            report: fidl_next_fuchsia_input_report::FeatureReport {
1009                touch:
1010                    Some(fidl_next_fuchsia_input_report::TouchFeatureReport {
1011                        input_mode:
1012                            Some(
1013                                fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection
1014                                | fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
1015                            ),
1016                        ..
1017                    }),
1018                ..
1019            }
1020        })) => TouchDeviceType::WindowsPrecisionTouchpad,
1021        _ => TouchDeviceType::TouchScreen,
1022    }
1023}
1024
1025#[cfg(test)]
1026mod tests {
1027    use super::*;
1028    use crate::testing_utilities::{
1029        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
1030        create_touch_screen_event_with_buttons, create_touchpad_event, spawn_input_stream_handler,
1031    };
1032    use crate::utils::Position;
1033    use assert_matches::assert_matches;
1034    use diagnostics_assertions::AnyProperty;
1035    use fuchsia_async as fasync;
1036    use futures::StreamExt;
1037    use pretty_assertions::assert_eq;
1038    use test_case::test_case;
1039
1040    #[fasync::run_singlethreaded(test)]
1041    async fn process_empty_reports() {
1042        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1043        let previous_report = create_touch_input_report(
1044            vec![],
1045            /* pressed_buttons= */ None,
1046            previous_report_time,
1047        );
1048        let report_time = zx::MonotonicInstant::get().into_nanos();
1049        let report =
1050            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
1051
1052        let descriptor =
1053            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1054                device_id: 1,
1055                contacts: vec![],
1056            });
1057        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1058
1059        let inspector = fuchsia_inspect::Inspector::default();
1060        let test_node = inspector.root().create_child("TestDevice_Touch");
1061        let mut inspect_status = InputDeviceStatus::new(test_node);
1062        inspect_status.health_node.set_ok();
1063
1064        let (returned_report, _) = TouchBinding::process_reports(
1065            vec![report],
1066            Some(previous_report),
1067            &descriptor,
1068            &mut event_sender,
1069            &inspect_status,
1070            &metrics::MetricsLogger::default(),
1071            &input_device::InputPipelineFeatureFlags::default(),
1072        );
1073        assert!(returned_report.is_some());
1074        assert_eq!(returned_report.unwrap().event_time, Some(report_time));
1075
1076        // Assert there are no pending events on the receiver.
1077        let event = event_receiver.try_next();
1078        assert!(event.is_err());
1079
1080        diagnostics_assertions::assert_data_tree!(inspector, root: {
1081            "TestDevice_Touch": contains {
1082                reports_received_count: 1u64,
1083                reports_filtered_count: 1u64,
1084                events_generated: 0u64,
1085                last_received_timestamp_ns: report_time as u64,
1086                last_generated_timestamp_ns: 0u64,
1087                "fuchsia.inspect.Health": {
1088                    status: "OK",
1089                    // Timestamp value is unpredictable and not relevant in this context,
1090                    // so we only assert that the property is present.
1091                    start_timestamp_nanos: AnyProperty
1092                },
1093            }
1094        });
1095    }
1096
1097    // Tests that a input report with a new contact generates an event with an add and a down.
1098    #[fasync::run_singlethreaded(test)]
1099    async fn add_and_down() {
1100        const TOUCH_ID: u32 = 2;
1101
1102        let descriptor =
1103            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1104                device_id: 1,
1105                contacts: vec![],
1106            });
1107        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1108
1109        let contact = fidl_fuchsia_input_report::ContactInputReport {
1110            contact_id: Some(TOUCH_ID),
1111            position_x: Some(0),
1112            position_y: Some(0),
1113            pressure: None,
1114            contact_width: None,
1115            contact_height: None,
1116            ..Default::default()
1117        };
1118        let reports = vec![create_touch_input_report(
1119            vec![contact],
1120            /* pressed_buttons= */ None,
1121            event_time_i64,
1122        )];
1123
1124        let expected_events = vec![create_touch_screen_event(
1125            hashmap! {
1126                fidl_ui_input::PointerEventPhase::Add
1127                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1128                fidl_ui_input::PointerEventPhase::Down
1129                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1130            },
1131            event_time_u64,
1132            &descriptor,
1133        )];
1134
1135        assert_input_report_sequence_generates_events!(
1136            input_reports: reports,
1137            expected_events: expected_events,
1138            device_descriptor: descriptor,
1139            device_type: TouchBinding,
1140        );
1141    }
1142
1143    // Tests that up and remove events are sent when a touch is released.
1144    #[fasync::run_singlethreaded(test)]
1145    async fn up_and_remove() {
1146        const TOUCH_ID: u32 = 2;
1147
1148        let descriptor =
1149            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1150                device_id: 1,
1151                contacts: vec![],
1152            });
1153        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1154
1155        let contact = fidl_fuchsia_input_report::ContactInputReport {
1156            contact_id: Some(TOUCH_ID),
1157            position_x: Some(0),
1158            position_y: Some(0),
1159            pressure: None,
1160            contact_width: None,
1161            contact_height: None,
1162            ..Default::default()
1163        };
1164        let reports = vec![
1165            create_touch_input_report(
1166                vec![contact],
1167                /* pressed_buttons= */ None,
1168                event_time_i64,
1169            ),
1170            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1171        ];
1172
1173        let expected_events = vec![
1174            create_touch_screen_event(
1175                hashmap! {
1176                    fidl_ui_input::PointerEventPhase::Add
1177                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1178                    fidl_ui_input::PointerEventPhase::Down
1179                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1180                },
1181                event_time_u64,
1182                &descriptor,
1183            ),
1184            create_touch_screen_event(
1185                hashmap! {
1186                    fidl_ui_input::PointerEventPhase::Up
1187                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1188                    fidl_ui_input::PointerEventPhase::Remove
1189                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1190                },
1191                event_time_u64,
1192                &descriptor,
1193            ),
1194        ];
1195
1196        assert_input_report_sequence_generates_events!(
1197            input_reports: reports,
1198            expected_events: expected_events,
1199            device_descriptor: descriptor,
1200            device_type: TouchBinding,
1201        );
1202    }
1203
1204    // Tests that a move generates the correct event.
1205    #[fasync::run_singlethreaded(test)]
1206    async fn add_down_move() {
1207        const TOUCH_ID: u32 = 2;
1208        let first = Position { x: 10.0, y: 30.0 };
1209        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1210
1211        let descriptor =
1212            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1213                device_id: 1,
1214                contacts: vec![],
1215            });
1216        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1217
1218        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1219            contact_id: Some(TOUCH_ID),
1220            position_x: Some(first.x as i64),
1221            position_y: Some(first.y as i64),
1222            pressure: None,
1223            contact_width: None,
1224            contact_height: None,
1225            ..Default::default()
1226        };
1227        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1228            contact_id: Some(TOUCH_ID),
1229            position_x: Some(first.x as i64 * 2),
1230            position_y: Some(first.y as i64 * 2),
1231            pressure: None,
1232            contact_width: None,
1233            contact_height: None,
1234            ..Default::default()
1235        };
1236
1237        let reports = vec![
1238            create_touch_input_report(
1239                vec![first_contact],
1240                /* pressed_buttons= */ None,
1241                event_time_i64,
1242            ),
1243            create_touch_input_report(
1244                vec![second_contact],
1245                /* pressed_buttons= */ None,
1246                event_time_i64,
1247            ),
1248        ];
1249
1250        let expected_events = vec![
1251            create_touch_screen_event(
1252                hashmap! {
1253                    fidl_ui_input::PointerEventPhase::Add
1254                        => vec![create_touch_contact(TOUCH_ID, first)],
1255                    fidl_ui_input::PointerEventPhase::Down
1256                        => vec![create_touch_contact(TOUCH_ID, first)],
1257                },
1258                event_time_u64,
1259                &descriptor,
1260            ),
1261            create_touch_screen_event(
1262                hashmap! {
1263                    fidl_ui_input::PointerEventPhase::Move
1264                        => vec![create_touch_contact(TOUCH_ID, second)],
1265                },
1266                event_time_u64,
1267                &descriptor,
1268            ),
1269        ];
1270
1271        assert_input_report_sequence_generates_events!(
1272            input_reports: reports,
1273            expected_events: expected_events,
1274            device_descriptor: descriptor,
1275            device_type: TouchBinding,
1276        );
1277    }
1278
1279    #[fasync::run_singlethreaded(test)]
1280    async fn sent_event_has_trace_id() {
1281        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1282        let previous_report = create_touch_input_report(
1283            vec![],
1284            /* pressed_buttons= */ None,
1285            previous_report_time,
1286        );
1287
1288        let report_time = zx::MonotonicInstant::get().into_nanos();
1289        let contact = fidl_fuchsia_input_report::ContactInputReport {
1290            contact_id: Some(222),
1291            position_x: Some(333),
1292            position_y: Some(444),
1293            ..Default::default()
1294        };
1295        let report =
1296            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1297
1298        let descriptor =
1299            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1300                device_id: 1,
1301                contacts: vec![],
1302            });
1303        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1304
1305        let inspector = fuchsia_inspect::Inspector::default();
1306        let test_node = inspector.root().create_child("TestDevice_Touch");
1307        let mut inspect_status = InputDeviceStatus::new(test_node);
1308        inspect_status.health_node.set_ok();
1309
1310        let _ = TouchBinding::process_reports(
1311            vec![report],
1312            Some(previous_report),
1313            &descriptor,
1314            &mut event_sender,
1315            &inspect_status,
1316            &metrics::MetricsLogger::default(),
1317            &input_device::InputPipelineFeatureFlags::default(),
1318        );
1319        assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1320    }
1321
1322    #[fuchsia::test(allow_stalls = false)]
1323    async fn enables_touchpad_mode_automatically() {
1324        let (set_feature_report_sender, set_feature_report_receiver) =
1325            futures::channel::mpsc::unbounded();
1326        let input_device_proxy = spawn_input_stream_handler(move |input_device_request| {
1327            let set_feature_report_sender = set_feature_report_sender.clone();
1328            async move {
1329                match input_device_request {
1330                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1331                        let _ = responder.send(&get_touchpad_device_descriptor(
1332                            true, /* has_mouse_descriptor */
1333                        ));
1334                    }
1335                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1336                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1337                            touch: Some(fidl_input_report::TouchFeatureReport {
1338                                input_mode: Some(
1339                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1340                                ),
1341                                ..Default::default()
1342                            }),
1343                            ..Default::default()
1344                        }));
1345                    }
1346                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1347                        responder,
1348                        report,
1349                    } => {
1350                        match set_feature_report_sender.unbounded_send(report) {
1351                            Ok(_) => {
1352                                let _ = responder.send(Ok(()));
1353                            }
1354                            Err(e) => {
1355                                panic!("try_send set_feature_report_request failed: {}", e);
1356                            }
1357                        };
1358                    }
1359                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1360                        // Do not panic as `initialize_report_stream()` will call this protocol.
1361                    }
1362                    r => panic!("unsupported request {:?}", r),
1363                }
1364            }
1365        });
1366
1367        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1368
1369        // Create a test inspect node as required by TouchBinding::new()
1370        let inspector = fuchsia_inspect::Inspector::default();
1371        let test_node = inspector.root().create_child("test_node");
1372
1373        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1374        // the binding immediately, so that `set_feature_report_receiver.collect()`
1375        // does not hang.
1376        TouchBinding::new(
1377            input_device_proxy,
1378            0,
1379            device_event_sender,
1380            test_node,
1381            input_device::InputPipelineFeatureFlags::default(),
1382            metrics::MetricsLogger::default(),
1383        )
1384        .await
1385        .unwrap();
1386        assert_matches!(
1387            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1388            [fidl_input_report::FeatureReport {
1389                touch: Some(fidl_input_report::TouchFeatureReport {
1390                    input_mode: Some(
1391                        fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1392                    ),
1393                    ..
1394                }),
1395                ..
1396            }]
1397        );
1398    }
1399
1400    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1401    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1402    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1403    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1404    #[fuchsia::test(allow_stalls = false)]
1405    async fn identifies_correct_touch_device_type(
1406        has_mouse_descriptor: bool,
1407        touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1408        expect_touch_device_type: TouchDeviceType,
1409    ) {
1410        let input_device_proxy =
1411            spawn_input_stream_handler(move |input_device_request| async move {
1412                match input_device_request {
1413                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1414                        let _ =
1415                            responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1416                    }
1417                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1418                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1419                            touch: Some(fidl_input_report::TouchFeatureReport {
1420                                input_mode: touch_input_mode,
1421                                ..Default::default()
1422                            }),
1423                            ..Default::default()
1424                        }));
1425                    }
1426                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1427                        responder, ..
1428                    } => {
1429                        let _ = responder.send(Ok(()));
1430                    }
1431                    r => panic!("unsupported request {:?}", r),
1432                }
1433            });
1434
1435        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1436
1437        // Create a test inspect node as required by TouchBinding::new()
1438        let inspector = fuchsia_inspect::Inspector::default();
1439        let test_node = inspector.root().create_child("test_node");
1440
1441        let binding = TouchBinding::new(
1442            input_device_proxy,
1443            0,
1444            device_event_sender,
1445            test_node,
1446            input_device::InputPipelineFeatureFlags::default(),
1447            metrics::MetricsLogger::default(),
1448        )
1449        .await
1450        .unwrap();
1451        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1452    }
1453
1454    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1455    /// touchpad related tests.
1456    fn get_touchpad_device_descriptor(
1457        has_mouse_descriptor: bool,
1458    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1459        fidl_input_report::DeviceDescriptor {
1460            mouse: match has_mouse_descriptor {
1461                true => Some(fidl_input_report::MouseDescriptor::default()),
1462                false => None,
1463            },
1464            touch: Some(fidl_input_report::TouchDescriptor {
1465                input: Some(fidl_input_report::TouchInputDescriptor {
1466                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1467                        position_x: Some(fidl_input_report::Axis {
1468                            range: fidl_input_report::Range { min: 1, max: 2 },
1469                            unit: fidl_input_report::Unit {
1470                                type_: fidl_input_report::UnitType::None,
1471                                exponent: 0,
1472                            },
1473                        }),
1474                        position_y: Some(fidl_input_report::Axis {
1475                            range: fidl_input_report::Range { min: 2, max: 3 },
1476                            unit: fidl_input_report::Unit {
1477                                type_: fidl_input_report::UnitType::Other,
1478                                exponent: 100000,
1479                            },
1480                        }),
1481                        pressure: Some(fidl_input_report::Axis {
1482                            range: fidl_input_report::Range { min: 3, max: 4 },
1483                            unit: fidl_input_report::Unit {
1484                                type_: fidl_input_report::UnitType::Grams,
1485                                exponent: -991,
1486                            },
1487                        }),
1488                        contact_width: Some(fidl_input_report::Axis {
1489                            range: fidl_input_report::Range { min: 5, max: 6 },
1490                            unit: fidl_input_report::Unit {
1491                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1492                                exponent: 123,
1493                            },
1494                        }),
1495                        contact_height: Some(fidl_input_report::Axis {
1496                            range: fidl_input_report::Range { min: 7, max: 8 },
1497                            unit: fidl_input_report::Unit {
1498                                type_: fidl_input_report::UnitType::Pascals,
1499                                exponent: 100,
1500                            },
1501                        }),
1502                        ..Default::default()
1503                    }]),
1504                    ..Default::default()
1505                }),
1506                ..Default::default()
1507            }),
1508            ..Default::default()
1509        }
1510    }
1511
1512    #[fasync::run_singlethreaded(test)]
1513    async fn send_touchpad_event_button() {
1514        const TOUCH_ID: u32 = 1;
1515        const PRIMARY_BUTTON: u8 = 255;
1516
1517        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1518            device_id: 1,
1519            contacts: vec![],
1520        });
1521        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1522
1523        let contact = fidl_fuchsia_input_report::ContactInputReport {
1524            contact_id: Some(TOUCH_ID),
1525            position_x: Some(0),
1526            position_y: Some(0),
1527            pressure: None,
1528            contact_width: None,
1529            contact_height: None,
1530            ..Default::default()
1531        };
1532        let reports = vec![create_touch_input_report(
1533            vec![contact],
1534            Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1535                unknown_ordinal: PRIMARY_BUTTON,
1536            }]),
1537            event_time_i64,
1538        )];
1539
1540        let expected_events = vec![create_touchpad_event(
1541            vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1542            vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1543                unknown_ordinal: PRIMARY_BUTTON,
1544            }]
1545            .into_iter()
1546            .collect(),
1547            event_time_u64,
1548            &descriptor,
1549        )];
1550
1551        assert_input_report_sequence_generates_events!(
1552            input_reports: reports,
1553            expected_events: expected_events,
1554            device_descriptor: descriptor,
1555            device_type: TouchBinding,
1556        );
1557    }
1558
1559    #[fasync::run_singlethreaded(test)]
1560    async fn send_touchpad_event_2_fingers_down_up() {
1561        const TOUCH_ID_1: u32 = 1;
1562        const TOUCH_ID_2: u32 = 2;
1563
1564        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1565            device_id: 1,
1566            contacts: vec![],
1567        });
1568        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1569
1570        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1571            contact_id: Some(TOUCH_ID_1),
1572            position_x: Some(0),
1573            position_y: Some(0),
1574            pressure: None,
1575            contact_width: None,
1576            contact_height: None,
1577            ..Default::default()
1578        };
1579        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1580            contact_id: Some(TOUCH_ID_2),
1581            position_x: Some(10),
1582            position_y: Some(10),
1583            pressure: None,
1584            contact_width: None,
1585            contact_height: None,
1586            ..Default::default()
1587        };
1588        let reports = vec![
1589            create_touch_input_report(
1590                vec![contact1, contact2],
1591                /* pressed_buttons= */ None,
1592                event_time_i64,
1593            ),
1594            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1595        ];
1596
1597        let expected_events = vec![
1598            create_touchpad_event(
1599                vec![
1600                    create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1601                    create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1602                ],
1603                HashSet::new(),
1604                event_time_u64,
1605                &descriptor,
1606            ),
1607            create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1608        ];
1609
1610        assert_input_report_sequence_generates_events!(
1611            input_reports: reports,
1612            expected_events: expected_events,
1613            device_descriptor: descriptor,
1614            device_type: TouchBinding,
1615        );
1616    }
1617
1618    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1619    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1620    #[fasync::run_singlethreaded(test)]
1621    async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1622        const TOUCH_ID: u32 = 1;
1623
1624        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1625            device_id: 1,
1626            contacts: vec![],
1627        });
1628        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1629
1630        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1631            contact_id: Some(TOUCH_ID),
1632            position_x: Some(p0.x as i64),
1633            position_y: Some(p0.y as i64),
1634            pressure: None,
1635            contact_width: None,
1636            contact_height: None,
1637            ..Default::default()
1638        };
1639        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1640            contact_id: Some(TOUCH_ID),
1641            position_x: Some(p1.x as i64),
1642            position_y: Some(p1.y as i64),
1643            pressure: None,
1644            contact_width: None,
1645            contact_height: None,
1646            ..Default::default()
1647        };
1648        let reports = vec![
1649            create_touch_input_report(
1650                vec![contact1],
1651                /* pressed_buttons= */ None,
1652                event_time_i64,
1653            ),
1654            create_touch_input_report(
1655                vec![contact2],
1656                /* pressed_buttons= */ None,
1657                event_time_i64,
1658            ),
1659        ];
1660
1661        let expected_events = vec![
1662            create_touchpad_event(
1663                vec![create_touch_contact(TOUCH_ID, p0)],
1664                HashSet::new(),
1665                event_time_u64,
1666                &descriptor,
1667            ),
1668            create_touchpad_event(
1669                vec![create_touch_contact(TOUCH_ID, p1)],
1670                HashSet::new(),
1671                event_time_u64,
1672                &descriptor,
1673            ),
1674        ];
1675
1676        assert_input_report_sequence_generates_events!(
1677            input_reports: reports,
1678            expected_events: expected_events,
1679            device_descriptor: descriptor,
1680            device_type: TouchBinding,
1681        );
1682    }
1683
1684    // Tests that a pressed button with no contacts generates an event with the
1685    // button.
1686    #[test_case(true; "merge touch events enabled")]
1687    #[test_case(false; "merge touch events disabled")]
1688    #[fasync::run_singlethreaded(test)]
1689    async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1690        let descriptor =
1691            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1692                device_id: 1,
1693                contacts: vec![],
1694            });
1695        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1696
1697        let reports = vec![create_touch_input_report(
1698            vec![],
1699            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1700            event_time_i64,
1701        )];
1702
1703        let expected_events = vec![create_touch_screen_event_with_buttons(
1704            hashmap! {},
1705            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1706            event_time_u64,
1707            &descriptor,
1708        )];
1709
1710        assert_input_report_sequence_generates_events_with_feature_flags!(
1711            input_reports: reports,
1712            expected_events: expected_events,
1713            device_descriptor: descriptor,
1714            device_type: TouchBinding,
1715            feature_flags: input_device::InputPipelineFeatureFlags {
1716                enable_merge_touch_events,
1717                ..Default::default()
1718            },
1719        );
1720    }
1721
1722    // Tests that a pressed button with a contact generates an event with
1723    // contact and button.
1724    #[test_case(true; "merge touch events enabled")]
1725    #[test_case(false; "merge touch events disabled")]
1726    #[fasync::run_singlethreaded(test)]
1727    async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1728        const TOUCH_ID: u32 = 2;
1729
1730        let descriptor =
1731            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1732                device_id: 1,
1733                contacts: vec![],
1734            });
1735        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1736
1737        let contact = fidl_fuchsia_input_report::ContactInputReport {
1738            contact_id: Some(TOUCH_ID),
1739            position_x: Some(0),
1740            position_y: Some(0),
1741            pressure: None,
1742            contact_width: None,
1743            contact_height: None,
1744            ..Default::default()
1745        };
1746        let reports = vec![create_touch_input_report(
1747            vec![contact],
1748            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1749            event_time_i64,
1750        )];
1751
1752        let expected_events = vec![create_touch_screen_event_with_buttons(
1753            hashmap! {
1754                fidl_ui_input::PointerEventPhase::Add
1755                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1756                fidl_ui_input::PointerEventPhase::Down
1757                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1758            },
1759            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1760            event_time_u64,
1761            &descriptor,
1762        )];
1763
1764        assert_input_report_sequence_generates_events_with_feature_flags!(
1765            input_reports: reports,
1766            expected_events: expected_events,
1767            device_descriptor: descriptor,
1768            device_type: TouchBinding,
1769            feature_flags: input_device::InputPipelineFeatureFlags {
1770                enable_merge_touch_events,
1771                ..Default::default()
1772            },
1773        );
1774    }
1775
1776    // Tests that multiple pressed buttons with contacts generates an event
1777    // with contact and buttons.
1778    #[test_case(true; "merge touch events enabled")]
1779    #[test_case(false; "merge touch events disabled")]
1780    #[fasync::run_singlethreaded(test)]
1781    async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1782        const TOUCH_ID: u32 = 2;
1783
1784        let descriptor =
1785            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1786                device_id: 1,
1787                contacts: vec![],
1788            });
1789        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1790
1791        let contact = fidl_fuchsia_input_report::ContactInputReport {
1792            contact_id: Some(TOUCH_ID),
1793            position_x: Some(0),
1794            position_y: Some(0),
1795            pressure: None,
1796            contact_width: None,
1797            contact_height: None,
1798            ..Default::default()
1799        };
1800        let reports = vec![create_touch_input_report(
1801            vec![contact],
1802            Some(vec![
1803                fidl_fuchsia_input_report::TouchButton::Palm,
1804                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1805            ]),
1806            event_time_i64,
1807        )];
1808
1809        let expected_events = vec![create_touch_screen_event_with_buttons(
1810            hashmap! {
1811                fidl_ui_input::PointerEventPhase::Add
1812                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1813                fidl_ui_input::PointerEventPhase::Down
1814                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1815            },
1816            vec![
1817                fidl_fuchsia_input_report::TouchButton::Palm,
1818                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1819            ],
1820            event_time_u64,
1821            &descriptor,
1822        )];
1823
1824        assert_input_report_sequence_generates_events_with_feature_flags!(
1825            input_reports: reports,
1826            expected_events: expected_events,
1827            device_descriptor: descriptor,
1828            device_type: TouchBinding,
1829            feature_flags: input_device::InputPipelineFeatureFlags {
1830                enable_merge_touch_events,
1831                ..Default::default()
1832            },
1833        );
1834    }
1835
1836    // Tests that no buttons and no contacts generates no events.
1837    #[test_case(true; "merge touch events enabled")]
1838    #[test_case(false; "merge touch events disabled")]
1839    #[fasync::run_singlethreaded(test)]
1840    async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1841        let descriptor =
1842            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1843                device_id: 1,
1844                contacts: vec![],
1845            });
1846        let (event_time_i64, _) = testing_utilities::event_times();
1847
1848        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1849
1850        let expected_events: Vec<input_device::InputEvent> = vec![];
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 a buttons event after a contact event does not remove contacts.
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_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1869        const TOUCH_ID: u32 = 2;
1870
1871        let descriptor =
1872            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1873                device_id: 1,
1874                contacts: vec![],
1875            });
1876        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1877
1878        let contact = fidl_fuchsia_input_report::ContactInputReport {
1879            contact_id: Some(TOUCH_ID),
1880            position_x: Some(0),
1881            position_y: Some(0),
1882            pressure: None,
1883            contact_width: None,
1884            contact_height: None,
1885            ..Default::default()
1886        };
1887        let reports = vec![
1888            create_touch_input_report(vec![contact], None, event_time_i64),
1889            create_touch_input_report(
1890                vec![],
1891                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1892                event_time_i64,
1893            ),
1894            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1895        ];
1896
1897        let expected_events = vec![
1898            create_touch_screen_event_with_buttons(
1899                hashmap! {
1900                    fidl_ui_input::PointerEventPhase::Add
1901                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1902                    fidl_ui_input::PointerEventPhase::Down
1903                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1904                },
1905                vec![],
1906                event_time_u64,
1907                &descriptor,
1908            ),
1909            create_touch_screen_event_with_buttons(
1910                hashmap! {},
1911                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1912                event_time_u64,
1913                &descriptor,
1914            ),
1915            create_touch_screen_event_with_buttons(
1916                hashmap! {},
1917                vec![],
1918                event_time_u64,
1919                &descriptor,
1920            ),
1921        ];
1922
1923        assert_input_report_sequence_generates_events_with_feature_flags!(
1924            input_reports: reports,
1925            expected_events: expected_events,
1926            device_descriptor: descriptor,
1927            device_type: TouchBinding,
1928            feature_flags: input_device::InputPipelineFeatureFlags {
1929                enable_merge_touch_events,
1930                ..Default::default()
1931            },
1932        );
1933    }
1934
1935    #[fasync::run_singlethreaded(test)]
1936    async fn process_reports_batches_events() {
1937        const TOUCH_ID: u32 = 2;
1938
1939        let descriptor =
1940            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1941                device_id: 1,
1942                contacts: vec![],
1943            });
1944        let (event_time_i64, _) = testing_utilities::event_times();
1945
1946        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1947            contact_id: Some(TOUCH_ID),
1948            position_x: Some(0),
1949            position_y: Some(0),
1950            ..Default::default()
1951        };
1952        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1953            contact_id: Some(TOUCH_ID),
1954            position_x: Some(10),
1955            position_y: Some(10),
1956            ..Default::default()
1957        };
1958        let reports = vec![
1959            create_touch_input_report(vec![contact1], None, event_time_i64),
1960            create_touch_input_report(vec![contact2], None, event_time_i64),
1961        ];
1962
1963        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1964
1965        let inspector = fuchsia_inspect::Inspector::default();
1966        let test_node = inspector.root().create_child("TestDevice_Touch");
1967        let mut inspect_status = InputDeviceStatus::new(test_node);
1968        inspect_status.health_node.set_ok();
1969
1970        let _ = TouchBinding::process_reports(
1971            reports,
1972            None,
1973            &descriptor,
1974            &mut event_sender,
1975            &inspect_status,
1976            &metrics::MetricsLogger::default(),
1977            &input_device::InputPipelineFeatureFlags::default(),
1978        );
1979
1980        // Expect EXACTLY one batch containing two events.
1981        let batch = event_receiver.try_next().expect("Expected a batch of events");
1982        let events = batch.expect("Expected events in the batch");
1983        assert_eq!(events.len(), 2);
1984
1985        // Verify no more batches.
1986        assert!(event_receiver.try_next().is_err());
1987    }
1988
1989    #[fasync::run_singlethreaded(test)]
1990    async fn process_reports_merges_touch_events_when_enabled() {
1991        const TOUCH_ID: u32 = 2;
1992        let descriptor =
1993            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1994                device_id: 1,
1995                contacts: vec![],
1996            });
1997        let (event_time_i64, _) = testing_utilities::event_times();
1998
1999        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2000            contact_id: Some(TOUCH_ID),
2001            position_x: Some(0),
2002            position_y: Some(0),
2003            ..Default::default()
2004        };
2005        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2006            contact_id: Some(TOUCH_ID),
2007            position_x: Some(10),
2008            position_y: Some(10),
2009            ..Default::default()
2010        };
2011        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2012            contact_id: Some(TOUCH_ID),
2013            position_x: Some(20),
2014            position_y: Some(20),
2015            ..Default::default()
2016        };
2017        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2018            contact_id: Some(TOUCH_ID),
2019            position_x: Some(30),
2020            position_y: Some(30),
2021            ..Default::default()
2022        };
2023        let reports = vec![
2024            create_touch_input_report(vec![contact_add], None, event_time_i64),
2025            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2026            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2027            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2028            create_touch_input_report(vec![], None, event_time_i64),
2029        ];
2030
2031        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2032        let inspector = fuchsia_inspect::Inspector::default();
2033        let mut inspect_status =
2034            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2035        inspect_status.health_node.set_ok();
2036
2037        let _ = TouchBinding::process_reports(
2038            reports,
2039            None,
2040            &descriptor,
2041            &mut event_sender,
2042            &inspect_status,
2043            &metrics::MetricsLogger::default(),
2044            &input_device::InputPipelineFeatureFlags {
2045                enable_merge_touch_events: true,
2046                ..Default::default()
2047            },
2048        );
2049
2050        let batch = event_receiver.try_next().unwrap().unwrap();
2051
2052        // Expected events: Add, Move(30), Remove.
2053        assert_eq!(batch.len(), 3);
2054
2055        // Verify Add event
2056        assert_matches!(
2057            &batch[0].device_event,
2058            input_device::InputDeviceEvent::TouchScreen(event)
2059                if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
2060        );
2061        // Verify Move event (merged to the last one)
2062        assert_matches!(
2063            &batch[1].device_event,
2064            input_device::InputDeviceEvent::TouchScreen(event)
2065                if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
2066        );
2067        // Verify Remove event
2068        assert_matches!(
2069            &batch[2].device_event,
2070            input_device::InputDeviceEvent::TouchScreen(event)
2071                if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
2072        );
2073    }
2074
2075    #[fasync::run_singlethreaded(test)]
2076    async fn process_reports_does_not_merge_touch_events_when_disabled() {
2077        const TOUCH_ID: u32 = 2;
2078        let descriptor =
2079            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
2080                device_id: 1,
2081                contacts: vec![],
2082            });
2083        let (event_time_i64, _) = testing_utilities::event_times();
2084
2085        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
2086            contact_id: Some(TOUCH_ID),
2087            position_x: Some(0),
2088            position_y: Some(0),
2089            ..Default::default()
2090        };
2091        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
2092            contact_id: Some(TOUCH_ID),
2093            position_x: Some(10),
2094            position_y: Some(10),
2095            ..Default::default()
2096        };
2097        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
2098            contact_id: Some(TOUCH_ID),
2099            position_x: Some(20),
2100            position_y: Some(20),
2101            ..Default::default()
2102        };
2103        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
2104            contact_id: Some(TOUCH_ID),
2105            position_x: Some(30),
2106            position_y: Some(30),
2107            ..Default::default()
2108        };
2109        let reports = vec![
2110            create_touch_input_report(vec![contact_add], None, event_time_i64),
2111            create_touch_input_report(vec![contact_move1], None, event_time_i64),
2112            create_touch_input_report(vec![contact_move2], None, event_time_i64),
2113            create_touch_input_report(vec![contact_move3], None, event_time_i64),
2114            create_touch_input_report(vec![], None, event_time_i64),
2115        ];
2116
2117        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
2118        let inspector = fuchsia_inspect::Inspector::default();
2119        let mut inspect_status =
2120            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
2121        inspect_status.health_node.set_ok();
2122
2123        let _ = TouchBinding::process_reports(
2124            reports,
2125            None,
2126            &descriptor,
2127            &mut event_sender,
2128            &inspect_status,
2129            &metrics::MetricsLogger::default(),
2130            &input_device::InputPipelineFeatureFlags {
2131                enable_merge_touch_events: false,
2132                ..Default::default()
2133            },
2134        );
2135
2136        let batch = event_receiver.try_next().unwrap().unwrap();
2137
2138        // Expected events: Add, Move(10), Move(20), Move(30), Remove.
2139        assert_eq!(batch.len(), 5);
2140    }
2141}