SDL: Disable XInput2 keyboard events

From c8489a3710fdf9ee3c5f09df0b3a3c439513a952 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 28 Mar 2024 08:50:47 -0700
Subject: [PATCH] Disable XInput2 keyboard events

It turns out they're only delivered to the window with mouse focus, not keyboard focus.

Fixes https://github.com/libsdl-org/SDL/issues/9374
---
 src/video/x11/SDL_x11events.c  |  8 ++++----
 src/video/x11/SDL_x11window.c  |  9 +++++++--
 src/video/x11/SDL_x11window.h  |  3 ++-
 src/video/x11/SDL_x11xinput2.c | 21 ++++++++++++++++-----
 4 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 246f565e3000a..4372132d4c1f2 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1528,7 +1528,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
     case KeyPress:
     case KeyRelease:
     {
-        if (data->using_xinput2) {
+        if (data->xinput2_keyboard_enabled) {
             // This input is being handled by XInput2
             break;
         }
@@ -1538,7 +1538,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
 
     case MotionNotify:
     {
-        if (data->using_xinput2) {
+        if (data->xinput2_mouse_enabled) {
             // This input is being handled by XInput2
             break;
         }
@@ -1556,7 +1556,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
 
     case ButtonPress:
     {
-        if (data->using_xinput2) {
+        if (data->xinput2_mouse_enabled) {
             // This input is being handled by XInput2
             break;
         }
@@ -1567,7 +1567,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
 
     case ButtonRelease:
     {
-        if (data->using_xinput2) {
+        if (data->xinput2_mouse_enabled) {
             // This input is being handled by XInput2
             break;
         }
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index f056abb2dfe38..8eb0487b00ed3 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -796,9 +796,14 @@ int X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesI
     {
         unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask;
         unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
-        if (X11_Xinput2SelectMouseAndKeyboard(_this, window)) {
-            /* If XInput2 can handle pointer and keyboard events, we don't track them here */
+
+        X11_Xinput2SelectMouseAndKeyboard(_this, window);
+
+        /* If XInput2 can handle pointer and keyboard events, we don't track them here */
+        if (windowdata->xinput2_keyboard_enabled) {
             x11_keyboard_events = 0;
+        }
+        if (windowdata->xinput2_mouse_enabled) {
             x11_pointer_events = 0;
         }
 
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 7f5c0c682d5ea..7c4da2cb35e1f 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -59,7 +59,8 @@ struct SDL_WindowData
     int border_right;
     int border_top;
     int border_bottom;
-    SDL_bool using_xinput2;
+    SDL_bool xinput2_mouse_enabled;
+    SDL_bool xinput2_keyboard_enabled;
     SDL_bool mouse_grabbed;
     Uint64 last_focus_event_time;
     PendingFocusEnum pending_focus;
diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c
index a80f4361bbcaf..e9ebc89f7dd90 100644
--- a/src/video/x11/SDL_x11xinput2.c
+++ b/src/video/x11/SDL_x11xinput2.c
@@ -607,27 +607,38 @@ SDL_bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *w
         eventmask.mask = mask;
         eventmask.deviceid = XIAllDevices;
 
+/* This is not enabled by default because these events are only delivered to the window with mouse focus, not keyboard focus */
+#ifdef USE_XINPUT2_KEYBOARD
         XISetMask(mask, XI_KeyPress);
         XISetMask(mask, XI_KeyRelease);
+        windowdata->xinput2_keyboard_enabled = SDL_TRUE;
+#endif
+
         XISetMask(mask, XI_ButtonPress);
         XISetMask(mask, XI_ButtonRelease);
         XISetMask(mask, XI_Motion);
+        windowdata->xinput2_mouse_enabled = SDL_TRUE;
+
         XISetMask(mask, XI_Enter);
         XISetMask(mask, XI_Leave);
+
         /* Hotplugging: */
         XISetMask(mask, XI_DeviceChanged);
         XISetMask(mask, XI_HierarchyChanged);
         XISetMask(mask, XI_PropertyEvent); /* E.g., when swapping tablet pens */
 
-        if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) == Success) {
-            windowdata->using_xinput2 = SDL_TRUE;
-        } else {
+        if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) != Success) {
             SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling\n");
-            windowdata->using_xinput2 = SDL_FALSE;
+            windowdata->xinput2_keyboard_enabled = SDL_FALSE;
+            windowdata->xinput2_mouse_enabled = SDL_FALSE;
         }
     }
 #endif
-    return windowdata->using_xinput2;
+
+    if (windowdata->xinput2_keyboard_enabled || windowdata->xinput2_mouse_enabled) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
 }
 
 int X11_Xinput2IsMultitouchSupported(void)