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