SDL: Use the real device IDs for the X11 master keyboard and pointer

From 476245564cf57d092ed7ec9ec559bf042b75a61e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 21 Mar 2024 11:53:10 -0700
Subject: [PATCH] Use the real device IDs for the X11 master keyboard and
 pointer

These always exist so they don't really help detect whether a keyboard/mouse is attached. Looking at the slave devices isn't that helpful either, as they're very permissive in what they might send.

For example, on my system with a single mouse and keyboard attached, xinput -list shows:
 Virtual core pointer                    	id=2	[master pointer  (3)]
   - Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
   - Generic USB Mouse                       	id=10	[slave  pointer  (2)]
   - Generic USB Consumer Control            	id=11	[slave  pointer  (2)]
   - KINESIS CORPORATION KB800HM Kinesis Freestyle2 for Mac	id=18	[slave  pointer  (2)]
   - Logitech Gaming Mouse G502 Consumer Control	id=15	[slave  pointer  (2)]
   - Logitech Gaming Mouse G502              	id=17	[slave  pointer  (2)]
 Virtual core keyboard                   	id=3	[master keyboard (2)]
   - Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
   - Power Button                            	id=6	[slave  keyboard (3)]
   - Power Button                            	id=7	[slave  keyboard (3)]
   - Sleep Button                            	id=8	[slave  keyboard (3)]
   - Generic USB                             	id=9	[slave  keyboard (3)]
   - Generic USB System Control              	id=12	[slave  keyboard (3)]
   - Generic USB Consumer Control            	id=19	[slave  keyboard (3)]
   - KINESIS CORPORATION KB800HM Kinesis Freestyle2 for Mac	id=20	[slave  keyboard (3)]
   - KINESIS CORPORATION KB800HM Kinesis Freestyle2 for Mac	id=21	[slave  keyboard (3)]
   - Logitech Gaming Mouse G502 Keyboard     	id=13	[slave  keyboard (3)]
   - Logitech Gaming Mouse G502 System Control	id=14	[slave  keyboard (3)]
   - Logitech Gaming Mouse G502 Consumer Control	id=16	[slave  keyboard (3)]
---
 src/video/x11/SDL_x11video.c   |  10 ++-
 src/video/x11/SDL_x11xinput2.c | 107 +++++++++++++++++++--------------
 src/video/x11/SDL_x11xinput2.h |   2 +-
 3 files changed, 72 insertions(+), 47 deletions(-)

diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index e62889f5b2893..2e6cce79a40f6 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -25,6 +25,8 @@
 #include <unistd.h> /* For getpid() and readlink() */
 
 #include "../../core/linux/SDL_system_theme.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "../../events/SDL_mouse_c.h"
 #include "../SDL_pixels_c.h"
 #include "../SDL_sysvideo.h"
 
@@ -415,7 +417,13 @@ int X11_VideoInit(SDL_VideoDevice *_this)
         return -1;
     }
 
-    X11_InitXinput2(_this);
+    if (!X11_InitXinput2(_this)) {
+        /* Assume a mouse and keyboard are attached */
+        data->keyboardID = SDL_GetNextObjectID();
+        SDL_AddKeyboard(data->keyboardID, SDL_FALSE);
+        data->mouseID = SDL_GetNextObjectID();
+        SDL_AddMouse(data->mouseID, SDL_FALSE);
+    }
 
 #ifdef SDL_VIDEO_DRIVER_X11_XFIXES
     X11_InitXfixes(_this);
diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c
index 2aa7025ad01ee..d742ecd783216 100644
--- a/src/video/x11/SDL_x11xinput2.c
+++ b/src/video/x11/SDL_x11xinput2.c
@@ -95,6 +95,55 @@ static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window
     return windowdata ? windowdata->window : NULL;
 }
 
+static void xinput2_init_device_list(SDL_VideoData *videodata)
+{
+    XIDeviceInfo *info;
+    int ndevices;
+
+    info = X11_XIQueryDevice(videodata->display, XIAllDevices, &ndevices);
+
+    for (int i = 0; i < ndevices; i++) {
+        XIDeviceInfo *dev = &info[i];
+
+        switch (dev->use) {
+        case XIMasterKeyboard:
+            videodata->keyboardID = (SDL_KeyboardID)dev->deviceid;
+            SDL_AddKeyboard(videodata->keyboardID, SDL_FALSE);
+            break;
+        case XIMasterPointer:
+            videodata->mouseID = (SDL_MouseID)dev->deviceid;
+            SDL_AddMouse(videodata->mouseID, SDL_FALSE);
+            break;
+        default:
+            break;
+        }
+
+#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
+        for (int j = 0; j < dev->num_classes; j++) {
+            SDL_TouchID touchId;
+            SDL_TouchDeviceType touchType;
+            XIAnyClassInfo *class = dev->classes[j];
+            XITouchClassInfo *t = (XITouchClassInfo *)class;
+
+            /* Only touch devices */
+            if (class->type != XITouchClass) {
+                continue;
+            }
+
+            if (t->mode == XIDependentTouch) {
+                touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
+            } else { /* XIDirectTouch */
+                touchType = SDL_TOUCH_DEVICE_DIRECT;
+            }
+
+            touchId = t->sourceid;
+            SDL_AddTouch(touchId, touchType, dev->name);
+        }
+#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
+    }
+    X11_XIFreeDeviceInfo(info);
+}
+
 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
 static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y)
 {
@@ -119,7 +168,7 @@ static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x,
 
 #endif /* SDL_VIDEO_DRIVER_X11_XINPUT2 */
 
-void X11_InitXinput2(SDL_VideoDevice *_this)
+SDL_bool X11_InitXinput2(SDL_VideoDevice *_this)
 {
 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
     SDL_VideoData *data = _this->driverdata;
@@ -140,13 +189,13 @@ void X11_InitXinput2(SDL_VideoDevice *_this)
      */
     if (!SDL_X11_HAVE_XINPUT2 ||
         !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
-        return; /* X server does not have XInput at all */
+        return SDL_FALSE; /* X server does not have XInput at all */
     }
 
     /* We need at least 2.2 for Multitouch, 2.0 otherwise. */
     version = query_xinput2_version(data->display, 2, 2);
     if (!xinput2_version_atleast(version, 2, 0)) {
-        return; /* X server does not support the version we want at all. */
+        return SDL_FALSE; /* X server does not support the version we want at all. */
     }
 
     xinput2_initialized = 1;
@@ -156,6 +205,8 @@ void X11_InitXinput2(SDL_VideoDevice *_this)
 #endif
 
     /* Enable raw motion events for this display */
+    SDL_zero(eventmask);
+    SDL_zeroa(mask);
     eventmask.deviceid = XIAllMasterDevices;
     eventmask.mask_len = sizeof(mask);
     eventmask.mask = mask;
@@ -173,9 +224,7 @@ void X11_InitXinput2(SDL_VideoDevice *_this)
     }
 #endif
 
-    if (X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1) != Success) {
-        return;
-    }
+    X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
 
     SDL_zero(eventmask);
     SDL_zeroa(mask);
@@ -184,9 +233,13 @@ void X11_InitXinput2(SDL_VideoDevice *_this)
     eventmask.mask = mask;
 
     XISetMask(mask, XI_HierarchyChanged);
-    if (X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1) != Success) {
-        return;
-    }
+    X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
+
+    xinput2_init_device_list(data);
+
+    return SDL_TRUE;
+#else
+    return SDL_FALSE;
 #endif
 }
 
@@ -512,42 +565,6 @@ int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
 
 void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
 {
-#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
-    SDL_VideoData *data = _this->driverdata;
-    XIDeviceInfo *info;
-    int ndevices, i, j;
-
-    if (!X11_Xinput2IsMultitouchSupported()) {
-        return;
-    }
-
-    info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
-
-    for (i = 0; i < ndevices; i++) {
-        XIDeviceInfo *dev = &info[i];
-        for (j = 0; j < dev->num_classes; j++) {
-            SDL_TouchID touchId;
-            SDL_TouchDeviceType touchType;
-            XIAnyClassInfo *class = dev->classes[j];
-            XITouchClassInfo *t = (XITouchClassInfo *)class;
-
-            /* Only touch devices */
-            if (class->type != XITouchClass) {
-                continue;
-            }
-
-            if (t->mode == XIDependentTouch) {
-                touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
-            } else { /* XIDirectTouch */
-                touchType = SDL_TOUCH_DEVICE_DIRECT;
-            }
-
-            touchId = t->sourceid;
-            SDL_AddTouch(touchId, touchType, dev->name);
-        }
-    }
-    X11_XIFreeDeviceInfo(info);
-#endif
 }
 
 void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window)
diff --git a/src/video/x11/SDL_x11xinput2.h b/src/video/x11/SDL_x11xinput2.h
index eba6cdf739f82..e67e1cb4c95a9 100644
--- a/src/video/x11/SDL_x11xinput2.h
+++ b/src/video/x11/SDL_x11xinput2.h
@@ -30,7 +30,7 @@ struct XGenericEventCookie;
 typedef struct XGenericEventCookie XGenericEventCookie;
 #endif
 
-extern void X11_InitXinput2(SDL_VideoDevice *_this);
+extern SDL_bool X11_InitXinput2(SDL_VideoDevice *_this);
 extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this);
 extern int X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie);
 extern int X11_Xinput2IsInitialized(void);