From 8368de2bc35056d466f0a536eadc353c69c0e6e3 Mon Sep 17 00:00:00 2001 From: Jehan Date: Fri, 17 May 2013 23:04:40 +0900 Subject: [PATCH] Bug 575767: fix crashes when XInput device disappears. Ignore X11 errors from querying state of unplugged input devices. GTK+ 3 handle this better with hotplugging support in XInput 2, but this is working workaround for avoiding ugly crashes and data loss with GTK+ 2. --- gdk/x11/gdkinput-x11.c | 38 +++++++++++++++++++++++++++++++++++++- gdk/x11/gdkinput-xfree.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/gdk/x11/gdkinput-x11.c b/gdk/x11/gdkinput-x11.c index 767b0702c4..83b5b280fc 100644 --- a/gdk/x11/gdkinput-x11.c +++ b/gdk/x11/gdkinput-x11.c @@ -51,6 +51,13 @@ static void gdk_input_update_axes (GdkDevicePrivate *gdkd static guint gdk_input_translate_state (guint state, guint device_state); +/* A temporary error handler for ignoring device unplugging-related errors. */ +static int +ignore_errors (Display *display, XErrorEvent *event) +{ + return True; +} + GdkDevicePrivate * _gdk_input_find_device (GdkDisplay *display, guint32 id) @@ -314,6 +321,7 @@ void _gdk_input_select_events (GdkWindow *impl_window, GdkDevicePrivate *gdkdev) { + int (*old_handler) (Display *, XErrorEvent *); XEventClass classes[GDK_MAX_DEVICE_CLASSES]; gint num_classes; guint event_mask; @@ -341,9 +349,22 @@ _gdk_input_select_events (GdkWindow *impl_window, _gdk_input_common_find_events (gdkdev, event_mask, classes, &num_classes); + + /* From X11 doc: + * "XSelectExtensionEvent can generate a BadWindow or BadClass error." + * In particular when a device is unplugged, a requested event class + * could no longer be valid and raise a BadClass, which would cause + * the program to crash. + * + * To handle this case gracefully, we simply ignore XSelectExtensionEvent() errors. + * This is OK since there is no events to report for the unplugged device anyway. + * So simply the device remains "silent". + */ + old_handler = XSetErrorHandler (ignore_errors); XSelectExtensionEvent (GDK_WINDOW_XDISPLAY (impl_window), GDK_WINDOW_XWINDOW (impl_window), classes, num_classes); + XSetErrorHandler (old_handler); } gint @@ -893,8 +914,9 @@ gdk_device_get_state (GdkDevice *device, } else { + int (*old_handler) (Display *, XErrorEvent *); GdkDevicePrivate *gdkdev; - XDeviceState *state; + XDeviceState *state = NULL; XInputClass *input_class; if (mask) @@ -902,8 +924,22 @@ gdk_device_get_state (GdkDevice *device, gdkdev = (GdkDevicePrivate *)device; + /* From X11 doc: "XQueryDeviceState can generate a BadDevice error." + * This would occur in particular when a device is unplugged, + * which would cause the program to crash (see bug 575767). + * + * To handle this case gracefully, we simply ignore the device. + * GTK+ 3 handles this better with XInput 2's hotplugging support; + * but this is better than a crash in GTK+ 2. + */ + old_handler = XSetErrorHandler (ignore_errors); state = XQueryDeviceState (GDK_WINDOW_XDISPLAY (window), gdkdev->xdevice); + XSetErrorHandler (old_handler); + + if (! state) + return; + input_class = state->data; for (i=0; inum_classes; i++) { diff --git a/gdk/x11/gdkinput-xfree.c b/gdk/x11/gdkinput-xfree.c index 5d87ccd21e..566a0c0f7a 100644 --- a/gdk/x11/gdkinput-xfree.c +++ b/gdk/x11/gdkinput-xfree.c @@ -76,6 +76,12 @@ gdk_device_set_mode (GdkDevice *device, return TRUE; } +static int +ignore_errors (Display *display, XErrorEvent *event) +{ + return True; +} + static void gdk_input_check_proximity (GdkDisplay *display) { @@ -91,10 +97,31 @@ gdk_input_check_proximity (GdkDisplay *display) && !GDK_IS_CORE (gdkdev) && gdkdev->xdevice) { - XDeviceState *state = XQueryDeviceState(display_impl->xdisplay, - gdkdev->xdevice); - XInputClass *xic; - int i; + int (*old_handler) (Display *, XErrorEvent *); + XDeviceState *state = NULL; + XInputClass *xic; + int i; + + /* From X11 doc: "XQueryDeviceState can generate a BadDevice error." + * This would occur in particular when a device is unplugged, + * which would cause the program to crash (see bug 575767). + * + * To handle this case gracefully, we simply ignore the device. + * GTK+ 3 handles this better with XInput 2's hotplugging support; + * but this is better than a crash in GTK+ 2. + */ + old_handler = XSetErrorHandler (ignore_errors); + state = XQueryDeviceState(display_impl->xdisplay, gdkdev->xdevice); + XSetErrorHandler (old_handler); + + if (! state) + { + /* Broken device. It may have been disconnected. + * Ignore it. + */ + tmp_list = tmp_list->next; + continue; + } xic = state->data; for (i=0; inum_classes; i++)