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 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    mut 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    // A touch input report containing button state should persist prior contact state,
584    // for the sake of pointer event continuity in future reports containing contact data.
585    if current_contacts.is_empty()
586        && !previous_contacts.is_empty()
587        && (!current_buttons.is_empty() || !previous_buttons.is_empty())
588    {
589        if let Some(touch_report) = report.touch.as_mut() {
590            touch_report.contacts =
591                previous_report.unwrap().touch.as_ref().unwrap().contacts.clone();
592        }
593    }
594
595    // Contacts which exist only in current.
596    let added_contacts: Vec<TouchContact> = Vec::from_iter(
597        current_contacts
598            .values()
599            .cloned()
600            .filter(|contact| !previous_contacts.contains_key(&contact.id)),
601    );
602    // Contacts which exist in both previous and current.
603    let moved_contacts: Vec<TouchContact> = Vec::from_iter(
604        current_contacts
605            .values()
606            .cloned()
607            .filter(|contact| previous_contacts.contains_key(&contact.id)),
608    );
609    // Contacts which exist only in previous.
610    let removed_contacts: Vec<TouchContact> =
611        Vec::from_iter(previous_contacts.values().cloned().filter(|contact| {
612            current_buttons.is_empty()
613                && previous_buttons.is_empty()
614                && !current_contacts.contains_key(&contact.id)
615        }));
616
617    let trace_id = fuchsia_trace::Id::random();
618    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
619    send_touch_screen_event(
620        hashmap! {
621            fidl_ui_input::PointerEventPhase::Add => added_contacts.clone(),
622            fidl_ui_input::PointerEventPhase::Down => added_contacts.clone(),
623            fidl_ui_input::PointerEventPhase::Move => moved_contacts.clone(),
624            fidl_ui_input::PointerEventPhase::Up => removed_contacts.clone(),
625            fidl_ui_input::PointerEventPhase::Remove => removed_contacts.clone(),
626        },
627        hashmap! {
628            pointerinjector::EventPhase::Add => added_contacts,
629            pointerinjector::EventPhase::Change => moved_contacts,
630            pointerinjector::EventPhase::Remove => removed_contacts,
631        },
632        current_buttons,
633        device_descriptor,
634        input_event_sender,
635        trace_id,
636        inspect_status,
637        metrics_logger,
638    );
639
640    (Some(report), None)
641}
642
643fn process_touchpad_reports(
644    report: InputReport,
645    device_descriptor: &input_device::InputDeviceDescriptor,
646    input_event_sender: &mut UnboundedSender<InputEvent>,
647    inspect_status: &InputDeviceStatus,
648    metrics_logger: &metrics::MetricsLogger,
649) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
650    fuchsia_trace::duration!(c"input", c"touch-binding-process-report");
651    if let Some(trace_id) = report.trace_id {
652        fuchsia_trace::flow_end!(c"input", c"input_report", trace_id.into());
653    }
654
655    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
656    let touch_report: &fidl_fuchsia_input_report::TouchInputReport = match &report.touch {
657        Some(touch) => touch,
658        None => {
659            inspect_status.count_filtered_report();
660            return (None, None);
661        }
662    };
663
664    let current_contacts: Vec<TouchContact> = touch_report
665        .contacts
666        .as_ref()
667        .and_then(|unwrapped_contacts| {
668            // Once the contacts are found, convert them into `TouchContact`s.
669            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
670        })
671        .unwrap_or_default();
672
673    let buttons: HashSet<mouse_binding::MouseButton> = match &touch_report.pressed_buttons {
674        Some(buttons) => HashSet::from_iter(buttons.iter().filter_map(|button| match button {
675            fidl_fuchsia_input_report::TouchButton::Palm => Some(1),
676            fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal } => {
677                log::warn!("unknown TouchButton ordinal {unknown_ordinal:?}");
678                None
679            }
680        })),
681        None => HashSet::new(),
682    };
683
684    let trace_id = fuchsia_trace::Id::random();
685    fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
686    send_touchpad_event(
687        current_contacts,
688        buttons,
689        device_descriptor,
690        input_event_sender,
691        trace_id,
692        inspect_status,
693        metrics_logger,
694    );
695
696    (Some(report), None)
697}
698
699fn touch_contacts_and_buttons_from_touch_report(
700    touch_report: &fidl_fuchsia_input_report::TouchInputReport,
701) -> (HashMap<u32, TouchContact>, Vec<fidl_fuchsia_input_report::TouchButton>) {
702    // First unwrap all the optionals in the input report to get to the contacts.
703    let contacts: Vec<TouchContact> = touch_report
704        .contacts
705        .as_ref()
706        .and_then(|unwrapped_contacts| {
707            // Once the contacts are found, convert them into `TouchContact`s.
708            Some(unwrapped_contacts.iter().map(TouchContact::from).collect())
709        })
710        .unwrap_or_default();
711
712    (
713        contacts.into_iter().map(|contact| (contact.id, contact)).collect(),
714        touch_report.pressed_buttons.clone().unwrap_or_default(),
715    )
716}
717
718/// Sends a TouchScreenEvent over `input_event_sender`.
719///
720/// # Parameters
721/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
722/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
723///                        pointer events into Scenic.
724/// - `device_descriptor`: The descriptor for the input device generating the input reports.
725/// - `input_event_sender`: The sender for the device binding's input event stream.
726fn send_touch_screen_event(
727    contacts: HashMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
728    injector_contacts: HashMap<pointerinjector::EventPhase, Vec<TouchContact>>,
729    pressed_buttons: Vec<fidl_input_report::TouchButton>,
730    device_descriptor: &input_device::InputDeviceDescriptor,
731    input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
732    trace_id: fuchsia_trace::Id,
733    inspect_status: &InputDeviceStatus,
734    metrics_logger: &metrics::MetricsLogger,
735) {
736    let event = input_device::InputEvent {
737        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
738            contacts,
739            injector_contacts,
740            pressed_buttons,
741        }),
742        device_descriptor: device_descriptor.clone(),
743        event_time: zx::MonotonicInstant::get(),
744        handled: Handled::No,
745        trace_id: Some(trace_id),
746    };
747
748    match input_event_sender.unbounded_send(event.clone()) {
749        Err(e) => {
750            metrics_logger.log_error(
751                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
752                std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
753            );
754        }
755        _ => inspect_status.count_generated_event(event),
756    }
757}
758
759/// Sends a TouchpadEvent over `input_event_sender`.
760///
761/// # Parameters
762/// - `injector_contacts`: The contact points relevant to the new TouchpadEvent.
763/// - `pressed_buttons`: The pressing button of the new TouchpadEvent.
764/// - `device_descriptor`: The descriptor for the input device generating the input reports.
765/// - `input_event_sender`: The sender for the device binding's input event stream.
766fn send_touchpad_event(
767    injector_contacts: Vec<TouchContact>,
768    pressed_buttons: HashSet<mouse_binding::MouseButton>,
769    device_descriptor: &input_device::InputDeviceDescriptor,
770    input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
771    trace_id: fuchsia_trace::Id,
772    inspect_status: &InputDeviceStatus,
773    metrics_logger: &metrics::MetricsLogger,
774) {
775    let event = input_device::InputEvent {
776        device_event: input_device::InputDeviceEvent::Touchpad(TouchpadEvent {
777            injector_contacts,
778            pressed_buttons,
779        }),
780        device_descriptor: device_descriptor.clone(),
781        event_time: zx::MonotonicInstant::get(),
782        handled: Handled::No,
783        trace_id: Some(trace_id),
784    };
785
786    match input_event_sender.unbounded_send(event.clone()) {
787        Err(e) => {
788            metrics_logger.log_error(
789                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchpadEvent,
790                std::format!("Failed to send TouchpadEvent with error: {:?}", e),
791            );
792        }
793        _ => inspect_status.count_generated_event(event),
794    }
795}
796
797/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
798///
799/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
800/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
801/// because some touch screen does not report this method.
802async fn get_device_type(input_device: &fidl_input_report::InputDeviceProxy) -> TouchDeviceType {
803    match input_device.get_feature_report().await {
804        Ok(Ok(fidl_input_report::FeatureReport {
805            touch:
806                Some(fidl_input_report::TouchFeatureReport {
807                    input_mode:
808                        Some(
809                            fidl_input_report::TouchConfigurationInputMode::MouseCollection
810                            | fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
811                        ),
812                    ..
813                }),
814            ..
815        })) => TouchDeviceType::WindowsPrecisionTouchpad,
816        _ => TouchDeviceType::TouchScreen,
817    }
818}
819
820#[cfg(test)]
821mod tests {
822    use super::*;
823    use crate::testing_utilities::{
824        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
825        create_touch_screen_event_with_buttons, create_touchpad_event,
826    };
827    use crate::utils::Position;
828    use assert_matches::assert_matches;
829    use diagnostics_assertions::AnyProperty;
830    use fidl_test_util::spawn_stream_handler;
831    use fuchsia_async as fasync;
832    use futures::StreamExt;
833    use pretty_assertions::assert_eq;
834    use test_case::test_case;
835
836    #[fasync::run_singlethreaded(test)]
837    async fn process_empty_reports() {
838        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
839        let previous_report = create_touch_input_report(
840            vec![],
841            /* pressed_buttons= */ None,
842            previous_report_time,
843        );
844        let report_time = zx::MonotonicInstant::get().into_nanos();
845        let report =
846            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
847
848        let descriptor =
849            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
850                device_id: 1,
851                contacts: vec![],
852            });
853        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
854
855        let inspector = fuchsia_inspect::Inspector::default();
856        let test_node = inspector.root().create_child("TestDevice_Touch");
857        let mut inspect_status = InputDeviceStatus::new(test_node);
858        inspect_status.health_node.set_ok();
859
860        let (returned_report, _) = TouchBinding::process_reports(
861            report,
862            Some(previous_report),
863            &descriptor,
864            &mut event_sender,
865            &inspect_status,
866            &metrics::MetricsLogger::default(),
867        );
868        assert!(returned_report.is_some());
869        assert_eq!(returned_report.unwrap().event_time, Some(report_time));
870
871        // Assert there are no pending events on the receiver.
872        let event = event_receiver.try_next();
873        assert!(event.is_err());
874
875        diagnostics_assertions::assert_data_tree!(inspector, root: {
876            "TestDevice_Touch": contains {
877                reports_received_count: 1u64,
878                reports_filtered_count: 1u64,
879                events_generated: 0u64,
880                last_received_timestamp_ns: report_time as u64,
881                last_generated_timestamp_ns: 0u64,
882                "fuchsia.inspect.Health": {
883                    status: "OK",
884                    // Timestamp value is unpredictable and not relevant in this context,
885                    // so we only assert that the property is present.
886                    start_timestamp_nanos: AnyProperty
887                },
888            }
889        });
890    }
891
892    // Tests that a input report with a new contact generates an event with an add and a down.
893    #[fasync::run_singlethreaded(test)]
894    async fn add_and_down() {
895        const TOUCH_ID: u32 = 2;
896
897        let descriptor =
898            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
899                device_id: 1,
900                contacts: vec![],
901            });
902        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
903
904        let contact = fidl_fuchsia_input_report::ContactInputReport {
905            contact_id: Some(TOUCH_ID),
906            position_x: Some(0),
907            position_y: Some(0),
908            pressure: None,
909            contact_width: None,
910            contact_height: None,
911            ..Default::default()
912        };
913        let reports = vec![create_touch_input_report(
914            vec![contact],
915            /* pressed_buttons= */ None,
916            event_time_i64,
917        )];
918
919        let expected_events = vec![create_touch_screen_event(
920            hashmap! {
921                fidl_ui_input::PointerEventPhase::Add
922                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
923                fidl_ui_input::PointerEventPhase::Down
924                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
925            },
926            event_time_u64,
927            &descriptor,
928        )];
929
930        assert_input_report_sequence_generates_events!(
931            input_reports: reports,
932            expected_events: expected_events,
933            device_descriptor: descriptor,
934            device_type: TouchBinding,
935        );
936    }
937
938    // Tests that up and remove events are sent when a touch is released.
939    #[fasync::run_singlethreaded(test)]
940    async fn up_and_remove() {
941        const TOUCH_ID: u32 = 2;
942
943        let descriptor =
944            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
945                device_id: 1,
946                contacts: vec![],
947            });
948        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
949
950        let contact = fidl_fuchsia_input_report::ContactInputReport {
951            contact_id: Some(TOUCH_ID),
952            position_x: Some(0),
953            position_y: Some(0),
954            pressure: None,
955            contact_width: None,
956            contact_height: None,
957            ..Default::default()
958        };
959        let reports = vec![
960            create_touch_input_report(
961                vec![contact],
962                /* pressed_buttons= */ None,
963                event_time_i64,
964            ),
965            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
966        ];
967
968        let expected_events = vec![
969            create_touch_screen_event(
970                hashmap! {
971                    fidl_ui_input::PointerEventPhase::Add
972                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
973                    fidl_ui_input::PointerEventPhase::Down
974                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
975                },
976                event_time_u64,
977                &descriptor,
978            ),
979            create_touch_screen_event(
980                hashmap! {
981                    fidl_ui_input::PointerEventPhase::Up
982                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
983                    fidl_ui_input::PointerEventPhase::Remove
984                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
985                },
986                event_time_u64,
987                &descriptor,
988            ),
989        ];
990
991        assert_input_report_sequence_generates_events!(
992            input_reports: reports,
993            expected_events: expected_events,
994            device_descriptor: descriptor,
995            device_type: TouchBinding,
996        );
997    }
998
999    // Tests that a move generates the correct event.
1000    #[fasync::run_singlethreaded(test)]
1001    async fn add_down_move() {
1002        const TOUCH_ID: u32 = 2;
1003        let first = Position { x: 10.0, y: 30.0 };
1004        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1005
1006        let descriptor =
1007            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1008                device_id: 1,
1009                contacts: vec![],
1010            });
1011        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1012
1013        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1014            contact_id: Some(TOUCH_ID),
1015            position_x: Some(first.x as i64),
1016            position_y: Some(first.y as i64),
1017            pressure: None,
1018            contact_width: None,
1019            contact_height: None,
1020            ..Default::default()
1021        };
1022        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1023            contact_id: Some(TOUCH_ID),
1024            position_x: Some(first.x as i64 * 2),
1025            position_y: Some(first.y as i64 * 2),
1026            pressure: None,
1027            contact_width: None,
1028            contact_height: None,
1029            ..Default::default()
1030        };
1031
1032        let reports = vec![
1033            create_touch_input_report(
1034                vec![first_contact],
1035                /* pressed_buttons= */ None,
1036                event_time_i64,
1037            ),
1038            create_touch_input_report(
1039                vec![second_contact],
1040                /* pressed_buttons= */ None,
1041                event_time_i64,
1042            ),
1043        ];
1044
1045        let expected_events = vec![
1046            create_touch_screen_event(
1047                hashmap! {
1048                    fidl_ui_input::PointerEventPhase::Add
1049                        => vec![create_touch_contact(TOUCH_ID, first)],
1050                    fidl_ui_input::PointerEventPhase::Down
1051                        => vec![create_touch_contact(TOUCH_ID, first)],
1052                },
1053                event_time_u64,
1054                &descriptor,
1055            ),
1056            create_touch_screen_event(
1057                hashmap! {
1058                    fidl_ui_input::PointerEventPhase::Move
1059                        => vec![create_touch_contact(TOUCH_ID, second)],
1060                },
1061                event_time_u64,
1062                &descriptor,
1063            ),
1064        ];
1065
1066        assert_input_report_sequence_generates_events!(
1067            input_reports: reports,
1068            expected_events: expected_events,
1069            device_descriptor: descriptor,
1070            device_type: TouchBinding,
1071        );
1072    }
1073
1074    #[fasync::run_singlethreaded(test)]
1075    async fn sent_event_has_trace_id() {
1076        let previous_report_time = zx::MonotonicInstant::get().into_nanos();
1077        let previous_report = create_touch_input_report(
1078            vec![],
1079            /* pressed_buttons= */ None,
1080            previous_report_time,
1081        );
1082
1083        let report_time = zx::MonotonicInstant::get().into_nanos();
1084        let contact = fidl_fuchsia_input_report::ContactInputReport {
1085            contact_id: Some(222),
1086            position_x: Some(333),
1087            position_y: Some(444),
1088            ..Default::default()
1089        };
1090        let report =
1091            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1092
1093        let descriptor =
1094            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1095                device_id: 1,
1096                contacts: vec![],
1097            });
1098        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1099
1100        let inspector = fuchsia_inspect::Inspector::default();
1101        let test_node = inspector.root().create_child("TestDevice_Touch");
1102        let mut inspect_status = InputDeviceStatus::new(test_node);
1103        inspect_status.health_node.set_ok();
1104
1105        let _ = TouchBinding::process_reports(
1106            report,
1107            Some(previous_report),
1108            &descriptor,
1109            &mut event_sender,
1110            &inspect_status,
1111            &metrics::MetricsLogger::default(),
1112        );
1113        assert_matches!(event_receiver.try_next(), Ok(Some(InputEvent { trace_id: Some(_), .. })));
1114    }
1115
1116    #[fuchsia::test(allow_stalls = false)]
1117    async fn enables_touchpad_mode_automatically() {
1118        let (set_feature_report_sender, set_feature_report_receiver) =
1119            futures::channel::mpsc::unbounded();
1120        let input_device_proxy = spawn_stream_handler(move |input_device_request| {
1121            let set_feature_report_sender = set_feature_report_sender.clone();
1122            async move {
1123                match input_device_request {
1124                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1125                        let _ = responder.send(&get_touchpad_device_descriptor(
1126                            true, /* has_mouse_descriptor */
1127                        ));
1128                    }
1129                    fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1130                        let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1131                            touch: Some(fidl_input_report::TouchFeatureReport {
1132                                input_mode: Some(
1133                                    fidl_input_report::TouchConfigurationInputMode::MouseCollection,
1134                                ),
1135                                ..Default::default()
1136                            }),
1137                            ..Default::default()
1138                        }));
1139                    }
1140                    fidl_input_report::InputDeviceRequest::SetFeatureReport {
1141                        responder,
1142                        report,
1143                    } => {
1144                        match set_feature_report_sender.unbounded_send(report) {
1145                            Ok(_) => {
1146                                let _ = responder.send(Ok(()));
1147                            }
1148                            Err(e) => {
1149                                panic!("try_send set_feature_report_request failed: {}", e);
1150                            }
1151                        };
1152                    }
1153                    fidl_input_report::InputDeviceRequest::GetInputReportsReader { .. } => {
1154                        // Do not panic as `initialize_report_stream()` will call this protocol.
1155                    }
1156                    r => panic!("unsupported request {:?}", r),
1157                }
1158            }
1159        });
1160
1161        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1162
1163        // Create a test inspect node as required by TouchBinding::new()
1164        let inspector = fuchsia_inspect::Inspector::default();
1165        let test_node = inspector.root().create_child("test_node");
1166
1167        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1168        // the binding immediately, so that `set_feature_report_receiver.collect()`
1169        // does not hang.
1170        TouchBinding::new(
1171            input_device_proxy,
1172            0,
1173            device_event_sender,
1174            test_node,
1175            metrics::MetricsLogger::default(),
1176        )
1177        .await
1178        .unwrap();
1179        assert_matches!(
1180            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1181            [fidl_input_report::FeatureReport {
1182                touch: Some(fidl_input_report::TouchFeatureReport {
1183                    input_mode: Some(
1184                        fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1185                    ),
1186                    ..
1187                }),
1188                ..
1189            }]
1190        );
1191    }
1192
1193    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1194    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1195    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1196    #[test_case(true, Some(fidl_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1197    #[fuchsia::test(allow_stalls = false)]
1198    async fn identifies_correct_touch_device_type(
1199        has_mouse_descriptor: bool,
1200        touch_input_mode: Option<fidl_input_report::TouchConfigurationInputMode>,
1201        expect_touch_device_type: TouchDeviceType,
1202    ) {
1203        let input_device_proxy = spawn_stream_handler(move |input_device_request| async move {
1204            match input_device_request {
1205                fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1206                    let _ = responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1207                }
1208                fidl_input_report::InputDeviceRequest::GetFeatureReport { responder } => {
1209                    let _ = responder.send(Ok(&fidl_input_report::FeatureReport {
1210                        touch: Some(fidl_input_report::TouchFeatureReport {
1211                            input_mode: touch_input_mode,
1212                            ..Default::default()
1213                        }),
1214                        ..Default::default()
1215                    }));
1216                }
1217                fidl_input_report::InputDeviceRequest::SetFeatureReport { responder, .. } => {
1218                    let _ = responder.send(Ok(()));
1219                }
1220                r => panic!("unsupported request {:?}", r),
1221            }
1222        });
1223
1224        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1225
1226        // Create a test inspect node as required by TouchBinding::new()
1227        let inspector = fuchsia_inspect::Inspector::default();
1228        let test_node = inspector.root().create_child("test_node");
1229
1230        let binding = TouchBinding::new(
1231            input_device_proxy,
1232            0,
1233            device_event_sender,
1234            test_node,
1235            metrics::MetricsLogger::default(),
1236        )
1237        .await
1238        .unwrap();
1239        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1240    }
1241
1242    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1243    /// touchpad related tests.
1244    fn get_touchpad_device_descriptor(
1245        has_mouse_descriptor: bool,
1246    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1247        fidl_input_report::DeviceDescriptor {
1248            mouse: match has_mouse_descriptor {
1249                true => Some(fidl_input_report::MouseDescriptor::default()),
1250                false => None,
1251            },
1252            touch: Some(fidl_input_report::TouchDescriptor {
1253                input: Some(fidl_input_report::TouchInputDescriptor {
1254                    contacts: Some(vec![fidl_input_report::ContactInputDescriptor {
1255                        position_x: Some(fidl_input_report::Axis {
1256                            range: fidl_input_report::Range { min: 1, max: 2 },
1257                            unit: fidl_input_report::Unit {
1258                                type_: fidl_input_report::UnitType::None,
1259                                exponent: 0,
1260                            },
1261                        }),
1262                        position_y: Some(fidl_input_report::Axis {
1263                            range: fidl_input_report::Range { min: 2, max: 3 },
1264                            unit: fidl_input_report::Unit {
1265                                type_: fidl_input_report::UnitType::Other,
1266                                exponent: 100000,
1267                            },
1268                        }),
1269                        pressure: Some(fidl_input_report::Axis {
1270                            range: fidl_input_report::Range { min: 3, max: 4 },
1271                            unit: fidl_input_report::Unit {
1272                                type_: fidl_input_report::UnitType::Grams,
1273                                exponent: -991,
1274                            },
1275                        }),
1276                        contact_width: Some(fidl_input_report::Axis {
1277                            range: fidl_input_report::Range { min: 5, max: 6 },
1278                            unit: fidl_input_report::Unit {
1279                                type_: fidl_input_report::UnitType::EnglishAngularVelocity,
1280                                exponent: 123,
1281                            },
1282                        }),
1283                        contact_height: Some(fidl_input_report::Axis {
1284                            range: fidl_input_report::Range { min: 7, max: 8 },
1285                            unit: fidl_input_report::Unit {
1286                                type_: fidl_input_report::UnitType::Pascals,
1287                                exponent: 100,
1288                            },
1289                        }),
1290                        ..Default::default()
1291                    }]),
1292                    ..Default::default()
1293                }),
1294                ..Default::default()
1295            }),
1296            ..Default::default()
1297        }
1298    }
1299
1300    #[fasync::run_singlethreaded(test)]
1301    async fn send_touchpad_event_button() {
1302        const TOUCH_ID: u32 = 1;
1303        const PRIMARY_BUTTON: u8 = 1;
1304
1305        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1306            device_id: 1,
1307            contacts: vec![],
1308        });
1309        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1310
1311        let contact = fidl_fuchsia_input_report::ContactInputReport {
1312            contact_id: Some(TOUCH_ID),
1313            position_x: Some(0),
1314            position_y: Some(0),
1315            pressure: None,
1316            contact_width: None,
1317            contact_height: None,
1318            ..Default::default()
1319        };
1320        let reports = vec![create_touch_input_report(
1321            vec![contact],
1322            Some(vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1323                unknown_ordinal: PRIMARY_BUTTON,
1324            }]),
1325            event_time_i64,
1326        )];
1327
1328        let expected_events = vec![create_touchpad_event(
1329            vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1330            vec![fidl_fuchsia_input_report::TouchButton::__SourceBreaking {
1331                unknown_ordinal: PRIMARY_BUTTON,
1332            }]
1333            .into_iter()
1334            .collect(),
1335            event_time_u64,
1336            &descriptor,
1337        )];
1338
1339        assert_input_report_sequence_generates_events!(
1340            input_reports: reports,
1341            expected_events: expected_events,
1342            device_descriptor: descriptor,
1343            device_type: TouchBinding,
1344        );
1345    }
1346
1347    #[fasync::run_singlethreaded(test)]
1348    async fn send_touchpad_event_2_fingers_down_up() {
1349        const TOUCH_ID_1: u32 = 1;
1350        const TOUCH_ID_2: u32 = 2;
1351
1352        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1353            device_id: 1,
1354            contacts: vec![],
1355        });
1356        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1357
1358        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1359            contact_id: Some(TOUCH_ID_1),
1360            position_x: Some(0),
1361            position_y: Some(0),
1362            pressure: None,
1363            contact_width: None,
1364            contact_height: None,
1365            ..Default::default()
1366        };
1367        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1368            contact_id: Some(TOUCH_ID_2),
1369            position_x: Some(10),
1370            position_y: Some(10),
1371            pressure: None,
1372            contact_width: None,
1373            contact_height: None,
1374            ..Default::default()
1375        };
1376        let reports = vec![
1377            create_touch_input_report(
1378                vec![contact1, contact2],
1379                /* pressed_buttons= */ None,
1380                event_time_i64,
1381            ),
1382            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1383        ];
1384
1385        let expected_events = vec![
1386            create_touchpad_event(
1387                vec![
1388                    create_touch_contact(TOUCH_ID_1, Position { x: 0.0, y: 0.0 }),
1389                    create_touch_contact(TOUCH_ID_2, Position { x: 10.0, y: 10.0 }),
1390                ],
1391                HashSet::new(),
1392                event_time_u64,
1393                &descriptor,
1394            ),
1395            create_touchpad_event(vec![], HashSet::new(), event_time_u64, &descriptor),
1396        ];
1397
1398        assert_input_report_sequence_generates_events!(
1399            input_reports: reports,
1400            expected_events: expected_events,
1401            device_descriptor: descriptor,
1402            device_type: TouchBinding,
1403        );
1404    }
1405
1406    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 5.0, y: 5.0}; "down move")]
1407    #[test_case(Position{x: 0.0, y: 0.0}, Position{x: 0.0, y: 0.0}; "down hold")]
1408    #[fasync::run_singlethreaded(test)]
1409    async fn send_touchpad_event_1_finger(p0: Position, p1: Position) {
1410        const TOUCH_ID: u32 = 1;
1411
1412        let descriptor = input_device::InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
1413            device_id: 1,
1414            contacts: vec![],
1415        });
1416        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1417
1418        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1419            contact_id: Some(TOUCH_ID),
1420            position_x: Some(p0.x as i64),
1421            position_y: Some(p0.y as i64),
1422            pressure: None,
1423            contact_width: None,
1424            contact_height: None,
1425            ..Default::default()
1426        };
1427        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1428            contact_id: Some(TOUCH_ID),
1429            position_x: Some(p1.x as i64),
1430            position_y: Some(p1.y as i64),
1431            pressure: None,
1432            contact_width: None,
1433            contact_height: None,
1434            ..Default::default()
1435        };
1436        let reports = vec![
1437            create_touch_input_report(
1438                vec![contact1],
1439                /* pressed_buttons= */ None,
1440                event_time_i64,
1441            ),
1442            create_touch_input_report(
1443                vec![contact2],
1444                /* pressed_buttons= */ None,
1445                event_time_i64,
1446            ),
1447        ];
1448
1449        let expected_events = vec![
1450            create_touchpad_event(
1451                vec![create_touch_contact(TOUCH_ID, p0)],
1452                HashSet::new(),
1453                event_time_u64,
1454                &descriptor,
1455            ),
1456            create_touchpad_event(
1457                vec![create_touch_contact(TOUCH_ID, p1)],
1458                HashSet::new(),
1459                event_time_u64,
1460                &descriptor,
1461            ),
1462        ];
1463
1464        assert_input_report_sequence_generates_events!(
1465            input_reports: reports,
1466            expected_events: expected_events,
1467            device_descriptor: descriptor,
1468            device_type: TouchBinding,
1469        );
1470    }
1471
1472    // Tests that a pressed button with no contacts generates an event with the
1473    // button.
1474    #[fasync::run_singlethreaded(test)]
1475    async fn send_pressed_button_no_contact() {
1476        let descriptor =
1477            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1478                device_id: 1,
1479                contacts: vec![],
1480            });
1481        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1482
1483        let reports = vec![create_touch_input_report(
1484            vec![],
1485            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1486            event_time_i64,
1487        )];
1488
1489        let expected_events = vec![create_touch_screen_event_with_buttons(
1490            hashmap! {},
1491            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1492            event_time_u64,
1493            &descriptor,
1494        )];
1495
1496        assert_input_report_sequence_generates_events!(
1497            input_reports: reports,
1498            expected_events: expected_events,
1499            device_descriptor: descriptor,
1500            device_type: TouchBinding,
1501        );
1502    }
1503
1504    // Tests that a pressed button with a contact generates an event with
1505    // contact and button.
1506    #[fasync::run_singlethreaded(test)]
1507    async fn send_pressed_button_with_contact() {
1508        const TOUCH_ID: u32 = 2;
1509
1510        let descriptor =
1511            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1512                device_id: 1,
1513                contacts: vec![],
1514            });
1515        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1516
1517        let contact = fidl_fuchsia_input_report::ContactInputReport {
1518            contact_id: Some(TOUCH_ID),
1519            position_x: Some(0),
1520            position_y: Some(0),
1521            pressure: None,
1522            contact_width: None,
1523            contact_height: None,
1524            ..Default::default()
1525        };
1526        let reports = vec![create_touch_input_report(
1527            vec![contact],
1528            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1529            event_time_i64,
1530        )];
1531
1532        let expected_events = vec![create_touch_screen_event_with_buttons(
1533            hashmap! {
1534                fidl_ui_input::PointerEventPhase::Add
1535                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1536                fidl_ui_input::PointerEventPhase::Down
1537                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1538            },
1539            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1540            event_time_u64,
1541            &descriptor,
1542        )];
1543
1544        assert_input_report_sequence_generates_events!(
1545            input_reports: reports,
1546            expected_events: expected_events,
1547            device_descriptor: descriptor,
1548            device_type: TouchBinding,
1549        );
1550    }
1551
1552    // Tests that multiple pressed buttons with contacts generates an event
1553    // with contact and buttons.
1554    #[fasync::run_singlethreaded(test)]
1555    async fn send_multiple_pressed_buttons_with_contact() {
1556        const TOUCH_ID: u32 = 2;
1557
1558        let descriptor =
1559            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1560                device_id: 1,
1561                contacts: vec![],
1562            });
1563        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1564
1565        let contact = fidl_fuchsia_input_report::ContactInputReport {
1566            contact_id: Some(TOUCH_ID),
1567            position_x: Some(0),
1568            position_y: Some(0),
1569            pressure: None,
1570            contact_width: None,
1571            contact_height: None,
1572            ..Default::default()
1573        };
1574        let reports = vec![create_touch_input_report(
1575            vec![contact],
1576            Some(vec![
1577                fidl_fuchsia_input_report::TouchButton::Palm,
1578                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1579            ]),
1580            event_time_i64,
1581        )];
1582
1583        let expected_events = vec![create_touch_screen_event_with_buttons(
1584            hashmap! {
1585                fidl_ui_input::PointerEventPhase::Add
1586                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1587                fidl_ui_input::PointerEventPhase::Down
1588                    => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1589            },
1590            vec![
1591                fidl_fuchsia_input_report::TouchButton::Palm,
1592                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1593            ],
1594            event_time_u64,
1595            &descriptor,
1596        )];
1597
1598        assert_input_report_sequence_generates_events!(
1599            input_reports: reports,
1600            expected_events: expected_events,
1601            device_descriptor: descriptor,
1602            device_type: TouchBinding,
1603        );
1604    }
1605
1606    // Tests that no buttons and no contacts generates no events.
1607    #[fasync::run_singlethreaded(test)]
1608    async fn send_no_buttons_no_contacts() {
1609        let descriptor =
1610            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1611                device_id: 1,
1612                contacts: vec![],
1613            });
1614        let (event_time_i64, _) = testing_utilities::event_times();
1615
1616        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1617
1618        let expected_events: Vec<input_device::InputEvent> = vec![];
1619
1620        assert_input_report_sequence_generates_events!(
1621            input_reports: reports,
1622            expected_events: expected_events,
1623            device_descriptor: descriptor,
1624            device_type: TouchBinding,
1625        );
1626    }
1627
1628    // Tests a buttons event after a contact event does not remove contacts.
1629    #[fasync::run_singlethreaded(test)]
1630    async fn send_button_does_not_remove_contacts() {
1631        const TOUCH_ID: u32 = 2;
1632
1633        let descriptor =
1634            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1635                device_id: 1,
1636                contacts: vec![],
1637            });
1638        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1639
1640        let contact = fidl_fuchsia_input_report::ContactInputReport {
1641            contact_id: Some(TOUCH_ID),
1642            position_x: Some(0),
1643            position_y: Some(0),
1644            pressure: None,
1645            contact_width: None,
1646            contact_height: None,
1647            ..Default::default()
1648        };
1649        let reports = vec![
1650            create_touch_input_report(vec![contact], None, event_time_i64),
1651            create_touch_input_report(
1652                vec![],
1653                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1654                event_time_i64,
1655            ),
1656            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1657        ];
1658
1659        let expected_events = vec![
1660            create_touch_screen_event_with_buttons(
1661                hashmap! {
1662                    fidl_ui_input::PointerEventPhase::Add
1663                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1664                    fidl_ui_input::PointerEventPhase::Down
1665                        => vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1666                },
1667                vec![],
1668                event_time_u64,
1669                &descriptor,
1670            ),
1671            create_touch_screen_event_with_buttons(
1672                hashmap! {},
1673                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1674                event_time_u64,
1675                &descriptor,
1676            ),
1677            create_touch_screen_event_with_buttons(
1678                hashmap! {},
1679                vec![],
1680                event_time_u64,
1681                &descriptor,
1682            ),
1683        ];
1684
1685        assert_input_report_sequence_generates_events!(
1686            input_reports: reports,
1687            expected_events: expected_events,
1688            device_descriptor: descriptor,
1689            device_type: TouchBinding,
1690        );
1691    }
1692}