input_pipeline/
touch_binding.rs

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