1use super::ev_dev_touch_screen::touch_position;
9use super::{EvDevDeviceHandler, EvDevDeviceInfo, EvDevRawEvent};
10use crate::native_unsafe_methods::{abs_axis, ev_type, input_absinfo, input_event};
11use ferroui_base::input::raw::RawPointerEventType;
12use ferroui_base::{Matrix, Size};
13
14#[derive(Clone, Debug, PartialEq)]
17pub struct EvDevMultiTouchScreenDeviceDescription {
18 pub path: Option<String>,
19 pub calibration_matrix: Matrix,
20}
21
22impl Default for EvDevMultiTouchScreenDeviceDescription {
23 fn default() -> Self {
24 Self { path: None, calibration_matrix: Matrix::IDENTITY }
25 }
26}
27
28#[derive(Clone, Copy, Default)]
29struct Slot {
30 active: bool,
32 reported: (i32, i32),
34 x: i32,
35 y: i32,
36 ends_first: bool,
39 begins: bool,
41 ends_after_begin: bool,
43 moved: bool,
45}
46
47pub struct EvDevMultiTouchScreen {
48 calibration: Matrix,
49 axis_x: input_absinfo,
50 axis_y: input_absinfo,
51 slots: Vec<Slot>,
52 current_slot: usize,
53}
54
55const MAX_SLOTS: usize = 64;
57
58impl EvDevMultiTouchScreen {
59 pub fn new(device: &EvDevDeviceInfo, description: &EvDevMultiTouchScreenDeviceDescription) -> Result<Self, String> {
62 let axis = |code: u16| device.abs.get(&code).copied();
63 let (Some(axis_x), Some(axis_y)) = (axis(abs_axis::ABS_MT_POSITION_X), axis(abs_axis::ABS_MT_POSITION_Y))
64 else {
65 return Err("Device is not a multi-touch screen".to_owned());
66 };
67 let slot_count =
68 axis(abs_axis::ABS_MT_SLOT).map_or(1, |slot| (slot.maximum.max(0) as usize + 1).min(MAX_SLOTS));
69 Ok(Self {
70 calibration: description.calibration_matrix,
71 axis_x,
72 axis_y,
73 slots: vec![Slot::default(); slot_count],
74 current_slot: 0,
75 })
76 }
77
78 fn slot(&mut self) -> Option<&mut Slot> {
79 if self.current_slot >= MAX_SLOTS {
80 return None;
81 }
82 if self.current_slot >= self.slots.len() {
83 self.slots.resize(self.current_slot + 1, Slot::default());
84 }
85 self.slots.get_mut(self.current_slot)
86 }
87
88 fn report(&mut self, timestamp: u64, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
89 let (axis_x, axis_y, calibration) = (self.axis_x, self.axis_y, self.calibration);
90 for (index, slot) in self.slots.iter_mut().enumerate() {
91 let mut push = |type_: RawPointerEventType, (x, y): (i32, i32)| {
92 let position = touch_position(x, y, &axis_x, &axis_y, calibration, screen_size);
93 out.push(EvDevRawEvent::Touch { type_, timestamp, position, raw_pointer_id: index as i64 });
94 };
95 if slot.ends_first {
98 push(RawPointerEventType::TouchEnd, slot.reported);
99 slot.active = false;
100 }
101 if slot.begins {
102 push(RawPointerEventType::TouchBegin, (slot.x, slot.y));
103 slot.active = true;
104 if slot.ends_after_begin {
105 push(RawPointerEventType::TouchEnd, (slot.x, slot.y));
106 slot.active = false;
107 }
108 slot.reported = (slot.x, slot.y);
109 } else if slot.moved && slot.active {
110 push(RawPointerEventType::TouchUpdate, (slot.x, slot.y));
111 slot.reported = (slot.x, slot.y);
112 }
113 slot.ends_first = false;
114 slot.begins = false;
115 slot.ends_after_begin = false;
116 slot.moved = false;
117 }
118 }
119}
120
121impl EvDevDeviceHandler for EvDevMultiTouchScreen {
122 fn handle_event(&mut self, ev: &input_event, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
123 if ev.type_ == ev_type::EV_ABS {
124 match ev.code {
125 abs_axis::ABS_MT_SLOT => self.current_slot = ev.value.max(0) as usize,
126 abs_axis::ABS_MT_TRACKING_ID => {
127 let begins = ev.value >= 0;
128 if let Some(slot) = self.slot() {
129 if begins {
130 if !slot.begins {
133 slot.ends_first = slot.active;
134 slot.begins = true;
135 }
136 } else if slot.begins {
137 slot.ends_after_begin = true;
138 } else if slot.active {
139 slot.ends_first = true;
140 }
141 }
142 }
143 abs_axis::ABS_MT_POSITION_X => {
144 if let Some(slot) = self.slot() {
145 slot.x = ev.value;
146 slot.moved = true;
147 }
148 }
149 abs_axis::ABS_MT_POSITION_Y => {
150 if let Some(slot) = self.slot() {
151 slot.y = ev.value;
152 slot.moved = true;
153 }
154 }
155 _ => {}
156 }
157 }
158
159 if ev.type_ == ev_type::EV_SYN {
160 self.report(ev.timestamp(), screen_size, out);
161 }
162 }
163
164 fn reset(&mut self, timestamp: u64, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
165 for slot in &mut self.slots {
167 slot.begins = false;
168 slot.ends_after_begin = false;
169 slot.moved = false;
170 slot.ends_first = slot.active;
171 }
172 self.report(timestamp, screen_size, out);
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::super::ev_dev_touch_screen::tests::{ev, info, run, syn};
179 use super::*;
180 use crate::native_unsafe_methods::syn_code;
181 use ferroui_base::Point;
182
183 fn abs(ms: i64, code: u16, value: i32) -> input_event {
184 ev(ms, ev_type::EV_ABS, code, value)
185 }
186
187 fn touch(type_: RawPointerEventType, timestamp: u64, x: f64, y: f64, id: i64) -> EvDevRawEvent {
188 EvDevRawEvent::Touch { type_, timestamp, position: Point::new(x, y), raw_pointer_id: id }
189 }
190
191 fn screen_and_handler() -> (Size, EvDevMultiTouchScreen) {
192 let info = info(&[
193 (abs_axis::ABS_MT_SLOT, 0, 9),
194 (abs_axis::ABS_MT_POSITION_X, 0, 1000),
195 (abs_axis::ABS_MT_POSITION_Y, 0, 500),
196 ]);
197 let handler = EvDevMultiTouchScreen::new(&info, &EvDevMultiTouchScreenDeviceDescription::default()).unwrap();
198 (Size::new(1000.0, 500.0), handler)
199 }
200
201 #[test]
202 fn two_fingers_are_two_contacts_with_the_slot_as_identifier() {
203 let (screen, mut handler) = screen_and_handler();
204 use RawPointerEventType::{TouchBegin, TouchEnd, TouchUpdate};
205 let events = [
206 abs(1, abs_axis::ABS_MT_TRACKING_ID, 45),
208 abs(1, abs_axis::ABS_MT_POSITION_X, 100),
209 abs(1, abs_axis::ABS_MT_POSITION_Y, 200),
210 syn(1),
211 abs(2, abs_axis::ABS_MT_SLOT, 1),
213 abs(2, abs_axis::ABS_MT_TRACKING_ID, 46),
214 abs(2, abs_axis::ABS_MT_POSITION_X, 300),
215 abs(2, abs_axis::ABS_MT_POSITION_Y, 400),
216 syn(2),
217 abs(3, abs_axis::ABS_MT_SLOT, 0),
219 abs(3, abs_axis::ABS_MT_POSITION_X, 110),
220 abs(3, abs_axis::ABS_MT_SLOT, 1),
221 abs(3, abs_axis::ABS_MT_POSITION_Y, 410),
222 syn(3),
223 abs(4, abs_axis::ABS_MT_SLOT, 0),
225 abs(4, abs_axis::ABS_MT_TRACKING_ID, -1),
226 syn(4),
227 abs(5, abs_axis::ABS_MT_SLOT, 1),
228 abs(5, abs_axis::ABS_MT_TRACKING_ID, -1),
229 syn(5),
230 ];
231 assert_eq!(
232 run(&mut handler, &events, screen),
233 [
234 touch(TouchBegin, 1, 100.0, 200.0, 0),
235 touch(TouchBegin, 2, 300.0, 400.0, 1),
236 touch(TouchUpdate, 3, 110.0, 200.0, 0),
237 touch(TouchUpdate, 3, 300.0, 410.0, 1),
238 touch(TouchEnd, 4, 110.0, 200.0, 0),
239 touch(TouchEnd, 5, 300.0, 410.0, 1),
240 ]
241 );
242 }
243
244 #[test]
245 fn a_contact_that_is_replaced_ends_before_the_new_one_begins() {
246 let (screen, mut handler) = screen_and_handler();
247 use RawPointerEventType::{TouchBegin, TouchEnd};
248 let events = [
249 abs(1, abs_axis::ABS_MT_TRACKING_ID, 1),
250 abs(1, abs_axis::ABS_MT_POSITION_X, 10),
251 abs(1, abs_axis::ABS_MT_POSITION_Y, 10),
252 syn(1),
253 abs(2, abs_axis::ABS_MT_TRACKING_ID, 2),
254 abs(2, abs_axis::ABS_MT_POSITION_X, 50),
255 syn(2),
256 abs(3, abs_axis::ABS_MT_SLOT, 3),
258 abs(3, abs_axis::ABS_MT_TRACKING_ID, 3),
259 abs(3, abs_axis::ABS_MT_POSITION_X, 70),
260 abs(3, abs_axis::ABS_MT_POSITION_Y, 80),
261 abs(3, abs_axis::ABS_MT_TRACKING_ID, -1),
262 syn(3),
263 ];
264 assert_eq!(
265 run(&mut handler, &events, screen),
266 [
267 touch(TouchBegin, 1, 10.0, 10.0, 0),
268 touch(TouchEnd, 2, 10.0, 10.0, 0),
269 touch(TouchBegin, 2, 50.0, 10.0, 0),
270 touch(TouchBegin, 3, 70.0, 80.0, 3),
271 touch(TouchEnd, 3, 70.0, 80.0, 3),
272 ]
273 );
274 }
275
276 #[test]
277 fn a_position_without_a_contact_and_a_lift_without_one_report_nothing() {
278 let (screen, mut handler) = screen_and_handler();
279 let events = [
280 abs(1, abs_axis::ABS_MT_POSITION_X, 10),
281 syn(1),
282 abs(2, abs_axis::ABS_MT_TRACKING_ID, -1),
283 syn(2),
284 abs(3, abs_axis::ABS_MT_SLOT, 5000),
286 abs(3, abs_axis::ABS_MT_TRACKING_ID, 9),
287 syn(3),
288 ];
289 assert_eq!(run(&mut handler, &events, screen), []);
290 }
291
292 #[test]
293 fn dropped_events_end_the_contacts() {
294 let (screen, mut handler) = screen_and_handler();
295 let events = [
296 abs(1, abs_axis::ABS_MT_TRACKING_ID, 1),
297 abs(1, abs_axis::ABS_MT_POSITION_X, 10),
298 abs(1, abs_axis::ABS_MT_POSITION_Y, 20),
299 syn(1),
300 ev(2, ev_type::EV_SYN, syn_code::SYN_DROPPED, 0),
301 abs(2, abs_axis::ABS_MT_POSITION_X, 500),
302 syn(2),
303 ];
304 let out = run(&mut handler, &events, screen);
305 assert_eq!(out[1], touch(RawPointerEventType::TouchEnd, 2, 10.0, 20.0, 0));
306 assert_eq!(out.len(), 2);
307 }
308
309 #[test]
310 fn a_device_without_the_position_axes_is_not_a_multi_touch_screen() {
311 let info = info(&[(abs_axis::ABS_X, 0, 10), (abs_axis::ABS_Y, 0, 10)]);
312 assert!(EvDevMultiTouchScreen::new(&info, &EvDevMultiTouchScreenDeviceDescription::default()).is_err());
313 }
314}