[mutter/wip/multitouch: 31/73] core: Keep track of per pointer/keyboard grabs



commit 7c86cb85ba00e3ff59789f2263c9316c39c75a77
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sun Jun 19 00:55:10 2011 +0200

    core: Keep track of per pointer/keyboard grabs
    
    There is now a MetaGrabInfo struct, holding all information about
    an ongoing grab, there were unfortunately no means of making this
    commit smaller, as things are too intertwined to separate it,
    
    On places where it was most obvious, the correct device has been
    used (in reaction to events, for example), in other parts, the
    private API has been extended to include the MetaDevice (or device
    ID for core.h functions), in other places, the virtual core
    pointer/keyboard are used out of cluelessness.

 src/compositor/compositor.c   |   35 ++-
 src/core/constraints.c        |    5 +-
 src/core/core.c               |   84 ++++---
 src/core/core.h               |   12 +-
 src/core/device-map-private.h |    6 +
 src/core/devices-core.c       |    1 +
 src/core/devices-core.h       |    5 -
 src/core/display-private.h    |   94 +++++---
 src/core/display.c            |  524 +++++++++++++++++++++++++----------------
 src/core/edge-resistance.c    |  177 ++++++++------
 src/core/edge-resistance.h    |   39 ++--
 src/core/frame.c              |    3 +-
 src/core/keybindings.c        |  183 ++++++++++-----
 src/core/screen.c             |   12 +-
 src/core/window-private.h     |   10 +-
 src/core/window.c             |  445 +++++++++++++++++++++++------------
 src/core/workspace.c          |   28 ++-
 src/meta/display.h            |    5 +
 src/ui/frames.c               |  109 +++++----
 src/ui/frames.h               |    3 +-
 src/ui/menu.c                 |    4 +-
 src/ui/tile-preview.c         |    2 +
 src/ui/tile-preview.h         |    2 +
 23 files changed, 1122 insertions(+), 666 deletions(-)
---
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 04bca45..60343e8 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -347,8 +347,17 @@ meta_begin_modal_for_plugin (MetaScreen       *screen,
   gboolean pointer_grabbed = FALSE;
   gboolean keyboard_grabbed = FALSE;
   int result;
+  MetaDevice     *device;
+  MetaGrabInfo   *grab_info;
 
-  if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
+  /* FIXME: need a real device here, and probably
+   * some exclusion mode for other devices */
+  device = meta_device_map_lookup (display->device_map,
+                                   META_CORE_POINTER_ID);
+
+  grab_info = meta_display_get_grab_info (display, device);
+
+  if (compositor->modal_plugin != NULL || grab_info != NULL)
     return FALSE;
 
   if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
@@ -380,11 +389,13 @@ meta_begin_modal_for_plugin (MetaScreen       *screen,
       keyboard_grabbed = TRUE;
     }
 
-  display->grab_op = META_GRAB_OP_COMPOSITOR;
-  display->grab_window = NULL;
-  display->grab_screen = screen;
-  display->grab_have_pointer = TRUE;
-  display->grab_have_keyboard = TRUE;
+  grab_info = meta_display_create_grab_info (display, device);
+
+  grab_info->grab_op = META_GRAB_OP_COMPOSITOR;
+  grab_info->grab_window = NULL;
+  grab_info->grab_screen = screen;
+  grab_info->grab_have_pointer = TRUE;
+  grab_info->grab_have_keyboard = TRUE;
 
   compositor->modal_plugin = plugin;
 
@@ -407,18 +418,18 @@ meta_end_modal_for_plugin (MetaScreen     *screen,
   MetaDisplay    *display    = meta_screen_get_display (screen);
   Display        *xdpy = meta_display_get_xdisplay (display);
   MetaCompositor *compositor = display->compositor;
+  MetaDevice     *device;
 
   g_return_if_fail (compositor->modal_plugin == plugin);
 
+  /* FIXME: need a real device here */
+  device = meta_device_map_lookup (display->device_map,
+                                   META_CORE_POINTER_ID);
+
   XUngrabPointer (xdpy, timestamp);
   XUngrabKeyboard (xdpy, timestamp);
 
-  display->grab_op = META_GRAB_OP_NONE;
-  display->grab_window = NULL;
-  display->grab_screen = NULL;
-  display->grab_have_pointer = FALSE;
-  display->grab_have_keyboard = FALSE;
-
+  meta_display_remove_grab_info (display, device);
   compositor->modal_plugin = NULL;
 }
 
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 6ceb336..9d0c7d6 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -1363,15 +1363,18 @@ constrain_titlebar_visible (MetaWindow         *window,
   int bottom_amount;
   int horiz_amount_offscreen, vert_amount_offscreen;
   int horiz_amount_onscreen,  vert_amount_onscreen;
+  MetaGrabInfo *grab_info;
 
   if (priority > PRIORITY_TITLEBAR_VISIBLE)
     return TRUE;
 
+  grab_info = window->cur_grab;
+
   /* Allow the titlebar beyond the top of the screen only if the user wasn't
    * clicking on the frame to start the move.
    */
   unconstrained_user_action =
-    info->is_user_action && !window->display->grab_frame_action;
+    info->is_user_action && (!grab_info || !grab_info->grab_frame_action);
 
   /* Exit early if we know the constraint won't apply--note that this constraint
    * is only meant for normal windows (e.g. we don't want docks to be shoved 
diff --git a/src/core/core.c b/src/core/core.c
index 002e6ea..0607525 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -268,16 +268,21 @@ meta_core_user_lower_and_unfocus (Display *xdisplay,
 void
 meta_core_lower_beneath_grab_window (Display *xdisplay,
                                      Window   xwindow,
+                                     int      device_id,
                                      guint32  timestamp)
 {
   XWindowChanges changes;
   MetaDisplay *display;
   MetaScreen *screen;
   MetaWindow *grab_window;
+  MetaDevice *pointer;
+  MetaGrabInfo *grab_info;
 
   display = meta_display_for_x_display (xdisplay);
   screen = meta_display_screen_for_xwindow (display, xwindow);
-  grab_window = display->grab_window;
+  pointer = meta_device_map_lookup (display->device_map, device_id);
+  grab_info = meta_display_get_grab_info (display, pointer);
+  grab_window = grab_info->grab_window;
 
   if (grab_window == NULL)
     return;
@@ -643,6 +648,7 @@ meta_core_get_workspace_name_with_index (Display *xdisplay,
 gboolean
 meta_core_begin_grab_op (Display    *xdisplay,
                          Window      frame_xwindow,
+                         int         device_id,
                          MetaGrabOp  op,
                          gboolean    pointer_already_grabbed,
                          gboolean    frame_action,
@@ -655,13 +661,16 @@ meta_core_begin_grab_op (Display    *xdisplay,
   MetaWindow *window = get_window (xdisplay, frame_xwindow);
   MetaDisplay *display;
   MetaScreen *screen;
-  
+  MetaDevice *device;
+
   display = meta_display_for_x_display (xdisplay);
   screen = meta_display_screen_for_xwindow (display, frame_xwindow);
 
   g_assert (screen != NULL);
-  
-  return meta_display_begin_grab_op (display, screen, window,
+
+  device = meta_device_map_lookup (display->device_map, device_id);
+
+  return meta_display_begin_grab_op (display, screen, window, device,
                                      op, pointer_already_grabbed,
                                      frame_action,
                                      button, modmask,
@@ -670,57 +679,58 @@ meta_core_begin_grab_op (Display    *xdisplay,
 
 void
 meta_core_end_grab_op (Display *xdisplay,
+                       int      device_id,
                        guint32  timestamp)
 {
   MetaDisplay *display;
-  
+  MetaDevice *device;
+
   display = meta_display_for_x_display (xdisplay);
+  device = meta_device_map_lookup (display->device_map, device_id);
 
-  meta_display_end_grab_op (display, timestamp);
+  meta_display_end_grab_op (display, device, timestamp);
 }
 
 MetaGrabOp
-meta_core_get_grab_op (Display *xdisplay)
+meta_core_frame_has_grab (Display    *xdisplay,
+                          Window      frame_xwindow,
+                          gint       *device_id,
+                          gint       *button_ret)
 {
-  MetaDisplay *display;
-  
-  display = meta_display_for_x_display (xdisplay);
+  MetaWindow *window;
 
-  return display->grab_op;
-}
+  window = get_window (xdisplay, frame_xwindow);
 
-Window
-meta_core_get_grab_frame (Display *xdisplay)
-{
-  MetaDisplay *display;
-  
-  display = meta_display_for_x_display (xdisplay);
+  if (window != NULL &&
+      window->cur_grab != NULL)
+    {
+      if (button_ret)
+        *button_ret = window->cur_grab->grab_button;
 
-  g_assert (display != NULL);
-  g_assert (display->grab_op == META_GRAB_OP_NONE || 
-            display->grab_screen != NULL);
-  g_assert (display->grab_op == META_GRAB_OP_NONE ||
-            display->grab_screen->display->xdisplay == xdisplay);
-  
-  if (display->grab_op != META_GRAB_OP_NONE &&
-      display->grab_window &&
-      display->grab_window->frame)
-    return display->grab_window->frame->xwindow;
-  else
-    return None;
+      if (device_id)
+        *device_id = meta_device_get_id (window->cur_grab->grab_pointer);
+
+      return window->cur_grab->grab_op;
+    }
+
+  return META_GRAB_OP_NONE;
 }
 
-int
-meta_core_get_grab_button (Display  *xdisplay)
+Window
+meta_core_get_frame (Display *xdisplay,
+                     Window   client_xwindow)
 {
   MetaDisplay *display;
-  
+  MetaWindow *window;
+
   display = meta_display_for_x_display (xdisplay);
+  window = meta_display_lookup_x_window (display, client_xwindow);
 
-  if (display->grab_op == META_GRAB_OP_NONE)
-    return -1;
-  
-  return display->grab_button;
+  if (window &&
+      window->frame)
+    return window->frame->xwindow;
+
+  return None;
 }
 
 void
diff --git a/src/core/core.h b/src/core/core.h
index 95f8f46..ba26967 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -118,6 +118,7 @@ void meta_core_user_focus   (Display *xdisplay,
 
 void meta_core_lower_beneath_grab_window (Display *xdisplay,
                                           Window   xwindow,
+                                          int      device_id,
                                           guint32  timestamp);
 
 void meta_core_minimize         (Display *xdisplay,
@@ -175,6 +176,7 @@ void meta_core_get_menu_accelerator (MetaMenuOp           menu_op,
 
 gboolean   meta_core_begin_grab_op (Display    *xdisplay,
                                     Window      frame_xwindow,
+                                    int         device_id,
                                     MetaGrabOp  op,
                                     gboolean    pointer_already_grabbed,
                                     gboolean    frame_action,
@@ -184,11 +186,15 @@ gboolean   meta_core_begin_grab_op (Display    *xdisplay,
                                     int         root_x,
                                     int         root_y);
 void       meta_core_end_grab_op   (Display    *xdisplay,
+                                    int         device_id,
                                     guint32     timestamp);
-MetaGrabOp meta_core_get_grab_op     (Display    *xdisplay);
-Window     meta_core_get_grab_frame  (Display   *xdisplay);
-int        meta_core_get_grab_button (Display  *xdisplay);
 
+MetaGrabOp meta_core_frame_has_grab  (Display    *xdisplay,
+                                      Window      frame_xwindow,
+                                      gint       *device_id,
+                                      gint       *button_ret);
+Window     meta_core_get_frame       (Display    *xdisplay,
+                                      Window      client_xwindow);
 
 void       meta_core_grab_buttons  (Display *xdisplay,
                                     Window   frame_xwindow);
diff --git a/src/core/device-map-private.h b/src/core/device-map-private.h
index 28e75f2..00a2fcb 100644
--- a/src/core/device-map-private.h
+++ b/src/core/device-map-private.h
@@ -35,6 +35,12 @@
 #include "display-private.h"
 #include "device-private.h"
 
+/* Device IDs for Virtual Core Pointer/Keyboard,
+ * use only in case of emergency.
+ */
+#define META_CORE_POINTER_ID  2
+#define META_CORE_KEYBOARD_ID 3
+
 struct _MetaDeviceMap
 {
   GObject parent_instance;
diff --git a/src/core/devices-core.c b/src/core/devices-core.c
index 755d40c..d4ad7cf 100644
--- a/src/core/devices-core.c
+++ b/src/core/devices-core.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #include "screen-private.h"
 #include "devices-core.h"
+#include "device-map-private.h"
 
 /* Common functions */
 static void
diff --git a/src/core/devices-core.h b/src/core/devices-core.h
index bdb938f..7b7e89f 100644
--- a/src/core/devices-core.h
+++ b/src/core/devices-core.h
@@ -32,11 +32,6 @@
 #include "device-pointer.h"
 #include "device-keyboard.h"
 
-/* These IDs are the same than those of the
- * VCP/VCK in XInput2, to keep consistency */
-#define META_CORE_POINTER_ID  2
-#define META_CORE_KEYBOARD_ID 3
-
 /* Pointer */
 #define META_TYPE_DEVICE_POINTER_CORE            (meta_device_pointer_core_get_type ())
 #define META_DEVICE_POINTER_CORE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_DEVICE_POINTER_CORE, MetaDevicePointerCore))
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 88992e2..393a319 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -57,6 +57,8 @@ typedef struct _MetaWindowPropHooks MetaWindowPropHooks;
 
 typedef struct MetaEdgeResistanceData MetaEdgeResistanceData;
 
+typedef struct _MetaGrabInfo MetaGrabInfo;
+
 typedef void (* MetaWindowPingFunc) (MetaDisplay *display,
 				     Window       xwindow,
 				     guint32      timestamp,
@@ -87,6 +89,46 @@ typedef enum {
   META_TILE_MAXIMIZED
 } MetaTileMode;
 
+struct _MetaGrabInfo
+{
+  MetaDevice *grab_pointer;
+  MetaDevice *grab_keyboard;
+
+  MetaGrabOp  grab_op;
+  MetaScreen *grab_screen;
+  MetaWindow *grab_window;
+  Window      grab_xwindow;
+  int         grab_button;
+  int         grab_anchor_root_x;
+  int         grab_anchor_root_y;
+  MetaRectangle grab_anchor_window_pos;
+  MetaTileMode  grab_tile_mode;
+  int           grab_tile_monitor_number;
+  int         grab_latest_motion_x;
+  int         grab_latest_motion_y;
+  gulong      grab_mask;
+  guint       grab_have_pointer : 1;
+  guint       grab_have_keyboard : 1;
+  guint       grab_frame_action : 1;
+  /* During a resize operation, the directions in which we've broken
+   * out of the initial maximization state */
+  guint       grab_resize_unmaximize : 2; /* MetaMaximizeFlags */
+  MetaRectangle grab_initial_window_pos;
+  int         grab_initial_x, grab_initial_y;  /* These are only relevant for */
+  gboolean    grab_threshold_movement_reached; /* raise_on_click == FALSE.    */
+  MetaResizePopup *grab_resize_popup;
+  GTimeVal    grab_last_moveresize_time;
+  guint32     grab_motion_notify_time;
+  GList*      grab_old_window_stacking;
+  unsigned int grab_last_user_action_was_snap;
+
+#ifdef HAVE_XSYNC
+  /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
+  XSyncAlarm  grab_sync_request_alarm;
+#endif
+  int	      grab_resize_timeout_id;
+};
+
 struct _MetaDisplay
 {
   GObject parent_instance;
@@ -180,35 +222,11 @@ struct _MetaDisplay
   /* Alt+click button grabs */
   unsigned int window_grab_modifiers;
   
-  /* current window operation */
-  MetaGrabOp  grab_op;
-  MetaScreen *grab_screen;
-  MetaWindow *grab_window;
-  Window      grab_xwindow;
-  int         grab_button;
-  int         grab_anchor_root_x;
-  int         grab_anchor_root_y;
-  MetaRectangle grab_anchor_window_pos;
-  MetaTileMode  grab_tile_mode;
-  int           grab_tile_monitor_number;
-  int         grab_latest_motion_x;
-  int         grab_latest_motion_y;
-  gulong      grab_mask;
-  guint       grab_have_pointer : 1;
-  guint       grab_have_keyboard : 1;
-  guint       grab_frame_action : 1;
-  /* During a resize operation, the directions in which we've broken
-   * out of the initial maximization state */
-  guint       grab_resize_unmaximize : 2; /* MetaMaximizeFlags */
-  MetaRectangle grab_initial_window_pos;
-  int         grab_initial_x, grab_initial_y;  /* These are only relevant for */
-  gboolean    grab_threshold_movement_reached; /* raise_on_click == FALSE.    */
-  MetaResizePopup *grab_resize_popup;
-  GTimeVal    grab_last_moveresize_time;
-  guint32     grab_motion_notify_time;
-  GList*      grab_old_window_stacking;
-  MetaEdgeResistanceData *grab_edge_resistance_data;
-  unsigned int grab_last_user_action_was_snap;
+  /* per-device current window operation */
+  GHashTable *current_grabs;
+
+  /* per-screen edge resistance cache */
+  GHashTable *edge_resistance_info;
 
   /* we use property updates as sentinels for certain window focus events
    * to avoid some race conditions on EnterNotify events
@@ -219,11 +237,6 @@ struct _MetaDisplay
   int         xkb_base_event_type;
   guint32     last_bell_time;
 #endif
-#ifdef HAVE_XSYNC
-  /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
-  XSyncAlarm  grab_sync_request_alarm;
-#endif
-  int	      grab_resize_timeout_id;
 
   /* Keybindings stuff */
   MetaKeyBinding *key_bindings;
@@ -387,12 +400,14 @@ Cursor         meta_display_create_x_cursor (MetaDisplay *display,
 
 void     meta_display_set_grab_op_cursor (MetaDisplay *display,
                                           MetaScreen  *screen,
+                                          MetaDevice  *device,
                                           MetaGrabOp   op,
                                           gboolean     change_pointer,
                                           Window       grab_xwindow,
                                           guint32      timestamp);
 
 void    meta_display_check_threshold_reached (MetaDisplay *display,
+                                              MetaDevice  *device,
                                               int          x,
                                               int          y);
 void     meta_display_grab_window_buttons    (MetaDisplay *display,
@@ -406,7 +421,8 @@ void meta_display_ungrab_focus_window_button (MetaDisplay *display,
                                               MetaWindow  *window);
 
 /* Next function is defined in edge-resistance.c */
-void meta_display_cleanup_edges              (MetaDisplay *display);
+void meta_display_cleanup_edges              (MetaDisplay *display,
+                                              MetaScreen  *screen);
 
 /* make a request to ensure the event serial has changed */
 void     meta_display_increment_event_serial (MetaDisplay *display);
@@ -454,4 +470,12 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
 /* In above-tab-keycode.c */
 guint meta_display_get_above_tab_keycode (MetaDisplay *display);
 
+MetaGrabInfo * meta_display_create_grab_info         (MetaDisplay  *display,
+                                                      MetaDevice   *device);
+void           meta_display_remove_grab_info         (MetaDisplay  *display,
+                                                      MetaDevice   *device);
+
+MetaGrabInfo * meta_display_get_grab_info            (MetaDisplay  *display,
+                                                      MetaDevice   *device);
+
 #endif
diff --git a/src/core/display.c b/src/core/display.c
index 294e531..d9661e2 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -48,6 +48,7 @@
 #include "xprops.h"
 #include "workspace-private.h"
 #include "bell.h"
+#include "device-keyboard.h"
 #include "device-private.h"
 #include "input-events.h"
 #include <meta/compositor.h>
@@ -469,16 +470,11 @@ meta_display_open (void)
   the_display->autoraise_window = NULL;
   the_display->focus_window = NULL;
   the_display->expected_focus_window = NULL;
-  the_display->grab_old_window_stacking = NULL;
 
   the_display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
   the_display->allow_terminal_deactivation = TRUE; /* Only relevant for when a
                                                   terminal has the focus */
 
-#ifdef HAVE_XSYNC
-  the_display->grab_sync_request_alarm = None;
-#endif
-  
   /* FIXME copy the checks from GDK probably */
   the_display->static_gravity_works = g_getenv ("MUTTER_USE_STATIC_GRAVITY") != NULL;
   
@@ -549,21 +545,13 @@ meta_display_open (void)
   the_display->current_time = CurrentTime;
   the_display->sentinel_counter = 0;
 
-  the_display->grab_resize_timeout_id = 0;
-  the_display->grab_have_keyboard = FALSE;
-  
 #ifdef HAVE_XKB  
   the_display->last_bell_time = 0;
 #endif
 
-  the_display->grab_op = META_GRAB_OP_NONE;
-  the_display->grab_window = NULL;
-  the_display->grab_screen = NULL;
-  the_display->grab_resize_popup = NULL;
-  the_display->grab_tile_mode = META_TILE_NONE;
-  the_display->grab_tile_monitor_number = -1;
-
-  the_display->grab_edge_resistance_data = NULL;
+  the_display->current_grabs = g_hash_table_new_full (NULL, NULL, NULL,
+                                                      (GDestroyNotify) g_free);
+  the_display->edge_resistance_info = g_hash_table_new (NULL, NULL);
 
 #ifdef HAVE_XSYNC
   {
@@ -995,9 +983,6 @@ meta_display_close (MetaDisplay *display,
   
   meta_display_remove_autoraise_callback (display);
 
-  if (display->grab_old_window_stacking)
-    g_list_free (display->grab_old_window_stacking);
-  
   /* Stop caring about events */
   meta_ui_remove_event_func (display->xdisplay,
                              event_callback,
@@ -1594,6 +1579,7 @@ event_callback (XEvent   *event,
   MetaWindow *window;
   MetaWindow *property_for_window;
   MetaDisplay *display;
+  MetaGrabInfo *grab_info;
   Window modified;
   gboolean frame_was_receiver;
   gboolean bypass_compositor;
@@ -1678,15 +1664,30 @@ event_callback (XEvent   *event,
 
 #ifdef HAVE_XSYNC
   if (META_DISPLAY_HAS_XSYNC (display) && 
-      event->type == (display->xsync_event_base + XSyncAlarmNotify) &&
-      ((XSyncAlarmNotifyEvent*)event)->alarm == display->grab_sync_request_alarm)
+      event->type == (display->xsync_event_base + XSyncAlarmNotify))
     {
-      filter_out_event = TRUE; /* GTK doesn't want to see this really */
-      
-      if (display->grab_op != META_GRAB_OP_NONE &&
-          display->grab_window != NULL &&
-          grab_op_is_mouse (display->grab_op))
-	meta_window_handle_mouse_grab_op_event (display->grab_window, event);
+      GHashTableIter iter;
+      gpointer key, value;
+
+      g_hash_table_iter_init (&iter, display->current_grabs);
+
+      /* Each ongoing grab has its own sync alarm */
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          grab_info = value;
+
+          if (((XSyncAlarmNotifyEvent*)event)->alarm != grab_info->grab_sync_request_alarm)
+            continue;
+
+          filter_out_event = TRUE; /* GTK doesn't want to see this really */
+
+          if (grab_info->grab_op != META_GRAB_OP_NONE &&
+              grab_info->grab_window != NULL &&
+              grab_op_is_mouse (grab_info->grab_op))
+            meta_window_handle_mouse_grab_op_event (grab_info->grab_window,
+                                                    event);
+          break;
+        }
     }
 #endif /* HAVE_XSYNC */
 
@@ -1778,12 +1779,13 @@ event_callback (XEvent   *event,
         }
 
       device = meta_input_event_get_device (display, event);
+      grab_info = meta_display_get_grab_info (display, device);
 
       switch (evtype)
         {
         case KeyPress:
         case KeyRelease:
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
           /* For key events, it's important to enforce single-handling, or
@@ -1803,26 +1805,27 @@ event_callback (XEvent   *event,
                                             &ev_root_x,
                                             &ev_root_y);
 
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
           if (n_button == 4 || n_button == 5)
             /* Scrollwheel event, do nothing and deliver event to compositor below */
             break;
 
-          if ((window &&
-               grab_op_is_mouse (display->grab_op) &&
-               display->grab_button != (int) n_button &&
-               display->grab_window == window) ||
-              grab_op_is_keyboard (display->grab_op))
+          if (grab_info &&
+              ((window &&
+                grab_op_is_mouse (grab_info->grab_op) &&
+                grab_info->grab_button != (int) n_button &&
+                grab_info->grab_window == window) ||
+               grab_op_is_keyboard (grab_info->grab_op)))
             {
               meta_topic (META_DEBUG_WINDOW_OPS,
                           "Ending grab op %u on window %s due to button press\n",
-                          display->grab_op,
-                          (display->grab_window ?
-                           display->grab_window->desc :
+                          grab_info->grab_op,
+                          (grab_info->grab_window ?
+                           grab_info->grab_window->desc :
                            "none"));
-              if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
+              if (GRAB_OP_IS_WINDOW_SWITCH (grab_info->grab_op))
                 {
                   MetaScreen *screen;
                   meta_topic (META_DEBUG_WINDOW_OPS,
@@ -1830,13 +1833,16 @@ event_callback (XEvent   *event,
                   screen =
                     meta_display_screen_for_root (display, xwindow);
 
+                  /* FIXME: won't work well with multiple
+                   * devices messing with stacking
+                   */
                   if (screen!=NULL)
                     meta_stack_set_positions (screen->stack,
-                                              display->grab_old_window_stacking);
+                                              grab_info->grab_old_window_stacking);
                 }
-              meta_display_end_grab_op (display, evtime);
+              meta_display_end_grab_op (display, device, evtime);
             }
-          else if (window && display->grab_op == META_GRAB_OP_NONE)
+          else if (window && !grab_info)
             {
               gboolean begin_move = FALSE;
               unsigned int grab_mask;
@@ -1929,6 +1935,7 @@ event_callback (XEvent   *event,
                         meta_display_begin_grab_op (display,
                                                     window->screen,
                                                     window,
+                                                    device,
                                                     op,
                                                     TRUE,
                                                     FALSE,
@@ -1985,6 +1992,7 @@ event_callback (XEvent   *event,
                   meta_display_begin_grab_op (display,
                                               window->screen,
                                               window,
+                                              device,
                                               META_GRAB_OP_MOVING,
                                               TRUE,
                                               FALSE,
@@ -1997,27 +2005,30 @@ event_callback (XEvent   *event,
             }
           break;
         case ButtonRelease:
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
-          if (display->grab_window == window &&
-              grab_op_is_mouse (display->grab_op))
+          if (grab_info &&
+              grab_info->grab_window == window &&
+              grab_op_is_mouse (grab_info->grab_op))
             meta_window_handle_mouse_grab_op_event (window, event);
           break;
         case MotionNotify:
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
-          if (display->grab_window == window &&
-              grab_op_is_mouse (display->grab_op))
+          if (grab_info &&
+              grab_info->grab_window == window &&
+              grab_op_is_mouse (grab_info->grab_op))
             meta_window_handle_mouse_grab_op_event (window, event);
           break;
         case EnterNotify:
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
-          if (display->grab_window == window &&
-              grab_op_is_mouse (display->grab_op))
+          if (grab_info &&
+              grab_info->grab_window == window &&
+              grab_op_is_mouse (grab_info->grab_op))
             {
               meta_window_handle_mouse_grab_op_event (window, event);
               break;
@@ -2110,11 +2121,12 @@ event_callback (XEvent   *event,
             }
           break;
         case LeaveNotify:
-          if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+          if (grab_info && grab_info->grab_op == META_GRAB_OP_COMPOSITOR)
             break;
 
-          if (display->grab_window == window &&
-              grab_op_is_mouse (display->grab_op))
+          if (grab_info &&
+              grab_info->grab_window == window &&
+              grab_op_is_mouse (grab_info->grab_op))
             meta_window_handle_mouse_grab_op_event (window, event);
           else if (window != NULL)
             {
@@ -2235,11 +2247,15 @@ event_callback (XEvent   *event,
                * will change one day?
                */
               guint32 timestamp;
+
               timestamp = meta_display_get_current_time_roundtrip (display);
+              grab_info = window->cur_grab;
 
-              if (display->grab_op != META_GRAB_OP_NONE &&
-                  display->grab_window == window)
-                meta_display_end_grab_op (display, timestamp);
+              if (grab_info &&
+                  grab_info->grab_op != META_GRAB_OP_NONE)
+                meta_display_end_grab_op (display,
+                                          grab_info->grab_pointer,
+                                          timestamp);
 
               if (frame_was_receiver)
                 {
@@ -2265,12 +2281,16 @@ event_callback (XEvent   *event,
                * will change one day?
                */
               guint32 timestamp;
+
               timestamp = meta_display_get_current_time_roundtrip (display);
+              grab_info = window->cur_grab;
 
-              if (display->grab_op != META_GRAB_OP_NONE &&
-                  display->grab_window == window &&
+              if (grab_info &&
+                  grab_info->grab_op != META_GRAB_OP_NONE &&
                   ((window->frame == NULL) || !window->frame->mapped))
-                meta_display_end_grab_op (display, timestamp);
+                meta_display_end_grab_op (display,
+                                          grab_info->grab_pointer,
+                                          timestamp);
 
               if (!frame_was_receiver)
                 {
@@ -3451,14 +3471,17 @@ xcursor_for_op (MetaDisplay *display,
 void
 meta_display_set_grab_op_cursor (MetaDisplay *display,
                                  MetaScreen  *screen,
+                                 MetaDevice  *device,
                                  MetaGrabOp   op,
                                  gboolean     change_pointer,
                                  Window       grab_xwindow,
                                  guint32      timestamp)
 {
+  MetaGrabInfo *grab_info;
   Cursor cursor;
 
   cursor = xcursor_for_op (display, op);
+  grab_info = meta_display_get_grab_info (display, device);
 
 #define GRAB_MASK (PointerMotionMask |                          \
                    ButtonPressMask | ButtonReleaseMask |        \
@@ -3479,8 +3502,8 @@ meta_display_set_grab_op_cursor (MetaDisplay *display,
         {
           meta_topic (META_DEBUG_WINDOW_OPS,
                       "Error trapped from XChangeActivePointerGrab()\n");
-          if (display->grab_have_pointer)
-            display->grab_have_pointer = FALSE;
+          if (grab_info->grab_have_pointer)
+            grab_info->grab_have_pointer = FALSE;
         }
     }
   else
@@ -3497,7 +3520,7 @@ meta_display_set_grab_op_cursor (MetaDisplay *display,
                         cursor,
                         timestamp) == GrabSuccess)
         {
-          display->grab_have_pointer = TRUE;
+          grab_info->grab_have_pointer = TRUE;
           meta_topic (META_DEBUG_WINDOW_OPS,
                       "XGrabPointer() returned GrabSuccess time %u\n",
                       timestamp);
@@ -3521,6 +3544,7 @@ gboolean
 meta_display_begin_grab_op (MetaDisplay *display,
 			    MetaScreen  *screen,
                             MetaWindow  *window,
+                            MetaDevice  *device,
                             MetaGrabOp   op,
                             gboolean     pointer_already_grabbed,
                             gboolean     frame_action,
@@ -3531,22 +3555,29 @@ meta_display_begin_grab_op (MetaDisplay *display,
                             int          root_y)
 {
   MetaWindow *grab_window = NULL;
+  MetaGrabInfo *grab_info;
   Window grab_xwindow;
   
   meta_topic (META_DEBUG_WINDOW_OPS,
               "Doing grab op %u on window %s button %d pointer already grabbed: %d pointer pos %d,%d\n",
               op, window ? window->desc : "none", button, pointer_already_grabbed,
               root_x, root_y);
+
+  grab_info = meta_display_get_grab_info (display, device);
   
-  if (display->grab_op != META_GRAB_OP_NONE)
+  if (grab_info != NULL &&
+      grab_info->grab_op != META_GRAB_OP_NONE)
     {
       if (window)
         meta_warning ("Attempt to perform window operation %u on window %s when operation %u on %s already in effect\n",
-                      op, window->desc, display->grab_op,
-                      display->grab_window ? display->grab_window->desc : "none");
+                      op, window->desc, grab_info->grab_op,
+                      grab_info->grab_window ? grab_info->grab_window->desc : "none");
       return FALSE;
     }
 
+  if (grab_info == NULL)
+    grab_info = meta_display_create_grab_info (display, device);
+
   if (window &&
       (meta_grab_op_is_moving (op) || meta_grab_op_is_resizing (op)))
     {
@@ -3554,9 +3585,9 @@ meta_display_begin_grab_op (MetaDisplay *display,
         meta_window_raise (window);
       else
         {
-          display->grab_initial_x = root_x;
-          display->grab_initial_y = root_y;
-          display->grab_threshold_movement_reached = FALSE;
+          grab_info->grab_initial_x = root_x;
+          grab_info->grab_initial_y = root_y;
+          grab_info->grab_threshold_movement_reached = FALSE;
         }
     }
 
@@ -3581,15 +3612,15 @@ meta_display_begin_grab_op (MetaDisplay *display,
   else
     grab_xwindow = screen->xroot;
 
-  display->grab_have_pointer = FALSE;
-  
+  grab_info->grab_have_pointer = FALSE;
+
   if (pointer_already_grabbed)
-    display->grab_have_pointer = TRUE;
-  
-  meta_display_set_grab_op_cursor (display, screen, op, FALSE, grab_xwindow,
-                                   timestamp);
+    grab_info->grab_have_pointer = TRUE;
+
+  meta_display_set_grab_op_cursor (display, screen, device, op, FALSE,
+                                   grab_xwindow, timestamp);
 
-  if (!display->grab_have_pointer && !grab_op_is_keyboard (op))
+  if (!grab_info->grab_have_pointer && !grab_op_is_keyboard (op))
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "XGrabPointer() failed\n");
@@ -3600,69 +3631,70 @@ meta_display_begin_grab_op (MetaDisplay *display,
   if (grab_op_is_keyboard (op) || grab_op_is_mouse_only (op))
     {
       if (grab_window)
-        display->grab_have_keyboard =
+        grab_info->grab_have_keyboard =
                      meta_window_grab_all_keys (grab_window, timestamp);
 
       else
-        display->grab_have_keyboard =
+        grab_info->grab_have_keyboard =
                      meta_screen_grab_all_keys (screen, timestamp);
       
-      if (!display->grab_have_keyboard)
+      if (!grab_info->grab_have_keyboard)
         {
           meta_topic (META_DEBUG_WINDOW_OPS,
                       "grabbing all keys failed, ungrabbing pointer\n");
           XUngrabPointer (display->xdisplay, timestamp);
-          display->grab_have_pointer = FALSE;
+          grab_info->grab_have_pointer = FALSE;
           return FALSE;
         }
     }
   
-  display->grab_op = op;
-  display->grab_window = grab_window;
-  display->grab_screen = screen;
-  display->grab_xwindow = grab_xwindow;
-  display->grab_button = button;
-  display->grab_mask = modmask;
+  grab_info->grab_op = op;
+  grab_info->grab_window = grab_window;
+  grab_info->grab_screen = screen;
+  grab_info->grab_xwindow = grab_xwindow;
+  grab_info->grab_button = button;
+  grab_info->grab_mask = modmask;
   if (window)
     {
-      display->grab_tile_mode = window->tile_mode;
-      display->grab_tile_monitor_number = window->tile_monitor_number;
+      grab_info->grab_tile_mode = window->tile_mode;
+      grab_info->grab_tile_monitor_number = window->tile_monitor_number;
     }
-  else
-    {
-      display->grab_tile_mode = META_TILE_NONE;
-      display->grab_tile_monitor_number = -1;
-    }
-  display->grab_anchor_root_x = root_x;
-  display->grab_anchor_root_y = root_y;
-  display->grab_latest_motion_x = root_x;
-  display->grab_latest_motion_y = root_y;
-  display->grab_last_moveresize_time.tv_sec = 0;
-  display->grab_last_moveresize_time.tv_usec = 0;
-  display->grab_motion_notify_time = 0;
-  display->grab_old_window_stacking = NULL;
+    else
+      {
+        grab_info->grab_tile_mode = META_TILE_NONE;
+        grab_info->grab_tile_monitor_number = -1;
+      }
+  grab_info->grab_anchor_root_x = root_x;
+  grab_info->grab_anchor_root_y = root_y;
+  grab_info->grab_latest_motion_x = root_x;
+  grab_info->grab_latest_motion_y = root_y;
+  grab_info->grab_last_moveresize_time.tv_sec = 0;
+  grab_info->grab_last_moveresize_time.tv_usec = 0;
+  grab_info->grab_motion_notify_time = 0;
+  grab_info->grab_old_window_stacking = NULL;
 #ifdef HAVE_XSYNC
-  display->grab_sync_request_alarm = None;
-  display->grab_last_user_action_was_snap = FALSE;
+  grab_info->grab_sync_request_alarm = None;
+  grab_info->grab_last_user_action_was_snap = FALSE;
 #endif
-  display->grab_frame_action = frame_action;
-  display->grab_resize_unmaximize = 0;
+  grab_info->grab_frame_action = frame_action;
+  grab_info->grab_resize_unmaximize = 0;
 
-  if (display->grab_resize_timeout_id)
+  if (grab_info->grab_resize_timeout_id)
     {
-      g_source_remove (display->grab_resize_timeout_id);
-      display->grab_resize_timeout_id = 0;
+      g_source_remove (grab_info->grab_resize_timeout_id);
+      grab_info->grab_resize_timeout_id = 0;
     }
 	
-  if (display->grab_window)
+  if (grab_info->grab_window)
     {
-      meta_window_get_client_root_coords (display->grab_window,
-                                          &display->grab_initial_window_pos);
-      display->grab_anchor_window_pos = display->grab_initial_window_pos;
+      grab_info->grab_window->cur_grab = grab_info;
+      meta_window_get_client_root_coords (grab_info->grab_window,
+                                          &grab_info->grab_initial_window_pos);
+      grab_info->grab_anchor_window_pos = grab_info->grab_initial_window_pos;
 
 #ifdef HAVE_XSYNC
-      if ( meta_grab_op_is_resizing (display->grab_op) &&
-          display->grab_window->sync_request_counter != None)
+      if (meta_grab_op_is_resizing (grab_info->grab_op) &&
+          grab_info->grab_window->sync_request_counter != None)
         {
           XSyncAlarmAttributes values;
 	  XSyncValue init;
@@ -3673,90 +3705,103 @@ meta_display_begin_grab_op (MetaDisplay *display,
 	   * responses to the client messages will always trigger
 	   * a PositiveTransition
 	   */
-	  
+
 	  XSyncIntToValue (&init, 0);
 	  XSyncSetCounter (display->xdisplay,
-			   display->grab_window->sync_request_counter, init);
-	  
-	  display->grab_window->sync_request_serial = 0;
-	  display->grab_window->sync_request_time.tv_sec = 0;
-	  display->grab_window->sync_request_time.tv_usec = 0;
-	  
-          values.trigger.counter = display->grab_window->sync_request_counter;
+			   grab_info->grab_window->sync_request_counter, init);
+
+	  grab_info->grab_window->sync_request_serial = 0;
+	  grab_info->grab_window->sync_request_time.tv_sec = 0;
+	  grab_info->grab_window->sync_request_time.tv_usec = 0;
+
+          values.trigger.counter = grab_info->grab_window->sync_request_counter;
           values.trigger.value_type = XSyncAbsolute;
           values.trigger.test_type = XSyncPositiveTransition;
           XSyncIntToValue (&values.trigger.wait_value,
-			   display->grab_window->sync_request_serial + 1);
-	  
+			   grab_info->grab_window->sync_request_serial + 1);
+
           /* After triggering, increment test_value by this.
            * (NOT wait_value above)
            */
           XSyncIntToValue (&values.delta, 1);
-	  
+
           /* we want events (on by default anyway) */
           values.events = True;
-          
-          display->grab_sync_request_alarm = XSyncCreateAlarm (display->xdisplay,
-                                                         XSyncCACounter |
-                                                         XSyncCAValueType |
-                                                         XSyncCAValue |
-                                                         XSyncCATestType |
-                                                         XSyncCADelta |
-                                                         XSyncCAEvents,
-                                                         &values);
+
+          grab_info->grab_sync_request_alarm = XSyncCreateAlarm (display->xdisplay,
+                                                                 XSyncCACounter |
+                                                                 XSyncCAValueType |
+                                                                 XSyncCAValue |
+                                                                 XSyncCATestType |
+                                                                 XSyncCADelta |
+                                                                 XSyncCAEvents,
+                                                                 &values);
 
           if (meta_error_trap_pop_with_return (display) != Success)
-	    display->grab_sync_request_alarm = None;
+	    grab_info->grab_sync_request_alarm = None;
 
           meta_topic (META_DEBUG_RESIZING,
                       "Created update alarm 0x%lx\n",
-                      display->grab_sync_request_alarm);
+                      grab_info->grab_sync_request_alarm);
         }
 #endif
     }
-  
+
   meta_topic (META_DEBUG_WINDOW_OPS,
               "Grab op %u on window %s successful\n",
-              display->grab_op, window ? window->desc : "(null)");
+              grab_info->grab_op, window ? window->desc : "(null)");
 
-  g_assert (display->grab_window != NULL || display->grab_screen != NULL);
-  g_assert (display->grab_op != META_GRAB_OP_NONE);
+  g_assert (grab_info->grab_window != NULL || grab_info->grab_screen != NULL);
+  g_assert (grab_info->grab_op != META_GRAB_OP_NONE);
 
   /* Save the old stacking */
-  if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
+  if (GRAB_OP_IS_WINDOW_SWITCH (grab_info->grab_op))
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Saving old stack positions; old pointer was %p.\n",
-                  display->grab_old_window_stacking);
-      display->grab_old_window_stacking = 
+                  grab_info->grab_old_window_stacking);
+      grab_info->grab_old_window_stacking =
         meta_stack_get_positions (screen->stack);
     }
 
-  if (display->grab_window)
+  if (grab_info->grab_window)
     {
-      meta_window_refresh_resize_popup (display->grab_window);
+      meta_window_refresh_resize_popup (grab_info->grab_window);
     }
-  
+
   return TRUE;
 }
 
 void
 meta_display_end_grab_op (MetaDisplay *display,
+                          MetaDevice  *device,
                           guint32      timestamp)
 {
+  MetaGrabInfo *grab_info;
+
+  grab_info = meta_display_get_grab_info (display, device);
+
+  if (!grab_info)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Ending non-existent grab op at time %u\n",
+                  timestamp);
+      return;
+    }
+
   meta_topic (META_DEBUG_WINDOW_OPS,
-              "Ending grab op %u at time %u\n", display->grab_op, timestamp);
-  
-  if (display->grab_op == META_GRAB_OP_NONE)
+              "Ending grab op %u at time %u\n", grab_info->grab_op, timestamp);
+
+  if (grab_info->grab_op == META_GRAB_OP_NONE)
     return;
 
-  if (display->grab_window != NULL)
-    display->grab_window->shaken_loose = FALSE;
-  
-  if (display->grab_window != NULL &&
+  if (grab_info->grab_window != NULL)
+    grab_info->grab_window->shaken_loose = FALSE;
+
+  if (grab_info->grab_window != NULL &&
       !meta_prefs_get_raise_on_click () &&
-      (meta_grab_op_is_moving (display->grab_op) ||
-       meta_grab_op_is_resizing (display->grab_op)))
+      (meta_grab_op_is_moving (grab_info->grab_op) ||
+       meta_grab_op_is_resizing (grab_info->grab_op)))
     {
       /* Only raise the window in orthogonal raise
        * ('do-not-raise-on-click') mode if the user didn't try to move
@@ -3764,86 +3809,95 @@ meta_display_end_grab_op (MetaDisplay *display,
        * For raise on click mode, the window was raised at the
        * beginning of the grab_op.
        */
-      if (!display->grab_threshold_movement_reached)
-        meta_window_raise (display->grab_window);
+      if (!grab_info->grab_threshold_movement_reached)
+        meta_window_raise (grab_info->grab_window);
     }
 
-  if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op) ||
-      display->grab_op == META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING)
+  if (GRAB_OP_IS_WINDOW_SWITCH (grab_info->grab_op) ||
+      grab_info->grab_op == META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING)
     {
-      if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
-        meta_screen_tab_popup_destroy (display->grab_screen);
+      if (GRAB_OP_IS_WINDOW_SWITCH (grab_info->grab_op))
+        meta_screen_tab_popup_destroy (grab_info->grab_screen);
       else
-        meta_screen_workspace_popup_destroy (display->grab_screen);
+        meta_screen_workspace_popup_destroy (grab_info->grab_screen);
 
       /* If the ungrab here causes an EnterNotify, ignore it for
        * sloppy focus
        */
-      display->ungrab_should_not_cause_focus_window = display->grab_xwindow;
+      display->ungrab_should_not_cause_focus_window = grab_info->grab_xwindow;
     }
-  
+
   /* If this was a move or resize clear out the edge cache */
-  if (meta_grab_op_is_resizing (display->grab_op) || 
-      meta_grab_op_is_moving (display->grab_op))
+  if (meta_grab_op_is_resizing (grab_info->grab_op) ||
+      meta_grab_op_is_moving (grab_info->grab_op))
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Clearing out the edges for resistance/snapping");
-      meta_display_cleanup_edges (display);
+      meta_display_cleanup_edges (display, grab_info->grab_screen);
     }
 
-  if (display->grab_old_window_stacking != NULL)
+  if (grab_info->grab_old_window_stacking != NULL)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Clearing out the old stack position, which was %p.\n",
-                  display->grab_old_window_stacking);
-      g_list_free (display->grab_old_window_stacking);
-      display->grab_old_window_stacking = NULL;
+                  grab_info->grab_old_window_stacking);
+      g_list_free (grab_info->grab_old_window_stacking);
+      grab_info->grab_old_window_stacking = NULL;
     }
 
-  if (display->grab_have_pointer)
+  if (grab_info->grab_have_pointer)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Ungrabbing pointer with timestamp %u\n", timestamp);
       XUngrabPointer (display->xdisplay, timestamp);
     }
 
-  if (display->grab_have_keyboard)
+  if (grab_info->grab_have_keyboard)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Ungrabbing all keys timestamp %u\n", timestamp);
-      if (display->grab_window)
-        meta_window_ungrab_all_keys (display->grab_window, timestamp);
+      if (grab_info->grab_window)
+        meta_window_ungrab_all_keys (grab_info->grab_window, timestamp);
       else
-        meta_screen_ungrab_all_keys (display->grab_screen, timestamp);
+        meta_screen_ungrab_all_keys (grab_info->grab_screen, timestamp);
     }
 
 #ifdef HAVE_XSYNC
-  if (display->grab_sync_request_alarm != None)
+  if (grab_info->grab_sync_request_alarm != None)
     {
       XSyncDestroyAlarm (display->xdisplay,
-                         display->grab_sync_request_alarm);
-      display->grab_sync_request_alarm = None;
+                         grab_info->grab_sync_request_alarm);
+      grab_info->grab_sync_request_alarm = None;
     }
 #endif /* HAVE_XSYNC */
-  
-  display->grab_window = NULL;
-  display->grab_screen = NULL;
-  display->grab_xwindow = None;
-  display->grab_tile_mode = META_TILE_NONE;
-  display->grab_tile_monitor_number = -1;
-  display->grab_op = META_GRAB_OP_NONE;
-
-  if (display->grab_resize_popup)
-    {
-      meta_ui_resize_popup_free (display->grab_resize_popup);
-      display->grab_resize_popup = NULL;
-    }
 
-  if (display->grab_resize_timeout_id)
+  if (grab_info->grab_resize_popup)
+    meta_ui_resize_popup_free (grab_info->grab_resize_popup);
+
+  if (grab_info->grab_resize_timeout_id)
     {
-      g_source_remove (display->grab_resize_timeout_id);
-      display->grab_resize_timeout_id = 0;
+      g_source_remove (grab_info->grab_resize_timeout_id);
+      grab_info->grab_resize_timeout_id = 0;
     }
+
+  if (grab_info->grab_window != NULL)
+    grab_info->grab_window->cur_grab = NULL;
+
+  meta_display_remove_grab_info (display, device);
+}
+
+MetaGrabOp
+meta_display_get_device_grab_op (MetaDisplay *display,
+                                 MetaDevice  *device)
+{
+  MetaGrabInfo *info;
+
+  info = meta_display_get_grab_info (display, device);
+
+  if (info)
+    return info->grab_op;
+
+  return META_GRAB_OP_NONE;
 }
 
 /**
@@ -3858,22 +3912,34 @@ meta_display_end_grab_op (MetaDisplay *display,
 MetaGrabOp
 meta_display_get_grab_op (MetaDisplay *display)
 {
-  return display->grab_op;
+  MetaDevice *device;
+
+  device = meta_device_map_lookup (display->device_map,
+                                   META_CORE_POINTER_ID);
+  return meta_display_get_device_grab_op (display, device);
 }
 
 void
 meta_display_check_threshold_reached (MetaDisplay *display,
+                                      MetaDevice  *device,
                                       int          x,
                                       int          y)
 {
+  MetaGrabInfo *grab_info;
+
+  grab_info = meta_display_get_grab_info (display, device);
+
+  if (!grab_info)
+    return;
+
   /* Don't bother doing the check again if we've already reached the threshold */
   if (meta_prefs_get_raise_on_click () ||
-      display->grab_threshold_movement_reached)
+      grab_info->grab_threshold_movement_reached)
     return;
 
-  if (ABS (display->grab_initial_x - x) >= 8 ||
-      ABS (display->grab_initial_y - y) >= 8)
-    display->grab_threshold_movement_reached = TRUE;
+  if (ABS (grab_info->grab_initial_x - x) >= 8 ||
+      ABS (grab_info->grab_initial_y - y) >= 8)
+    grab_info->grab_threshold_movement_reached = TRUE;
 }
 
 static void
@@ -5527,3 +5593,63 @@ meta_display_get_device_map (MetaDisplay *display)
 
   return display->device_map;
 }
+
+MetaGrabInfo *
+meta_display_create_grab_info (MetaDisplay *display,
+                               MetaDevice  *device)
+{
+  MetaGrabInfo *grab_info;
+  gpointer key;
+
+  g_assert (meta_display_get_grab_info (display, device) == NULL);
+
+  grab_info = g_new0 (MetaGrabInfo, 1);
+
+  if (META_IS_DEVICE_KEYBOARD (device))
+    {
+      grab_info->grab_keyboard = device;
+      grab_info->grab_pointer = meta_device_get_paired_device (device);
+    }
+  else
+    {
+      grab_info->grab_pointer = device;
+      grab_info->grab_keyboard = meta_device_get_paired_device (device);
+    }
+
+  key = GINT_TO_POINTER (meta_device_get_id (grab_info->grab_pointer));
+  g_hash_table_insert (display->current_grabs, key, grab_info);
+
+  return grab_info;
+}
+
+void
+meta_display_remove_grab_info (MetaDisplay *display,
+                               MetaDevice  *device)
+{
+  g_hash_table_remove (display->current_grabs,
+                       GINT_TO_POINTER (meta_device_get_id (device)));
+
+  device = meta_device_get_paired_device (device);
+  g_hash_table_remove (display->current_grabs,
+                       GINT_TO_POINTER (meta_device_get_id (device)));
+}
+
+MetaGrabInfo *
+meta_display_get_grab_info (MetaDisplay *display,
+                            MetaDevice  *device)
+{
+  MetaGrabInfo *info;
+
+  info = g_hash_table_lookup (display->current_grabs,
+                              GINT_TO_POINTER (meta_device_get_id (device)));
+
+  if (!info)
+    {
+      /* Try with the paired device */
+      device = meta_device_get_paired_device (device);
+      info = g_hash_table_lookup (display->current_grabs,
+                                  GINT_TO_POINTER (meta_device_get_id (device)));
+    }
+
+  return info;
+}
diff --git a/src/core/edge-resistance.c b/src/core/edge-resistance.c
index 5972002..18fbb26 100644
--- a/src/core/edge-resistance.c
+++ b/src/core/edge-resistance.c
@@ -30,12 +30,12 @@
 /* A simple macro for whether a given window's edges are potentially
  * relevant for resistance/snapping during a move/resize operation
  */
-#define WINDOW_EDGES_RELEVANT(window, display) \
-  meta_window_should_be_showing (window) &&    \
-  window->screen == display->grab_screen &&    \
-  window         != display->grab_window &&    \
-  window->type   != META_WINDOW_DESKTOP &&     \
-  window->type   != META_WINDOW_MENU    &&     \
+#define WINDOW_EDGES_RELEVANT(window, display, screen) \
+  meta_window_should_be_showing (window) &&            \
+  window->screen == screen &&                          \
+  window->cur_grab == NULL &&                          \
+  window->type   != META_WINDOW_DESKTOP &&             \
+  window->type   != META_WINDOW_MENU    &&             \
   window->type   != META_WINDOW_SPLASHSCREEN
 
 struct ResistanceDataForAnEdge
@@ -44,8 +44,9 @@ struct ResistanceDataForAnEdge
   guint        timeout_id;
   int          timeout_edge_pos;
   gboolean     timeout_over;
-  GSourceFunc  timeout_func;
+  MetaEdgeResistanceFunc timeout_func;
   MetaWindow  *window;
+  MetaDevice  *device;
   int          keyboard_buildup;
 };
 typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge;
@@ -63,7 +64,9 @@ struct MetaEdgeResistanceData
   ResistanceDataForAnEdge bottom_data;
 };
 
-static void compute_resistance_and_snapping_edges (MetaDisplay *display);
+static MetaEdgeResistanceData *
+        compute_resistance_and_snapping_edges (MetaDisplay *display,
+                                               MetaScreen  *screen);
 
 /* !WARNING!: this function can return invalid indices (namely, either -1 or
  * edges->len); this is by design, but you need to remember this.
@@ -318,20 +321,22 @@ edge_resistance_timeout (gpointer data)
 
   resistance_data->timeout_over = TRUE;
   resistance_data->timeout_id = 0;
-  (*resistance_data->timeout_func)(resistance_data->window);
+  (*resistance_data->timeout_func) (resistance_data->window,
+                                    resistance_data->device);
 
   return FALSE;
 }
 
 static int
 apply_edge_resistance (MetaWindow                *window,
+                       MetaDevice                *device,
                        int                        old_pos,
                        int                        new_pos,
                        const MetaRectangle       *old_rect,
                        const MetaRectangle       *new_rect,
                        GArray                    *edges,
                        ResistanceDataForAnEdge   *resistance_data,
-                       GSourceFunc                timeout_func,
+                       MetaEdgeResistanceFunc     timeout_func,
                        gboolean                   xdir,
                        gboolean                   keyboard_op)
 {
@@ -445,6 +450,7 @@ apply_edge_resistance (MetaWindow                *window,
                   resistance_data->timeout_over = FALSE;
                   resistance_data->timeout_func = timeout_func;
                   resistance_data->window = window;
+                  resistance_data->device = device;
                 }
               if (!resistance_data->timeout_over &&
                   timeout_length_ms != 0)
@@ -533,29 +539,28 @@ apply_edge_snapping (int                  old_pos,
  * a proposed new position (ignoring edge resistance/snapping), and then
  * applies edge resistance to EACH edge (separately) updating new_outer.
  * It returns true if new_outer is modified, false otherwise.
- *
- * display->grab_edge_resistance_data MUST already be setup or calling this
- * function will cause a crash.
  */
 static gboolean 
-apply_edge_resistance_to_each_side (MetaDisplay         *display,
-                                    MetaWindow          *window,
-                                    const MetaRectangle *old_outer,
-                                    MetaRectangle       *new_outer,
-                                    GSourceFunc          timeout_func,
-                                    gboolean             auto_snap,
-                                    gboolean             keyboard_op,
-                                    gboolean             is_resize)
+apply_edge_resistance_to_each_side (MetaDisplay            *display,
+                                    MetaWindow             *window,
+                                    MetaDevice             *device,
+                                    const MetaRectangle    *old_outer,
+                                    MetaRectangle          *new_outer,
+                                    MetaEdgeResistanceFunc  timeout_func,
+                                    gboolean                auto_snap,
+                                    gboolean                keyboard_op,
+                                    gboolean                is_resize)
 {
   MetaEdgeResistanceData *edge_data;
   MetaRectangle           modified_rect;
   gboolean                modified;
   int new_left, new_right, new_top, new_bottom;
 
-  if (display->grab_edge_resistance_data == NULL)
-    compute_resistance_and_snapping_edges (display);
+  edge_data = g_hash_table_lookup (display->edge_resistance_info,
+                                   window->screen);
 
-  edge_data = display->grab_edge_resistance_data;
+  if (!edge_data)
+    edge_data = compute_resistance_and_snapping_edges (display, window->screen);
 
   if (auto_snap)
     {
@@ -601,7 +606,7 @@ apply_edge_resistance_to_each_side (MetaDisplay         *display,
       if (!is_resize || window->size_hints.width_inc == 1)
         {
           /* Now, apply the normal horizontal edge resistance */
-          new_left   = apply_edge_resistance (window,
+          new_left   = apply_edge_resistance (window, device,
                                               BOX_LEFT (*old_outer),
                                               BOX_LEFT (*new_outer),
                                               old_outer,
@@ -611,7 +616,7 @@ apply_edge_resistance_to_each_side (MetaDisplay         *display,
                                               timeout_func,
                                               TRUE,
                                               keyboard_op);
-          new_right  = apply_edge_resistance (window,
+          new_right  = apply_edge_resistance (window, device,
                                               BOX_RIGHT (*old_outer),
                                               BOX_RIGHT (*new_outer),
                                               old_outer,
@@ -630,7 +635,7 @@ apply_edge_resistance_to_each_side (MetaDisplay         *display,
       /* Same for vertical resizes... */
       if (!is_resize || window->size_hints.height_inc == 1)
         {
-          new_top    = apply_edge_resistance (window,
+          new_top    = apply_edge_resistance (window, device,
                                               BOX_TOP (*old_outer),
                                               BOX_TOP (*new_outer),
                                               old_outer,
@@ -640,7 +645,7 @@ apply_edge_resistance_to_each_side (MetaDisplay         *display,
                                               timeout_func,
                                               FALSE,
                                               keyboard_op);
-          new_bottom = apply_edge_resistance (window,
+          new_bottom = apply_edge_resistance (window, device,
                                               BOX_BOTTOM (*old_outer),
                                               BOX_BOTTOM (*new_outer),
                                               old_outer,
@@ -669,15 +674,20 @@ apply_edge_resistance_to_each_side (MetaDisplay         *display,
 }
 
 void
-meta_display_cleanup_edges (MetaDisplay *display)
+meta_display_cleanup_edges (MetaDisplay *display,
+                            MetaScreen  *screen)
 {
   guint i,j;
-  MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
+  MetaEdgeResistanceData *edge_data;
   GHashTable *edges_to_be_freed;
 
+  edge_data = g_hash_table_lookup (display->edge_resistance_info, screen);
+
   if (edge_data == NULL) /* Not currently cached */
     return;
 
+  g_hash_table_steal (display->edge_resistance_info, screen);
+
   /* We first need to clean out any window edges */
   edges_to_be_freed = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                              g_free, NULL);
@@ -750,8 +760,7 @@ meta_display_cleanup_edges (MetaDisplay *display)
       edge_data->bottom_data.timeout_id != 0)
     g_source_remove (edge_data->bottom_data.timeout_id);
 
-  g_free (display->grab_edge_resistance_data);
-  display->grab_edge_resistance_data = NULL;
+  g_free (edge_data);
 }
 
 static int
@@ -763,8 +772,9 @@ stupid_sort_requiring_extra_pointer_dereference (gconstpointer a,
   return meta_rectangle_edge_cmp_ignore_type (*a_edge, *b_edge);
 }
 
-static void
+static MetaEdgeResistanceData *
 cache_edges (MetaDisplay *display,
+             MetaScreen  *screen,
              GList *window_edges,
              GList *monitor_edges,
              GList *screen_edges)
@@ -848,9 +858,8 @@ cache_edges (MetaDisplay *display,
   /*
    * 2nd: Allocate the edges
    */
-  g_assert (display->grab_edge_resistance_data == NULL);
-  display->grab_edge_resistance_data = g_new0 (MetaEdgeResistanceData, 1);
-  edge_data = display->grab_edge_resistance_data;
+  edge_data = g_new0 (MetaEdgeResistanceData, 1);
+
   edge_data->left_edges   = g_array_sized_new (FALSE,
                                                FALSE,
                                                sizeof(MetaEdge*),
@@ -917,21 +926,21 @@ cache_edges (MetaDisplay *display,
    * avoided this sort by sticking them into the array with some simple
    * merging of the lists).
    */
-  g_array_sort (display->grab_edge_resistance_data->left_edges, 
+  g_array_sort (edge_data->left_edges,
                 stupid_sort_requiring_extra_pointer_dereference);
-  g_array_sort (display->grab_edge_resistance_data->right_edges, 
+  g_array_sort (edge_data->right_edges,
                 stupid_sort_requiring_extra_pointer_dereference);
-  g_array_sort (display->grab_edge_resistance_data->top_edges, 
+  g_array_sort (edge_data->top_edges,
                 stupid_sort_requiring_extra_pointer_dereference);
-  g_array_sort (display->grab_edge_resistance_data->bottom_edges, 
+  g_array_sort (edge_data->bottom_edges,
                 stupid_sort_requiring_extra_pointer_dereference);
+
+  return edge_data;
 }
 
 static void
-initialize_grab_edge_resistance_data (MetaDisplay *display)
+initialize_grab_edge_resistance_data (MetaEdgeResistanceData *edge_data)
 {
-  MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
-
   edge_data->left_data.timeout_setup   = FALSE;
   edge_data->right_data.timeout_setup  = FALSE;
   edge_data->top_data.timeout_setup    = FALSE;
@@ -943,8 +952,9 @@ initialize_grab_edge_resistance_data (MetaDisplay *display)
   edge_data->bottom_data.keyboard_buildup = 0;
 }
 
-static void
-compute_resistance_and_snapping_edges (MetaDisplay *display)
+static MetaEdgeResistanceData *
+compute_resistance_and_snapping_edges (MetaDisplay *display,
+                                       MetaScreen  *screen)
 {
   GList *stacked_windows;
   GList *cur_window_iter;
@@ -956,18 +966,17 @@ compute_resistance_and_snapping_edges (MetaDisplay *display)
    * in the layer that we are working on
    */
   GSList *rem_windows, *rem_win_stacking;
+  MetaEdgeResistanceData *edge_data;
 
-  g_assert (display->grab_window != NULL);
   meta_topic (META_DEBUG_WINDOW_OPS,
-              "Computing edges to resist-movement or snap-to for %s.\n",
-              display->grab_window->desc);
+              "Computing edges to resist-movement or snap-to for screen %s.\n",
+              screen->screen_name);
 
   /*
    * 1st: Get the list of relevant windows, from bottom to top
    */
   stacked_windows = 
-    meta_stack_list_windows (display->grab_screen->stack,
-                             display->grab_screen->active_workspace);
+    meta_stack_list_windows (screen->stack, screen->active_workspace);
 
   /*
    * 2nd: we need to separate that stacked list into a list of windows that
@@ -981,7 +990,7 @@ compute_resistance_and_snapping_edges (MetaDisplay *display)
   while (cur_window_iter != NULL)
     {
       MetaWindow *cur_window = cur_window_iter->data;
-      if (WINDOW_EDGES_RELEVANT (cur_window, display))
+      if (WINDOW_EDGES_RELEVANT (cur_window, display, screen))
         {
           MetaRectangle *new_rect;
           new_rect = g_new (MetaRectangle, 1);
@@ -1016,7 +1025,7 @@ compute_resistance_and_snapping_edges (MetaDisplay *display)
        * resistance (note that dock edges are considered screen edges
        * which are handled separately
        */
-      if (WINDOW_EDGES_RELEVANT (cur_window, display) &&
+      if (WINDOW_EDGES_RELEVANT (cur_window, display, screen) &&
           cur_window->type != META_WINDOW_DOCK)
         {
           GList *new_edges;
@@ -1028,7 +1037,7 @@ compute_resistance_and_snapping_edges (MetaDisplay *display)
            * by other windows or DOCKS, but that's handled below).
            */
           meta_rectangle_intersect (&cur_rect, 
-                                    &display->grab_screen->rect,
+                                    &screen->rect,
                                     &reduced);
 
           new_edges = NULL;
@@ -1123,32 +1132,36 @@ compute_resistance_and_snapping_edges (MetaDisplay *display)
    * monitor edges in an array for quick access.  Free the edges since
    * they've been cached elsewhere.
    */
-  cache_edges (display,
-               edges,
-               display->grab_screen->active_workspace->monitor_edges,
-               display->grab_screen->active_workspace->screen_edges);
+  edge_data = cache_edges (display, screen,
+                           edges,
+                           screen->active_workspace->monitor_edges,
+                           screen->active_workspace->screen_edges);
   g_list_free (edges);
 
   /*
    * 6th: Initialize the resistance timeouts and buildups
    */
-  initialize_grab_edge_resistance_data (display);
+  initialize_grab_edge_resistance_data (edge_data);
+
+  return edge_data;
 }
 
 /* Note that old_[xy] and new_[xy] are with respect to inner positions of
  * the window.
  */
 void
-meta_window_edge_resistance_for_move (MetaWindow  *window,
-                                      int          old_x,
-                                      int          old_y,
-                                      int         *new_x,
-                                      int         *new_y,
-                                      GSourceFunc  timeout_func,
-                                      gboolean     snap,
-                                      gboolean     is_keyboard_op)
+meta_window_edge_resistance_for_move (MetaWindow             *window,
+                                      MetaDevice             *device,
+                                      int                     old_x,
+                                      int                     old_y,
+                                      int                    *new_x,
+                                      int                    *new_y,
+                                      MetaEdgeResistanceFunc  timeout_func,
+                                      gboolean                snap,
+                                      gboolean                is_keyboard_op)
 {
   MetaRectangle old_outer, proposed_outer, new_outer;
+  MetaGrabInfo *grab_info;
   gboolean is_resize;
 
   meta_window_get_outer_rect (window, &old_outer);
@@ -1158,10 +1171,14 @@ meta_window_edge_resistance_for_move (MetaWindow  *window,
   proposed_outer.y += (*new_y - old_y);
   new_outer = proposed_outer;
 
-  window->display->grab_last_user_action_was_snap = snap;
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
+  grab_info->grab_last_user_action_was_snap = snap;
   is_resize = FALSE;
   if (apply_edge_resistance_to_each_side (window->display,
                                           window,
+                                          device,
                                           &old_outer,
                                           &new_outer,
                                           timeout_func,
@@ -1223,18 +1240,20 @@ meta_window_edge_resistance_for_move (MetaWindow  *window,
  * sizes of the inner window.
  */
 void
-meta_window_edge_resistance_for_resize (MetaWindow  *window,
-                                        int          old_width,
-                                        int          old_height,
-                                        int         *new_width,
-                                        int         *new_height,
-                                        int          gravity,
-                                        GSourceFunc  timeout_func,
-                                        gboolean     snap,
-                                        gboolean     is_keyboard_op)
+meta_window_edge_resistance_for_resize (MetaWindow             *window,
+                                        MetaDevice             *device,
+                                        int                     old_width,
+                                        int                     old_height,
+                                        int                    *new_width,
+                                        int                    *new_height,
+                                        int                     gravity,
+                                        MetaEdgeResistanceFunc  timeout_func,
+                                        gboolean                snap,
+                                        gboolean                is_keyboard_op)
 {
   MetaRectangle old_outer, new_outer;
   int proposed_outer_width, proposed_outer_height;
+  MetaGrabInfo *grab_info;
   gboolean is_resize;
 
   meta_window_get_outer_rect (window, &old_outer);
@@ -1246,10 +1265,14 @@ meta_window_edge_resistance_for_resize (MetaWindow  *window,
                                       proposed_outer_width,
                                       proposed_outer_height);
 
-  window->display->grab_last_user_action_was_snap = snap;
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
+  grab_info->grab_last_user_action_was_snap = snap;
   is_resize = TRUE;
   if (apply_edge_resistance_to_each_side (window->display,
                                           window,
+                                          device,
                                           &old_outer,
                                           &new_outer,
                                           timeout_func,
diff --git a/src/core/edge-resistance.h b/src/core/edge-resistance.h
index 14ba17a..06fba01 100644
--- a/src/core/edge-resistance.h
+++ b/src/core/edge-resistance.h
@@ -26,23 +26,28 @@
 
 #include "window-private.h"
 
-void        meta_window_edge_resistance_for_move   (MetaWindow  *window,
-                                                    int          old_x,
-                                                    int          old_y,
-                                                    int         *new_x,
-                                                    int         *new_y,
-                                                    GSourceFunc  timeout_func,
-                                                    gboolean     snap,
-                                                    gboolean     is_keyboard_op);
-void        meta_window_edge_resistance_for_resize (MetaWindow  *window,
-                                                    int          old_width,
-                                                    int          old_height,
-                                                    int         *new_width,
-                                                    int         *new_height,
-                                                    int          gravity,
-                                                    GSourceFunc  timeout_func,
-                                                    gboolean     snap,
-                                                    gboolean     is_keyboard_op);
+typedef gboolean (* MetaEdgeResistanceFunc) (MetaWindow *window,
+                                             MetaDevice *device);
+
+void meta_window_edge_resistance_for_move   (MetaWindow             *window,
+                                             MetaDevice             *device,
+                                             int                     old_x,
+                                             int                     old_y,
+                                             int                    *new_x,
+                                             int                    *new_y,
+                                             MetaEdgeResistanceFunc  func,
+                                             gboolean                snap,
+                                             gboolean                is_keyboard_op);
+void meta_window_edge_resistance_for_resize (MetaWindow             *window,
+                                             MetaDevice             *device,
+                                             int                     old_width,
+                                             int                     old_height,
+                                             int                    *new_width,
+                                             int                    *new_height,
+                                             int                     gravity,
+                                             MetaEdgeResistanceFunc  func,
+                                             gboolean                snap,
+                                             gboolean                is_keyboard_op);
 
 #endif /* META_EDGE_RESISTANCE_H */
 
diff --git a/src/core/frame.c b/src/core/frame.c
index 2038053..fe07768 100644
--- a/src/core/frame.c
+++ b/src/core/frame.c
@@ -377,8 +377,7 @@ meta_frame_sync_to_window (MetaFrame *frame,
       /* If we're interactively resizing the frame, repaint
        * it immediately so we don't start to lag.
        */
-      if (frame->window->display->grab_window ==
-          frame->window)
+      if (frame->window->cur_grab != NULL)
         meta_ui_repaint_frame (frame->window->screen->ui,
                                frame->xwindow);
     }
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 19ba80b..48086b4 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -1534,16 +1534,20 @@ meta_display_process_key_event (MetaDisplay *display,
   keep_grab = TRUE;
   if (all_keys_grabbed)
     {
-      if (display->grab_op == META_GRAB_OP_NONE)
+      MetaGrabInfo *grab_info;
+
+      grab_info = meta_display_get_grab_info (display, keyboard);
+
+      if (!grab_info)
         return TRUE;
       /* If we get here we have a global grab, because
         * we're in some special keyboard mode such as window move
         * mode.
         */
-      if (window ? (window == display->grab_window) :
-          (screen == display->grab_screen))
+      if (window ? (window == grab_info->grab_window) :
+          (screen == grab_info->grab_screen))
         {
-          switch (display->grab_op)
+          switch (grab_info->grab_op)
             {
             case META_GRAB_OP_MOVING:
             case META_GRAB_OP_RESIZING_SE:
@@ -1610,8 +1614,8 @@ meta_display_process_key_event (MetaDisplay *display,
         {
           meta_topic (META_DEBUG_KEYBINDINGS,
                       "Ending grab op %u on key event sym %s\n",
-                      display->grab_op, XKeysymToString (keysym));
-          meta_display_end_grab_op (display,
+                      grab_info->grab_op, XKeysymToString (keysym));
+          meta_display_end_grab_op (display, keyboard,
                                     meta_input_event_get_time (display, event));
         }
 
@@ -1638,13 +1642,20 @@ process_mouse_move_resize_grab (MetaDisplay *display,
 
   if (keysym == XK_Escape)
     {
+      MetaGrabInfo *grab_info;
+      MetaDevice *device;
+
+      device = meta_input_event_get_device (display, event);
+      grab_info = meta_display_get_grab_info (display, device);
+      g_assert (grab_info != NULL);
+
       /* Hide the tiling preview if necessary */
       if (window->tile_mode != META_TILE_NONE)
         meta_screen_tile_preview_hide (screen);
 
       /* Restore the original tile mode */
-      window->tile_mode = display->grab_tile_mode;
-      window->tile_monitor_number = display->grab_tile_monitor_number;
+      window->tile_mode = grab_info->grab_tile_mode;
+      window->tile_monitor_number = grab_info->grab_tile_monitor_number;
 
       /* End move or resize and restore to original state.  If the
        * window was a maximized window that had been "shaken loose" we
@@ -1658,12 +1669,12 @@ process_mouse_move_resize_grab (MetaDisplay *display,
       else if (window->tile_mode != META_TILE_NONE)
         meta_window_tile (window);
       else
-        meta_window_move_resize (display->grab_window,
+        meta_window_move_resize (grab_info->grab_window,
                                  TRUE,
-                                 display->grab_initial_window_pos.x,
-                                 display->grab_initial_window_pos.y,
-                                 display->grab_initial_window_pos.width,
-                                 display->grab_initial_window_pos.height);
+                                 grab_info->grab_initial_window_pos.x,
+                                 grab_info->grab_initial_window_pos.y,
+                                 grab_info->grab_initial_window_pos.width,
+                                 grab_info->grab_initial_window_pos.height);
 
       /* End grab */
       return FALSE;
@@ -1714,6 +1725,13 @@ process_keyboard_move_grab (MetaDisplay *display,
 
   if (keysym == XK_Escape)
     {
+      MetaGrabInfo *grab_info;
+      MetaDevice *device;
+
+      device = meta_input_event_get_device (display, event);
+      grab_info = meta_display_get_grab_info (display, device);
+      g_assert (grab_info != NULL);
+
       /* End move and restore to original state.  If the window was a
        * maximized window that had been "shaken loose" we need to
        * remaximize it.  In normal cases, we need to do a moveresize
@@ -1724,12 +1742,12 @@ process_keyboard_move_grab (MetaDisplay *display,
                               META_MAXIMIZE_HORIZONTAL |
                               META_MAXIMIZE_VERTICAL);
       else
-        meta_window_move_resize (display->grab_window,
+        meta_window_move_resize (grab_info->grab_window,
                                  TRUE,
-                                 display->grab_initial_window_pos.x,
-                                 display->grab_initial_window_pos.y,
-                                 display->grab_initial_window_pos.width,
-                                 display->grab_initial_window_pos.height);
+                                 grab_info->grab_initial_window_pos.x,
+                                 grab_info->grab_initial_window_pos.y,
+                                 grab_info->grab_initial_window_pos.width,
+                                 grab_info->grab_initial_window_pos.height);
     }
   
   /* When moving by increments, we still snap to edges if the move
@@ -1776,13 +1794,16 @@ process_keyboard_move_grab (MetaDisplay *display,
   if (handled)
     {
       MetaRectangle old_rect;
+      MetaDevice *device;
+
       meta_topic (META_DEBUG_KEYBINDINGS,
                   "Computed new window location %d,%d due to keypress\n",
                   x, y);
 
+      device = meta_input_event_get_device (window->display, event);
       meta_window_get_client_root_coords (window, &old_rect);
 
-      meta_window_edge_resistance_for_move (window, 
+      meta_window_edge_resistance_for_move (window, device,
                                             old_rect.x,
                                             old_rect.y,
                                             &x,
@@ -1792,7 +1813,7 @@ process_keyboard_move_grab (MetaDisplay *display,
                                             TRUE);
 
       meta_window_move (window, TRUE, x, y);
-      meta_window_update_keyboard_move (window);
+      meta_window_update_keyboard_move (window, device);
     }
 
   return handled;
@@ -1805,32 +1826,37 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
                                         XEvent      *event,
                                         KeySym       keysym)
 {
+  MetaGrabInfo *grab_info;
+  MetaDevice *device;
   gboolean handled;
 
+  device = meta_input_event_get_device (display, event);
+  grab_info = meta_display_get_grab_info (display, device);
+
   handled = FALSE;
-  switch (display->grab_op)
+  switch (grab_info->grab_op)
     {
     case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
       switch (keysym)
         {
         case XK_Up:
         case XK_KP_Up:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;          
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;          
           handled = TRUE;
           break;
         case XK_Down:
         case XK_KP_Down:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
           handled = TRUE;
           break;
         case XK_Left:
         case XK_KP_Left:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
           handled = TRUE;
           break;
         case XK_Right:
         case XK_KP_Right:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
           handled = TRUE;
           break;
         }
@@ -1841,12 +1867,12 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
         {
         case XK_Left:
         case XK_KP_Left:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
           handled = TRUE;
           break;
         case XK_Right:
         case XK_KP_Right:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
           handled = TRUE;
           break;
         }
@@ -1857,12 +1883,12 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
         {
         case XK_Left:
         case XK_KP_Left:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
           handled = TRUE;
           break;
         case XK_Right:
         case XK_KP_Right:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
           handled = TRUE;
           break;
         }
@@ -1873,12 +1899,12 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
         {
         case XK_Up:
         case XK_KP_Up:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;          
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;          
           handled = TRUE;
           break;
         case XK_Down:
         case XK_KP_Down:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
           handled = TRUE;
           break;
         }
@@ -1889,12 +1915,12 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
         {
         case XK_Up:
         case XK_KP_Up:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; 
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; 
           handled = TRUE;
           break;
         case XK_Down:
         case XK_KP_Down:
-          display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
           handled = TRUE;
           break;
         }
@@ -1913,7 +1939,7 @@ process_keyboard_resize_grab_op_change (MetaDisplay *display,
 
   if (handled)
     {
-      meta_window_update_keyboard_resize (window, TRUE);
+      meta_window_update_keyboard_resize (window, device, TRUE);
       return TRUE; 
     }
 
@@ -1934,6 +1960,8 @@ process_keyboard_resize_grab (MetaDisplay *display,
   gboolean smart_snap;
   int gravity;
   guint keycode, state;
+  MetaDevice *device;
+  MetaGrabInfo *grab_info;
 
   handled = FALSE;
 
@@ -1948,15 +1976,18 @@ process_keyboard_resize_grab (MetaDisplay *display,
   if (is_modifier (display, keycode))
     return TRUE;
 
+  device = meta_input_event_get_device (display, event);
+  grab_info = meta_display_get_grab_info (display, device);
+
   if (keysym == XK_Escape)
     {
       /* End resize and restore to original state. */
-      meta_window_move_resize (display->grab_window,
+      meta_window_move_resize (grab_info->grab_window,
                                TRUE,
-                               display->grab_initial_window_pos.x,
-                               display->grab_initial_window_pos.y,
-                               display->grab_initial_window_pos.width,
-                               display->grab_initial_window_pos.height);
+                               grab_info->grab_initial_window_pos.x,
+                               grab_info->grab_initial_window_pos.y,
+                               grab_info->grab_initial_window_pos.width,
+                               grab_info->grab_initial_window_pos.height);
 
       return FALSE;
     }
@@ -1968,7 +1999,7 @@ process_keyboard_resize_grab (MetaDisplay *display,
   width = window->rect.width;
   height = window->rect.height;
 
-  gravity = meta_resize_gravity_from_grab_op (display->grab_op);
+  gravity = meta_resize_gravity_from_grab_op (grab_info->grab_op);
 
   smart_snap = (state & ShiftMask) != 0;
   
@@ -2134,7 +2165,7 @@ process_keyboard_resize_grab (MetaDisplay *display,
       old_rect = window->rect;  /* Don't actually care about x,y */
 
       /* Do any edge resistance/snapping */
-      meta_window_edge_resistance_for_resize (window,
+      meta_window_edge_resistance_for_resize (window, device,
                                               old_rect.width,
                                               old_rect.height,
                                               &width,
@@ -2154,23 +2185,28 @@ process_keyboard_resize_grab (MetaDisplay *display,
                                          height,
                                          gravity);
 
-      meta_window_update_keyboard_resize (window, FALSE);
+      meta_window_update_keyboard_resize (window, device, FALSE);
     }
 
   return handled;
 }
 
 static gboolean
-end_keyboard_grab (MetaDisplay *display,
-		   unsigned int keycode)
+end_keyboard_grab (MetaDisplay  *display,
+                   MetaDevice   *device,
+		   unsigned int  keycode)
 {
+  MetaGrabInfo *grab_info;
+
+  grab_info = meta_display_get_grab_info (display, device);
+
 #ifdef HAVE_XKB
   if (display->xkb_base_event_type > 0)
     {
       unsigned int primary_modifier;
       XkbStateRec state;
   
-      primary_modifier = get_primary_modifier (display, display->grab_mask);
+      primary_modifier = get_primary_modifier (display, grab_info->grab_mask);
       
       XkbGetState (display->xdisplay, XkbUseCoreKbd, &state);
 
@@ -2180,7 +2216,7 @@ end_keyboard_grab (MetaDisplay *display,
   else
 #endif
     {
-      if (keycode_is_primary_modifier (display, keycode, display->grab_mask))
+      if (keycode_is_primary_modifier (display, keycode, grab_info->grab_mask))
 	return TRUE;
     }
 
@@ -2200,8 +2236,16 @@ process_tab_grab (MetaDisplay *display,
   gboolean key_used;
   MetaWindow *prev_window;
   guint evtype, keycode;
+  MetaDevice *device;
+  MetaGrabInfo *grab_info;
+
+  device = meta_input_event_get_device (display, event);
+  grab_info = meta_display_get_grab_info (display, device);
 
-  if (screen != display->grab_screen)
+  if (!grab_info)
+    return FALSE;
+
+  if (screen != grab_info->grab_screen)
     return FALSE;
 
   if (!meta_input_event_get_type (display, event, &evtype) ||
@@ -2211,7 +2255,7 @@ process_tab_grab (MetaDisplay *display,
   binding = display_get_keybinding (display,
                                     keysym,
                                     keycode,
-                                    display->grab_mask);
+                                    grab_info->grab_mask);
   if (binding)
     action = meta_prefs_get_keybinding_action (binding->name);
   else
@@ -2226,7 +2270,7 @@ process_tab_grab (MetaDisplay *display,
     {
       if (evtype == KeyRelease)
         {
-          if (end_keyboard_grab (display, keycode))
+          if (end_keyboard_grab (display, device, keycode))
             {
               invoke_handler_by_name (display, screen, "tab_popup_select", NULL, event);
 
@@ -2289,7 +2333,7 @@ process_tab_grab (MetaDisplay *display,
     }
 
   if (evtype == KeyRelease &&
-      end_keyboard_grab (display, keycode))
+      end_keyboard_grab (display, device, keycode))
     {
       /* We're done, move to the new window. */
       MetaWindow *target_window;
@@ -2317,7 +2361,7 @@ process_tab_grab (MetaDisplay *display,
 
           meta_topic (META_DEBUG_KEYBINDINGS,
                       "Ending grab early so we can focus the target window\n");
-          meta_display_end_grab_op (display, evtime);
+          meta_display_end_grab_op (display, device, evtime);
 
           return TRUE; /* we already ended the grab */
         }
@@ -2347,7 +2391,7 @@ process_tab_grab (MetaDisplay *display,
       /* CYCLE_* are traditionally Escape-based actions,
        * and should cancel traditionally Tab-based ones.
        */
-       switch (display->grab_op)
+       switch (grab_info->grab_op)
         {
         case META_GRAB_OP_KEYBOARD_ESCAPING_NORMAL:
         case META_GRAB_OP_KEYBOARD_ESCAPING_DOCK:
@@ -2364,7 +2408,7 @@ process_tab_grab (MetaDisplay *display,
       /* SWITCH_* are traditionally Tab-based actions,
        * and should cancel traditionally Escape-based ones.
        */
-      switch (display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_KEYBOARD_TABBING_NORMAL:
         case META_GRAB_OP_KEYBOARD_TABBING_DOCK:
@@ -2375,7 +2419,7 @@ process_tab_grab (MetaDisplay *display,
            * we'd previously raised and unminimized.
            */
           meta_stack_set_positions (screen->stack,
-                                    screen->display->grab_old_window_stacking);
+                                    grab_info->grab_old_window_stacking);
           if (prev_window && prev_window->tab_unminimized)
             {
               meta_window_minimize (prev_window);
@@ -2388,7 +2432,7 @@ process_tab_grab (MetaDisplay *display,
     case META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD:
     case META_KEYBINDING_ACTION_SWITCH_GROUP:
     case META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD:
-      switch (display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_KEYBOARD_ESCAPING_GROUP:
         case META_GRAB_OP_KEYBOARD_TABBING_GROUP:
@@ -2469,7 +2513,7 @@ process_tab_grab (MetaDisplay *display,
           MetaWindow *target_window;
 
           meta_stack_set_positions (screen->stack,
-                                    display->grab_old_window_stacking);
+                                    grab_info->grab_old_window_stacking);
 
           target_window = meta_screen_tab_popup_get_selected (screen);
           
@@ -2496,7 +2540,7 @@ process_tab_grab (MetaDisplay *display,
       meta_topic (META_DEBUG_KEYBINDINGS, 
                   "Syncing to old stack positions.\n");
       meta_stack_set_positions (screen->stack,
-                                screen->display->grab_old_window_stacking);
+                                grab_info->grab_old_window_stacking);
 
       if (prev_window && prev_window->tab_unminimized)
         {
@@ -2895,9 +2939,14 @@ process_workspace_switch_grab (MetaDisplay *display,
 {
   MetaWorkspace *workspace;
   guint evtype, keycode;
+  MetaDevice *device;
+  MetaGrabInfo *grab_info;
   Time evtime;
 
-  if (screen != display->grab_screen || !screen->ws_popup)
+  device = meta_input_event_get_device (display, event);
+  grab_info = meta_display_get_grab_info (display, device);
+
+  if (screen != grab_info->grab_screen || !screen->ws_popup)
     return FALSE;
 
   if (!meta_input_event_get_type (display, event, &evtype) ||
@@ -2907,7 +2956,7 @@ process_workspace_switch_grab (MetaDisplay *display,
   evtime = meta_input_event_get_time (display, event);
 
   if (evtype == KeyRelease &&
-      end_keyboard_grab (display, keycode))
+      end_keyboard_grab (display, device, keycode))
     {
       /* We're done, move to the new workspace. */
       MetaWorkspace *target_workspace;
@@ -2921,7 +2970,7 @@ process_workspace_switch_grab (MetaDisplay *display,
         {
           meta_topic (META_DEBUG_KEYBINDINGS,
                       "Ending grab so we can focus on the target workspace\n");
-          meta_display_end_grab_op (display, evtime);
+          meta_display_end_grab_op (display, device, evtime);
 
           meta_topic (META_DEBUG_KEYBINDINGS,
                       "Focusing default window on target workspace\n");
@@ -2957,7 +3006,7 @@ process_workspace_switch_grab (MetaDisplay *display,
 
       action = meta_display_get_keybinding_action (display,
                                                    keycode,
-                                                   display->grab_mask);
+                                                   grab_info->grab_mask);
 
       switch (action)
         {
@@ -3172,6 +3221,7 @@ do_choose_window (MetaDisplay    *display,
 {
   MetaTabList type = binding->handler->data;
   MetaWindow *initial_selection;
+  MetaDevice *device;
   guint state;
   Time evtime;
 
@@ -3186,6 +3236,7 @@ do_choose_window (MetaDisplay    *display,
     backward = !backward;
 
   evtime = meta_input_event_get_time (display, event);
+  device = meta_input_event_get_device (display, event);
 
   initial_selection = meta_display_get_tab_next (display,
                                                  type,
@@ -3230,6 +3281,7 @@ do_choose_window (MetaDisplay    *display,
   if (!meta_display_begin_grab_op (display,
                                    screen,
                                    NULL,
+                                   device,
                                    show_popup ?
                                    tab_op_from_tab_type (type) :
                                    cycle_op_from_tab_type (type),
@@ -3253,7 +3305,7 @@ do_choose_window (MetaDisplay    *display,
                   "modifier was released prior to grab\n",
                   initial_selection->desc);
 
-      meta_display_end_grab_op (display, evtime);
+      meta_display_end_grab_op (display, device, evtime);
       display->mouse_mode = FALSE;
       meta_window_activate (initial_selection, evtime);
       return;
@@ -3452,6 +3504,7 @@ handle_begin_move         (MetaDisplay    *display,
   if (window->has_move_func)
     {
       meta_window_begin_grab_op (window,
+                                 meta_input_event_get_device (display, event),
                                  META_GRAB_OP_KEYBOARD_MOVING,
                                  FALSE,
                                  meta_input_event_get_time (display, event));
@@ -3469,6 +3522,7 @@ handle_begin_resize       (MetaDisplay    *display,
   if (window->has_resize_func)
     {
       meta_window_begin_grab_op (window,
+                                 meta_input_event_get_device (display, event),
                                  META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
                                  FALSE,
                                  meta_input_event_get_time (display, event));
@@ -3625,6 +3679,7 @@ handle_workspace_switch  (MetaDisplay    *display,
   unsigned int grab_mask;
   MetaWorkspace *next;
   gboolean grabbed_before_release;
+  MetaDevice *device;
   guint state;
   Time evtime;
 
@@ -3639,10 +3694,12 @@ handle_workspace_switch  (MetaDisplay    *display,
   /* FIXME should we use binding->mask ? */
   grab_mask = state & ~(display->ignored_modifier_mask);
   evtime = meta_input_event_get_time (display, event);
+  device = meta_input_event_get_device (display, event);
 
   if (!meta_display_begin_grab_op (display,
                                    screen,
                                    NULL,
+                                   device,
                                    META_GRAB_OP_KEYBOARD_WORKSPACE_SWITCHING,
                                    FALSE,
                                    FALSE,
@@ -3666,7 +3723,7 @@ handle_workspace_switch  (MetaDisplay    *display,
        * release event. Must end grab before we can switch
        * spaces.
        */
-      meta_display_end_grab_op (display, evtime);
+      meta_display_end_grab_op (display, device, evtime);
     }
 
   meta_workspace_activate (next, evtime);
diff --git a/src/core/screen.c b/src/core/screen.c
index ad03ce7..41ecab1 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -1937,9 +1937,17 @@ static gboolean
 meta_screen_tile_preview_update_timeout (gpointer data)
 {
   MetaScreen *screen = data;
-  MetaWindow *window = screen->display->grab_window;
+  MetaWindow *window = NULL;
   gboolean composited = screen->display->compositor != NULL;
   gboolean needs_preview = FALSE;
+  MetaGrabInfo *grab_info;
+  GHashTableIter iter;
+
+  /* FIXME: we're just handling the first grab we find */
+  g_hash_table_iter_init (&iter, screen->display->current_grabs);
+
+  if (g_hash_table_iter_next (&iter, NULL, (gpointer *) &grab_info))
+    window = grab_info->grab_window;
 
   screen->tile_preview_timeout_id = 0;
 
@@ -1983,7 +1991,7 @@ meta_screen_tile_preview_update_timeout (gpointer data)
       MetaRectangle tile_rect;
 
       meta_window_get_current_tile_area (window, &tile_rect);
-      meta_tile_preview_show (screen->tile_preview, &tile_rect);
+      meta_tile_preview_show (screen->tile_preview, pointer, &tile_rect);
     }
   else
     meta_tile_preview_hide (screen->tile_preview);
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 55afdde..09666a1 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -399,6 +399,9 @@ struct _MetaWindow
 
   /* Focused window that is (directly or indirectly) attached to this one */
   MetaWindow *attached_focus_window;
+
+  /* Current grab op for this window, or NULL */
+  MetaGrabInfo *cur_grab;
 };
 
 struct _MetaWindowClass
@@ -619,13 +622,16 @@ void meta_window_free_delete_dialog (MetaWindow *window);
 
 
 void meta_window_begin_grab_op (MetaWindow *window,
+                                MetaDevice *device,
                                 MetaGrabOp  op,
                                 gboolean    frame_action,
                                 guint32     timestamp);
 
 void meta_window_update_keyboard_resize (MetaWindow *window,
+                                         MetaDevice *device,
                                          gboolean    update_cursor);
-void meta_window_update_keyboard_move   (MetaWindow *window);
+void meta_window_update_keyboard_move   (MetaWindow *window,
+                                         MetaDevice *device);
 
 void meta_window_update_layer (MetaWindow *window);
 
@@ -651,4 +657,6 @@ void meta_window_propagate_focus_appearance (MetaWindow *window,
 
 gboolean meta_window_should_attach_to_parent (MetaWindow *window);
 
+MetaDevice * meta_window_guess_grab_pointer (MetaWindow *window);
+
 #endif
diff --git a/src/core/window.c b/src/core/window.c
index 9c10dd1..bdd02e0 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -112,16 +112,20 @@ static void meta_window_move_resize_now (MetaWindow  *window);
 static void meta_window_unqueue (MetaWindow *window, guint queuebits);
 
 static void     update_move           (MetaWindow   *window,
+                                       MetaDevice   *device,
                                        gboolean      snap,
                                        int           x,
                                        int           y);
-static gboolean update_move_timeout   (gpointer data);
+static gboolean update_move_timeout   (MetaWindow   *window,
+                                       MetaDevice   *device);
 static void     update_resize         (MetaWindow   *window,
+                                       MetaDevice   *device,
                                        gboolean      snap,
                                        int           x,
                                        int           y,
                                        gboolean      force);
-static gboolean update_resize_timeout (gpointer data);
+static gboolean update_resize_timeout (MetaWindow   *window,
+                                       MetaDevice   *device);
 static gboolean should_be_on_all_workspaces (MetaWindow *window);
 
 static void meta_window_flush_calc_showing   (MetaWindow *window);
@@ -1659,10 +1663,12 @@ meta_window_unmanage (MetaWindow  *window,
       invalidate_work_areas (window);
     }
 
-  if (window->display->grab_window == window)
-    meta_display_end_grab_op (window->display, timestamp);
+  if (window->cur_grab != NULL)
+    meta_display_end_grab_op (window->display,
+                              window->cur_grab->grab_pointer,
+                              timestamp);
 
-  g_assert (window->display->grab_window != window);
+  g_assert (window->cur_grab == NULL);
 
   if (window->display->focus_window == window)
     {
@@ -3674,6 +3680,7 @@ meta_window_unmaximize_internal (MetaWindow        *window,
       (unmaximize_vertically   && window->maximized_vertically))
     {
       MetaRectangle target_rect;
+      MetaGrabInfo *grab_info;
 
       meta_topic (META_DEBUG_WINDOW_OPS,
                   "Unmaximizing %s%s\n",
@@ -3687,6 +3694,8 @@ meta_window_unmaximize_internal (MetaWindow        *window,
       window->maximized_vertically =
         window->maximized_vertically   && !unmaximize_vertically;
 
+      grab_info = window->cur_grab;
+
       /* Reset the tile mode for maximized tiled windows for consistency
        * with "normal" maximized windows, but keep other tile modes,
        * as side-by-side tiled windows may snap back.
@@ -3756,10 +3765,11 @@ meta_window_unmaximize_internal (MetaWindow        *window,
        * it after the actual operation, as the window may have been moved
        * by constraints.
        */
-      if (meta_grab_op_is_moving (window->display->grab_op) &&
-          window->display->grab_window == window)
+      if (grab_info &&
+          meta_grab_op_is_moving (grab_info->grab_op) &&
+          grab_info->grab_window == window)
         {
-          window->display->grab_anchor_window_pos = window->user_rect;
+          grab_info->grab_anchor_window_pos = window->user_rect;
         }
 
       recalc_window_features (window);
@@ -4796,6 +4806,8 @@ meta_window_move_resize_internal (MetaWindow          *window,
 
   if (mask != 0)
     {
+      MetaGrabInfo *grab_info;
+
       {
         int newx, newy;
         meta_window_get_position (window, &newx, &newy);
@@ -4809,10 +4821,11 @@ meta_window_move_resize_internal (MetaWindow          *window,
       }
 
       meta_error_trap_push (window->display);
+      grab_info = window->cur_grab;
 
 #ifdef HAVE_XSYNC
       if (window->sync_request_counter != None &&
-	  window->display->grab_sync_request_alarm != None &&
+          grab_info && grab_info->grab_sync_request_alarm != None &&
 	  window->sync_request_time.tv_usec == 0 &&
 	  window->sync_request_time.tv_sec == 0)
 	{
@@ -5395,6 +5408,8 @@ meta_window_focus (MetaWindow  *window,
                    guint32      timestamp)
 {
   MetaWindow *modal_transient;
+  MetaGrabInfo *grab_info;
+  MetaDevice *device;
 
   g_return_if_fail (!window->override_redirect);
 
@@ -5402,12 +5417,19 @@ meta_window_focus (MetaWindow  *window,
               "Setting input focus to window %s, input: %d take_focus: %d\n",
               window->desc, window->input, window->take_focus);
 
-  if (window->display->grab_window &&
-      window->display->grab_window->all_keys_grabbed)
+  device = meta_window_guess_grab_pointer (window);
+  grab_info = meta_display_get_grab_info (window->display, device);
+
+  if (grab_info &&
+      grab_info->grab_window &&
+      grab_info->grab_window->all_keys_grabbed)
     {
       meta_topic (META_DEBUG_FOCUS,
-                  "Current focus window %s has global keygrab, not focusing window %s after all\n",
-                  window->display->grab_window->desc, window->desc);
+                  "Current focus window %s for device %d has global "
+                  "keygrab, not focusing window %s after all\n",
+                  grab_info->grab_window->desc,
+                  meta_device_get_id (device),
+                  window->desc);
       return;
     }
 
@@ -5827,6 +5849,7 @@ meta_window_move_resize_request (MetaWindow *window,
   gboolean allow_position_change;
   gboolean in_grab_op;
   MetaMoveResizeFlags flags;
+  MetaGrabInfo *grab_info;
 
   /* We ignore configure requests while the user is moving/resizing
    * the window, since these represent the app sucking and fighting
@@ -5837,10 +5860,11 @@ meta_window_move_resize_request (MetaWindow *window,
    * app asked for the current size/position instead of the new one.
    */
   in_grab_op = FALSE;
-  if (window->display->grab_op != META_GRAB_OP_NONE &&
-      window == window->display->grab_window)
+  grab_info = window->cur_grab;
+
+  if (grab_info != NULL)
     {
-      switch (window->display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_MOVING:
         case META_GRAB_OP_RESIZING_SE:
@@ -6404,6 +6428,7 @@ meta_window_client_message (MetaWindow *window,
       MetaGrabOp op;
       int button;
       guint32 timestamp;
+      MetaDevice *device;
 
       /* _NET_WM_MOVERESIZE messages are almost certainly going to come from
        * clients when users click on the fake "frame" that the client has,
@@ -6467,15 +6492,26 @@ meta_window_client_message (MetaWindow *window,
           break;
         }
 
+      if (window->cur_grab != NULL)
+        device = window->cur_grab->grab_pointer;
+      else
+        {
+          /* No current device grab, find out
+           * the best device to operate on
+           */
+          device = meta_window_guess_grab_pointer (window);
+        }
+
+
       if (action == _NET_WM_MOVERESIZE_CANCEL)
         {
-          meta_display_end_grab_op (window->display, timestamp);
+          meta_display_end_grab_op (window->display, device, timestamp);
         }
       else if (op != META_GRAB_OP_NONE &&
           ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
            (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
         {
-          meta_window_begin_grab_op (window, op, frame_action, timestamp);
+          meta_window_begin_grab_op (window, device, op, frame_action, timestamp);
         }
       else if (op != META_GRAB_OP_NONE &&
                ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
@@ -6522,6 +6558,7 @@ meta_window_client_message (MetaWindow *window,
               meta_display_begin_grab_op (window->display,
                                           window->screen,
                                           window,
+                                          device,
                                           op,
                                           FALSE,
                                           frame_action,
@@ -7958,6 +7995,13 @@ menu_callback (MetaWindowMenu *menu,
 
   if (window != NULL) /* window can be NULL */
     {
+      MetaDevice *device;
+
+      if (window->cur_grab != NULL)
+        device = window->cur_grab->grab_pointer;
+      else
+        device = meta_window_guess_grab_pointer (window);
+
       meta_verbose ("Menu op %u on %s\n", op, window->desc);
 
       switch (op)
@@ -8036,14 +8080,14 @@ menu_callback (MetaWindowMenu *menu,
           break;
 
         case META_MENU_OP_MOVE:
-          meta_window_begin_grab_op (window,
+          meta_window_begin_grab_op (window, device,
                                      META_GRAB_OP_KEYBOARD_MOVING,
                                      TRUE,
                                      timestamp);
           break;
 
         case META_MENU_OP_RESIZE:
-          meta_window_begin_grab_op (window,
+          meta_window_begin_grab_op (window, device,
                                      META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
                                      TRUE,
                                      timestamp);
@@ -8326,16 +8370,17 @@ time_diff (const GTimeVal *first,
 }
 
 static gboolean
-check_moveresize_frequency (MetaWindow *window,
-			    gdouble    *remaining)
+check_moveresize_frequency (MetaWindow   *window,
+                            MetaGrabInfo *grab_info,
+			    gdouble      *remaining)
 {
   GTimeVal current_time;
 
   g_get_current_time (&current_time);
 
 #ifdef HAVE_XSYNC
-  if (!window->disable_sync &&
-      window->display->grab_sync_request_alarm != None)
+  if (!window->disable_sync && grab_info &&
+      grab_info->grab_sync_request_alarm != None)
     {
       if (window->sync_request_time.tv_sec != 0 ||
 	  window->sync_request_time.tv_usec != 0)
@@ -8377,7 +8422,7 @@ check_moveresize_frequency (MetaWindow *window,
       const double ms_between_resizes = 1000.0 / max_resizes_per_second;
       double elapsed;
 
-      elapsed = time_diff (&current_time, &window->display->grab_last_moveresize_time);
+      elapsed = time_diff (&current_time, &grab_info->grab_last_moveresize_time);
 
       if (elapsed >= 0.0 && elapsed < ms_between_resizes)
 	{
@@ -8400,20 +8445,25 @@ check_moveresize_frequency (MetaWindow *window,
 }
 
 static gboolean
-update_move_timeout (gpointer data)
+update_move_timeout (MetaWindow *window,
+                     MetaDevice *device)
 {
-  MetaWindow *window = data;
+  MetaGrabInfo *grab_info;
 
-  update_move (window,
-               window->display->grab_last_user_action_was_snap,
-               window->display->grab_latest_motion_x,
-               window->display->grab_latest_motion_y);
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
+  update_move (window, device,
+               grab_info->grab_last_user_action_was_snap,
+               grab_info->grab_latest_motion_x,
+               grab_info->grab_latest_motion_y);
 
   return FALSE;
 }
 
 static void
 update_move (MetaWindow  *window,
+             MetaDevice  *device,
              gboolean     snap,
              int          x,
              int          y)
@@ -8423,22 +8473,25 @@ update_move (MetaWindow  *window,
   MetaRectangle old;
   int shake_threshold;
   MetaDisplay *display = window->display;
+  MetaGrabInfo *grab_info;
+
+  grab_info = meta_display_get_grab_info (display, device);
 
-  display->grab_latest_motion_x = x;
-  display->grab_latest_motion_y = y;
+  grab_info->grab_latest_motion_x = x;
+  grab_info->grab_latest_motion_y = y;
 
-  dx = x - display->grab_anchor_root_x;
-  dy = y - display->grab_anchor_root_y;
+  dx = x - grab_info->grab_anchor_root_x;
+  dy = y - grab_info->grab_anchor_root_y;
 
-  new_x = display->grab_anchor_window_pos.x + dx;
-  new_y = display->grab_anchor_window_pos.y + dy;
+  new_x = grab_info->grab_anchor_window_pos.x + dx;
+  new_y = grab_info->grab_anchor_window_pos.y + dy;
 
   meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
                 x, y,
-                display->grab_anchor_root_x,
-                display->grab_anchor_root_y,
-                display->grab_anchor_window_pos.x,
-                display->grab_anchor_window_pos.y,
+                grab_info->grab_anchor_root_x,
+                grab_info->grab_anchor_root_y,
+                grab_info->grab_anchor_window_pos.x,
+                grab_info->grab_anchor_window_pos.y,
                 dx, dy);
 
   /* Don't bother doing anything if no move has been specified.  (This
@@ -8527,22 +8580,22 @@ update_move (MetaWindow  *window,
 
       /* move the unmaximized window to the cursor */
       prop =
-        ((double)(x - display->grab_initial_window_pos.x)) /
-        ((double)display->grab_initial_window_pos.width);
+        ((double)(x - grab_info->grab_initial_window_pos.x)) /
+        ((double)grab_info->grab_initial_window_pos.width);
 
-      display->grab_initial_window_pos.x =
+      grab_info->grab_initial_window_pos.x =
         x - window->saved_rect.width * prop;
-      display->grab_initial_window_pos.y = y;
+      grab_info->grab_initial_window_pos.y = y;
 
       if (window->frame)
         {
-          display->grab_initial_window_pos.y += window->frame->child_y / 2;
+          grab_info->grab_initial_window_pos.y += window->frame->child_y / 2;
         }
 
-      window->saved_rect.x = display->grab_initial_window_pos.x;
-      window->saved_rect.y = display->grab_initial_window_pos.y;
-      display->grab_anchor_root_x = x;
-      display->grab_anchor_root_y = y;
+      window->saved_rect.x = grab_info->grab_initial_window_pos.x;
+      window->saved_rect.y = grab_info->grab_initial_window_pos.y;
+      grab_info->grab_anchor_root_x = x;
+      grab_info->grab_anchor_root_y = y;
 
       meta_window_unmaximize (window,
                               META_MAXIMIZE_HORIZONTAL |
@@ -8596,9 +8649,9 @@ update_move (MetaWindow  *window,
                                           META_MAXIMIZE_VERTICAL);
                 }
 
-              display->grab_initial_window_pos = work_area;
-              display->grab_anchor_root_x = x;
-              display->grab_anchor_root_y = y;
+              grab_info->grab_initial_window_pos = work_area;
+              grab_info->grab_anchor_root_x = x;
+              grab_info->grab_anchor_root_y = y;
               window->shaken_loose = FALSE;
 
               meta_window_maximize (window,
@@ -8627,6 +8680,7 @@ update_move (MetaWindow  *window,
 
   /* Do any edge resistance/snapping */
   meta_window_edge_resistance_for_move (window,
+                                        device,
                                         old.x,
                                         old.y,
                                         &new_x,
@@ -8647,11 +8701,13 @@ update_move (MetaWindow  *window,
  */
 static MetaMaximizeFlags
 check_resize_unmaximize(MetaWindow *window,
+                        MetaDevice *device,
                         int         dx,
                         int         dy)
 {
   int threshold;
   MetaMaximizeFlags new_unmaximize;
+  MetaGrabInfo *grab_info;
 
 #define DRAG_THRESHOLD_TO_RESIZE_THRESHOLD_FACTOR 3
 
@@ -8659,9 +8715,12 @@ check_resize_unmaximize(MetaWindow *window,
     DRAG_THRESHOLD_TO_RESIZE_THRESHOLD_FACTOR;
   new_unmaximize = 0;
 
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
   if (window->maximized_horizontally ||
       window->tile_mode != META_TILE_NONE ||
-      (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
+      (grab_info->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
     {
       int x_amount;
 
@@ -8670,7 +8729,7 @@ check_resize_unmaximize(MetaWindow *window,
        * monitor. If we wanted to only allow resizing smaller than the
        * monitor, we'd use - dx for NE/E/SE and dx for SW/W/NW.
        */
-      switch (window->display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_RESIZING_NE:
         case META_GRAB_OP_KEYBOARD_RESIZING_NE:
@@ -8696,11 +8755,11 @@ check_resize_unmaximize(MetaWindow *window,
     }
 
   if (window->maximized_vertically ||
-      (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0)
+      (grab_info->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0)
     {
       int y_amount;
 
-      switch (window->display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_RESIZING_N:
         case META_GRAB_OP_KEYBOARD_RESIZING_N:
@@ -8735,10 +8794,10 @@ check_resize_unmaximize(MetaWindow *window,
       new_unmaximize = 0;
 
       if (window->maximized_horizontally ||
-          (window->display->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
+          (grab_info->grab_resize_unmaximize & META_MAXIMIZE_HORIZONTAL) != 0)
         new_unmaximize |= META_MAXIMIZE_HORIZONTAL;
       if (window->maximized_vertically ||
-          (window->display->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0)
+          (grab_info->grab_resize_unmaximize & META_MAXIMIZE_VERTICAL) != 0)
         new_unmaximize |= META_MAXIMIZE_VERTICAL;
     }
 
@@ -8746,20 +8805,39 @@ check_resize_unmaximize(MetaWindow *window,
 }
 
 static gboolean
-update_resize_timeout (gpointer data)
+update_resize_timeout (MetaWindow *window,
+                       MetaDevice *device)
 {
-  MetaWindow *window = data;
+  MetaGrabInfo *grab_info;
 
-  update_resize (window,
-                 window->display->grab_last_user_action_was_snap,
-                 window->display->grab_latest_motion_x,
-                 window->display->grab_latest_motion_y,
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
+  update_resize (window, device,
+                 grab_info->grab_last_user_action_was_snap,
+                 grab_info->grab_latest_motion_x,
+                 grab_info->grab_latest_motion_y,
                  TRUE);
   return FALSE;
 }
 
+typedef struct
+{
+  MetaWindow *window;
+  MetaDevice *device;
+} ResizeTimeoutData;
+
+static gboolean
+update_resize_timeout_cb (gpointer user_data)
+{
+  ResizeTimeoutData *data = user_data;
+
+  return update_resize_timeout (data->window, data->device);
+}
+
 static void
 update_resize (MetaWindow *window,
+               MetaDevice *device,
                gboolean    snap,
                int x, int y,
                gboolean force)
@@ -8770,12 +8848,16 @@ update_resize (MetaWindow *window,
   MetaRectangle old;
   double remaining;
   MetaMaximizeFlags new_unmaximize;
+  MetaGrabInfo *grab_info;
+
+  grab_info = meta_display_get_grab_info (window->display,
+                                          device);
 
-  window->display->grab_latest_motion_x = x;
-  window->display->grab_latest_motion_y = y;
+  grab_info->grab_latest_motion_x = x;
+  grab_info->grab_latest_motion_y = y;
 
-  dx = x - window->display->grab_anchor_root_x;
-  dy = y - window->display->grab_anchor_root_y;
+  dx = x - grab_info->grab_anchor_root_x;
+  dy = y - grab_info->grab_anchor_root_y;
 
   /* Attached modal dialogs are special in that horizontal
    * size changes apply to both sides, so that the dialog
@@ -8784,8 +8866,8 @@ update_resize (MetaWindow *window,
   if (meta_window_is_attached_dialog (window))
     dx *= 2;
 
-  new_w = window->display->grab_anchor_window_pos.width;
-  new_h = window->display->grab_anchor_window_pos.height;
+  new_w = grab_info->grab_anchor_window_pos.width;
+  new_h = grab_info->grab_anchor_window_pos.height;
 
   /* Don't bother doing anything if no move has been specified.  (This
    * happens often, even in keyboard resizing, due to the warping of the
@@ -8794,53 +8876,53 @@ update_resize (MetaWindow *window,
   if (dx == 0 && dy == 0)
     return;
 
-  if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)
+  if (grab_info->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)
     {
       if ((dx > 0) && (dy > 0))
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SE;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if ((dx < 0) && (dy > 0))
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_SW;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if ((dx > 0) && (dy < 0))
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NE;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if ((dx < 0) && (dy < 0))
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_NW;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if (dx < 0)
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if (dx > 0)
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if (dy > 0)
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
       else if (dy < 0)
         {
-          window->display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
-          meta_window_update_keyboard_resize (window, TRUE);
+          grab_info->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N;
+          meta_window_update_keyboard_resize (window, device, TRUE);
         }
     }
 
-  new_unmaximize = check_resize_unmaximize (window, dx, dy);
+  new_unmaximize = check_resize_unmaximize (window, device, dx, dy);
 
-  switch (window->display->grab_op)
+  switch (grab_info->grab_op)
     {
     case META_GRAB_OP_RESIZING_SE:
     case META_GRAB_OP_RESIZING_NE:
@@ -8863,7 +8945,7 @@ update_resize (MetaWindow *window,
 	  break;
 	}
 
-  switch (window->display->grab_op)
+  switch (grab_info->grab_op)
     {
     case META_GRAB_OP_RESIZING_SE:
     case META_GRAB_OP_RESIZING_S:
@@ -8886,17 +8968,25 @@ update_resize (MetaWindow *window,
       break;
     }
 
-  if (!check_moveresize_frequency (window, &remaining) && !force)
+  if (!check_moveresize_frequency (window, grab_info, &remaining) && !force)
     {
       /* we are ignoring an event here, so we schedule a
        * compensation event when we would otherwise not ignore
        * an event. Otherwise we can become stuck if the user never
        * generates another event.
        */
-      if (!window->display->grab_resize_timeout_id)
+      if (!grab_info->grab_resize_timeout_id)
 	{
-	  window->display->grab_resize_timeout_id =
-	    g_timeout_add ((int)remaining, update_resize_timeout, window);
+          ResizeTimeoutData *data;
+
+          data = g_new (ResizeTimeoutData, 1);
+          data->window = window;
+          data->device = device;
+
+	  grab_info->grab_resize_timeout_id =
+	    g_timeout_add_full (G_PRIORITY_DEFAULT,
+                                (int)remaining, update_resize_timeout_cb,
+                                data, (GDestroyNotify) g_free);
 	}
 
       return;
@@ -8907,10 +8997,10 @@ update_resize (MetaWindow *window,
     meta_compositor_set_updates (window->display->compositor, window, TRUE);
 
   /* Remove any scheduled compensation events */
-  if (window->display->grab_resize_timeout_id)
+  if (grab_info->grab_resize_timeout_id)
     {
-      g_source_remove (window->display->grab_resize_timeout_id);
-      window->display->grab_resize_timeout_id = 0;
+      g_source_remove (grab_info->grab_resize_timeout_id);
+      grab_info->grab_resize_timeout_id = 0;
     }
 
   old = window->rect;  /* Don't actually care about x,y */
@@ -8919,7 +9009,7 @@ update_resize (MetaWindow *window,
    * aspect ratio windows don't interact nicely with the above stuff.  So,
    * to avoid some nasty flicker, we enforce that.
    */
-  switch (window->display->grab_op)
+  switch (grab_info->grab_op)
     {
     case META_GRAB_OP_RESIZING_S:
     case META_GRAB_OP_RESIZING_N:
@@ -8936,11 +9026,12 @@ update_resize (MetaWindow *window,
     }
 
   /* compute gravity of client during operation */
-  gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
+  gravity = meta_resize_gravity_from_grab_op (grab_info->grab_op);
   g_assert (gravity >= 0);
 
   /* Do any edge resistance/snapping */
   meta_window_edge_resistance_for_resize (window,
+                                          device,
                                           old.width,
                                           old.height,
                                           &new_w,
@@ -8950,7 +9041,7 @@ update_resize (MetaWindow *window,
                                           snap,
                                           FALSE);
 
-  if (new_unmaximize == window->display->grab_resize_unmaximize)
+  if (new_unmaximize == grab_info->grab_resize_unmaximize)
     {
       /* We don't need to update unless the specified width and height
        * are actually different from what we had before.
@@ -8962,27 +9053,27 @@ update_resize (MetaWindow *window,
     }
   else
     {
-      if ((new_unmaximize & ~window->display->grab_resize_unmaximize) != 0)
+      if ((new_unmaximize & ~grab_info->grab_resize_unmaximize) != 0)
         {
           meta_window_unmaximize_with_gravity (window,
-                                               (new_unmaximize & ~window->display->grab_resize_unmaximize),
+                                               (new_unmaximize & ~grab_info->grab_resize_unmaximize),
                                                new_w, new_h, gravity);
         }
 
-      if ((window->display->grab_resize_unmaximize & ~new_unmaximize))
+      if ((grab_info->grab_resize_unmaximize & ~new_unmaximize))
         {
           MetaRectangle saved_rect = window->saved_rect;
           meta_window_maximize (window,
-                                (window->display->grab_resize_unmaximize & ~new_unmaximize));
+                                (grab_info->grab_resize_unmaximize & ~new_unmaximize));
           window->saved_rect = saved_rect;
         }
     }
 
-  window->display->grab_resize_unmaximize = new_unmaximize;
+  grab_info->grab_resize_unmaximize = new_unmaximize;
 
   /* Store the latest resize time, if we actually resized. */
   if (window->rect.width != old.width || window->rect.height != old.height)
-    g_get_current_time (&window->display->grab_last_moveresize_time);
+    g_get_current_time (&grab_info->grab_last_moveresize_time);
 }
 
 typedef struct
@@ -9037,24 +9128,32 @@ check_use_this_motion_notify (MetaWindow *window,
                               XEvent     *event)
 {
   EventScannerData esd;
+  MetaDevice *device;
+  MetaGrabInfo *grab_info;
   XEvent useless;
 
+  device = meta_input_event_get_device (window->display, event);
+  grab_info = meta_display_get_grab_info (window->display, device);
+
+  if (!grab_info)
+    return FALSE;
+
   /* This code is copied from Owen's GDK code. */
 
-  if (window->display->grab_motion_notify_time != 0)
+  if (grab_info->grab_motion_notify_time != 0)
     {
       Time evtime;
 
       evtime = meta_input_event_get_time (window->display, event);
 
       /* == is really the right test, but I'm all for paranoia */
-      if (window->display->grab_motion_notify_time <= evtime)
+      if (grab_info->grab_motion_notify_time <= evtime)
         {
           meta_topic (META_DEBUG_RESIZING,
                       "Arrived at event with time %u (waiting for %u), using it\n",
                       (unsigned int) evtime,
-                      window->display->grab_motion_notify_time);
-          window->display->grab_motion_notify_time = 0;
+                      grab_info->grab_motion_notify_time);
+          grab_info->grab_motion_notify_time = 0;
           return TRUE;
         }
       else
@@ -9087,7 +9186,7 @@ check_use_this_motion_notify (MetaWindow *window,
       /* Save this timestamp, and ignore all motion notify
        * until we get to the one with this stamp.
        */
-      window->display->grab_motion_notify_time = esd.last_time;
+      grab_info->grab_motion_notify_time = esd.last_time;
       return FALSE;
     }
 }
@@ -9115,15 +9214,20 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
 {
   gdouble x_root, y_root;
   guint evtype, state;
+  MetaDevice *device;
+  MetaGrabInfo *grab_info;
   Window xroot;
 
 #ifdef HAVE_XSYNC
   if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
     {
+      grab_info = window->cur_grab;
+      g_assert (grab_info != NULL);
+
       meta_topic (META_DEBUG_RESIZING,
                   "Alarm event received last motion x = %d y = %d\n",
-                  window->display->grab_latest_motion_x,
-                  window->display->grab_latest_motion_y);
+                  grab_info->grab_latest_motion_x,
+                  grab_info->grab_latest_motion_y);
 
       /* If sync was previously disabled, turn it back on and hope
        * the application has come to its senses (maybe it was just
@@ -9134,7 +9238,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
       window->sync_request_time.tv_usec = 0;
 
       /* This means we are ready for another configure. */
-      switch (window->display->grab_op)
+      switch (grab_info->grab_op)
         {
         case META_GRAB_OP_RESIZING_E:
         case META_GRAB_OP_RESIZING_W:
@@ -9154,9 +9258,10 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
         case META_GRAB_OP_KEYBOARD_RESIZING_NW:
           /* no pointer round trip here, to keep in sync */
           update_resize (window,
-                         window->display->grab_last_user_action_was_snap,
-                         window->display->grab_latest_motion_x,
-                         window->display->grab_latest_motion_y,
+                         grab_info->grab_pointer,
+                         grab_info->grab_last_user_action_was_snap,
+                         grab_info->grab_latest_motion_x,
+                         grab_info->grab_latest_motion_y,
                          TRUE);
           break;
 
@@ -9173,32 +9278,34 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
     return;
 
   xroot = meta_input_event_get_root_window (window->display, event);
+  device = meta_input_event_get_device (window->display, event);
+  grab_info = meta_display_get_grab_info (window->display, device);
 
   switch (evtype)
     {
     case ButtonRelease:
-      meta_display_check_threshold_reached (window->display,
+      meta_display_check_threshold_reached (window->display, device,
                                             x_root, y_root);
       /* If the user was snap moving then ignore the button release
        * because they may have let go of shift before releasing the
        * mouse button and they almost certainly do not want a
        * non-snapped movement to occur from the button release.
        */
-      if (!window->display->grab_last_user_action_was_snap)
+      if (!grab_info->grab_last_user_action_was_snap)
         {
-          if (meta_grab_op_is_moving (window->display->grab_op))
+          if (meta_grab_op_is_moving (grab_info->grab_op))
             {
               if (window->tile_mode != META_TILE_NONE)
                 meta_window_tile (window);
               else if (xroot == window->screen->xroot)
-                update_move (window,
+                update_move (window, device,
                              state & ShiftMask,
                              x_root, y_root);
             }
-          else if (meta_grab_op_is_resizing (window->display->grab_op))
+          else if (meta_grab_op_is_resizing (grab_info->grab_op))
             {
               if (xroot == window->screen->xroot)
-                update_resize (window,
+                update_resize (window, device,
                                state & ShiftMask,
                                x_root, y_root,
                                TRUE);
@@ -9216,32 +9323,32 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
             }
         }
 
-      meta_display_end_grab_op (window->display,
+      meta_display_end_grab_op (window->display, device,
                                 meta_input_event_get_time (window->display,
                                                            event));
       break;
 
     case MotionNotify:
-      meta_display_check_threshold_reached (window->display,
+      meta_display_check_threshold_reached (window->display, device,
                                             x_root, y_root);
-      if (meta_grab_op_is_moving (window->display->grab_op))
+      if (meta_grab_op_is_moving (grab_info->grab_op))
         {
           if (xroot == window->screen->xroot)
             {
               if (check_use_this_motion_notify (window,
                                                 event))
-                update_move (window,
+                update_move (window, device,
                              state & ShiftMask,
                              x_root, y_root);
             }
         }
-      else if (meta_grab_op_is_resizing (window->display->grab_op))
+      else if (meta_grab_op_is_resizing (grab_info->grab_op))
         {
           if (xroot == window->screen->xroot)
             {
               if (check_use_this_motion_notify (window,
                                                 event))
-                update_resize (window,
+                update_resize (window, device,
                                state & ShiftMask,
                                x_root, y_root,
                                FALSE);
@@ -9414,13 +9521,14 @@ meta_window_same_client (MetaWindow *window,
 void
 meta_window_refresh_resize_popup (MetaWindow *window)
 {
-  if (window->display->grab_op == META_GRAB_OP_NONE)
-    return;
+  MetaGrabInfo *grab_info;
+
+  grab_info = window->cur_grab;
 
-  if (window->display->grab_window != window)
+  if (!grab_info)
     return;
 
-  switch (window->display->grab_op)
+  switch (grab_info->grab_op)
     {
     case META_GRAB_OP_RESIZING_SE:
     case META_GRAB_OP_RESIZING_S:
@@ -9446,29 +9554,29 @@ meta_window_refresh_resize_popup (MetaWindow *window)
       return;
     }
 
-  if (window->display->grab_resize_popup == NULL)
+  if (grab_info->grab_resize_popup == NULL)
     {
       if (window->size_hints.width_inc > 1 ||
           window->size_hints.height_inc > 1)
-        window->display->grab_resize_popup =
+        grab_info->grab_resize_popup =
           meta_ui_resize_popup_new (window->display->xdisplay,
                                     window->screen->number);
     }
 
-  if (window->display->grab_resize_popup != NULL)
+  if (grab_info->grab_resize_popup != NULL)
     {
       MetaRectangle rect;
 
       meta_window_get_client_root_coords (window, &rect);
 
-      meta_ui_resize_popup_set (window->display->grab_resize_popup,
+      meta_ui_resize_popup_set (grab_info->grab_resize_popup,
                                 rect,
                                 window->size_hints.base_width,
                                 window->size_hints.base_height,
                                 window->size_hints.width_inc,
                                 window->size_hints.height_inc);
 
-      meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
+      meta_ui_resize_popup_set_showing (grab_info->grab_resize_popup,
                                         TRUE);
     }
 }
@@ -9591,17 +9699,22 @@ meta_window_is_ancestor_of_transient (MetaWindow *window,
  */
 static gboolean
 warp_grab_pointer (MetaWindow          *window,
+                   MetaDevice          *device,
                    MetaGrabOp           grab_op,
                    int                 *x,
                    int                 *y)
 {
   MetaRectangle  rect;
   MetaDisplay   *display;
+  MetaGrabInfo *grab_info;
 
   display = window->display;
+  grab_info = meta_display_get_grab_info (display, device);
 
   /* We may not have done begin_grab_op yet, i.e. may not be in a grab
    */
+  if (!grab_info)
+    grab_info = meta_display_create_grab_info (display, device);
 
   meta_window_get_outer_rect (window, &rect);
 
@@ -9674,12 +9787,12 @@ warp_grab_pointer (MetaWindow          *window,
    * events generated by the XWarpPointer() call below don't cause complete
    * funkiness.  See bug 124582 and bug 122670.
    */
-  display->grab_anchor_root_x = *x;
-  display->grab_anchor_root_y = *y;
-  display->grab_latest_motion_x = *x;
-  display->grab_latest_motion_y = *y;
+  grab_info->grab_anchor_root_x = *x;
+  grab_info->grab_anchor_root_y = *y;
+  grab_info->grab_latest_motion_x = *x;
+  grab_info->grab_latest_motion_y = *y;
   meta_window_get_client_root_coords (window,
-                                      &display->grab_anchor_window_pos);
+                                      &grab_info->grab_anchor_window_pos);
 
   XWarpPointer (display->xdisplay,
                 None,
@@ -9699,18 +9812,20 @@ warp_grab_pointer (MetaWindow          *window,
 
 void
 meta_window_begin_grab_op (MetaWindow *window,
+                           MetaDevice *device,
                            MetaGrabOp  op,
                            gboolean    frame_action,
                            guint32     timestamp)
 {
   int x, y;
 
-  warp_grab_pointer (window,
+  warp_grab_pointer (window, device,
                      op, &x, &y);
 
   meta_display_begin_grab_op (window->display,
                               window->screen,
                               window,
+                              device,
                               op,
                               FALSE,
                               frame_action,
@@ -9722,12 +9837,17 @@ meta_window_begin_grab_op (MetaWindow *window,
 
 void
 meta_window_update_keyboard_resize (MetaWindow *window,
+                                    MetaDevice *device,
                                     gboolean    update_cursor)
 {
+  MetaGrabInfo *grab_info;
   int x, y;
 
-  warp_grab_pointer (window,
-                     window->display->grab_op,
+  grab_info = meta_display_get_grab_info (window->display, device);
+  g_assert (grab_info != NULL);
+
+  warp_grab_pointer (window, device,
+                     grab_info->grab_op,
                      &x, &y);
 
   if (update_cursor)
@@ -9736,21 +9856,25 @@ meta_window_update_keyboard_resize (MetaWindow *window,
       /* FIXME: Using CurrentTime is really bad mojo */
       timestamp = CurrentTime;
       meta_display_set_grab_op_cursor (window->display,
-                                       NULL,
-                                       window->display->grab_op,
+                                       NULL, device,
+                                       grab_info->grab_op,
                                        TRUE,
-                                       window->display->grab_xwindow,
+                                       grab_info->grab_xwindow,
                                        timestamp);
     }
 }
 
 void
-meta_window_update_keyboard_move (MetaWindow *window)
+meta_window_update_keyboard_move (MetaWindow *window,
+                                  MetaDevice *device)
 {
+  MetaGrabInfo *grab_info;
   int x, y;
 
-  warp_grab_pointer (window,
-                     window->display->grab_op,
+  grab_info = meta_display_get_grab_info (window->display, device);
+
+  warp_grab_pointer (window, device,
+                     grab_info->grab_op,
                      &x, &y);
 }
 
@@ -10516,3 +10640,18 @@ meta_window_is_attached_dialog (MetaWindow *window)
 {
   return window->attached;
 }
+
+/* Guesses the better device to grab on if a grab is to be started,
+ * only should be be used in circumstances where we don't know a
+ * device at all.
+ */
+MetaDevice *
+meta_window_guess_grab_pointer (MetaWindow *window)
+{
+  /* FIXME: This ought to be the very last resort, ideally
+   * the current/last focus device should be used to find
+   * this out, or the client pointer.
+   */
+  return meta_device_map_lookup (window->display->device_map,
+                                 META_CORE_POINTER_ID);
+}
diff --git a/src/core/workspace.c b/src/core/workspace.c
index 66f9dd0..8bc64d5 100644
--- a/src/core/workspace.c
+++ b/src/core/workspace.c
@@ -538,7 +538,9 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
   MetaWorkspaceLayout layout1, layout2;
   gint num_workspaces, current_space, new_space;
   MetaMotionDirection direction;
-  
+  MetaGrabInfo *grab_info;
+  GHashTableIter iter;
+
   meta_verbose ("Activating workspace %d\n",
                 meta_workspace_index (workspace));
   
@@ -547,7 +549,7 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
 
   /* Free any cached pointers to the workspaces's edges from
    * a current resize or move operation */
-  meta_display_cleanup_edges (workspace->screen->display);
+  meta_display_cleanup_edges (workspace->screen->display, workspace->screen);
 
   if (workspace->screen->active_workspace)
     workspace_switch_sound (workspace->screen->active_workspace, workspace);
@@ -570,10 +572,22 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
     return;
 
   move_window = NULL;
-  if (workspace->screen->display->grab_op == META_GRAB_OP_MOVING ||
-      workspace->screen->display->grab_op == META_GRAB_OP_KEYBOARD_MOVING)
-    move_window = workspace->screen->display->grab_window;
-      
+
+  /* FIXME: not quite multidevice friendly, but the whole
+   * "move window to another workspace" isn't.
+   */
+  g_hash_table_iter_init (&iter, workspace->screen->display->current_grabs);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &grab_info))
+    {
+      if (grab_info->grab_op == META_GRAB_OP_MOVING ||
+          grab_info->grab_op == META_GRAB_OP_KEYBOARD_MOVING)
+        {
+          move_window = grab_info->grab_window;
+          break;
+        }
+    }
+
   if (move_window != NULL)
     {
       if (move_window->on_all_workspaces)
@@ -774,7 +788,7 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace)
   /* If we are in the middle of a resize or move operation, we
    * might have cached pointers to the workspace's edges */
   if (workspace == workspace->screen->active_workspace)
-    meta_display_cleanup_edges (workspace->screen->display);
+    meta_display_cleanup_edges (workspace->screen->display, workspace->screen);
 
   g_free (workspace->work_area_monitor);
   workspace->work_area_monitor = NULL;
diff --git a/src/meta/display.h b/src/meta/display.h
index 11f85ea..73d758e 100644
--- a/src/meta/display.h
+++ b/src/meta/display.h
@@ -112,6 +112,7 @@ MetaWindow* meta_display_get_tab_current (MetaDisplay   *display,
 gboolean meta_display_begin_grab_op (MetaDisplay *display,
                                      MetaScreen  *screen,
                                      MetaWindow  *window,
+                                     MetaDevice  *device,
                                      MetaGrabOp   op,
                                      gboolean     pointer_already_grabbed,
                                      gboolean     frame_action,
@@ -121,10 +122,14 @@ gboolean meta_display_begin_grab_op (MetaDisplay *display,
                                      int          root_x,
                                      int          root_y);
 void     meta_display_end_grab_op   (MetaDisplay *display,
+                                     MetaDevice  *device,
                                      guint32      timestamp);
 
 MetaGrabOp meta_display_get_grab_op (MetaDisplay *display);
 
+MetaGrabOp meta_display_get_device_grab_op (MetaDisplay *display,
+                                            MetaDevice  *device);
+
 MetaKeyBindingAction meta_display_get_keybinding_action (MetaDisplay  *display,
                                                          unsigned int  keycode,
                                                          unsigned long mask);
diff --git a/src/ui/frames.c b/src/ui/frames.c
index 83003ee..3149b21 100644
--- a/src/ui/frames.c
+++ b/src/ui/frames.c
@@ -1432,7 +1432,9 @@ meta_frames_button_press_event (GtkWidget      *widget,
   MetaFrames *frames;
   MetaFrameControl control;
   Display *display;
-  
+  GdkDevice *device;
+  int device_id;
+
   frames = META_FRAMES (widget);
   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
@@ -1465,7 +1467,10 @@ meta_frames_button_press_event (GtkWidget      *widget,
   /* don't do the rest of this if on client area */
   if (control == META_FRAME_CONTROL_CLIENT_AREA)
     return FALSE; /* not on the frame, just passed through from client */
-  
+
+  device = gdk_event_get_device ((GdkEvent *) event);
+  device_id = gdk_x11_device_get_id (device);
+
   /* We want to shade even if we have a GrabOp, since we'll have a move grab
    * if we double click the titlebar.
    */
@@ -1473,13 +1478,13 @@ meta_frames_button_press_event (GtkWidget      *widget,
       event->button == 1 &&
       event->type == GDK_2BUTTON_PRESS)
     {
-      meta_core_end_grab_op (display, event->time);
+      meta_core_end_grab_op (display, device_id, event->time);
       return meta_frame_double_click_event (frame, event);
     }
 
-  if (meta_core_get_grab_op (display) !=
+  if (meta_core_frame_has_grab (display, frame->xwindow, NULL, NULL) !=
       META_GRAB_OP_NONE)
-    return FALSE; /* already up to something */  
+    return FALSE; /* already up to something */
 
   if (event->button == 1 &&
       (control == META_FRAME_CONTROL_MAXIMIZE ||
@@ -1538,6 +1543,7 @@ meta_frames_button_press_event (GtkWidget      *widget,
 
       meta_core_begin_grab_op (display,
                                frame->xwindow,
+                               device_id,
                                op,
                                TRUE,
                                TRUE,
@@ -1623,6 +1629,7 @@ meta_frames_button_press_event (GtkWidget      *widget,
 
       meta_core_begin_grab_op (display,
                                frame->xwindow,
+                               device_id,
                                op,
                                TRUE,
                                TRUE,
@@ -1645,6 +1652,7 @@ meta_frames_button_press_event (GtkWidget      *widget,
         {          
           meta_core_begin_grab_op (display,
                                    frame->xwindow,
+                                   device_id,
                                    META_GRAB_OP_MOVING,
                                    TRUE,
                                    TRUE,
@@ -1668,28 +1676,28 @@ meta_frames_button_press_event (GtkWidget      *widget,
 }
 
 void
-meta_frames_notify_menu_hide (MetaFrames *frames)
+meta_frames_notify_menu_hide (MetaFrames *frames,
+                              Window      client_xwindow)
 {
   Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
-  if (meta_core_get_grab_op (display) ==
+  Window frame_xwindow;
+  int device_id;
+
+  frame_xwindow = meta_core_get_frame (display, client_xwindow);
+
+  if (frame_xwindow != None &&
+      meta_core_frame_has_grab (display, frame_xwindow, &device_id, NULL) ==
       META_GRAB_OP_CLICKING_MENU)
     {
-      Window grab_frame;
+      MetaUIFrame *frame;
 
-      grab_frame = meta_core_get_grab_frame (display);
+      frame = meta_frames_lookup_window (frames, frame_xwindow);
 
-      if (grab_frame != None)
+      if (frame)
         {
-          MetaUIFrame *frame;
-
-          frame = meta_frames_lookup_window (frames, grab_frame);
-
-          if (frame)
-            {
-              redraw_control (frames, frame,
-                              META_FRAME_CONTROL_MENU);
-              meta_core_end_grab_op (display, CurrentTime);
-            }
+          redraw_control (frames, frame,
+                          META_FRAME_CONTROL_MENU);
+          meta_core_end_grab_op (display, device_id, CurrentTime);
         }
     }
 }
@@ -1702,7 +1710,10 @@ meta_frames_button_release_event    (GtkWidget           *widget,
   MetaFrames *frames;
   MetaGrabOp op;
   Display *display;
-  
+  int grab_button;
+  GdkDevice *device;
+  int device_id, grab_device_id;
+
   frames = META_FRAMES (widget);
   display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
   
@@ -1712,17 +1723,21 @@ meta_frames_button_release_event    (GtkWidget           *widget,
 
   clear_tip (frames);
 
-  op = meta_core_get_grab_op (display);
+  op = meta_core_frame_has_grab (display, frame->xwindow,
+                                 &grab_device_id, &grab_button);
+
+  device = gdk_event_get_device ((GdkEvent *) event);
+  device_id = gdk_x11_device_get_id (device);
 
-  if (op == META_GRAB_OP_NONE)
+  if (op == META_GRAB_OP_NONE ||
+      grab_device_id != device_id)
     return FALSE;
 
   /* We only handle the releases we handled the presses for (things
    * involving frame controls). Window ops that don't require a
    * frame are handled in the Xlib part of the code, display.c/window.c
    */
-  if (frame->xwindow == meta_core_get_grab_frame (display) &&
-      ((int) event->button) == meta_core_get_grab_button (display))
+  if (((int) event->button) == grab_button)
     {
       MetaFrameControl control;
 
@@ -1733,8 +1748,8 @@ meta_frames_button_release_event    (GtkWidget           *widget,
         case META_GRAB_OP_CLICKING_MINIMIZE:
           if (control == META_FRAME_CONTROL_MINIMIZE)
             meta_core_minimize (display, frame->xwindow);
-          
-          meta_core_end_grab_op (display, event->time);
+
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
 
         case META_GRAB_OP_CLICKING_MAXIMIZE:
@@ -1746,67 +1761,67 @@ meta_frames_button_release_event    (GtkWidget           *widget,
                             event->time);      
             meta_core_maximize (display, frame->xwindow);
           }
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
 
         case META_GRAB_OP_CLICKING_UNMAXIMIZE:
           if (control == META_FRAME_CONTROL_UNMAXIMIZE)
             meta_core_unmaximize (display, frame->xwindow);
-          
-          meta_core_end_grab_op (display, event->time);
+
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
           
         case META_GRAB_OP_CLICKING_DELETE:
           if (control == META_FRAME_CONTROL_DELETE)
             meta_core_delete (display, frame->xwindow, event->time);
-          
-          meta_core_end_grab_op (display, event->time);
+
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
           
         case META_GRAB_OP_CLICKING_MENU:
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
 
         case META_GRAB_OP_CLICKING_SHADE:
           if (control == META_FRAME_CONTROL_SHADE)
             meta_core_shade (display, frame->xwindow, event->time);
-          
-          meta_core_end_grab_op (display, event->time);
+
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
  
         case META_GRAB_OP_CLICKING_UNSHADE:
           if (control == META_FRAME_CONTROL_UNSHADE)
             meta_core_unshade (display, frame->xwindow, event->time);
 
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
 
         case META_GRAB_OP_CLICKING_ABOVE:
           if (control == META_FRAME_CONTROL_ABOVE)
             meta_core_make_above (display, frame->xwindow);
-          
-          meta_core_end_grab_op (display, event->time);
+
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
  
         case META_GRAB_OP_CLICKING_UNABOVE:
           if (control == META_FRAME_CONTROL_UNABOVE)
             meta_core_unmake_above (display, frame->xwindow);
 
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
 
         case META_GRAB_OP_CLICKING_STICK:
           if (control == META_FRAME_CONTROL_STICK)
             meta_core_stick (display, frame->xwindow);
 
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
  
         case META_GRAB_OP_CLICKING_UNSTICK:
           if (control == META_FRAME_CONTROL_UNSTICK)
             meta_core_unstick (display, frame->xwindow);
 
-          meta_core_end_grab_op (display, event->time);
+          meta_core_end_grab_op (display, device_id, event->time);
           break;
           
         default:
@@ -1952,8 +1967,8 @@ meta_frames_motion_notify_event     (GtkWidget           *widget,
 
   frames->last_motion_frame = frame;
 
-  grab_op = meta_core_get_grab_op (display);
-  
+  grab_op = meta_core_frame_has_grab (display, frame->xwindow, NULL, NULL);
+
   switch (grab_op)
     {
     case META_GRAB_OP_CLICKING_MENU:
@@ -2368,7 +2383,6 @@ meta_frames_paint (MetaFrames   *frames,
   GdkPixbuf *icon;
   int w, h;
   MetaButtonState button_states[META_BUTTON_TYPE_LAST];
-  Window grab_frame;
   int i;
   MetaButtonLayout button_layout;
   MetaGrabOp grab_op;
@@ -2380,11 +2394,8 @@ meta_frames_paint (MetaFrames   *frames,
   for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
     button_states[i] = META_BUTTON_STATE_NORMAL;
 
-  grab_frame = meta_core_get_grab_frame (display);
-  grab_op = meta_core_get_grab_op (display);
-  if (grab_frame != frame->xwindow)
-    grab_op = META_GRAB_OP_NONE;
-  
+  grab_op = meta_core_frame_has_grab (display, frame->xwindow, NULL, NULL);
+
   /* Set prelight state */
   switch (frame->prelit_control)
     {
diff --git a/src/ui/frames.h b/src/ui/frames.h
index ef9733a..a9d60e0 100644
--- a/src/ui/frames.h
+++ b/src/ui/frames.h
@@ -165,7 +165,8 @@ void meta_frames_move_resize_frame (MetaFrames *frames,
 void meta_frames_queue_draw (MetaFrames *frames,
                              Window      xwindow);
 
-void meta_frames_notify_menu_hide (MetaFrames *frames);
+void meta_frames_notify_menu_hide (MetaFrames *frames,
+                                   Window      client_xwindow);
 
 Window meta_frames_get_moving_frame (MetaFrames *frames);
 
diff --git a/src/ui/menu.c b/src/ui/menu.c
index eef421a..b3cad32 100644
--- a/src/ui/menu.c
+++ b/src/ui/menu.c
@@ -137,7 +137,7 @@ menu_closed (GtkMenu *widget,
   
   menu = data;
 
-  meta_frames_notify_menu_hide (menu->frames);
+  meta_frames_notify_menu_hide (menu->frames, menu->client_xwindow);
   (* menu->func) (menu,
                   GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
                   menu->client_xwindow,
@@ -157,7 +157,7 @@ activate_cb (GtkWidget *menuitem, gpointer data)
   
   md = data;
 
-  meta_frames_notify_menu_hide (md->menu->frames);
+  meta_frames_notify_menu_hide (md->menu->frames, md->menu->client_xwindow);
   (* md->menu->func) (md->menu,
                       GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
                       md->menu->client_xwindow,
diff --git a/src/ui/tile-preview.c b/src/ui/tile-preview.c
index f09c62a..e0fecab 100644
--- a/src/ui/tile-preview.c
+++ b/src/ui/tile-preview.c
@@ -171,6 +171,7 @@ meta_tile_preview_free (MetaTilePreview *preview)
 
 void
 meta_tile_preview_show (MetaTilePreview *preview,
+                        MetaDevice      *pointer,
                         MetaRectangle   *tile_rect)
 {
   GdkWindow *window;
@@ -187,6 +188,7 @@ meta_tile_preview_show (MetaTilePreview *preview,
   window = gtk_widget_get_window (preview->preview_window);
   meta_core_lower_beneath_grab_window (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
                                        GDK_WINDOW_XID (window),
+                                       meta_device_get_id (pointer),
                                        gtk_get_current_event_time ());
 
   old_rect.x = old_rect.y = 0;
diff --git a/src/ui/tile-preview.h b/src/ui/tile-preview.h
index 740f41f..b331f79 100644
--- a/src/ui/tile-preview.h
+++ b/src/ui/tile-preview.h
@@ -24,6 +24,7 @@
 #define META_TILE_PREVIEW_H
 
 #include <meta/boxes.h>
+#include <meta/device.h>
 
 typedef struct _MetaTilePreview MetaTilePreview;
 
@@ -31,6 +32,7 @@ MetaTilePreview   *meta_tile_preview_new    (int                screen_number,
                                              gboolean           composited);
 void               meta_tile_preview_free   (MetaTilePreview   *preview);
 void               meta_tile_preview_show   (MetaTilePreview   *preview,
+                                             MetaDevice        *pointer,
                                              MetaRectangle     *rect);
 void               meta_tile_preview_hide   (MetaTilePreview   *preview);
 Window             meta_tile_preview_get_xwindow (MetaTilePreview   *preview,



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