Using `XI_Raw*`-events for input processing on X11 causes a risk of input-bind clashing between the Application and WM
I wrote a WM and got a bug report of weird scrolling behaviour using alacritty.
The issue: Alacritty receives and acts upon input that should be grabbed.
What I believe is the cause:
Alacritty uses glutin which uses winit for input. Winit listens to XI_Raw* events which, according to this ancient changelog are sent regardless of grab state.
Using XI_Raw* means that if any Rust application built on winit has clashing input-bindings with a WM it'll cause a bug.
In this particular case, Mod+scroll to resize a window causes unwanted scrolling inside the window as well.
This isn't really a major bug and I'm guessing that there's a reason to use XI_Raw* over the alternatives. But if not, I think it'd be good to not use them to prevent clashes on X11
The only "raw" event we use for scrolling is XI_RawMotion, but that only feeds into a DeviceEvent, which Alacritty (to my knowledge) ignores. I suspect that the issue has something to do with how we use XI_Motion for regular scroll events (we also use XI_ButtonPress and XI_ButtonRelease, but those shouldn't suffer from the "xinput2 ignores grab state for raw events" issue).
Could you check which X11 event it is that causes the issue for you with this patch on winit?
diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs
index bbd07ff1..ed58239f 100644
--- a/src/platform_impl/linux/x11/event_processor.rs
+++ b/src/platform_impl/linux/x11/event_processor.rs
@@ -703,6 +703,7 @@ impl<T: 'static> EventProcessor<T> {
// turn) as axis motion, so we don't otherwise special-case these button presses.
4 | 5 | 6 | 7 => {
if xev.flags & ffi::XIPointerEmulated == 0 {
+ println!("XI_ButtonPress | XI_ButtonRelease");
callback(Event::WindowEvent {
window_id,
event: MouseWheel {
@@ -786,6 +787,7 @@ impl<T: 'static> EventProcessor<T> {
{
let delta = (x - info.position) / info.increment;
info.position = x;
+ println!("XI_Motion");
events.push(Event::WindowEvent {
window_id,
event: MouseWheel {
Running cargo run --example window | grep -E "(WindowEvent.*MouseWheel|XI_)" should print out the messages you need to figure out what's happening on your end.
I think it's the below code, since the scroll is a ButtonPressEvent on X11, and since winit uses XI_RAW I can't grab it.
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::DeviceEvent {
device_id: mkdid(xev.deviceid),
event: DeviceEvent::Button {
button: xev.detail as u32,
state: match xev.evtype {
ffi::XI_RawButtonPress => Pressed,
ffi::XI_RawButtonRelease => Released,
_ => unreachable!(),
},
},
});
}
}
On the last three I'm pressing the modifier (LOGO) which should grab the button but since it's a XI_RawButtonPress it doesn't get grabbed. I could be wrong but I'm fairly sure.
Edit: Oh no it's actually here https://github.com/rust-windowing/winit/blob/master/src/platform_impl/linux/x11/event_processor.rs#L704, that should be grab-able since it's just a regular XI_ButtonPress, I'm gonna try to grab that and see if it works!
Edit2: Preliminary grabbing events seems to work for everything that isn't winit:
x11rb::xcb::xinput::xi_passive_grab_device(
&mut connection.connection,
CURRENT_TIME,
root_win,
0,
x11_dl::xinput2::XIAnyButton as u32,
dev.device_id,
GrabType::BUTTON,
GrabMode22::ASYNC,
GrabMode::ASYNC,
GrabOwner::OWNER,
&[mask],
&[ControlMask],
false,
)?
.reply(&mut connection.connection)?;
On any other window, holding control disables all button input, including scrolling, but not the example winit window or alacritty, gonna keep digging
Edit3: It is definitely XI_MOTION as you said, it's this one https://github.com/rust-windowing/winit/blob/master/src/platform_impl/linux/x11/event_processor.rs#L789 grabbing motion isn't as straight forward as grabbing keys though, I'm gonna experiment a bit with trying to grab that
Alacritty isn't using any DeviceEvent's in fact it'll opt out from them entirely in the next release. So I do think that this bug in your wm, since it's the first time I see something like this.
Alacritty isn't using any
DeviceEvent's in fact it'll opt out from them entirely in the next release. So I do think that this bug in your wm, since it's the first time I see something like this.
I don't think it's especially common to grab the scroll wheel as a mouse key, but again, it's only winit applications that display this behaviour.
I made a change using ButtonPress over XI_ButtonPress, this solves the issue but winit is complex and it probably risks causing other issues, @maroider is there any reason not to use regular ButtonPressEvents as you do with KeyPressEvents, I even saw that they'd been commented out, I'm guessing it's because of similar issues as I've encountered here? Otherwise I could make a PR with that change.
E: Tested building alacritty <- local glutin <- this changed local winit, and the scrolling behaviour is now as expected
I made a more legitimate PR with this change here https://github.com/rust-windowing/winit/pull/2362. XI_ButtonPress is disregarded completely in favor of ButtonPress, however, XI_Motion is still used but if the device is targeting the core pointer it won't send* MouseWheel events, as those would be duplicated with the ButtonPressEvent for the mousewheel which comes immediately after