Skip to content

Commit

Permalink
On X11, filter out tiny device mouse events
Browse files Browse the repository at this point in the history
Usually, if mouse events are equal to (0, 0) we filter them out.
However, if the event is very close to zero it will still be given to
the user. In some cases this can be caused by bad float math on the X11
server side.

Fix it by filtering absolute values smaller than floating point epsilon.

Signed-off-by: John Nunley <[email protected]>
Closes: #3500
  • Loading branch information
notgull authored and kchibisov committed Mar 1, 2024
1 parent 8b6c8ef commit 2dae807
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Unreleased` header.

# Unreleased

- On X11, filter close to zero values in mouse device events

# 0.29.11

- On Wayland, fix DeviceEvent::Motion not being sent
Expand Down
16 changes: 8 additions & 8 deletions src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,8 +1502,8 @@ impl<T: 'static> EventProcessor<T> {
let mask =
unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
let mut value = xev.raw_values;
let mut mouse_delta = (0.0, 0.0);
let mut scroll_delta = (0.0, 0.0);
let mut mouse_delta = util::Delta::default();
let mut scroll_delta = util::Delta::default();
for i in 0..xev.valuators.mask_len * 8 {
if !xinput2::XIMaskIsSet(mask, i) {
continue;
Expand All @@ -1513,10 +1513,10 @@ impl<T: 'static> EventProcessor<T> {
// We assume that every XInput2 device with analog axes is a pointing device emitting
// relative coordinates.
match i {
0 => mouse_delta.0 = x,
1 => mouse_delta.1 = x,
2 => scroll_delta.0 = x as f32,
3 => scroll_delta.1 = x as f32,
0 => mouse_delta.set_x(x),
1 => mouse_delta.set_y(x),
2 => scroll_delta.set_x(x as f32),
3 => scroll_delta.set_y(x as f32),
_ => {}
}

Expand All @@ -1532,15 +1532,15 @@ impl<T: 'static> EventProcessor<T> {
value = unsafe { value.offset(1) };
}

if mouse_delta != (0.0, 0.0) {
if let Some(mouse_delta) = mouse_delta.consume() {
let event = Event::DeviceEvent {
device_id: did,
event: DeviceEvent::MouseMotion { delta: mouse_delta },
};
callback(&self.target, event);
}

if scroll_delta != (0.0, 0.0) {
if let Some(scroll_delta) = scroll_delta.consume() {
let event = Event::DeviceEvent {
device_id: did,
event: DeviceEvent::MouseWheel {
Expand Down
3 changes: 2 additions & 1 deletion src/platform_impl/linux/x11/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ mod icon;
mod input;
pub mod keys;
pub(crate) mod memory;
mod mouse;
mod randr;
mod window_property;
mod wm;
mod xmodmap;

pub use self::{
geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
geometry::*, hint::*, input::*, mouse::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
};

use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};
Expand Down
52 changes: 52 additions & 0 deletions src/platform_impl/linux/x11/util/mouse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Utilities for handling mouse events.
/// Recorded mouse delta designed to filter out noise.
pub struct Delta<T> {
x: T,
y: T,
}

impl<T: Default> Default for Delta<T> {
fn default() -> Self {
Self {
x: Default::default(),
y: Default::default(),
}
}
}

impl<T: Default> Delta<T> {
pub(crate) fn set_x(&mut self, x: T) {
self.x = x;
}

pub(crate) fn set_y(&mut self, y: T) {
self.y = y;
}
}

macro_rules! consume {
($this:expr, $ty:ty) => {{
let this = $this;
let (x, y) = match (this.x.abs() < <$ty>::EPSILON, this.y.abs() < <$ty>::EPSILON) {
(true, true) => return None,
(true, false) => (this.x, 0.0),
(false, true) => (0.0, this.y),
(false, false) => (this.x, this.y),
};

Some((x, y))
}};
}

impl Delta<f32> {
pub(crate) fn consume(self) -> Option<(f32, f32)> {
consume!(self, f32)
}
}

impl Delta<f64> {
pub(crate) fn consume(self) -> Option<(f64, f64)> {
consume!(self, f64)
}
}

0 comments on commit 2dae807

Please sign in to comment.