Skip to main content

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