Skip to main content

input_pipeline/
mouse_binding.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::utils::{self, Position};
7use crate::{Transport, metrics, mouse_model_database};
8use anyhow::{Error, format_err};
9use async_trait::async_trait;
10use fidl::HandleBased;
11use fidl_next_fuchsia_input_report::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: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
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: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
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_next_fuchsia_input_report::DeviceDescriptor = match device
384            .get_descriptor()
385            .await
386        {
387            Ok(res) => res.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
413                .position_x
414                .as_ref()
415                .map(|axis| utils::range_to_old(&axis.range)),
416            absolute_y_range: mouse_input_descriptor
417                .position_y
418                .as_ref()
419                .map(|axis| utils::range_to_old(&axis.range)),
420            wheel_v_range: utils::axis_to_old(mouse_input_descriptor.scroll_v.as_ref()),
421            wheel_h_range: utils::axis_to_old(mouse_input_descriptor.scroll_h.as_ref()),
422            buttons: mouse_input_descriptor.buttons,
423            counts_per_mm: model.counts_per_mm,
424        };
425
426        Ok((Self { event_sender: input_event_sender, device_descriptor }, input_device_status))
427    }
428
429    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
430    ///
431    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
432    ///
433    /// # Parameters
434    /// `reports`: The incoming [`InputReport`].
435    /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be
436    ///                    used to determine, for example, which keys are no longer present in
437    ///                    a keyboard report to generate key released events. If `None`, no
438    ///                    previous report was found.
439    /// `device_descriptor`: The descriptor for the input device generating the input reports.
440    /// `input_event_sender`: The sender for the device binding's input event stream.
441    ///
442    /// # Returns
443    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
444    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
445    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
446    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
447    /// binding does not generate InputEvents asynchronously, this will be `None`.
448    ///
449    /// The returned [`InputReport`] is guaranteed to have no `wake_lease`.
450    fn process_reports(
451        reports: Vec<InputReport>,
452        mut previous_report: Option<InputReport>,
453        device_descriptor: &input_device::InputDeviceDescriptor,
454        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
455        inspect_status: &InputDeviceStatus,
456        metrics_logger: &metrics::MetricsLogger,
457        _feature_flags: &input_device::InputPipelineFeatureFlags,
458    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
459        fuchsia_trace::duration!("input", "mouse-binding-process-reports", "num_reports" => reports.len());
460        for report in reports {
461            previous_report = Self::process_report(
462                report,
463                previous_report,
464                device_descriptor,
465                input_event_sender,
466                inspect_status,
467                metrics_logger,
468            );
469        }
470        (previous_report, None)
471    }
472
473    fn process_report(
474        mut report: InputReport,
475        previous_report: Option<InputReport>,
476        device_descriptor: &input_device::InputDeviceDescriptor,
477        input_event_sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
478        inspect_status: &InputDeviceStatus,
479        metrics_logger: &metrics::MetricsLogger,
480    ) -> Option<InputReport> {
481        if let Some(trace_id) = report.trace_id {
482            fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
483        }
484
485        // Extract the wake_lease early to prevent it from leaking. If this is moved
486        // below an early return, the lease could accidentally be stored inside
487        // `previous_report`, which would prevent the system from suspending.
488        let wake_lease = report.wake_lease.take();
489
490        inspect_status.count_received_report(&report);
491        // Input devices can have multiple types so ensure `report` is a MouseInputReport.
492        let mouse_report: &fidl_next_fuchsia_input_report::MouseInputReport = match &report.mouse {
493            Some(mouse) => mouse,
494            None => {
495                inspect_status.count_filtered_report();
496                return previous_report;
497            }
498        };
499
500        let previous_buttons: HashSet<MouseButton> =
501            buttons_from_optional_report(&previous_report.as_ref());
502        let current_buttons: HashSet<MouseButton> = buttons_from_report(&report);
503
504        // Send a Down event with:
505        // * affected_buttons: the buttons that were pressed since the previous report,
506        //   i.e. that are in the current report, but were not in the previous report.
507        // * pressed_buttons: the full set of currently pressed buttons, including the
508        //   recently pressed ones (affected_buttons).
509        send_mouse_event(
510            MouseLocation::Relative(Default::default()),
511            None, /* wheel_delta_v */
512            None, /* wheel_delta_h */
513            MousePhase::Down,
514            current_buttons.difference(&previous_buttons).cloned().collect(),
515            current_buttons.clone(),
516            device_descriptor,
517            input_event_sender,
518            inspect_status,
519            metrics_logger,
520            wake_lease.as_ref().map(|lease| {
521                lease
522                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
523                    .expect("failed to duplicate event pair")
524            }),
525        );
526
527        let counts_per_mm = match device_descriptor {
528            input_device::InputDeviceDescriptor::Mouse(ds) => ds.counts_per_mm,
529            _ => {
530                metrics_logger.log_error(
531                    InputPipelineErrorMetricDimensionEvent::MouseDescriptionNotMouse,
532                    "mouse_binding::process_reports got device_descriptor not mouse".to_string(),
533                );
534                mouse_model_database::db::DEFAULT_COUNTS_PER_MM
535            }
536        };
537
538        // Create a location for the move event. Use the absolute position if available.
539        let location = if let (Some(position_x), Some(position_y)) =
540            (mouse_report.position_x, mouse_report.position_y)
541        {
542            MouseLocation::Absolute(Position { x: position_x as f32, y: position_y as f32 })
543        } else {
544            let movement_x = mouse_report.movement_x.unwrap_or_default() as f32;
545            let movement_y = mouse_report.movement_y.unwrap_or_default() as f32;
546            MouseLocation::Relative(RelativeLocation {
547                millimeters: Position {
548                    x: movement_x / counts_per_mm as f32,
549                    y: movement_y / counts_per_mm as f32,
550                },
551            })
552        };
553
554        // Send a Move event with buttons from both the current report and the previous report.
555        // * affected_buttons and pressed_buttons are identical in this case, since the full
556        //   set of currently pressed buttons are the same set affected by the event.
557        send_mouse_event(
558            location,
559            None, /* wheel_delta_v */
560            None, /* wheel_delta_h */
561            MousePhase::Move,
562            current_buttons.union(&previous_buttons).cloned().collect(),
563            current_buttons.union(&previous_buttons).cloned().collect(),
564            device_descriptor,
565            input_event_sender,
566            inspect_status,
567            metrics_logger,
568            wake_lease.as_ref().map(|lease| {
569                lease
570                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
571                    .expect("failed to duplicate event pair")
572            }),
573        );
574
575        let wheel_delta_v = match mouse_report.scroll_v {
576            None => None,
577            Some(ticks) => {
578                Some(WheelDelta { raw_data: RawWheelDelta::Ticks(ticks), physical_pixel: None })
579            }
580        };
581
582        let wheel_delta_h = match mouse_report.scroll_h {
583            None => None,
584            Some(ticks) => {
585                Some(WheelDelta { raw_data: RawWheelDelta::Ticks(ticks), physical_pixel: None })
586            }
587        };
588
589        // Send a mouse wheel event.
590        send_mouse_event(
591            MouseLocation::Relative(Default::default()),
592            wheel_delta_v,
593            wheel_delta_h,
594            MousePhase::Wheel,
595            current_buttons.union(&previous_buttons).cloned().collect(),
596            current_buttons.union(&previous_buttons).cloned().collect(),
597            device_descriptor,
598            input_event_sender,
599            inspect_status,
600            metrics_logger,
601            wake_lease.as_ref().map(|lease| {
602                lease
603                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
604                    .expect("failed to duplicate event pair")
605            }),
606        );
607
608        // Send an Up event with:
609        // * affected_buttons: the buttons that were released since the previous report,
610        //   i.e. that were in the previous report, but are not in the current report.
611        // * pressed_buttons: the full set of currently pressed buttons, excluding the
612        //   recently released ones (affected_buttons).
613        send_mouse_event(
614            MouseLocation::Relative(Default::default()),
615            None, /* wheel_delta_v */
616            None, /* wheel_delta_h */
617            MousePhase::Up,
618            previous_buttons.difference(&current_buttons).cloned().collect(),
619            current_buttons.clone(),
620            device_descriptor,
621            input_event_sender,
622            inspect_status,
623            metrics_logger,
624            wake_lease,
625        );
626
627        Some(report)
628    }
629}
630
631/// Sends an InputEvent over `sender`.
632///
633/// When no buttons are present, only [`MousePhase::Move`] events will
634/// be sent.
635///
636/// # Parameters
637/// - `location`: The mouse location.
638/// - `wheel_delta_v`: The mouse wheel delta in vertical.
639/// - `wheel_delta_h`: The mouse wheel delta in horizontal.
640/// - `phase`: The phase of the [`buttons`] associated with the input event.
641/// - `buttons`: The buttons relevant to the event.
642/// - `device_descriptor`: The descriptor for the input device generating the input reports.
643/// - `sender`: The stream to send the MouseEvent to.
644/// - `wake_lease`: The wake lease to send with the event.
645fn send_mouse_event(
646    location: MouseLocation,
647    wheel_delta_v: Option<WheelDelta>,
648    wheel_delta_h: Option<WheelDelta>,
649    phase: MousePhase,
650    affected_buttons: HashSet<MouseButton>,
651    pressed_buttons: HashSet<MouseButton>,
652    device_descriptor: &input_device::InputDeviceDescriptor,
653    sender: &mut UnboundedSender<Vec<input_device::InputEvent>>,
654    inspect_status: &InputDeviceStatus,
655    metrics_logger: &metrics::MetricsLogger,
656    wake_lease: Option<zx::EventPair>,
657) {
658    // Only send Down/Up events when there are buttons affected.
659    if (phase == MousePhase::Down || phase == MousePhase::Up) && affected_buttons.is_empty() {
660        return;
661    }
662
663    // Don't send Move events when there is no relative movement.
664    // However, absolute movement is always reported.
665    if phase == MousePhase::Move && location == MouseLocation::Relative(Default::default()) {
666        return;
667    }
668
669    // Only send wheel events when the delta has value.
670    if phase == MousePhase::Wheel && wheel_delta_v.is_none() && wheel_delta_h.is_none() {
671        return;
672    }
673
674    let trace_id = fuchsia_trace::Id::random();
675    fuchsia_trace::duration!("input", "mouse-binding-send-event");
676    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
677
678    let event = input_device::InputEvent {
679        device_event: input_device::InputDeviceEvent::Mouse(MouseEvent::new(
680            location,
681            wheel_delta_v,
682            wheel_delta_h,
683            phase,
684            affected_buttons,
685            pressed_buttons,
686            match phase {
687                MousePhase::Wheel => Some(PrecisionScroll::No),
688                _ => None,
689            },
690            wake_lease,
691        )),
692        device_descriptor: device_descriptor.clone(),
693        event_time: zx::MonotonicInstant::get(),
694        handled: Handled::No,
695        trace_id: Some(trace_id),
696    };
697    let events = vec![event.clone_with_wake_lease()];
698
699    match sender.unbounded_send(events.clone()) {
700        Err(e) => {
701            metrics_logger.log_error(
702                InputPipelineErrorMetricDimensionEvent::MouseFailedToSendEvent,
703                std::format!("Failed to send MouseEvent with error: {:?}", e),
704            );
705        }
706        _ => inspect_status.count_generated_events(&events),
707    }
708}
709
710/// Returns a u32 representation of `buttons`, where each u8 of `buttons` is an id of a button and
711/// indicates the position of a bit to set.
712///
713/// This supports hashsets with numbers from 1 to fidl_input_report::MOUSE_MAX_NUM_BUTTONS.
714///
715/// # Parameters
716/// - `buttons`: The hashset containing the position of bits to be set.
717///
718/// # Example
719/// ```
720/// let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, 5]).into_iter());
721/// assert_eq!(bits, 21 /* ...00010101 */)
722/// ```
723pub fn get_u32_from_buttons(buttons: &HashSet<MouseButton>) -> u32 {
724    let mut bits: u32 = 0;
725    for button in buttons {
726        if *button > 0 && *button <= fidl_input_report::MOUSE_MAX_NUM_BUTTONS as u8 {
727            bits = ((1 as u32) << *button - 1) | bits;
728        }
729    }
730
731    bits
732}
733
734/// Returns the set of pressed buttons present in the given input report.
735///
736/// # Parameters
737/// - `report`: The input report to parse the mouse buttons from.
738fn buttons_from_report(
739    input_report: &fidl_next_fuchsia_input_report::InputReport,
740) -> HashSet<MouseButton> {
741    buttons_from_optional_report(&Some(input_report))
742}
743
744/// Returns the set of pressed buttons present in the given input report.
745///
746/// # Parameters
747/// - `report`: The input report to parse the mouse buttons from.
748fn buttons_from_optional_report(
749    input_report: &Option<&fidl_next_fuchsia_input_report::InputReport>,
750) -> HashSet<MouseButton> {
751    input_report
752        .as_ref()
753        .and_then(|unwrapped_report| unwrapped_report.mouse.as_ref())
754        .and_then(|mouse_report| match &mouse_report.pressed_buttons {
755            Some(buttons) => Some(HashSet::from_iter(buttons.iter().cloned())),
756            None => None,
757        })
758        .unwrap_or_default()
759}
760
761#[cfg(test)]
762mod tests {
763    use super::*;
764    use crate::testing_utilities;
765    use fuchsia_async as fasync;
766    use futures::StreamExt;
767    use pretty_assertions::assert_eq;
768
769    const DEVICE_ID: u32 = 1;
770    const COUNTS_PER_MM: u32 = 12;
771
772    fn mouse_device_descriptor(device_id: u32) -> input_device::InputDeviceDescriptor {
773        input_device::InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
774            device_id,
775            absolute_x_range: None,
776            absolute_y_range: None,
777            wheel_v_range: Some(fidl_fuchsia_input_report::Axis {
778                range: fidl_input_report::Range { min: -1, max: 1 },
779                unit: fidl_input_report::Unit {
780                    type_: fidl_input_report::UnitType::Other,
781                    exponent: 1,
782                },
783            }),
784            wheel_h_range: Some(fidl_fuchsia_input_report::Axis {
785                range: fidl_input_report::Range { min: -1, max: 1 },
786                unit: fidl_input_report::Unit {
787                    type_: fidl_input_report::UnitType::Other,
788                    exponent: 1,
789                },
790            }),
791            buttons: None,
792            counts_per_mm: COUNTS_PER_MM,
793        })
794    }
795
796    fn wheel_delta_ticks(delta: i64) -> Option<WheelDelta> {
797        Some(WheelDelta { raw_data: RawWheelDelta::Ticks(delta), physical_pixel: None })
798    }
799
800    // Tests that the right u32 representation is returned from a vector of digits.
801    #[test]
802    fn get_u32_from_buttons_test() {
803        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, 5].into_iter()));
804        assert_eq!(bits, 21 /* 0...00010101 */)
805    }
806
807    // Tests that the right u32 representation is returned from a vector of digits that includes 0.
808    #[test]
809    fn get_u32_with_0_in_vector() {
810        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![0, 1, 3].into_iter()));
811        assert_eq!(bits, 5 /* 0...00000101 */)
812    }
813
814    // Tests that the right u32 representation is returned from an empty vector.
815    #[test]
816    fn get_u32_with_empty_vector() {
817        let bits = get_u32_from_buttons(&HashSet::new());
818        assert_eq!(bits, 0 /* 0...00000000 */)
819    }
820
821    // Tests that the right u32 representation is returned from a vector containing std::u8::MAX.
822    #[test]
823    fn get_u32_with_u8_max_in_vector() {
824        let bits = get_u32_from_buttons(&HashSet::from_iter(vec![1, 3, std::u8::MAX].into_iter()));
825        assert_eq!(bits, 5 /* 0...00000101 */)
826    }
827
828    // Tests that the right u32 representation is returned from a vector containing the largest
829    // button id possible.
830    #[test]
831    fn get_u32_with_max_mouse_buttons() {
832        let bits = get_u32_from_buttons(&HashSet::from_iter(
833            vec![1, 3, fidl_input_report::MOUSE_MAX_NUM_BUTTONS as MouseButton].into_iter(),
834        ));
835        assert_eq!(bits, 2147483653 /* 10...00000101 */)
836    }
837
838    /// Tests that a report containing no buttons but with movement generates a move event.
839    #[fasync::run_singlethreaded(test)]
840    async fn movement_without_button() {
841        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
842        let first_report = testing_utilities::create_mouse_input_report_relative(
843            Position { x: 10.0, y: 16.0 },
844            None, /* scroll_v */
845            None, /* scroll_h */
846            vec![],
847            event_time_i64,
848        );
849        let descriptor = mouse_device_descriptor(DEVICE_ID);
850
851        let input_reports = vec![first_report];
852        let expected_events = vec![testing_utilities::create_mouse_event(
853            MouseLocation::Relative(RelativeLocation {
854                millimeters: Position {
855                    x: 10.0 / COUNTS_PER_MM as f32,
856                    y: 16.0 / COUNTS_PER_MM as f32,
857                },
858            }),
859            None, /* wheel_delta_v */
860            None, /* wheel_delta_h */
861            None, /* is_precision_scroll */
862            MousePhase::Move,
863            HashSet::new(),
864            HashSet::new(),
865            event_time_u64,
866            &descriptor,
867        )];
868
869        assert_input_report_sequence_generates_events!(
870            input_reports: input_reports,
871            expected_events: expected_events,
872            device_descriptor: descriptor,
873            device_type: MouseBinding,
874        );
875    }
876
877    /// Tests that a report containing a new mouse button generates a down event.
878    #[fasync::run_singlethreaded(test)]
879    async fn down_without_movement() {
880        let mouse_button: MouseButton = 3;
881        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
882        let first_report = testing_utilities::create_mouse_input_report_relative(
883            Position::zero(),
884            None, /* scroll_v */
885            None, /* scroll_h */
886            vec![mouse_button],
887            event_time_i64,
888        );
889        let descriptor = mouse_device_descriptor(DEVICE_ID);
890
891        let input_reports = vec![first_report];
892        let expected_events = vec![testing_utilities::create_mouse_event(
893            MouseLocation::Relative(Default::default()),
894            None, /* wheel_delta_v */
895            None, /* wheel_delta_h */
896            None, /* is_precision_scroll */
897            MousePhase::Down,
898            HashSet::from_iter(vec![mouse_button].into_iter()),
899            HashSet::from_iter(vec![mouse_button].into_iter()),
900            event_time_u64,
901            &descriptor,
902        )];
903
904        assert_input_report_sequence_generates_events!(
905            input_reports: input_reports,
906            expected_events: expected_events,
907            device_descriptor: descriptor,
908            device_type: MouseBinding,
909        );
910    }
911
912    /// Tests that a report containing a new mouse button with movement generates a down event and a
913    /// move event.
914    #[fasync::run_singlethreaded(test)]
915    async fn down_with_movement() {
916        let mouse_button: MouseButton = 3;
917        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
918        let first_report = testing_utilities::create_mouse_input_report_relative(
919            Position { x: 10.0, y: 16.0 },
920            None, /* scroll_v */
921            None, /* scroll_h */
922            vec![mouse_button],
923            event_time_i64,
924        );
925        let descriptor = mouse_device_descriptor(DEVICE_ID);
926
927        let input_reports = vec![first_report];
928        let expected_events = vec![
929            testing_utilities::create_mouse_event(
930                MouseLocation::Relative(Default::default()),
931                None, /* wheel_delta_v */
932                None, /* wheel_delta_h */
933                None, /* is_precision_scroll */
934                MousePhase::Down,
935                HashSet::from_iter(vec![mouse_button].into_iter()),
936                HashSet::from_iter(vec![mouse_button].into_iter()),
937                event_time_u64,
938                &descriptor,
939            ),
940            testing_utilities::create_mouse_event(
941                MouseLocation::Relative(RelativeLocation {
942                    millimeters: Position {
943                        x: 10.0 / COUNTS_PER_MM as f32,
944                        y: 16.0 / COUNTS_PER_MM as f32,
945                    },
946                }),
947                None, /* wheel_delta_v */
948                None, /* wheel_delta_h */
949                None, /* is_precision_scroll */
950                MousePhase::Move,
951                HashSet::from_iter(vec![mouse_button].into_iter()),
952                HashSet::from_iter(vec![mouse_button].into_iter()),
953                event_time_u64,
954                &descriptor,
955            ),
956        ];
957
958        assert_input_report_sequence_generates_events!(
959            input_reports: input_reports,
960            expected_events: expected_events,
961            device_descriptor: descriptor,
962            device_type: MouseBinding,
963        );
964    }
965
966    /// Tests that a press and release of a mouse button without movement generates a down and up event.
967    #[fasync::run_singlethreaded(test)]
968    async fn down_up() {
969        let button = 1;
970        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
971        let first_report = testing_utilities::create_mouse_input_report_relative(
972            Position::zero(),
973            None, /* scroll_v */
974            None, /* scroll_h */
975            vec![button],
976            event_time_i64,
977        );
978        let second_report = testing_utilities::create_mouse_input_report_relative(
979            Position::zero(),
980            None, /* scroll_v */
981            None, /* scroll_h */
982            vec![],
983            event_time_i64,
984        );
985        let descriptor = mouse_device_descriptor(DEVICE_ID);
986
987        let input_reports = vec![first_report, second_report];
988        let expected_events = vec![
989            testing_utilities::create_mouse_event(
990                MouseLocation::Relative(Default::default()),
991                None, /* wheel_delta_v */
992                None, /* wheel_delta_h */
993                None, /* is_precision_scroll */
994                MousePhase::Down,
995                HashSet::from_iter(vec![button].into_iter()),
996                HashSet::from_iter(vec![button].into_iter()),
997                event_time_u64,
998                &descriptor,
999            ),
1000            testing_utilities::create_mouse_event(
1001                MouseLocation::Relative(Default::default()),
1002                None, /* wheel_delta_v */
1003                None, /* wheel_delta_h */
1004                None, /* is_precision_scroll */
1005                MousePhase::Up,
1006                HashSet::from_iter(vec![button].into_iter()),
1007                HashSet::new(),
1008                event_time_u64,
1009                &descriptor,
1010            ),
1011        ];
1012
1013        assert_input_report_sequence_generates_events!(
1014            input_reports: input_reports,
1015            expected_events: expected_events,
1016            device_descriptor: descriptor,
1017            device_type: MouseBinding,
1018        );
1019    }
1020
1021    /// Tests that a press and release of a mouse button with movement generates down, move, and up events.
1022    #[fasync::run_singlethreaded(test)]
1023    async fn down_up_with_movement() {
1024        let button = 1;
1025
1026        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1027        let first_report = testing_utilities::create_mouse_input_report_relative(
1028            Position::zero(),
1029            None, /* scroll_v */
1030            None, /* scroll_h */
1031            vec![button],
1032            event_time_i64,
1033        );
1034        let second_report = testing_utilities::create_mouse_input_report_relative(
1035            Position { x: 10.0, y: 16.0 },
1036            None, /* scroll_v */
1037            None, /* scroll_h */
1038            vec![],
1039            event_time_i64,
1040        );
1041        let descriptor = mouse_device_descriptor(DEVICE_ID);
1042
1043        let input_reports = vec![first_report, second_report];
1044        let expected_events = vec![
1045            testing_utilities::create_mouse_event(
1046                MouseLocation::Relative(Default::default()),
1047                None, /* wheel_delta_v */
1048                None, /* wheel_delta_h */
1049                None, /* is_precision_scroll */
1050                MousePhase::Down,
1051                HashSet::from_iter(vec![button].into_iter()),
1052                HashSet::from_iter(vec![button].into_iter()),
1053                event_time_u64,
1054                &descriptor,
1055            ),
1056            testing_utilities::create_mouse_event(
1057                MouseLocation::Relative(RelativeLocation {
1058                    millimeters: Position {
1059                        x: 10.0 / COUNTS_PER_MM as f32,
1060                        y: 16.0 / COUNTS_PER_MM as f32,
1061                    },
1062                }),
1063                None, /* wheel_delta_v */
1064                None, /* wheel_delta_h */
1065                None, /* is_precision_scroll */
1066                MousePhase::Move,
1067                HashSet::from_iter(vec![button].into_iter()),
1068                HashSet::from_iter(vec![button].into_iter()),
1069                event_time_u64,
1070                &descriptor,
1071            ),
1072            testing_utilities::create_mouse_event(
1073                MouseLocation::Relative(Default::default()),
1074                None, /* wheel_delta_v */
1075                None, /* wheel_delta_h */
1076                None, /* is_precision_scroll */
1077                MousePhase::Up,
1078                HashSet::from_iter(vec![button].into_iter()),
1079                HashSet::new(),
1080                event_time_u64,
1081                &descriptor,
1082            ),
1083        ];
1084
1085        assert_input_report_sequence_generates_events!(
1086            input_reports: input_reports,
1087            expected_events: expected_events,
1088            device_descriptor: descriptor,
1089            device_type: MouseBinding,
1090        );
1091    }
1092
1093    /// Tests that a press, move, and release of a button generates down, move, and up events.
1094    /// This specifically tests the separate input report containing the movement, instead of sending
1095    /// the movement as part of the down or up events.
1096    #[fasync::run_singlethreaded(test)]
1097    async fn down_move_up() {
1098        let button = 1;
1099
1100        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1101        let first_report = testing_utilities::create_mouse_input_report_relative(
1102            Position::zero(),
1103            None, /* scroll_v */
1104            None, /* scroll_h */
1105            vec![button],
1106            event_time_i64,
1107        );
1108        let second_report = testing_utilities::create_mouse_input_report_relative(
1109            Position { x: 10.0, y: 16.0 },
1110            None, /* scroll_v */
1111            None, /* scroll_h */
1112            vec![button],
1113            event_time_i64,
1114        );
1115        let third_report = testing_utilities::create_mouse_input_report_relative(
1116            Position::zero(),
1117            None, /* scroll_v */
1118            None, /* scroll_h */
1119            vec![],
1120            event_time_i64,
1121        );
1122        let descriptor = mouse_device_descriptor(DEVICE_ID);
1123
1124        let input_reports = vec![first_report, second_report, third_report];
1125        let expected_events = vec![
1126            testing_utilities::create_mouse_event(
1127                MouseLocation::Relative(Default::default()),
1128                None, /* wheel_delta_v */
1129                None, /* wheel_delta_h */
1130                None, /* is_precision_scroll */
1131                MousePhase::Down,
1132                HashSet::from_iter(vec![button].into_iter()),
1133                HashSet::from_iter(vec![button].into_iter()),
1134                event_time_u64,
1135                &descriptor,
1136            ),
1137            testing_utilities::create_mouse_event(
1138                MouseLocation::Relative(RelativeLocation {
1139                    millimeters: Position {
1140                        x: 10.0 / COUNTS_PER_MM as f32,
1141                        y: 16.0 / COUNTS_PER_MM as f32,
1142                    },
1143                }),
1144                None, /* wheel_delta_v */
1145                None, /* wheel_delta_h */
1146                None, /* is_precision_scroll */
1147                MousePhase::Move,
1148                HashSet::from_iter(vec![button].into_iter()),
1149                HashSet::from_iter(vec![button].into_iter()),
1150                event_time_u64,
1151                &descriptor,
1152            ),
1153            testing_utilities::create_mouse_event(
1154                MouseLocation::Relative(Default::default()),
1155                None, /* wheel_delta_v */
1156                None, /* wheel_delta_h */
1157                None, /* is_precision_scroll */
1158                MousePhase::Up,
1159                HashSet::from_iter(vec![button].into_iter()),
1160                HashSet::new(),
1161                event_time_u64,
1162                &descriptor,
1163            ),
1164        ];
1165
1166        assert_input_report_sequence_generates_events!(
1167            input_reports: input_reports,
1168            expected_events: expected_events,
1169            device_descriptor: descriptor,
1170            device_type: MouseBinding,
1171        );
1172    }
1173
1174    /// Tests that a report with absolute movement to {0, 0} generates a move event.
1175    #[fasync::run_until_stalled(test)]
1176    async fn absolute_movement_to_origin() {
1177        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1178        let descriptor = mouse_device_descriptor(DEVICE_ID);
1179
1180        let input_reports = vec![testing_utilities::create_mouse_input_report_absolute(
1181            Position::zero(),
1182            None, /* wheel_delta_v */
1183            None, /* wheel_delta_h */
1184            vec![],
1185            event_time_i64,
1186        )];
1187        let expected_events = vec![testing_utilities::create_mouse_event(
1188            MouseLocation::Absolute(Position { x: 0.0, y: 0.0 }),
1189            None, /* wheel_delta_v */
1190            None, /* wheel_delta_h */
1191            None, /* is_precision_scroll */
1192            MousePhase::Move,
1193            HashSet::new(),
1194            HashSet::new(),
1195            event_time_u64,
1196            &descriptor,
1197        )];
1198
1199        assert_input_report_sequence_generates_events!(
1200            input_reports: input_reports,
1201            expected_events: expected_events,
1202            device_descriptor: descriptor,
1203            device_type: MouseBinding,
1204        );
1205    }
1206
1207    /// Tests that a report that contains both a relative movement and absolute position
1208    /// generates a move event to the absolute position.
1209    #[fasync::run_until_stalled(test)]
1210    async fn report_with_both_movement_and_position() {
1211        let relative_movement = Position { x: 5.0, y: 5.0 };
1212        let absolute_position = Position { x: 10.0, y: 10.0 };
1213        let expected_location = MouseLocation::Absolute(absolute_position);
1214
1215        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1216        let descriptor = mouse_device_descriptor(DEVICE_ID);
1217
1218        let input_reports = vec![fidl_next_fuchsia_input_report::InputReport {
1219            event_time: Some(event_time_i64),
1220            keyboard: None,
1221            mouse: Some(fidl_next_fuchsia_input_report::MouseInputReport {
1222                movement_x: Some(relative_movement.x as i64),
1223                movement_y: Some(relative_movement.y as i64),
1224                position_x: Some(absolute_position.x as i64),
1225                position_y: Some(absolute_position.y as i64),
1226                scroll_h: None,
1227                scroll_v: None,
1228                pressed_buttons: None,
1229                ..Default::default()
1230            }),
1231            touch: None,
1232            sensor: None,
1233            consumer_control: None,
1234            trace_id: None,
1235            ..Default::default()
1236        }];
1237        let expected_events = vec![testing_utilities::create_mouse_event(
1238            expected_location,
1239            None, /* wheel_delta_v */
1240            None, /* wheel_delta_h */
1241            None, /* is_precision_scroll */
1242            MousePhase::Move,
1243            HashSet::new(),
1244            HashSet::new(),
1245            event_time_u64,
1246            &descriptor,
1247        )];
1248
1249        assert_input_report_sequence_generates_events!(
1250            input_reports: input_reports,
1251            expected_events: expected_events,
1252            device_descriptor: descriptor,
1253            device_type: MouseBinding,
1254        );
1255    }
1256
1257    /// Tests that two separate button presses generate two separate down events with differing
1258    /// sets of `affected_buttons` and `pressed_buttons`.
1259    #[fasync::run_singlethreaded(test)]
1260    async fn down_down() {
1261        const PRIMARY_BUTTON: u8 = 1;
1262        const SECONDARY_BUTTON: u8 = 2;
1263
1264        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1265        let first_report = testing_utilities::create_mouse_input_report_relative(
1266            Position::zero(),
1267            None, /* scroll_v */
1268            None, /* scroll_h */
1269            vec![PRIMARY_BUTTON],
1270            event_time_i64,
1271        );
1272        let second_report = testing_utilities::create_mouse_input_report_relative(
1273            Position::zero(),
1274            None, /* scroll_v */
1275            None, /* scroll_h */
1276            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1277            event_time_i64,
1278        );
1279        let descriptor = mouse_device_descriptor(DEVICE_ID);
1280
1281        let input_reports = vec![first_report, second_report];
1282        let expected_events = vec![
1283            testing_utilities::create_mouse_event(
1284                MouseLocation::Relative(Default::default()),
1285                None, /* wheel_delta_v */
1286                None, /* wheel_delta_h */
1287                None, /* is_precision_scroll */
1288                MousePhase::Down,
1289                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1290                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1291                event_time_u64,
1292                &descriptor,
1293            ),
1294            testing_utilities::create_mouse_event(
1295                MouseLocation::Relative(Default::default()),
1296                None, /* wheel_delta_v */
1297                None, /* wheel_delta_h */
1298                None, /* is_precision_scroll */
1299                MousePhase::Down,
1300                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1301                HashSet::from_iter(vec![PRIMARY_BUTTON, SECONDARY_BUTTON].into_iter()),
1302                event_time_u64,
1303                &descriptor,
1304            ),
1305        ];
1306
1307        assert_input_report_sequence_generates_events!(
1308            input_reports: input_reports,
1309            expected_events: expected_events,
1310            device_descriptor: descriptor,
1311            device_type: MouseBinding,
1312        );
1313    }
1314
1315    /// Tests that two staggered button presses followed by stagged releases generate four mouse
1316    /// events with distinct `affected_buttons` and `pressed_buttons`.
1317    /// Specifically, we test and expect the following in order:
1318    /// | Action           | MousePhase | `affected_buttons` | `pressed_buttons` |
1319    /// | ---------------- | ---------- | ------------------ | ----------------- |
1320    /// | Press button 1   | Down       | [1]                | [1]               |
1321    /// | Press button 2   | Down       | [2]                | [1, 2]            |
1322    /// | Release button 1 | Up         | [1]                | [2]               |
1323    /// | Release button 2 | Up         | [2]                | []                |
1324    #[fasync::run_singlethreaded(test)]
1325    async fn down_down_up_up() {
1326        const PRIMARY_BUTTON: u8 = 1;
1327        const SECONDARY_BUTTON: u8 = 2;
1328
1329        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1330        let first_report = testing_utilities::create_mouse_input_report_relative(
1331            Position::zero(),
1332            None, /* scroll_v */
1333            None, /* scroll_h */
1334            vec![PRIMARY_BUTTON],
1335            event_time_i64,
1336        );
1337        let second_report = testing_utilities::create_mouse_input_report_relative(
1338            Position::zero(),
1339            None, /* scroll_v */
1340            None, /* scroll_h */
1341            vec![PRIMARY_BUTTON, SECONDARY_BUTTON],
1342            event_time_i64,
1343        );
1344        let third_report = testing_utilities::create_mouse_input_report_relative(
1345            Position::zero(),
1346            None, /* scroll_v */
1347            None, /* scroll_h */
1348            vec![SECONDARY_BUTTON],
1349            event_time_i64,
1350        );
1351        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1352            Position::zero(),
1353            None, /* scroll_v */
1354            None, /* scroll_h */
1355            vec![],
1356            event_time_i64,
1357        );
1358        let descriptor = mouse_device_descriptor(DEVICE_ID);
1359
1360        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1361        let expected_events = vec![
1362            testing_utilities::create_mouse_event(
1363                MouseLocation::Relative(Default::default()),
1364                None, /* wheel_delta_v */
1365                None, /* wheel_delta_h */
1366                None, /* is_precision_scroll */
1367                MousePhase::Down,
1368                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1369                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1370                event_time_u64,
1371                &descriptor,
1372            ),
1373            testing_utilities::create_mouse_event(
1374                MouseLocation::Relative(Default::default()),
1375                None, /* wheel_delta_v */
1376                None, /* wheel_delta_h */
1377                None, /* is_precision_scroll */
1378                MousePhase::Down,
1379                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1380                HashSet::from_iter(vec![PRIMARY_BUTTON, SECONDARY_BUTTON].into_iter()),
1381                event_time_u64,
1382                &descriptor,
1383            ),
1384            testing_utilities::create_mouse_event(
1385                MouseLocation::Relative(Default::default()),
1386                None, /* wheel_delta_v */
1387                None, /* wheel_delta_h */
1388                None, /* is_precision_scroll */
1389                MousePhase::Up,
1390                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1391                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1392                event_time_u64,
1393                &descriptor,
1394            ),
1395            testing_utilities::create_mouse_event(
1396                MouseLocation::Relative(Default::default()),
1397                None, /* wheel_delta_v */
1398                None, /* wheel_delta_h */
1399                None, /* is_precision_scroll */
1400                MousePhase::Up,
1401                HashSet::from_iter(vec![SECONDARY_BUTTON].into_iter()),
1402                HashSet::new(),
1403                event_time_u64,
1404                &descriptor,
1405            ),
1406        ];
1407
1408        assert_input_report_sequence_generates_events!(
1409            input_reports: input_reports,
1410            expected_events: expected_events,
1411            device_descriptor: descriptor,
1412            device_type: MouseBinding,
1413        );
1414    }
1415
1416    /// Test simple scroll in vertical and horizontal.
1417    #[fasync::run_singlethreaded(test)]
1418    async fn scroll() {
1419        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1420        let first_report = testing_utilities::create_mouse_input_report_relative(
1421            Position::zero(),
1422            Some(1),
1423            None,
1424            vec![],
1425            event_time_i64,
1426        );
1427        let second_report = testing_utilities::create_mouse_input_report_relative(
1428            Position::zero(),
1429            None,
1430            Some(1),
1431            vec![],
1432            event_time_i64,
1433        );
1434
1435        let descriptor = mouse_device_descriptor(DEVICE_ID);
1436
1437        let input_reports = vec![first_report, second_report];
1438        let expected_events = vec![
1439            testing_utilities::create_mouse_event(
1440                MouseLocation::Relative(Default::default()),
1441                wheel_delta_ticks(1),
1442                None,
1443                Some(PrecisionScroll::No),
1444                MousePhase::Wheel,
1445                HashSet::new(),
1446                HashSet::new(),
1447                event_time_u64,
1448                &descriptor,
1449            ),
1450            testing_utilities::create_mouse_event(
1451                MouseLocation::Relative(Default::default()),
1452                None,
1453                wheel_delta_ticks(1),
1454                Some(PrecisionScroll::No),
1455                MousePhase::Wheel,
1456                HashSet::new(),
1457                HashSet::new(),
1458                event_time_u64,
1459                &descriptor,
1460            ),
1461        ];
1462
1463        assert_input_report_sequence_generates_events!(
1464            input_reports: input_reports,
1465            expected_events: expected_events,
1466            device_descriptor: descriptor,
1467            device_type: MouseBinding,
1468        );
1469    }
1470
1471    /// Test button down -> scroll -> button up -> continue scroll.
1472    #[fasync::run_singlethreaded(test)]
1473    async fn down_scroll_up_scroll() {
1474        const PRIMARY_BUTTON: u8 = 1;
1475
1476        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1477        let first_report = testing_utilities::create_mouse_input_report_relative(
1478            Position::zero(),
1479            None, /* scroll_v */
1480            None, /* scroll_h */
1481            vec![PRIMARY_BUTTON],
1482            event_time_i64,
1483        );
1484        let second_report = testing_utilities::create_mouse_input_report_relative(
1485            Position::zero(),
1486            Some(1),
1487            None,
1488            vec![PRIMARY_BUTTON],
1489            event_time_i64,
1490        );
1491        let third_report = testing_utilities::create_mouse_input_report_relative(
1492            Position::zero(),
1493            None, /* scroll_v */
1494            None, /* scroll_h */
1495            vec![],
1496            event_time_i64,
1497        );
1498        let fourth_report = testing_utilities::create_mouse_input_report_relative(
1499            Position::zero(),
1500            Some(1),
1501            None,
1502            vec![],
1503            event_time_i64,
1504        );
1505
1506        let descriptor = mouse_device_descriptor(DEVICE_ID);
1507
1508        let input_reports = vec![first_report, second_report, third_report, fourth_report];
1509        let expected_events = vec![
1510            testing_utilities::create_mouse_event(
1511                MouseLocation::Relative(Default::default()),
1512                None, /* wheel_delta_v */
1513                None, /* wheel_delta_h */
1514                None, /* is_precision_scroll */
1515                MousePhase::Down,
1516                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1517                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1518                event_time_u64,
1519                &descriptor,
1520            ),
1521            testing_utilities::create_mouse_event(
1522                MouseLocation::Relative(Default::default()),
1523                wheel_delta_ticks(1),
1524                None,
1525                Some(PrecisionScroll::No),
1526                MousePhase::Wheel,
1527                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1528                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1529                event_time_u64,
1530                &descriptor,
1531            ),
1532            testing_utilities::create_mouse_event(
1533                MouseLocation::Relative(Default::default()),
1534                None, /* wheel_delta_v */
1535                None, /* wheel_delta_h */
1536                None, /* is_precision_scroll */
1537                MousePhase::Up,
1538                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1539                HashSet::new(),
1540                event_time_u64,
1541                &descriptor,
1542            ),
1543            testing_utilities::create_mouse_event(
1544                MouseLocation::Relative(Default::default()),
1545                wheel_delta_ticks(1),
1546                None,
1547                Some(PrecisionScroll::No),
1548                MousePhase::Wheel,
1549                HashSet::new(),
1550                HashSet::new(),
1551                event_time_u64,
1552                &descriptor,
1553            ),
1554        ];
1555
1556        assert_input_report_sequence_generates_events!(
1557            input_reports: input_reports,
1558            expected_events: expected_events,
1559            device_descriptor: descriptor,
1560            device_type: MouseBinding,
1561        );
1562    }
1563
1564    /// Test button down with scroll -> button up with scroll -> scroll.
1565    #[fasync::run_singlethreaded(test)]
1566    async fn down_scroll_bundle_up_scroll_bundle() {
1567        const PRIMARY_BUTTON: u8 = 1;
1568
1569        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1570        let first_report = testing_utilities::create_mouse_input_report_relative(
1571            Position::zero(),
1572            Some(1),
1573            None,
1574            vec![PRIMARY_BUTTON],
1575            event_time_i64,
1576        );
1577        let second_report = testing_utilities::create_mouse_input_report_relative(
1578            Position::zero(),
1579            Some(1),
1580            None,
1581            vec![],
1582            event_time_i64,
1583        );
1584        let third_report = testing_utilities::create_mouse_input_report_relative(
1585            Position::zero(),
1586            Some(1),
1587            None,
1588            vec![],
1589            event_time_i64,
1590        );
1591
1592        let descriptor = mouse_device_descriptor(DEVICE_ID);
1593
1594        let input_reports = vec![first_report, second_report, third_report];
1595        let expected_events = vec![
1596            testing_utilities::create_mouse_event(
1597                MouseLocation::Relative(Default::default()),
1598                None, /* wheel_delta_v */
1599                None, /* wheel_delta_h */
1600                None, /* is_precision_scroll */
1601                MousePhase::Down,
1602                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1603                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1604                event_time_u64,
1605                &descriptor,
1606            ),
1607            testing_utilities::create_mouse_event(
1608                MouseLocation::Relative(Default::default()),
1609                wheel_delta_ticks(1),
1610                None,
1611                Some(PrecisionScroll::No),
1612                MousePhase::Wheel,
1613                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1614                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1615                event_time_u64,
1616                &descriptor,
1617            ),
1618            testing_utilities::create_mouse_event(
1619                MouseLocation::Relative(Default::default()),
1620                wheel_delta_ticks(1),
1621                None,
1622                Some(PrecisionScroll::No),
1623                MousePhase::Wheel,
1624                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1625                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1626                event_time_u64,
1627                &descriptor,
1628            ),
1629            testing_utilities::create_mouse_event(
1630                MouseLocation::Relative(Default::default()),
1631                None, /* wheel_delta_v */
1632                None, /* wheel_delta_h */
1633                None, /* is_precision_scroll */
1634                MousePhase::Up,
1635                HashSet::from_iter(vec![PRIMARY_BUTTON].into_iter()),
1636                HashSet::new(),
1637                event_time_u64,
1638                &descriptor,
1639            ),
1640            testing_utilities::create_mouse_event(
1641                MouseLocation::Relative(Default::default()),
1642                wheel_delta_ticks(1),
1643                None,
1644                Some(PrecisionScroll::No),
1645                MousePhase::Wheel,
1646                HashSet::new(),
1647                HashSet::new(),
1648                event_time_u64,
1649                &descriptor,
1650            ),
1651        ];
1652
1653        assert_input_report_sequence_generates_events!(
1654            input_reports: input_reports,
1655            expected_events: expected_events,
1656            device_descriptor: descriptor,
1657            device_type: MouseBinding,
1658        );
1659    }
1660}