Fix XI_BadDevice crash on USB device disconnect (#9723)

When a USB HID device (keyboard/mouse) is disconnected, X11 fires an
XI_HierarchyChanged event, which triggers read_xi_scroll_devices().
That function calls XIGetProperty() on devices from XIQueryDevice().
There is a race condition: if a device is removed between these calls,
X11 generates an XI_BadDevice error. Without a custom error handler, the
default X11 handler calls exit(), killing kitty.

Fix: wrap the device query loop in read_xi_scroll_devices() with
_glfwGrabErrorHandlerX11() / _glfwReleaseErrorHandlerX11() so that any
XI_BadDevice error is captured by kitty's own handler rather than the
default fatal one.

Fixes #9723
Fixes #9724
This commit is contained in:
copilot-swe-agent[bot] 2026-03-22 02:09:42 +00:00 committed by Kovid Goyal
parent 07099b3a3d
commit 9b9bfeb02d
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C

6
glfw/x11_init.c vendored
View file

@ -156,8 +156,11 @@ read_xi_scroll_devices(void) {
if (!xi.available || xi.major < 2 || (xi.major == 2 && xi.minor < 1) || !xi.LIBINPUT_SCROLL_METHOD_ENABLED) return;
#undef xi
int deviceCount;
// Grab the error handler to prevent XI_BadDevice errors from killing kitty
// when a device is removed between XIQueryDevice and XIGetProperty calls.
_glfwGrabErrorHandlerX11();
XIDeviceInfo* devices = XIQueryDevice(_glfw.x11.display, XIAllDevices, &deviceCount);
if (!devices) return;
if (!devices) { _glfwReleaseErrorHandlerX11(); return; }
for (int i = 0; i < deviceCount; i++) {
XIDeviceInfo* device = &devices[i];
if (device->use == XIMasterPointer) _glfw.x11.xi.master_pointer_id = device->deviceid;
@ -251,6 +254,7 @@ read_xi_scroll_devices(void) {
}
}
XIFreeDeviceInfo(devices);
_glfwReleaseErrorHandlerX11();
}
// Look for and initialize supported X11 extensions