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