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, 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_rs::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.clone_with_wake_lease()];
699
700    match sender.unbounded_send(events.clone()) {
701        Err(e) => {
702            metrics_logger.log_error(
703                InputPipelineErrorMetricDimensionEvent::MouseFailedToSendEvent,
704                std::format!("Failed to send MouseEvent with error: {:?}", e),
705            );
706        }
707        _ => inspect_status.count_generated_events(&events),
708    }
709}
710
711/// Returns the set of pressed buttons present in the given input report.
712///
713/// # Parameters
714/// - `report`: The input report to parse the mouse buttons from.
715fn buttons_from_report(
716    input_report: &fidl_next_fuchsia_input_report::InputReport,
717) -> SortedVecSet<MouseButton> {
718    buttons_from_optional_report(&Some(input_report))
719}
720
721/// Returns the set of pressed buttons present in the given input report.
722///
723/// # Parameters
724/// - `report`: The input report to parse the mouse buttons from.
725fn buttons_from_optional_report(
726    input_report: &Option<&fidl_next_fuchsia_input_report::InputReport>,
727) -> SortedVecSet<MouseButton> {
728    input_report
729        .as_ref()
730        .and_then(|unwrapped_report| unwrapped_report.mouse.as_ref())
731        .and_then(|mouse_report| match &mouse_report.pressed_buttons {
732            Some(buttons) => Some(SortedVecSet::from(buttons.clone())),
733            None => None,
734        })
735        .unwrap_or_default()
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741    use crate::testing_utilities;
742    use fuchsia_async as fasync;
743    use futures::StreamExt;
744    use sorted_vec_map_rs::SortedVecSet;
745
746    const DEVICE_ID: u32 = 1;
747    const COUNTS_PER_MM: u32 = 12;
748
749    fn mouse_device_descriptor(device_id: u32) -> input_device::InputDeviceDescriptor {
750        input_device::InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
751            device_id,
752            absolute_x_range: None,
753            absolute_y_range: None,
754            wheel_v_range: Some(fidl_fuchsia_input_report::Axis {
755                range: fidl_input_report::Range { min: -1, max: 1 },
756                unit: fidl_input_report::Unit {
757                    type_: fidl_input_report::UnitType::Other,
758                    exponent: 1,
759                },
760            }),
761            wheel_h_range: Some(fidl_fuchsia_input_report::Axis {
762                range: fidl_input_report::Range { min: -1, max: 1 },
763                unit: fidl_input_report::Unit {
764                    type_: fidl_input_report::UnitType::Other,
765                    exponent: 1,
766                },
767            }),
768            buttons: None,
769            counts_per_mm: COUNTS_PER_MM,
770        })
771    }
772
773    fn wheel_delta_ticks(delta: i64) -> Option<WheelDelta> {
774        Some(WheelDelta { raw_data: RawWheelDelta::Ticks(delta), physical_pixel: None })
775    }
776
777    /// Tests that a report containing no buttons but with movement generates a move event.
778    #[fasync::run_singlethreaded(test)]
779    async fn movement_without_button() {
780        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
781        let first_report = testing_utilities::create_mouse_input_report_relative(
782            Position { x: 10.0, y: 16.0 },
783            None, /* scroll_v */
784            None, /* scroll_h */
785            vec![],
786            event_time_i64,
787        );
788        let descriptor = mouse_device_descriptor(DEVICE_ID);
789
790        let input_reports = vec![first_report];
791        let expected_events = vec![testing_utilities::create_mouse_event(
792            MouseLocation::Relative(RelativeLocation {
793                millimeters: Position {
794                    x: 10.0 / COUNTS_PER_MM as f32,
795                    y: 16.0 / COUNTS_PER_MM as f32,
796                },
797            }),
798            None, /* wheel_delta_v */
799            None, /* wheel_delta_h */
800            None, /* is_precision_scroll */
801            MousePhase::Move,
802            SortedVecSet::new(),
803            SortedVecSet::new(),
804            event_time_u64,
805            &descriptor,
806        )];
807
808        assert_input_report_sequence_generates_events!(
809            input_reports: input_reports,
810            expected_events: expected_events,
811            device_descriptor: descriptor,
812            device_type: MouseBinding,
813        );
814    }
815
816    /// Tests that a report containing a new mouse button generates a down event.
817    #[fasync::run_singlethreaded(test)]
818    async fn down_without_movement() {
819        let mouse_button: MouseButton = 3;
820        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
821        let first_report = testing_utilities::create_mouse_input_report_relative(
822            Position::zero(),
823            None, /* scroll_v */
824            None, /* scroll_h */
825            vec![mouse_button],
826            event_time_i64,
827        );
828        let descriptor = mouse_device_descriptor(DEVICE_ID);
829
830        let input_reports = vec![first_report];
831        let expected_events = vec![testing_utilities::create_mouse_event(
832            MouseLocation::Relative(Default::default()),
833            None, /* wheel_delta_v */
834            None, /* wheel_delta_h */
835            None, /* is_precision_scroll */
836            MousePhase::Down,
837            SortedVecSet::from(vec![mouse_button]),
838            SortedVecSet::from(vec![mouse_button]),
839            event_time_u64,
840            &descriptor,
841        )];
842
843        assert_input_report_sequence_generates_events!(
844            input_reports: input_reports,
845            expected_events: expected_events,
846            device_descriptor: descriptor,
847            device_type: MouseBinding,
848        );
849    }
850
851    /// Tests that a report containing a new mouse button with movement generates a down event and a
852    /// move event.
853    #[fasync::run_singlethreaded(test)]
854    async fn down_with_movement() {
855        let mouse_button: MouseButton = 3;
856        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
857        let first_report = testing_utilities::create_mouse_input_report_relative(
858            Position { x: 10.0, y: 16.0 },
859            None, /* scroll_v */
860            None, /* scroll_h */
861            vec![mouse_button],
862            event_time_i64,
863        );
864        let descriptor = mouse_device_descriptor(DEVICE_ID);
865
866        let input_reports = vec![first_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![mouse_button]),
875                SortedVecSet::from(vec![mouse_button]),
876                event_time_u64,
877                &descriptor,
878            ),
879            testing_utilities::create_mouse_event(
880                MouseLocation::Relative(RelativeLocation {
881                    millimeters: Position {
882                        x: 10.0 / COUNTS_PER_MM as f32,
883                        y: 16.0 / COUNTS_PER_MM as f32,
884                    },
885                }),
886                None, /* wheel_delta_v */
887                None, /* wheel_delta_h */
888                None, /* is_precision_scroll */
889                MousePhase::Move,
890                SortedVecSet::from(vec![mouse_button]),
891                SortedVecSet::from(vec![mouse_button]),
892                event_time_u64,
893                &descriptor,
894            ),
895        ];
896
897        assert_input_report_sequence_generates_events!(
898            input_reports: input_reports,
899            expected_events: expected_events,
900            device_descriptor: descriptor,
901            device_type: MouseBinding,
902        );
903    }
904
905    /// Tests that a press and release of a mouse button without movement generates a down and up event.
906    #[fasync::run_singlethreaded(test)]
907    async fn down_up() {
908        let button = 1;
909        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
910        let first_report = testing_utilities::create_mouse_input_report_relative(
911            Position::zero(),
912            None, /* scroll_v */
913            None, /* scroll_h */
914            vec![button],
915            event_time_i64,
916        );
917        let second_report = testing_utilities::create_mouse_input_report_relative(
918            Position::zero(),
919            None, /* scroll_v */
920            None, /* scroll_h */
921            vec![],
922            event_time_i64,
923        );
924        let descriptor = mouse_device_descriptor(DEVICE_ID);
925
926        let input_reports = vec![first_report, second_report];
927        let expected_events = vec![
928            testing_utilities::create_mouse_event(
929                MouseLocation::Relative(Default::default()),
930                None, /* wheel_delta_v */
931                None, /* wheel_delta_h */
932                None, /* is_precision_scroll */
933                MousePhase::Down,
934                SortedVecSet::from(vec![button]),
935                SortedVecSet::from(vec![button]),
936                event_time_u64,
937                &descriptor,
938            ),
939            testing_utilities::create_mouse_event(
940                MouseLocation::Relative(Default::default()),
941                None, /* wheel_delta_v */
942                None, /* wheel_delta_h */
943                None, /* is_precision_scroll */
944                MousePhase::Up,
945                SortedVecSet::from(vec![button]),
946                SortedVecSet::new(),
947                event_time_u64,
948                &descriptor,
949            ),
950        ];
951
952        assert_input_report_sequence_generates_events!(
953            input_reports: input_reports,
954            expected_events: expected_events,
955            device_descriptor: descriptor,
956            device_type: MouseBinding,
957        );
958    }
959
960    /// Tests that a press and release of a mouse button with movement generates down, move, and up events.
961    #[fasync::run_singlethreaded(test)]
962    async fn down_up_with_movement() {
963        let button = 1;
964
965        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
966        let first_report = testing_utilities::create_mouse_input_report_relative(
967            Position::zero(),
968            None, /* scroll_v */
969            None, /* scroll_h */
970            vec![button],
971            event_time_i64,
972        );
973        let second_report = testing_utilities::create_mouse_input_report_relative(
974            Position { x: 10.0, y: 16.0 },
975            None, /* scroll_v */
976            None, /* scroll_h */
977            vec![],
978            event_time_i64,
979        );
980        let descriptor = mouse_device_descriptor(DEVICE_ID);
981
982        let input_reports = vec![first_report, second_report];
983        let expected_events = vec![
984            testing_utilities::create_mouse_event(
985                MouseLocation::Relative(Default::default()),
986                None, /* wheel_delta_v */
987                None, /* wheel_delta_h */
988                None, /* is_precision_scroll */
989                MousePhase::Down,
990                SortedVecSet::from(vec![button]),
991                SortedVecSet::from(vec![button]),
992                event_time_u64,
993                &descriptor,
994            ),
995            testing_utilities::create_mouse_event(
996                MouseLocation::Relative(RelativeLocation {
997                    millimeters: Position {
998                        x: 10.0 / COUNTS_PER_MM as f32,
999                        y: 16.0 / COUNTS_PER_MM as f32,
1000                    },
1001                }),
1002                None, /* wheel_delta_v */
1003                None, /* wheel_delta_h */
1004                None, /* is_precision_scroll */
1005                MousePhase::Move,
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(Default::default()),
1013                None, /* wheel_delta_v */
1014                None, /* wheel_delta_h */
1015                None, /* is_precision_scroll */
1016                MousePhase::Up,
1017                SortedVecSet::from(vec![button]),
1018                SortedVecSet::new(),
1019                event_time_u64,
1020                &descriptor,
1021            ),
1022        ];
1023
1024        assert_input_report_sequence_generates_events!(
1025            input_reports: input_reports,
1026            expected_events: expected_events,
1027            device_descriptor: descriptor,
1028            device_type: MouseBinding,
1029        );
1030    }
1031
1032    /// Tests that a press, move, and release of a button generates down, move, and up events.
1033    /// This specifically tests the separate input report containing the movement, instead of sending
1034    /// the movement as part of the down or up events.
1035    #[fasync::run_singlethreaded(test)]
1036    async fn down_move_up() {
1037        let button = 1;
1038
1039        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1040        let first_report = testing_utilities::create_mouse_input_report_relative(
1041            Position::zero(),
1042            None, /* scroll_v */
1043            None, /* scroll_h */
1044            vec![button],
1045            event_time_i64,
1046        );
1047        let second_report = testing_utilities::create_mouse_input_report_relative(
1048            Position { x: 10.0, y: 16.0 },
1049            None, /* scroll_v */
1050            None, /* scroll_h */
1051            vec![button],
1052            event_time_i64,
1053        );
1054        let third_report = testing_utilities::create_mouse_input_report_relative(
1055            Position::zero(),
1056            None, /* scroll_v */
1057            None, /* scroll_h */
1058            vec![],
1059            event_time_i64,
1060        );
1061        let descriptor = mouse_device_descriptor(DEVICE_ID);
1062
1063        let input_reports = vec![first_report, second_report, third_report];
1064        let expected_events = vec![
1065            testing_utilities::create_mouse_event(
1066                MouseLocation::Relative(Default::default()),
1067                None, /* wheel_delta_v */
1068                None, /* wheel_delta_h */
1069                None, /* is_precision_scroll */
1070                MousePhase::Down,
1071                SortedVecSet::from(vec![button]),
1072                SortedVecSet::from(vec![button]),
1073                event_time_u64,
1074                &descriptor,
1075            ),
1076            testing_utilities::create_mouse_event(
1077                MouseLocation::Relative(RelativeLocation {
1078                    millimeters: Position {
1079                        x: 10.0 / COUNTS_PER_MM as f32,
1080                        y: 16.0 / COUNTS_PER_MM as f32,
1081                    },
1082                }),
1083                None, /* wheel_delta_v */
1084                None, /* wheel_delta_h */
1085                None, /* is_precision_scroll */
1086                MousePhase::Move,
1087                SortedVecSet::from(vec![button]),
1088                SortedVecSet::from(vec![button]),
1089                event_time_u64,
1090                &descriptor,
1091            ),
1092            testing_utilities::create_mouse_event(
1093                MouseLocation::Relative(Default::default()),
1094                None, /* wheel_delta_v */
1095                None, /* wheel_delta_h */
1096                None, /* is_precision_scroll */
1097                MousePhase::Up,
1098                SortedVecSet::from(vec![button]),
1099                SortedVecSet::new(),
1100                event_time_u64,
1101                &descriptor,
1102            ),
1103        ];
1104
1105        assert_input_report_sequence_generates_events!(
1106            input_reports: input_reports,
1107            expected_events: expected_events,
1108            device_descriptor: descriptor,
1109            device_type: MouseBinding,
1110        );
1111    }
1112
1113    /// Tests that a report with absolute movement to {0, 0} generates a move event.
1114    #[fasync::run_until_stalled(test)]
1115    async fn absolute_movement_to_origin() {
1116        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1117        let descriptor = mouse_device_descriptor(DEVICE_ID);
1118
1119        let input_reports = vec![testing_utilities::create_mouse_input_report_absolute(
1120            Position::zero(),
1121            None, /* wheel_delta_v */
1122            None, /* wheel_delta_h */
1123            vec![],
1124            event_time_i64,
1125        )];
1126        let expected_events = vec![testing_utilities::create_mouse_event(
1127            MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
1128            None, /* wheel_delta_v */
1129            None, /* wheel_delta_h */
1130            None, /* is_precision_scroll */
1131            MousePhase::Move,
1132            SortedVecSet::new(),
1133            SortedVecSet::new(),
1134            event_time_u64,
1135            &descriptor,
1136        )];
1137
1138        assert_input_report_sequence_generates_events!(
1139            input_reports: input_reports,
1140            expected_events: expected_events,
1141            device_descriptor: descriptor,
1142            device_type: MouseBinding,
1143        );
1144    }
1145
1146    /// Tests that a report that contains both a relative movement and absolute position
1147    /// generates a move event to the absolute position.
1148    #[fasync::run_until_stalled(test)]
1149    async fn report_with_both_movement_and_position() {
1150        let relative_movement = Position { x: 5.0, y: 5.0 };
1151        let absolute_position = Position { x: 10.0, y: 10.0 };
1152        let expected_location = MouseLocation::Absolute(absolute_position);
1153
1154        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1155        let descriptor = mouse_device_descriptor(DEVICE_ID);
1156
1157        let input_reports = vec![fidl_next_fuchsia_input_report::InputReport {
1158            event_time: Some(event_time_i64),
1159            keyboard: None,
1160            mouse: Some(fidl_next_fuchsia_input_report::MouseInputReport {
1161                movement_x: Some(relative_movement.x as i64),
1162                movement_y: Some(relative_movement.y as i64),
1163                position_x: Some(absolute_position.x as i64),
1164                position_y: Some(absolute_position.y as i64),
1165                scroll_h: None,
1166                scroll_v: None,
1167                pressed_buttons: None,
1168                ..Default::default()
1169            }),
1170            touch: None,
1171            sensor: None,
1172            consumer_control: None,
1173            trace_id: None,
1174            ..Default::default()
1175        }];
1176        let expected_events = vec![testing_utilities::create_mouse_event(
1177            expected_location,
1178            None, /* wheel_delta_v */
1179            None, /* wheel_delta_h */
1180            None, /* is_precision_scroll */
1181            MousePhase::Move,
1182            SortedVecSet::new(),
1183            SortedVecSet::new(),
1184            event_time_u64,
1185            &descriptor,
1186        )];
1187
1188        assert_input_report_sequence_generates_events!(
1189            input_reports: input_reports,
1190            expected_events: expected_events,
1191            device_descriptor: descriptor,
1192            device_type: MouseBinding,
1193        );
1194    }
1195
1196    /// Tests that two separate button presses generate two separate down events with differing
1197    /// sets of `affected_buttons` and `pressed_buttons`.
1198    #[fasync::run_singlethreaded(test)]
1199    async fn down_down() {
1200        const PRIMARY_BUTTON: u8 = 1;
1201        const SECONDARY_BUTTON: u8 = 2;
1202
1203        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1204        let first_report = testing_utilities::create_mouse_input_report_relative(
1205            Position::zero(),
1206            None, /* scroll_v */
1207            None, /* scroll_h */
1208            vec![PRIMARY_BUTTON],
1209            event_time_i64,
1210        );
1211        let second_report = testing_utilities::create_mouse_input_report_relative(
1212            Position::zero(),
1213            None, /* scroll_v */
1214            None, /* scroll_h */
1215            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1216            event_time_i64,
1217        );
1218        let descriptor = mouse_device_descriptor(DEVICE_ID);
1219
1220        let input_reports = vec![first_report, second_report];
1221        let expected_events = vec![
1222            testing_utilities::create_mouse_event(
1223                MouseLocation::Relative(Default::default()),
1224                None, /* wheel_delta_v */
1225                None, /* wheel_delta_h */
1226                None, /* is_precision_scroll */
1227                MousePhase::Down,
1228                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1229                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1230                event_time_u64,
1231                &descriptor,
1232            ),
1233            testing_utilities::create_mouse_event(
1234                MouseLocation::Relative(Default::default()),
1235                None, /* wheel_delta_v */
1236                None, /* wheel_delta_h */
1237                None, /* is_precision_scroll */
1238                MousePhase::Down,
1239                SortedVecSet::from(vec![SECONDARY_BUTTON]),
1240                SortedVecSet::from(vec![PRIMARY_BUTTON, SECONDARY_BUTTON]),
1241                event_time_u64,
1242                &descriptor,
1243            ),
1244        ];
1245
1246        assert_input_report_sequence_generates_events!(
1247            input_reports: input_reports,
1248            expected_events: expected_events,
1249            device_descriptor: descriptor,
1250            device_type: MouseBinding,
1251        );
1252    }
1253
1254    /// Tests that two staggered button presses followed by stagged releases generate four mouse
1255    /// events with distinct `affected_buttons` and `pressed_buttons`.
1256    /// Specifically, we test and expect the following in order:
1257    /// | Action           | MousePhase | `affected_buttons` | `pressed_buttons` |
1258    /// | ---------------- | ---------- | ------------------ | ----------------- |
1259    /// | Press button 1   | Down       | [1]                | [1]               |
1260    /// | Press button 2   | Down       | [2]                | [1, 2]            |
1261    /// | Release button 1 | Up         | [1]                | [2]               |
1262    /// | Release button 2 | Up         | [2]                | []                |
1263    #[fasync::run_singlethreaded(test)]
1264    async fn down_down_up_up() {
1265        const PRIMARY_BUTTON: u8 = 1;
1266        const SECONDARY_BUTTON: u8 = 2;
1267
1268        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1269        let first_report = testing_utilities::create_mouse_input_report_relative(
1270            Position::zero(),
1271            None, /* scroll_v */
1272            None, /* scroll_h */
1273            vec![PRIMARY_BUTTON],
1274            event_time_i64,
1275        );
1276        let second_report = testing_utilities::create_mouse_input_report_relative(
1277            Position::zero(),
1278            None, /* scroll_v */
1279            None, /* scroll_h */
1280            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1281            event_time_i64,
1282        );
1283        let third_report = testing_utilities::create_mouse_input_report_relative(
1284            Position::zero(),
1285            None, /* scroll_v */
1286            None, /* scroll_h */
1287            vec![SECONDARY_BUTTON],
1288            event_time_i64,
1289        );
1290        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1291            Position::zero(),
1292            None, /* scroll_v */
1293            None, /* scroll_h */
1294            vec![],
1295            event_time_i64,
1296        );
1297        let descriptor = mouse_device_descriptor(DEVICE_ID);
1298
1299        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1300        let expected_events = vec![
1301            testing_utilities::create_mouse_event(
1302                MouseLocation::Relative(Default::default()),
1303                None, /* wheel_delta_v */
1304                None, /* wheel_delta_h */
1305                None, /* is_precision_scroll */
1306                MousePhase::Down,
1307                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1308                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1309                event_time_u64,
1310                &descriptor,
1311            ),
1312            testing_utilities::create_mouse_event(
1313                MouseLocation::Relative(Default::default()),
1314                None, /* wheel_delta_v */
1315                None, /* wheel_delta_h */
1316                None, /* is_precision_scroll */
1317                MousePhase::Down,
1318                SortedVecSet::from(vec![SECONDARY_BUTTON]),
1319                SortedVecSet::from(vec![PRIMARY_BUTTON, SECONDARY_BUTTON]),
1320                event_time_u64,
1321                &descriptor,
1322            ),
1323            testing_utilities::create_mouse_event(
1324                MouseLocation::Relative(Default::default()),
1325                None, /* wheel_delta_v */
1326                None, /* wheel_delta_h */
1327                None, /* is_precision_scroll */
1328                MousePhase::Up,
1329                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1330                SortedVecSet::from(vec![SECONDARY_BUTTON]),
1331                event_time_u64,
1332                &descriptor,
1333            ),
1334            testing_utilities::create_mouse_event(
1335                MouseLocation::Relative(Default::default()),
1336                None, /* wheel_delta_v */
1337                None, /* wheel_delta_h */
1338                None, /* is_precision_scroll */
1339                MousePhase::Up,
1340                SortedVecSet::from(vec![SECONDARY_BUTTON]),
1341                SortedVecSet::new(),
1342                event_time_u64,
1343                &descriptor,
1344            ),
1345        ];
1346
1347        assert_input_report_sequence_generates_events!(
1348            input_reports: input_reports,
1349            expected_events: expected_events,
1350            device_descriptor: descriptor,
1351            device_type: MouseBinding,
1352        );
1353    }
1354
1355    /// Test simple scroll in vertical and horizontal.
1356    #[fasync::run_singlethreaded(test)]
1357    async fn scroll() {
1358        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1359        let first_report = testing_utilities::create_mouse_input_report_relative(
1360            Position::zero(),
1361            Some(1),
1362            None,
1363            vec![],
1364            event_time_i64,
1365        );
1366        let second_report = testing_utilities::create_mouse_input_report_relative(
1367            Position::zero(),
1368            None,
1369            Some(1),
1370            vec![],
1371            event_time_i64,
1372        );
1373
1374        let descriptor = mouse_device_descriptor(DEVICE_ID);
1375
1376        let input_reports = vec![first_report, second_report];
1377        let expected_events = vec![
1378            testing_utilities::create_mouse_event(
1379                MouseLocation::Relative(Default::default()),
1380                wheel_delta_ticks(1),
1381                None,
1382                Some(PrecisionScroll::No),
1383                MousePhase::Wheel,
1384                SortedVecSet::new(),
1385                SortedVecSet::new(),
1386                event_time_u64,
1387                &descriptor,
1388            ),
1389            testing_utilities::create_mouse_event(
1390                MouseLocation::Relative(Default::default()),
1391                None,
1392                wheel_delta_ticks(1),
1393                Some(PrecisionScroll::No),
1394                MousePhase::Wheel,
1395                SortedVecSet::new(),
1396                SortedVecSet::new(),
1397                event_time_u64,
1398                &descriptor,
1399            ),
1400        ];
1401
1402        assert_input_report_sequence_generates_events!(
1403            input_reports: input_reports,
1404            expected_events: expected_events,
1405            device_descriptor: descriptor,
1406            device_type: MouseBinding,
1407        );
1408    }
1409
1410    /// Test button down -> scroll -> button up -> continue scroll.
1411    #[fasync::run_singlethreaded(test)]
1412    async fn down_scroll_up_scroll() {
1413        const PRIMARY_BUTTON: u8 = 1;
1414
1415        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1416        let first_report = testing_utilities::create_mouse_input_report_relative(
1417            Position::zero(),
1418            None, /* scroll_v */
1419            None, /* scroll_h */
1420            vec![PRIMARY_BUTTON],
1421            event_time_i64,
1422        );
1423        let second_report = testing_utilities::create_mouse_input_report_relative(
1424            Position::zero(),
1425            Some(1),
1426            None,
1427            vec![PRIMARY_BUTTON],
1428            event_time_i64,
1429        );
1430        let third_report = testing_utilities::create_mouse_input_report_relative(
1431            Position::zero(),
1432            None, /* scroll_v */
1433            None, /* scroll_h */
1434            vec![],
1435            event_time_i64,
1436        );
1437        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1438            Position::zero(),
1439            Some(1),
1440            None,
1441            vec![],
1442            event_time_i64,
1443        );
1444
1445        let descriptor = mouse_device_descriptor(DEVICE_ID);
1446
1447        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1448        let expected_events = vec![
1449            testing_utilities::create_mouse_event(
1450                MouseLocation::Relative(Default::default()),
1451                None, /* wheel_delta_v */
1452                None, /* wheel_delta_h */
1453                None, /* is_precision_scroll */
1454                MousePhase::Down,
1455                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1456                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1457                event_time_u64,
1458                &descriptor,
1459            ),
1460            testing_utilities::create_mouse_event(
1461                MouseLocation::Relative(Default::default()),
1462                wheel_delta_ticks(1),
1463                None,
1464                Some(PrecisionScroll::No),
1465                MousePhase::Wheel,
1466                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1467                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1468                event_time_u64,
1469                &descriptor,
1470            ),
1471            testing_utilities::create_mouse_event(
1472                MouseLocation::Relative(Default::default()),
1473                None, /* wheel_delta_v */
1474                None, /* wheel_delta_h */
1475                None, /* is_precision_scroll */
1476                MousePhase::Up,
1477                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1478                SortedVecSet::new(),
1479                event_time_u64,
1480                &descriptor,
1481            ),
1482            testing_utilities::create_mouse_event(
1483                MouseLocation::Relative(Default::default()),
1484                wheel_delta_ticks(1),
1485                None,
1486                Some(PrecisionScroll::No),
1487                MousePhase::Wheel,
1488                SortedVecSet::new(),
1489                SortedVecSet::new(),
1490                event_time_u64,
1491                &descriptor,
1492            ),
1493        ];
1494
1495        assert_input_report_sequence_generates_events!(
1496            input_reports: input_reports,
1497            expected_events: expected_events,
1498            device_descriptor: descriptor,
1499            device_type: MouseBinding,
1500        );
1501    }
1502
1503    /// Test button down with scroll -> button up with scroll -> scroll.
1504    #[fasync::run_singlethreaded(test)]
1505    async fn down_scroll_bundle_up_scroll_bundle() {
1506        const PRIMARY_BUTTON: u8 = 1;
1507
1508        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1509        let first_report = testing_utilities::create_mouse_input_report_relative(
1510            Position::zero(),
1511            Some(1),
1512            None,
1513            vec![PRIMARY_BUTTON],
1514            event_time_i64,
1515        );
1516        let second_report = testing_utilities::create_mouse_input_report_relative(
1517            Position::zero(),
1518            Some(1),
1519            None,
1520            vec![],
1521            event_time_i64,
1522        );
1523        let third_report = testing_utilities::create_mouse_input_report_relative(
1524            Position::zero(),
1525            Some(1),
1526            None,
1527            vec![],
1528            event_time_i64,
1529        );
1530
1531        let descriptor = mouse_device_descriptor(DEVICE_ID);
1532
1533        let input_reports = vec![first_report, second_report, third_report];
1534        let expected_events = vec![
1535            testing_utilities::create_mouse_event(
1536                MouseLocation::Relative(Default::default()),
1537                None, /* wheel_delta_v */
1538                None, /* wheel_delta_h */
1539                None, /* is_precision_scroll */
1540                MousePhase::Down,
1541                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1542                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1543                event_time_u64,
1544                &descriptor,
1545            ),
1546            testing_utilities::create_mouse_event(
1547                MouseLocation::Relative(Default::default()),
1548                wheel_delta_ticks(1),
1549                None,
1550                Some(PrecisionScroll::No),
1551                MousePhase::Wheel,
1552                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1553                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1554                event_time_u64,
1555                &descriptor,
1556            ),
1557            testing_utilities::create_mouse_event(
1558                MouseLocation::Relative(Default::default()),
1559                wheel_delta_ticks(1),
1560                None,
1561                Some(PrecisionScroll::No),
1562                MousePhase::Wheel,
1563                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1564                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1565                event_time_u64,
1566                &descriptor,
1567            ),
1568            testing_utilities::create_mouse_event(
1569                MouseLocation::Relative(Default::default()),
1570                None, /* wheel_delta_v */
1571                None, /* wheel_delta_h */
1572                None, /* is_precision_scroll */
1573                MousePhase::Up,
1574                SortedVecSet::from(vec![PRIMARY_BUTTON]),
1575                SortedVecSet::new(),
1576                event_time_u64,
1577                &descriptor,
1578            ),
1579            testing_utilities::create_mouse_event(
1580                MouseLocation::Relative(Default::default()),
1581                wheel_delta_ticks(1),
1582                None,
1583                Some(PrecisionScroll::No),
1584                MousePhase::Wheel,
1585                SortedVecSet::new(),
1586                SortedVecSet::new(),
1587                event_time_u64,
1588                &descriptor,
1589            ),
1590        ];
1591
1592        assert_input_report_sequence_generates_events!(
1593            input_reports: input_reports,
1594            expected_events: expected_events,
1595            device_descriptor: descriptor,
1596            device_type: MouseBinding,
1597        );
1598    }
1599}