input_pipeline/
mouse_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;
7use crate::{metrics, mouse_model_database};
8use anyhow::{Error, format_err};
9use async_trait::async_trait;
10use fidl::HandleBased;
11use fidl_fuchsia_input_report::{InputDeviceProxy, InputReport};
12use fuchsia_inspect::ArrayProperty;
13use fuchsia_inspect::health::Reporter;
14use fuchsia_sync::Mutex;
15use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
16use metrics_registry::*;
17use std::collections::HashSet;
18use {fidl_fuchsia_input_report as fidl_input_report, zx};
19
20pub type MouseButton = u8;
21
22/// Flag to indicate the scroll event is from device reporting precision delta.
23#[derive(Copy, Clone, Debug, PartialEq)]
24pub enum PrecisionScroll {
25    /// Touchpad and some mouse able to report precision delta.
26    Yes,
27    /// Tick based mouse wheel.
28    No,
29}
30
31/// A [`MouseLocation`] represents the mouse pointer location at the time of a pointer event.
32#[derive(Copy, Clone, Debug, PartialEq)]
33pub enum MouseLocation {
34    /// A mouse movement relative to its current position.
35    Relative(RelativeLocation),
36
37    /// An absolute position, in device coordinates.
38    Absolute(Position),
39}
40
41#[derive(Copy, Clone, Debug, PartialEq)]
42pub enum MousePhase {
43    Down,  // One or more buttons were newly pressed.
44    Move,  // The mouse moved with no change in button state.
45    Up,    // One or more buttons were newly released.
46    Wheel, // Mouse wheel is rotating.
47}
48
49/// A [`RelativeLocation`] contains the relative mouse pointer location at the time of a pointer event.
50#[derive(Copy, Clone, Debug, PartialEq)]
51pub struct RelativeLocation {
52    /// A pointer location in millimeters.
53    pub millimeters: Position,
54}
55
56impl Default for RelativeLocation {
57    fn default() -> Self {
58        RelativeLocation { millimeters: Position::zero() }
59    }
60}
61
62/// [`RawWheelDelta`] is the wheel delta from driver or gesture arena.
63#[derive(Clone, Debug, PartialEq)]
64pub enum RawWheelDelta {
65    /// For tick based mouse wheel, driver will report how many ticks rotated in i64.
66    Ticks(i64),
67    /// For Touchpad, gesture arena will compute how many swipe distance in mm in f32.
68    Millimeters(f32),
69}
70
71/// A [`WheelDelta`] contains raw wheel delta from driver or gesture arena
72/// and scaled wheel delta in physical pixels.
73#[derive(Clone, Debug, PartialEq)]
74
75pub struct WheelDelta {
76    pub raw_data: RawWheelDelta,
77    pub physical_pixel: Option<f32>,
78}
79
80/// A [`MouseEvent`] represents a pointer event with a specified phase, and the buttons
81/// involved in said phase. The supported phases for mice include Up, Down, and Move.
82///
83/// # Example
84/// The following MouseEvent represents a relative movement of 40 units in the x axis
85/// and 20 units in the y axis while holding the primary button (1) down.
86///
87/// ```
88/// let mouse_device_event = input_device::InputDeviceEvent::Mouse(MouseEvent::new(
89///     MouseLocation::Relative(RelativePosition {
90///       millimeters: Position { x: 4.0, y: 2.0 },
91///     }),
92///     Some(1),
93///     Some(1),
94///     MousePhase::Move,
95///     HashSet::from_iter(vec![1]).into_iter()),
96///     HashSet::from_iter(vec![1]).into_iter()),,
97///     None, // wake_lease
98/// ));
99/// ```
100#[derive(Debug)]
101pub struct MouseEvent {
102    /// The mouse location.
103    pub location: MouseLocation,
104
105    /// The mouse wheel rotated delta in vertical.
106    pub wheel_delta_v: Option<WheelDelta>,
107
108    /// The mouse wheel rotated delta in horizontal.
109    pub wheel_delta_h: Option<WheelDelta>,
110
111    /// The mouse device reports precision scroll delta.
112    pub is_precision_scroll: Option<PrecisionScroll>,
113
114    /// The phase of the [`buttons`] associated with this input event.
115    pub phase: MousePhase,
116
117    /// The buttons relevant to this event.
118    pub affected_buttons: HashSet<MouseButton>,
119
120    /// The complete button state including this event.
121    pub pressed_buttons: HashSet<MouseButton>,
122
123    /// The wake lease for this event.
124    pub wake_lease: Mutex<Option<zx::EventPair>>,
125}
126
127impl Clone for MouseEvent {
128    fn clone(&self) -> Self {
129        log::debug!("MouseEvent cloned without wake lease.");
130        Self {
131            location: self.location,
132            wheel_delta_v: self.wheel_delta_v.clone(),
133            wheel_delta_h: self.wheel_delta_h.clone(),
134            is_precision_scroll: self.is_precision_scroll,
135            phase: self.phase,
136            affected_buttons: self.affected_buttons.clone(),
137            pressed_buttons: self.pressed_buttons.clone(),
138            wake_lease: None.into(),
139        }
140    }
141}
142
143impl PartialEq for MouseEvent {
144    fn eq(&self, other: &Self) -> bool {
145        self.location == other.location
146            && self.wheel_delta_v == other.wheel_delta_v
147            && self.wheel_delta_h == other.wheel_delta_h
148            && self.is_precision_scroll == other.is_precision_scroll
149            && self.phase == other.phase
150            && self.affected_buttons == other.affected_buttons
151            && self.pressed_buttons == other.pressed_buttons
152    }
153}
154
155impl MouseEvent {
156    /// Creates a new [`MouseEvent`].
157    ///
158    /// # Parameters
159    /// - `location`: The mouse location.
160    /// - `phase`: The phase of the [`buttons`] associated with this input event.
161    /// - `buttons`: The buttons relevant to this event.
162    /// - `wake_lease`: The wake lease for this event.
163    pub fn new(
164        location: MouseLocation,
165        wheel_delta_v: Option<WheelDelta>,
166        wheel_delta_h: Option<WheelDelta>,
167        phase: MousePhase,
168        affected_buttons: HashSet<MouseButton>,
169        pressed_buttons: HashSet<MouseButton>,
170        is_precision_scroll: Option<PrecisionScroll>,
171        wake_lease: Option<zx::EventPair>,
172    ) -> MouseEvent {
173        MouseEvent {
174            location,
175            wheel_delta_v,
176            wheel_delta_h,
177            phase,
178            affected_buttons,
179            pressed_buttons,
180            is_precision_scroll,
181            wake_lease: Mutex::new(wake_lease),
182        }
183    }
184
185    pub fn clone_with_wake_lease(&self) -> Self {
186        log::debug!("MouseEvent cloned with wake lease: {:?}", self.wake_lease);
187        Self {
188            location: self.location,
189            wheel_delta_v: self.wheel_delta_v.clone(),
190            wheel_delta_h: self.wheel_delta_h.clone(),
191            is_precision_scroll: self.is_precision_scroll,
192            phase: self.phase,
193            affected_buttons: self.affected_buttons.clone(),
194            pressed_buttons: self.pressed_buttons.clone(),
195            wake_lease: Mutex::new(self.wake_lease.lock().as_ref().map(|lease| {
196                lease
197                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
198                    .expect("failed to duplicate event pair")
199            })),
200        }
201    }
202
203    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
204        match self.location {
205            MouseLocation::Relative(pos) => {
206                node.record_child("location_relative", move |location_node| {
207                    location_node.record_double("x", f64::from(pos.millimeters.x));
208                    location_node.record_double("y", f64::from(pos.millimeters.y));
209                })
210            }
211            MouseLocation::Absolute(pos) => {
212                node.record_child("location_absolute", move |location_node| {
213                    location_node.record_double("x", f64::from(pos.x));
214                    location_node.record_double("y", f64::from(pos.y));
215                })
216            }
217        };
218
219        if let Some(wheel_delta_v) = &self.wheel_delta_v {
220            node.record_child("wheel_delta_v", move |wheel_delta_v_node| {
221                match wheel_delta_v.raw_data {
222                    RawWheelDelta::Ticks(ticks) => wheel_delta_v_node.record_int("ticks", ticks),
223                    RawWheelDelta::Millimeters(mm) => {
224                        wheel_delta_v_node.record_double("millimeters", f64::from(mm))
225                    }
226                }
227                if let Some(physical_pixel) = wheel_delta_v.physical_pixel {
228                    wheel_delta_v_node.record_double("physical_pixel", f64::from(physical_pixel));
229                }
230            });
231        }
232
233        if let Some(wheel_delta_h) = &self.wheel_delta_h {
234            node.record_child("wheel_delta_h", move |wheel_delta_h_node| {
235                match wheel_delta_h.raw_data {
236                    RawWheelDelta::Ticks(ticks) => wheel_delta_h_node.record_int("ticks", ticks),
237                    RawWheelDelta::Millimeters(mm) => {
238                        wheel_delta_h_node.record_double("millimeters", f64::from(mm))
239                    }
240                }
241                if let Some(physical_pixel) = wheel_delta_h.physical_pixel {
242                    wheel_delta_h_node.record_double("physical_pixel", f64::from(physical_pixel));
243                }
244            });
245        }
246
247        if let Some(is_precision_scroll) = self.is_precision_scroll {
248            match is_precision_scroll {
249                PrecisionScroll::Yes => node.record_string("is_precision_scroll", "yes"),
250                PrecisionScroll::No => node.record_string("is_precision_scroll", "no"),
251            }
252        }
253
254        match self.phase {
255            MousePhase::Down => node.record_string("phase", "down"),
256            MousePhase::Move => node.record_string("phase", "move"),
257            MousePhase::Up => node.record_string("phase", "up"),
258            MousePhase::Wheel => node.record_string("phase", "wheel"),
259        }
260
261        let affected_buttons_node =
262            node.create_uint_array("affected_buttons", self.affected_buttons.len());
263        self.affected_buttons.iter().enumerate().for_each(|(i, button)| {
264            affected_buttons_node.set(i, *button);
265        });
266        node.record(affected_buttons_node);
267
268        let pressed_buttons_node =
269            node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
270        self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
271            pressed_buttons_node.set(i, *button);
272        });
273        node.record(pressed_buttons_node);
274    }
275}
276
277/// A [`MouseBinding`] represents a connection to a mouse input device.
278///
279/// The [`MouseBinding`] parses and exposes mouse descriptor properties (e.g., the range of
280/// possible x values) for the device it is associated with. It also parses [`InputReport`]s
281/// from the device, and sends them to the device binding owner over `event_sender`.
282pub struct MouseBinding {
283    /// The channel to stream InputEvents to.
284    event_sender: UnboundedSender<input_device::InputEvent>,
285
286    /// Holds information about this device.
287    device_descriptor: MouseDeviceDescriptor,
288}
289
290#[derive(Clone, Debug, Eq, PartialEq)]
291pub struct MouseDeviceDescriptor {
292    /// The id of the connected mouse input device.
293    pub device_id: u32,
294
295    /// The range of possible x values of absolute mouse positions reported by this device.
296    pub absolute_x_range: Option<fidl_input_report::Range>,
297
298    /// The range of possible y values of absolute mouse positions reported by this device.
299    pub absolute_y_range: Option<fidl_input_report::Range>,
300
301    /// The range of possible vertical wheel delta reported by this device.
302    pub wheel_v_range: Option<fidl_input_report::Axis>,
303
304    /// The range of possible horizontal wheel delta reported by this device.
305    pub wheel_h_range: Option<fidl_input_report::Axis>,
306
307    /// This is a vector of ids for the mouse buttons.
308    pub buttons: Option<Vec<MouseButton>>,
309
310    /// This is the conversion factor between counts and millimeters for the
311    /// connected mouse input device.
312    pub counts_per_mm: u32,
313}
314
315#[async_trait]
316impl input_device::InputDeviceBinding for MouseBinding {
317    fn input_event_sender(&self) -> UnboundedSender<input_device::InputEvent> {
318        self.event_sender.clone()
319    }
320
321    fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
322        input_device::InputDeviceDescriptor::Mouse(self.device_descriptor.clone())
323    }
324}
325
326impl MouseBinding {
327    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
328    ///
329    /// The binding will start listening for input reports immediately and send new InputEvents
330    /// to the device binding owner over `input_event_sender`.
331    ///
332    /// # Parameters
333    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
334    /// - `device_id`: The id of the connected mouse device.
335    /// - `input_event_sender`: The channel to send new InputEvents to.
336    /// - `device_node`: The inspect node for this device binding
337    /// - `metrics_logger`: The metrics logger.
338    ///
339    /// # Errors
340    /// If there was an error binding to the proxy.
341    pub async fn new(
342        device_proxy: InputDeviceProxy,
343        device_id: u32,
344        input_event_sender: UnboundedSender<input_device::InputEvent>,
345        device_node: fuchsia_inspect::Node,
346        metrics_logger: metrics::MetricsLogger,
347    ) -> Result<Self, Error> {
348        let (device_binding, mut inspect_status) =
349            Self::bind_device(&device_proxy, device_id, input_event_sender, device_node).await?;
350        inspect_status.health_node.set_ok();
351        input_device::initialize_report_stream(
352            device_proxy,
353            device_binding.get_device_descriptor(),
354            device_binding.input_event_sender(),
355            inspect_status,
356            metrics_logger,
357            Self::process_reports,
358        );
359
360        Ok(device_binding)
361    }
362
363    /// Binds the provided input device to a new instance of `Self`.
364    ///
365    /// # Parameters
366    /// - `device`: The device to use to initialize the binding.
367    /// - `device_id`: The id of the connected mouse device.
368    /// - `input_event_sender`: The channel to send new InputEvents to.
369    /// - `device_node`: The inspect node for this device binding
370    ///
371    /// # Errors
372    /// If the device descriptor could not be retrieved, or the descriptor could
373    /// not be parsed correctly.
374    async fn bind_device(
375        device: &InputDeviceProxy,
376        device_id: u32,
377        input_event_sender: UnboundedSender<input_device::InputEvent>,
378        device_node: fuchsia_inspect::Node,
379    ) -> Result<(Self, InputDeviceStatus), Error> {
380        let mut input_device_status = InputDeviceStatus::new(device_node);
381        let device_descriptor: fidl_input_report::DeviceDescriptor = match device
382            .get_descriptor()
383            .await
384        {
385            Ok(descriptor) => descriptor,
386            Err(_) => {
387                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
388                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
389            }
390        };
391
392        let mouse_descriptor = device_descriptor.mouse.ok_or_else(|| {
393            input_device_status
394                .health_node
395                .set_unhealthy("DeviceDescriptor does not have a MouseDescriptor.");
396            format_err!("DeviceDescriptor does not have a MouseDescriptor")
397        })?;
398
399        let mouse_input_descriptor = mouse_descriptor.input.ok_or_else(|| {
400            input_device_status
401                .health_node
402                .set_unhealthy("MouseDescriptor does not have a MouseInputDescriptor.");
403            format_err!("MouseDescriptor does not have a MouseInputDescriptor")
404        })?;
405
406        let model = mouse_model_database::db::get_mouse_model(device_descriptor.device_information);
407
408        let device_descriptor: MouseDeviceDescriptor = MouseDeviceDescriptor {
409            device_id,
410            absolute_x_range: mouse_input_descriptor.position_x.map(|axis| axis.range),
411            absolute_y_range: mouse_input_descriptor.position_y.map(|axis| axis.range),
412            wheel_v_range: mouse_input_descriptor.scroll_v,
413            wheel_h_range: mouse_input_descriptor.scroll_h,
414            buttons: mouse_input_descriptor.buttons,
415            counts_per_mm: model.counts_per_mm,
416        };
417
418        Ok((
419            MouseBinding { event_sender: input_event_sender, device_descriptor },
420            input_device_status,
421        ))
422    }
423
424    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
425    ///
426    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
427    ///
428    /// # Parameters
429    /// `reports`: The incoming [`InputReport`].
430    /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be
431    ///                    used to determine, for example, which keys are no longer present in
432    ///                    a keyboard report to generate key released events. If `None`, no
433    ///                    previous report was found.
434    /// `device_descriptor`: The descriptor for the input device generating the input reports.
435    /// `input_event_sender`: The sender for the device binding's input event stream.
436    ///
437    /// # Returns
438    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
439    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
440    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
441    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
442    /// binding does not generate InputEvents asynchronously, this will be `None`.
443    fn process_reports(
444        reports: Vec<InputReport>,
445        mut previous_report: Option<InputReport>,
446        device_descriptor: &input_device::InputDeviceDescriptor,
447        input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
448        inspect_status: &InputDeviceStatus,
449        metrics_logger: &metrics::MetricsLogger,
450    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
451        fuchsia_trace::duration!("input", "mouse-binding-process-reports", "num_reports" => reports.len());
452        for report in reports {
453            previous_report = Self::process_report(
454                report,
455                previous_report,
456                device_descriptor,
457                input_event_sender,
458                inspect_status,
459                metrics_logger,
460            );
461        }
462        (previous_report, None)
463    }
464
465    fn process_report(
466        mut report: InputReport,
467        previous_report: Option<InputReport>,
468        device_descriptor: &input_device::InputDeviceDescriptor,
469        input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
470        inspect_status: &InputDeviceStatus,
471        metrics_logger: &metrics::MetricsLogger,
472    ) -> Option<InputReport> {
473        if let Some(trace_id) = report.trace_id {
474            fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
475        }
476
477        inspect_status.count_received_report(&report);
478        // Input devices can have multiple types so ensure `report` is a MouseInputReport.
479        let mouse_report: &fidl_input_report::MouseInputReport = match &report.mouse {
480            Some(mouse) => mouse,
481            None => {
482                inspect_status.count_filtered_report();
483                return previous_report;
484            }
485        };
486
487        let previous_buttons: HashSet<MouseButton> =
488            buttons_from_optional_report(&previous_report.as_ref());
489        let current_buttons: HashSet<MouseButton> = buttons_from_report(&report);
490        let wake_lease = report.wake_lease.take();
491
492        // Send a Down event with:
493        // * affected_buttons: the buttons that were pressed since the previous report,
494        //   i.e. that are in the current report, but were not in the previous report.
495        // * pressed_buttons: the full set of currently pressed buttons, including the
496        //   recently pressed ones (affected_buttons).
497        send_mouse_event(
498            MouseLocation::Relative(Default::default()),
499            None, /* wheel_delta_v */
500            None, /* wheel_delta_h */
501            MousePhase::Down,
502            current_buttons.difference(&previous_buttons).cloned().collect(),
503            current_buttons.clone(),
504            device_descriptor,
505            input_event_sender,
506            inspect_status,
507            metrics_logger,
508            wake_lease.as_ref().map(|lease| {
509                lease
510                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
511                    .expect("failed to duplicate event pair")
512            }),
513        );
514
515        let counts_per_mm = match device_descriptor {
516            input_device::InputDeviceDescriptor::Mouse(ds) => ds.counts_per_mm,
517            _ => {
518                metrics_logger.log_error(
519                    InputPipelineErrorMetricDimensionEvent::MouseDescriptionNotMouse,
520                    "mouse_binding::process_reports got device_descriptor not mouse".to_string(),
521                );
522                mouse_model_database::db::DEFAULT_COUNTS_PER_MM
523            }
524        };
525
526        // Create a location for the move event. Use the absolute position if available.
527        let location = if let (Some(position_x), Some(position_y)) =
528            (mouse_report.position_x, mouse_report.position_y)
529        {
530            MouseLocation::Absolute(Position { x: position_x as f32, y: position_y as f32 })
531        } else {
532            let movement_x = mouse_report.movement_x.unwrap_or_default() as f32;
533            let movement_y = mouse_report.movement_y.unwrap_or_default() as f32;
534            MouseLocation::Relative(RelativeLocation {
535                millimeters: Position {
536                    x: movement_x / counts_per_mm as f32,
537                    y: movement_y / counts_per_mm as f32,
538                },
539            })
540        };
541
542        // Send a Move event with buttons from both the current report and the previous report.
543        // * affected_buttons and pressed_buttons are identical in this case, since the full
544        //   set of currently pressed buttons are the same set affected by the event.
545        send_mouse_event(
546            location,
547            None, /* wheel_delta_v */
548            None, /* wheel_delta_h */
549            MousePhase::Move,
550            current_buttons.union(&previous_buttons).cloned().collect(),
551            current_buttons.union(&previous_buttons).cloned().collect(),
552            device_descriptor,
553            input_event_sender,
554            inspect_status,
555            metrics_logger,
556            wake_lease.as_ref().map(|lease| {
557                lease
558                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
559                    .expect("failed to duplicate event pair")
560            }),
561        );
562
563        let wheel_delta_v = match mouse_report.scroll_v {
564            None => None,
565            Some(ticks) => {
566                Some(WheelDelta { raw_data: RawWheelDelta::Ticks(ticks), physical_pixel: None })
567            }
568        };
569
570        let wheel_delta_h = match mouse_report.scroll_h {
571            None => None,
572            Some(ticks) => {
573                Some(WheelDelta { raw_data: RawWheelDelta::Ticks(ticks), physical_pixel: None })
574            }
575        };
576
577        // Send a mouse wheel event.
578        send_mouse_event(
579            MouseLocation::Relative(Default::default()),
580            wheel_delta_v,
581            wheel_delta_h,
582            MousePhase::Wheel,
583            current_buttons.union(&previous_buttons).cloned().collect(),
584            current_buttons.union(&previous_buttons).cloned().collect(),
585            device_descriptor,
586            input_event_sender,
587            inspect_status,
588            metrics_logger,
589            wake_lease.as_ref().map(|lease| {
590                lease
591                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
592                    .expect("failed to duplicate event pair")
593            }),
594        );
595
596        // Send an Up event with:
597        // * affected_buttons: the buttons that were released since the previous report,
598        //   i.e. that were in the previous report, but are not in the current report.
599        // * pressed_buttons: the full set of currently pressed buttons, excluding the
600        //   recently released ones (affected_buttons).
601        send_mouse_event(
602            MouseLocation::Relative(Default::default()),
603            None, /* wheel_delta_v */
604            None, /* wheel_delta_h */
605            MousePhase::Up,
606            previous_buttons.difference(&current_buttons).cloned().collect(),
607            current_buttons.clone(),
608            device_descriptor,
609            input_event_sender,
610            inspect_status,
611            metrics_logger,
612            wake_lease,
613        );
614
615        Some(report)
616    }
617}
618
619/// Sends an InputEvent over `sender`.
620///
621/// When no buttons are present, only [`MousePhase::Move`] events will
622/// be sent.
623///
624/// # Parameters
625/// - `location`: The mouse location.
626/// - `wheel_delta_v`: The mouse wheel delta in vertical.
627/// - `wheel_delta_h`: The mouse wheel delta in horizontal.
628/// - `phase`: The phase of the [`buttons`] associated with the input event.
629/// - `buttons`: The buttons relevant to the event.
630/// - `device_descriptor`: The descriptor for the input device generating the input reports.
631/// - `sender`: The stream to send the MouseEvent to.
632/// - `wake_lease`: The wake lease to send with the event.
633fn send_mouse_event(
634    location: MouseLocation,
635    wheel_delta_v: Option<WheelDelta>,
636    wheel_delta_h: Option<WheelDelta>,
637    phase: MousePhase,
638    affected_buttons: HashSet<MouseButton>,
639    pressed_buttons: HashSet<MouseButton>,
640    device_descriptor: &input_device::InputDeviceDescriptor,
641    sender: &mut UnboundedSender<input_device::InputEvent>,
642    inspect_status: &InputDeviceStatus,
643    metrics_logger: &metrics::MetricsLogger,
644    wake_lease: Option<zx::EventPair>,
645) {
646    // Only send Down/Up events when there are buttons affected.
647    if (phase == MousePhase::Down || phase == MousePhase::Up) && affected_buttons.is_empty() {
648        return;
649    }
650
651    // Don't send Move events when there is no relative movement.
652    // However, absolute movement is always reported.
653    if phase == MousePhase::Move && location == MouseLocation::Relative(Default::default()) {
654        return;
655    }
656
657    // Only send wheel events when the delta has value.
658    if phase == MousePhase::Wheel && wheel_delta_v.is_none() && wheel_delta_h.is_none() {
659        return;
660    }
661
662    let trace_id = fuchsia_trace::Id::random();
663    fuchsia_trace::duration!("input", "mouse-binding-send-event");
664    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
665
666    let event = input_device::InputEvent {
667        device_event: input_device::InputDeviceEvent::Mouse(MouseEvent::new(
668            location,
669            wheel_delta_v,
670            wheel_delta_h,
671            phase,
672            affected_buttons,
673            pressed_buttons,
674            match phase {
675                MousePhase::Wheel => Some(PrecisionScroll::No),
676                _ => None,
677            },
678            wake_lease,
679        )),
680        device_descriptor: device_descriptor.clone(),
681        event_time: zx::MonotonicInstant::get(),
682        handled: Handled::No,
683        trace_id: Some(trace_id),
684    };
685
686    match sender.unbounded_send(event.clone_with_wake_lease()) {
687        Err(e) => {
688            metrics_logger.log_error(
689                InputPipelineErrorMetricDimensionEvent::MouseFailedToSendEvent,
690                std::format!("Failed to send MouseEvent with error: {:?}", e),
691            );
692        }
693        _ => inspect_status.count_generated_event(event),
694    }
695}
696
697/// Returns a u32 representation of `buttons`, where each u8 of `buttons` is an id of a button and
698/// indicates the position of a bit to set.
699///
700/// This supports hashsets with numbers from 1 to fidl_input_report::MOUSE_MAX_NUM_BUTTONS.
701///
702/// # Parameters
703/// - `buttons`: The hashset containing the position of bits to be set.
704///
705/// # Example
706/// ```
707/// let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, 5]).into_iter());
708/// assert_eq!(bits, 21 /* ...00010101 */)
709/// ```
710pub fn get_u32_from_buttons(buttons: &HashSet<MouseButton>) -> u32 {
711    let mut bits: u32 = 0;
712    for button in buttons {
713        if *button > 0 && *button <= fidl_input_report::MOUSE_MAX_NUM_BUTTONS as u8 {
714            bits = ((1 as u32) << *button - 1) | bits;
715        }
716    }
717
718    bits
719}
720
721/// Returns the set of pressed buttons present in the given input report.
722///
723/// # Parameters
724/// - `report`: The input report to parse the mouse buttons from.
725fn buttons_from_report(input_report: &fidl_input_report::InputReport) -> HashSet<MouseButton> {
726    buttons_from_optional_report(&Some(input_report))
727}
728
729/// Returns the set of pressed buttons present in the given input report.
730///
731/// # Parameters
732/// - `report`: The input report to parse the mouse buttons from.
733fn buttons_from_optional_report(
734    input_report: &Option<&fidl_input_report::InputReport>,
735) -> HashSet<MouseButton> {
736    input_report
737        .as_ref()
738        .and_then(|unwrapped_report| unwrapped_report.mouse.as_ref())
739        .and_then(|mouse_report| match &mouse_report.pressed_buttons {
740            Some(buttons) => Some(HashSet::from_iter(buttons.iter().cloned())),
741            None => None,
742        })
743        .unwrap_or_default()
744}
745
746#[cfg(test)]
747mod tests {
748    use super::*;
749    use crate::testing_utilities;
750    use fuchsia_async as fasync;
751    use futures::StreamExt;
752    use pretty_assertions::assert_eq;
753
754    const DEVICE_ID: u32 = 1;
755    const COUNTS_PER_MM: u32 = 12;
756
757    fn mouse_device_descriptor(device_id: u32) -> input_device::InputDeviceDescriptor {
758        input_device::InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
759            device_id,
760            absolute_x_range: None,
761            absolute_y_range: None,
762            wheel_v_range: Some(fidl_fuchsia_input_report::Axis {
763                range: fidl_input_report::Range { min: -1, max: 1 },
764                unit: fidl_input_report::Unit {
765                    type_: fidl_input_report::UnitType::Other,
766                    exponent: 1,
767                },
768            }),
769            wheel_h_range: Some(fidl_fuchsia_input_report::Axis {
770                range: fidl_input_report::Range { min: -1, max: 1 },
771                unit: fidl_input_report::Unit {
772                    type_: fidl_input_report::UnitType::Other,
773                    exponent: 1,
774                },
775            }),
776            buttons: None,
777            counts_per_mm: COUNTS_PER_MM,
778        })
779    }
780
781    fn wheel_delta_ticks(delta: i64) -> Option<WheelDelta> {
782        Some(WheelDelta { raw_data: RawWheelDelta::Ticks(delta), physical_pixel: None })
783    }
784
785    // Tests that the right u32 representation is returned from a vector of digits.
786    #[test]
787    fn get_u32_from_buttons_test() {
788        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, 5].into_iter()));
789        assert_eq!(bits, 21 /* 0...00010101 */)
790    }
791
792    // Tests that the right u32 representation is returned from a vector of digits that includes 0.
793    #[test]
794    fn get_u32_with_0_in_vector() {
795        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![0, 1, 3].into_iter()));
796        assert_eq!(bits, 5 /* 0...00000101 */)
797    }
798
799    // Tests that the right u32 representation is returned from an empty vector.
800    #[test]
801    fn get_u32_with_empty_vector() {
802        let bits = get_u32_from_buttons(&HashSet::new());
803        assert_eq!(bits, 0 /* 0...00000000 */)
804    }
805
806    // Tests that the right u32 representation is returned from a vector containing std::u8::MAX.
807    #[test]
808    fn get_u32_with_u8_max_in_vector() {
809        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, std::u8::MAX].into_iter()));
810        assert_eq!(bits, 5 /* 0...00000101 */)
811    }
812
813    // Tests that the right u32 representation is returned from a vector containing the largest
814    // button id possible.
815    #[test]
816    fn get_u32_with_max_mouse_buttons() {
817        let bits = get_u32_from_buttons(&HashSet::from_iter(
818            vec![1, 3, fidl_input_report::MOUSE_MAX_NUM_BUTTONS as MouseButton].into_iter(),
819        ));
820        assert_eq!(bits, 2147483653 /* 10...00000101 */)
821    }
822
823    /// Tests that a report containing no buttons but with movement generates a move event.
824    #[fasync::run_singlethreaded(test)]
825    async fn movement_without_button() {
826        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
827        let first_report = testing_utilities::create_mouse_input_report_relative(
828            Position { x: 10.0, y: 16.0 },
829            None, /* scroll_v */
830            None, /* scroll_h */
831            vec![],
832            event_time_i64,
833        );
834        let descriptor = mouse_device_descriptor(DEVICE_ID);
835
836        let input_reports = vec![first_report];
837        let expected_events = vec![testing_utilities::create_mouse_event(
838            MouseLocation::Relative(RelativeLocation {
839                millimeters: Position {
840                    x: 10.0 / COUNTS_PER_MM as f32,
841                    y: 16.0 / COUNTS_PER_MM as f32,
842                },
843            }),
844            None, /* wheel_delta_v */
845            None, /* wheel_delta_h */
846            None, /* is_precision_scroll */
847            MousePhase::Move,
848            HashSet::new(),
849            HashSet::new(),
850            event_time_u64,
851            &descriptor,
852        )];
853
854        assert_input_report_sequence_generates_events!(
855            input_reports: input_reports,
856            expected_events: expected_events,
857            device_descriptor: descriptor,
858            device_type: MouseBinding,
859        );
860    }
861
862    /// Tests that a report containing a new mouse button generates a down event.
863    #[fasync::run_singlethreaded(test)]
864    async fn down_without_movement() {
865        let mouse_button: MouseButton = 3;
866        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
867        let first_report = testing_utilities::create_mouse_input_report_relative(
868            Position::zero(),
869            None, /* scroll_v */
870            None, /* scroll_h */
871            vec![mouse_button],
872            event_time_i64,
873        );
874        let descriptor = mouse_device_descriptor(DEVICE_ID);
875
876        let input_reports = vec![first_report];
877        let expected_events = vec![testing_utilities::create_mouse_event(
878            MouseLocation::Relative(Default::default()),
879            None, /* wheel_delta_v */
880            None, /* wheel_delta_h */
881            None, /* is_precision_scroll */
882            MousePhase::Down,
883            HashSet::from_iter(vec![mouse_button].into_iter()),
884            HashSet::from_iter(vec![mouse_button].into_iter()),
885            event_time_u64,
886            &descriptor,
887        )];
888
889        assert_input_report_sequence_generates_events!(
890            input_reports: input_reports,
891            expected_events: expected_events,
892            device_descriptor: descriptor,
893            device_type: MouseBinding,
894        );
895    }
896
897    /// Tests that a report containing a new mouse button with movement generates a down event and a
898    /// move event.
899    #[fasync::run_singlethreaded(test)]
900    async fn down_with_movement() {
901        let mouse_button: MouseButton = 3;
902        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
903        let first_report = testing_utilities::create_mouse_input_report_relative(
904            Position { x: 10.0, y: 16.0 },
905            None, /* scroll_v */
906            None, /* scroll_h */
907            vec![mouse_button],
908            event_time_i64,
909        );
910        let descriptor = mouse_device_descriptor(DEVICE_ID);
911
912        let input_reports = vec![first_report];
913        let expected_events = vec![
914            testing_utilities::create_mouse_event(
915                MouseLocation::Relative(Default::default()),
916                None, /* wheel_delta_v */
917                None, /* wheel_delta_h */
918                None, /* is_precision_scroll */
919                MousePhase::Down,
920                HashSet::from_iter(vec![mouse_button].into_iter()),
921                HashSet::from_iter(vec![mouse_button].into_iter()),
922                event_time_u64,
923                &descriptor,
924            ),
925            testing_utilities::create_mouse_event(
926                MouseLocation::Relative(RelativeLocation {
927                    millimeters: Position {
928                        x: 10.0 / COUNTS_PER_MM as f32,
929                        y: 16.0 / COUNTS_PER_MM as f32,
930                    },
931                }),
932                None, /* wheel_delta_v */
933                None, /* wheel_delta_h */
934                None, /* is_precision_scroll */
935                MousePhase::Move,
936                HashSet::from_iter(vec![mouse_button].into_iter()),
937                HashSet::from_iter(vec![mouse_button].into_iter()),
938                event_time_u64,
939                &descriptor,
940            ),
941        ];
942
943        assert_input_report_sequence_generates_events!(
944            input_reports: input_reports,
945            expected_events: expected_events,
946            device_descriptor: descriptor,
947            device_type: MouseBinding,
948        );
949    }
950
951    /// Tests that a press and release of a mouse button without movement generates a down and up event.
952    #[fasync::run_singlethreaded(test)]
953    async fn down_up() {
954        let button = 1;
955        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
956        let first_report = testing_utilities::create_mouse_input_report_relative(
957            Position::zero(),
958            None, /* scroll_v */
959            None, /* scroll_h */
960            vec![button],
961            event_time_i64,
962        );
963        let second_report = testing_utilities::create_mouse_input_report_relative(
964            Position::zero(),
965            None, /* scroll_v */
966            None, /* scroll_h */
967            vec![],
968            event_time_i64,
969        );
970        let descriptor = mouse_device_descriptor(DEVICE_ID);
971
972        let input_reports = vec![first_report, second_report];
973        let expected_events = vec![
974            testing_utilities::create_mouse_event(
975                MouseLocation::Relative(Default::default()),
976                None, /* wheel_delta_v */
977                None, /* wheel_delta_h */
978                None, /* is_precision_scroll */
979                MousePhase::Down,
980                HashSet::from_iter(vec![button].into_iter()),
981                HashSet::from_iter(vec![button].into_iter()),
982                event_time_u64,
983                &descriptor,
984            ),
985            testing_utilities::create_mouse_event(
986                MouseLocation::Relative(Default::default()),
987                None, /* wheel_delta_v */
988                None, /* wheel_delta_h */
989                None, /* is_precision_scroll */
990                MousePhase::Up,
991                HashSet::from_iter(vec![button].into_iter()),
992                HashSet::new(),
993                event_time_u64,
994                &descriptor,
995            ),
996        ];
997
998        assert_input_report_sequence_generates_events!(
999            input_reports: input_reports,
1000            expected_events: expected_events,
1001            device_descriptor: descriptor,
1002            device_type: MouseBinding,
1003        );
1004    }
1005
1006    /// Tests that a press and release of a mouse button with movement generates down, move, and up events.
1007    #[fasync::run_singlethreaded(test)]
1008    async fn down_up_with_movement() {
1009        let button = 1;
1010
1011        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1012        let first_report = testing_utilities::create_mouse_input_report_relative(
1013            Position::zero(),
1014            None, /* scroll_v */
1015            None, /* scroll_h */
1016            vec![button],
1017            event_time_i64,
1018        );
1019        let second_report = testing_utilities::create_mouse_input_report_relative(
1020            Position { x: 10.0, y: 16.0 },
1021            None, /* scroll_v */
1022            None, /* scroll_h */
1023            vec![],
1024            event_time_i64,
1025        );
1026        let descriptor = mouse_device_descriptor(DEVICE_ID);
1027
1028        let input_reports = vec![first_report, second_report];
1029        let expected_events = vec![
1030            testing_utilities::create_mouse_event(
1031                MouseLocation::Relative(Default::default()),
1032                None, /* wheel_delta_v */
1033                None, /* wheel_delta_h */
1034                None, /* is_precision_scroll */
1035                MousePhase::Down,
1036                HashSet::from_iter(vec![button].into_iter()),
1037                HashSet::from_iter(vec![button].into_iter()),
1038                event_time_u64,
1039                &descriptor,
1040            ),
1041            testing_utilities::create_mouse_event(
1042                MouseLocation::Relative(RelativeLocation {
1043                    millimeters: Position {
1044                        x: 10.0 / COUNTS_PER_MM as f32,
1045                        y: 16.0 / COUNTS_PER_MM as f32,
1046                    },
1047                }),
1048                None, /* wheel_delta_v */
1049                None, /* wheel_delta_h */
1050                None, /* is_precision_scroll */
1051                MousePhase::Move,
1052                HashSet::from_iter(vec![button].into_iter()),
1053                HashSet::from_iter(vec![button].into_iter()),
1054                event_time_u64,
1055                &descriptor,
1056            ),
1057            testing_utilities::create_mouse_event(
1058                MouseLocation::Relative(Default::default()),
1059                None, /* wheel_delta_v */
1060                None, /* wheel_delta_h */
1061                None, /* is_precision_scroll */
1062                MousePhase::Up,
1063                HashSet::from_iter(vec![button].into_iter()),
1064                HashSet::new(),
1065                event_time_u64,
1066                &descriptor,
1067            ),
1068        ];
1069
1070        assert_input_report_sequence_generates_events!(
1071            input_reports: input_reports,
1072            expected_events: expected_events,
1073            device_descriptor: descriptor,
1074            device_type: MouseBinding,
1075        );
1076    }
1077
1078    /// Tests that a press, move, and release of a button generates down, move, and up events.
1079    /// This specifically tests the separate input report containing the movement, instead of sending
1080    /// the movement as part of the down or up events.
1081    #[fasync::run_singlethreaded(test)]
1082    async fn down_move_up() {
1083        let button = 1;
1084
1085        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1086        let first_report = testing_utilities::create_mouse_input_report_relative(
1087            Position::zero(),
1088            None, /* scroll_v */
1089            None, /* scroll_h */
1090            vec![button],
1091            event_time_i64,
1092        );
1093        let second_report = testing_utilities::create_mouse_input_report_relative(
1094            Position { x: 10.0, y: 16.0 },
1095            None, /* scroll_v */
1096            None, /* scroll_h */
1097            vec![button],
1098            event_time_i64,
1099        );
1100        let third_report = testing_utilities::create_mouse_input_report_relative(
1101            Position::zero(),
1102            None, /* scroll_v */
1103            None, /* scroll_h */
1104            vec![],
1105            event_time_i64,
1106        );
1107        let descriptor = mouse_device_descriptor(DEVICE_ID);
1108
1109        let input_reports = vec![first_report, second_report, third_report];
1110        let expected_events = vec![
1111            testing_utilities::create_mouse_event(
1112                MouseLocation::Relative(Default::default()),
1113                None, /* wheel_delta_v */
1114                None, /* wheel_delta_h */
1115                None, /* is_precision_scroll */
1116                MousePhase::Down,
1117                HashSet::from_iter(vec![button].into_iter()),
1118                HashSet::from_iter(vec![button].into_iter()),
1119                event_time_u64,
1120                &descriptor,
1121            ),
1122            testing_utilities::create_mouse_event(
1123                MouseLocation::Relative(RelativeLocation {
1124                    millimeters: Position {
1125                        x: 10.0 / COUNTS_PER_MM as f32,
1126                        y: 16.0 / COUNTS_PER_MM as f32,
1127                    },
1128                }),
1129                None, /* wheel_delta_v */
1130                None, /* wheel_delta_h */
1131                None, /* is_precision_scroll */
1132                MousePhase::Move,
1133                HashSet::from_iter(vec![button].into_iter()),
1134                HashSet::from_iter(vec![button].into_iter()),
1135                event_time_u64,
1136                &descriptor,
1137            ),
1138            testing_utilities::create_mouse_event(
1139                MouseLocation::Relative(Default::default()),
1140                None, /* wheel_delta_v */
1141                None, /* wheel_delta_h */
1142                None, /* is_precision_scroll */
1143                MousePhase::Up,
1144                HashSet::from_iter(vec![button].into_iter()),
1145                HashSet::new(),
1146                event_time_u64,
1147                &descriptor,
1148            ),
1149        ];
1150
1151        assert_input_report_sequence_generates_events!(
1152            input_reports: input_reports,
1153            expected_events: expected_events,
1154            device_descriptor: descriptor,
1155            device_type: MouseBinding,
1156        );
1157    }
1158
1159    /// Tests that a report with absolute movement to {0, 0} generates a move event.
1160    #[fasync::run_until_stalled(test)]
1161    async fn absolute_movement_to_origin() {
1162        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1163        let descriptor = mouse_device_descriptor(DEVICE_ID);
1164
1165        let input_reports = vec![testing_utilities::create_mouse_input_report_absolute(
1166            Position::zero(),
1167            None, /* wheel_delta_v */
1168            None, /* wheel_delta_h */
1169            vec![],
1170            event_time_i64,
1171        )];
1172        let expected_events = vec![testing_utilities::create_mouse_event(
1173            MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
1174            None, /* wheel_delta_v */
1175            None, /* wheel_delta_h */
1176            None, /* is_precision_scroll */
1177            MousePhase::Move,
1178            HashSet::new(),
1179            HashSet::new(),
1180            event_time_u64,
1181            &descriptor,
1182        )];
1183
1184        assert_input_report_sequence_generates_events!(
1185            input_reports: input_reports,
1186            expected_events: expected_events,
1187            device_descriptor: descriptor,
1188            device_type: MouseBinding,
1189        );
1190    }
1191
1192    /// Tests that a report that contains both a relative movement and absolute position
1193    /// generates a move event to the absolute position.
1194    #[fasync::run_until_stalled(test)]
1195    async fn report_with_both_movement_and_position() {
1196        let relative_movement = Position { x: 5.0, y: 5.0 };
1197        let absolute_position = Position { x: 10.0, y: 10.0 };
1198        let expected_location = MouseLocation::Absolute(absolute_position);
1199
1200        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1201        let descriptor = mouse_device_descriptor(DEVICE_ID);
1202
1203        let input_reports = vec![fidl_input_report::InputReport {
1204            event_time: Some(event_time_i64),
1205            keyboard: None,
1206            mouse: Some(fidl_input_report::MouseInputReport {
1207                movement_x: Some(relative_movement.x as i64),
1208                movement_y: Some(relative_movement.y as i64),
1209                position_x: Some(absolute_position.x as i64),
1210                position_y: Some(absolute_position.y as i64),
1211                scroll_h: None,
1212                scroll_v: None,
1213                pressed_buttons: None,
1214                ..Default::default()
1215            }),
1216            touch: None,
1217            sensor: None,
1218            consumer_control: None,
1219            trace_id: None,
1220            ..Default::default()
1221        }];
1222        let expected_events = vec![testing_utilities::create_mouse_event(
1223            expected_location,
1224            None, /* wheel_delta_v */
1225            None, /* wheel_delta_h */
1226            None, /* is_precision_scroll */
1227            MousePhase::Move,
1228            HashSet::new(),
1229            HashSet::new(),
1230            event_time_u64,
1231            &descriptor,
1232        )];
1233
1234        assert_input_report_sequence_generates_events!(
1235            input_reports: input_reports,
1236            expected_events: expected_events,
1237            device_descriptor: descriptor,
1238            device_type: MouseBinding,
1239        );
1240    }
1241
1242    /// Tests that two separate button presses generate two separate down events with differing
1243    /// sets of `affected_buttons` and `pressed_buttons`.
1244    #[fasync::run_singlethreaded(test)]
1245    async fn down_down() {
1246        const PRIMARY_BUTTON: u8 = 1;
1247        const SECONDARY_BUTTON: u8 = 2;
1248
1249        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1250        let first_report = testing_utilities::create_mouse_input_report_relative(
1251            Position::zero(),
1252            None, /* scroll_v */
1253            None, /* scroll_h */
1254            vec![PRIMARY_BUTTON],
1255            event_time_i64,
1256        );
1257        let second_report = testing_utilities::create_mouse_input_report_relative(
1258            Position::zero(),
1259            None, /* scroll_v */
1260            None, /* scroll_h */
1261            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1262            event_time_i64,
1263        );
1264        let descriptor = mouse_device_descriptor(DEVICE_ID);
1265
1266        let input_reports = vec![first_report, second_report];
1267        let expected_events = vec![
1268            testing_utilities::create_mouse_event(
1269                MouseLocation::Relative(Default::default()),
1270                None, /* wheel_delta_v */
1271                None, /* wheel_delta_h */
1272                None, /* is_precision_scroll */
1273                MousePhase::Down,
1274                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1275                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1276                event_time_u64,
1277                &descriptor,
1278            ),
1279            testing_utilities::create_mouse_event(
1280                MouseLocation::Relative(Default::default()),
1281                None, /* wheel_delta_v */
1282                None, /* wheel_delta_h */
1283                None, /* is_precision_scroll */
1284                MousePhase::Down,
1285                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1286                HashSet::from_iter(vec![PRIMARY_BUTTON, SECONDARY_BUTTON].into_iter()),
1287                event_time_u64,
1288                &descriptor,
1289            ),
1290        ];
1291
1292        assert_input_report_sequence_generates_events!(
1293            input_reports: input_reports,
1294            expected_events: expected_events,
1295            device_descriptor: descriptor,
1296            device_type: MouseBinding,
1297        );
1298    }
1299
1300    /// Tests that two staggered button presses followed by stagged releases generate four mouse
1301    /// events with distinct `affected_buttons` and `pressed_buttons`.
1302    /// Specifically, we test and expect the following in order:
1303    /// | Action           | MousePhase | `affected_buttons` | `pressed_buttons` |
1304    /// | ---------------- | ---------- | ------------------ | ----------------- |
1305    /// | Press button 1   | Down       | [1]                | [1]               |
1306    /// | Press button 2   | Down       | [2]                | [1, 2]            |
1307    /// | Release button 1 | Up         | [1]                | [2]               |
1308    /// | Release button 2 | Up         | [2]                | []                |
1309    #[fasync::run_singlethreaded(test)]
1310    async fn down_down_up_up() {
1311        const PRIMARY_BUTTON: u8 = 1;
1312        const SECONDARY_BUTTON: u8 = 2;
1313
1314        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1315        let first_report = testing_utilities::create_mouse_input_report_relative(
1316            Position::zero(),
1317            None, /* scroll_v */
1318            None, /* scroll_h */
1319            vec![PRIMARY_BUTTON],
1320            event_time_i64,
1321        );
1322        let second_report = testing_utilities::create_mouse_input_report_relative(
1323            Position::zero(),
1324            None, /* scroll_v */
1325            None, /* scroll_h */
1326            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1327            event_time_i64,
1328        );
1329        let third_report = testing_utilities::create_mouse_input_report_relative(
1330            Position::zero(),
1331            None, /* scroll_v */
1332            None, /* scroll_h */
1333            vec![SECONDARY_BUTTON],
1334            event_time_i64,
1335        );
1336        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1337            Position::zero(),
1338            None, /* scroll_v */
1339            None, /* scroll_h */
1340            vec![],
1341            event_time_i64,
1342        );
1343        let descriptor = mouse_device_descriptor(DEVICE_ID);
1344
1345        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1346        let expected_events = vec![
1347            testing_utilities::create_mouse_event(
1348                MouseLocation::Relative(Default::default()),
1349                None, /* wheel_delta_v */
1350                None, /* wheel_delta_h */
1351                None, /* is_precision_scroll */
1352                MousePhase::Down,
1353                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1354                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1355                event_time_u64,
1356                &descriptor,
1357            ),
1358            testing_utilities::create_mouse_event(
1359                MouseLocation::Relative(Default::default()),
1360                None, /* wheel_delta_v */
1361                None, /* wheel_delta_h */
1362                None, /* is_precision_scroll */
1363                MousePhase::Down,
1364                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1365                HashSet::from_iter(vec![PRIMARY_BUTTON, SECONDARY_BUTTON].into_iter()),
1366                event_time_u64,
1367                &descriptor,
1368            ),
1369            testing_utilities::create_mouse_event(
1370                MouseLocation::Relative(Default::default()),
1371                None, /* wheel_delta_v */
1372                None, /* wheel_delta_h */
1373                None, /* is_precision_scroll */
1374                MousePhase::Up,
1375                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1376                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1377                event_time_u64,
1378                &descriptor,
1379            ),
1380            testing_utilities::create_mouse_event(
1381                MouseLocation::Relative(Default::default()),
1382                None, /* wheel_delta_v */
1383                None, /* wheel_delta_h */
1384                None, /* is_precision_scroll */
1385                MousePhase::Up,
1386                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1387                HashSet::new(),
1388                event_time_u64,
1389                &descriptor,
1390            ),
1391        ];
1392
1393        assert_input_report_sequence_generates_events!(
1394            input_reports: input_reports,
1395            expected_events: expected_events,
1396            device_descriptor: descriptor,
1397            device_type: MouseBinding,
1398        );
1399    }
1400
1401    /// Test simple scroll in vertical and horizontal.
1402    #[fasync::run_singlethreaded(test)]
1403    async fn scroll() {
1404        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1405        let first_report = testing_utilities::create_mouse_input_report_relative(
1406            Position::zero(),
1407            Some(1),
1408            None,
1409            vec![],
1410            event_time_i64,
1411        );
1412        let second_report = testing_utilities::create_mouse_input_report_relative(
1413            Position::zero(),
1414            None,
1415            Some(1),
1416            vec![],
1417            event_time_i64,
1418        );
1419
1420        let descriptor = mouse_device_descriptor(DEVICE_ID);
1421
1422        let input_reports = vec![first_report, second_report];
1423        let expected_events = vec![
1424            testing_utilities::create_mouse_event(
1425                MouseLocation::Relative(Default::default()),
1426                wheel_delta_ticks(1),
1427                None,
1428                Some(PrecisionScroll::No),
1429                MousePhase::Wheel,
1430                HashSet::new(),
1431                HashSet::new(),
1432                event_time_u64,
1433                &descriptor,
1434            ),
1435            testing_utilities::create_mouse_event(
1436                MouseLocation::Relative(Default::default()),
1437                None,
1438                wheel_delta_ticks(1),
1439                Some(PrecisionScroll::No),
1440                MousePhase::Wheel,
1441                HashSet::new(),
1442                HashSet::new(),
1443                event_time_u64,
1444                &descriptor,
1445            ),
1446        ];
1447
1448        assert_input_report_sequence_generates_events!(
1449            input_reports: input_reports,
1450            expected_events: expected_events,
1451            device_descriptor: descriptor,
1452            device_type: MouseBinding,
1453        );
1454    }
1455
1456    /// Test button down -> scroll -> button up -> continue scroll.
1457    #[fasync::run_singlethreaded(test)]
1458    async fn down_scroll_up_scroll() {
1459        const PRIMARY_BUTTON: u8 = 1;
1460
1461        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1462        let first_report = testing_utilities::create_mouse_input_report_relative(
1463            Position::zero(),
1464            None, /* scroll_v */
1465            None, /* scroll_h */
1466            vec![PRIMARY_BUTTON],
1467            event_time_i64,
1468        );
1469        let second_report = testing_utilities::create_mouse_input_report_relative(
1470            Position::zero(),
1471            Some(1),
1472            None,
1473            vec![PRIMARY_BUTTON],
1474            event_time_i64,
1475        );
1476        let third_report = testing_utilities::create_mouse_input_report_relative(
1477            Position::zero(),
1478            None, /* scroll_v */
1479            None, /* scroll_h */
1480            vec![],
1481            event_time_i64,
1482        );
1483        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1484            Position::zero(),
1485            Some(1),
1486            None,
1487            vec![],
1488            event_time_i64,
1489        );
1490
1491        let descriptor = mouse_device_descriptor(DEVICE_ID);
1492
1493        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1494        let expected_events = vec![
1495            testing_utilities::create_mouse_event(
1496                MouseLocation::Relative(Default::default()),
1497                None, /* wheel_delta_v */
1498                None, /* wheel_delta_h */
1499                None, /* is_precision_scroll */
1500                MousePhase::Down,
1501                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1502                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1503                event_time_u64,
1504                &descriptor,
1505            ),
1506            testing_utilities::create_mouse_event(
1507                MouseLocation::Relative(Default::default()),
1508                wheel_delta_ticks(1),
1509                None,
1510                Some(PrecisionScroll::No),
1511                MousePhase::Wheel,
1512                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1513                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1514                event_time_u64,
1515                &descriptor,
1516            ),
1517            testing_utilities::create_mouse_event(
1518                MouseLocation::Relative(Default::default()),
1519                None, /* wheel_delta_v */
1520                None, /* wheel_delta_h */
1521                None, /* is_precision_scroll */
1522                MousePhase::Up,
1523                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1524                HashSet::new(),
1525                event_time_u64,
1526                &descriptor,
1527            ),
1528            testing_utilities::create_mouse_event(
1529                MouseLocation::Relative(Default::default()),
1530                wheel_delta_ticks(1),
1531                None,
1532                Some(PrecisionScroll::No),
1533                MousePhase::Wheel,
1534                HashSet::new(),
1535                HashSet::new(),
1536                event_time_u64,
1537                &descriptor,
1538            ),
1539        ];
1540
1541        assert_input_report_sequence_generates_events!(
1542            input_reports: input_reports,
1543            expected_events: expected_events,
1544            device_descriptor: descriptor,
1545            device_type: MouseBinding,
1546        );
1547    }
1548
1549    /// Test button down with scroll -> button up with scroll -> scroll.
1550    #[fasync::run_singlethreaded(test)]
1551    async fn down_scroll_bundle_up_scroll_bundle() {
1552        const PRIMARY_BUTTON: u8 = 1;
1553
1554        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1555        let first_report = testing_utilities::create_mouse_input_report_relative(
1556            Position::zero(),
1557            Some(1),
1558            None,
1559            vec![PRIMARY_BUTTON],
1560            event_time_i64,
1561        );
1562        let second_report = testing_utilities::create_mouse_input_report_relative(
1563            Position::zero(),
1564            Some(1),
1565            None,
1566            vec![],
1567            event_time_i64,
1568        );
1569        let third_report = testing_utilities::create_mouse_input_report_relative(
1570            Position::zero(),
1571            Some(1),
1572            None,
1573            vec![],
1574            event_time_i64,
1575        );
1576
1577        let descriptor = mouse_device_descriptor(DEVICE_ID);
1578
1579        let input_reports = vec![first_report, second_report, third_report];
1580        let expected_events = vec![
1581            testing_utilities::create_mouse_event(
1582                MouseLocation::Relative(Default::default()),
1583                None, /* wheel_delta_v */
1584                None, /* wheel_delta_h */
1585                None, /* is_precision_scroll */
1586                MousePhase::Down,
1587                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1588                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1589                event_time_u64,
1590                &descriptor,
1591            ),
1592            testing_utilities::create_mouse_event(
1593                MouseLocation::Relative(Default::default()),
1594                wheel_delta_ticks(1),
1595                None,
1596                Some(PrecisionScroll::No),
1597                MousePhase::Wheel,
1598                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1599                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1600                event_time_u64,
1601                &descriptor,
1602            ),
1603            testing_utilities::create_mouse_event(
1604                MouseLocation::Relative(Default::default()),
1605                wheel_delta_ticks(1),
1606                None,
1607                Some(PrecisionScroll::No),
1608                MousePhase::Wheel,
1609                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1610                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1611                event_time_u64,
1612                &descriptor,
1613            ),
1614            testing_utilities::create_mouse_event(
1615                MouseLocation::Relative(Default::default()),
1616                None, /* wheel_delta_v */
1617                None, /* wheel_delta_h */
1618                None, /* is_precision_scroll */
1619                MousePhase::Up,
1620                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1621                HashSet::new(),
1622                event_time_u64,
1623                &descriptor,
1624            ),
1625            testing_utilities::create_mouse_event(
1626                MouseLocation::Relative(Default::default()),
1627                wheel_delta_ticks(1),
1628                None,
1629                Some(PrecisionScroll::No),
1630                MousePhase::Wheel,
1631                HashSet::new(),
1632                HashSet::new(),
1633                event_time_u64,
1634                &descriptor,
1635            ),
1636        ];
1637
1638        assert_input_report_sequence_generates_events!(
1639            input_reports: input_reports,
1640            expected_events: expected_events,
1641            device_descriptor: descriptor,
1642            device_type: MouseBinding,
1643        );
1644    }
1645}