Skip to main content

starnix_modules_input_event_conversion/
mouse_fuchsia_to_linux.rs

1// Copyright 2026 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 fidl_fuchsia_ui_pointer::{MouseEvent as FidlMouseEvent, MousePointerSample};
6use starnix_types::time::timeval_from_time;
7use starnix_uapi::uapi;
8use std::collections::VecDeque;
9
10pub struct LinuxMouseEventBatch {
11    pub events: VecDeque<uapi::input_event>,
12    pub count_ignored_events: u64,
13    pub count_converted_events: u64,
14    pub count_unexpected_events: u64,
15    pub last_event_time_ns: i64,
16}
17
18pub fn parse_fidl_mouse_events(mouse_events: Vec<FidlMouseEvent>) -> LinuxMouseEventBatch {
19    let mut count_ignored_events: u64 = 0;
20    let mut count_converted_events: u64 = 0;
21    let mut count_unexpected_events: u64 = 0;
22    let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
23    let mut last_event_time_ns = zx::MonotonicInstant::get();
24
25    let mut total_ticks: i32 = 0;
26
27    for event in mouse_events {
28        match event {
29            FidlMouseEvent {
30                timestamp: Some(time),
31                pointer_sample: Some(MousePointerSample { scroll_v: Some(ticks), .. }),
32                ..
33            } => {
34                last_event_time_ns = zx::MonotonicInstant::from_nanos(time);
35                if ticks != 0 {
36                    total_ticks += ticks as i32;
37                    count_converted_events += 1;
38                } else {
39                    count_ignored_events += 1;
40                }
41            }
42            _ => {
43                count_unexpected_events += 1;
44            }
45        }
46    }
47
48    if total_ticks != 0 {
49        new_events.push_back(uapi::input_event {
50            time: timeval_from_time(last_event_time_ns),
51            type_: uapi::EV_REL as u16,
52            code: uapi::REL_WHEEL as u16,
53            value: total_ticks,
54        });
55    }
56
57    if !new_events.is_empty() {
58        new_events.push_back(uapi::input_event {
59            time: timeval_from_time(last_event_time_ns),
60            type_: uapi::EV_SYN as u16,
61            code: uapi::SYN_REPORT as u16,
62            value: 0,
63        });
64    }
65
66    LinuxMouseEventBatch {
67        events: new_events,
68        count_ignored_events,
69        count_converted_events,
70        count_unexpected_events,
71        last_event_time_ns: last_event_time_ns.into_nanos(),
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use pretty_assertions::assert_eq;
79
80    #[test]
81    fn test_mouse_wheel_event() {
82        let fidl_event = FidlMouseEvent {
83            timestamp: Some(1000),
84            pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
85            ..Default::default()
86        };
87        let batch = parse_fidl_mouse_events(vec![fidl_event]);
88
89        assert_eq!(batch.events.len(), 2);
90        assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
91        assert_eq!(batch.events[0].code, uapi::REL_WHEEL as u16);
92        assert_eq!(batch.events[0].value, 1);
93        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
94        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
95        assert_eq!(batch.count_converted_events, 1);
96        assert_eq!(batch.count_ignored_events, 0);
97        assert_eq!(batch.count_unexpected_events, 0);
98        assert_eq!(batch.last_event_time_ns, 1000);
99    }
100
101    #[test]
102    fn test_mouse_wheel_merge() {
103        let fidl_event1 = FidlMouseEvent {
104            timestamp: Some(1000),
105            pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
106            ..Default::default()
107        };
108        let fidl_event2 = FidlMouseEvent {
109            timestamp: Some(2000),
110            pointer_sample: Some(MousePointerSample { scroll_v: Some(2), ..Default::default() }),
111            ..Default::default()
112        };
113        let batch = parse_fidl_mouse_events(vec![fidl_event1, fidl_event2]);
114
115        assert_eq!(batch.events.len(), 2);
116        assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
117        assert_eq!(batch.events[0].code, uapi::REL_WHEEL as u16);
118        assert_eq!(batch.events[0].value, 3);
119        assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
120        assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
121        assert_eq!(batch.count_converted_events, 2);
122        assert_eq!(batch.count_ignored_events, 0);
123        assert_eq!(batch.count_unexpected_events, 0);
124        assert_eq!(batch.last_event_time_ns, 2000);
125    }
126
127    #[test]
128    fn test_mouse_wheel_merge_to_zero() {
129        let fidl_event1 = FidlMouseEvent {
130            timestamp: Some(1000),
131            pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
132            ..Default::default()
133        };
134        let fidl_event2 = FidlMouseEvent {
135            timestamp: Some(2000),
136            pointer_sample: Some(MousePointerSample { scroll_v: Some(-1), ..Default::default() }),
137            ..Default::default()
138        };
139        let batch = parse_fidl_mouse_events(vec![fidl_event1, fidl_event2]);
140
141        assert_eq!(batch.events.len(), 0);
142        assert_eq!(batch.count_converted_events, 2);
143        assert_eq!(batch.count_ignored_events, 0);
144        assert_eq!(batch.count_unexpected_events, 0);
145        assert_eq!(batch.last_event_time_ns, 2000);
146    }
147
148    #[test]
149    fn test_mouse_wheel_zero_ticks() {
150        let fidl_event = FidlMouseEvent {
151            timestamp: Some(1000),
152            pointer_sample: Some(MousePointerSample { scroll_v: Some(0), ..Default::default() }),
153            ..Default::default()
154        };
155        let batch = parse_fidl_mouse_events(vec![fidl_event]);
156
157        assert_eq!(batch.events.len(), 0);
158        assert_eq!(batch.count_converted_events, 0);
159        assert_eq!(batch.count_ignored_events, 1);
160        assert_eq!(batch.count_unexpected_events, 0);
161    }
162
163    #[test]
164    fn test_mouse_unexpected_event() {
165        let fidl_event = FidlMouseEvent { timestamp: Some(1000), ..Default::default() };
166        let batch = parse_fidl_mouse_events(vec![fidl_event]);
167
168        assert_eq!(batch.events.len(), 0);
169        assert_eq!(batch.count_converted_events, 0);
170        assert_eq!(batch.count_ignored_events, 0);
171        assert_eq!(batch.count_unexpected_events, 1);
172    }
173}