use super::{EvDevDeviceHandler, EvDevDeviceInfo, EvDevRawEvent};
use crate::native_unsafe_methods::{abs_axis, axis_event_code, ev_key, ev_type, input_absinfo, input_event};
use ferroui_base::input::raw::RawPointerEventType;
use ferroui_base::input::RawInputModifiers;
use ferroui_base::{Point, Size, Vector};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct EvDevMouseDeviceDescription {
pub path: Option<String>,
}
pub struct EvDevMouse {
axis_x: Option<input_absinfo>,
axis_y: Option<input_absinfo>,
position: Point,
abs_x: i32,
abs_y: i32,
has_relative_movement: bool,
has_absolute_movement: bool,
buttons: Vec<(u16, bool)>,
wheel: Vector,
modifiers: RawInputModifiers,
}
impl EvDevMouse {
pub fn new(device: &EvDevDeviceInfo) -> Self {
Self {
axis_x: device.abs_x(),
axis_y: device.abs_y(),
position: Point::default(),
abs_x: 0,
abs_y: 0,
has_relative_movement: false,
has_absolute_movement: false,
buttons: Vec::new(),
wheel: Vector::default(),
modifiers: RawInputModifiers::empty(),
}
}
fn scale(value: i32, axis: &input_absinfo, size: f64) -> f64 {
let range = (axis.maximum - axis.minimum) as f64;
if range <= 0.0 {
return 0.0;
}
(value - axis.minimum) as f64 * size / range
}
}
impl EvDevDeviceHandler for EvDevMouse {
fn handle_event(&mut self, ev: &input_event, screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
match ev.type_ {
ev_type::EV_REL => match ev.code {
axis_event_code::REL_X => {
self.position = Point::new(self.position.x + ev.value as f64, self.position.y);
self.has_relative_movement = true;
}
axis_event_code::REL_Y => {
self.position = Point::new(self.position.x, self.position.y + ev.value as f64);
self.has_relative_movement = true;
}
axis_event_code::REL_WHEEL => self.wheel = Vector::new(self.wheel.x, self.wheel.y + ev.value as f64),
axis_event_code::REL_HWHEEL => self.wheel = Vector::new(self.wheel.x - ev.value as f64, self.wheel.y),
_ => {}
},
ev_type::EV_ABS => match ev.code {
abs_axis::ABS_X => {
self.abs_x = ev.value;
self.has_absolute_movement = true;
}
abs_axis::ABS_Y => {
self.abs_y = ev.value;
self.has_absolute_movement = true;
}
_ => {}
},
ev_type::EV_KEY => {
if matches!(ev.code, ev_key::BTN_LEFT | ev_key::BTN_RIGHT | ev_key::BTN_MIDDLE) && ev.value != 2 {
self.buttons.push((ev.code, ev.value != 0));
}
}
ev_type::EV_SYN => {
let timestamp = ev.timestamp();
if self.has_absolute_movement {
if let (Some(axis_x), Some(axis_y)) = (&self.axis_x, &self.axis_y) {
self.position = Point::new(
Self::scale(self.abs_x, axis_x, screen_size.width),
Self::scale(self.abs_y, axis_y, screen_size.height),
);
}
}
if self.has_relative_movement {
self.position = Point::new(
self.position.x.clamp(0.0, (screen_size.width - 1.0).max(0.0)),
self.position.y.clamp(0.0, (screen_size.height - 1.0).max(0.0)),
);
}
if self.has_relative_movement || self.has_absolute_movement {
out.push(EvDevRawEvent::Pointer {
type_: RawPointerEventType::Move,
timestamp,
position: self.position,
modifiers: self.modifiers,
});
}
for (button, down) in std::mem::take(&mut self.buttons) {
let (flag, pressed, released) = match button {
ev_key::BTN_LEFT => (
RawInputModifiers::LEFT_MOUSE_BUTTON,
RawPointerEventType::LeftButtonDown,
RawPointerEventType::LeftButtonUp,
),
ev_key::BTN_RIGHT => (
RawInputModifiers::RIGHT_MOUSE_BUTTON,
RawPointerEventType::RightButtonDown,
RawPointerEventType::RightButtonUp,
),
_ => (
RawInputModifiers::MIDDLE_MOUSE_BUTTON,
RawPointerEventType::MiddleButtonDown,
RawPointerEventType::MiddleButtonUp,
),
};
self.modifiers.set(flag, down);
out.push(EvDevRawEvent::Pointer {
type_: if down { pressed } else { released },
timestamp,
position: self.position,
modifiers: self.modifiers,
});
}
if self.wheel != Vector::default() {
out.push(EvDevRawEvent::Wheel {
timestamp,
position: self.position,
delta: self.wheel,
modifiers: self.modifiers,
});
}
self.has_relative_movement = false;
self.has_absolute_movement = false;
self.wheel = Vector::default();
}
_ => {}
}
}
fn reset(&mut self, timestamp: u64, _screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
for (flag, released) in [
(RawInputModifiers::LEFT_MOUSE_BUTTON, RawPointerEventType::LeftButtonUp),
(RawInputModifiers::RIGHT_MOUSE_BUTTON, RawPointerEventType::RightButtonUp),
(RawInputModifiers::MIDDLE_MOUSE_BUTTON, RawPointerEventType::MiddleButtonUp),
] {
if self.modifiers.contains(flag) {
self.modifiers.remove(flag);
out.push(EvDevRawEvent::Pointer {
type_: released,
timestamp,
position: self.position,
modifiers: self.modifiers,
});
}
}
self.buttons.clear();
self.wheel = Vector::default();
self.has_relative_movement = false;
self.has_absolute_movement = false;
}
}
#[cfg(test)]
mod tests {
use super::super::ev_dev_touch_screen::tests::{ev, info, run, syn};
use super::*;
use crate::native_unsafe_methods::syn_code;
fn pointer(
type_: RawPointerEventType,
timestamp: u64,
x: f64,
y: f64,
modifiers: RawInputModifiers,
) -> EvDevRawEvent {
EvDevRawEvent::Pointer { type_, timestamp, position: Point::new(x, y), modifiers }
}
#[test]
fn relative_motion_accumulates_and_stays_on_the_screen() {
let mut mouse = EvDevMouse::new(&info(&[]));
let screen = Size::new(100.0, 50.0);
let none = RawInputModifiers::empty();
let events = [
ev(1, ev_type::EV_REL, axis_event_code::REL_X, 30),
ev(1, ev_type::EV_REL, axis_event_code::REL_Y, 20),
syn(1),
ev(2, ev_type::EV_REL, axis_event_code::REL_X, 500),
syn(2),
ev(3, ev_type::EV_REL, axis_event_code::REL_X, -10),
ev(3, ev_type::EV_REL, axis_event_code::REL_Y, -100),
syn(3),
];
assert_eq!(
run(&mut mouse, &events, screen),
[
pointer(RawPointerEventType::Move, 1, 30.0, 20.0, none),
pointer(RawPointerEventType::Move, 2, 99.0, 20.0, none),
pointer(RawPointerEventType::Move, 3, 89.0, 0.0, none),
]
);
}
#[test]
fn buttons_follow_the_move_of_their_report_and_are_modifiers_while_held() {
let mut mouse = EvDevMouse::new(&info(&[]));
let screen = Size::new(100.0, 100.0);
let left = RawInputModifiers::LEFT_MOUSE_BUTTON;
let right = RawInputModifiers::RIGHT_MOUSE_BUTTON;
let events = [
ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
ev(1, ev_type::EV_REL, axis_event_code::REL_X, 5),
syn(1),
ev(2, ev_type::EV_KEY, ev_key::BTN_RIGHT, 1),
syn(2),
ev(3, ev_type::EV_KEY, ev_key::BTN_RIGHT, 0),
ev(3, ev_type::EV_KEY, ev_key::BTN_LEFT, 0),
syn(3),
ev(4, ev_type::EV_KEY, ev_key::BTN_MIDDLE, 1),
syn(4),
];
assert_eq!(
run(&mut mouse, &events, screen),
[
pointer(RawPointerEventType::Move, 1, 5.0, 0.0, RawInputModifiers::empty()),
pointer(RawPointerEventType::LeftButtonDown, 1, 5.0, 0.0, left),
pointer(RawPointerEventType::RightButtonDown, 2, 5.0, 0.0, left | right),
pointer(RawPointerEventType::RightButtonUp, 3, 5.0, 0.0, left),
pointer(RawPointerEventType::LeftButtonUp, 3, 5.0, 0.0, RawInputModifiers::empty()),
pointer(RawPointerEventType::MiddleButtonDown, 4, 5.0, 0.0, RawInputModifiers::MIDDLE_MOUSE_BUTTON),
]
);
}
#[test]
fn the_wheel_is_a_delta_at_the_position() {
let mut mouse = EvDevMouse::new(&info(&[]));
let events = [
ev(1, ev_type::EV_REL, axis_event_code::REL_WHEEL, -1),
syn(1),
ev(2, ev_type::EV_REL, axis_event_code::REL_HWHEEL, 2),
syn(2),
];
let wheel = |timestamp, x, y| EvDevRawEvent::Wheel {
timestamp,
position: Point::default(),
delta: Vector::new(x, y),
modifiers: RawInputModifiers::empty(),
};
assert_eq!(run(&mut mouse, &events, Size::new(10.0, 10.0)), [wheel(1, 0.0, -1.0), wheel(2, -2.0, 0.0)]);
}
#[test]
fn an_absolute_pointer_is_scaled_to_the_screen() {
let mut mouse = EvDevMouse::new(&info(&[(abs_axis::ABS_X, 0, 32767), (abs_axis::ABS_Y, 0, 32767)]));
let events = [
ev(1, ev_type::EV_ABS, abs_axis::ABS_X, 32767),
ev(1, ev_type::EV_ABS, abs_axis::ABS_Y, 0),
ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
syn(1),
];
let left = RawInputModifiers::LEFT_MOUSE_BUTTON;
assert_eq!(
run(&mut mouse, &events, Size::new(1024.0, 768.0)),
[
pointer(RawPointerEventType::Move, 1, 1024.0, 0.0, RawInputModifiers::empty()),
pointer(RawPointerEventType::LeftButtonDown, 1, 1024.0, 0.0, left),
]
);
}
#[test]
fn dropped_events_release_the_held_buttons() {
let mut mouse = EvDevMouse::new(&info(&[]));
let events = [
ev(1, ev_type::EV_KEY, ev_key::BTN_LEFT, 1),
syn(1),
ev(2, ev_type::EV_SYN, syn_code::SYN_DROPPED, 0),
ev(2, ev_type::EV_REL, axis_event_code::REL_X, 5),
syn(2),
];
let out = run(&mut mouse, &events, Size::new(10.0, 10.0));
assert_eq!(out[1], pointer(RawPointerEventType::LeftButtonUp, 2, 0.0, 0.0, RawInputModifiers::empty()));
assert_eq!(out.len(), 2);
}
}