[gtk+/gtk-2-24] Bug 575767: fix crashes when XInput device disappears.



commit 8368de2bc35056d466f0a536eadc353c69c0e6e3
Author: Jehan <jehan girinstud io>
Date:   Fri May 17 23:04:40 2013 +0900

    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 767b070..83b5b28 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; i<state->num_classes; i++)
        {
diff --git a/gdk/x11/gdkinput-xfree.c b/gdk/x11/gdkinput-xfree.c
index 5d87ccd..566a0c0 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; i<state->num_classes; i++)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]