From 3d4c53459a7e9a616df9673610a2e6bb48205e4f Mon Sep 17 00:00:00 2001 From: John Nunley Date: Fri, 1 Mar 2024 01:11:28 -0800 Subject: [PATCH] On X11, filter out tiny device mouse events 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 Closes: #3500 --- CHANGELOG.md | 1 + .../linux/x11/event_processor.rs | 16 +++--- src/platform_impl/linux/x11/util/mod.rs | 4 +- src/platform_impl/linux/x11/util/mouse.rs | 52 +++++++++++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 src/platform_impl/linux/x11/util/mouse.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be2cf75..9417bd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- On X11, filter close to zero values in mouse device events - Move `dpi` types to its own crate, and re-export it from the root crate. - Implement `Sync` for `EventLoopProxy`. - **Breaking:** Move `Window::new` to `ActiveEventLoop::create_window` and `EventLoop::create_window` (with the latter being deprecated). diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index c6cd61e9..bca85ae6 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1518,8 +1518,8 @@ impl EventProcessor { 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; @@ -1529,10 +1529,10 @@ impl EventProcessor { // 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), _ => {} } @@ -1548,7 +1548,7 @@ impl EventProcessor { 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 }, @@ -1556,7 +1556,7 @@ impl EventProcessor { 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 { diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index 3246b072..0db5986c 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -15,13 +15,15 @@ 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::{ - cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap, + cursor::*, geometry::*, hint::*, input::*, mouse::*, window_property::*, wm::*, + xmodmap::ModifierKeymap, }; use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError}; diff --git a/src/platform_impl/linux/x11/util/mouse.rs b/src/platform_impl/linux/x11/util/mouse.rs new file mode 100644 index 00000000..212ea572 --- /dev/null +++ b/src/platform_impl/linux/x11/util/mouse.rs @@ -0,0 +1,52 @@ +//! Utilities for handling mouse events. + +/// Recorded mouse delta designed to filter out noise. +pub struct Delta { + x: T, + y: T, +} + +impl Default for Delta { + fn default() -> Self { + Self { + x: Default::default(), + y: Default::default(), + } + } +} + +impl Delta { + 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 { + pub(crate) fn consume(self) -> Option<(f32, f32)> { + consume!(self, f32) + } +} + +impl Delta { + pub(crate) fn consume(self) -> Option<(f64, f64)> { + consume!(self, f64) + } +}