input_pipeline/
mouse_binding.rs

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