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