Skip to main content

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