From 6f9205a3c3986a150b7991ce5a50f1517e139019 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 20 May 2026 11:01:40 -0400
Subject: [PATCH] x11: Reject click-through button events based on serial
XInput2 may send mouse buttons presses on both the master and slave devices, and the click-through button event should be ignored on both if required.
(cherry picked from commit 8371c09aa7d7afeffbcb782d8252b7959033c5f5)
---
src/video/x11/SDL_x11events.c | 12 +++++++-----
src/video/x11/SDL_x11events.h | 2 +-
src/video/x11/SDL_x11window.h | 1 +
src/video/x11/SDL_x11xinput2.c | 2 +-
4 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 979ce0ea04ede..dd44207f6294a 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1130,7 +1130,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
}
}
-void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time)
+void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time, unsigned long serial)
{
SDL_Window *window = windowdata->window;
int xticks = 0, yticks = 0;
@@ -1149,7 +1149,6 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S
if (X11_IsWheelEvent(button, &xticks, &yticks)) {
SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL);
} else {
- bool ignore_click = false;
if (button > 7) {
/* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ...
=> subtract (8-SDL_BUTTON_X1) to get value SDL expects */
@@ -1164,11 +1163,14 @@ void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, S
if (windowdata->last_focus_event_time) {
const int X11_FOCUS_CLICK_TIMEOUT = 10;
if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) {
- ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false);
+ if (!SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false)) {
+ // Ignore all press events with this serial.
+ windowdata->ignore_button_press_serial = serial;
+ }
}
windowdata->last_focus_event_time = 0;
}
- if (!ignore_click) {
+ if (serial != windowdata->ignore_button_press_serial) {
SDL_SendMouseButton(timestamp, window, mouseID, button, true);
}
}
@@ -1868,7 +1870,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
}
X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button,
- xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time);
+ xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time, xevent->xbutton.serial);
} break;
case ButtonRelease:
diff --git a/src/video/x11/SDL_x11events.h b/src/video/x11/SDL_x11events.h
index 8174f509cbc53..fbd0bace0a509 100644
--- a/src/video/x11/SDL_x11events.h
+++ b/src/video/x11/SDL_x11events.h
@@ -31,7 +31,7 @@ extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this);
extern void X11_GetBorderValues(SDL_WindowData *data);
extern Uint64 X11_GetEventTimestamp(unsigned long time);
extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent);
-extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time);
+extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time, unsigned long serial);
extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time);
extern SDL_WindowData *X11_FindWindow(SDL_VideoData *videodata, Window window);
extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result);
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 1ba725b9bd385..a9cc77c3b5307 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -63,6 +63,7 @@ struct SDL_WindowData
bool xinput2_keyboard_enabled;
bool mouse_grabbed;
Uint64 last_focus_event_time;
+ unsigned long ignore_button_press_serial;
PendingFocusEnum pending_focus;
Uint64 pending_focus_time;
bool pending_move;
diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c
index 86ca96b2b5f45..230d2d477c606 100644
--- a/src/video/x11/SDL_x11xinput2.c
+++ b/src/video/x11/SDL_x11xinput2.c
@@ -631,7 +631,7 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
if (down) {
X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button,
- (float)xev->event_x, (float)xev->event_y, xev->time);
+ (float)xev->event_x, (float)xev->event_y, xev->time, xev->serial);
} else {
X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time);
}