[mutter/wip/carlosg/clutter-seat: 48/69] x11: Implement ClutterSeat



commit 0dcece8a874ceca57033f06fd0e863aa361db2bf
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Sep 24 11:24:48 2019 +0200

    x11: Implement ClutterSeat
    
    The ClutterDeviceManager signaling becomes implemented on top of this.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/852

 src/backends/x11/meta-clutter-backend-x11.c |   45 +-
 src/backends/x11/meta-device-manager-x11.c  | 2014 +------------------------
 src/backends/x11/meta-device-manager-x11.h  |   13 +-
 src/backends/x11/meta-seat-x11.c            | 2115 +++++++++++++++++++++++++++
 src/backends/x11/meta-seat-x11.h            |   40 +
 src/meson.build                             |    2 +
 6 files changed, 2249 insertions(+), 1980 deletions(-)
---
diff --git a/src/backends/x11/meta-clutter-backend-x11.c b/src/backends/x11/meta-clutter-backend-x11.c
index b6334de8b..d0654fa8a 100644
--- a/src/backends/x11/meta-clutter-backend-x11.c
+++ b/src/backends/x11/meta-clutter-backend-x11.c
@@ -31,6 +31,7 @@
 #include "backends/x11/meta-clutter-backend-x11.h"
 #include "backends/x11/meta-device-manager-x11.h"
 #include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-seat-x11.h"
 #include "backends/x11/meta-xkb-a11y-x11.h"
 #include "backends/x11/nested/meta-stage-x11-nested.h"
 #include "clutter/clutter-mutter.h"
@@ -43,6 +44,7 @@ struct _MetaClutterBackendX11
   ClutterBackendX11 parent;
   MetaKeymapX11 *keymap;
   MetaDeviceManagerX11 *device_manager;
+  MetaSeatX11 *core_seat;
 };
 
 G_DEFINE_TYPE (MetaClutterBackendX11, meta_clutter_backend_x11,
@@ -119,7 +121,6 @@ meta_clutter_backend_x11_translate_event (ClutterBackend *backend,
                                           ClutterEvent   *event)
 {
   MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
-  MetaDeviceManagerX11 *device_manager_x11;
   MetaStageX11 *stage_x11;
   ClutterBackendClass *clutter_backend_class;
 
@@ -135,28 +136,12 @@ meta_clutter_backend_x11_translate_event (ClutterBackend *backend,
   if (meta_stage_x11_translate_event (stage_x11, native, event))
     return TRUE;
 
-  device_manager_x11 = META_DEVICE_MANAGER_X11 (backend_x11->device_manager);
-  if (meta_device_manager_x11_translate_event (device_manager_x11,
-                                               native, event))
+  if (meta_seat_x11_translate_event (backend_x11->core_seat, native, event))
     return TRUE;
 
   return FALSE;
 }
 
-static void
-on_keymap_state_change (MetaKeymapX11 *keymap_x11,
-                        gpointer       data)
-{
-  ClutterDeviceManager *device_manager = CLUTTER_DEVICE_MANAGER (data);
-  ClutterKbdA11ySettings kbd_a11y_settings;
-
-  /* On keymaps state change, just reapply the current settings, it'll
-   * take care of enabling/disabling mousekeys based on NumLock state.
-   */
-  clutter_device_manager_get_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
-  meta_device_manager_x11_apply_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
-}
-
 static void
 meta_clutter_backend_x11_init_events (ClutterBackend *backend)
 {
@@ -175,25 +160,30 @@ meta_clutter_backend_x11_init_events (ClutterBackend *backend)
       if (XIQueryVersion (clutter_x11_get_default_display (),
                           &major, &minor) != BadRequest)
         {
+          backend_x11->core_seat =
+            meta_seat_x11_new (event_base,
+                               META_VIRTUAL_CORE_POINTER_ID,
+                               META_VIRTUAL_CORE_KEYBOARD_ID);
+
           g_debug ("Creating XI2 device manager");
           backend_x11->device_manager =
             g_object_new (META_TYPE_DEVICE_MANAGER_X11,
                           "backend", backend_x11,
-                          "opcode", event_base,
+                          "seat", backend_x11->core_seat,
                           NULL);
         }
     }
 
-  if (!backend_x11->device_manager)
+  if (!backend_x11->core_seat)
     g_error ("No XInput 2.3 support");
+}
+
+static ClutterSeat *
+meta_clutter_backend_x11_get_default_seat (ClutterBackend *backend)
+{
+  MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend);
 
-  backend_x11->keymap = g_object_new (META_TYPE_KEYMAP_X11,
-                                      "backend", backend_x11,
-                                      NULL);
-  g_signal_connect (backend_x11->keymap,
-                    "state-changed",
-                    G_CALLBACK (on_keymap_state_change),
-                    backend_x11->device_manager);
+  return CLUTTER_SEAT (backend_x11->core_seat);
 }
 
 static void
@@ -214,4 +204,5 @@ meta_clutter_backend_x11_class_init (MetaClutterBackendX11Class *klass)
   clutter_backend_class->get_keymap = meta_clutter_backend_x11_get_keymap;
   clutter_backend_class->translate_event = meta_clutter_backend_x11_translate_event;
   clutter_backend_class->init_events = meta_clutter_backend_x11_init_events;
+  clutter_backend_class->get_default_seat = meta_clutter_backend_x11_get_default_seat;
 }
diff --git a/src/backends/x11/meta-device-manager-x11.c b/src/backends/x11/meta-device-manager-x11.c
index db1fadc3e..946aea5b0 100644
--- a/src/backends/x11/meta-device-manager-x11.c
+++ b/src/backends/x11/meta-device-manager-x11.c
@@ -28,6 +28,7 @@
 #include "backends/x11/meta-input-device-x11.h"
 #include "backends/x11/meta-input-device-tool-x11.h"
 #include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-seat-x11.h"
 #include "backends/x11/meta-stage-x11.h"
 #include "backends/x11/meta-virtual-input-device-x11.h"
 #include "backends/x11/meta-xkb-a11y-x11.h"
@@ -40,1026 +41,38 @@ enum
 {
   PROP_0,
 
-  PROP_OPCODE,
+  PROP_SEAT,
 
   PROP_LAST
 };
 
 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
 
-static const char *clutter_input_axis_atom_names[] = {
-  "Abs X",              /* CLUTTER_INPUT_AXIS_X */
-  "Abs Y",              /* CLUTTER_INPUT_AXIS_Y */
-  "Abs Pressure",       /* CLUTTER_INPUT_AXIS_PRESSURE */
-  "Abs Tilt X",         /* CLUTTER_INPUT_AXIS_XTILT */
-  "Abs Tilt Y",         /* CLUTTER_INPUT_AXIS_YTILT */
-  "Abs Wheel",          /* CLUTTER_INPUT_AXIS_WHEEL */
-  "Abs Distance",       /* CLUTTER_INPUT_AXIS_DISTANCE */
-};
-
-#define N_AXIS_ATOMS    G_N_ELEMENTS (clutter_input_axis_atom_names)
-
-static const char *wacom_type_atoms[] = {
-    "STYLUS",
-    "CURSOR",
-    "ERASER",
-    "PAD",
-    "TOUCH"
-};
-#define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms)
-
-enum
-{
-    WACOM_TYPE_STYLUS,
-    WACOM_TYPE_CURSOR,
-    WACOM_TYPE_ERASER,
-    WACOM_TYPE_PAD,
-    WACOM_TYPE_TOUCH,
-};
-
-enum
-{
-  PAD_AXIS_FIRST  = 3, /* First axes are always x/y/pressure, ignored in pads */
-  PAD_AXIS_STRIP1 = PAD_AXIS_FIRST,
-  PAD_AXIS_STRIP2,
-  PAD_AXIS_RING1,
-  PAD_AXIS_RING2,
-};
-
-static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
-
-G_DEFINE_TYPE (MetaDeviceManagerX11,
-               meta_device_manager_x11,
-               CLUTTER_TYPE_DEVICE_MANAGER)
-
-static void
-meta_device_manager_x11_copy_event_data (ClutterDeviceManager *device_manager,
-                                         const ClutterEvent   *src,
-                                         ClutterEvent         *dest)
-{
-  gpointer event_x11;
-
-  event_x11 = _clutter_event_get_platform_data (src);
-  if (event_x11 != NULL)
-    _clutter_event_set_platform_data (dest, meta_event_x11_copy (event_x11));
-}
-
-static void
-meta_device_manager_x11_free_event_data (ClutterDeviceManager *device_manager,
-                                         ClutterEvent         *event)
-{
-  gpointer event_x11;
-
-  event_x11 = _clutter_event_get_platform_data (event);
-  if (event_x11 != NULL)
-    meta_event_x11_free (event_x11);
-}
-
-static void
-translate_valuator_class (Display             *xdisplay,
-                          ClutterInputDevice  *device,
-                          XIValuatorClassInfo *class)
-{
-  static gboolean atoms_initialized = FALSE;
-  ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
-
-  if (G_UNLIKELY (!atoms_initialized))
-    {
-      XInternAtoms (xdisplay,
-                    (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
-                    False,
-                    clutter_input_axis_atoms);
-
-      atoms_initialized = TRUE;
-    }
-
-  for (i = 0;
-       i < N_AXIS_ATOMS;
-       i += 1)
-    {
-      if (clutter_input_axis_atoms[i] == class->label)
-        {
-          axis = i + 1;
-          break;
-        }
-    }
-
-  _clutter_input_device_add_axis (device, axis,
-                                  class->min,
-                                  class->max,
-                                  class->resolution);
-
-  g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
-           clutter_input_axis_atom_names[axis],
-           class->min,
-           class->max,
-           class->resolution,
-           device->id);
-}
-
-static void
-translate_device_classes (Display             *xdisplay,
-                          ClutterInputDevice  *device,
-                          XIAnyClassInfo     **classes,
-                          int                  n_classes)
-{
-  int i;
-
-  for (i = 0; i < n_classes; i++)
-    {
-      XIAnyClassInfo *class_info = classes[i];
-
-      switch (class_info->type)
-        {
-        case XIKeyClass:
-          {
-            XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
-            int j;
-
-            _clutter_input_device_set_n_keys (device,
-                                              key_info->num_keycodes);
-
-            for (j = 0; j < key_info->num_keycodes; j++)
-              {
-                clutter_input_device_set_key (device, j,
-                                              key_info->keycodes[i],
-                                              0);
-              }
-          }
-          break;
-
-        case XIValuatorClass:
-          translate_valuator_class (xdisplay, device,
-                                    (XIValuatorClassInfo *) class_info);
-          break;
-
-        case XIScrollClass:
-          {
-            XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
-            ClutterScrollDirection direction;
-
-            if (scroll_info->scroll_type == XIScrollTypeVertical)
-              direction = CLUTTER_SCROLL_DOWN;
-            else
-              direction = CLUTTER_SCROLL_RIGHT;
-
-            g_debug ("Scroll valuator %d: %s, increment: %f",
-                     scroll_info->number,
-                     scroll_info->scroll_type == XIScrollTypeVertical
-                     ? "vertical"
-                     : "horizontal",
-                     scroll_info->increment);
-
-            _clutter_input_device_add_scroll_info (device,
-                                                   scroll_info->number,
-                                                   direction,
-                                                   scroll_info->increment);
-          }
-          break;
-
-        default:
-          break;
-        }
-    }
-}
-
-static gboolean
-is_touch_device (XIAnyClassInfo         **classes,
-                 int                      n_classes,
-                 ClutterInputDeviceType  *device_type,
-                 uint32_t                *n_touch_points)
-{
-  int i;
-
-  for (i = 0; i < n_classes; i++)
-    {
-      XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
-
-      if (class->type != XITouchClass)
-        continue;
-
-      if (class->num_touches > 0)
-        {
-          if (class->mode == XIDirectTouch)
-            *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
-          else if (class->mode == XIDependentTouch)
-            *device_type = CLUTTER_TOUCHPAD_DEVICE;
-          else
-            continue;
-
-          *n_touch_points = class->num_touches;
-
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static gboolean
-is_touchpad_device (XIDeviceInfo *info)
-{
-  gulong nitems, bytes_after;
-  uint32_t *data = NULL;
-  int rc, format;
-  Atom type;
-  Atom prop;
-
-  prop = XInternAtom (clutter_x11_get_default_display (), "libinput Tapping Enabled", True);
-  if (prop == None)
-    return FALSE;
-
-  clutter_x11_trap_x_errors ();
-  rc = XIGetProperty (clutter_x11_get_default_display (),
-                      info->deviceid,
-                      prop,
-                      0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
-                      (guchar **) &data);
-  clutter_x11_untrap_x_errors ();
-
-  /* We don't care about the data */
-  XFree (data);
-
-  if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1)
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-get_device_ids (XIDeviceInfo  *info,
-                char         **vendor_id,
-                char         **product_id)
-{
-  gulong nitems, bytes_after;
-  uint32_t *data = NULL;
-  int rc, format;
-  Atom type;
-
-  clutter_x11_trap_x_errors ();
-  rc = XIGetProperty (clutter_x11_get_default_display (),
-                      info->deviceid,
-                      XInternAtom (clutter_x11_get_default_display (), "Device Product ID", False),
-                      0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
-                      (guchar **) &data);
-  clutter_x11_untrap_x_errors ();
-
-  if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2)
-    {
-      XFree (data);
-      return FALSE;
-    }
-
-  if (vendor_id)
-    *vendor_id = g_strdup_printf ("%.4x", data[0]);
-  if (product_id)
-    *product_id = g_strdup_printf ("%.4x", data[1]);
-
-  XFree (data);
-
-  return TRUE;
-}
-
-static char *
-get_device_node_path (XIDeviceInfo *info)
-{
-  gulong nitems, bytes_after;
-  guchar *data;
-  int rc, format;
-  Atom prop, type;
-  char *node_path;
-
-  prop = XInternAtom (clutter_x11_get_default_display (), "Device Node", False);
-  if (prop == None)
-    return NULL;
-
-  clutter_x11_trap_x_errors ();
-
-  rc = XIGetProperty (clutter_x11_get_default_display (),
-                      info->deviceid, prop, 0, 1024, False,
-                      XA_STRING, &type, &format, &nitems, &bytes_after,
-                      (guchar **) &data);
-
-  if (clutter_x11_untrap_x_errors ())
-    return NULL;
-
-  if (rc != Success || type != XA_STRING || format != 8)
-    {
-      XFree (data);
-      return FALSE;
-    }
-
-  node_path = g_strdup ((char *) data);
-  XFree (data);
-
-  return node_path;
-}
-
-static void
-get_pad_features (XIDeviceInfo *info,
-                  uint32_t     *n_rings,
-                  uint32_t     *n_strips)
-{
-  int i, rings = 0, strips = 0;
-
-  for (i = PAD_AXIS_FIRST; i < info->num_classes; i++)
-    {
-      XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i];
-      int axis = valuator->number;
-
-      if (valuator->type != XIValuatorClass)
-        continue;
-      if (valuator->max <= 1)
-        continue;
-
-      /* Ring/strip axes are fixed in pad devices as handled by the
-       * wacom driver. Match those to detect pad features.
-       */
-      if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2)
-        strips++;
-      else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2)
-        rings++;
-    }
-
-  *n_rings = rings;
-  *n_strips = strips;
-}
-
-/* The Wacom driver exports the tool type as property. Use that over
-   guessing based on the device name */
-static gboolean
-guess_source_from_wacom_type (XIDeviceInfo            *info,
-                              ClutterInputDeviceType  *source_out)
-{
-  gulong nitems, bytes_after;
-  uint32_t *data = NULL;
-  int rc, format;
-  Atom type;
-  Atom prop;
-  Atom device_type;
-  Atom types[N_WACOM_TYPE_ATOMS];
-
-  prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Tool Type", True);
-  if (prop == None)
-    return FALSE;
-
-  clutter_x11_trap_x_errors ();
-  rc = XIGetProperty (clutter_x11_get_default_display (),
-                      info->deviceid,
-                      prop,
-                      0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after,
-                      (guchar **) &data);
-  clutter_x11_untrap_x_errors ();
-
-  if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1)
-    {
-      XFree (data);
-      return FALSE;
-    }
-
-  device_type = *data;
-  XFree (data);
-
-  if (device_type == 0)
-      return FALSE;
-
-  rc = XInternAtoms (clutter_x11_get_default_display (),
-                     (char **)wacom_type_atoms,
-                     N_WACOM_TYPE_ATOMS,
-                     False,
-                     types);
-  if (rc == 0)
-      return FALSE;
-
-  if (device_type == types[WACOM_TYPE_STYLUS])
-    {
-      *source_out = CLUTTER_PEN_DEVICE;
-    }
-  else if (device_type == types[WACOM_TYPE_CURSOR])
-    {
-      *source_out = CLUTTER_CURSOR_DEVICE;
-    }
-  else if (device_type == types[WACOM_TYPE_ERASER])
-    {
-      *source_out = CLUTTER_ERASER_DEVICE;
-    }
-  else if (device_type == types[WACOM_TYPE_PAD])
-    {
-      *source_out = CLUTTER_PAD_DEVICE;
-    }
-  else if (device_type == types[WACOM_TYPE_TOUCH])
-    {
-        uint32_t num_touches = 0;
-
-        if (!is_touch_device (info->classes, info->num_classes,
-                              source_out, &num_touches))
-            *source_out = CLUTTER_TOUCHSCREEN_DEVICE;
-    }
-  else
-    {
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-static ClutterInputDevice *
-create_device (MetaDeviceManagerX11 *manager_xi2,
-               ClutterBackend       *backend,
-               XIDeviceInfo         *info)
-{
-  ClutterInputDeviceType source, touch_source;
-  ClutterInputDevice *retval;
-  ClutterInputMode mode;
-  gboolean is_enabled;
-  uint32_t num_touches = 0, num_rings = 0, num_strips = 0;
-  char *vendor_id = NULL, *product_id = NULL, *node_path = NULL;
-
-  if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
-    {
-      source = CLUTTER_KEYBOARD_DEVICE;
-    }
-  else if (is_touchpad_device (info))
-    {
-      source = CLUTTER_TOUCHPAD_DEVICE;
-    }
-  else if (info->use == XISlavePointer &&
-           is_touch_device (info->classes, info->num_classes,
-                            &touch_source,
-                            &num_touches))
-    {
-      source = touch_source;
-    }
-  else if (!guess_source_from_wacom_type (info, &source))
-    {
-      char *name;
-
-      name = g_ascii_strdown (info->name, -1);
-
-      if (strstr (name, "eraser") != NULL)
-        source = CLUTTER_ERASER_DEVICE;
-      else if (strstr (name, "cursor") != NULL)
-        source = CLUTTER_CURSOR_DEVICE;
-      else if (strstr (name, " pad") != NULL)
-        source = CLUTTER_PAD_DEVICE;
-      else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
-        source = CLUTTER_PEN_DEVICE;
-      else if (strstr (name, "touchpad") != NULL)
-        source = CLUTTER_TOUCHPAD_DEVICE;
-      else
-        source = CLUTTER_POINTER_DEVICE;
-
-      g_free (name);
-    }
-
-  switch (info->use)
-    {
-    case XIMasterKeyboard:
-    case XIMasterPointer:
-      mode = CLUTTER_INPUT_MODE_MASTER;
-      is_enabled = TRUE;
-      break;
-
-    case XISlaveKeyboard:
-    case XISlavePointer:
-      mode = CLUTTER_INPUT_MODE_SLAVE;
-      is_enabled = FALSE;
-      break;
-
-    case XIFloatingSlave:
-    default:
-      mode = CLUTTER_INPUT_MODE_FLOATING;
-      is_enabled = FALSE;
-      break;
-    }
-
-  if (info->use != XIMasterKeyboard &&
-      info->use != XIMasterPointer)
-    {
-      get_device_ids (info, &vendor_id, &product_id);
-      node_path = get_device_node_path (info);
-    }
-
-  if (source == CLUTTER_PAD_DEVICE)
-    {
-      is_enabled = TRUE;
-      get_pad_features (info, &num_rings, &num_strips);
-    }
-
-  retval = g_object_new (META_TYPE_INPUT_DEVICE_X11,
-                         "name", info->name,
-                         "id", info->deviceid,
-                         "has-cursor", (info->use == XIMasterPointer),
-                         "device-manager", manager_xi2,
-                         "device-type", source,
-                         "device-mode", mode,
-                         "backend", backend,
-                         "enabled", is_enabled,
-                         "vendor-id", vendor_id,
-                         "product-id", product_id,
-                         "device-node", node_path,
-                         "n-rings", num_rings,
-                         "n-strips", num_strips,
-                         "n-mode-groups", MAX (num_rings, num_strips),
-                         NULL);
-
-  translate_device_classes (clutter_x11_get_default_display (), retval,
-                            info->classes,
-                            info->num_classes);
-
-#ifdef HAVE_LIBWACOM
-  if (source == CLUTTER_PAD_DEVICE)
-    meta_input_device_x11_ensure_wacom_info (retval, manager_xi2->wacom_db);
-#endif
-
-  g_free (vendor_id);
-  g_free (product_id);
-  g_free (node_path);
-
-  g_debug ("Created device '%s' (id: %d, has-cursor: %s)",
-           info->name,
-           info->deviceid,
-           info->use == XIMasterPointer ? "yes" : "no");
-
-  return retval;
-}
-
-static void
-pad_passive_button_grab (ClutterInputDevice *device)
-{
-  XIGrabModifiers xi_grab_mods = { XIAnyModifier, };
-  XIEventMask xi_event_mask;
-  int device_id, rc;
-
-  device_id = clutter_input_device_get_device_id (device);
-
-  xi_event_mask.deviceid = device_id;
-  xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT);
-  xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len);
-
-  XISetMask (xi_event_mask.mask, XI_Motion);
-  XISetMask (xi_event_mask.mask, XI_ButtonPress);
-  XISetMask (xi_event_mask.mask, XI_ButtonRelease);
-
-  clutter_x11_trap_x_errors ();
-  rc = XIGrabButton (clutter_x11_get_default_display (),
-                     device_id, XIAnyButton,
-                     clutter_x11_get_root_window (), None,
-                     XIGrabModeSync, XIGrabModeSync,
-                     True, &xi_event_mask, 1, &xi_grab_mods);
-  if (rc != 0)
-    {
-      g_warning ("Could not passively grab pad device: %s",
-                 clutter_input_device_get_device_name (device));
-    }
-  else
-    {
-      XIAllowEvents (clutter_x11_get_default_display (),
-                     device_id, XIAsyncDevice,
-                     CLUTTER_CURRENT_TIME);
-    }
-
-  clutter_x11_untrap_x_errors ();
-
-  g_free (xi_event_mask.mask);
-}
-
-static ClutterInputDevice *
-add_device (MetaDeviceManagerX11 *manager_xi2,
-            ClutterBackend       *backend,
-            XIDeviceInfo         *info,
-            gboolean              in_construction)
-{
-  ClutterInputDevice *device;
-
-  device = create_device (manager_xi2, backend, info);
-
-  /* we don't go through the DeviceManager::add_device() vfunc because
-   * that emits the signal, and we only do it conditionally
-   */
-  g_hash_table_replace (manager_xi2->devices_by_id,
-                        GINT_TO_POINTER (info->deviceid),
-                        device);
-
-  if (info->use == XIMasterPointer ||
-      info->use == XIMasterKeyboard)
-    {
-      manager_xi2->master_devices =
-        g_list_prepend (manager_xi2->master_devices, device);
-    }
-  else if (info->use == XISlavePointer ||
-           info->use == XISlaveKeyboard ||
-           info->use == XIFloatingSlave)
-    {
-      manager_xi2->slave_devices =
-        g_list_prepend (manager_xi2->slave_devices, device);
-    }
-  else
-    g_warning ("Unhandled device: %s",
-               clutter_input_device_get_device_name (device));
-
-  if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
-    pad_passive_button_grab (device);
-
-  /* relationships between devices and signal emissions are not
-   * necessary while we're constructing the device manager instance
-   */
-  if (!in_construction)
-    {
-      if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
-        {
-          ClutterInputDevice *master;
-
-          master = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                        GINT_TO_POINTER (info->attachment));
-          _clutter_input_device_set_associated_device (device, master);
-          _clutter_input_device_add_slave (master, device);
-        }
-
-      /* blow the cache */
-      g_slist_free (manager_xi2->all_devices);
-      manager_xi2->all_devices = NULL;
-
-      g_signal_emit_by_name (manager_xi2, "device-added", device);
-    }
-
-  return device;
-}
-
-static void
-remove_device (MetaDeviceManagerX11 *manager_xi2,
-               int                   device_id)
-{
-  ClutterInputDevice *device;
-
-  device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                GINT_TO_POINTER (device_id));
-
-  if (device != NULL)
-    {
-      manager_xi2->master_devices =
-        g_list_remove (manager_xi2->master_devices, device);
-      manager_xi2->slave_devices =
-        g_list_remove (manager_xi2->slave_devices, device);
-
-      /* blow the cache */
-      g_slist_free (manager_xi2->all_devices);
-      manager_xi2->all_devices = NULL;
-
-      g_signal_emit_by_name (manager_xi2, "device-removed", device);
-
-      g_object_run_dispose (G_OBJECT (device));
-
-      g_hash_table_remove (manager_xi2->devices_by_id,
-                           GINT_TO_POINTER (device_id));
-    }
-}
-
-static void
-translate_hierarchy_event (ClutterBackend       *backend,
-                           MetaDeviceManagerX11 *manager_xi2,
-                           XIHierarchyEvent     *ev)
-{
-  int i;
-
-  for (i = 0; i < ev->num_info; i++)
-    {
-      if (ev->info[i].flags & XIDeviceEnabled &&
-          !g_hash_table_lookup (manager_xi2->devices_by_id,
-                                GINT_TO_POINTER (ev->info[i].deviceid)))
-        {
-          XIDeviceInfo *info;
-          int n_devices;
-
-          g_debug ("Hierarchy event: device enabled");
-
-          clutter_x11_trap_x_errors ();
-          info = XIQueryDevice (clutter_x11_get_default_display (),
-                                ev->info[i].deviceid,
-                                &n_devices);
-          clutter_x11_untrap_x_errors ();
-          if (info != NULL)
-            {
-              add_device (manager_xi2, backend, &info[0], FALSE);
-              XIFreeDeviceInfo (info);
-            }
-        }
-      else if (ev->info[i].flags & XIDeviceDisabled)
-        {
-          g_debug ("Hierarchy event: device disabled");
-
-          remove_device (manager_xi2, ev->info[i].deviceid);
-        }
-      else if ((ev->info[i].flags & XISlaveAttached) ||
-               (ev->info[i].flags & XISlaveDetached))
-        {
-          ClutterInputDevice *master, *slave;
-          XIDeviceInfo *info;
-          int n_devices;
-
-          g_debug ("Hierarchy event: slave %s",
-                   (ev->info[i].flags & XISlaveAttached)
-                   ? "attached"
-                   : "detached");
-
-          slave = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                       GINT_TO_POINTER (ev->info[i].deviceid));
-          master = clutter_input_device_get_associated_device (slave);
-
-          /* detach the slave in both cases */
-          if (master != NULL)
-            {
-              _clutter_input_device_remove_slave (master, slave);
-              _clutter_input_device_set_associated_device (slave, NULL);
-            }
-
-          /* and attach the slave to the new master if needed */
-          if (ev->info[i].flags & XISlaveAttached)
-            {
-              clutter_x11_trap_x_errors ();
-              info = XIQueryDevice (clutter_x11_get_default_display (),
-                                    ev->info[i].deviceid,
-                                    &n_devices);
-              clutter_x11_untrap_x_errors ();
-              if (info != NULL)
-                {
-                  master = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                                GINT_TO_POINTER (info->attachment));
-                  if (master != NULL)
-                    {
-                      _clutter_input_device_set_associated_device (slave, master);
-                      _clutter_input_device_add_slave (master, slave);
-                    }
-                  XIFreeDeviceInfo (info);
-                }
-            }
-        }
-    }
-}
-
-static void
-meta_device_manager_x11_select_events (ClutterDeviceManager *manager,
-                                       Window                xwindow,
-                                       XIEventMask          *event_mask)
-{
-  Display *xdisplay;
-
-  xdisplay = clutter_x11_get_default_display ();
-
-  XISelectEvents (xdisplay, xwindow, event_mask, 1);
-}
-
-static ClutterStage *
-get_event_stage (MetaDeviceManagerX11 *manager_xi2,
-                 XIEvent              *xi_event)
-{
-  Window xwindow = None;
-
-  switch (xi_event->evtype)
-    {
-    case XI_KeyPress:
-    case XI_KeyRelease:
-    case XI_ButtonPress:
-    case XI_ButtonRelease:
-    case XI_Motion:
-    case XI_TouchBegin:
-    case XI_TouchUpdate:
-    case XI_TouchEnd:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
-        xwindow = xev->event;
-      }
-      break;
-
-    case XI_Enter:
-    case XI_Leave:
-    case XI_FocusIn:
-    case XI_FocusOut:
-      {
-        XIEnterEvent *xev = (XIEnterEvent *) xi_event;
-
-        xwindow = xev->event;
-      }
-      break;
-
-    default:
-      break;
-    }
-
-  if (xwindow == None)
-    return NULL;
-
-  return meta_x11_get_stage_from_window (xwindow);
-}
-
-/*
- * print_key_sym: Translate a symbol to its printable form if any
- * @symbol: the symbol to translate
- * @buffer: the buffer where to put the translated string
- * @len: size of the buffer
- *
- * Translates @symbol into a printable representation in @buffer, if possible.
- *
- * Return value: The number of bytes of the translated string, 0 if the
- *               symbol can't be printed
- *
- * Note: The code is derived from libX11's src/KeyBind.c
- *       Copyright 1985, 1987, 1998  The Open Group
- *
- * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
- *       does the work for the other keysyms.
- */
-static int
-print_keysym (uint32_t symbol,
-              char    *buffer,
-              int      len)
-{
-  unsigned long high_bytes;
-  unsigned char c;
-
-  high_bytes = symbol >> 8;
-  if (!(len &&
-        ((high_bytes == 0) ||
-         ((high_bytes == 0xFF) &&
-          (((symbol >= CLUTTER_KEY_BackSpace) &&
-            (symbol <= CLUTTER_KEY_Clear)) ||
-           (symbol == CLUTTER_KEY_Return) ||
-           (symbol == CLUTTER_KEY_Escape) ||
-           (symbol == CLUTTER_KEY_KP_Space) ||
-           (symbol == CLUTTER_KEY_KP_Tab) ||
-           (symbol == CLUTTER_KEY_KP_Enter) ||
-           ((symbol >= CLUTTER_KEY_KP_Multiply) &&
-            (symbol <= CLUTTER_KEY_KP_9)) ||
-           (symbol == CLUTTER_KEY_KP_Equal) ||
-           (symbol == CLUTTER_KEY_Delete))))))
-    return 0;
-
-  /* if X keysym, convert to ascii by grabbing low 7 bits */
-  if (symbol == CLUTTER_KEY_KP_Space)
-    c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
-  else if (high_bytes == 0xFF)
-    c = symbol & 0x7F;
-  else
-    c = symbol & 0xFF;
-
-  buffer[0] = c;
-  return 1;
-}
-
-static double *
-translate_axes (ClutterInputDevice *device,
-                double              x,
-                double              y,
-                XIValuatorState    *valuators)
-{
-  uint32_t n_axes = clutter_input_device_get_n_axes (device);
-  uint32_t i;
-  double *retval;
-  double *values;
-
-  retval = g_new0 (double, n_axes);
-  values = valuators->values;
-
-  for (i = 0; i < valuators->mask_len * 8; i++)
-    {
-      ClutterInputAxis axis;
-      double val;
-
-      if (!XIMaskIsSet (valuators->mask, i))
-        continue;
-
-      axis = clutter_input_device_get_axis (device, i);
-      val = *values++;
-
-      switch (axis)
-        {
-        case CLUTTER_INPUT_AXIS_X:
-          retval[i] = x;
-          break;
-
-        case CLUTTER_INPUT_AXIS_Y:
-          retval[i] = y;
-          break;
-
-        default:
-          _clutter_input_device_translate_axis (device, i, val, &retval[i]);
-          break;
-        }
-    }
-
-  return retval;
-}
-
-static gboolean
-translate_pad_axis (ClutterInputDevice *device,
-                    XIValuatorState    *valuators,
-                    ClutterEventType   *evtype,
-                    uint32_t           *number,
-                    double             *value)
-{
-  double *values;
-  int i;
-
-  values = valuators->values;
-
-  for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++)
-    {
-      double val;
-      uint32_t axis_number = 0;
-
-      if (!XIMaskIsSet (valuators->mask, i))
-        continue;
-
-      val = *values++;
-      if (val <= 0)
-        continue;
-
-      _clutter_input_device_translate_axis (device, i, val, value);
-
-      if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2)
-        {
-          *evtype = CLUTTER_PAD_RING;
-          (*value) *= 360.0;
-        }
-      else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2)
-        {
-          *evtype = CLUTTER_PAD_STRIP;
-        }
-      else
-        continue;
-
-      if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2)
-        axis_number++;
-
-      *number = axis_number;
-      return TRUE;
-    }
-
-  return FALSE;
-}
+G_DEFINE_TYPE (MetaDeviceManagerX11,
+               meta_device_manager_x11,
+               CLUTTER_TYPE_DEVICE_MANAGER)
 
 static void
-translate_coords (MetaStageX11 *stage_x11,
-                  double        event_x,
-                  double        event_y,
-                  float        *x_out,
-                  float        *y_out)
+meta_device_manager_x11_copy_event_data (ClutterDeviceManager *device_manager,
+                                         const ClutterEvent   *src,
+                                         ClutterEvent         *dest)
 {
-  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
-  ClutterActor *stage = CLUTTER_ACTOR (stage_cogl->wrapper);
-  float stage_width;
-  float stage_height;
-
-  clutter_actor_get_size (stage, &stage_width, &stage_height);
+  gpointer event_x11;
 
-  *x_out = CLAMP (event_x, 0, stage_width);
-  *y_out = CLAMP (event_y, 0, stage_height);
+  event_x11 = _clutter_event_get_platform_data (src);
+  if (event_x11 != NULL)
+    _clutter_event_set_platform_data (dest, meta_event_x11_copy (event_x11));
 }
 
-static double
-scroll_valuators_changed (ClutterInputDevice *device,
-                          XIValuatorState    *valuators,
-                          double             *dx_p,
-                          double             *dy_p)
+static void
+meta_device_manager_x11_free_event_data (ClutterDeviceManager *device_manager,
+                                         ClutterEvent         *event)
 {
-  gboolean retval = FALSE;
-  uint32_t n_axes, n_val, i;
-  double *values;
-
-  n_axes = clutter_input_device_get_n_axes (device);
-  values = valuators->values;
-
-  *dx_p = *dy_p = 0.0;
-
-  n_val = 0;
-
-  for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
-    {
-      ClutterScrollDirection direction;
-      double delta;
-
-      if (!XIMaskIsSet (valuators->mask, i))
-        continue;
-
-      if (_clutter_input_device_get_scroll_delta (device, i,
-                                                  values[n_val],
-                                                  &direction,
-                                                  &delta))
-        {
-          retval = TRUE;
-
-          if (direction == CLUTTER_SCROLL_UP ||
-              direction == CLUTTER_SCROLL_DOWN)
-            *dy_p = delta;
-          else
-            *dx_p = delta;
-        }
-
-      n_val += 1;
-    }
+  gpointer event_x11;
 
-  return retval;
+  event_x11 = _clutter_event_get_platform_data (event);
+  if (event_x11 != NULL)
+    meta_event_x11_free (event_x11);
 }
 
 static void
@@ -1098,806 +111,6 @@ meta_device_manager_x11_select_stage_events (ClutterDeviceManager *manager,
   g_free (mask);
 }
 
-static uint
-device_get_tool_serial (ClutterInputDevice *device)
-{
-  gulong nitems, bytes_after;
-  uint32_t *data = NULL;
-  int serial_id = 0;
-  int rc, format;
-  Atom type;
-  Atom prop;
-
-  prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
-  if (prop == None)
-    return 0;
-
-  clutter_x11_trap_x_errors ();
-  rc = XIGetProperty (clutter_x11_get_default_display (),
-                      clutter_input_device_get_device_id (device),
-                      prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after,
-                      (guchar **) &data);
-  clutter_x11_untrap_x_errors ();
-
-  if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4)
-    serial_id = data[3];
-
-  XFree (data);
-
-  return serial_id;
-}
-
-static void
-handle_property_event (MetaDeviceManagerX11 *manager_xi2,
-                       XIEvent              *event)
-{
-  XIPropertyEvent *xev = (XIPropertyEvent *) event;
-  Atom serial_ids_prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
-  ClutterInputDevice *device;
-
-  device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                GINT_TO_POINTER (xev->deviceid));
-  if (!device)
-    return;
-
-  if (xev->property == serial_ids_prop)
-    {
-      ClutterInputDeviceTool *tool = NULL;
-      ClutterInputDeviceToolType type;
-      int serial_id;
-
-      serial_id = device_get_tool_serial (device);
-
-      if (serial_id != 0)
-        {
-          tool = g_hash_table_lookup (manager_xi2->tools_by_serial,
-                                      GUINT_TO_POINTER (serial_id));
-          if (!tool)
-            {
-              type = clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE ?
-                CLUTTER_INPUT_DEVICE_TOOL_ERASER : CLUTTER_INPUT_DEVICE_TOOL_PEN;
-              tool = meta_input_device_tool_x11_new (serial_id, type);
-              g_hash_table_insert (manager_xi2->tools_by_serial,
-                                   GUINT_TO_POINTER (serial_id),
-                                   tool);
-            }
-        }
-
-      meta_input_device_x11_update_tool (device, tool);
-      g_signal_emit_by_name (manager_xi2, "tool-changed", device, tool);
-    }
-}
-
-static gboolean
-translate_pad_event (ClutterEvent       *event,
-                     XIDeviceEvent      *xev,
-                     ClutterInputDevice *device)
-{
-  double value;
-  uint32_t number, mode = 0;
-
-  if (!translate_pad_axis (device, &xev->valuators,
-                           &event->any.type,
-                           &number, &value))
-    return FALSE;
-
-  /* When touching a ring/strip a first XI_Motion event
-   * is generated. Use it to reset the pad state, so
-   * later events actually have a directionality.
-   */
-  if (xev->evtype == XI_Motion)
-    value = -1;
-
-#ifdef HAVE_LIBWACOM
-  mode = meta_input_device_x11_get_pad_group_mode (device, number);
-#endif
-
-  if (event->any.type == CLUTTER_PAD_RING)
-    {
-      event->pad_ring.ring_number = number;
-      event->pad_ring.angle = value;
-      event->pad_ring.mode = mode;
-    }
-  else
-    {
-      event->pad_strip.strip_number = number;
-      event->pad_strip.value = value;
-      event->pad_strip.mode = mode;
-    }
-
-  event->any.time = xev->time;
-  clutter_event_set_device (event, device);
-  clutter_event_set_source_device (event, device);
-
-  g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
-           "(value:%f)",
-           event->any.type == CLUTTER_PAD_RING
-           ? "pad ring  "
-           : "pad strip",
-           (unsigned int) xev->event,
-           device->id,
-           device->device_name,
-           event->any.time, value);
-
-  return TRUE;
-}
-
-static void
-handle_raw_event (MetaDeviceManagerX11 *manager_xi2,
-                  XEvent               *xevent)
-{
-  ClutterInputDevice *device;
-  XGenericEventCookie *cookie;
-  XIEvent *xi_event;
-  XIRawEvent *xev;
-  float x,y;
-
-  cookie = &xevent->xcookie;
-  xi_event = (XIEvent *) cookie->data;
-  xev = (XIRawEvent *) xi_event;
-
-  device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                GINT_TO_POINTER (xev->deviceid));
-  if (device == NULL)
-    return;
-
-  if (!_clutter_is_input_pointer_a11y_enabled (device))
-    return;
-
-  switch (cookie->evtype)
-    {
-    case XI_RawMotion:
-      g_debug ("raw motion: device:%d '%s'",
-               device->id,
-               device->device_name);
-
-      /* We don't get actual pointer location with raw events, and we cannot
-       * rely on `clutter_input_device_get_coords()` either because of
-       * unreparented toplevels (like all client-side decoration windows),
-       * so we need to explicitely query the pointer here...
-       */
-      if (meta_input_device_x11_get_pointer_location (device, &x, &y))
-        _clutter_input_pointer_a11y_on_motion_event (device, x, y);
-      break;
-    case XI_RawButtonPress:
-    case XI_RawButtonRelease:
-      g_debug ("raw button %s: device:%d '%s' button %i",
-               cookie->evtype == XI_RawButtonPress
-               ? "press  "
-               : "release",
-               device->id,
-               device->device_name,
-               xev->detail);
-      _clutter_input_pointer_a11y_on_button_event (device,
-                                                  xev->detail,
-                                                  (cookie->evtype == XI_RawButtonPress));
-      break;
-    }
-}
-
-gboolean
-meta_device_manager_x11_translate_event (MetaDeviceManagerX11 *manager_xi2,
-                                         XEvent               *xevent,
-                                         ClutterEvent         *event)
-{
-  gboolean retval = FALSE;
-  ClutterBackend *backend = clutter_get_default_backend ();
-  MetaStageX11 *stage_x11 = NULL;
-  ClutterStage *stage = NULL;
-  ClutterInputDevice *device, *source_device;
-  XGenericEventCookie *cookie;
-  XIEvent *xi_event;
-
-  cookie = &xevent->xcookie;
-
-  if (cookie->type != GenericEvent ||
-      cookie->extension != manager_xi2->opcode)
-    return FALSE;
-
-  xi_event = (XIEvent *) cookie->data;
-
-  if (!xi_event)
-    return FALSE;
-
-  if (cookie->evtype == XI_RawMotion ||
-      cookie->evtype == XI_RawButtonPress ||
-      cookie->evtype == XI_RawButtonRelease)
-    {
-      handle_raw_event (manager_xi2, xevent);
-      return FALSE;
-    }
-
-  if (!(xi_event->evtype == XI_HierarchyChanged ||
-        xi_event->evtype == XI_DeviceChanged ||
-        xi_event->evtype == XI_PropertyEvent))
-    {
-      stage = get_event_stage (manager_xi2, xi_event);
-      if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
-        return FALSE;
-      else
-        stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
-    }
-
-  event->any.stage = stage;
-
-  switch (xi_event->evtype)
-    {
-    case XI_HierarchyChanged:
-      {
-        XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
-
-        translate_hierarchy_event (backend, manager_xi2, xev);
-      }
-      retval = FALSE;
-      break;
-
-    case XI_DeviceChanged:
-      {
-        XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
-
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-        if (device)
-          {
-            _clutter_input_device_reset_axes (device);
-            translate_device_classes (clutter_x11_get_default_display (),
-                                      device,
-                                      xev->classes,
-                                      xev->num_classes);
-          }
-
-        if (source_device)
-          _clutter_input_device_reset_scroll_info (source_device);
-      }
-      retval = FALSE;
-      break;
-
-    case XI_KeyPress:
-    case XI_KeyRelease:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-        MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (clutter_backend_get_keymap (backend));
-        MetaEventX11 *event_x11;
-        char buffer[7] = { 0, };
-        gunichar n;
-
-        event->key.type = event->type = (xev->evtype == XI_KeyPress)
-                                      ? CLUTTER_KEY_PRESS
-                                      : CLUTTER_KEY_RELEASE;
-
-        if (xev->evtype == XI_KeyPress && xev->flags & XIKeyRepeat)
-          clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED);
-
-        event->key.time = xev->time;
-        event->key.stage = stage;
-        meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group);
-        event->key.hardware_keycode = xev->detail;
-
-          /* keyval is the key ignoring all modifiers ('1' vs. '!') */
-        event->key.keyval =
-          meta_keymap_x11_translate_key_state (keymap_x11,
-                                               event->key.hardware_keycode,
-                                               &event->key.modifier_state,
-                                               NULL);
-
-        /* KeyEvents have platform specific data associated to them */
-        event_x11 = meta_event_x11_new ();
-        _clutter_event_set_platform_data (event, event_x11);
-
-        event_x11->key_group =
-          meta_keymap_x11_get_key_group (keymap_x11,
-                                         event->key.modifier_state);
-        event_x11->key_is_modifier =
-          meta_keymap_x11_get_is_modifier (keymap_x11,
-                                           event->key.hardware_keycode);
-        event_x11->num_lock_set =
-          clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (keymap_x11));
-        event_x11->caps_lock_set =
-          clutter_keymap_get_caps_lock_state (CLUTTER_KEYMAP (keymap_x11));
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-        clutter_event_set_source_device (event, source_device);
-
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-        clutter_event_set_device (event, device);
-
-        /* XXX keep this in sync with the evdev device manager */
-        n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
-        if (n == 0)
-          {
-            /* not printable */
-            event->key.unicode_value = (gunichar) '\0';
-          }
-        else
-          {
-            event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
-            if (event->key.unicode_value == -1 ||
-                event->key.unicode_value == -2)
-              event->key.unicode_value = (gunichar) '\0';
-          }
-
-        g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)",
-                 event->any.type == CLUTTER_KEY_PRESS
-                 ? "key press  "
-                 : "key release",
-                 (unsigned int) stage_x11->xwin,
-                 xev->deviceid,
-                 xev->sourceid,
-                 event->key.keyval ? buffer : "(none)",
-                 event->key.keyval);
-
-        if (xi_event->evtype == XI_KeyPress)
-          meta_stage_x11_set_user_time (stage_x11, event->key.time);
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_ButtonPress:
-    case XI_ButtonRelease:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-
-        /* Set the stage for core events coming out of nowhere (see bug #684509) */
-        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
-            clutter_input_device_get_pointer_stage (device) == NULL &&
-            stage != NULL)
-          _clutter_input_device_set_stage (device, stage);
-
-       if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
-          {
-            /* We got these events because of the passive button grab */
-            XIAllowEvents (clutter_x11_get_default_display (),
-                           xev->sourceid,
-                           XIAsyncDevice,
-                           xev->time);
-
-            event->any.stage = stage;
-
-            if (xev->detail >= 4 && xev->detail <= 7)
-              {
-                retval = FALSE;
-
-                if (xi_event->evtype == XI_ButtonPress &&
-                    translate_pad_event (event, xev, source_device))
-                  retval = TRUE;
-
-                break;
-              }
-
-            event->any.type =
-              (xi_event->evtype == XI_ButtonPress) ? CLUTTER_PAD_BUTTON_PRESS
-                                                   : CLUTTER_PAD_BUTTON_RELEASE;
-            event->any.time = xev->time;
-
-            /* The 4-7 button range is taken as non-existent on pad devices,
-             * let the buttons above that take over this range.
-             */
-            if (xev->detail > 7)
-              xev->detail -= 4;
-
-            /* Pad buttons are 0-indexed */
-            event->pad_button.button = xev->detail - 1;
-#ifdef HAVE_LIBWACOM
-            meta_input_device_x11_update_pad_state (device,
-                                                    event->pad_button.button,
-                                                    (xi_event->evtype == XI_ButtonPress),
-                                                    &event->pad_button.group,
-                                                    &event->pad_button.mode);
-#endif
-            clutter_event_set_device (event, device);
-            clutter_event_set_source_device (event, source_device);
-
-            g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
-                     "(button:%d)",
-                     event->any.type == CLUTTER_BUTTON_PRESS
-                     ? "pad button press  "
-                     : "pad button release",
-                     (unsigned int) stage_x11->xwin,
-                     device->id,
-                     device->device_name,
-                     event->any.time,
-                     event->pad_button.button);
-            retval = TRUE;
-            break;
-          }
-
-        switch (xev->detail)
-          {
-          case 4:
-          case 5:
-          case 6:
-          case 7:
-            /* we only generate Scroll events on ButtonPress */
-            if (xi_event->evtype == XI_ButtonRelease)
-              return FALSE;
-
-            event->scroll.type = event->type = CLUTTER_SCROLL;
-
-            if (xev->detail == 4)
-              event->scroll.direction = CLUTTER_SCROLL_UP;
-            else if (xev->detail == 5)
-              event->scroll.direction = CLUTTER_SCROLL_DOWN;
-            else if (xev->detail == 6)
-              event->scroll.direction = CLUTTER_SCROLL_LEFT;
-            else
-              event->scroll.direction = CLUTTER_SCROLL_RIGHT;
-
-            event->scroll.stage = stage;
-
-            event->scroll.time = xev->time;
-            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
-            meta_input_device_x11_translate_state (event,
-                                                   &xev->mods,
-                                                   &xev->buttons,
-                                                   &xev->group);
-
-            clutter_event_set_source_device (event, source_device);
-            clutter_event_set_device (event, device);
-
-            event->scroll.axes = translate_axes (event->scroll.device,
-                                                 event->scroll.x,
-                                                 event->scroll.y,
-                                                 &xev->valuators);
-            g_debug ("scroll: win:0x%x, device:%d '%s', time:%d "
-                     "(direction:%s, "
-                     "x:%.2f, y:%.2f, "
-                     "emulated:%s)",
-                     (unsigned int) stage_x11->xwin,
-                     device->id,
-                     device->device_name,
-                     event->any.time,
-                     event->scroll.direction == CLUTTER_SCROLL_UP ? "up" :
-                     event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" :
-                     event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" :
-                     event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" :
-                     "invalid",
-                     event->scroll.x,
-                     event->scroll.y,
-                     (xev->flags & XIPointerEmulated) ? "yes" : "no");
-            break;
-
-          default:
-            event->button.type = event->type =
-              (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
-                                                   : CLUTTER_BUTTON_RELEASE;
-
-            event->button.stage = stage;
-
-            event->button.time = xev->time;
-            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->button.x, &event->button.y);
-            event->button.button = xev->detail;
-            meta_input_device_x11_translate_state (event,
-                                                   &xev->mods,
-                                                   &xev->buttons,
-                                                   &xev->group);
-
-            clutter_event_set_source_device (event, source_device);
-            clutter_event_set_device (event, device);
-            clutter_event_set_device_tool (event,
-                                           meta_input_device_x11_get_current_tool (source_device));
-
-            event->button.axes = translate_axes (event->button.device,
-                                                 event->button.x,
-                                                 event->button.y,
-                                                 &xev->valuators);
-            g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
-                     "(button:%d, "
-                     "x:%.2f, y:%.2f, "
-                     "axes:%s, "
-                     "emulated:%s)",
-                     event->any.type == CLUTTER_BUTTON_PRESS
-                     ? "button press  "
-                     : "button release",
-                     (unsigned int) stage_x11->xwin,
-                     device->id,
-                     device->device_name,
-                     event->any.time,
-                     event->button.button,
-                     event->button.x,
-                     event->button.y,
-                     event->button.axes != NULL ? "yes" : "no",
-                     (xev->flags & XIPointerEmulated) ? "yes" : "no");
-            break;
-          }
-
-        if (source_device != NULL && device->stage != NULL)
-          _clutter_input_device_set_stage (source_device, device->stage);
-
-        if (xev->flags & XIPointerEmulated)
-          _clutter_event_set_pointer_emulated (event, TRUE);
-
-        if (xi_event->evtype == XI_ButtonPress)
-          meta_stage_x11_set_user_time (stage_x11, event->button.time);
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_Motion:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-        double delta_x, delta_y;
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-
-        if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
-          {
-            event->any.stage = stage;
-
-            if (translate_pad_event (event, xev, source_device))
-              retval = TRUE;
-            break;
-          }
-
-        /* Set the stage for core events coming out of nowhere (see bug #684509) */
-        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
-            clutter_input_device_get_pointer_stage (device) == NULL &&
-            stage != NULL)
-          _clutter_input_device_set_stage (device, stage);
-
-        if (scroll_valuators_changed (source_device,
-                                      &xev->valuators,
-                                      &delta_x, &delta_y))
-          {
-            event->scroll.type = event->type = CLUTTER_SCROLL;
-            event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
-
-            event->scroll.stage = stage;
-            event->scroll.time = xev->time;
-            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
-            meta_input_device_x11_translate_state (event,
-                                                   &xev->mods,
-                                                   &xev->buttons,
-                                                   &xev->group);
-
-            clutter_event_set_scroll_delta (event, delta_x, delta_y);
-            clutter_event_set_source_device (event, source_device);
-            clutter_event_set_device (event, device);
-
-            g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
-                     (unsigned int) stage_x11->xwin,
-                     event->scroll.device->id,
-                     event->scroll.device->device_name,
-                     event->scroll.x,
-                     event->scroll.y,
-                     delta_x, delta_y);
-
-            retval = TRUE;
-            break;
-          }
-
-        event->motion.type = event->type = CLUTTER_MOTION;
-
-        event->motion.stage = stage;
-
-        event->motion.time = xev->time;
-        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->motion.x, &event->motion.y);
-        meta_input_device_x11_translate_state (event,
-                                               &xev->mods,
-                                               &xev->buttons,
-                                               &xev->group);
-
-        clutter_event_set_source_device (event, source_device);
-        clutter_event_set_device (event, device);
-        clutter_event_set_device_tool (event,
-                                       meta_input_device_x11_get_current_tool (source_device));
-
-        event->motion.axes = translate_axes (event->motion.device,
-                                             event->motion.x,
-                                             event->motion.y,
-                                             &xev->valuators);
-
-        if (source_device != NULL && device->stage != NULL)
-          _clutter_input_device_set_stage (source_device, device->stage);
-
-        if (xev->flags & XIPointerEmulated)
-          _clutter_event_set_pointer_emulated (event, TRUE);
-
-        g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
-                 (unsigned int) stage_x11->xwin,
-                 event->motion.device->id,
-                 event->motion.device->device_name,
-                 event->motion.x,
-                 event->motion.y,
-                 event->motion.axes != NULL ? "yes" : "no");
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_TouchBegin:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-        if (!_clutter_input_device_get_stage (device))
-          _clutter_input_device_set_stage (device, stage);
-      }
-      /* Fall through */
-    case XI_TouchEnd:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-
-        if (xi_event->evtype == XI_TouchBegin)
-          event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
-        else
-          event->touch.type = event->type = CLUTTER_TOUCH_END;
-
-        event->touch.stage = stage;
-        event->touch.time = xev->time;
-        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
-        meta_input_device_x11_translate_state (event,
-                                               &xev->mods,
-                                               &xev->buttons,
-                                               &xev->group);
-
-        clutter_event_set_source_device (event, source_device);
-
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-        clutter_event_set_device (event, device);
-
-        event->touch.axes = translate_axes (event->touch.device,
-                                            event->motion.x,
-                                            event->motion.y,
-                                            &xev->valuators);
-
-        if (xi_event->evtype == XI_TouchBegin)
-          {
-            event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
-
-            meta_stage_x11_set_user_time (stage_x11, event->touch.time);
-          }
-
-        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
-
-        if (xev->flags & XITouchEmulatingPointer)
-          _clutter_event_set_pointer_emulated (event, TRUE);
-
-        g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
-                 event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
-                 (unsigned int) stage_x11->xwin,
-                 event->touch.device->id,
-                 event->touch.device->device_name,
-                 GPOINTER_TO_UINT (event->touch.sequence),
-                 event->touch.x,
-                 event->touch.y,
-                 event->touch.axes != NULL ? "yes" : "no");
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_TouchUpdate:
-      {
-        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-
-        event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
-        event->touch.stage = stage;
-        event->touch.time = xev->time;
-        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
-        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
-
-        clutter_event_set_source_device (event, source_device);
-
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-        clutter_event_set_device (event, device);
-
-        event->touch.axes = translate_axes (event->touch.device,
-                                            event->motion.x,
-                                            event->motion.y,
-                                            &xev->valuators);
-
-        meta_input_device_x11_translate_state (event,
-                                               &xev->mods,
-                                               &xev->buttons,
-                                               &xev->group);
-        event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
-
-        if (xev->flags & XITouchEmulatingPointer)
-          _clutter_event_set_pointer_emulated (event, TRUE);
-
-        g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
-                 (unsigned int) stage_x11->xwin,
-                 event->touch.device->id,
-                 event->touch.device->device_name,
-                 GPOINTER_TO_UINT (event->touch.sequence),
-                 event->touch.x,
-                 event->touch.y,
-                 event->touch.axes != NULL ? "yes" : "no");
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_Enter:
-    case XI_Leave:
-      {
-        XIEnterEvent *xev = (XIEnterEvent *) xi_event;
-
-        device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                      GINT_TO_POINTER (xev->deviceid));
-
-        source_device = g_hash_table_lookup (manager_xi2->devices_by_id,
-                                             GINT_TO_POINTER (xev->sourceid));
-
-        if (xi_event->evtype == XI_Enter)
-          {
-            event->crossing.type = event->type = CLUTTER_ENTER;
-
-            event->crossing.stage = stage;
-            event->crossing.source = CLUTTER_ACTOR (stage);
-            event->crossing.related = NULL;
-
-            event->crossing.time = xev->time;
-            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
-
-            _clutter_input_device_set_stage (device, stage);
-          }
-        else
-          {
-            if (device->stage == NULL)
-              {
-                g_debug ("Discarding Leave for ButtonRelease "
-                         "event off-stage");
-                retval = FALSE;
-                break;
-              }
-
-            event->crossing.type = event->type = CLUTTER_LEAVE;
-
-            event->crossing.stage = stage;
-            event->crossing.source = CLUTTER_ACTOR (stage);
-            event->crossing.related = NULL;
-
-            event->crossing.time = xev->time;
-            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
-
-            _clutter_input_device_set_stage (device, NULL);
-          }
-
-        _clutter_input_device_reset_scroll_info (source_device);
-
-        clutter_event_set_device (event, device);
-        clutter_event_set_source_device (event, source_device);
-
-        retval = TRUE;
-      }
-      break;
-
-    case XI_FocusIn:
-    case XI_FocusOut:
-      retval = FALSE;
-      break;
-    case XI_PropertyEvent:
-      handle_property_event (manager_xi2, xi_event);
-      retval = FALSE;
-      break;
-    }
-
-  return retval;
-}
-
 static void
 meta_device_manager_x11_add_device (ClutterDeviceManager *manager,
                                     ClutterInputDevice   *device)
@@ -1916,17 +129,28 @@ static const GSList *
 meta_device_manager_x11_get_devices (ClutterDeviceManager *manager)
 {
   MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
+  ClutterBackend *backend = clutter_get_default_backend ();
+  ClutterSeat *seat = clutter_backend_get_default_seat (backend);
   GSList *all_devices = NULL;
-  GList *l;
+  GList *l, *devices;
 
   if (manager_xi2->all_devices != NULL)
     return manager_xi2->all_devices;
 
-  for (l = manager_xi2->master_devices; l != NULL; l = l->next)
+  all_devices = g_slist_prepend (all_devices,
+                                 clutter_seat_get_pointer (seat));
+  all_devices = g_slist_prepend (all_devices,
+                                 clutter_seat_get_keyboard (seat));
+
+  devices = clutter_seat_list_devices (seat, clutter_seat_get_pointer (seat));
+  for (l = devices; l; l = l->next)
     all_devices = g_slist_prepend (all_devices, l->data);
+  g_list_free (devices);
 
-  for (l = manager_xi2->slave_devices; l != NULL; l = l->next)
+  devices = clutter_seat_list_devices (seat, clutter_seat_get_keyboard (seat));
+  for (l = devices; l; l = l->next)
     all_devices = g_slist_prepend (all_devices, l->data);
+  g_list_free (devices);
 
   manager_xi2->all_devices = g_slist_reverse (all_devices);
 
@@ -1937,40 +161,26 @@ static ClutterInputDevice *
 meta_device_manager_x11_get_device (ClutterDeviceManager *manager,
                                     gint                  id)
 {
-  MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
+  ClutterBackend *backend = clutter_get_default_backend ();
+  ClutterSeat *seat = clutter_backend_get_default_seat (backend);
 
-  return g_hash_table_lookup (manager_xi2->devices_by_id,
-                              GINT_TO_POINTER (id));
+  return meta_seat_x11_lookup_device_id (META_SEAT_X11 (seat), id);
 }
 
 static ClutterInputDevice *
 meta_device_manager_x11_get_core_device (ClutterDeviceManager   *manager,
                                          ClutterInputDeviceType  device_type)
 {
-  MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (manager);
-  ClutterInputDevice *pointer = NULL;
-  GList *l;
-
-  for (l = manager_xi2->master_devices; l != NULL ; l = l->next)
-    {
-      ClutterInputDevice *device = l->data;
-      if (clutter_input_device_get_device_type (device) == CLUTTER_POINTER_DEVICE)
-        {
-          pointer = device;
-          break;
-        }
-    }
-
-  if (pointer == NULL)
-    return NULL;
+  ClutterBackend *backend = clutter_get_default_backend ();
+  ClutterSeat *seat = clutter_backend_get_default_seat (backend);
 
   switch (device_type)
     {
     case CLUTTER_POINTER_DEVICE:
-      return pointer;
+      return clutter_seat_get_pointer (seat);;
 
     case CLUTTER_KEYBOARD_DEVICE:
-      return clutter_input_device_get_associated_device (pointer);
+      return clutter_seat_get_keyboard (seat);;
 
     default:
       break;
@@ -1980,112 +190,44 @@ meta_device_manager_x11_get_core_device (ClutterDeviceManager   *manager,
 }
 
 static void
-relate_masters (gpointer key,
-                gpointer value,
-                gpointer data)
+on_device_added (ClutterSeat          *seat,
+                 ClutterInputDevice   *parent,
+                 ClutterInputDevice   *device,
+                 ClutterDeviceManager *manager)
 {
-  MetaDeviceManagerX11 *manager_xi2 = data;
-  ClutterInputDevice *device, *relative;
-
-  device = g_hash_table_lookup (manager_xi2->devices_by_id, key);
-  relative = g_hash_table_lookup (manager_xi2->devices_by_id, value);
-
-  _clutter_input_device_set_associated_device (device, relative);
-  _clutter_input_device_set_associated_device (relative, device);
+  g_signal_emit_by_name (manager, "device-added", device);
 }
 
 static void
-relate_slaves (gpointer key,
-               gpointer value,
-               gpointer data)
+on_device_removed (ClutterSeat          *seat,
+                   ClutterInputDevice   *parent,
+                   ClutterInputDevice   *device,
+                   ClutterDeviceManager *manager)
 {
-  MetaDeviceManagerX11 *manager_xi2 = data;
-  ClutterInputDevice *master, *slave;
-
-  slave = g_hash_table_lookup (manager_xi2->devices_by_id, key);
-  master = g_hash_table_lookup (manager_xi2->devices_by_id, value);
+  g_signal_emit_by_name (manager, "device-removed", device);
+}
 
-  _clutter_input_device_set_associated_device (slave, master);
-  _clutter_input_device_add_slave (master, slave);
+static void
+on_tool_changed (ClutterSeat            *seat,
+                 ClutterInputDevice     *device,
+                 ClutterInputDeviceTool *tool,
+                 ClutterDeviceManager   *manager)
+{
+  g_signal_emit_by_name (manager, "tool-changed", device, tool);
 }
 
 static void
 meta_device_manager_x11_constructed (GObject *object)
 {
-  MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (object);
   ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (object);
-  ClutterBackend *backend = clutter_get_default_backend ();
-  GHashTable *masters, *slaves;
-  XIDeviceInfo *info;
-  XIEventMask event_mask;
-  unsigned char mask[(XI_LASTEVENT + 7) / 8] = { 0, };
-  int n_devices, i;
-
-  masters = g_hash_table_new (NULL, NULL);
-  slaves = g_hash_table_new (NULL, NULL);
-
-  info = XIQueryDevice (clutter_x11_get_default_display (),
-                        XIAllDevices, &n_devices);
-
-  for (i = 0; i < n_devices; i++)
-    {
-      XIDeviceInfo *xi_device = &info[i];
-
-      if (!xi_device->enabled)
-        continue;
-
-      add_device (manager_xi2, backend, xi_device, TRUE);
-
-      if (xi_device->use == XIMasterPointer ||
-          xi_device->use == XIMasterKeyboard)
-        {
-          g_hash_table_insert (masters,
-                               GINT_TO_POINTER (xi_device->deviceid),
-                               GINT_TO_POINTER (xi_device->attachment));
-        }
-      else if (xi_device->use == XISlavePointer ||
-               xi_device->use == XISlaveKeyboard)
-        {
-          g_hash_table_insert (slaves,
-                               GINT_TO_POINTER (xi_device->deviceid),
-                               GINT_TO_POINTER (xi_device->attachment));
-        }
-    }
-
-  XIFreeDeviceInfo (info);
-
-  g_hash_table_foreach (masters, relate_masters, manager_xi2);
-  g_hash_table_destroy (masters);
-
-  g_hash_table_foreach (slaves, relate_slaves, manager_xi2);
-  g_hash_table_destroy (slaves);
-
-  XISetMask (mask, XI_HierarchyChanged);
-  XISetMask (mask, XI_DeviceChanged);
-  XISetMask (mask, XI_PropertyEvent);
-
-  event_mask.deviceid = XIAllDevices;
-  event_mask.mask_len = sizeof (mask);
-  event_mask.mask = mask;
-
-  meta_device_manager_x11_select_events (manager,
-                                         clutter_x11_get_root_window (),
-                                         &event_mask);
-
-  memset(mask, 0, sizeof (mask));
-  XISetMask (mask, XI_RawMotion);
-  XISetMask (mask, XI_RawButtonPress);
-  XISetMask (mask, XI_RawButtonRelease);
-
-  event_mask.deviceid = XIAllMasterDevices;
-  event_mask.mask_len = sizeof (mask);
-  event_mask.mask = mask;
-
-  meta_device_manager_x11_select_events (manager,
-                                        clutter_x11_get_root_window (),
-                                        &event_mask);
+  MetaDeviceManagerX11 *manager_xi2 = META_DEVICE_MANAGER_X11 (object);
 
-  XSync (clutter_x11_get_default_display (), False);
+  g_signal_connect (manager_xi2->seat, "device-added",
+                    G_CALLBACK (on_device_added), manager_xi2);
+  g_signal_connect (manager_xi2->seat, "device-added",
+                    G_CALLBACK (on_device_removed), manager_xi2);
+  g_signal_connect (manager_xi2->seat, "tool-changed",
+                    G_CALLBACK (on_tool_changed), manager_xi2);
 
   meta_device_manager_x11_a11y_init (manager);
 
@@ -2103,8 +245,8 @@ meta_device_manager_x11_set_property (GObject      *object,
 
   switch (prop_id)
     {
-    case PROP_OPCODE:
-      manager_xi2->opcode = g_value_get_int (value);
+    case PROP_SEAT:
+      manager_xi2->seat = g_value_get_object (value);
       break;
 
     default:
@@ -2136,13 +278,12 @@ meta_device_manager_x11_class_init (MetaDeviceManagerX11Class *klass)
   ClutterDeviceManagerClass *manager_class;
   GObjectClass *gobject_class;
 
-  obj_props[PROP_OPCODE] =
-    g_param_spec_int ("opcode",
-                      "Opcode",
-                      "The XI2 opcode",
-                      -1, G_MAXINT,
-                      -1,
-                      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+  obj_props[PROP_SEAT] =
+    g_param_spec_object ("seat",
+                         "Seat",
+                         "Seat",
+                         CLUTTER_TYPE_SEAT,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->constructed = meta_device_manager_x11_constructed;
@@ -2167,13 +308,4 @@ meta_device_manager_x11_class_init (MetaDeviceManagerX11Class *klass)
 static void
 meta_device_manager_x11_init (MetaDeviceManagerX11 *self)
 {
-  self->devices_by_id = g_hash_table_new_full (NULL, NULL,
-                                               NULL,
-                                               (GDestroyNotify) g_object_unref);
-  self->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL,
-                                                 (GDestroyNotify) g_object_unref);
-
-#ifdef HAVE_LIBWACOM
-  self->wacom_db = libwacom_database_new ();
-#endif
 }
diff --git a/src/backends/x11/meta-device-manager-x11.h b/src/backends/x11/meta-device-manager-x11.h
index 4bf762cdd..90d811526 100644
--- a/src/backends/x11/meta-device-manager-x11.h
+++ b/src/backends/x11/meta-device-manager-x11.h
@@ -36,19 +36,8 @@ struct _MetaDeviceManagerX11
 {
   ClutterDeviceManager parent_instance;
 
-  GHashTable *devices_by_id;
-  GHashTable *tools_by_serial;
-
   GSList *all_devices;
-
-  GList *master_devices;
-  GList *slave_devices;
-
-  int opcode;
-
-#ifdef HAVE_LIBWACOM
-  WacomDeviceDatabase *wacom_db;
-#endif
+  ClutterSeat *seat;
 };
 
 gboolean meta_device_manager_x11_translate_event (MetaDeviceManagerX11 *manager_xi2,
diff --git a/src/backends/x11/meta-seat-x11.c b/src/backends/x11/meta-seat-x11.c
new file mode 100644
index 000000000..9704aca24
--- /dev/null
+++ b/src/backends/x11/meta-seat-x11.c
@@ -0,0 +1,2115 @@
+/*
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#include "config.h"
+
+#include <X11/extensions/XInput2.h>
+
+#include "backends/x11/meta-event-x11.h"
+#include "backends/x11/meta-input-device-tool-x11.h"
+#include "backends/x11/meta-input-device-x11.h"
+#include "backends/x11/meta-keymap-x11.h"
+#include "backends/x11/meta-stage-x11.h"
+#include "clutter/clutter-mutter.h"
+#include "clutter/x11/clutter-x11.h"
+#include "meta-seat-x11.h"
+
+enum
+{
+  PROP_0,
+  PROP_OPCODE,
+  PROP_POINTER_ID,
+  PROP_KEYBOARD_ID,
+  N_PROPS
+};
+
+struct _MetaSeatX11
+{
+  ClutterSeat parent_instance;
+  ClutterInputDevice *core_pointer;
+  ClutterInputDevice *core_keyboard;
+  GList *devices;
+  GHashTable *devices_by_id;
+  GHashTable *tools_by_serial;
+
+  int pointer_id;
+  int keyboard_id;
+  int opcode;
+
+#ifdef HAVE_LIBWACOM
+  WacomDeviceDatabase *wacom_db;
+#endif
+};
+
+static GParamSpec *props[N_PROPS] = { 0 };
+
+G_DEFINE_TYPE (MetaSeatX11, meta_seat_x11, CLUTTER_TYPE_SEAT)
+
+static const char *clutter_input_axis_atom_names[] = {
+  "Abs X",              /* CLUTTER_INPUT_AXIS_X */
+  "Abs Y",              /* CLUTTER_INPUT_AXIS_Y */
+  "Abs Pressure",       /* CLUTTER_INPUT_AXIS_PRESSURE */
+  "Abs Tilt X",         /* CLUTTER_INPUT_AXIS_XTILT */
+  "Abs Tilt Y",         /* CLUTTER_INPUT_AXIS_YTILT */
+  "Abs Wheel",          /* CLUTTER_INPUT_AXIS_WHEEL */
+  "Abs Distance",       /* CLUTTER_INPUT_AXIS_DISTANCE */
+};
+
+static const char *wacom_type_atoms[] = {
+    "STYLUS",
+    "CURSOR",
+    "ERASER",
+    "PAD",
+    "TOUCH"
+};
+#define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms)
+
+enum
+{
+    WACOM_TYPE_STYLUS,
+    WACOM_TYPE_CURSOR,
+    WACOM_TYPE_ERASER,
+    WACOM_TYPE_PAD,
+    WACOM_TYPE_TOUCH,
+};
+
+enum
+{
+  PAD_AXIS_FIRST  = 3, /* First axes are always x/y/pressure, ignored in pads */
+  PAD_AXIS_STRIP1 = PAD_AXIS_FIRST,
+  PAD_AXIS_STRIP2,
+  PAD_AXIS_RING1,
+  PAD_AXIS_RING2,
+};
+
+#define N_AXIS_ATOMS    G_N_ELEMENTS (clutter_input_axis_atom_names)
+
+static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
+
+static void
+translate_valuator_class (Display             *xdisplay,
+                          ClutterInputDevice  *device,
+                          XIValuatorClassInfo *class)
+{
+  static gboolean atoms_initialized = FALSE;
+  ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
+
+  if (G_UNLIKELY (!atoms_initialized))
+    {
+      XInternAtoms (xdisplay,
+                    (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
+                    False,
+                    clutter_input_axis_atoms);
+
+      atoms_initialized = TRUE;
+    }
+
+  for (i = 0;
+       i < N_AXIS_ATOMS;
+       i += 1)
+    {
+      if (clutter_input_axis_atoms[i] == class->label)
+        {
+          axis = i + 1;
+          break;
+        }
+    }
+
+  _clutter_input_device_add_axis (device, axis,
+                                  class->min,
+                                  class->max,
+                                  class->resolution);
+
+  g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
+           clutter_input_axis_atom_names[axis],
+           class->min,
+           class->max,
+           class->resolution,
+           device->id);
+}
+
+static void
+translate_device_classes (Display             *xdisplay,
+                          ClutterInputDevice  *device,
+                          XIAnyClassInfo     **classes,
+                          int                  n_classes)
+{
+  int i;
+
+  for (i = 0; i < n_classes; i++)
+    {
+      XIAnyClassInfo *class_info = classes[i];
+
+      switch (class_info->type)
+        {
+        case XIKeyClass:
+          {
+            XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
+            int j;
+
+            _clutter_input_device_set_n_keys (device,
+                                              key_info->num_keycodes);
+
+            for (j = 0; j < key_info->num_keycodes; j++)
+              {
+                clutter_input_device_set_key (device, j,
+                                              key_info->keycodes[i],
+                                              0);
+              }
+          }
+          break;
+
+        case XIValuatorClass:
+          translate_valuator_class (xdisplay, device,
+                                    (XIValuatorClassInfo *) class_info);
+          break;
+
+        case XIScrollClass:
+          {
+            XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
+            ClutterScrollDirection direction;
+
+            if (scroll_info->scroll_type == XIScrollTypeVertical)
+              direction = CLUTTER_SCROLL_DOWN;
+            else
+              direction = CLUTTER_SCROLL_RIGHT;
+
+            g_debug ("Scroll valuator %d: %s, increment: %f",
+                     scroll_info->number,
+                     scroll_info->scroll_type == XIScrollTypeVertical
+                     ? "vertical"
+                     : "horizontal",
+                     scroll_info->increment);
+
+            _clutter_input_device_add_scroll_info (device,
+                                                   scroll_info->number,
+                                                   direction,
+                                                   scroll_info->increment);
+          }
+          break;
+
+        default:
+          break;
+        }
+    }
+}
+
+static gboolean
+is_touch_device (XIAnyClassInfo         **classes,
+                 int                      n_classes,
+                 ClutterInputDeviceType  *device_type,
+                 uint32_t                *n_touch_points)
+{
+  int i;
+
+  for (i = 0; i < n_classes; i++)
+    {
+      XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
+
+      if (class->type != XITouchClass)
+        continue;
+
+      if (class->num_touches > 0)
+        {
+          if (class->mode == XIDirectTouch)
+            *device_type = CLUTTER_TOUCHSCREEN_DEVICE;
+          else if (class->mode == XIDependentTouch)
+            *device_type = CLUTTER_TOUCHPAD_DEVICE;
+          else
+            continue;
+
+          *n_touch_points = class->num_touches;
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+is_touchpad_device (XIDeviceInfo *info)
+{
+  gulong nitems, bytes_after;
+  uint32_t *data = NULL;
+  int rc, format;
+  Atom type;
+  Atom prop;
+
+  prop = XInternAtom (clutter_x11_get_default_display (), "libinput Tapping Enabled", True);
+  if (prop == None)
+    return FALSE;
+
+  clutter_x11_trap_x_errors ();
+  rc = XIGetProperty (clutter_x11_get_default_display (),
+                      info->deviceid,
+                      prop,
+                      0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+                      (guchar **) &data);
+  clutter_x11_untrap_x_errors ();
+
+  /* We don't care about the data */
+  XFree (data);
+
+  if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+get_device_ids (XIDeviceInfo  *info,
+                char         **vendor_id,
+                char         **product_id)
+{
+  gulong nitems, bytes_after;
+  uint32_t *data = NULL;
+  int rc, format;
+  Atom type;
+
+  clutter_x11_trap_x_errors ();
+  rc = XIGetProperty (clutter_x11_get_default_display (),
+                      info->deviceid,
+                      XInternAtom (clutter_x11_get_default_display (), "Device Product ID", False),
+                      0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+                      (guchar **) &data);
+  clutter_x11_untrap_x_errors ();
+
+  if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2)
+    {
+      XFree (data);
+      return FALSE;
+    }
+
+  if (vendor_id)
+    *vendor_id = g_strdup_printf ("%.4x", data[0]);
+  if (product_id)
+    *product_id = g_strdup_printf ("%.4x", data[1]);
+
+  XFree (data);
+
+  return TRUE;
+}
+
+static char *
+get_device_node_path (XIDeviceInfo *info)
+{
+  gulong nitems, bytes_after;
+  guchar *data;
+  int rc, format;
+  Atom prop, type;
+  char *node_path;
+
+  prop = XInternAtom (clutter_x11_get_default_display (), "Device Node", False);
+  if (prop == None)
+    return NULL;
+
+  clutter_x11_trap_x_errors ();
+
+  rc = XIGetProperty (clutter_x11_get_default_display (),
+                      info->deviceid, prop, 0, 1024, False,
+                      XA_STRING, &type, &format, &nitems, &bytes_after,
+                      (guchar **) &data);
+
+  if (clutter_x11_untrap_x_errors ())
+    return NULL;
+
+  if (rc != Success || type != XA_STRING || format != 8)
+    {
+      XFree (data);
+      return FALSE;
+    }
+
+  node_path = g_strdup ((char *) data);
+  XFree (data);
+
+  return node_path;
+}
+
+static void
+get_pad_features (XIDeviceInfo *info,
+                  uint32_t     *n_rings,
+                  uint32_t     *n_strips)
+{
+  int i, rings = 0, strips = 0;
+
+  for (i = PAD_AXIS_FIRST; i < info->num_classes; i++)
+    {
+      XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i];
+      int axis = valuator->number;
+
+      if (valuator->type != XIValuatorClass)
+        continue;
+      if (valuator->max <= 1)
+        continue;
+
+      /* Ring/strip axes are fixed in pad devices as handled by the
+       * wacom driver. Match those to detect pad features.
+       */
+      if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2)
+        strips++;
+      else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2)
+        rings++;
+    }
+
+  *n_rings = rings;
+  *n_strips = strips;
+}
+
+/* The Wacom driver exports the tool type as property. Use that over
+   guessing based on the device name */
+static gboolean
+guess_source_from_wacom_type (XIDeviceInfo            *info,
+                              ClutterInputDeviceType  *source_out)
+{
+  gulong nitems, bytes_after;
+  uint32_t *data = NULL;
+  int rc, format;
+  Atom type;
+  Atom prop;
+  Atom device_type;
+  Atom types[N_WACOM_TYPE_ATOMS];
+
+  prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Tool Type", True);
+  if (prop == None)
+    return FALSE;
+
+  clutter_x11_trap_x_errors ();
+  rc = XIGetProperty (clutter_x11_get_default_display (),
+                      info->deviceid,
+                      prop,
+                      0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after,
+                      (guchar **) &data);
+  clutter_x11_untrap_x_errors ();
+
+  if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1)
+    {
+      XFree (data);
+      return FALSE;
+    }
+
+  device_type = *data;
+  XFree (data);
+
+  if (device_type == 0)
+      return FALSE;
+
+  rc = XInternAtoms (clutter_x11_get_default_display (),
+                     (char **)wacom_type_atoms,
+                     N_WACOM_TYPE_ATOMS,
+                     False,
+                     types);
+  if (rc == 0)
+      return FALSE;
+
+  if (device_type == types[WACOM_TYPE_STYLUS])
+    {
+      *source_out = CLUTTER_PEN_DEVICE;
+    }
+  else if (device_type == types[WACOM_TYPE_CURSOR])
+    {
+      *source_out = CLUTTER_CURSOR_DEVICE;
+    }
+  else if (device_type == types[WACOM_TYPE_ERASER])
+    {
+      *source_out = CLUTTER_ERASER_DEVICE;
+    }
+  else if (device_type == types[WACOM_TYPE_PAD])
+    {
+      *source_out = CLUTTER_PAD_DEVICE;
+    }
+  else if (device_type == types[WACOM_TYPE_TOUCH])
+    {
+        uint32_t num_touches = 0;
+
+        if (!is_touch_device (info->classes, info->num_classes,
+                              source_out, &num_touches))
+            *source_out = CLUTTER_TOUCHSCREEN_DEVICE;
+    }
+  else
+    {
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static ClutterInputDevice *
+create_device (MetaSeatX11    *seat_x11,
+               ClutterBackend *backend,
+               XIDeviceInfo   *info)
+{
+  ClutterInputDeviceType source, touch_source;
+  ClutterInputDevice *retval;
+  ClutterInputMode mode;
+  gboolean is_enabled;
+  uint32_t num_touches = 0, num_rings = 0, num_strips = 0;
+  char *vendor_id = NULL, *product_id = NULL, *node_path = NULL;
+
+  if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
+    {
+      source = CLUTTER_KEYBOARD_DEVICE;
+    }
+  else if (is_touchpad_device (info))
+    {
+      source = CLUTTER_TOUCHPAD_DEVICE;
+    }
+  else if (info->use == XISlavePointer &&
+           is_touch_device (info->classes, info->num_classes,
+                            &touch_source,
+                            &num_touches))
+    {
+      source = touch_source;
+    }
+  else if (!guess_source_from_wacom_type (info, &source))
+    {
+      char *name;
+
+      name = g_ascii_strdown (info->name, -1);
+
+      if (strstr (name, "eraser") != NULL)
+        source = CLUTTER_ERASER_DEVICE;
+      else if (strstr (name, "cursor") != NULL)
+        source = CLUTTER_CURSOR_DEVICE;
+      else if (strstr (name, " pad") != NULL)
+        source = CLUTTER_PAD_DEVICE;
+      else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
+        source = CLUTTER_PEN_DEVICE;
+      else if (strstr (name, "touchpad") != NULL)
+        source = CLUTTER_TOUCHPAD_DEVICE;
+      else
+        source = CLUTTER_POINTER_DEVICE;
+
+      g_free (name);
+    }
+
+  switch (info->use)
+    {
+    case XIMasterKeyboard:
+    case XIMasterPointer:
+      mode = CLUTTER_INPUT_MODE_MASTER;
+      is_enabled = TRUE;
+      break;
+
+    case XISlaveKeyboard:
+    case XISlavePointer:
+      mode = CLUTTER_INPUT_MODE_SLAVE;
+      is_enabled = FALSE;
+      break;
+
+    case XIFloatingSlave:
+    default:
+      mode = CLUTTER_INPUT_MODE_FLOATING;
+      is_enabled = FALSE;
+      break;
+    }
+
+  if (info->use != XIMasterKeyboard &&
+      info->use != XIMasterPointer)
+    {
+      get_device_ids (info, &vendor_id, &product_id);
+      node_path = get_device_node_path (info);
+    }
+
+  if (source == CLUTTER_PAD_DEVICE)
+    {
+      is_enabled = TRUE;
+      get_pad_features (info, &num_rings, &num_strips);
+    }
+
+  retval = g_object_new (META_TYPE_INPUT_DEVICE_X11,
+                         "name", info->name,
+                         "id", info->deviceid,
+                         "has-cursor", (info->use == XIMasterPointer),
+                         "device-type", source,
+                         "device-mode", mode,
+                         "backend", backend,
+                         "enabled", is_enabled,
+                         "vendor-id", vendor_id,
+                         "product-id", product_id,
+                         "device-node", node_path,
+                         "n-rings", num_rings,
+                         "n-strips", num_strips,
+                         "n-mode-groups", MAX (num_rings, num_strips),
+                         "seat", seat_x11,
+                         NULL);
+
+  translate_device_classes (clutter_x11_get_default_display (), retval,
+                            info->classes,
+                            info->num_classes);
+
+#ifdef HAVE_LIBWACOM
+  if (source == CLUTTER_PAD_DEVICE)
+    meta_input_device_x11_ensure_wacom_info (retval, seat_x11->wacom_db);
+#endif
+
+  g_free (vendor_id);
+  g_free (product_id);
+  g_free (node_path);
+
+  g_debug ("Created device '%s' (id: %d, has-cursor: %s)",
+           info->name,
+           info->deviceid,
+           info->use == XIMasterPointer ? "yes" : "no");
+
+  return retval;
+}
+
+static void
+pad_passive_button_grab (ClutterInputDevice *device)
+{
+  XIGrabModifiers xi_grab_mods = { XIAnyModifier, };
+  XIEventMask xi_event_mask;
+  int device_id, rc;
+
+  device_id = clutter_input_device_get_device_id (device);
+
+  xi_event_mask.deviceid = device_id;
+  xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT);
+  xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len);
+
+  XISetMask (xi_event_mask.mask, XI_Motion);
+  XISetMask (xi_event_mask.mask, XI_ButtonPress);
+  XISetMask (xi_event_mask.mask, XI_ButtonRelease);
+
+  clutter_x11_trap_x_errors ();
+  rc = XIGrabButton (clutter_x11_get_default_display (),
+                     device_id, XIAnyButton,
+                     clutter_x11_get_root_window (), None,
+                     XIGrabModeSync, XIGrabModeSync,
+                     True, &xi_event_mask, 1, &xi_grab_mods);
+  if (rc != 0)
+    {
+      g_warning ("Could not passively grab pad device: %s",
+                 clutter_input_device_get_device_name (device));
+    }
+  else
+    {
+      XIAllowEvents (clutter_x11_get_default_display (),
+                     device_id, XIAsyncDevice,
+                     CLUTTER_CURRENT_TIME);
+    }
+
+  clutter_x11_untrap_x_errors ();
+
+  g_free (xi_event_mask.mask);
+}
+
+static ClutterInputDevice *
+add_device (MetaSeatX11    *seat_x11,
+            ClutterBackend *backend,
+            XIDeviceInfo   *info,
+            gboolean        in_construction)
+{
+  ClutterInputDevice *device;
+
+  device = create_device (seat_x11, backend, info);
+
+  g_hash_table_replace (seat_x11->devices_by_id,
+                        GINT_TO_POINTER (info->deviceid),
+                        device);
+
+  if (info->use == XIMasterPointer &&
+      info->deviceid == seat_x11->pointer_id)
+    {
+      seat_x11->core_pointer = device;
+    }
+  else if (info->use == XIMasterKeyboard &&
+           info->deviceid == seat_x11->keyboard_id)
+    {
+      seat_x11->core_keyboard = device;
+    }
+  else if ((info->use == XISlavePointer &&
+            info->attachment == seat_x11->pointer_id) ||
+           (info->use == XISlaveKeyboard &&
+            info->attachment == seat_x11->keyboard_id))
+    {
+      seat_x11->devices = g_list_prepend (seat_x11->devices, device);
+    }
+  else
+    {
+      g_warning ("Unhandled device: %s",
+                 clutter_input_device_get_device_name (device));
+    }
+
+  if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
+    pad_passive_button_grab (device);
+
+  /* relationships between devices and signal emissions are not
+   * necessary while we're constructing the device manager instance
+   */
+  if (!in_construction)
+    {
+      if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
+        {
+          ClutterInputDevice *master;
+
+          master = g_hash_table_lookup (seat_x11->devices_by_id,
+                                        GINT_TO_POINTER (info->attachment));
+          _clutter_input_device_set_associated_device (device, master);
+          _clutter_input_device_add_slave (master, device);
+
+          g_signal_emit_by_name (seat_x11, "device-added", device);
+        }
+    }
+
+  return device;
+}
+
+static void
+remove_device (MetaSeatX11 *seat_x11,
+               int          device_id)
+{
+  ClutterInputDevice *device;
+
+  device = g_hash_table_lookup (seat_x11->devices_by_id,
+                                GINT_TO_POINTER (device_id));
+
+  if (device != NULL)
+    {
+      if (seat_x11->core_pointer == device)
+        {
+          seat_x11->core_pointer = NULL;
+        }
+      else if (seat_x11->core_keyboard == device)
+        {
+          seat_x11->core_keyboard = NULL;
+        }
+      else
+        {
+          seat_x11->devices = g_list_remove (seat_x11->devices, device);
+          g_signal_emit_by_name (seat_x11, "device-removed", device);
+          g_hash_table_remove (seat_x11->devices_by_id,
+                               GINT_TO_POINTER (device_id));
+        }
+
+      g_object_run_dispose (G_OBJECT (device));
+      g_object_unref (device);
+    }
+}
+
+static void
+relate_masters (gpointer key,
+                gpointer value,
+                gpointer data)
+{
+  MetaSeatX11 *seat_x11 = data;
+  ClutterInputDevice *device, *relative;
+
+  device = g_hash_table_lookup (seat_x11->devices_by_id, key);
+  relative = g_hash_table_lookup (seat_x11->devices_by_id, value);
+
+  _clutter_input_device_set_associated_device (device, relative);
+  _clutter_input_device_set_associated_device (relative, device);
+}
+
+static void
+relate_slaves (gpointer key,
+               gpointer value,
+               gpointer data)
+{
+  MetaSeatX11 *seat_x11 = data;
+  ClutterInputDevice *master, *slave;
+
+  slave = g_hash_table_lookup (seat_x11->devices_by_id, key);
+  master = g_hash_table_lookup (seat_x11->devices_by_id, value);
+
+  _clutter_input_device_set_associated_device (slave, master);
+  _clutter_input_device_add_slave (master, slave);
+}
+
+static uint
+device_get_tool_serial (ClutterInputDevice *device)
+{
+  gulong nitems, bytes_after;
+  uint32_t *data = NULL;
+  int serial_id = 0;
+  int rc, format;
+  Atom type;
+  Atom prop;
+
+  prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
+  if (prop == None)
+    return 0;
+
+  clutter_x11_trap_x_errors ();
+  rc = XIGetProperty (clutter_x11_get_default_display (),
+                      clutter_input_device_get_device_id (device),
+                      prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after,
+                      (guchar **) &data);
+  clutter_x11_untrap_x_errors ();
+
+  if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4)
+    serial_id = data[3];
+
+  XFree (data);
+
+  return serial_id;
+}
+
+static void
+translate_hierarchy_event (ClutterBackend   *backend,
+                           MetaSeatX11      *seat_x11,
+                           XIHierarchyEvent *ev)
+{
+  int i;
+
+  for (i = 0; i < ev->num_info; i++)
+    {
+      if (ev->info[i].flags & XIDeviceEnabled &&
+          !g_hash_table_lookup (seat_x11->devices_by_id,
+                                GINT_TO_POINTER (ev->info[i].deviceid)))
+        {
+          XIDeviceInfo *info;
+          int n_devices;
+
+          g_debug ("Hierarchy event: device enabled");
+
+          clutter_x11_trap_x_errors ();
+          info = XIQueryDevice (clutter_x11_get_default_display (),
+                                ev->info[i].deviceid,
+                                &n_devices);
+          clutter_x11_untrap_x_errors ();
+          if (info != NULL)
+            {
+              add_device (seat_x11, backend, &info[0], FALSE);
+              XIFreeDeviceInfo (info);
+            }
+        }
+      else if (ev->info[i].flags & XIDeviceDisabled)
+        {
+          g_debug ("Hierarchy event: device disabled");
+
+          remove_device (seat_x11, ev->info[i].deviceid);
+        }
+      else if ((ev->info[i].flags & XISlaveAttached) ||
+               (ev->info[i].flags & XISlaveDetached))
+        {
+          ClutterInputDevice *master, *slave;
+          XIDeviceInfo *info;
+          int n_devices;
+
+          g_debug ("Hierarchy event: slave %s",
+                   (ev->info[i].flags & XISlaveAttached)
+                   ? "attached"
+                   : "detached");
+
+          slave = g_hash_table_lookup (seat_x11->devices_by_id,
+                                       GINT_TO_POINTER (ev->info[i].deviceid));
+          master = clutter_input_device_get_associated_device (slave);
+
+          /* detach the slave in both cases */
+          if (master != NULL)
+            {
+              _clutter_input_device_remove_slave (master, slave);
+              _clutter_input_device_set_associated_device (slave, NULL);
+            }
+
+          /* and attach the slave to the new master if needed */
+          if (ev->info[i].flags & XISlaveAttached)
+            {
+              clutter_x11_trap_x_errors ();
+              info = XIQueryDevice (clutter_x11_get_default_display (),
+                                    ev->info[i].deviceid,
+                                    &n_devices);
+              clutter_x11_untrap_x_errors ();
+              if (info != NULL)
+                {
+                  master = g_hash_table_lookup (seat_x11->devices_by_id,
+                                                GINT_TO_POINTER (info->attachment));
+                  if (master != NULL)
+                    {
+                      _clutter_input_device_set_associated_device (slave, master);
+                      _clutter_input_device_add_slave (master, slave);
+                    }
+                  XIFreeDeviceInfo (info);
+                }
+            }
+        }
+    }
+}
+
+static void
+translate_property_event (MetaSeatX11 *seat_x11,
+                          XIEvent     *event)
+{
+  XIPropertyEvent *xev = (XIPropertyEvent *) event;
+  Atom serial_ids_prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True);
+  ClutterInputDevice *device;
+
+  device = g_hash_table_lookup (seat_x11->devices_by_id,
+                                GINT_TO_POINTER (xev->deviceid));
+  if (!device)
+    return;
+
+  if (xev->property == serial_ids_prop)
+    {
+      ClutterInputDeviceTool *tool = NULL;
+      ClutterInputDeviceToolType type;
+      int serial_id;
+
+      serial_id = device_get_tool_serial (device);
+
+      if (serial_id != 0)
+        {
+          tool = g_hash_table_lookup (seat_x11->tools_by_serial,
+                                      GUINT_TO_POINTER (serial_id));
+          if (!tool)
+            {
+              type = clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE ?
+                CLUTTER_INPUT_DEVICE_TOOL_ERASER : CLUTTER_INPUT_DEVICE_TOOL_PEN;
+              tool = meta_input_device_tool_x11_new (serial_id, type);
+              g_hash_table_insert (seat_x11->tools_by_serial,
+                                   GUINT_TO_POINTER (serial_id),
+                                   tool);
+            }
+        }
+
+      meta_input_device_x11_update_tool (device, tool);
+      g_signal_emit_by_name (seat_x11, "tool-changed", device, tool);
+    }
+}
+
+static void
+translate_raw_event (MetaSeatX11 *seat_x11,
+                     XEvent      *xevent)
+{
+  ClutterInputDevice *device;
+  XGenericEventCookie *cookie;
+  XIEvent *xi_event;
+  XIRawEvent *xev;
+  float x,y;
+
+  cookie = &xevent->xcookie;
+  xi_event = (XIEvent *) cookie->data;
+  xev = (XIRawEvent *) xi_event;
+
+  device = g_hash_table_lookup (seat_x11->devices_by_id,
+                                GINT_TO_POINTER (xev->deviceid));
+  if (device == NULL)
+    return;
+
+  if (!_clutter_is_input_pointer_a11y_enabled (device))
+    return;
+
+  switch (cookie->evtype)
+    {
+    case XI_RawMotion:
+      g_debug ("raw motion: device:%d '%s'",
+               device->id,
+               device->device_name);
+
+      /* We don't get actual pointer location with raw events, and we cannot
+       * rely on `clutter_input_device_get_coords()` either because of
+       * unreparented toplevels (like all client-side decoration windows),
+       * so we need to explicitely query the pointer here...
+       */
+      if (meta_input_device_x11_get_pointer_location (device, &x, &y))
+        _clutter_input_pointer_a11y_on_motion_event (device, x, y);
+      break;
+    case XI_RawButtonPress:
+    case XI_RawButtonRelease:
+      g_debug ("raw button %s: device:%d '%s' button %i",
+               cookie->evtype == XI_RawButtonPress
+               ? "press  "
+               : "release",
+               device->id,
+               device->device_name,
+               xev->detail);
+      _clutter_input_pointer_a11y_on_button_event (device,
+                                                  xev->detail,
+                                                  (cookie->evtype == XI_RawButtonPress));
+      break;
+    }
+}
+
+static gboolean
+translate_pad_axis (ClutterInputDevice *device,
+                    XIValuatorState    *valuators,
+                    ClutterEventType   *evtype,
+                    uint32_t           *number,
+                    double             *value)
+{
+  double *values;
+  int i;
+
+  values = valuators->values;
+
+  for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++)
+    {
+      double val;
+      uint32_t axis_number = 0;
+
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      val = *values++;
+      if (val <= 0)
+        continue;
+
+      _clutter_input_device_translate_axis (device, i, val, value);
+
+      if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2)
+        {
+          *evtype = CLUTTER_PAD_RING;
+          (*value) *= 360.0;
+        }
+      else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2)
+        {
+          *evtype = CLUTTER_PAD_STRIP;
+        }
+      else
+        continue;
+
+      if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2)
+        axis_number++;
+
+      *number = axis_number;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+translate_pad_event (ClutterEvent       *event,
+                     XIDeviceEvent      *xev,
+                     ClutterInputDevice *device)
+{
+  double value;
+  uint32_t number, mode = 0;
+
+  if (!translate_pad_axis (device, &xev->valuators,
+                           &event->any.type,
+                           &number, &value))
+    return FALSE;
+
+  /* When touching a ring/strip a first XI_Motion event
+   * is generated. Use it to reset the pad state, so
+   * later events actually have a directionality.
+   */
+  if (xev->evtype == XI_Motion)
+    value = -1;
+
+#ifdef HAVE_LIBWACOM
+  mode = meta_input_device_x11_get_pad_group_mode (device, number);
+#endif
+
+  if (event->any.type == CLUTTER_PAD_RING)
+    {
+      event->pad_ring.ring_number = number;
+      event->pad_ring.angle = value;
+      event->pad_ring.mode = mode;
+    }
+  else
+    {
+      event->pad_strip.strip_number = number;
+      event->pad_strip.value = value;
+      event->pad_strip.mode = mode;
+    }
+
+  event->any.time = xev->time;
+  clutter_event_set_device (event, device);
+  clutter_event_set_source_device (event, device);
+
+  g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+           "(value:%f)",
+           event->any.type == CLUTTER_PAD_RING
+           ? "pad ring  "
+           : "pad strip",
+           (unsigned int) xev->event,
+           device->id,
+           device->device_name,
+           event->any.time, value);
+
+  return TRUE;
+}
+
+static ClutterStage *
+get_event_stage (MetaSeatX11 *seat_x11,
+                 XIEvent     *xi_event)
+{
+  Window xwindow = None;
+
+  switch (xi_event->evtype)
+    {
+    case XI_KeyPress:
+    case XI_KeyRelease:
+    case XI_ButtonPress:
+    case XI_ButtonRelease:
+    case XI_Motion:
+    case XI_TouchBegin:
+    case XI_TouchUpdate:
+    case XI_TouchEnd:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+        xwindow = xev->event;
+      }
+      break;
+
+    case XI_Enter:
+    case XI_Leave:
+    case XI_FocusIn:
+    case XI_FocusOut:
+      {
+        XIEnterEvent *xev = (XIEnterEvent *) xi_event;
+
+        xwindow = xev->event;
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  if (xwindow == None)
+    return NULL;
+
+  return meta_x11_get_stage_from_window (xwindow);
+}
+
+/*
+ * print_key_sym: Translate a symbol to its printable form if any
+ * @symbol: the symbol to translate
+ * @buffer: the buffer where to put the translated string
+ * @len: size of the buffer
+ *
+ * Translates @symbol into a printable representation in @buffer, if possible.
+ *
+ * Return value: The number of bytes of the translated string, 0 if the
+ *               symbol can't be printed
+ *
+ * Note: The code is derived from libX11's src/KeyBind.c
+ *       Copyright 1985, 1987, 1998  The Open Group
+ *
+ * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
+ *       does the work for the other keysyms.
+ */
+static int
+print_keysym (uint32_t symbol,
+              char    *buffer,
+              int      len)
+{
+  unsigned long high_bytes;
+  unsigned char c;
+
+  high_bytes = symbol >> 8;
+  if (!(len &&
+        ((high_bytes == 0) ||
+         ((high_bytes == 0xFF) &&
+          (((symbol >= CLUTTER_KEY_BackSpace) &&
+            (symbol <= CLUTTER_KEY_Clear)) ||
+           (symbol == CLUTTER_KEY_Return) ||
+           (symbol == CLUTTER_KEY_Escape) ||
+           (symbol == CLUTTER_KEY_KP_Space) ||
+           (symbol == CLUTTER_KEY_KP_Tab) ||
+           (symbol == CLUTTER_KEY_KP_Enter) ||
+           ((symbol >= CLUTTER_KEY_KP_Multiply) &&
+            (symbol <= CLUTTER_KEY_KP_9)) ||
+           (symbol == CLUTTER_KEY_KP_Equal) ||
+           (symbol == CLUTTER_KEY_Delete))))))
+    return 0;
+
+  /* if X keysym, convert to ascii by grabbing low 7 bits */
+  if (symbol == CLUTTER_KEY_KP_Space)
+    c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
+  else if (high_bytes == 0xFF)
+    c = symbol & 0x7F;
+  else
+    c = symbol & 0xFF;
+
+  buffer[0] = c;
+  return 1;
+}
+
+static double *
+translate_axes (ClutterInputDevice *device,
+                double              x,
+                double              y,
+                XIValuatorState    *valuators)
+{
+  uint32_t n_axes = clutter_input_device_get_n_axes (device);
+  uint32_t i;
+  double *retval;
+  double *values;
+
+  retval = g_new0 (double, n_axes);
+  values = valuators->values;
+
+  for (i = 0; i < valuators->mask_len * 8; i++)
+    {
+      ClutterInputAxis axis;
+      double val;
+
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      axis = clutter_input_device_get_axis (device, i);
+      val = *values++;
+
+      switch (axis)
+        {
+        case CLUTTER_INPUT_AXIS_X:
+          retval[i] = x;
+          break;
+
+        case CLUTTER_INPUT_AXIS_Y:
+          retval[i] = y;
+          break;
+
+        default:
+          _clutter_input_device_translate_axis (device, i, val, &retval[i]);
+          break;
+        }
+    }
+
+  return retval;
+}
+
+static double
+scroll_valuators_changed (ClutterInputDevice *device,
+                          XIValuatorState    *valuators,
+                          double             *dx_p,
+                          double             *dy_p)
+{
+  gboolean retval = FALSE;
+  uint32_t n_axes, n_val, i;
+  double *values;
+
+  n_axes = clutter_input_device_get_n_axes (device);
+  values = valuators->values;
+
+  *dx_p = *dy_p = 0.0;
+
+  n_val = 0;
+
+  for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
+    {
+      ClutterScrollDirection direction;
+      double delta;
+
+      if (!XIMaskIsSet (valuators->mask, i))
+        continue;
+
+      if (_clutter_input_device_get_scroll_delta (device, i,
+                                                  values[n_val],
+                                                  &direction,
+                                                  &delta))
+        {
+          retval = TRUE;
+
+          if (direction == CLUTTER_SCROLL_UP ||
+              direction == CLUTTER_SCROLL_DOWN)
+            *dy_p = delta;
+          else
+            *dx_p = delta;
+        }
+
+      n_val += 1;
+    }
+
+  return retval;
+}
+
+static void
+translate_coords (MetaStageX11 *stage_x11,
+                  double        event_x,
+                  double        event_y,
+                  float        *x_out,
+                  float        *y_out)
+{
+  ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11);
+  ClutterActor *stage = CLUTTER_ACTOR (stage_cogl->wrapper);
+  float stage_width;
+  float stage_height;
+
+  clutter_actor_get_size (stage, &stage_width, &stage_height);
+
+  *x_out = CLAMP (event_x, 0, stage_width);
+  *y_out = CLAMP (event_y, 0, stage_height);
+}
+
+static void
+meta_seat_x11_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+  switch (prop_id)
+    {
+    case PROP_OPCODE:
+      seat_x11->opcode = g_value_get_int (value);
+      break;
+    case PROP_POINTER_ID:
+      seat_x11->pointer_id = g_value_get_int (value);
+      break;
+    case PROP_KEYBOARD_ID:
+      seat_x11->keyboard_id = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_seat_x11_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+  switch (prop_id)
+    {
+    case PROP_OPCODE:
+      g_value_set_int (value, seat_x11->opcode);
+      break;
+    case PROP_POINTER_ID:
+      g_value_set_int (value, seat_x11->pointer_id);
+      break;
+    case PROP_KEYBOARD_ID:
+      g_value_set_int (value, seat_x11->keyboard_id);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_seat_x11_constructed (GObject *object)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+  ClutterBackend *backend = clutter_get_default_backend ();
+  GHashTable *masters, *slaves;
+  XIDeviceInfo *info;
+  XIEventMask event_mask;
+  unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0, };
+  int n_devices, i;
+  Display *xdisplay;
+
+  xdisplay = clutter_x11_get_default_display ();
+  masters = g_hash_table_new (NULL, NULL);
+  slaves = g_hash_table_new (NULL, NULL);
+
+  info = XIQueryDevice (clutter_x11_get_default_display (),
+                        XIAllDevices, &n_devices);
+
+  for (i = 0; i < n_devices; i++)
+    {
+      XIDeviceInfo *xi_device = &info[i];
+
+      if (!xi_device->enabled)
+        continue;
+
+      add_device (seat_x11, backend, xi_device, TRUE);
+
+      if (xi_device->use == XIMasterPointer ||
+          xi_device->use == XIMasterKeyboard)
+        {
+          g_hash_table_insert (masters,
+                               GINT_TO_POINTER (xi_device->deviceid),
+                               GINT_TO_POINTER (xi_device->attachment));
+        }
+      else if (xi_device->use == XISlavePointer ||
+               xi_device->use == XISlaveKeyboard)
+        {
+          g_hash_table_insert (slaves,
+                               GINT_TO_POINTER (xi_device->deviceid),
+                               GINT_TO_POINTER (xi_device->attachment));
+        }
+    }
+
+  XIFreeDeviceInfo (info);
+
+  g_hash_table_foreach (masters, relate_masters, seat_x11);
+  g_hash_table_destroy (masters);
+
+  g_hash_table_foreach (slaves, relate_slaves, seat_x11);
+  g_hash_table_destroy (slaves);
+
+  XISetMask (mask, XI_HierarchyChanged);
+  XISetMask (mask, XI_DeviceChanged);
+  XISetMask (mask, XI_PropertyEvent);
+
+  event_mask.deviceid = XIAllDevices;
+  event_mask.mask_len = sizeof (mask);
+  event_mask.mask = mask;
+
+  XISelectEvents (xdisplay, clutter_x11_get_root_window (),
+                  &event_mask, 1);
+
+  memset(mask, 0, sizeof (mask));
+  XISetMask (mask, XI_RawMotion);
+  XISetMask (mask, XI_RawButtonPress);
+  XISetMask (mask, XI_RawButtonRelease);
+
+  event_mask.deviceid = XIAllMasterDevices;
+  event_mask.mask_len = sizeof (mask);
+  event_mask.mask = mask;
+
+  XISelectEvents (xdisplay, clutter_x11_get_root_window (),
+                  &event_mask, 1);
+
+  XSync (xdisplay, False);
+
+  if (G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed)
+    G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed (object);
+}
+
+static void
+meta_seat_x11_finalize (GObject *object)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
+
+  g_hash_table_unref (seat_x11->devices_by_id);
+  g_hash_table_unref (seat_x11->tools_by_serial);
+  g_list_free (seat_x11->devices);
+
+#ifdef HAVE_LIBWACOM
+  libwacom_database_destroy (seat_x11->wacom_db);
+#endif
+
+  G_OBJECT_CLASS (meta_seat_x11_parent_class)->finalize (object);
+}
+
+static ClutterInputDevice *
+meta_seat_x11_get_pointer (ClutterSeat *seat)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+
+  return seat_x11->core_pointer;
+}
+
+static ClutterInputDevice *
+meta_seat_x11_get_keyboard (ClutterSeat *seat)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+
+  return seat_x11->core_keyboard;
+}
+
+static GList *
+meta_seat_x11_list_devices (ClutterSeat *seat)
+{
+  MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
+  GList *retval = NULL, *l;
+
+  for (l = seat_x11->devices; l; l = l->next)
+    retval = g_list_prepend (retval, l->data);
+
+  return retval;
+}
+
+static void
+meta_seat_x11_class_init (MetaSeatX11Class *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass);
+
+  object_class->set_property = meta_seat_x11_set_property;
+  object_class->get_property = meta_seat_x11_get_property;
+  object_class->constructed = meta_seat_x11_constructed;
+  object_class->finalize = meta_seat_x11_finalize;
+
+  seat_class->get_pointer = meta_seat_x11_get_pointer;
+  seat_class->get_keyboard = meta_seat_x11_get_keyboard;
+  seat_class->list_devices = meta_seat_x11_list_devices;
+
+  props[PROP_OPCODE] =
+    g_param_spec_int ("opcode",
+                      "Opcode",
+                      "Opcode",
+                      0, G_MAXINT, 0,
+                      G_PARAM_READWRITE |
+                      G_PARAM_CONSTRUCT_ONLY);
+  props[PROP_POINTER_ID] =
+    g_param_spec_int ("pointer-id",
+                      "Pointer ID",
+                      "Pointer ID",
+                      2, G_MAXINT, 2,
+                      G_PARAM_READWRITE |
+                      G_PARAM_CONSTRUCT_ONLY);
+  props[PROP_KEYBOARD_ID] =
+    g_param_spec_int ("keyboard-id",
+                      "Keyboard ID",
+                      "Keyboard ID",
+                      2, G_MAXINT, 2,
+                      G_PARAM_READWRITE |
+                      G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+meta_seat_x11_init (MetaSeatX11 *seat)
+{
+  seat->devices_by_id = g_hash_table_new_full (NULL, NULL,
+                                               NULL,
+                                               (GDestroyNotify) g_object_unref);
+  seat->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL,
+                                                 (GDestroyNotify) g_object_unref);
+
+#ifdef HAVE_LIBWACOM
+  seat->wacom_db = libwacom_database_new ();
+#endif
+}
+
+MetaSeatX11 *
+meta_seat_x11_new (int opcode,
+                   int master_pointer,
+                   int master_keyboard)
+{
+  return g_object_new (META_TYPE_SEAT_X11,
+                       "opcode", opcode,
+                       "pointer-id", master_pointer,
+                       "keyboard-id", master_keyboard,
+                       NULL);
+}
+
+gboolean
+meta_seat_x11_translate_event (MetaSeatX11  *seat,
+                               XEvent       *xevent,
+                               ClutterEvent *event)
+{
+  gboolean retval = FALSE;
+  ClutterBackend *backend = clutter_get_default_backend ();
+  ClutterStage *stage = NULL;
+  MetaStageX11 *stage_x11 = NULL;
+  ClutterInputDevice *device, *source_device;
+  XGenericEventCookie *cookie;
+  XIEvent *xi_event;
+
+  cookie = &xevent->xcookie;
+
+  if (cookie->type != GenericEvent ||
+      cookie->extension != seat->opcode)
+    return FALSE;
+
+  xi_event = (XIEvent *) cookie->data;
+
+  if (!xi_event)
+    return FALSE;
+
+  if (cookie->evtype == XI_RawMotion ||
+      cookie->evtype == XI_RawButtonPress ||
+      cookie->evtype == XI_RawButtonRelease)
+    {
+      translate_raw_event (seat, xevent);
+      return FALSE;
+    }
+
+  if (!(xi_event->evtype == XI_HierarchyChanged ||
+        xi_event->evtype == XI_DeviceChanged ||
+        xi_event->evtype == XI_PropertyEvent))
+    {
+      stage = get_event_stage (seat, xi_event);
+      if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
+        return FALSE;
+      else
+        stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
+    }
+
+  event->any.stage = stage;
+
+  switch (xi_event->evtype)
+    {
+    case XI_HierarchyChanged:
+      {
+        XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
+
+        translate_hierarchy_event (backend, seat, xev);
+      }
+      retval = FALSE;
+      break;
+
+    case XI_DeviceChanged:
+      {
+        XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
+
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+        if (device)
+          {
+            _clutter_input_device_reset_axes (device);
+            translate_device_classes (clutter_x11_get_default_display (),
+                                      device,
+                                      xev->classes,
+                                      xev->num_classes);
+          }
+
+        if (source_device)
+          _clutter_input_device_reset_scroll_info (source_device);
+      }
+      retval = FALSE;
+      break;
+    case XI_KeyPress:
+    case XI_KeyRelease:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+        MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (clutter_backend_get_keymap (backend));
+        MetaEventX11 *event_x11;
+        char buffer[7] = { 0, };
+        gunichar n;
+
+        event->key.type = event->type = (xev->evtype == XI_KeyPress)
+                                      ? CLUTTER_KEY_PRESS
+                                      : CLUTTER_KEY_RELEASE;
+
+        if (xev->evtype == XI_KeyPress && xev->flags & XIKeyRepeat)
+          clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED);
+
+        event->key.time = xev->time;
+        event->key.stage = stage;
+        meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group);
+        event->key.hardware_keycode = xev->detail;
+
+          /* keyval is the key ignoring all modifiers ('1' vs. '!') */
+        event->key.keyval =
+          meta_keymap_x11_translate_key_state (keymap_x11,
+                                               event->key.hardware_keycode,
+                                               &event->key.modifier_state,
+                                               NULL);
+
+        /* KeyEvents have platform specific data associated to them */
+        event_x11 = meta_event_x11_new ();
+        _clutter_event_set_platform_data (event, event_x11);
+
+        event_x11->key_group =
+          meta_keymap_x11_get_key_group (keymap_x11,
+                                         event->key.modifier_state);
+        event_x11->key_is_modifier =
+          meta_keymap_x11_get_is_modifier (keymap_x11,
+                                           event->key.hardware_keycode);
+        event_x11->num_lock_set =
+          clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (keymap_x11));
+        event_x11->caps_lock_set =
+          clutter_keymap_get_caps_lock_state (CLUTTER_KEYMAP (keymap_x11));
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+        clutter_event_set_source_device (event, source_device);
+
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+        clutter_event_set_device (event, device);
+
+        /* XXX keep this in sync with the evdev device manager */
+        n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
+        if (n == 0)
+          {
+            /* not printable */
+            event->key.unicode_value = (gunichar) '\0';
+          }
+        else
+          {
+            event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
+            if (event->key.unicode_value == -1 ||
+                event->key.unicode_value == -2)
+              event->key.unicode_value = (gunichar) '\0';
+          }
+
+        g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)",
+                 event->any.type == CLUTTER_KEY_PRESS
+                 ? "key press  "
+                 : "key release",
+                 (unsigned int) stage_x11->xwin,
+                 xev->deviceid,
+                 xev->sourceid,
+                 event->key.keyval ? buffer : "(none)",
+                 event->key.keyval);
+
+        if (xi_event->evtype == XI_KeyPress)
+          meta_stage_x11_set_user_time (stage_x11, event->key.time);
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_ButtonPress:
+    case XI_ButtonRelease:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+
+        /* Set the stage for core events coming out of nowhere (see bug #684509) */
+        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
+            clutter_input_device_get_pointer_stage (device) == NULL &&
+            stage != NULL)
+          _clutter_input_device_set_stage (device, stage);
+
+       if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
+          {
+            /* We got these events because of the passive button grab */
+            XIAllowEvents (clutter_x11_get_default_display (),
+                           xev->sourceid,
+                           XIAsyncDevice,
+                           xev->time);
+
+            event->any.stage = stage;
+
+            if (xev->detail >= 4 && xev->detail <= 7)
+              {
+                retval = FALSE;
+
+                if (xi_event->evtype == XI_ButtonPress &&
+                    translate_pad_event (event, xev, source_device))
+                  retval = TRUE;
+
+                break;
+              }
+
+            event->any.type =
+              (xi_event->evtype == XI_ButtonPress) ? CLUTTER_PAD_BUTTON_PRESS
+                                                   : CLUTTER_PAD_BUTTON_RELEASE;
+            event->any.time = xev->time;
+
+            /* The 4-7 button range is taken as non-existent on pad devices,
+             * let the buttons above that take over this range.
+             */
+            if (xev->detail > 7)
+              xev->detail -= 4;
+
+            /* Pad buttons are 0-indexed */
+            event->pad_button.button = xev->detail - 1;
+#ifdef HAVE_LIBWACOM
+            meta_input_device_x11_update_pad_state (device,
+                                                    event->pad_button.button,
+                                                    (xi_event->evtype == XI_ButtonPress),
+                                                    &event->pad_button.group,
+                                                    &event->pad_button.mode);
+#endif
+            clutter_event_set_device (event, device);
+            clutter_event_set_source_device (event, source_device);
+
+            g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+                     "(button:%d)",
+                     event->any.type == CLUTTER_BUTTON_PRESS
+                     ? "pad button press  "
+                     : "pad button release",
+                     (unsigned int) stage_x11->xwin,
+                     device->id,
+                     device->device_name,
+                     event->any.time,
+                     event->pad_button.button);
+            retval = TRUE;
+            break;
+          }
+
+        switch (xev->detail)
+          {
+          case 4:
+          case 5:
+          case 6:
+          case 7:
+            /* we only generate Scroll events on ButtonPress */
+            if (xi_event->evtype == XI_ButtonRelease)
+              return FALSE;
+
+            event->scroll.type = event->type = CLUTTER_SCROLL;
+
+            if (xev->detail == 4)
+              event->scroll.direction = CLUTTER_SCROLL_UP;
+            else if (xev->detail == 5)
+              event->scroll.direction = CLUTTER_SCROLL_DOWN;
+            else if (xev->detail == 6)
+              event->scroll.direction = CLUTTER_SCROLL_LEFT;
+            else
+              event->scroll.direction = CLUTTER_SCROLL_RIGHT;
+
+            event->scroll.stage = stage;
+
+            event->scroll.time = xev->time;
+            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
+            meta_input_device_x11_translate_state (event,
+                                                   &xev->mods,
+                                                   &xev->buttons,
+                                                   &xev->group);
+
+            clutter_event_set_source_device (event, source_device);
+            clutter_event_set_device (event, device);
+
+            event->scroll.axes = translate_axes (event->scroll.device,
+                                                 event->scroll.x,
+                                                 event->scroll.y,
+                                                 &xev->valuators);
+            g_debug ("scroll: win:0x%x, device:%d '%s', time:%d "
+                     "(direction:%s, "
+                     "x:%.2f, y:%.2f, "
+                     "emulated:%s)",
+                     (unsigned int) stage_x11->xwin,
+                     device->id,
+                     device->device_name,
+                     event->any.time,
+                     event->scroll.direction == CLUTTER_SCROLL_UP ? "up" :
+                     event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" :
+                     event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" :
+                     event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" :
+                     "invalid",
+                     event->scroll.x,
+                     event->scroll.y,
+                     (xev->flags & XIPointerEmulated) ? "yes" : "no");
+            break;
+
+          default:
+            event->button.type = event->type =
+              (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
+                                                   : CLUTTER_BUTTON_RELEASE;
+
+            event->button.stage = stage;
+
+            event->button.time = xev->time;
+            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->button.x, &event->button.y);
+            event->button.button = xev->detail;
+            meta_input_device_x11_translate_state (event,
+                                                   &xev->mods,
+                                                   &xev->buttons,
+                                                   &xev->group);
+
+            clutter_event_set_source_device (event, source_device);
+            clutter_event_set_device (event, device);
+            clutter_event_set_device_tool (event,
+                                           meta_input_device_x11_get_current_tool (source_device));
+
+            event->button.axes = translate_axes (event->button.device,
+                                                 event->button.x,
+                                                 event->button.y,
+                                                 &xev->valuators);
+            g_debug ("%s: win:0x%x, device:%d '%s', time:%d "
+                     "(button:%d, "
+                     "x:%.2f, y:%.2f, "
+                     "axes:%s, "
+                     "emulated:%s)",
+                     event->any.type == CLUTTER_BUTTON_PRESS
+                     ? "button press  "
+                     : "button release",
+                     (unsigned int) stage_x11->xwin,
+                     device->id,
+                     device->device_name,
+                     event->any.time,
+                     event->button.button,
+                     event->button.x,
+                     event->button.y,
+                     event->button.axes != NULL ? "yes" : "no",
+                     (xev->flags & XIPointerEmulated) ? "yes" : "no");
+            break;
+          }
+
+        if (source_device != NULL && device->stage != NULL)
+          _clutter_input_device_set_stage (source_device, device->stage);
+
+        if (xev->flags & XIPointerEmulated)
+          _clutter_event_set_pointer_emulated (event, TRUE);
+
+        if (xi_event->evtype == XI_ButtonPress)
+          meta_stage_x11_set_user_time (stage_x11, event->button.time);
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_Motion:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+        double delta_x, delta_y;
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+
+        if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
+          {
+            event->any.stage = stage;
+
+            if (translate_pad_event (event, xev, source_device))
+              retval = TRUE;
+            break;
+          }
+
+        /* Set the stage for core events coming out of nowhere (see bug #684509) */
+        if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER &&
+            clutter_input_device_get_pointer_stage (device) == NULL &&
+            stage != NULL)
+          _clutter_input_device_set_stage (device, stage);
+
+        if (scroll_valuators_changed (source_device,
+                                      &xev->valuators,
+                                      &delta_x, &delta_y))
+          {
+            event->scroll.type = event->type = CLUTTER_SCROLL;
+            event->scroll.direction = CLUTTER_SCROLL_SMOOTH;
+
+            event->scroll.stage = stage;
+            event->scroll.time = xev->time;
+            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y);
+            meta_input_device_x11_translate_state (event,
+                                                   &xev->mods,
+                                                   &xev->buttons,
+                                                   &xev->group);
+
+            clutter_event_set_scroll_delta (event, delta_x, delta_y);
+            clutter_event_set_source_device (event, source_device);
+            clutter_event_set_device (event, device);
+
+            g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
+                     (unsigned int) stage_x11->xwin,
+                     event->scroll.device->id,
+                     event->scroll.device->device_name,
+                     event->scroll.x,
+                     event->scroll.y,
+                     delta_x, delta_y);
+
+            retval = TRUE;
+            break;
+          }
+
+        event->motion.type = event->type = CLUTTER_MOTION;
+
+        event->motion.stage = stage;
+
+        event->motion.time = xev->time;
+        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->motion.x, &event->motion.y);
+        meta_input_device_x11_translate_state (event,
+                                               &xev->mods,
+                                               &xev->buttons,
+                                               &xev->group);
+
+        clutter_event_set_source_device (event, source_device);
+        clutter_event_set_device (event, device);
+        clutter_event_set_device_tool (event,
+                                       meta_input_device_x11_get_current_tool (source_device));
+
+        event->motion.axes = translate_axes (event->motion.device,
+                                             event->motion.x,
+                                             event->motion.y,
+                                             &xev->valuators);
+
+        if (source_device != NULL && device->stage != NULL)
+          _clutter_input_device_set_stage (source_device, device->stage);
+
+        if (xev->flags & XIPointerEmulated)
+          _clutter_event_set_pointer_emulated (event, TRUE);
+
+        g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
+                 (unsigned int) stage_x11->xwin,
+                 event->motion.device->id,
+                 event->motion.device->device_name,
+                 event->motion.x,
+                 event->motion.y,
+                 event->motion.axes != NULL ? "yes" : "no");
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_TouchBegin:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+        if (!_clutter_input_device_get_stage (device))
+          _clutter_input_device_set_stage (device, stage);
+      }
+      /* Fall through */
+    case XI_TouchEnd:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+
+        if (xi_event->evtype == XI_TouchBegin)
+          event->touch.type = event->type = CLUTTER_TOUCH_BEGIN;
+        else
+          event->touch.type = event->type = CLUTTER_TOUCH_END;
+
+        event->touch.stage = stage;
+        event->touch.time = xev->time;
+        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
+        meta_input_device_x11_translate_state (event,
+                                               &xev->mods,
+                                               &xev->buttons,
+                                               &xev->group);
+
+        clutter_event_set_source_device (event, source_device);
+
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+        clutter_event_set_device (event, device);
+
+        event->touch.axes = translate_axes (event->touch.device,
+                                            event->motion.x,
+                                            event->motion.y,
+                                            &xev->valuators);
+
+        if (xi_event->evtype == XI_TouchBegin)
+          {
+            event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
+
+            meta_stage_x11_set_user_time (stage_x11, event->touch.time);
+          }
+
+        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+
+        if (xev->flags & XITouchEmulatingPointer)
+          _clutter_event_set_pointer_emulated (event, TRUE);
+
+        g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
+                 event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
+                 (unsigned int) stage_x11->xwin,
+                 event->touch.device->id,
+                 event->touch.device->device_name,
+                 GPOINTER_TO_UINT (event->touch.sequence),
+                 event->touch.x,
+                 event->touch.y,
+                 event->touch.axes != NULL ? "yes" : "no");
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_TouchUpdate:
+      {
+        XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+
+        event->touch.type = event->type = CLUTTER_TOUCH_UPDATE;
+        event->touch.stage = stage;
+        event->touch.time = xev->time;
+        event->touch.sequence = GUINT_TO_POINTER (xev->detail);
+        translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y);
+
+        clutter_event_set_source_device (event, source_device);
+
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+        clutter_event_set_device (event, device);
+
+        event->touch.axes = translate_axes (event->touch.device,
+                                            event->motion.x,
+                                            event->motion.y,
+                                            &xev->valuators);
+
+        meta_input_device_x11_translate_state (event,
+                                               &xev->mods,
+                                               &xev->buttons,
+                                               &xev->group);
+        event->touch.modifier_state |= CLUTTER_BUTTON1_MASK;
+
+        if (xev->flags & XITouchEmulatingPointer)
+          _clutter_event_set_pointer_emulated (event, TRUE);
+
+        g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)",
+                 (unsigned int) stage_x11->xwin,
+                 event->touch.device->id,
+                 event->touch.device->device_name,
+                 GPOINTER_TO_UINT (event->touch.sequence),
+                 event->touch.x,
+                 event->touch.y,
+                 event->touch.axes != NULL ? "yes" : "no");
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_Enter:
+    case XI_Leave:
+      {
+        XIEnterEvent *xev = (XIEnterEvent *) xi_event;
+
+        device = g_hash_table_lookup (seat->devices_by_id,
+                                      GINT_TO_POINTER (xev->deviceid));
+
+        source_device = g_hash_table_lookup (seat->devices_by_id,
+                                             GINT_TO_POINTER (xev->sourceid));
+
+        if (xi_event->evtype == XI_Enter)
+          {
+            event->crossing.type = event->type = CLUTTER_ENTER;
+
+            event->crossing.stage = stage;
+            event->crossing.source = CLUTTER_ACTOR (stage);
+            event->crossing.related = NULL;
+
+            event->crossing.time = xev->time;
+            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
+
+            _clutter_input_device_set_stage (device, stage);
+          }
+        else
+          {
+            if (device->stage == NULL)
+              {
+                g_debug ("Discarding Leave for ButtonRelease "
+                         "event off-stage");
+                retval = FALSE;
+                break;
+              }
+
+            event->crossing.type = event->type = CLUTTER_LEAVE;
+
+            event->crossing.stage = stage;
+            event->crossing.source = CLUTTER_ACTOR (stage);
+            event->crossing.related = NULL;
+
+            event->crossing.time = xev->time;
+            translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y);
+
+            _clutter_input_device_set_stage (device, NULL);
+          }
+
+        _clutter_input_device_reset_scroll_info (source_device);
+
+        clutter_event_set_device (event, device);
+        clutter_event_set_source_device (event, source_device);
+
+        retval = TRUE;
+      }
+      break;
+
+    case XI_FocusIn:
+    case XI_FocusOut:
+      retval = FALSE;
+      break;
+    case XI_PropertyEvent:
+      translate_property_event (seat, xi_event);
+      retval = FALSE;
+      break;
+    }
+
+  return retval;
+}
+
+ClutterInputDevice *
+meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11,
+                                int          device_id)
+{
+  return g_hash_table_lookup (seat_x11->devices_by_id,
+                              GINT_TO_POINTER (device_id));
+}
diff --git a/src/backends/x11/meta-seat-x11.h b/src/backends/x11/meta-seat-x11.h
new file mode 100644
index 000000000..ff7d1000d
--- /dev/null
+++ b/src/backends/x11/meta-seat-x11.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#ifndef META_SEAT_X11_H
+#define META_SEAT_X11_H
+
+#include "clutter/clutter.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_SEAT_X11 meta_seat_x11_get_type ()
+G_DECLARE_FINAL_TYPE (MetaSeatX11, meta_seat_x11, META, SEAT_X11, ClutterSeat)
+
+MetaSeatX11 * meta_seat_x11_new (int opcode,
+                                 int master_pointer,
+                                int master_keyboard);
+gboolean meta_seat_x11_translate_event (MetaSeatX11  *seat,
+                                       XEvent       *xevent,
+                                       ClutterEvent *event);
+ClutterInputDevice * meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11,
+                                                     int          device_id);
+
+G_END_DECLS
+
+#endif /* META_SEAT_X11_H */
diff --git a/src/meson.build b/src/meson.build
index db63ed317..da2b52f46 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -256,6 +256,8 @@ mutter_sources = [
   'backends/x11/meta-input-device-tool-x11.h',
   'backends/x11/meta-input-settings-x11.c',
   'backends/x11/meta-input-settings-x11.h',
+  'backends/x11/meta-seat-x11.c',
+  'backends/x11/meta-seat-x11.h',
   'backends/x11/meta-keymap-x11.c',
   'backends/x11/meta-keymap-x11.h',
   'backends/x11/meta-monitor-manager-xrandr.c',


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