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