input_pipeline/
mouse_binding.rs

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