1use fidl_fuchsia_ui_pointer::MouseEvent as FidlMouseEvent;
6use sorted_vec_map::SortedVecSet;
7use starnix_types::time::timeval_from_time;
8use starnix_uapi::uapi;
9use std::collections::VecDeque;
10
11#[derive(Clone, Debug, Default, PartialEq)]
12pub struct FuchsiaMouseEventToLinuxMouseEventConverter {
13 currently_pressed_buttons: SortedVecSet<u8>,
14}
15
16pub struct LinuxMouseEventBatch {
17 pub events: VecDeque<uapi::input_event>,
18 pub count_ignored_events: u64,
19 pub count_converted_events: u64,
20 pub count_unexpected_events: u64,
21 pub last_event_time_ns: i64,
22}
23
24pub fn fuchsia_mouse_button_to_linux_button(button: u8) -> Option<u16> {
29 match button {
30 1 => Some(uapi::BTN_LEFT as u16),
31 2 => Some(uapi::BTN_RIGHT as u16),
32 3 => Some(uapi::BTN_MIDDLE as u16),
33 4 => Some(uapi::BTN_SIDE as u16),
34 5 => Some(uapi::BTN_EXTRA as u16),
35 _ => None,
36 }
37}
38
39impl FuchsiaMouseEventToLinuxMouseEventConverter {
40 pub fn create() -> Self {
41 Self::default()
42 }
43
44 pub fn handle(&mut self, mouse_events: Vec<FidlMouseEvent>) -> LinuxMouseEventBatch {
52 let mut count_ignored_events: u64 = 0;
53 let mut count_converted_events: u64 = 0;
54 let mut count_unexpected_events: u64 = 0;
55 let mut last_event_time = zx::MonotonicInstant::get();
56
57 let mut total_rel_x: i32 = 0;
58 let mut total_rel_y: i32 = 0;
59 let mut total_scroll_v: i32 = 0;
60 let mut total_scroll_h: i32 = 0;
61 let mut button_events: Vec<uapi::input_event> = Vec::new();
62
63 for event in mouse_events {
64 let Some(sample) = event.pointer_sample else {
65 if event.stream_info.is_some()
66 || event.view_parameters.is_some()
67 || event.device_info.is_some()
68 {
69 count_ignored_events += 1;
70 } else {
71 count_unexpected_events += 1;
72 }
73 continue;
74 };
75
76 let event_time = match event.timestamp {
77 Some(time) if time > 0 => zx::MonotonicInstant::from_nanos(time),
78 _ => zx::MonotonicInstant::get(),
79 };
80 last_event_time = event_time;
81 let time = timeval_from_time(event_time);
82
83 let mut sample_had_data = false;
84
85 if let Some([rx, ry]) = sample.relative_motion {
93 let dx = rx.round() as i32;
94 let dy = ry.round() as i32;
95 if dx != 0 {
96 total_rel_x += dx;
97 sample_had_data = true;
98 }
99 if dy != 0 {
100 total_rel_y += dy;
101 sample_had_data = true;
102 }
103 }
104
105 if let Some(ticks) = sample.scroll_v {
107 if ticks != 0 {
108 total_scroll_v += ticks as i32;
109 sample_had_data = true;
110 }
111 }
112 if let Some(ticks) = sample.scroll_h {
113 if ticks != 0 {
114 total_scroll_h += ticks as i32;
115 sample_had_data = true;
116 }
117 }
118
119 let new_pressed_buttons: SortedVecSet<u8> =
121 sample.pressed_buttons.map(|vec| vec.into_iter().collect()).unwrap_or_default();
122
123 for &btn in new_pressed_buttons.difference(&self.currently_pressed_buttons) {
124 if let Some(code) = fuchsia_mouse_button_to_linux_button(btn) {
125 button_events.push(uapi::input_event {
126 time,
127 type_: uapi::EV_KEY as u16,
128 code,
129 value: 1,
130 });
131 sample_had_data = true;
132 }
133 }
134
135 for &btn in self.currently_pressed_buttons.difference(&new_pressed_buttons) {
136 if let Some(code) = fuchsia_mouse_button_to_linux_button(btn) {
137 button_events.push(uapi::input_event {
138 time,
139 type_: uapi::EV_KEY as u16,
140 code,
141 value: 0,
142 });
143 sample_had_data = true;
144 }
145 }
146
147 self.currently_pressed_buttons = new_pressed_buttons;
148
149 if sample_had_data {
150 count_converted_events += 1;
151 } else {
152 count_ignored_events += 1;
153 }
154 }
155
156 let time = timeval_from_time(last_event_time);
157 let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
158
159 if total_rel_x != 0 {
160 new_events.push_back(uapi::input_event {
161 time,
162 type_: uapi::EV_REL as u16,
163 code: uapi::REL_X as u16,
164 value: total_rel_x,
165 });
166 }
167 if total_rel_y != 0 {
168 new_events.push_back(uapi::input_event {
169 time,
170 type_: uapi::EV_REL as u16,
171 code: uapi::REL_Y as u16,
172 value: total_rel_y,
173 });
174 }
175 if total_scroll_v != 0 {
176 new_events.push_back(uapi::input_event {
177 time,
178 type_: uapi::EV_REL as u16,
179 code: uapi::REL_WHEEL as u16,
180 value: total_scroll_v,
181 });
182 }
183 if total_scroll_h != 0 {
184 new_events.push_back(uapi::input_event {
185 time,
186 type_: uapi::EV_REL as u16,
187 code: uapi::REL_HWHEEL as u16,
188 value: total_scroll_h,
189 });
190 }
191 new_events.extend(button_events);
192
193 if !new_events.is_empty() {
194 new_events.push_back(uapi::input_event {
195 time,
196 type_: uapi::EV_SYN as u16,
197 code: uapi::SYN_REPORT as u16,
198 value: 0,
199 });
200 }
201
202 LinuxMouseEventBatch {
203 events: new_events,
204 count_ignored_events,
205 count_converted_events,
206 count_unexpected_events,
207 last_event_time_ns: last_event_time.into_nanos(),
208 }
209 }
210}
211
212pub fn parse_fidl_mouse_events(mouse_events: Vec<FidlMouseEvent>) -> LinuxMouseEventBatch {
213 FuchsiaMouseEventToLinuxMouseEventConverter::create().handle(mouse_events)
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use fidl_fuchsia_ui_pointer::{MouseEventStreamInfo, MousePointerSample, MouseViewStatus};
220 use pretty_assertions::assert_eq;
221
222 #[test]
223 fn test_mouse_wheel_event() {
224 let fidl_event = FidlMouseEvent {
225 timestamp: Some(1000),
226 pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
227 ..Default::default()
228 };
229 let batch = parse_fidl_mouse_events(vec![fidl_event]);
230
231 assert_eq!(batch.events.len(), 2);
232 assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
233 assert_eq!(batch.events[0].code, uapi::REL_WHEEL as u16);
234 assert_eq!(batch.events[0].value, 1);
235 assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
236 assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
237 assert_eq!(batch.count_converted_events, 1);
238 assert_eq!(batch.count_ignored_events, 0);
239 assert_eq!(batch.count_unexpected_events, 0);
240 assert_eq!(batch.last_event_time_ns, 1000);
241 }
242
243 #[test]
244 fn test_mouse_horizontal_wheel_event() {
245 let fidl_event = FidlMouseEvent {
246 timestamp: Some(1000),
247 pointer_sample: Some(MousePointerSample { scroll_h: Some(3), ..Default::default() }),
248 ..Default::default()
249 };
250 let batch = parse_fidl_mouse_events(vec![fidl_event]);
251
252 assert_eq!(batch.events.len(), 2);
253 assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
254 assert_eq!(batch.events[0].code, uapi::REL_HWHEEL as u16);
255 assert_eq!(batch.events[0].value, 3);
256 assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
257 assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
258 assert_eq!(batch.count_converted_events, 1);
259 assert_eq!(batch.count_ignored_events, 0);
260 assert_eq!(batch.count_unexpected_events, 0);
261 assert_eq!(batch.last_event_time_ns, 1000);
262 }
263
264 #[test]
265 fn test_mouse_wheel_merge() {
266 let fidl_event1 = FidlMouseEvent {
267 timestamp: Some(1000),
268 pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
269 ..Default::default()
270 };
271 let fidl_event2 = FidlMouseEvent {
272 timestamp: Some(2000),
273 pointer_sample: Some(MousePointerSample { scroll_v: Some(2), ..Default::default() }),
274 ..Default::default()
275 };
276 let batch = parse_fidl_mouse_events(vec![fidl_event1, fidl_event2]);
277
278 assert_eq!(batch.events.len(), 2);
279 assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
280 assert_eq!(batch.events[0].code, uapi::REL_WHEEL as u16);
281 assert_eq!(batch.events[0].value, 3);
282 assert_eq!(batch.events[1].type_, uapi::EV_SYN as u16);
283 assert_eq!(batch.events[1].code, uapi::SYN_REPORT as u16);
284 assert_eq!(batch.count_converted_events, 2);
285 assert_eq!(batch.count_ignored_events, 0);
286 assert_eq!(batch.count_unexpected_events, 0);
287 assert_eq!(batch.last_event_time_ns, 2000);
288 }
289
290 #[test]
291 fn test_mouse_wheel_merge_to_zero() {
292 let fidl_event1 = FidlMouseEvent {
293 timestamp: Some(1000),
294 pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
295 ..Default::default()
296 };
297 let fidl_event2 = FidlMouseEvent {
298 timestamp: Some(2000),
299 pointer_sample: Some(MousePointerSample { scroll_v: Some(-1), ..Default::default() }),
300 ..Default::default()
301 };
302 let batch = parse_fidl_mouse_events(vec![fidl_event1, fidl_event2]);
303
304 assert_eq!(batch.events.len(), 0);
305 assert_eq!(batch.count_converted_events, 2);
306 assert_eq!(batch.count_ignored_events, 0);
307 assert_eq!(batch.count_unexpected_events, 0);
308 assert_eq!(batch.last_event_time_ns, 2000);
309 }
310
311 #[test]
312 fn test_mouse_wheel_zero_ticks() {
313 let fidl_event = FidlMouseEvent {
314 timestamp: Some(1000),
315 pointer_sample: Some(MousePointerSample { scroll_v: Some(0), ..Default::default() }),
316 ..Default::default()
317 };
318 let batch = parse_fidl_mouse_events(vec![fidl_event]);
319
320 assert_eq!(batch.events.len(), 0);
321 assert_eq!(batch.count_converted_events, 0);
322 assert_eq!(batch.count_ignored_events, 1);
323 assert_eq!(batch.count_unexpected_events, 0);
324 }
325
326 #[test]
327 fn test_mouse_relative_motion() {
328 let fidl_event = FidlMouseEvent {
329 timestamp: Some(1000),
330 pointer_sample: Some(MousePointerSample {
331 relative_motion: Some([10.4, -5.2]),
332 ..Default::default()
333 }),
334 ..Default::default()
335 };
336 let batch = parse_fidl_mouse_events(vec![fidl_event]);
337
338 assert_eq!(batch.events.len(), 3);
339 assert_eq!(batch.events[0].type_, uapi::EV_REL as u16);
340 assert_eq!(batch.events[0].code, uapi::REL_X as u16);
341 assert_eq!(batch.events[0].value, 10);
342 assert_eq!(batch.events[1].type_, uapi::EV_REL as u16);
343 assert_eq!(batch.events[1].code, uapi::REL_Y as u16);
344 assert_eq!(batch.events[1].value, -5);
345 assert_eq!(batch.events[2].type_, uapi::EV_SYN as u16);
346 assert_eq!(batch.events[2].code, uapi::SYN_REPORT as u16);
347 assert_eq!(batch.count_converted_events, 1);
348 assert_eq!(batch.count_ignored_events, 0);
349 assert_eq!(batch.count_unexpected_events, 0);
350 }
351
352 #[test]
353 fn test_mouse_button_press_and_release() {
354 let mut converter = FuchsiaMouseEventToLinuxMouseEventConverter::create();
355
356 let press_event = FidlMouseEvent {
358 timestamp: Some(1000),
359 pointer_sample: Some(MousePointerSample {
360 pressed_buttons: Some(vec![1, 2]),
361 ..Default::default()
362 }),
363 ..Default::default()
364 };
365 let batch1 = converter.handle(vec![press_event]);
366 assert_eq!(batch1.events.len(), 3);
367 assert_eq!(batch1.events[0].type_, uapi::EV_KEY as u16);
368 assert_eq!(batch1.events[0].code, uapi::BTN_LEFT as u16);
369 assert_eq!(batch1.events[0].value, 1);
370 assert_eq!(batch1.events[1].type_, uapi::EV_KEY as u16);
371 assert_eq!(batch1.events[1].code, uapi::BTN_RIGHT as u16);
372 assert_eq!(batch1.events[1].value, 1);
373 assert_eq!(batch1.events[2].type_, uapi::EV_SYN as u16);
374 assert_eq!(batch1.events[2].code, uapi::SYN_REPORT as u16);
375 assert_eq!(batch1.count_converted_events, 1);
376
377 let release_event = FidlMouseEvent {
379 timestamp: Some(2000),
380 pointer_sample: Some(MousePointerSample {
381 pressed_buttons: Some(vec![2]),
382 ..Default::default()
383 }),
384 ..Default::default()
385 };
386 let batch2 = converter.handle(vec![release_event]);
387 assert_eq!(batch2.events.len(), 2);
388 assert_eq!(batch2.events[0].type_, uapi::EV_KEY as u16);
389 assert_eq!(batch2.events[0].code, uapi::BTN_LEFT as u16);
390 assert_eq!(batch2.events[0].value, 0);
391 assert_eq!(batch2.events[1].type_, uapi::EV_SYN as u16);
392 assert_eq!(batch2.events[1].code, uapi::SYN_REPORT as u16);
393 assert_eq!(batch2.count_converted_events, 1);
394
395 let release_all = FidlMouseEvent {
397 timestamp: Some(3000),
398 pointer_sample: Some(MousePointerSample {
399 pressed_buttons: Some(vec![]),
400 ..Default::default()
401 }),
402 ..Default::default()
403 };
404 let batch3 = converter.handle(vec![release_all]);
405 assert_eq!(batch3.events.len(), 2);
406 assert_eq!(batch3.events[0].type_, uapi::EV_KEY as u16);
407 assert_eq!(batch3.events[0].code, uapi::BTN_RIGHT as u16);
408 assert_eq!(batch3.events[0].value, 0);
409 assert_eq!(batch3.events[1].type_, uapi::EV_SYN as u16);
410 assert_eq!(batch3.events[1].code, uapi::SYN_REPORT as u16);
411 assert_eq!(batch3.count_converted_events, 1);
412 }
413
414 #[test]
415 fn test_unsupported_mouse_button_ignored() {
416 let mut converter = FuchsiaMouseEventToLinuxMouseEventConverter::create();
417 let fidl_event = FidlMouseEvent {
418 timestamp: Some(1000),
419 pointer_sample: Some(MousePointerSample {
420 pressed_buttons: Some(vec![99]),
421 ..Default::default()
422 }),
423 ..Default::default()
424 };
425 let batch = converter.handle(vec![fidl_event]);
426 assert_eq!(batch.events.len(), 0);
427 assert_eq!(batch.count_converted_events, 0);
428 assert_eq!(batch.count_ignored_events, 1);
429 }
430
431 #[test]
432 fn test_zero_timestamp_uses_monotonic_instant() {
433 let fidl_event = FidlMouseEvent {
434 timestamp: Some(0),
435 pointer_sample: Some(MousePointerSample { scroll_v: Some(1), ..Default::default() }),
436 ..Default::default()
437 };
438 let batch = parse_fidl_mouse_events(vec![fidl_event]);
439 assert_eq!(batch.events.len(), 2);
440 assert_eq!(batch.events[0].code, uapi::REL_WHEEL as u16);
441 assert!(batch.events[0].time.tv_sec > 0);
442 assert!(batch.last_event_time_ns > 0);
443 }
444
445 #[test]
446 fn test_metadata_events_ignored() {
447 let stream_info_event = FidlMouseEvent {
448 stream_info: Some(MouseEventStreamInfo {
449 device_id: 1,
450 status: MouseViewStatus::Entered,
451 }),
452 ..Default::default()
453 };
454 let batch = parse_fidl_mouse_events(vec![stream_info_event]);
455 assert_eq!(batch.events.len(), 0);
456 assert_eq!(batch.count_ignored_events, 1);
457 assert_eq!(batch.count_unexpected_events, 0);
458 }
459
460 #[test]
461 fn test_mouse_unexpected_event() {
462 let fidl_event = FidlMouseEvent { timestamp: Some(1000), ..Default::default() };
463 let batch = parse_fidl_mouse_events(vec![fidl_event]);
464
465 assert_eq!(batch.events.len(), 0);
466 assert_eq!(batch.count_converted_events, 0);
467 assert_eq!(batch.count_ignored_events, 0);
468 assert_eq!(batch.count_unexpected_events, 1);
469 }
470}