Skip to main content

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