1use super::{EvDevDeviceInfo, EvDevTouchScreenDeviceDescription};
11use crate::native_unsafe_methods::{abs_axis, ev_key, ev_type, input_absinfo, input_event, syn_code};
12use ferroui_base::input::raw::{RawKeyEventType, RawPointerEventType};
13use ferroui_base::input::{Key, PhysicalKey, RawInputModifiers};
14use ferroui_base::{Matrix, Point, Size, Vector};
15
16#[derive(Clone, Debug, PartialEq)]
19pub enum EvDevRawEvent {
20 Touch {
21 type_: RawPointerEventType,
22 timestamp: u64,
23 position: Point,
24 raw_pointer_id: i64,
25 },
26 Pointer {
27 type_: RawPointerEventType,
28 timestamp: u64,
29 position: Point,
30 modifiers: RawInputModifiers,
31 },
32 Wheel {
33 timestamp: u64,
34 position: Point,
35 delta: Vector,
36 modifiers: RawInputModifiers,
37 },
38 Key {
39 type_: RawKeyEventType,
40 timestamp: u64,
41 key: Key,
42 modifiers: RawInputModifiers,
43 physical_key: PhysicalKey,
44 key_symbol: Option<String>,
45 },
46 Text {
47 timestamp: u64,
48 text: String,
49 },
50}
51
52pub trait EvDevDeviceHandler {
54 fn handle_event(&mut self, ev: &input_event, screen_size: Size, out: &mut Vec<EvDevRawEvent>);
56
57 fn reset(&mut self, _timestamp: u64, _screen_size: Size, _out: &mut Vec<EvDevRawEvent>) {}
60}
61
62pub fn handle_events(
69 handler: &mut dyn EvDevDeviceHandler,
70 dropped: &mut bool,
71 events: &[input_event],
72 screen_size: Size,
73 out: &mut Vec<EvDevRawEvent>,
74) {
75 for ev in events {
76 if ev.type_ == ev_type::EV_SYN && ev.code == syn_code::SYN_DROPPED {
77 *dropped = true;
78 continue;
79 }
80 if *dropped {
81 if ev.type_ == ev_type::EV_SYN && ev.code == syn_code::SYN_REPORT {
82 *dropped = false;
83 handler.reset(ev.timestamp(), screen_size, out);
84 }
85 continue;
86 }
87 handler.handle_event(ev, screen_size, out);
88 }
89}
90
91pub(crate) fn touch_position(
94 x: i32,
95 y: i32,
96 axis_x: &input_absinfo,
97 axis_y: &input_absinfo,
98 calibration: Matrix,
99 screen_size: Size,
100) -> Point {
101 let touch_width = (axis_x.maximum - axis_x.minimum) as f64;
102 let touch_height = (axis_y.maximum - axis_y.minimum) as f64;
103
104 let point = Point::new((x - axis_x.minimum) as f64, (y - axis_y.minimum) as f64);
106
107 let point = point * calibration;
109
110 Point::new(point.x * screen_size.width / touch_width, point.y * screen_size.height / touch_height)
112}
113
114pub struct EvDevSingleTouchScreen {
115 calibration: Matrix,
116 axis_x: input_absinfo,
117 axis_y: input_absinfo,
118 current_x: i32,
119 current_y: i32,
120 has_movement: bool,
121 press_action: Option<bool>,
122}
123
124impl EvDevSingleTouchScreen {
125 pub fn new(device: &EvDevDeviceInfo, description: &EvDevTouchScreenDeviceDescription) -> Result<Self, String> {
127 let (Some(axis_x), Some(axis_y)) = (device.abs_x(), device.abs_y()) else {
128 return Err("Device is not a touchscreen".to_owned());
129 };
130 Ok(Self {
131 calibration: description.calibration_matrix,
132 axis_x,
133 axis_y,
134 current_x: 0,
135 current_y: 0,
136 has_movement: false,
137 press_action: None,
138 })
139 }
140
141 fn raise_event(&self, type_: RawPointerEventType, timestamp: u64, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
142 let position =
143 touch_position(self.current_x, self.current_y, &self.axis_x, &self.axis_y, self.calibration, screen_size);
144 out.push(EvDevRawEvent::Touch { type_, timestamp, position, raw_pointer_id: 1 });
145 }
146}
147
148impl EvDevDeviceHandler for EvDevSingleTouchScreen {
149 fn handle_event(&mut self, ev: &input_event, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
150 if ev.type_ == ev_type::EV_ABS {
151 if ev.code == abs_axis::ABS_X {
152 self.current_x = ev.value;
153 self.has_movement = true;
154 }
155
156 if ev.code == abs_axis::ABS_Y {
157 self.current_y = ev.value;
158 self.has_movement = true;
159 }
160 }
161
162 if ev.type_ == ev_type::EV_KEY && ev.code == ev_key::BTN_TOUCH {
163 self.press_action = Some(ev.value != 0);
164 }
165
166 if ev.type_ == ev_type::EV_SYN {
167 if let Some(press_action) = self.press_action {
168 let type_ = if press_action { RawPointerEventType::TouchBegin } else { RawPointerEventType::TouchEnd };
169 self.raise_event(type_, ev.timestamp(), screen_size, out);
170 } else if self.has_movement {
171 self.raise_event(RawPointerEventType::TouchUpdate, ev.timestamp(), screen_size, out);
172 }
173
174 self.has_movement = false;
175 self.press_action = None;
176 }
177 }
178
179 fn reset(&mut self, _timestamp: u64, _screen_size: Size, _out: &mut Vec<EvDevRawEvent>) {
180 self.has_movement = false;
181 self.press_action = None;
182 }
183}
184
185#[cfg(test)]
186pub(crate) mod tests {
187 use super::*;
190
191 pub fn info(axes: &[(u16, i32, i32)]) -> EvDevDeviceInfo {
192 EvDevDeviceInfo {
193 name: Some("test".to_owned()),
194 event_types: vec![ev_type::EV_SYN, ev_type::EV_KEY, ev_type::EV_ABS],
195 abs: axes
196 .iter()
197 .map(|&(code, minimum, maximum)| (code, input_absinfo { minimum, maximum, ..Default::default() }))
198 .collect(),
199 }
200 }
201
202 pub fn ev(ms: i64, type_: u16, code: u16, value: i32) -> input_event {
203 input_event { tv_sec: ms / 1000, tv_usec: (ms % 1000) * 1000, type_, code, value }
204 }
205
206 pub fn syn(ms: i64) -> input_event {
207 ev(ms, ev_type::EV_SYN, syn_code::SYN_REPORT, 0)
208 }
209
210 pub fn run(handler: &mut dyn EvDevDeviceHandler, events: &[input_event], screen: Size) -> Vec<EvDevRawEvent> {
211 let mut out = Vec::new();
212 let mut dropped = false;
213 handle_events(handler, &mut dropped, events, screen, &mut out);
214 out
215 }
216
217 fn touch(type_: RawPointerEventType, timestamp: u64, x: f64, y: f64) -> EvDevRawEvent {
218 EvDevRawEvent::Touch { type_, timestamp, position: Point::new(x, y), raw_pointer_id: 1 }
219 }
220
221 #[test]
222 fn a_press_a_move_and_a_release_are_begin_update_and_end_scaled_to_the_screen() {
223 let info = info(&[(abs_axis::ABS_X, 0, 4095), (abs_axis::ABS_Y, 0, 4095)]);
224 let mut handler = EvDevSingleTouchScreen::new(&info, &EvDevTouchScreenDeviceDescription::default()).unwrap();
225 let screen = Size::new(819.0, 409.5);
226 let events = [
227 ev(1000, ev_type::EV_ABS, abs_axis::ABS_X, 1000),
228 ev(1000, ev_type::EV_ABS, abs_axis::ABS_Y, 2000),
229 ev(1000, ev_type::EV_KEY, ev_key::BTN_TOUCH, 1),
230 syn(1000),
231 ev(1016, ev_type::EV_ABS, abs_axis::ABS_X, 1500),
232 syn(1016),
233 syn(1020),
235 ev(1032, ev_type::EV_KEY, ev_key::BTN_TOUCH, 0),
236 syn(1032),
237 ];
238 assert_eq!(
239 run(&mut handler, &events, screen),
240 [
241 touch(RawPointerEventType::TouchBegin, 1000, 200.0, 200.0),
242 touch(RawPointerEventType::TouchUpdate, 1016, 300.0, 200.0),
243 touch(RawPointerEventType::TouchEnd, 1032, 300.0, 200.0),
244 ]
245 );
246 }
247
248 #[test]
249 fn the_axis_minimum_and_the_calibration_are_applied_before_the_scale() {
250 let info = info(&[(abs_axis::ABS_X, 100, 1100), (abs_axis::ABS_Y, 200, 700)]);
251 let description = EvDevTouchScreenDeviceDescription {
253 path: None,
254 calibration_matrix: Matrix::new(0.0, 1.0, 1.0, 0.0, 0.0, 0.0),
255 };
256 let mut handler = EvDevSingleTouchScreen::new(&info, &description).unwrap();
257 let events = [
258 ev(5, ev_type::EV_ABS, abs_axis::ABS_X, 600),
259 ev(5, ev_type::EV_ABS, abs_axis::ABS_Y, 300),
260 ev(5, ev_type::EV_KEY, ev_key::BTN_TOUCH, 1),
261 syn(5),
262 ];
263 assert_eq!(
265 run(&mut handler, &events, Size::new(1000.0, 500.0)),
266 [touch(RawPointerEventType::TouchBegin, 5, 100.0, 500.0)]
267 );
268 }
269
270 #[test]
271 fn a_device_without_the_two_axes_is_not_a_touch_screen() {
272 let info = info(&[(abs_axis::ABS_X, 0, 10)]);
273 let error = EvDevSingleTouchScreen::new(&info, &EvDevTouchScreenDeviceDescription::default()).err();
274 assert_eq!(error.as_deref(), Some("Device is not a touchscreen"));
275 }
276
277 #[test]
278 fn the_events_of_a_dropped_report_are_passed_over() {
279 let info = info(&[(abs_axis::ABS_X, 0, 100), (abs_axis::ABS_Y, 0, 100)]);
280 let mut handler = EvDevSingleTouchScreen::new(&info, &EvDevTouchScreenDeviceDescription::default()).unwrap();
281 let screen = Size::new(100.0, 100.0);
282 let mut out = Vec::new();
283 let mut dropped = false;
284 let first = [
285 ev(1, ev_type::EV_ABS, abs_axis::ABS_X, 10),
286 ev(1, ev_type::EV_SYN, syn_code::SYN_DROPPED, 0),
287 ev(1, ev_type::EV_KEY, ev_key::BTN_TOUCH, 1),
288 ];
289 handle_events(&mut handler, &mut dropped, &first, screen, &mut out);
290 assert!(dropped && out.is_empty());
291 let second = [
293 ev(2, ev_type::EV_ABS, abs_axis::ABS_Y, 20),
294 syn(2),
295 ev(3, ev_type::EV_ABS, abs_axis::ABS_X, 50),
296 ev(3, ev_type::EV_ABS, abs_axis::ABS_Y, 60),
297 syn(3),
298 ];
299 handle_events(&mut handler, &mut dropped, &second, screen, &mut out);
300 assert!(!dropped);
301 assert_eq!(out, [touch(RawPointerEventType::TouchUpdate, 3, 50.0, 60.0)]);
302 }
303}