+/* FIXME: modifying the XEvent window as in root_key_filter() isn't
+ * going to work with XGE/XI2, since the actual event to handle would
+ * be allocated/freed before GDK gets to translate the event.
+ * Active grabs on the keyboard are used instead at the moment...
+ */
+#if defined (GDK_WINDOWING_X11) && !defined (XINPUT_2)
+
+/*
+ * We want to handle a handful of keys during DND, e.g. Escape to abort.
+ * Grabbing the keyboard has the unfortunate side-effect of preventing
+ * useful things such as using Alt-Tab to cycle between windows or
+ * switching workspaces. Therefore, we just grab the few keys we are
+ * interested in. Note that we need to put the grabs on the root window
+ * in order for them to still work when the focus is moved to another
+ * app/workspace.
+ *
+ * GDK needs a little help to successfully deliver root key events...
+ */
+
+static GdkFilterReturn
+root_key_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *ev = (XEvent *) xevent;
+
+ if ((ev->type == KeyPress || ev->type == KeyRelease) &&
+ ev->xkey.root == ev->xkey.window)
+ ev->xkey.window = (Window)data;
+
+ return GDK_FILTER_CONTINUE;
+}
+
+typedef struct {
+ gint keysym;
+ gint modifiers;
+} GrabKey;
+
+static GrabKey grab_keys[] = {
+ { XK_Escape, 0 },
+ { XK_space, 0 },
+ { XK_KP_Space, 0 },
+ { XK_Return, 0 },
+ { XK_KP_Enter, 0 },
+ { XK_Up, 0 },
+ { XK_Up, Mod1Mask },
+ { XK_Down, 0 },
+ { XK_Down, Mod1Mask },
+ { XK_Left, 0 },
+ { XK_Left, Mod1Mask },
+ { XK_Right, 0 },
+ { XK_Right, Mod1Mask },
+ { XK_KP_Up, 0 },
+ { XK_KP_Up, Mod1Mask },
+ { XK_KP_Down, 0 },
+ { XK_KP_Down, Mod1Mask },
+ { XK_KP_Left, 0 },
+ { XK_KP_Left, Mod1Mask },
+ { XK_KP_Right, 0 },
+ { XK_KP_Right, Mod1Mask }
+};
+
+static void
+grab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
+ guint32 time)
+{
+ guint i;
+ GdkWindow *window, *root;
+ gint keycode;
+
+ window = gtk_widget_get_window (widget);
+ root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
+ XGrabKey (GDK_WINDOW_XDISPLAY (window),
+ keycode, grab_keys[i].modifiers,
+ GDK_WINDOW_XID (root),
+ FALSE,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+
+ gdk_flush ();
+ gdk_error_trap_pop ();
+
+ gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
+}
+
+static void
+ungrab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
+ guint32 time)
+{
+ guint i;
+ GdkWindow *window, *root;
+ gint keycode;
+
+ window = gtk_widget_get_window (widget);
+ root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+ gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
+ XUngrabKey (GDK_WINDOW_XDISPLAY (window),
+ keycode, grab_keys[i].modifiers,
+ GDK_WINDOW_XID (root));
+ }
+
+ gdk_flush ();
+ gdk_error_trap_pop ();
+}
+
+#else /* GDK_WINDOWING_X11 && !XINPUT_2 */
+
+static void
+grab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
+ guint32 time)
+{
+ gdk_device_grab (device,
+ gtk_widget_get_window (widget),
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL, time);
+}
+
+static void
+ungrab_dnd_keys (GtkWidget *widget,
+ GdkDevice *device,
+ guint32 time)
+{
+ gdk_device_ungrab (device, time);
+}
+
+#endif /* GDK_WINDOWING_X11 */
+