Skip to main content

ferroui_linuxframebuffer/input/ev_dev/
ev_dev_mouse.rs

1//! The handler of a pointer device: a mouse, or an absolute pointer such
2//! as the one of a virtual machine.
3//!
4//! An addition (docs/porting/linux-framebuffer-platform.md, section 7):
5//! the reference handles pointers in its `libinput` backend only, and
6//! there only absolute ones.
7
8use super::{EvDevDeviceHandler, EvDevDeviceInfo, EvDevRawEvent};
9use crate::native_unsafe_methods::{abs_axis, axis_event_code, ev_key, ev_type, input_absinfo, input_event};
10use ferroui_base::input::raw::RawPointerEventType;
11use ferroui_base::input::RawInputModifiers;
12use ferroui_base::{Point, Size, Vector};
13
14/// The description of a pointer device: `(path):type=mouse`.
15#[derive(Clone, Debug, Default, PartialEq)]
16pub struct EvDevMouseDeviceDescription {
17    pub path: Option<String>,
18}
19
20pub struct EvDevMouse {
21    axis_x: Option<input_absinfo>,
22    axis_y: Option<input_absinfo>,
23    /// The position of the pointer on the screen.
24    position: Point,
25    abs_x: i32,
26    abs_y: i32,
27    has_relative_movement: bool,
28    has_absolute_movement: bool,
29    /// The button changes of the report, in order.
30    buttons: Vec<(u16, bool)>,
31    wheel: Vector,
32    modifiers: RawInputModifiers,
33}
34
35impl EvDevMouse {
36    pub fn new(device: &EvDevDeviceInfo) -> Self {
37        Self {
38            axis_x: device.abs_x(),
39            axis_y: device.abs_y(),
40            position: Point::default(),
41            abs_x: 0,
42            abs_y: 0,
43            has_relative_movement: false,
44            has_absolute_movement: false,
45            buttons: Vec::new(),
46            wheel: Vector::default(),
47            modifiers: RawInputModifiers::empty(),
48        }
49    }
50
51    fn scale(value: i32, axis: &input_absinfo, size: f64) -> f64 {
52        let range = (axis.maximum - axis.minimum) as f64;
53        if range <= 0.0 {
54            return 0.0;
55        }
56        (value - axis.minimum) as f64 * size / range
57    }
58}
59
60impl EvDevDeviceHandler for EvDevMouse {
61    fn handle_event(&mut self, ev: &input_event, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
62        match ev.type_ {
63            ev_type::EV_REL => match ev.code {
64                axis_event_code::REL_X => {
65                    self.position = Point::new(self.position.x + ev.value as f64, self.position.y);
66                    self.has_relative_movement = true;
67                }
68                axis_event_code::REL_Y => {
69                    self.position = Point::new(self.position.x, self.position.y + ev.value as f64);
70                    self.has_relative_movement = true;
71                }
72                // A step away from the user scrolls up; a step to the
73                // right scrolls right, which is a negative delta.
74                axis_event_code::REL_WHEEL => self.wheel = Vector::new(self.wheel.x, self.wheel.y + ev.value as f64),
75                axis_event_code::REL_HWHEEL => self.wheel = Vector::new(self.wheel.x - ev.value as f64, self.wheel.y),
76                _ => {}
77            },
78            ev_type::EV_ABS => match ev.code {
79                abs_axis::ABS_X => {
80                    self.abs_x = ev.value;
81                    self.has_absolute_movement = true;
82                }
83                abs_axis::ABS_Y => {
84                    self.abs_y = ev.value;
85                    self.has_absolute_movement = true;
86                }
87                _ => {}
88            },
89            ev_type::EV_KEY => {
90                if matches!(ev.code, ev_key::BTN_LEFT | ev_key::BTN_RIGHT | ev_key::BTN_MIDDLE) && ev.value != 2 {
91                    self.buttons.push((ev.code, ev.value != 0));
92                }
93            }
94            ev_type::EV_SYN => {
95                let timestamp = ev.timestamp();
96                if self.has_absolute_movement {
97                    if let (Some(axis_x), Some(axis_y)) = (&self.axis_x, &self.axis_y) {
98                        self.position = Point::new(
99                            Self::scale(self.abs_x, axis_x, screen_size.width),
100                            Self::scale(self.abs_y, axis_y, screen_size.height),
101                        );
102                    }
103                }
104                if self.has_relative_movement {
105                    // The pointer stays on the screen.
106                    self.position = Point::new(
107                        self.position.x.clamp(0.0, (screen_size.width - 1.0).max(0.0)),
108                        self.position.y.clamp(0.0, (screen_size.height - 1.0).max(0.0)),
109                    );
110                }
111                if self.has_relative_movement || self.has_absolute_movement {
112                    out.push(EvDevRawEvent::Pointer {
113                        type_: RawPointerEventType::Move,
114                        timestamp,
115                        position: self.position,
116                        modifiers: self.modifiers,
117                    });
118                }
119                for (button, down) in std::mem::take(&mut self.buttons) {
120                    let (flag, pressed, released) = match button {
121                        ev_key::BTN_LEFT => (
122                            RawInputModifiers::LEFT_MOUSE_BUTTON,
123                            RawPointerEventType::LeftButtonDown,
124                            RawPointerEventType::LeftButtonUp,
125                        ),
126                        ev_key::BTN_RIGHT => (
127                            RawInputModifiers::RIGHT_MOUSE_BUTTON,
128                            RawPointerEventType::RightButtonDown,
129                            RawPointerEventType::RightButtonUp,
130                        ),
131                        _ => (
132                            RawInputModifiers::MIDDLE_MOUSE_BUTTON,
133                            RawPointerEventType::MiddleButtonDown,
134                            RawPointerEventType::MiddleButtonUp,
135                        ),
136                    };
137                    self.modifiers.set(flag, down);
138                    out.push(EvDevRawEvent::Pointer {
139                        type_: if down { pressed } else { released },
140                        timestamp,
141                        position: self.position,
142                        modifiers: self.modifiers,
143                    });
144                }
145                if self.wheel != Vector::default() {
146                    out.push(EvDevRawEvent::Wheel {
147                        timestamp,
148                        position: self.position,
149                        delta: self.wheel,
150                        modifiers: self.modifiers,
151                    });
152                }
153                self.has_relative_movement = false;
154                self.has_absolute_movement = false;
155                self.wheel = Vector::default();
156            }
157            _ => {}
158        }
159    }
160
161    fn reset(&mut self, timestamp: u64, _screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
162        // The buttons that were held are released where the pointer is.
163        for (flag, released) in [
164            (RawInputModifiers::LEFT_MOUSE_BUTTON, RawPointerEventType::LeftButtonUp),
165            (RawInputModifiers::RIGHT_MOUSE_BUTTON, RawPointerEventType::RightButtonUp),
166            (RawInputModifiers::MIDDLE_MOUSE_BUTTON, RawPointerEventType::MiddleButtonUp),
167        ] {
168            if self.modifiers.contains(flag) {
169                self.modifiers.remove(flag);
170                out.push(EvDevRawEvent::Pointer {
171                    type_: released,
172                    timestamp,
173                    position: self.position,
174                    modifiers: self.modifiers,
175                });
176            }
177        }
178        self.buttons.clear();
179        self.wheel = Vector::default();
180        self.has_relative_movement = false;
181        self.has_absolute_movement = false;
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::super::ev_dev_touch_screen::tests::{ev, info, run, syn};
188    use super::*;
189    use crate::native_unsafe_methods::syn_code;
190
191    fn pointer(
192        type_: RawPointerEventType,
193        timestamp: u64,
194        x: f64,
195        y: f64,
196        modifiers: RawInputModifiers,
197    ) -> EvDevRawEvent {
198        EvDevRawEvent::Pointer { type_, timestamp, position: Point::new(x, y), modifiers }
199    }
200
201    #[test]
202    fn relative_motion_accumulates_and_stays_on_the_screen() {
203        let mut mouse = EvDevMouse::new(&info(&[]));
204        let screen = Size::new(100.0, 50.0);
205        let none = RawInputModifiers::empty();
206        let events = [
207            ev(1, ev_type::EV_REL, axis_event_code::REL_X, 30),
208            ev(1, ev_type::EV_REL, axis_event_code::REL_Y, 20),
209            syn(1),
210            ev(2, ev_type::EV_REL, axis_event_code::REL_X, 500),
211            syn(2),
212            ev(3, ev_type::EV_REL, axis_event_code::REL_X, -10),
213            ev(3, ev_type::EV_REL, axis_event_code::REL_Y, -100),
214            syn(3),
215        ];
216        assert_eq!(
217            run(&mut mouse, &events, screen),
218            [
219                pointer(RawPointerEventType::Move, 1, 30.0, 20.0, none),
220                pointer(RawPointerEventType::Move, 2, 99.0, 20.0, none),
221                pointer(RawPointerEventType::Move, 3, 89.0, 0.0, none),
222            ]
223        );
224    }
225
226    #[test]
227    fn buttons_follow_the_move_of_their_report_and_are_modifiers_while_held() {
228        let mut mouse = EvDevMouse::new(&info(&[]));
229        let screen = Size::new(100.0, 100.0);
230        let left = RawInputModifiers::LEFT_MOUSE_BUTTON;
231        let right = RawInputModifiers::RIGHT_MOUSE_BUTTON;
232        let events = [
233            ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
234            ev(1, ev_type::EV_REL, axis_event_code::REL_X, 5),
235            syn(1),
236            ev(2, ev_type::EV_KEY, ev_key::BTN_RIGHT, 1),
237            syn(2),
238            ev(3, ev_type::EV_KEY, ev_key::BTN_RIGHT, 0),
239            ev(3, ev_type::EV_KEY, ev_key::BTN_LEFT, 0),
240            syn(3),
241            ev(4, ev_type::EV_KEY, ev_key::BTN_MIDDLE, 1),
242            syn(4),
243        ];
244        assert_eq!(
245            run(&mut mouse, &events, screen),
246            [
247                pointer(RawPointerEventType::Move, 1, 5.0, 0.0, RawInputModifiers::empty()),
248                pointer(RawPointerEventType::LeftButtonDown, 1, 5.0, 0.0, left),
249                pointer(RawPointerEventType::RightButtonDown, 2, 5.0, 0.0, left | right),
250                pointer(RawPointerEventType::RightButtonUp, 3, 5.0, 0.0, left),
251                pointer(RawPointerEventType::LeftButtonUp, 3, 5.0, 0.0, RawInputModifiers::empty()),
252                pointer(RawPointerEventType::MiddleButtonDown, 4, 5.0, 0.0, RawInputModifiers::MIDDLE_MOUSE_BUTTON),
253            ]
254        );
255    }
256
257    #[test]
258    fn the_wheel_is_a_delta_at_the_position() {
259        let mut mouse = EvDevMouse::new(&info(&[]));
260        let events = [
261            ev(1, ev_type::EV_REL, axis_event_code::REL_WHEEL, -1),
262            syn(1),
263            ev(2, ev_type::EV_REL, axis_event_code::REL_HWHEEL, 2),
264            syn(2),
265        ];
266        let wheel = |timestamp, x, y| EvDevRawEvent::Wheel {
267            timestamp,
268            position: Point::default(),
269            delta: Vector::new(x, y),
270            modifiers: RawInputModifiers::empty(),
271        };
272        assert_eq!(run(&mut mouse, &events, Size::new(10.0, 10.0)), [wheel(1, 0.0, -1.0), wheel(2, -2.0, 0.0)]);
273    }
274
275    #[test]
276    fn an_absolute_pointer_is_scaled_to_the_screen() {
277        // The tablet of a virtual machine: 0 to 32767 on both axes.
278        let mut mouse = EvDevMouse::new(&info(&[(abs_axis::ABS_X, 0, 32767), (abs_axis::ABS_Y, 0, 32767)]));
279        let events = [
280            ev(1, ev_type::EV_ABS, abs_axis::ABS_X, 32767),
281            ev(1, ev_type::EV_ABS, abs_axis::ABS_Y, 0),
282            ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
283            syn(1),
284        ];
285        let left = RawInputModifiers::LEFT_MOUSE_BUTTON;
286        assert_eq!(
287            run(&mut mouse, &events, Size::new(1024.0, 768.0)),
288            [
289                pointer(RawPointerEventType::Move, 1, 1024.0, 0.0, RawInputModifiers::empty()),
290                pointer(RawPointerEventType::LeftButtonDown, 1, 1024.0, 0.0, left),
291            ]
292        );
293    }
294
295    #[test]
296    fn dropped_events_release_the_held_buttons() {
297        let mut mouse = EvDevMouse::new(&info(&[]));
298        let events = [
299            ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
300            syn(1),
301            ev(2, ev_type::EV_SYN, syn_code::SYN_DROPPED, 0),
302            ev(2, ev_type::EV_REL, axis_event_code::REL_X, 5),
303            syn(2),
304        ];
305        let out = run(&mut mouse, &events, Size::new(10.0, 10.0));
306        assert_eq!(out[1], pointer(RawPointerEventType::LeftButtonUp, 2, 0.0, 0.0, RawInputModifiers::empty()));
307        assert_eq!(out.len(), 2);
308    }
309}