From e0577125658328a46a2f8932d875e18349d58816 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 10 Apr 2026 11:23:49 -0400 Subject: [PATCH] x11: Ignore slave button presses on non-focused windows When a window has the pointer grabbed, the X server will grab all master device events, and XInput2 will continue to deliver slave events to the window immediately under the pointer, regardless of grab status. Only send slave pointer events to the focused window, and fall back to the core X events to catch button presses missed when the pointer is over another window. (cherry picked from commit 0fc9db9b82c318b44562778cd4709e9638a57087) --- src/video/x11/SDL_x11events.c | 8 ++++---- src/video/x11/SDL_x11video.h | 1 + src/video/x11/SDL_x11xinput2.c | 10 +++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index b82590071e..979ce0ea04 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1862,8 +1862,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) case ButtonPress: { - if (data->xinput2_mouse_enabled) { - // This input is being handled by XInput2 + if (data->xinput2_mouse_enabled && xevent->xbutton.serial == videodata->xinput_last_button_serial) { + // This input event was handled by XInput2. break; } @@ -1873,8 +1873,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) case ButtonRelease: { - if (data->xinput2_mouse_enabled) { - // This input is being handled by XInput2 + if (data->xinput2_mouse_enabled && xevent->xbutton.serial == videodata->xinput_last_button_serial) { + // This input event was handled by XInput2. break; } diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index 2dc4d3ab60..c6069b2624 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -138,6 +138,7 @@ struct SDL_VideoData Uint32 global_mouse_buttons; SDL_XInput2DeviceInfo *mouse_device_info; + unsigned long xinput_last_button_serial; int xinput_master_pointer_device; bool xinput_hierarchy_changed; diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index 51a18a66e5..21c0f5b15d 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -607,8 +607,16 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) SDL_WindowData *windowdata = X11_FindWindow(videodata, xev->event); int x_ticks = 0, y_ticks = 0; - // Slave pointer devices don't have button remapping applied automatically, so do it manually. + // Store the button serial to filter out redundant core button events. + videodata->xinput_last_button_serial = xev->serial; + if (xev->deviceid != videodata->xinput_master_pointer_device) { + // Ignore slave button events on non-focused windows, or focus can be incorrectly set while a grab is active. + if (SDL_GetMouseFocus() != windowdata->window) { + break; + } + + // Slave pointer devices don't have button remapping applied automatically, so do it manually. if (button <= xinput2_pointer_button_map_size) { button = xinput2_pointer_button_map[button - 1]; }