starnix_modules_input_report_event_conversion/
mouse.rs1use fidl_fuchsia_input_report as fir;
6use sorted_vec_map::SortedVecSet;
7use starnix_types::time::timeval_from_time;
8use starnix_uapi::uapi;
9
10pub fn mouse_button_to_linux_button(button: u8) -> Option<u32> {
12 match button {
13 1 => Some(uapi::BTN_LEFT),
14 2 => Some(uapi::BTN_RIGHT),
15 3 => Some(uapi::BTN_MIDDLE),
16 4 => Some(uapi::BTN_SIDE),
17 5 => Some(uapi::BTN_EXTRA),
18 6 => Some(uapi::BTN_FORWARD),
19 7 => Some(uapi::BTN_BACK),
20 8 => Some(uapi::BTN_TASK),
21 _ => None,
22 }
23}
24
25#[derive(Debug, Default, PartialEq)]
27pub struct MouseReportConverter {
28 pressed_buttons: SortedVecSet<u16>,
29}
30
31impl MouseReportConverter {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn handle(
37 &mut self,
38 report: &fir::MouseInputReport,
39 event_time: zx::MonotonicInstant,
40 ) -> Vec<uapi::input_event> {
41 let mut events = Vec::new();
42 let time = timeval_from_time(event_time);
43
44 if let Some(movement_x) = report.movement_x {
45 if movement_x != 0 {
46 events.push(uapi::input_event {
47 time,
48 type_: uapi::EV_REL as u16,
49 code: uapi::REL_X as u16,
50 value: crate::clamp_i64_to_i32(movement_x),
51 });
52 }
53 }
54
55 if let Some(movement_y) = report.movement_y {
56 if movement_y != 0 {
57 events.push(uapi::input_event {
58 time,
59 type_: uapi::EV_REL as u16,
60 code: uapi::REL_Y as u16,
61 value: crate::clamp_i64_to_i32(movement_y),
62 });
63 }
64 }
65
66 if let Some(scroll_v) = report.scroll_v {
67 if scroll_v != 0 {
68 events.push(uapi::input_event {
69 time,
70 type_: uapi::EV_REL as u16,
71 code: uapi::REL_WHEEL as u16,
72 value: crate::clamp_i64_to_i32(scroll_v),
73 });
74 }
75 }
76
77 if let Some(scroll_h) = report.scroll_h {
78 if scroll_h != 0 {
79 events.push(uapi::input_event {
80 time,
81 type_: uapi::EV_REL as u16,
82 code: uapi::REL_HWHEEL as u16,
83 value: crate::clamp_i64_to_i32(scroll_h),
84 });
85 }
86 }
87
88 let pressed_buttons = report.pressed_buttons.as_deref().unwrap_or(&[]);
89 let mut current_buttons = SortedVecSet::new();
90 for &btn in pressed_buttons.iter() {
91 if let Some(linux_btn) = mouse_button_to_linux_button(btn) {
92 current_buttons.insert(linux_btn as u16);
93 }
94 }
95
96 crate::emit_key_transitions(&mut self.pressed_buttons, current_buttons, time, &mut events);
97
98 if !events.is_empty() {
99 events.push(uapi::input_event {
100 time,
101 type_: uapi::EV_SYN as u16,
102 code: uapi::SYN_REPORT as u16,
103 value: 0,
104 });
105 }
106
107 events
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[::fuchsia::test]
116 fn test_mouse_report_motion_and_buttons() {
117 let mut converter = MouseReportConverter::new();
118 let time = zx::MonotonicInstant::from_nanos(400_000_000);
119
120 let report = fir::MouseInputReport {
122 movement_x: Some(15),
123 movement_y: Some(-25),
124 scroll_v: Some(3),
125 scroll_h: Some(-1),
126 pressed_buttons: Some(vec![1]),
127 ..Default::default()
128 };
129
130 let events = converter.handle(&report, time);
131 assert_eq!(
132 events.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
133 vec![
134 (uapi::EV_REL as u16, uapi::REL_X as u16, 15),
135 (uapi::EV_REL as u16, uapi::REL_Y as u16, -25),
136 (uapi::EV_REL as u16, uapi::REL_WHEEL as u16, 3),
137 (uapi::EV_REL as u16, uapi::REL_HWHEEL as u16, -1),
138 (uapi::EV_KEY as u16, uapi::BTN_LEFT as u16, 1),
139 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
140 ]
141 );
142
143 let report2 =
145 fir::MouseInputReport { pressed_buttons: Some(vec![2]), ..Default::default() };
146 let events2 = converter.handle(&report2, time);
147 assert_eq!(
148 events2.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
149 vec![
150 (uapi::EV_KEY as u16, uapi::BTN_LEFT as u16, 0),
151 (uapi::EV_KEY as u16, uapi::BTN_RIGHT as u16, 1),
152 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
153 ]
154 );
155 }
156
157 #[::fuchsia::test]
158 fn test_mouse_report_clamping_out_of_range_axes() {
159 let mut converter = MouseReportConverter::new();
160 let time = zx::MonotonicInstant::from_nanos(100_000_000);
161 let report = fir::MouseInputReport {
162 movement_x: Some(i64::MAX),
163 movement_y: Some(i64::MIN),
164 scroll_v: Some(i64::MAX),
165 scroll_h: Some(i64::MIN),
166 ..Default::default()
167 };
168 let events = converter.handle(&report, time);
169 assert!(events.iter().any(|e| e.code == uapi::REL_X as u16 && e.value == i32::MAX));
170 assert!(events.iter().any(|e| e.code == uapi::REL_Y as u16 && e.value == i32::MIN));
171 assert!(events.iter().any(|e| e.code == uapi::REL_WHEEL as u16 && e.value == i32::MAX));
172 assert!(events.iter().any(|e| e.code == uapi::REL_HWHEEL as u16 && e.value == i32::MIN));
173 }
174
175 #[::fuchsia::test]
176 fn test_empty_mouse_reports_return_empty_events() {
177 let time = zx::MonotonicInstant::from_nanos(400_000_000);
178 assert!(
179 MouseReportConverter::new().handle(&fir::MouseInputReport::default(), time).is_empty()
180 );
181 }
182
183 #[::fuchsia::test]
184 fn test_mouse_report_none_pressed_buttons_releases_held_buttons() {
185 let mut converter = MouseReportConverter::new();
186 let time = zx::MonotonicInstant::from_nanos(300_000_000);
187
188 let report1 =
190 fir::MouseInputReport { pressed_buttons: Some(vec![1]), ..Default::default() };
191 let events1 = converter.handle(&report1, time);
192 assert_eq!(
193 events1.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
194 vec![
195 (uapi::EV_KEY as u16, uapi::BTN_LEFT as u16, 1),
196 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
197 ]
198 );
199
200 let report2 = fir::MouseInputReport { pressed_buttons: None, ..Default::default() };
202 let events2 = converter.handle(&report2, time);
203 assert_eq!(
204 events2.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
205 vec![
206 (uapi::EV_KEY as u16, uapi::BTN_LEFT as u16, 0),
207 (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
208 ]
209 );
210
211 let report3 = fir::MouseInputReport { pressed_buttons: None, ..Default::default() };
213 let events3 = converter.handle(&report3, time);
214 assert!(events3.is_empty());
215 }
216}