Skip to content

Commit

Permalink
Add X11 opt-in function for device events
Browse files Browse the repository at this point in the history
Previously on X11, by default all global events were broadcasted to
every Winit application. This unnecessarily drains battery due to
excessive CPU usage when moving the mouse.

To resolve this, device events are now ignored by default and users must
manually opt into it using `EventLoop::set_filter_device_events`.

Fixes (rust-windowing#1634) on Linux.
  • Loading branch information
chrisduerr committed Feb 16, 2022
1 parent 0e52672 commit c4d208d
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- **Breaking:** Bump `ndk` version to 0.6, ndk-sys to `v0.3`, `ndk-glue` to `0.6`.
- Remove no longer needed `WINIT_LINK_COLORSYNC` environment variable.
- **Breaking:** Rename the `Exit` variant of `ControlFlow` to `ExitWithCode`, which holds a value to control the exit code after running. Add an `Exit` constant which aliases to `ExitWithCode(0)` instead to avoid major breakage. This shouldn't affect most existing programs.
- **Breaking:** On X11, device events are now ignored by default, use `EventLoop::set_filter_device_events` to opt-into device events again.

# 0.26.1 (2022-01-05)

Expand Down
22 changes: 22 additions & 0 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,28 @@ impl<T> EventLoop<T> {
event_loop_proxy: self.event_loop.create_proxy(),
}
}

/// Change [`DeviceEvent`] filter mode.
///
/// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
/// will ignore them by default on Linux/BSD. This method allows changing this filter at
/// runtime to explicitly capture them again.
///
/// ## Platform-specific
///
/// - **Wayland / Windows / macOS / iOS / Android / Web**: Unsupported.
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn set_filter_device_events(&self, filter_events: bool) {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
self.event_loop.set_filter_device_events(filter_events);
}
}

impl<T> Deref for EventLoop<T> {
Expand Down
10 changes: 9 additions & 1 deletion src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,15 @@ impl<T: 'static> EventLoop<T> {
}

pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
x11_or_wayland!(match self; EventLoop(evl) => evl.window_target())
x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target())
}

#[cfg(feature = "x11")]
pub fn set_filter_device_events(&self, filter_events: bool) {
match self {
EventLoop::X(evlp) => evlp.set_filter_device_events(filter_events),
EventLoop::Wayland(_) => (),
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<T: 'static> EventProcessor<T> {
let mut devices = self.devices.borrow_mut();
if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
for info in info.iter() {
devices.insert(DeviceId(info.deviceid), Device::new(self, info));
devices.insert(DeviceId(info.deviceid), Device::new(info));
}
}
}
Expand Down
31 changes: 17 additions & 14 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,22 @@ impl<T: 'static> EventLoop<T> {
}
}

pub fn set_filter_device_events(&self, filter_events: bool) {
let mut mask = 0;
if !filter_events {
mask = ffi::XI_RawMotionMask
| ffi::XI_RawButtonPressMask
| ffi::XI_RawButtonReleaseMask
| ffi::XI_RawKeyPressMask
| ffi::XI_RawKeyReleaseMask;
}

let wt = get_xtarget(&self.event_processor.target);
wt.xconn
.select_xinput_events(wt.root, ffi::XIAllDevices, mask)
.queue();
}

pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
user_sender: self.user_sender.clone(),
Expand Down Expand Up @@ -695,24 +711,11 @@ enum ScrollOrientation {
}

impl Device {
fn new<T: 'static>(el: &EventProcessor<T>, info: &ffi::XIDeviceInfo) -> Self {
fn new(info: &ffi::XIDeviceInfo) -> Self {
let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
let mut scroll_axes = Vec::new();

let wt = get_xtarget(&el.target);

if Device::physical_device(info) {
// Register for global raw events
let mask = ffi::XI_RawMotionMask
| ffi::XI_RawButtonPressMask
| ffi::XI_RawButtonReleaseMask
| ffi::XI_RawKeyPressMask
| ffi::XI_RawKeyReleaseMask;
// The request buffer is flushed when we poll for events
wt.xconn
.select_xinput_events(wt.root, info.deviceid, mask)
.queue();

// Identify scroll axes
for class_ptr in Device::classes(info) {
let class = unsafe { &**class_ptr };
Expand Down

0 comments on commit c4d208d

Please sign in to comment.