Add X11 opt-in function for device events
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 `EventLoopWindowTarget::set_filter_device_events`. Fixes (#1634) on Linux.
This commit is contained in:
parent
c7f7181388
commit
f10a984ba3
5 changed files with 108 additions and 32 deletions
|
|
@ -30,7 +30,9 @@ use crate::{
|
|||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
event_loop::{
|
||||
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
||||
},
|
||||
icon::Icon,
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
|
||||
|
|
@ -732,7 +734,7 @@ 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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -793,6 +795,16 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) {
|
||||
match *self {
|
||||
#[cfg(feature = "wayland")]
|
||||
EventLoopWindowTarget::Wayland(_) => (),
|
||||
#[cfg(feature = "x11")]
|
||||
EventLoopWindowTarget::X(ref mut evlp) => evlp.set_device_event_filter(_filter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sticky_exit_callback<T, F>(
|
||||
|
|
|
|||
|
|
@ -49,7 +49,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -907,6 +907,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
if self.active_window != Some(xev.event) {
|
||||
self.active_window = Some(xev.event);
|
||||
|
||||
wt.update_device_event_filter(true);
|
||||
|
||||
let window_id = mkwid(xev.event);
|
||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
|
|
@ -956,6 +958,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
if !self.window_exists(xev.event) {
|
||||
return;
|
||||
}
|
||||
|
||||
wt.ime
|
||||
.borrow_mut()
|
||||
.unfocus(xev.event)
|
||||
|
|
@ -964,6 +967,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
if self.active_window.take() == Some(xev.event) {
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
wt.update_device_event_filter(false);
|
||||
|
||||
// Issue key release events for all pressed keys
|
||||
Self::handle_pressed_keys(
|
||||
wt,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ use self::{
|
|||
use crate::{
|
||||
error::OsError as RootOsError,
|
||||
event::{Event, StartCause},
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
event_loop::{
|
||||
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
||||
},
|
||||
platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
|
||||
window::WindowAttributes,
|
||||
};
|
||||
|
|
@ -105,6 +107,7 @@ pub struct EventLoopWindowTarget<T> {
|
|||
ime: RefCell<Ime>,
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
redraw_sender: WakeSender<WindowId>,
|
||||
device_event_filter: DeviceEventFilter,
|
||||
_marker: ::std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
|
|
@ -229,21 +232,27 @@ impl<T: 'static> EventLoop<T> {
|
|||
let (user_sender, user_channel) = std::sync::mpsc::channel();
|
||||
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
|
||||
|
||||
let window_target = EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
windows: Default::default(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
ime_sender,
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
redraw_sender: WakeSender {
|
||||
sender: redraw_sender, // not used again so no clone
|
||||
waker: waker.clone(),
|
||||
},
|
||||
device_event_filter: Default::default(),
|
||||
};
|
||||
|
||||
// Set initial device event filter.
|
||||
window_target.update_device_event_filter(true);
|
||||
|
||||
let target = Rc::new(RootELW {
|
||||
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
windows: Default::default(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
ime_sender,
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
redraw_sender: WakeSender {
|
||||
sender: redraw_sender, // not used again so no clone
|
||||
waker: waker.clone(),
|
||||
},
|
||||
}),
|
||||
p: super::EventLoopWindowTarget::X(window_target),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
});
|
||||
|
||||
|
|
@ -524,6 +533,29 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
pub fn x_connection(&self) -> &Arc<XConnection> {
|
||||
&self.xconn
|
||||
}
|
||||
|
||||
pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {
|
||||
self.device_event_filter = filter;
|
||||
}
|
||||
|
||||
/// Update the device event filter based on window focus.
|
||||
pub fn update_device_event_filter(&self, focus: bool) {
|
||||
let filter_events = self.device_event_filter == DeviceEventFilter::Never
|
||||
|| (self.device_event_filter == DeviceEventFilter::Unfocused && !focus);
|
||||
|
||||
let mut mask = 0;
|
||||
if !filter_events {
|
||||
mask = ffi::XI_RawMotionMask
|
||||
| ffi::XI_RawButtonPressMask
|
||||
| ffi::XI_RawButtonReleaseMask
|
||||
| ffi::XI_RawKeyPressMask
|
||||
| ffi::XI_RawKeyReleaseMask;
|
||||
}
|
||||
|
||||
self.xconn
|
||||
.select_xinput_events(self.root, ffi::XIAllDevices, mask)
|
||||
.queue();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
|
|
@ -698,24 +730,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 };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue