Skip to main content

input_pipeline/
mouse_binding.rs

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