From bfc21fc7a4532caced902c397b1260fccd2ab235 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 03:11:05 +0000 Subject: [PATCH] X11: handle XI device hotplugging Fixes #9370 --- glfw/x11_init.c | 15 ++++++++++++++- glfw/x11_platform.h | 2 ++ glfw/x11_window.c | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 25e22f215..69de72413 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -148,7 +148,7 @@ static void detectEWMH(void) XFree(supportedAtoms); } -static void +void read_xi_scroll_devices(void) { #define xi _glfw.x11.xi xi.num_scroll_devices = 0; @@ -512,6 +512,19 @@ static bool initExtensions(void) _glfw.x11.xi.LIBINPUT_SCROLL_METHOD_ENABLED = XInternAtom(_glfw.x11.display, "libinput Scroll Method Enabled", False); read_xi_scroll_devices(); + // Select XI_HierarchyChanged events to detect device add/remove + if (_glfw.x11.xi.available) { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_HierarchyChanged)] = { 0 }; + + em.deviceid = XIAllDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_HierarchyChanged); + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); + } + // The compositing manager selection name contains the screen number { char name[32]; diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index 73c58ca58..d1a6e2172 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -498,3 +498,5 @@ void _glfwInputErrorX11(int error, const char* message); void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, bool bypass_cache); void _glfwPushSelectionToManagerX11(void); +void read_xi_scroll_devices(void); + diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 32d45597e..f5865cc12 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -58,7 +58,6 @@ #define _GLFW_XDND_VERSION 5 - // Wait for data to arrive using poll // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions @@ -1388,6 +1387,20 @@ static void processEvent(XEvent *event) } } } + // Handle XI_HierarchyChanged for device hotplug + else if (event->xcookie.evtype == XI_HierarchyChanged) + { + XIHierarchyEvent* he = (XIHierarchyEvent*)event->xcookie.data; + // Check if any devices were added or removed + for (int i = 0; i < he->num_info; i++) { + if (he->info[i].flags & (XISlaveAdded | XISlaveRemoved | + XIMasterAdded | XIMasterRemoved)) { + // Re-read scroll devices when devices are added or removed + read_xi_scroll_devices(); + break; + } + } + } XFreeEventData(_glfw.x11.display, &event->xcookie); } }