[mutter/wip/carlosg/clutter-seat: 1/22] native: Implement ClutterSeat



commit 5bb3d86884e1533048608094b3473b9cd4bbb040
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Sep 25 23:04:25 2019 +0200

    native: Implement ClutterSeat
    
    We had the MetaSeatNative struct around, so use it as the base of
    this Clutter object. A few responsibilities were drawn from
    ClutterDeviceManager.
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/852

 src/backends/native/meta-backend-native.c          |   26 +-
 src/backends/native/meta-backend-native.h          |    2 +
 src/backends/native/meta-clutter-backend-native.c  |   38 +-
 src/backends/native/meta-clutter-backend-native.h  |    2 +
 src/backends/native/meta-device-manager-native.c   | 2404 +-------------------
 src/backends/native/meta-device-manager-native.h   |  123 +-
 src/backends/native/meta-input-device-native.c     |   27 +-
 src/backends/native/meta-input-device-native.h     |    6 +-
 src/backends/native/meta-launcher.c                |    9 +-
 src/backends/native/meta-seat-native.c             | 2241 +++++++++++++++++-
 src/backends/native/meta-seat-native.h             |  154 +-
 .../native/meta-virtual-input-device-native.c      |    7 +-
 src/wayland/meta-wayland-touch.c                   |   17 +-
 13 files changed, 2483 insertions(+), 2573 deletions(-)
---
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 469cd0cc1..efcf7febf 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -58,6 +58,7 @@
 #include "backends/native/meta-launcher.h"
 #include "backends/native/meta-monitor-manager-kms.h"
 #include "backends/native/meta-renderer-native.h"
+#include "backends/native/meta-seat-native.h"
 #include "backends/native/meta-stage-native.h"
 #include "cogl/cogl-trace.h"
 #include "core/meta-border.h"
@@ -340,15 +341,18 @@ meta_backend_native_create_clutter_backend (MetaBackend *backend)
 static void
 meta_backend_native_post_init (MetaBackend *backend)
 {
-  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
   MetaSettings *settings = meta_backend_get_settings (backend);
 
   META_BACKEND_CLASS (meta_backend_native_parent_class)->post_init (backend);
 
-  meta_device_manager_native_set_pointer_constrain_callback (manager, pointer_constrain_callback,
-                                                             NULL, NULL);
-  meta_device_manager_native_set_relative_motion_filter (manager, relative_motion_filter,
-                                                         meta_backend_get_monitor_manager (backend));
+  meta_seat_native_set_pointer_constrain_callback (META_SEAT_NATIVE (seat),
+                                                   pointer_constrain_callback,
+                                                   NULL, NULL);
+  meta_seat_native_set_relative_motion_filter (META_SEAT_NATIVE (seat),
+                                               relative_motion_filter,
+                                               meta_backend_get_monitor_manager (backend));
 
   if (meta_settings_is_experimental_feature_enabled (settings,
                                                      META_EXPERIMENTAL_FEATURE_RT_SCHEDULER))
@@ -413,7 +417,7 @@ meta_backend_native_warp_pointer (MetaBackend *backend,
   guint32 time_ = 0;
 
   /* Warp the input device pointer state. */
-  meta_device_manager_native_warp_pointer (device, time_, x, y);
+  meta_seat_native_warp_pointer (device, time_, x, y);
 
   /* Warp displayed pointer cursor. */
   meta_cursor_tracker_update_position (cursor_tracker, x, y);
@@ -796,11 +800,14 @@ meta_backend_native_pause (MetaBackendNative *native)
     meta_backend_get_monitor_manager (backend);
   MetaMonitorManagerKms *monitor_manager_kms =
     META_MONITOR_MANAGER_KMS (monitor_manager);
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  MetaSeatNative *seat =
+    META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
 
   COGL_TRACE_BEGIN_SCOPED (MetaBackendNativePause,
                            "Backend (pause)");
 
-  meta_device_manager_native_release_devices ();
+  meta_seat_native_release_devices (seat);
   clutter_stage_freeze_updates (stage);
 
   disconnect_udev_device_added_handler (native);
@@ -819,6 +826,9 @@ void meta_backend_native_resume (MetaBackendNative *native)
   MetaInputSettings *input_settings;
   MetaIdleMonitor *idle_monitor;
   ClutterDeviceManager *device_manager;
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  MetaSeatNative *seat =
+    META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
 
   COGL_TRACE_BEGIN_SCOPED (MetaBackendNativeResume,
                            "Backend (resume)");
@@ -827,7 +837,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
 
   connect_udev_device_added_handler (native);
 
-  meta_device_manager_native_reclaim_devices ();
+  meta_seat_native_reclaim_devices (seat);
   clutter_stage_thaw_updates (stage);
 
   clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
diff --git a/src/backends/native/meta-backend-native.h b/src/backends/native/meta-backend-native.h
index b389652b2..29a9b5c1b 100644
--- a/src/backends/native/meta-backend-native.h
+++ b/src/backends/native/meta-backend-native.h
@@ -47,4 +47,6 @@ MetaUdev * meta_backend_native_get_udev (MetaBackendNative *native);
 
 MetaKms * meta_backend_native_get_kms (MetaBackendNative *native);
 
+void meta_backend_native_set_seat_id (const gchar *seat_id);
+
 #endif /* META_BACKEND_NATIVE_H */
diff --git a/src/backends/native/meta-clutter-backend-native.c 
b/src/backends/native/meta-clutter-backend-native.c
index e60680803..2cab84734 100644
--- a/src/backends/native/meta-clutter-backend-native.c
+++ b/src/backends/native/meta-clutter-backend-native.c
@@ -46,6 +46,7 @@
 #include "backends/meta-renderer.h"
 #include "backends/native/meta-device-manager-native.h"
 #include "backends/native/meta-keymap-native.h"
+#include "backends/native/meta-seat-native.h"
 #include "backends/native/meta-stage-native.h"
 #include "clutter/clutter.h"
 #include "core/bell.h"
@@ -55,11 +56,14 @@ struct _MetaClutterBackendNative
 {
   ClutterBackendEglNative parent;
 
+  MetaSeatNative *main_seat;
   MetaStageNative *stage_native;
   MetaKeymapNative *keymap;
   MetaDeviceManagerNative *device_manager;
 };
 
+static gchar *evdev_seat_id;
+
 G_DEFINE_TYPE (MetaClutterBackendNative, meta_clutter_backend_native,
                CLUTTER_TYPE_BACKEND_EGL_NATIVE)
 
@@ -127,11 +131,23 @@ static void
 meta_clutter_backend_native_init_events (ClutterBackend *backend)
 {
   MetaClutterBackendNative *backend_native = META_CLUTTER_BACKEND_NATIVE (backend);
+  const gchar *seat_id = evdev_seat_id ? evdev_seat_id : "seat0";
 
   backend_native->keymap = g_object_new (META_TYPE_KEYMAP_NATIVE, NULL);
-  backend_native->device_manager = g_object_new (META_TYPE_DEVICE_MANAGER_NATIVE,
-                                                 "backend", backend,
-                                                 NULL);
+  backend_native->main_seat = g_object_new (META_TYPE_SEAT_NATIVE,
+                                            "backend", backend,
+                                            "seat-id", seat_id,
+                                            NULL);
+  backend_native->device_manager =
+    meta_device_manager_native_new (backend, backend_native->main_seat);
+}
+
+static ClutterSeat *
+meta_clutter_backend_native_get_default_seat (ClutterBackend *backend)
+{
+  MetaClutterBackendNative *backend_native = META_CLUTTER_BACKEND_NATIVE (backend);
+
+  return CLUTTER_SEAT (backend_native->main_seat);
 }
 
 static void
@@ -150,4 +166,20 @@ meta_clutter_backend_native_class_init (MetaClutterBackendNativeClass *klass)
   clutter_backend_class->get_device_manager = meta_clutter_backend_native_get_device_manager;
   clutter_backend_class->get_keymap = meta_clutter_backend_native_get_keymap;
   clutter_backend_class->init_events = meta_clutter_backend_native_init_events;
+  clutter_backend_class->get_default_seat = meta_clutter_backend_native_get_default_seat;
+}
+
+/**
+ * meta_cluter_backend_native_set_seat_id:
+ * @seat_id: The seat ID
+ *
+ * Sets the seat to assign to the libinput context.
+ *
+ * For reliable effects, this function must be called before clutter_init().
+ */
+void
+meta_clutter_backend_native_set_seat_id (const gchar *seat_id)
+{
+  g_free (evdev_seat_id);
+  evdev_seat_id = g_strdup (seat_id);
 }
diff --git a/src/backends/native/meta-clutter-backend-native.h 
b/src/backends/native/meta-clutter-backend-native.h
index 4cebc0b84..8914f8aa3 100644
--- a/src/backends/native/meta-clutter-backend-native.h
+++ b/src/backends/native/meta-clutter-backend-native.h
@@ -38,4 +38,6 @@ G_DECLARE_FINAL_TYPE (MetaClutterBackendNative, meta_clutter_backend_native,
 
 MetaStageNative * meta_clutter_backend_native_get_stage_native (ClutterBackend *backend);
 
+void meta_clutter_backend_native_set_seat_id (const gchar *seat_id);
+
 #endif /* META_CLUTTER_BACKEND_NATIVE_H */
diff --git a/src/backends/native/meta-device-manager-native.c 
b/src/backends/native/meta-device-manager-native.c
index c1fa4978c..299b4bcda 100644
--- a/src/backends/native/meta-device-manager-native.c
+++ b/src/backends/native/meta-device-manager-native.c
@@ -32,7 +32,6 @@
 #include <unistd.h>
 
 #include <glib.h>
-#include <libinput.h>
 
 #include "backends/native/meta-device-manager-native.h"
 #include "backends/native/meta-event-native.h"
@@ -44,56 +43,9 @@
 #include "backends/native/meta-xkb-utils.h"
 #include "clutter/clutter-mutter.h"
 
-/*
- * Clutter makes the assumption that two core devices have ID's 2 and 3 (core
- * pointer and core keyboard).
- *
- * Since the two first devices that will ever be created will be the virtual
- * pointer and virtual keyboard of the first seat, we fulfill the made
- * assumptions by having the first device having ID 2 and following 3.
- */
-#define INITIAL_DEVICE_ID 2
-
-typedef struct _MetaEventFilter MetaEventFilter;
-
-struct _MetaEventFilter
-{
-  MetaEvdevFilterFunc func;
-  gpointer data;
-  GDestroyNotify destroy_notify;
-};
-
-typedef struct _MetaEventSource  MetaEventSource;
-
 struct _MetaDeviceManagerNativePrivate
 {
-  struct libinput *libinput;
-
-  ClutterStage *stage;
-  gboolean released;
-
-  MetaEventSource *event_source;
-
-  GSList *devices;
-  GSList *seats;
-
   MetaSeatNative *main_seat;
-
-  MetaPointerConstrainCallback constrain_callback;
-  gpointer constrain_data;
-  GDestroyNotify constrain_data_notify;
-
-  MetaRelativeMotionFilter relative_motion_filter;
-  gpointer relative_motion_filter_user_data;
-
-  ClutterStageManager *stage_manager;
-  gulong stage_added_handler;
-  gulong stage_removed_handler;
-
-  GSList *event_filters;
-
-  gint device_id_next;
-  GList *free_device_ids;
 };
 
 G_DEFINE_TYPE_WITH_CODE (MetaDeviceManagerNative,
@@ -101,34 +53,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaDeviceManagerNative,
                          CLUTTER_TYPE_DEVICE_MANAGER,
                          G_ADD_PRIVATE (MetaDeviceManagerNative))
 
-static MetaOpenDeviceCallback  device_open_callback;
-static MetaCloseDeviceCallback device_close_callback;
-static gpointer                device_callback_data;
-static gchar *                 evdev_seat_id;
-
-#ifdef CLUTTER_ENABLE_DEBUG
-static const char *device_type_str[] = {
-  "pointer",            /* CLUTTER_POINTER_DEVICE */
-  "keyboard",           /* CLUTTER_KEYBOARD_DEVICE */
-  "extension",          /* CLUTTER_EXTENSION_DEVICE */
-  "joystick",           /* CLUTTER_JOYSTICK_DEVICE */
-  "tablet",             /* CLUTTER_TABLET_DEVICE */
-  "touchpad",           /* CLUTTER_TOUCHPAD_DEVICE */
-  "touchscreen",        /* CLUTTER_TOUCHSCREEN_DEVICE */
-  "pen",                /* CLUTTER_PEN_DEVICE */
-  "eraser",             /* CLUTTER_ERASER_DEVICE */
-  "cursor",             /* CLUTTER_CURSOR_DEVICE */
-  "pad",                /* CLUTTER_PAD_DEVICE */
-};
-#endif /* CLUTTER_ENABLE_DEBUG */
-
-/*
- * MetaEventSource management
- *
- * The device manager is responsible for managing the GSource when devices
- * appear and disappear from the system.
- */
-
 static void
 meta_device_manager_native_copy_event_data (ClutterDeviceManager *device_manager,
                                             const ClutterEvent   *src,
@@ -152,652 +76,6 @@ meta_device_manager_native_free_event_data (ClutterDeviceManager *device_manager
     meta_event_native_free (event_evdev);
 }
 
-/*
- * MetaEventSource for reading input devices
- */
-
-struct _MetaEventSource
-{
-  GSource source;
-
-  MetaDeviceManagerNative *manager_evdev;
-  GPollFD event_poll_fd;
-};
-
-static void
-process_events (MetaDeviceManagerNative *manager_evdev);
-
-static gboolean
-meta_event_prepare (GSource *source,
-                    gint    *timeout)
-{
-  gboolean retval;
-
-  _clutter_threads_acquire_lock ();
-
-  *timeout = -1;
-  retval = clutter_events_pending ();
-
-  _clutter_threads_release_lock ();
-
-  return retval;
-}
-
-static gboolean
-meta_event_check (GSource *source)
-{
-  MetaEventSource *event_source = (MetaEventSource *) source;
-  gboolean retval;
-
-  _clutter_threads_acquire_lock ();
-
-  retval = ((event_source->event_poll_fd.revents & G_IO_IN) ||
-            clutter_events_pending ());
-
-  _clutter_threads_release_lock ();
-
-  return retval;
-}
-
-static void
-queue_event (ClutterEvent *event)
-{
-  _clutter_event_push (event, FALSE);
-}
-
-void
-meta_device_manager_native_constrain_pointer (MetaDeviceManagerNative *manager_evdev,
-                                              ClutterInputDevice      *core_pointer,
-                                              uint64_t                 time_us,
-                                              float                    x,
-                                              float                    y,
-                                              float                   *new_x,
-                                              float                   *new_y)
-{
-  if (manager_evdev->priv->constrain_callback)
-    {
-      manager_evdev->priv->constrain_callback (core_pointer,
-                                               us2ms (time_us),
-                                               x, y,
-                                               new_x, new_y,
-                                              manager_evdev->priv->constrain_data);
-    }
-  else
-    {
-      ClutterActor *stage = CLUTTER_ACTOR (manager_evdev->priv->stage);
-      float stage_width = clutter_actor_get_width (stage);
-      float stage_height = clutter_actor_get_height (stage);
-
-      *new_x = CLAMP (x, 0.f, stage_width - 1);
-      *new_y = CLAMP (y, 0.f, stage_height - 1);
-    }
-}
-
-void
-meta_device_manager_native_filter_relative_motion (MetaDeviceManagerNative *manager_evdev,
-                                                   ClutterInputDevice      *device,
-                                                   float                    x,
-                                                   float                    y,
-                                                   float                   *dx,
-                                                   float                   *dy)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-
-  if (!priv->relative_motion_filter)
-    return;
-
-  priv->relative_motion_filter (device, x, y, dx, dy,
-                                priv->relative_motion_filter_user_data);
-}
-
-static ClutterEvent *
-new_absolute_motion_event (ClutterInputDevice *input_device,
-                           uint64_t            time_us,
-                           float               x,
-                           float               y,
-                           double             *axes)
-{
-  gfloat stage_width, stage_height;
-  MetaDeviceManagerNative *manager_evdev;
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event = NULL;
-
-  stage = _clutter_input_device_get_stage (input_device);
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (input_device->device_manager);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
-  stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
-
-  event = clutter_event_new (CLUTTER_MOTION);
-
-  if (manager_evdev->priv->constrain_callback &&
-      clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE)
-    {
-      manager_evdev->priv->constrain_callback (seat->core_pointer,
-                                               us2ms (time_us),
-                                               seat->pointer_x,
-                                               seat->pointer_y,
-                                               &x, &y,
-                                              manager_evdev->priv->constrain_data);
-    }
-  else
-    {
-      x = CLAMP (x, 0.f, stage_width - 1);
-      y = CLAMP (y, 0.f, stage_height - 1);
-    }
-
-  meta_event_native_set_time_usec (event, time_us);
-  event->motion.time = us2ms (time_us);
-  event->motion.stage = stage;
-  meta_xkb_translate_state (event, seat->xkb, seat->button_state);
-  event->motion.x = x;
-  event->motion.y = y;
-  meta_input_device_native_translate_coordinates (input_device, stage,
-                                                  &event->motion.x,
-                                                  &event->motion.y);
-  event->motion.axes = axes;
-  clutter_event_set_device (event, seat->core_pointer);
-  clutter_event_set_source_device (event, input_device);
-
-  if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE)
-    {
-      clutter_event_set_device_tool (event, device_evdev->last_tool);
-      clutter_event_set_device (event, input_device);
-    }
-  else
-    clutter_event_set_device (event, seat->core_pointer);
-
-  _clutter_input_device_set_stage (seat->core_pointer, stage);
-
-  if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE)
-    {
-      seat->pointer_x = x;
-      seat->pointer_y = y;
-    }
-
-  return event;
-}
-
-static void
-notify_absolute_motion (ClutterInputDevice *input_device,
-                        uint64_t            time_us,
-                        float               x,
-                        float               y,
-                        double             *axes)
-{
-  ClutterEvent *event;
-
-  event = new_absolute_motion_event (input_device, time_us, x, y, axes);
-
-  queue_event (event);
-}
-
-static void
-notify_relative_tool_motion (ClutterInputDevice *input_device,
-                             uint64_t            time_us,
-                             float               dx,
-                             float               dy,
-                             double             *axes)
-{
-  MetaInputDeviceNative *device_evdev;
-  ClutterEvent *event;
-  MetaSeatNative *seat;
-  gfloat x, y;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-  x = input_device->current_x + dx;
-  y = input_device->current_y + dy;
-
-  meta_device_manager_native_filter_relative_motion (seat->manager_evdev,
-                                                        input_device,
-                                                        seat->pointer_x,
-                                                        seat->pointer_y,
-                                                        &dx,
-                                                        &dy);
-
-  event = new_absolute_motion_event (input_device, time_us, x, y, axes);
-  meta_event_native_set_relative_motion (event, dx, dy, 0, 0);
-
-  queue_event (event);
-}
-
-static void
-notify_pinch_gesture_event (ClutterInputDevice          *input_device,
-                            ClutterTouchpadGesturePhase  phase,
-                            uint64_t                     time_us,
-                            double                       dx,
-                            double                       dy,
-                            double                       angle_delta,
-                            double                       scale,
-                            uint32_t                     n_fingers)
-{
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event = NULL;
-  graphene_point_t pos;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH);
-
-  clutter_input_device_get_coords (seat->core_pointer, NULL, &pos);
-
-  meta_event_native_set_time_usec (event, time_us);
-  event->touchpad_pinch.phase = phase;
-  event->touchpad_pinch.time = us2ms (time_us);
-  event->touchpad_pinch.stage = CLUTTER_STAGE (stage);
-  event->touchpad_pinch.x = pos.x;
-  event->touchpad_pinch.y = pos.y;
-  event->touchpad_pinch.dx = dx;
-  event->touchpad_pinch.dy = dy;
-  event->touchpad_pinch.angle_delta = angle_delta;
-  event->touchpad_pinch.scale = scale;
-  event->touchpad_pinch.n_fingers = n_fingers;
-
-  meta_xkb_translate_state (event, seat->xkb, seat->button_state);
-
-  clutter_event_set_device (event, seat->core_pointer);
-  clutter_event_set_source_device (event, input_device);
-
-  queue_event (event);
-}
-
-static void
-notify_swipe_gesture_event (ClutterInputDevice          *input_device,
-                            ClutterTouchpadGesturePhase  phase,
-                            uint64_t                     time_us,
-                            uint32_t                     n_fingers,
-                            double                       dx,
-                            double                       dy)
-{
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event = NULL;
-  graphene_point_t pos;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE);
-
-  meta_event_native_set_time_usec (event, time_us);
-  event->touchpad_swipe.phase = phase;
-  event->touchpad_swipe.time = us2ms (time_us);
-  event->touchpad_swipe.stage = CLUTTER_STAGE (stage);
-
-  clutter_input_device_get_coords (seat->core_pointer, NULL, &pos);
-  event->touchpad_swipe.x = pos.x;
-  event->touchpad_swipe.y = pos.y;
-  event->touchpad_swipe.dx = dx;
-  event->touchpad_swipe.dy = dy;
-  event->touchpad_swipe.n_fingers = n_fingers;
-
-  meta_xkb_translate_state (event, seat->xkb, seat->button_state);
-
-  clutter_event_set_device (event, seat->core_pointer);
-  clutter_event_set_source_device (event, input_device);
-
-  queue_event (event);
-}
-
-static void
-notify_proximity (ClutterInputDevice *input_device,
-                  uint64_t            time_us,
-                  gboolean            in)
-{
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event = NULL;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  if (in)
-    event = clutter_event_new (CLUTTER_PROXIMITY_IN);
-  else
-    event = clutter_event_new (CLUTTER_PROXIMITY_OUT);
-
-  meta_event_native_set_time_usec (event, time_us);
-
-  event->proximity.time = us2ms (time_us);
-  event->proximity.stage = CLUTTER_STAGE (stage);
-  clutter_event_set_device_tool (event, device_evdev->last_tool);
-  clutter_event_set_device (event, seat->core_pointer);
-  clutter_event_set_source_device (event, input_device);
-
-  _clutter_input_device_set_stage (seat->core_pointer, stage);
-
-  queue_event (event);
-}
-
-static void
-notify_pad_button (ClutterInputDevice *input_device,
-                   uint64_t            time_us,
-                   uint32_t            button,
-                   uint32_t            mode_group,
-                   uint32_t            mode,
-                   uint32_t            pressed)
-{
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  if (pressed)
-    event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS);
-  else
-    event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE);
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  meta_event_native_set_time_usec (event, time_us);
-  event->pad_button.stage = stage;
-  event->pad_button.button = button;
-  event->pad_button.group = mode_group;
-  event->pad_button.mode = mode;
-  clutter_event_set_device (event, input_device);
-  clutter_event_set_source_device (event, input_device);
-  clutter_event_set_time (event, us2ms (time_us));
-
-  _clutter_input_device_set_stage (seat->core_pointer, stage);
-
-  queue_event (event);
-}
-
-static void
-notify_pad_strip (ClutterInputDevice *input_device,
-                  uint64_t            time_us,
-                  uint32_t            strip_number,
-                  uint32_t            strip_source,
-                  uint32_t            mode_group,
-                  uint32_t            mode,
-                  double              value)
-{
-  MetaInputDeviceNative *device_evdev;
-  ClutterInputDevicePadSource source;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  if (strip_source == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER)
-    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER;
-  else
-    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  event = clutter_event_new (CLUTTER_PAD_STRIP);
-  meta_event_native_set_time_usec (event, time_us);
-  event->pad_strip.strip_source = source;
-  event->pad_strip.stage = stage;
-  event->pad_strip.strip_number = strip_number;
-  event->pad_strip.value = value;
-  event->pad_strip.group = mode_group;
-  event->pad_strip.mode = mode;
-  clutter_event_set_device (event, input_device);
-  clutter_event_set_source_device (event, input_device);
-  clutter_event_set_time (event, us2ms (time_us));
-
-  _clutter_input_device_set_stage (seat->core_pointer, stage);
-
-  queue_event (event);
-}
-
-static void
-notify_pad_ring (ClutterInputDevice *input_device,
-                 uint64_t            time_us,
-                 uint32_t            ring_number,
-                 uint32_t            ring_source,
-                 uint32_t            mode_group,
-                 uint32_t            mode,
-                 double              angle)
-{
-  MetaInputDeviceNative *device_evdev;
-  ClutterInputDevicePadSource source;
-  MetaSeatNative *seat;
-  ClutterStage *stage;
-  ClutterEvent *event;
-
-  /* We can drop the event on the floor if no stage has been
-   * associated with the device yet. */
-  stage = _clutter_input_device_get_stage (input_device);
-  if (stage == NULL)
-    return;
-
-  if (ring_source == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER)
-    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER;
-  else
-    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  event = clutter_event_new (CLUTTER_PAD_RING);
-  meta_event_native_set_time_usec (event, time_us);
-  event->pad_ring.ring_source = source;
-  event->pad_ring.stage = stage;
-  event->pad_ring.ring_number = ring_number;
-  event->pad_ring.angle = angle;
-  event->pad_ring.group = mode_group;
-  event->pad_ring.mode = mode;
-  clutter_event_set_device (event, input_device);
-  clutter_event_set_source_device (event, input_device);
-  clutter_event_set_time (event, us2ms (time_us));
-
-  _clutter_input_device_set_stage (seat->core_pointer, stage);
-
-  queue_event (event);
-}
-
-static void
-dispatch_libinput (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-
-  libinput_dispatch (priv->libinput);
-  process_events (manager_evdev);
-}
-
-static gboolean
-meta_event_dispatch (GSource     *g_source,
-                     GSourceFunc  callback,
-                     gpointer     user_data)
-{
-  MetaEventSource *source = (MetaEventSource *) g_source;
-  MetaDeviceManagerNative *manager_evdev;
-  ClutterEvent *event;
-
-  _clutter_threads_acquire_lock ();
-
-  manager_evdev = source->manager_evdev;
-
-  /* Don't queue more events if we haven't finished handling the previous batch
-   */
-  if (clutter_events_pending ())
-    goto queue_event;
-
-  dispatch_libinput (manager_evdev);
-
- queue_event:
-  event = clutter_event_get ();
-
-  if (event)
-    {
-      ClutterModifierType event_state;
-      ClutterInputDevice *input_device =
-        clutter_event_get_source_device (event);
-      MetaInputDeviceNative *device_evdev =
-        META_INPUT_DEVICE_NATIVE (input_device);
-      MetaSeatNative *seat =
-        meta_input_device_native_get_seat (device_evdev);
-
-      /* Drop events if we don't have any stage to forward them to */
-      if (!_clutter_input_device_get_stage (input_device))
-        goto out;
-
-      /* update the device states *before* the event */
-      event_state = seat->button_state |
-        xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_EFFECTIVE);
-      _clutter_input_device_set_state (seat->core_pointer, event_state);
-      _clutter_input_device_set_state (seat->core_keyboard, event_state);
-
-      /* forward the event into clutter for emission etc. */
-      _clutter_stage_queue_event (event->any.stage, event, FALSE);
-    }
-
-out:
-  _clutter_threads_release_lock ();
-
-  return TRUE;
-}
-static GSourceFuncs event_funcs = {
-  meta_event_prepare,
-  meta_event_check,
-  meta_event_dispatch,
-  NULL
-};
-
-static MetaEventSource *
-meta_event_source_new (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-  GSource *source;
-  MetaEventSource *event_source;
-  gint fd;
-
-  source = g_source_new (&event_funcs, sizeof (MetaEventSource));
-  event_source = (MetaEventSource *) source;
-
-  /* setup the source */
-  event_source->manager_evdev = manager_evdev;
-
-  fd = libinput_get_fd (priv->libinput);
-  event_source->event_poll_fd.fd = fd;
-  event_source->event_poll_fd.events = G_IO_IN;
-
-  /* and finally configure and attach the GSource */
-  g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
-  g_source_add_poll (source, &event_source->event_poll_fd);
-  g_source_set_can_recurse (source, TRUE);
-  g_source_attach (source, NULL);
-
-  return event_source;
-}
-
-static void
-meta_event_source_free (MetaEventSource *source)
-{
-  GSource *g_source = (GSource *) source;
-
-  /* ignore the return value of close, it's not like we can do something
-   * about it */
-  close (source->event_poll_fd.fd);
-
-  g_source_destroy (g_source);
-  g_source_unref (g_source);
-}
-
-static void
-evdev_add_device (MetaDeviceManagerNative *manager_evdev,
-                  struct libinput_device  *libinput_device)
-{
-  ClutterDeviceManager *manager = (ClutterDeviceManager *) manager_evdev;
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-  ClutterInputDeviceType type;
-  struct libinput_seat *libinput_seat;
-  MetaSeatNative *seat;
-  ClutterInputDevice *device;
-
-  libinput_seat = libinput_device_get_seat (libinput_device);
-  seat = libinput_seat_get_user_data (libinput_seat);
-  if (seat == NULL)
-    {
-      /* Clutter has the notion of global "core" pointers and keyboard devices,
-       * which are located on the main seat. Make whatever seat comes first the
-       * main seat. */
-      if (priv->main_seat->libinput_seat == NULL)
-        seat = priv->main_seat;
-      else
-        {
-          seat = meta_seat_native_new (manager_evdev);
-          priv->seats = g_slist_append (priv->seats, seat);
-        }
-
-      meta_seat_native_set_libinput_seat (seat, libinput_seat);
-    }
-
-  device = meta_input_device_native_new (manager, seat, libinput_device);
-  _clutter_input_device_set_stage (device, manager_evdev->priv->stage);
-
-  _clutter_device_manager_add_device (manager, device);
-
-  /* Clutter assumes that device types are exclusive in the
-   * ClutterInputDevice API */
-  type = meta_input_device_native_determine_type (libinput_device);
-
-  if (type == CLUTTER_KEYBOARD_DEVICE)
-    {
-      _clutter_input_device_set_associated_device (device, seat->core_keyboard);
-      _clutter_input_device_add_slave (seat->core_keyboard, device);
-    }
-  else if (type == CLUTTER_POINTER_DEVICE)
-    {
-      _clutter_input_device_set_associated_device (device, seat->core_pointer);
-      _clutter_input_device_add_slave (seat->core_pointer, device);
-    }
-}
-
-static void
-evdev_remove_device (MetaDeviceManagerNative *manager_evdev,
-                     MetaInputDeviceNative   *device_evdev)
-{
-  ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (manager_evdev);
-  ClutterInputDevice *input_device = CLUTTER_INPUT_DEVICE (device_evdev);
-
-  _clutter_device_manager_remove_device (manager, input_device);
-}
-
 /*
  * ClutterDeviceManager implementation
  */
@@ -806,48 +84,26 @@ static void
 meta_device_manager_native_add_device (ClutterDeviceManager *manager,
                                        ClutterInputDevice   *device)
 {
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  priv = manager_evdev->priv;
-  device_evdev = META_INPUT_DEVICE_NATIVE (device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-
-  seat->devices = g_slist_prepend (seat->devices, device);
-  priv->devices = g_slist_prepend (priv->devices, device);
 }
 
 static void
 meta_device_manager_native_remove_device (ClutterDeviceManager *manager,
                                           ClutterInputDevice   *device)
 {
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-  MetaInputDeviceNative *device_evdev;
-  MetaSeatNative *seat;
-
-  device_evdev = META_INPUT_DEVICE_NATIVE (device);
-  seat = meta_input_device_native_get_seat (device_evdev);
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  priv = manager_evdev->priv;
-
-  /* Remove the device */
-  seat->devices = g_slist_remove (seat->devices, device);
-  priv->devices = g_slist_remove (priv->devices, device);
-
-  if (seat->repeat_timer && seat->repeat_device == device)
-    meta_seat_native_clear_repeat_timer (seat);
-
-  g_object_unref (device);
 }
 
 static const GSList *
 meta_device_manager_native_get_devices (ClutterDeviceManager *manager)
 {
-  return META_DEVICE_MANAGER_NATIVE (manager)->priv->devices;
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
+  GSList *retval = NULL;
+
+  retval = g_slist_copy (META_SEAT_NATIVE (seat)->devices);
+  retval = g_slist_prepend (retval, clutter_seat_get_pointer (seat));
+  retval = g_slist_prepend (retval, clutter_seat_get_keyboard (seat));
+
+  return retval;
 }
 
 static ClutterInputDevice *
@@ -881,952 +137,19 @@ meta_device_manager_native_get_device (ClutterDeviceManager *manager,
                                        gint                  id)
 {
   MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-  GSList *l;
+  MetaSeatNative *seat;
+  ClutterInputDevice *device;
 
   manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  priv = manager_evdev->priv;
-
-  for (l = priv->seats; l; l = l->next)
-    {
-      MetaSeatNative *seat = l->data;
-      ClutterInputDevice *device = meta_seat_native_get_device (seat, id);
+  seat = manager_evdev->priv->main_seat;
+  device = meta_seat_native_get_device (seat, id);
 
-      if (device)
-        return device;
-    }
+  if (device)
+    return device;
 
   return NULL;
 }
 
-static void
-flush_event_queue (void)
-{
-  ClutterEvent *event;
-
-  while ((event = clutter_event_get ()) != NULL)
-    {
-      _clutter_process_event (event);
-      clutter_event_free (event);
-    }
-}
-
-static gboolean
-process_base_event (MetaDeviceManagerNative *manager_evdev,
-                    struct libinput_event   *event)
-{
-  ClutterInputDevice *device;
-  struct libinput_device *libinput_device;
-  gboolean handled = TRUE;
-
-  switch (libinput_event_get_type (event))
-    {
-    case LIBINPUT_EVENT_DEVICE_ADDED:
-      libinput_device = libinput_event_get_device (event);
-
-      evdev_add_device (manager_evdev, libinput_device);
-      break;
-
-    case LIBINPUT_EVENT_DEVICE_REMOVED:
-      /* Flush all queued events, there
-       * might be some from this device.
-       */
-      flush_event_queue ();
-
-      libinput_device = libinput_event_get_device (event);
-
-      device = libinput_device_get_user_data (libinput_device);
-      evdev_remove_device (manager_evdev,
-                           META_INPUT_DEVICE_NATIVE (device));
-      break;
-
-    default:
-      handled = FALSE;
-    }
-
-  return handled;
-}
-
-static ClutterScrollSource
-translate_scroll_source (enum libinput_pointer_axis_source source)
-{
-  switch (source)
-    {
-    case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
-      return CLUTTER_SCROLL_SOURCE_WHEEL;
-    case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
-      return CLUTTER_SCROLL_SOURCE_FINGER;
-    case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
-      return CLUTTER_SCROLL_SOURCE_CONTINUOUS;
-    default:
-      return CLUTTER_SCROLL_SOURCE_UNKNOWN;
-    }
-}
-
-static ClutterInputDeviceToolType
-translate_tool_type (struct libinput_tablet_tool *libinput_tool)
-{
-  enum libinput_tablet_tool_type tool;
-
-  tool = libinput_tablet_tool_get_type (libinput_tool);
-
-  switch (tool)
-    {
-    case LIBINPUT_TABLET_TOOL_TYPE_PEN:
-      return CLUTTER_INPUT_DEVICE_TOOL_PEN;
-    case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
-      return CLUTTER_INPUT_DEVICE_TOOL_ERASER;
-    case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
-      return CLUTTER_INPUT_DEVICE_TOOL_BRUSH;
-    case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
-      return CLUTTER_INPUT_DEVICE_TOOL_PENCIL;
-    case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
-      return CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH;
-    case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
-      return CLUTTER_INPUT_DEVICE_TOOL_MOUSE;
-    case LIBINPUT_TABLET_TOOL_TYPE_LENS:
-      return CLUTTER_INPUT_DEVICE_TOOL_LENS;
-    default:
-      return CLUTTER_INPUT_DEVICE_TOOL_NONE;
-    }
-}
-
-static void
-input_device_update_tool (ClutterInputDevice          *input_device,
-                          struct libinput_tablet_tool *libinput_tool)
-{
-  MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device);
-  ClutterInputDeviceTool *tool = NULL;
-  ClutterInputDeviceToolType tool_type;
-  uint64_t tool_serial;
-
-  if (libinput_tool)
-    {
-      tool_serial = libinput_tablet_tool_get_serial (libinput_tool);
-      tool_type = translate_tool_type (libinput_tool);
-      tool = clutter_input_device_lookup_tool (input_device,
-                                               tool_serial, tool_type);
-
-      if (!tool)
-        {
-          tool = meta_input_device_tool_native_new (libinput_tool,
-                                                    tool_serial, tool_type);
-          clutter_input_device_add_tool (input_device, tool);
-        }
-    }
-
-  if (evdev_device->last_tool != tool)
-    {
-      evdev_device->last_tool = tool;
-      g_signal_emit_by_name (clutter_device_manager_get_default (),
-                             "tool-changed", input_device, tool);
-    }
-}
-
-static gdouble *
-translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event,
-                       ClutterInputDeviceTool            *tool)
-{
-  GArray *axes = g_array_new (FALSE, FALSE, sizeof (gdouble));
-  struct libinput_tablet_tool *libinput_tool;
-  gdouble value;
-
-  libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event);
-
-  value = libinput_event_tablet_tool_get_x (tablet_event);
-  g_array_append_val (axes, value);
-  value = libinput_event_tablet_tool_get_y (tablet_event);
-  g_array_append_val (axes, value);
-
-  if (libinput_tablet_tool_has_distance (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_distance (tablet_event);
-      g_array_append_val (axes, value);
-    }
-
-  if (libinput_tablet_tool_has_pressure (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_pressure (tablet_event);
-      value = meta_input_device_tool_native_translate_pressure (tool, value);
-      g_array_append_val (axes, value);
-    }
-
-  if (libinput_tablet_tool_has_tilt (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_tilt_x (tablet_event);
-      g_array_append_val (axes, value);
-      value = libinput_event_tablet_tool_get_tilt_y (tablet_event);
-      g_array_append_val (axes, value);
-    }
-
-  if (libinput_tablet_tool_has_rotation (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_rotation (tablet_event);
-      g_array_append_val (axes, value);
-    }
-
-  if (libinput_tablet_tool_has_slider (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_slider_position (tablet_event);
-      g_array_append_val (axes, value);
-    }
-
-  if (libinput_tablet_tool_has_wheel (libinput_tool))
-    {
-      value = libinput_event_tablet_tool_get_wheel_delta (tablet_event);
-      g_array_append_val (axes, value);
-    }
-
-  if (axes->len == 0)
-    {
-      g_array_free (axes, TRUE);
-      return NULL;
-    }
-  else
-    return (gdouble *) g_array_free (axes, FALSE);
-}
-
-static MetaSeatNative *
-seat_from_device (ClutterInputDevice *device)
-{
-  MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device);
-
-  return meta_input_device_native_get_seat (device_evdev);
-}
-
-static void
-notify_continuous_axis (MetaSeatNative                *seat,
-                        ClutterInputDevice            *device,
-                        uint64_t                       time_us,
-                        ClutterScrollSource            scroll_source,
-                        struct libinput_event_pointer *axis_event)
-{
-  gdouble dx = 0.0, dy = 0.0;
-  ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE;
-
-  if (libinput_event_pointer_has_axis (axis_event,
-                                       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
-    {
-      dx = libinput_event_pointer_get_axis_value (
-          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-
-      if (fabs (dx) < DBL_EPSILON)
-        finish_flags |= CLUTTER_SCROLL_FINISHED_HORIZONTAL;
-    }
-  if (libinput_event_pointer_has_axis (axis_event,
-                                       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
-    {
-      dy = libinput_event_pointer_get_axis_value (
-          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
-
-      if (fabs (dy) < DBL_EPSILON)
-        finish_flags |= CLUTTER_SCROLL_FINISHED_VERTICAL;
-    }
-
-  meta_seat_native_notify_scroll_continuous (seat, device, time_us,
-                                             dx, dy,
-                                             scroll_source, finish_flags);
-}
-
-static void
-notify_discrete_axis (MetaSeatNative                *seat,
-                      ClutterInputDevice            *device,
-                      uint64_t                       time_us,
-                      ClutterScrollSource            scroll_source,
-                      struct libinput_event_pointer *axis_event)
-{
-  gdouble discrete_dx = 0.0, discrete_dy = 0.0;
-
-  if (libinput_event_pointer_has_axis (axis_event,
-                                       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
-    {
-      discrete_dx = libinput_event_pointer_get_axis_value_discrete (
-          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
-    }
-  if (libinput_event_pointer_has_axis (axis_event,
-                                       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
-    {
-      discrete_dy = libinput_event_pointer_get_axis_value_discrete (
-          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
-    }
-
-  meta_seat_native_notify_discrete_scroll (seat, device,
-                                           time_us,
-                                           discrete_dx, discrete_dy,
-                                           scroll_source);
-}
-
-static void
-process_tablet_axis (MetaDeviceManagerNative *manager_evdev,
-                     struct libinput_event   *event)
-{
-  struct libinput_device *libinput_device = libinput_event_get_device (event);
-  uint64_t time;
-  double x, y, dx, dy, *axes;
-  float stage_width, stage_height;
-  ClutterStage *stage;
-  ClutterInputDevice *device;
-  struct libinput_event_tablet_tool *tablet_event =
-    libinput_event_get_tablet_tool_event (event);
-  MetaInputDeviceNative *evdev_device;
-
-  device = libinput_device_get_user_data (libinput_device);
-  evdev_device = META_INPUT_DEVICE_NATIVE (device);
-
-  stage = _clutter_input_device_get_stage (device);
-  if (!stage)
-    return;
-
-  axes = translate_tablet_axes (tablet_event,
-                                evdev_device->last_tool);
-  if (!axes)
-    return;
-
-  stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
-  stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
-
-  time = libinput_event_tablet_tool_get_time_usec (tablet_event);
-
-  if (clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE ||
-      clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_MOUSE ||
-      clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_LENS)
-    {
-      dx = libinput_event_tablet_tool_get_dx (tablet_event);
-      dy = libinput_event_tablet_tool_get_dy (tablet_event);
-      notify_relative_tool_motion (device, time, dx, dy, axes);
-    }
-  else
-    {
-      x = libinput_event_tablet_tool_get_x_transformed (tablet_event, stage_width);
-      y = libinput_event_tablet_tool_get_y_transformed (tablet_event, stage_height);
-      notify_absolute_motion (device, time, x, y, axes);
-    }
-}
-
-static gboolean
-process_device_event (MetaDeviceManagerNative *manager_evdev,
-                      struct libinput_event   *event)
-{
-  gboolean handled = TRUE;
-  struct libinput_device *libinput_device = libinput_event_get_device(event);
-  ClutterInputDevice *device;
-  MetaInputDeviceNative *device_evdev;
-
-  switch (libinput_event_get_type (event))
-    {
-    case LIBINPUT_EVENT_KEYBOARD_KEY:
-      {
-        uint32_t key, key_state, seat_key_count;
-        uint64_t time_us;
-        struct libinput_event_keyboard *key_event =
-          libinput_event_get_keyboard_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_keyboard_get_time_usec (key_event);
-        key = libinput_event_keyboard_get_key (key_event);
-        key_state = libinput_event_keyboard_get_key_state (key_event) ==
-                    LIBINPUT_KEY_STATE_PRESSED;
-        seat_key_count =
-          libinput_event_keyboard_get_seat_key_count (key_event);
-
-       /* Ignore key events that are not seat wide state changes. */
-       if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
-            seat_key_count != 1) ||
-           (key_state == LIBINPUT_KEY_STATE_RELEASED &&
-            seat_key_count != 0))
-          break;
-
-        meta_seat_native_notify_key (seat_from_device (device),
-                                     device,
-                                     time_us, key, key_state, TRUE);
-
-        break;
-      }
-
-    case LIBINPUT_EVENT_POINTER_MOTION:
-      {
-        struct libinput_event_pointer *pointer_event =
-          libinput_event_get_pointer_event (event);
-        uint64_t time_us;
-        double dx;
-        double dy;
-        double dx_unaccel;
-        double dy_unaccel;
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_pointer_get_time_usec (pointer_event);
-        dx = libinput_event_pointer_get_dx (pointer_event);
-        dy = libinput_event_pointer_get_dy (pointer_event);
-        dx_unaccel = libinput_event_pointer_get_dx_unaccelerated (pointer_event);
-        dy_unaccel = libinput_event_pointer_get_dy_unaccelerated (pointer_event);
-
-        meta_seat_native_notify_relative_motion (seat_from_device (device),
-                                                 device,
-                                                 time_us,
-                                                 dx, dy,
-                                                 dx_unaccel, dy_unaccel);
-
-        break;
-      }
-
-    case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
-      {
-        uint64_t time_us;
-        double x, y;
-        float stage_width, stage_height;
-        ClutterStage *stage;
-        struct libinput_event_pointer *motion_event =
-          libinput_event_get_pointer_event (event);
-        device = libinput_device_get_user_data (libinput_device);
-
-        stage = _clutter_input_device_get_stage (device);
-        if (stage == NULL)
-          break;
-
-        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
-        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
-
-        time_us = libinput_event_pointer_get_time_usec (motion_event);
-        x = libinput_event_pointer_get_absolute_x_transformed (motion_event,
-                                                               stage_width);
-        y = libinput_event_pointer_get_absolute_y_transformed (motion_event,
-                                                               stage_height);
-
-        meta_seat_native_notify_absolute_motion (seat_from_device (device),
-                                                 device,
-                                                 time_us,
-                                                 x, y,
-                                                 NULL);
-
-        break;
-      }
-
-    case LIBINPUT_EVENT_POINTER_BUTTON:
-      {
-        uint32_t button, button_state, seat_button_count;
-        uint64_t time_us;
-        struct libinput_event_pointer *button_event =
-          libinput_event_get_pointer_event (event);
-        device = libinput_device_get_user_data (libinput_device);
-
-        time_us = libinput_event_pointer_get_time_usec (button_event);
-        button = libinput_event_pointer_get_button (button_event);
-        button_state = libinput_event_pointer_get_button_state (button_event) ==
-                       LIBINPUT_BUTTON_STATE_PRESSED;
-        seat_button_count =
-          libinput_event_pointer_get_seat_button_count (button_event);
-
-        /* Ignore button events that are not seat wide state changes. */
-        if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED &&
-             seat_button_count != 1) ||
-            (button_state == LIBINPUT_BUTTON_STATE_RELEASED &&
-             seat_button_count != 0))
-          break;
-
-        meta_seat_native_notify_button (seat_from_device (device), device,
-                                        time_us, button, button_state);
-        break;
-      }
-
-    case LIBINPUT_EVENT_POINTER_AXIS:
-      {
-        uint64_t time_us;
-        enum libinput_pointer_axis_source source;
-        struct libinput_event_pointer *axis_event =
-          libinput_event_get_pointer_event (event);
-        MetaSeatNative *seat;
-        ClutterScrollSource scroll_source;
-
-        device = libinput_device_get_user_data (libinput_device);
-        seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (device));
-
-        time_us = libinput_event_pointer_get_time_usec (axis_event);
-        source = libinput_event_pointer_get_axis_source (axis_event);
-        scroll_source = translate_scroll_source (source);
-
-        /* libinput < 0.8 sent wheel click events with value 10. Since 0.8
-           the value is the angle of the click in degrees. To keep
-           backwards-compat with existing clients, we just send multiples of
-           the click count. */
-
-        switch (scroll_source)
-          {
-          case CLUTTER_SCROLL_SOURCE_WHEEL:
-            notify_discrete_axis (seat, device, time_us, scroll_source,
-                                  axis_event);
-            break;
-          case CLUTTER_SCROLL_SOURCE_FINGER:
-          case CLUTTER_SCROLL_SOURCE_CONTINUOUS:
-          case CLUTTER_SCROLL_SOURCE_UNKNOWN:
-            notify_continuous_axis (seat, device, time_us, scroll_source,
-                                    axis_event);
-            break;
-          }
-        break;
-      }
-
-    case LIBINPUT_EVENT_TOUCH_DOWN:
-      {
-        int device_slot;
-        uint64_t time_us;
-        double x, y;
-        float stage_width, stage_height;
-        MetaSeatNative *seat;
-        ClutterStage *stage;
-        MetaTouchState *touch_state;
-        struct libinput_event_touch *touch_event =
-          libinput_event_get_touch_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        device_evdev = META_INPUT_DEVICE_NATIVE (device);
-        seat = meta_input_device_native_get_seat (device_evdev);
-
-        stage = _clutter_input_device_get_stage (device);
-        if (stage == NULL)
-          break;
-
-        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
-        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
-
-        device_slot = libinput_event_touch_get_slot (touch_event);
-        time_us = libinput_event_touch_get_time_usec (touch_event);
-        x = libinput_event_touch_get_x_transformed (touch_event,
-                                                    stage_width);
-        y = libinput_event_touch_get_y_transformed (touch_event,
-                                                    stage_height);
-
-        touch_state =
-          meta_input_device_native_acquire_touch_state (device_evdev,
-                                                        device_slot);
-        touch_state->coords.x = x;
-        touch_state->coords.y = y;
-
-        meta_seat_native_notify_touch_event (seat, device,
-                                             CLUTTER_TOUCH_BEGIN,
-                                             time_us,
-                                             touch_state->seat_slot,
-                                             touch_state->coords.x,
-                                             touch_state->coords.y);
-        break;
-      }
-
-    case LIBINPUT_EVENT_TOUCH_UP:
-      {
-        int device_slot;
-        uint64_t time_us;
-        MetaSeatNative *seat;
-        MetaTouchState *touch_state;
-        struct libinput_event_touch *touch_event =
-          libinput_event_get_touch_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        device_evdev = META_INPUT_DEVICE_NATIVE (device);
-        seat = meta_input_device_native_get_seat (device_evdev);
-
-        device_slot = libinput_event_touch_get_slot (touch_event);
-        time_us = libinput_event_touch_get_time_usec (touch_event);
-        touch_state =
-          meta_input_device_native_lookup_touch_state (device_evdev,
-                                                       device_slot);
-        if (!touch_state)
-          break;
-
-        meta_seat_native_notify_touch_event (seat, device,
-                                             CLUTTER_TOUCH_END, time_us,
-                                             touch_state->seat_slot,
-                                             touch_state->coords.x,
-                                             touch_state->coords.y);
-        meta_input_device_native_release_touch_state (device_evdev,
-                                                      touch_state);
-        break;
-      }
-
-    case LIBINPUT_EVENT_TOUCH_MOTION:
-      {
-        int device_slot;
-        uint64_t time_us;
-        double x, y;
-        float stage_width, stage_height;
-        MetaSeatNative *seat;
-        ClutterStage *stage;
-        MetaTouchState *touch_state;
-        struct libinput_event_touch *touch_event =
-          libinput_event_get_touch_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        device_evdev = META_INPUT_DEVICE_NATIVE (device);
-        seat = meta_input_device_native_get_seat (device_evdev);
-
-        stage = _clutter_input_device_get_stage (device);
-        if (stage == NULL)
-          break;
-
-        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
-        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
-
-        device_slot = libinput_event_touch_get_slot (touch_event);
-        time_us = libinput_event_touch_get_time_usec (touch_event);
-        x = libinput_event_touch_get_x_transformed (touch_event,
-                                                    stage_width);
-        y = libinput_event_touch_get_y_transformed (touch_event,
-                                                    stage_height);
-
-        touch_state =
-          meta_input_device_native_lookup_touch_state (device_evdev,
-                                                       device_slot);
-        if (!touch_state)
-          break;
-
-        touch_state->coords.x = x;
-        touch_state->coords.y = y;
-
-        meta_seat_native_notify_touch_event (seat, device,
-                                             CLUTTER_TOUCH_UPDATE,
-                                             time_us,
-                                             touch_state->seat_slot,
-                                             touch_state->coords.x,
-                                             touch_state->coords.y);
-        break;
-      }
-    case LIBINPUT_EVENT_TOUCH_CANCEL:
-      {
-        uint64_t time_us;
-        struct libinput_event_touch *touch_event =
-          libinput_event_get_touch_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        device_evdev = META_INPUT_DEVICE_NATIVE (device);
-        time_us = libinput_event_touch_get_time_usec (touch_event);
-
-        meta_input_device_native_release_touch_slots (device_evdev, time_us);
-
-        break;
-      }
-    case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
-    case LIBINPUT_EVENT_GESTURE_PINCH_END:
-      {
-        struct libinput_event_gesture *gesture_event =
-          libinput_event_get_gesture_event (event);
-        ClutterTouchpadGesturePhase phase;
-        uint32_t n_fingers;
-        uint64_t time_us;
-
-        if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_PINCH_BEGIN)
-          phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN;
-        else
-          phase = libinput_event_gesture_get_cancelled (gesture_event) ?
-            CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END;
-
-        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_gesture_get_time_usec (gesture_event);
-        notify_pinch_gesture_event (device, phase, time_us, 0, 0, 0, 0, n_fingers);
-        break;
-      }
-    case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
-      {
-        struct libinput_event_gesture *gesture_event =
-          libinput_event_get_gesture_event (event);
-        gdouble angle_delta, scale, dx, dy;
-        uint32_t n_fingers;
-        uint64_t time_us;
-
-        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_gesture_get_time_usec (gesture_event);
-        angle_delta = libinput_event_gesture_get_angle_delta (gesture_event);
-        scale = libinput_event_gesture_get_scale (gesture_event);
-        dx = libinput_event_gesture_get_dx (gesture_event);
-        dy = libinput_event_gesture_get_dy (gesture_event);
-
-        notify_pinch_gesture_event (device,
-                                    CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
-                                    time_us, dx, dy, angle_delta, scale, n_fingers);
-        break;
-      }
-    case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
-    case LIBINPUT_EVENT_GESTURE_SWIPE_END:
-      {
-        struct libinput_event_gesture *gesture_event =
-          libinput_event_get_gesture_event (event);
-        ClutterTouchpadGesturePhase phase;
-        uint32_t n_fingers;
-        uint64_t time_us;
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_gesture_get_time_usec (gesture_event);
-        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
-
-        if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN)
-          phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN;
-        else
-          phase = libinput_event_gesture_get_cancelled (gesture_event) ?
-            CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END;
-
-        notify_swipe_gesture_event (device, phase, time_us, n_fingers, 0, 0);
-        break;
-      }
-    case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
-      {
-        struct libinput_event_gesture *gesture_event =
-          libinput_event_get_gesture_event (event);
-        uint32_t n_fingers;
-        uint64_t time_us;
-        double dx, dy;
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_gesture_get_time_usec (gesture_event);
-        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
-        dx = libinput_event_gesture_get_dx (gesture_event);
-        dy = libinput_event_gesture_get_dy (gesture_event);
-
-        notify_swipe_gesture_event (device,
-                                    CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
-                                    time_us, n_fingers, dx, dy);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
-      {
-        process_tablet_axis (manager_evdev, event);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
-      {
-        uint64_t time;
-        struct libinput_event_tablet_tool *tablet_event =
-          libinput_event_get_tablet_tool_event (event);
-        struct libinput_tablet_tool *libinput_tool = NULL;
-        enum libinput_tablet_tool_proximity_state state;
-
-        state = libinput_event_tablet_tool_get_proximity_state (tablet_event);
-        time = libinput_event_tablet_tool_get_time_usec (tablet_event);
-        device = libinput_device_get_user_data (libinput_device);
-
-        libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event);
-
-        if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN)
-          input_device_update_tool (device, libinput_tool);
-        notify_proximity (device, time, state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
-        if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT)
-          input_device_update_tool (device, NULL);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
-      {
-        uint64_t time_us;
-        uint32_t button_state;
-        struct libinput_event_tablet_tool *tablet_event =
-          libinput_event_get_tablet_tool_event (event);
-        uint32_t tablet_button;
-
-        process_tablet_axis (manager_evdev, event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_tablet_tool_get_time_usec (tablet_event);
-        tablet_button = libinput_event_tablet_tool_get_button (tablet_event);
-
-        button_state = libinput_event_tablet_tool_get_button_state (tablet_event) ==
-                       LIBINPUT_BUTTON_STATE_PRESSED;
-
-        meta_seat_native_notify_button (seat_from_device (device), device,
-                                        time_us, tablet_button, button_state);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_TOOL_TIP:
-      {
-        uint64_t time_us;
-        uint32_t button_state;
-        struct libinput_event_tablet_tool *tablet_event =
-          libinput_event_get_tablet_tool_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        time_us = libinput_event_tablet_tool_get_time_usec (tablet_event);
-
-        button_state = libinput_event_tablet_tool_get_tip_state (tablet_event) ==
-                       LIBINPUT_TABLET_TOOL_TIP_DOWN;
-
-        /* To avoid jumps on tip, notify axes before the tip down event
-           but after the tip up event */
-        if (button_state)
-          process_tablet_axis (manager_evdev, event);
-
-        meta_seat_native_notify_button (seat_from_device (device), device,
-                                        time_us, BTN_TOUCH, button_state);
-        if (!button_state)
-          process_tablet_axis (manager_evdev, event);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
-      {
-        uint64_t time;
-        uint32_t button_state, button, group, mode;
-        struct libinput_tablet_pad_mode_group *mode_group;
-        struct libinput_event_tablet_pad *pad_event =
-          libinput_event_get_tablet_pad_event (event);
-
-        device = libinput_device_get_user_data (libinput_device);
-        time = libinput_event_tablet_pad_get_time_usec (pad_event);
-
-        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
-        group = libinput_tablet_pad_mode_group_get_index (mode_group);
-        mode = libinput_event_tablet_pad_get_mode (pad_event);
-
-        button = libinput_event_tablet_pad_get_button_number (pad_event);
-        button_state = libinput_event_tablet_pad_get_button_state (pad_event) ==
-                       LIBINPUT_BUTTON_STATE_PRESSED;
-        notify_pad_button (device, time, button, group, mode, button_state);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_PAD_STRIP:
-      {
-        uint64_t time;
-        uint32_t number, source, group, mode;
-        struct libinput_tablet_pad_mode_group *mode_group;
-        struct libinput_event_tablet_pad *pad_event =
-          libinput_event_get_tablet_pad_event (event);
-        double value;
-
-        device = libinput_device_get_user_data (libinput_device);
-        time = libinput_event_tablet_pad_get_time_usec (pad_event);
-        number = libinput_event_tablet_pad_get_strip_number (pad_event);
-        value = libinput_event_tablet_pad_get_strip_position (pad_event);
-        source = libinput_event_tablet_pad_get_strip_source (pad_event);
-
-        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
-        group = libinput_tablet_pad_mode_group_get_index (mode_group);
-        mode = libinput_event_tablet_pad_get_mode (pad_event);
-
-        notify_pad_strip (device, time, number, source, group, mode, value);
-        break;
-      }
-    case LIBINPUT_EVENT_TABLET_PAD_RING:
-      {
-        uint64_t time;
-        uint32_t number, source, group, mode;
-        struct libinput_tablet_pad_mode_group *mode_group;
-        struct libinput_event_tablet_pad *pad_event =
-          libinput_event_get_tablet_pad_event (event);
-        double angle;
-
-        device = libinput_device_get_user_data (libinput_device);
-        time = libinput_event_tablet_pad_get_time_usec (pad_event);
-        number = libinput_event_tablet_pad_get_ring_number (pad_event);
-        angle = libinput_event_tablet_pad_get_ring_position (pad_event);
-        source = libinput_event_tablet_pad_get_ring_source (pad_event);
-
-        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
-        group = libinput_tablet_pad_mode_group_get_index (mode_group);
-        mode = libinput_event_tablet_pad_get_mode (pad_event);
-
-        notify_pad_ring (device, time, number, source, group, mode, angle);
-        break;
-      }
-    default:
-      handled = FALSE;
-    }
-
-  return handled;
-}
-
-static gboolean
-filter_event (MetaDeviceManagerNative *manager_evdev,
-              struct libinput_event   *event)
-{
-  gboolean retval = CLUTTER_EVENT_PROPAGATE;
-  MetaEventFilter *filter;
-  GSList *tmp_list;
-
-  tmp_list = manager_evdev->priv->event_filters;
-
-  while (tmp_list)
-    {
-      filter = tmp_list->data;
-      retval = filter->func (event, filter->data);
-      tmp_list = tmp_list->next;
-
-      if (retval != CLUTTER_EVENT_PROPAGATE)
-        break;
-    }
-
-  return retval;
-}
-
-static void
-process_event (MetaDeviceManagerNative *manager_evdev,
-               struct libinput_event   *event)
-{
-  gboolean retval;
-
-  retval = filter_event (manager_evdev, event);
-
-  if (retval != CLUTTER_EVENT_PROPAGATE)
-    return;
-
-  if (process_base_event (manager_evdev, event))
-    return;
-  if (process_device_event (manager_evdev, event))
-    return;
-}
-
-static void
-process_events (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-  struct libinput_event *event;
-
-  while ((event = libinput_get_event (priv->libinput)))
-    {
-      process_event(manager_evdev, event);
-      libinput_event_destroy(event);
-    }
-}
-
-static int
-open_restricted (const char *path,
-                 int flags,
-                 void *user_data)
-{
-  gint fd;
-
-  if (device_open_callback)
-    {
-      GError *error = NULL;
-
-      fd = device_open_callback (path, flags, device_callback_data, &error);
-
-      if (fd < 0)
-        {
-          g_warning ("Could not open device %s: %s", path, error->message);
-          g_error_free (error);
-        }
-    }
-  else
-    {
-      fd = open (path, O_RDWR | O_NONBLOCK);
-      if (fd < 0)
-        {
-          g_warning ("Could not open device %s: %s", path, strerror (errno));
-        }
-    }
-
-  return fd;
-}
-
-static void
-close_restricted (int fd,
-                  void *user_data)
-{
-  if (device_close_callback)
-    device_close_callback (fd, device_callback_data);
-  else
-    close (fd);
-}
-
-static const struct libinput_interface libinput_interface = {
-  open_restricted,
-  close_restricted
-};
-
 static ClutterVirtualInputDevice *
 meta_device_manager_native_create_virtual_device (ClutterDeviceManager  *manager,
                                                   ClutterInputDeviceType device_type)
@@ -1862,138 +185,85 @@ meta_device_manager_native_compress_motion (ClutterDeviceManager *device_manger,
 
   if (!meta_event_native_get_relative_motion (to_discard,
                                               &dx, &dy,
-                                              &dx_unaccel, &dy_unaccel))
-    return;
-
-  meta_event_native_get_relative_motion (event,
-                                         &dst_dx, &dst_dy,
-                                         &dst_dx_unaccel, &dst_dy_unaccel);
-  meta_event_native_set_relative_motion (event,
-                                         dx + dst_dx,
-                                         dy + dst_dy,
-                                         dx_unaccel + dst_dx_unaccel,
-                                         dy_unaccel + dst_dy_unaccel);
-}
-
-static void
-meta_device_manager_native_apply_kbd_a11y_settings (ClutterDeviceManager   *device_manager,
-                                                    ClutterKbdA11ySettings *settings)
-{
-  ClutterInputDevice *device;
-
-  device = meta_device_manager_native_get_core_device (device_manager, CLUTTER_KEYBOARD_DEVICE);
-  if (device)
-    meta_input_device_native_apply_kbd_a11y_settings (META_INPUT_DEVICE_NATIVE (device),
-                                                      settings);
-}
-
-/*
- * GObject implementation
- */
-
-static void
-meta_device_manager_native_constructed (GObject *object)
-{
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-  MetaEventSource *source;
-  struct udev *udev;
-
-  udev = udev_new ();
-  if (G_UNLIKELY (udev == NULL))
-    {
-      g_warning ("Failed to create udev object");
-      return;
-    }
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (object);
-  priv = manager_evdev->priv;
-
-  priv->libinput = libinput_udev_create_context (&libinput_interface,
-                                                 manager_evdev,
-                                                 udev);
-  if (priv->libinput == NULL)
-    {
-      g_critical ("Failed to create the libinput object.");
-      return;
-    }
-
-  if (libinput_udev_assign_seat (priv->libinput,
-                                 evdev_seat_id ? evdev_seat_id : "seat0") == -1)
-    {
-      g_critical ("Failed to assign a seat to the libinput object.");
-      libinput_unref (priv->libinput);
-      priv->libinput = NULL;
-      return;
-    }
-
-  udev_unref (udev);
-
-  priv->main_seat = meta_seat_native_new (manager_evdev);
-  priv->seats = g_slist_append (priv->seats, priv->main_seat);
-
-  dispatch_libinput (manager_evdev);
+                                              &dx_unaccel, &dy_unaccel))
+    return;
 
-  source = meta_event_source_new (manager_evdev);
-  priv->event_source = source;
+  meta_event_native_get_relative_motion (event,
+                                         &dst_dx, &dst_dy,
+                                         &dst_dx_unaccel, &dst_dy_unaccel);
+  meta_event_native_set_relative_motion (event,
+                                         dx + dst_dx,
+                                         dy + dst_dy,
+                                         dx_unaccel + dst_dx_unaccel,
+                                         dy_unaccel + dst_dy_unaccel);
 }
 
 static void
-meta_device_manager_native_dispose (GObject *object)
+meta_device_manager_native_apply_kbd_a11y_settings (ClutterDeviceManager   *device_manager,
+                                                    ClutterKbdA11ySettings *settings)
 {
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (object);
-  priv = manager_evdev->priv;
-
-  g_clear_signal_handler (&priv->stage_added_handler, priv->stage_manager);
-  g_clear_signal_handler (&priv->stage_removed_handler, priv->stage_manager);
-
-  if (priv->stage_manager)
-    {
-      g_object_unref (priv->stage_manager);
-      priv->stage_manager = NULL;
-    }
+  ClutterInputDevice *device;
 
-  G_OBJECT_CLASS (meta_device_manager_native_parent_class)->dispose (object);
+  device = meta_device_manager_native_get_core_device (device_manager, CLUTTER_KEYBOARD_DEVICE);
+  if (device)
+    meta_input_device_native_apply_kbd_a11y_settings (META_INPUT_DEVICE_NATIVE (device),
+                                                      settings);
 }
 
 static void
-meta_device_manager_native_finalize (GObject *object)
+on_device_added (ClutterSeat          *seat,
+                 ClutterInputDevice   *parent,
+                 ClutterInputDevice   *device,
+                 ClutterDeviceManager *manager)
 {
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (object);
-  priv = manager_evdev->priv;
-
-  g_slist_free_full (priv->seats, (GDestroyNotify) meta_seat_native_free);
-  g_slist_free (priv->devices);
+  g_signal_emit_by_name (manager, "device-added", device);
+}
 
-  if (priv->event_source != NULL)
-    meta_event_source_free (priv->event_source);
+static void
+on_device_removed (ClutterSeat          *seat,
+                   ClutterInputDevice   *parent,
+                   ClutterInputDevice   *device,
+                   ClutterDeviceManager *manager)
+{
+  g_signal_emit_by_name (manager, "device-removed", device);
+}
 
-  if (priv->constrain_data_notify != NULL)
-    priv->constrain_data_notify (priv->constrain_data);
+static void
+on_tool_changed (ClutterSeat            *seat,
+                 ClutterInputDevice     *device,
+                 ClutterInputDeviceTool *tool,
+                 ClutterDeviceManager   *manager)
+{
+  g_signal_emit_by_name (manager, "tool-changed", device, tool);
+}
 
-  if (priv->libinput != NULL)
-    libinput_unref (priv->libinput);
+static void
+meta_device_manager_native_constructed (GObject *object)
+{
+  MetaDeviceManagerNative *manager_native = META_DEVICE_MANAGER_NATIVE (object);
 
-  g_list_free (priv->free_device_ids);
+  g_signal_connect (manager_native->priv->main_seat, "device-added",
+                    G_CALLBACK (on_device_added), manager_native);
+  g_signal_connect (manager_native->priv->main_seat, "device-added",
+                    G_CALLBACK (on_device_removed), manager_native);
+  g_signal_connect (manager_native->priv->main_seat, "tool-changed",
+                    G_CALLBACK (on_tool_changed), manager_native);
 
-  G_OBJECT_CLASS (meta_device_manager_native_parent_class)->finalize (object);
+  if (G_OBJECT_CLASS (meta_device_manager_native_parent_class)->constructed)
+    G_OBJECT_CLASS (meta_device_manager_native_parent_class)->constructed (object);
 }
 
+/*
+ * GObject implementation
+ */
 static void
 meta_device_manager_native_class_init (MetaDeviceManagerNativeClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   ClutterDeviceManagerClass *manager_class;
+  GObjectClass *object_class;
 
+  object_class = G_OBJECT_CLASS (klass);
   object_class->constructed = meta_device_manager_native_constructed;
-  object_class->finalize = meta_device_manager_native_finalize;
-  object_class->dispose = meta_device_manager_native_dispose;
 
   manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
   manager_class->add_device = meta_device_manager_native_add_device;
@@ -2009,294 +279,23 @@ meta_device_manager_native_class_init (MetaDeviceManagerNativeClass *klass)
   manager_class->free_event_data = meta_device_manager_native_free_event_data;
 }
 
-static void
-meta_device_manager_native_stage_added_cb (ClutterStageManager     *manager,
-                                           ClutterStage            *stage,
-                                           MetaDeviceManagerNative *self)
-{
-  MetaDeviceManagerNativePrivate *priv = self->priv;
-  GSList *l;
-
-  /* NB: Currently we can only associate a single stage with all evdev
-   * devices.
-   *
-   * We save a pointer to the stage so if we release/reclaim input
-   * devices due to switching virtual terminals then we know what
-   * stage to re associate the devices with.
-   */
-  priv->stage = stage;
-
-  /* Set the stage of any devices that don't already have a stage */
-  for (l = priv->seats; l; l = l->next)
-    {
-      MetaSeatNative *seat = l->data;
-
-      meta_seat_native_set_stage (seat, stage);
-    }
-
-  /* We only want to do this once so we can catch the default
-     stage. If the application has multiple stages then it will need
-     to manage the stage of the input devices itself */
-  g_clear_signal_handler (&priv->stage_added_handler, priv->stage_manager);
-}
-
-static void
-meta_device_manager_native_stage_removed_cb (ClutterStageManager     *manager,
-                                             ClutterStage            *stage,
-                                             MetaDeviceManagerNative *self)
-{
-  MetaDeviceManagerNativePrivate *priv = self->priv;
-  GSList *l;
-
-  /* Remove the stage of any input devices that were pointing to this
-     stage so we don't send events to invalid stages */
-  for (l = priv->seats; l; l = l->next)
-    {
-      MetaSeatNative *seat = l->data;
-
-      meta_seat_native_set_stage (seat, NULL);
-    }
-}
-
 static void
 meta_device_manager_native_init (MetaDeviceManagerNative *self)
 {
-  MetaDeviceManagerNativePrivate *priv;
-
-  priv = self->priv = meta_device_manager_native_get_instance_private (self);
-
-  priv->stage_manager = clutter_stage_manager_get_default ();
-  g_object_ref (priv->stage_manager);
-
-  /* evdev doesn't have any way to link an event to a particular stage
-     so we'll have to leave it up to applications to set the
-     corresponding stage for an input device. However to make it
-     easier for applications that are only using one fullscreen stage
-     (which is probably the most frequent use-case for the evdev
-     backend) we'll associate any input devices that don't have a
-     stage with the first stage created. */
-  priv->stage_added_handler =
-    g_signal_connect (priv->stage_manager,
-                      "stage-added",
-                      G_CALLBACK (meta_device_manager_native_stage_added_cb),
-                      self);
-  priv->stage_removed_handler =
-    g_signal_connect (priv->stage_manager,
-                      "stage-removed",
-                      G_CALLBACK (meta_device_manager_native_stage_removed_cb),
-                      self);
-
-  priv->device_id_next = INITIAL_DEVICE_ID;
-}
-
-gint
-meta_device_manager_native_acquire_device_id (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-  GList *first;
-  gint next_id;
-
-  if (priv->free_device_ids == NULL)
-    {
-      gint i;
-
-      /* We ran out of free ID's, so append 10 new ones. */
-      for (i = 0; i < 10; i++)
-        priv->free_device_ids =
-          g_list_append (priv->free_device_ids,
-                         GINT_TO_POINTER (priv->device_id_next++));
-    }
-
-  first = g_list_first (priv->free_device_ids);
-  next_id = GPOINTER_TO_INT (first->data);
-  priv->free_device_ids = g_list_delete_link (priv->free_device_ids, first);
-
-  return next_id;
-}
-
-void
-meta_device_manager_native_dispatch (MetaDeviceManagerNative *manager_evdev)
-{
-  dispatch_libinput (manager_evdev);
-}
-
-static int
-compare_ids (gconstpointer a,
-             gconstpointer b)
-{
-  return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
-}
-
-void
-meta_device_manager_native_release_device_id (MetaDeviceManagerNative *manager_evdev,
-                                              ClutterInputDevice      *device)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-  gint device_id;
-
-  device_id = clutter_input_device_get_device_id (device);
-  priv->free_device_ids = g_list_insert_sorted (priv->free_device_ids,
-                                                GINT_TO_POINTER (device_id),
-                                                compare_ids);
-}
-
-ClutterStage *
-meta_device_manager_native_get_stage (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-
-  return priv->stage;
-}
-
-/**
- * meta_device_manager_native_release_devices:
- *
- * Releases all the evdev devices that Clutter is currently managing. This api
- * is typically used when switching away from the Clutter application when
- * switching tty. The devices can be reclaimed later with a call to
- * meta_device_manager_native_reclaim_devices().
- *
- * This function should only be called after clutter has been initialized.
- *
- * Since: 1.10
- * Stability: unstable
- */
-void
-meta_device_manager_native_release_devices (void)
-{
-  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-
-  if (!manager)
-    {
-      g_warning ("clutter_evdev_release_devices shouldn't be called "
-                 "before clutter_init()");
-      return;
-    }
-
-  g_return_if_fail (META_IS_DEVICE_MANAGER_NATIVE (manager));
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  priv = manager_evdev->priv;
-
-  if (priv->released)
-    {
-      g_warning ("clutter_evdev_release_devices() shouldn't be called "
-                 "multiple times without a corresponding call to "
-                 "clutter_evdev_reclaim_devices() first");
-      return;
-    }
-
-  libinput_suspend (priv->libinput);
-  process_events (manager_evdev);
-
-  priv->released = TRUE;
-}
-
-static void
-update_xkb_state (MetaDeviceManagerNative *manager_evdev)
-{
-  MetaDeviceManagerNativePrivate *priv;
-  GSList *iter;
-  MetaSeatNative *seat;
-  xkb_mod_mask_t latched_mods;
-  xkb_mod_mask_t locked_mods;
-  struct xkb_keymap *xkb_keymap;
-  ClutterKeymap *keymap;
-
-  keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
-  xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap));
-
-  priv = manager_evdev->priv;
-
-  for (iter = priv->seats; iter; iter = iter->next)
-    {
-      seat = iter->data;
-
-      latched_mods = xkb_state_serialize_mods (seat->xkb,
-                                               XKB_STATE_MODS_LATCHED);
-      locked_mods = xkb_state_serialize_mods (seat->xkb,
-                                              XKB_STATE_MODS_LOCKED);
-      xkb_state_unref (seat->xkb);
-      seat->xkb = xkb_state_new (xkb_keymap);
-
-      xkb_state_update_mask (seat->xkb,
-                             0, /* depressed */
-                             latched_mods,
-                             locked_mods,
-                             0, 0, seat->layout_idx);
-
-      seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
-      seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
-      seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
-
-      meta_seat_native_sync_leds (seat);
-    }
 }
 
-/**
- * meta_device_manager_native_reclaim_devices:
- *
- * This causes Clutter to re-probe for evdev devices. This is must only be
- * called after a corresponding call to clutter_evdev_release_devices()
- * was previously used to release all evdev devices. This API is typically
- * used when a clutter application using evdev has regained focus due to
- * switching ttys.
- *
- * This function should only be called after clutter has been initialized.
- *
- * Since: 1.10
- * Stability: unstable
- */
-void
-meta_device_manager_native_reclaim_devices (void)
+MetaDeviceManagerNative *
+meta_device_manager_native_new (ClutterBackend *backend,
+                                MetaSeatNative *seat)
 {
-  ClutterDeviceManager *manager = clutter_device_manager_get_default ();
-  MetaDeviceManagerNative *manager_evdev =
-    META_DEVICE_MANAGER_NATIVE (manager);
-  MetaDeviceManagerNativePrivate *priv = manager_evdev->priv;
-
-  if (!priv->released)
-    {
-      g_warning ("Spurious call to clutter_evdev_reclaim_devices() without "
-                 "previous call to clutter_evdev_release_devices");
-      return;
-    }
+  MetaDeviceManagerNative *manager_native;
 
-  libinput_resume (priv->libinput);
-  update_xkb_state (manager_evdev);
-  process_events (manager_evdev);
+  manager_native = g_object_new (META_TYPE_DEVICE_MANAGER_NATIVE,
+                                 "backend", backend,
+                                 NULL);
+  manager_native->priv->main_seat = seat;
 
-  priv->released = FALSE;
-}
-
-/**
- * meta_device_manager_native_set_device_callbacks: (skip)
- * @open_callback: the user replacement for open()
- * @close_callback: the user replacement for close()
- * @user_data: user data for @callback
- *
- * Through this function, the application can set a custom callback
- * to invoked when Clutter is about to open an evdev device. It can do
- * so if special handling is needed, for example to circumvent permission
- * problems.
- *
- * Setting @callback to %NULL will reset the default behavior.
- *
- * For reliable effects, this function must be called before clutter_init().
- *
- * Since: 1.16
- * Stability: unstable
- */
-void
-meta_device_manager_native_set_device_callbacks (MetaOpenDeviceCallback  open_callback,
-                                                 MetaCloseDeviceCallback close_callback,
-                                                 gpointer                user_data)
-{
-  device_open_callback = open_callback;
-  device_close_callback = close_callback;
-  device_callback_data = user_data;
+  return manager_native;
 }
 
 /**
@@ -2326,7 +325,7 @@ meta_device_manager_native_set_keyboard_map (ClutterDeviceManager *evdev,
                                        xkb_keymap);
 
   manager_evdev = META_DEVICE_MANAGER_NATIVE (evdev);
-  update_xkb_state (manager_evdev);
+  meta_seat_native_update_xkb_state (manager_evdev->priv->main_seat);
 }
 
 /**
@@ -2367,11 +366,11 @@ meta_device_manager_native_set_keyboard_layout_index (ClutterDeviceManager *evde
                                                       xkb_layout_index_t    idx)
 {
   MetaDeviceManagerNative *manager_evdev;
+  MetaSeatNative *seat;
   xkb_mod_mask_t depressed_mods;
   xkb_mod_mask_t latched_mods;
   xkb_mod_mask_t locked_mods;
   struct xkb_state *state;
-  GSList *l;
 
   g_return_if_fail (META_IS_DEVICE_MANAGER_NATIVE (evdev));
 
@@ -2383,12 +382,9 @@ meta_device_manager_native_set_keyboard_layout_index (ClutterDeviceManager *evde
   locked_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED);
 
   xkb_state_update_mask (state, depressed_mods, latched_mods, locked_mods, 0, 0, idx);
-  for (l = manager_evdev->priv->seats; l; l = l->next)
-    {
-      MetaSeatNative *seat = l->data;
 
-      seat->layout_idx = idx;
-    }
+  seat = manager_evdev->priv->main_seat;
+  seat->layout_idx = idx;
 }
 
 /**
@@ -2418,7 +414,11 @@ meta_device_manager_native_set_keyboard_numlock (ClutterDeviceManager *evdev,
 {
   MetaDeviceManagerNative *manager_evdev;
   MetaDeviceManagerNativePrivate *priv;
-  GSList *iter;
+  MetaSeatNative *seat;
+  xkb_mod_mask_t depressed_mods;
+  xkb_mod_mask_t latched_mods;
+  xkb_mod_mask_t locked_mods;
+  xkb_mod_mask_t group_mods;
   xkb_mod_mask_t numlock;
   struct xkb_keymap *xkb_keymap;
   ClutterKeymap *keymap;
@@ -2433,87 +433,26 @@ meta_device_manager_native_set_keyboard_numlock (ClutterDeviceManager *evdev,
 
   numlock = (1 << xkb_keymap_mod_get_index (xkb_keymap, "Mod2"));
 
-  for (iter = priv->seats; iter; iter = iter->next)
-    {
-      MetaSeatNative *seat = iter->data;
-      xkb_mod_mask_t depressed_mods;
-      xkb_mod_mask_t latched_mods;
-      xkb_mod_mask_t locked_mods;
-      xkb_mod_mask_t group_mods;
-
-      depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED);
-      latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED);
-      locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED);
-      group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE);
-
-      if (numlock_state)
-        locked_mods |= numlock;
-      else
-        locked_mods &= ~numlock;
-
-      xkb_state_update_mask (seat->xkb,
-                             depressed_mods,
-                             latched_mods,
-                             locked_mods,
-                             0, 0,
-                             group_mods);
-
-      meta_seat_native_sync_leds (seat);
-    }
-}
-
-
-/**
- * meta_device_manager_native_set_pointer_constrain_callback:
- * @evdev: the #ClutterDeviceManager created by the evdev backend
- * @callback: the callback
- * @user_data: data to pass to the callback
- * @user_data_notify: function to be called when removing the callback
- *
- * Sets a callback to be invoked for every pointer motion. The callback
- * can then modify the new pointer coordinates to constrain movement within
- * a specific region.
- *
- * Since: 1.16
- * Stability: unstable
- */
-void
-meta_device_manager_native_set_pointer_constrain_callback (ClutterDeviceManager         *evdev,
-                                                           MetaPointerConstrainCallback  callback,
-                                                           gpointer                      user_data,
-                                                           GDestroyNotify                user_data_notify)
-{
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
-
-  g_return_if_fail (META_IS_DEVICE_MANAGER_NATIVE (evdev));
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (evdev);
-  priv = manager_evdev->priv;
-
-  if (priv->constrain_data_notify)
-    priv->constrain_data_notify (priv->constrain_data);
-
-  priv->constrain_callback = callback;
-  priv->constrain_data = user_data;
-  priv->constrain_data_notify = user_data_notify;
-}
+  seat = priv->main_seat;
 
-void
-meta_device_manager_native_set_relative_motion_filter (ClutterDeviceManager     *evdev,
-                                                       MetaRelativeMotionFilter  filter,
-                                                       gpointer                  user_data)
-{
-  MetaDeviceManagerNative *manager_evdev;
-  MetaDeviceManagerNativePrivate *priv;
+  depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED);
+  latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED);
+  locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED);
+  group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE);
 
-  g_return_if_fail (META_IS_DEVICE_MANAGER_NATIVE (evdev));
+  if (numlock_state)
+    locked_mods |= numlock;
+  else
+    locked_mods &= ~numlock;
 
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (evdev);
-  priv = manager_evdev->priv;
+  xkb_state_update_mask (seat->xkb,
+                         depressed_mods,
+                         latched_mods,
+                         locked_mods,
+                         0, 0,
+                         group_mods);
 
-  priv->relative_motion_filter = filter;
-  priv->relative_motion_filter_user_data = user_data;
+  meta_seat_native_sync_leds (seat);
 }
 
 /**
@@ -2550,139 +489,6 @@ meta_device_manager_native_set_keyboard_repeat (ClutterDeviceManager *evdev,
   seat->repeat_interval = interval;
 }
 
-/**
- * meta_device_manager_native_add_filter: (skip)
- * @func: (closure data): a filter function
- * @data: (allow-none): user data to be passed to the filter function, or %NULL
- * @destroy_notify: (allow-none): function to call on @data when the filter is removed, or %NULL
- *
- * Adds an event filter function.
- *
- * Since: 1.20
- * Stability: unstable
- */
-void
-meta_device_manager_native_add_filter (MetaEvdevFilterFunc func,
-                                       gpointer            data,
-                                       GDestroyNotify      destroy_notify)
-{
-  MetaDeviceManagerNative *manager_evdev;
-  ClutterDeviceManager *manager;
-  MetaEventFilter *filter;
-
-  g_return_if_fail (func != NULL);
-
-  manager = clutter_device_manager_get_default ();
-
-  if (!META_IS_DEVICE_MANAGER_NATIVE (manager))
-    {
-      g_critical ("The Clutter input backend is not a evdev backend");
-      return;
-    }
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-
-  filter = g_new0 (MetaEventFilter, 1);
-  filter->func = func;
-  filter->data = data;
-  filter->destroy_notify = destroy_notify;
-
-  manager_evdev->priv->event_filters =
-    g_slist_append (manager_evdev->priv->event_filters, filter);
-}
-
-/**
- * meta_device_manager_native_remove_filter: (skip)
- * @func: a filter function
- * @data: (allow-none): user data to be passed to the filter function, or %NULL
- *
- * Removes the given filter function.
- *
- * Since: 1.20
- * Stability: unstable
- */
-void
-meta_device_manager_native_remove_filter (MetaEvdevFilterFunc func,
-                                          gpointer            data)
-{
-  MetaDeviceManagerNative *manager_evdev;
-  ClutterDeviceManager *manager;
-  MetaEventFilter *filter;
-  GSList *tmp_list;
-
-  g_return_if_fail (func != NULL);
-
-  manager = clutter_device_manager_get_default ();
-
-  if (!META_IS_DEVICE_MANAGER_NATIVE (manager))
-    {
-      g_critical ("The Clutter input backend is not a evdev backend");
-      return;
-    }
-
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  tmp_list = manager_evdev->priv->event_filters;
-
-  while (tmp_list)
-    {
-      filter = tmp_list->data;
-
-      if (filter->func == func && filter->data == data)
-        {
-          if (filter->destroy_notify)
-            filter->destroy_notify (filter->data);
-          g_free (filter);
-          manager_evdev->priv->event_filters =
-            g_slist_delete_link (manager_evdev->priv->event_filters, tmp_list);
-          return;
-        }
-
-      tmp_list = tmp_list->next;
-    }
-}
-
-/**
- * meta_device_manager_native_warp_pointer:
- * @pointer_device: the pointer device to warp
- * @time: the timestamp for the warp event
- * @x: the new X position of the pointer
- * @y: the new Y position of the pointer
- *
- * Warps the pointer to a new location. Technically, this is
- * processed the same way as an absolute motion event from
- * libinput: it simply generates an absolute motion event that
- * will be processed on the next iteration of the mainloop.
- *
- * The intended use for this is for display servers that need
- * to warp cursor the cursor to a new location.
- *
- * Since: 1.20
- * Stability: unstable
- */
-void
-meta_device_manager_native_warp_pointer (ClutterInputDevice   *pointer_device,
-                                         uint32_t              time_,
-                                         int                   x,
-                                         int                   y)
-{
-  notify_absolute_motion (pointer_device, ms2us(time_), x, y, NULL);
-}
-
-/**
- * meta_device_manager_native_set_seat_id:
- * @seat_id: The seat ID
- *
- * Sets the seat to assign to the libinput context.
- *
- * For reliable effects, this function must be called before clutter_init().
- */
-void
-meta_device_manager_native_set_seat_id (const gchar *seat_id)
-{
-  g_free (evdev_seat_id);
-  evdev_seat_id = g_strdup (seat_id);
-}
-
 struct xkb_state *
 meta_device_manager_native_get_xkb_state (MetaDeviceManagerNative *manager_evdev)
 {
diff --git a/src/backends/native/meta-device-manager-native.h 
b/src/backends/native/meta-device-manager-native.h
index 9b8c410d5..feb5f7965 100644
--- a/src/backends/native/meta-device-manager-native.h
+++ b/src/backends/native/meta-device-manager-native.h
@@ -23,6 +23,7 @@
 #include <libinput.h>
 #include <xkbcommon/xkbcommon.h>
 
+#include "backends/native/meta-seat-native.h"
 #include "clutter/clutter-mutter.h"
 
 #define META_TYPE_DEVICE_MANAGER_NATIVE            (meta_device_manager_native_get_type ())
@@ -50,115 +51,11 @@ struct _MetaDeviceManagerNativeClass
 
 GType meta_device_manager_native_get_type (void) G_GNUC_CONST;
 
-gint  meta_device_manager_native_acquire_device_id (MetaDeviceManagerNative *manager_evdev);
-
-void  meta_device_manager_native_release_device_id (MetaDeviceManagerNative *manager_evdev,
-                                                    ClutterInputDevice      *device);
-
-ClutterStage * meta_device_manager_native_get_stage (MetaDeviceManagerNative *manager_evdev);
-
-void meta_device_manager_native_constrain_pointer (MetaDeviceManagerNative *manager_evdev,
-                                                   ClutterInputDevice      *core_pointer,
-                                                   uint64_t                 time_us,
-                                                   float                    x,
-                                                   float                    y,
-                                                   float                   *new_x,
-                                                   float                   *new_y);
-
-void meta_device_manager_native_filter_relative_motion (MetaDeviceManagerNative *manager_evdev,
-                                                        ClutterInputDevice      *device,
-                                                        float                    x,
-                                                        float                    y,
-                                                        float                   *dx,
-                                                        float                   *dy);
-
-void meta_device_manager_native_dispatch (MetaDeviceManagerNative *manager_evdev);
+MetaDeviceManagerNative * meta_device_manager_native_new (ClutterBackend *backend,
+                                                          MetaSeatNative *seat);
 
 struct xkb_state * meta_device_manager_native_get_xkb_state (MetaDeviceManagerNative *manager_evdev);
 
-static inline uint64_t
-us (uint64_t us)
-{
-  return us;
-}
-
-static inline uint64_t
-ms2us (uint64_t ms)
-{
-  return us (ms * 1000);
-}
-
-static inline uint32_t
-us2ms (uint64_t us)
-{
-  return (uint32_t) (us / 1000);
-}
-
-/**
- * MetaOpenDeviceCallback:
- * @path: the device path
- * @flags: flags to be passed to open
- *
- * This callback will be called when Clutter needs to access an input
- * device. It should return an open file descriptor for the file at @path,
- * or -1 if opening failed.
- */
-typedef int (*MetaOpenDeviceCallback) (const char  *path,
-                                       int          flags,
-                                       gpointer     user_data,
-                                       GError     **error);
-typedef void (*MetaCloseDeviceCallback) (int          fd,
-                                         gpointer     user_data);
-
-void  meta_device_manager_native_set_device_callbacks (MetaOpenDeviceCallback  open_callback,
-                                                       MetaCloseDeviceCallback close_callback,
-                                                       gpointer                user_data);
-
-void  meta_device_manager_native_set_seat_id (const gchar *seat_id);
-
-void  meta_device_manager_native_release_devices (void);
-void  meta_device_manager_native_reclaim_devices (void);
-
-/**
- * MetaPointerConstrainCallback:
- * @device: the core pointer device
- * @time: the event time in milliseconds
- * @x: (inout): the new X coordinate
- * @y: (inout): the new Y coordinate
- * @user_data: user data passed to this function
- *
- * This callback will be called for all pointer motion events, and should
- * update (@x, @y) to constrain the pointer position appropriately.
- * The subsequent motion event will use the updated values as the new coordinates.
- * Note that the coordinates are not clamped to the stage size, and the callback
- * must make sure that this happens before it returns.
- * Also note that the event will be emitted even if the pointer is constrained
- * to be in the same position.
- */
-typedef void (* MetaPointerConstrainCallback) (ClutterInputDevice *device,
-                                               uint32_t            time,
-                                               float               prev_x,
-                                               float               prev_y,
-                                               float              *x,
-                                               float              *y,
-                                               gpointer            user_data);
-
-void  meta_device_manager_native_set_pointer_constrain_callback (ClutterDeviceManager         *evdev,
-                                                                 MetaPointerConstrainCallback  callback,
-                                                                 gpointer                      user_data,
-                                                                 GDestroyNotify                
user_data_notify);
-
-typedef void (* MetaRelativeMotionFilter) (ClutterInputDevice *device,
-                                           float               x,
-                                           float               y,
-                                           float              *dx,
-                                           float              *dy,
-                                           gpointer            user_data);
-
-void meta_device_manager_native_set_relative_motion_filter (ClutterDeviceManager     *evdev,
-                                                            MetaRelativeMotionFilter  filter,
-                                                            gpointer                  user_data);
-
 void               meta_device_manager_native_set_keyboard_map   (ClutterDeviceManager *evdev,
                                                                   struct xkb_keymap    *keymap);
 
@@ -177,18 +74,4 @@ void meta_device_manager_native_set_keyboard_repeat (ClutterDeviceManager *evdev
                                                      uint32_t              delay,
                                                      uint32_t              interval);
 
-typedef gboolean (* MetaEvdevFilterFunc) (struct libinput_event *event,
-                                          gpointer               data);
-
-void meta_device_manager_native_add_filter    (MetaEvdevFilterFunc    func,
-                                               gpointer               data,
-                                               GDestroyNotify         destroy_notify);
-void meta_device_manager_native_remove_filter (MetaEvdevFilterFunc    func,
-                                               gpointer               data);
-
-void meta_device_manager_native_warp_pointer (ClutterInputDevice   *pointer_device,
-                                              uint32_t              time_,
-                                              int                   x,
-                                              int                   y);
-
 #endif /* META_DEVICE_MANAGER_NATIVE_H */
diff --git a/src/backends/native/meta-input-device-native.c b/src/backends/native/meta-input-device-native.c
index 312d715c0..296aee6ca 100644
--- a/src/backends/native/meta-input-device-native.c
+++ b/src/backends/native/meta-input-device-native.c
@@ -62,8 +62,8 @@ meta_input_device_native_finalize (GObject *object)
 {
   ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (object);
   MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (object);
-  MetaDeviceManagerNative *manager_evdev =
-    META_DEVICE_MANAGER_NATIVE (device->device_manager);
+  ClutterBackend *backend;
+  ClutterSeat *seat;
 
   if (device_evdev->libinput_device)
     libinput_device_unref (device_evdev->libinput_device);
@@ -71,7 +71,9 @@ meta_input_device_native_finalize (GObject *object)
   meta_input_device_native_release_touch_slots (device_evdev,
                                                 g_get_monotonic_time ());
 
-  meta_device_manager_native_release_device_id (manager_evdev, device);
+  backend = clutter_get_default_backend ();
+  seat = clutter_backend_get_default_seat (backend);
+  meta_seat_native_release_device_id (META_SEAT_NATIVE (seat), device);
 
   clear_slow_keys (device_evdev);
   stop_bounce_keys (device_evdev);
@@ -1320,13 +1322,11 @@ meta_input_device_native_init (MetaInputDeviceNative *self)
  * it with the provided seat.
  */
 ClutterInputDevice *
-meta_input_device_native_new (ClutterDeviceManager   *manager,
-                              MetaSeatNative         *seat,
+meta_input_device_native_new (MetaSeatNative         *seat,
                               struct libinput_device *libinput_device)
 {
   MetaInputDeviceNative *device;
   ClutterInputDeviceType type;
-  MetaDeviceManagerNative *manager_evdev;
   char *vendor, *product;
   int device_id, n_rings = 0, n_strips = 0, n_groups = 1;
   char *node_path;
@@ -1335,8 +1335,7 @@ meta_input_device_native_new (ClutterDeviceManager   *manager,
   type = meta_input_device_native_determine_type (libinput_device);
   vendor = g_strdup_printf ("%.4x", libinput_device_get_id_vendor (libinput_device));
   product = g_strdup_printf ("%.4x", libinput_device_get_id_product (libinput_device));
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  device_id = meta_device_manager_native_acquire_device_id (manager_evdev);
+  device_id = meta_seat_native_acquire_device_id (seat);
   node_path = g_strdup_printf ("/dev/input/%s", libinput_device_get_sysname (libinput_device));
 
   if (libinput_device_has_capability (libinput_device,
@@ -1350,7 +1349,6 @@ meta_input_device_native_new (ClutterDeviceManager   *manager,
   device = g_object_new (META_TYPE_INPUT_DEVICE_NATIVE,
                          "id", device_id,
                          "name", libinput_device_get_name (libinput_device),
-                         "device-manager", manager,
                          "device-type", type,
                          "device-mode", CLUTTER_INPUT_MODE_SLAVE,
                          "enabled", TRUE,
@@ -1360,6 +1358,7 @@ meta_input_device_native_new (ClutterDeviceManager   *manager,
                          "n-strips", n_strips,
                          "n-mode-groups", n_groups,
                          "device-node", node_path,
+                         "seat", seat,
                          NULL);
 
   device->seat = seat;
@@ -1379,20 +1378,17 @@ meta_input_device_native_new (ClutterDeviceManager   *manager,
 
 /*
  * meta_input_device_native_new_virtual:
- * @manager: the device manager
  * @seat: the seat the device will belong to
  * @type: the input device type
  *
  * Create a new virtual ClutterInputDevice of the given type.
  */
 ClutterInputDevice *
-meta_input_device_native_new_virtual (ClutterDeviceManager   *manager,
-                                      MetaSeatNative         *seat,
+meta_input_device_native_new_virtual (MetaSeatNative         *seat,
                                       ClutterInputDeviceType  type,
                                       ClutterInputMode        mode)
 {
   MetaInputDeviceNative *device;
-  MetaDeviceManagerNative *manager_evdev;
   const char *name;
   int device_id;
 
@@ -1412,15 +1408,14 @@ meta_input_device_native_new_virtual (ClutterDeviceManager   *manager,
       break;
     };
 
-  manager_evdev = META_DEVICE_MANAGER_NATIVE (manager);
-  device_id = meta_device_manager_native_acquire_device_id (manager_evdev);
+  device_id = meta_seat_native_acquire_device_id (seat);
   device = g_object_new (META_TYPE_INPUT_DEVICE_NATIVE,
                          "id", device_id,
                          "name", name,
-                         "device-manager", manager,
                          "device-type", type,
                          "device-mode", mode,
                          "enabled", TRUE,
+                         "seat", seat,
                          NULL);
 
   device->seat = seat;
diff --git a/src/backends/native/meta-input-device-native.h b/src/backends/native/meta-input-device-native.h
index 4b30c9265..9b3a21904 100644
--- a/src/backends/native/meta-input-device-native.h
+++ b/src/backends/native/meta-input-device-native.h
@@ -101,12 +101,10 @@ struct _MetaInputDeviceNativeClass
 
 GType                     meta_input_device_native_get_type        (void) G_GNUC_CONST;
 
-ClutterInputDevice *      meta_input_device_native_new             (ClutterDeviceManager    *manager,
-                                                                    MetaSeatNative          *seat,
+ClutterInputDevice *      meta_input_device_native_new             (MetaSeatNative          *seat,
                                                                     struct libinput_device  
*libinput_device);
 
-ClutterInputDevice *      meta_input_device_native_new_virtual     (ClutterDeviceManager    *manager,
-                                                                    MetaSeatNative          *seat,
+ClutterInputDevice *      meta_input_device_native_new_virtual     (MetaSeatNative          *seat,
                                                                     ClutterInputDeviceType   type,
                                                                     ClutterInputMode         mode);
 
diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c
index 0ccd24af5..cfb3c4131 100644
--- a/src/backends/native/meta-launcher.c
+++ b/src/backends/native/meta-launcher.c
@@ -37,6 +37,7 @@
 #include "backends/meta-backend-private.h"
 #include "backends/native/dbus-utils.h"
 #include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-clutter-backend-native.h"
 #include "backends/native/meta-cursor-renderer-native.h"
 #include "backends/native/meta-device-manager-native.h"
 #include "backends/native/meta-renderer-native.h"
@@ -525,11 +526,11 @@ meta_launcher_new (GError **error)
   self->sysfs_fds = g_hash_table_new (NULL, NULL);
   self->session_active = TRUE;
 
-  meta_device_manager_native_set_seat_id (self->seat_id);
+  meta_clutter_backend_native_set_seat_id (self->seat_id);
 
-  meta_device_manager_native_set_device_callbacks (on_evdev_device_open,
-                                                   on_evdev_device_close,
-                                                   self);
+  meta_seat_native_set_device_callbacks (on_evdev_device_open,
+                                         on_evdev_device_close,
+                                         self);
 
   g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self);
 
diff --git a/src/backends/native/meta-seat-native.c b/src/backends/native/meta-seat-native.c
index 75854e69a..e6325f164 100644
--- a/src/backends/native/meta-seat-native.c
+++ b/src/backends/native/meta-seat-native.c
@@ -26,6 +26,9 @@
 
 #include "config.h"
 
+#include <errno.h>
+#include <fcntl.h>
+#include <libinput.h>
 #include <linux/input.h>
 #include <math.h>
 
@@ -36,6 +39,16 @@
 #include "backends/native/meta-keymap-native.h"
 #include "clutter/clutter-mutter.h"
 
+/*
+ * Clutter makes the assumption that two core devices have ID's 2 and 3 (core
+ * pointer and core keyboard).
+ *
+ * Since the two first devices that will ever be created will be the virtual
+ * pointer and virtual keyboard of the first seat, we fulfill the made
+ * assumptions by having the first device having ID 2 and following 3.
+ */
+#define INITIAL_DEVICE_ID 2
+
 /* Try to keep the pointer inside the stage. Hopefully no one is using
  * this backend with stages smaller than this. */
 #define INITIAL_POINTER_X 16
@@ -49,6 +62,56 @@
 #define BTN_STYLUS3 0x149 /* Linux 4.15 */
 #endif
 
+typedef struct _MetaEventFilter MetaEventFilter;
+
+struct _MetaEventFilter
+{
+  MetaEvdevFilterFunc func;
+  gpointer data;
+  GDestroyNotify destroy_notify;
+};
+
+struct _MetaEventSource
+{
+  GSource source;
+
+  MetaSeatNative *seat;
+  GPollFD event_poll_fd;
+};
+
+static MetaOpenDeviceCallback  device_open_callback;
+static MetaCloseDeviceCallback device_close_callback;
+static gpointer                device_callback_data;
+
+#ifdef CLUTTER_ENABLE_DEBUG
+static const char *device_type_str[] = {
+  "pointer",            /* CLUTTER_POINTER_DEVICE */
+  "keyboard",           /* CLUTTER_KEYBOARD_DEVICE */
+  "extension",          /* CLUTTER_EXTENSION_DEVICE */
+  "joystick",           /* CLUTTER_JOYSTICK_DEVICE */
+  "tablet",             /* CLUTTER_TABLET_DEVICE */
+  "touchpad",           /* CLUTTER_TOUCHPAD_DEVICE */
+  "touchscreen",        /* CLUTTER_TOUCHSCREEN_DEVICE */
+  "pen",                /* CLUTTER_PEN_DEVICE */
+  "eraser",             /* CLUTTER_ERASER_DEVICE */
+  "cursor",             /* CLUTTER_CURSOR_DEVICE */
+  "pad",                /* CLUTTER_PAD_DEVICE */
+};
+#endif /* CLUTTER_ENABLE_DEBUG */
+
+enum
+{
+  PROP_0,
+  PROP_SEAT_ID,
+  N_PROPS
+};
+
+GParamSpec *props[N_PROPS] = { NULL };
+
+G_DEFINE_TYPE (MetaSeatNative, meta_seat_native, CLUTTER_TYPE_SEAT)
+
+static void process_events (MetaSeatNative *seat);
+
 void
 meta_seat_native_set_libinput_seat (MetaSeatNative       *seat,
                                     struct libinput_seat *libinput_seat)
@@ -145,63 +208,6 @@ meta_seat_native_release_touch_state (MetaSeatNative *seat,
                    clutter_touch_state_free);
 }
 
-MetaSeatNative *
-meta_seat_native_new (MetaDeviceManagerNative *manager_evdev)
-{
-  ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (manager_evdev);
-  MetaSeatNative *seat;
-  ClutterInputDevice *device;
-  ClutterStage *stage;
-  ClutterKeymap *keymap;
-  struct xkb_keymap *xkb_keymap;
-
-  seat = g_new0 (MetaSeatNative, 1);
-  if (!seat)
-    return NULL;
-
-  seat->manager_evdev = manager_evdev;
-  device = meta_input_device_native_new_virtual (
-      manager, seat, CLUTTER_POINTER_DEVICE,
-      CLUTTER_INPUT_MODE_MASTER);
-  stage = meta_device_manager_native_get_stage (manager_evdev);
-  _clutter_input_device_set_stage (device, stage);
-  seat->pointer_x = INITIAL_POINTER_X;
-  seat->pointer_y = INITIAL_POINTER_Y;
-  _clutter_input_device_set_coords (device, NULL,
-                                    seat->pointer_x, seat->pointer_y,
-                                    NULL);
-  _clutter_device_manager_add_device (manager, device);
-  seat->core_pointer = device;
-
-  device = meta_input_device_native_new_virtual (
-      manager, seat, CLUTTER_KEYBOARD_DEVICE,
-      CLUTTER_INPUT_MODE_MASTER);
-  _clutter_input_device_set_stage (device, stage);
-  _clutter_device_manager_add_device (manager, device);
-  seat->core_keyboard = device;
-
-  seat->repeat = TRUE;
-  seat->repeat_delay = 250;     /* ms */
-  seat->repeat_interval = 33;   /* ms */
-
-  keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
-  xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap));
-
-  if (xkb_keymap)
-    {
-      seat->xkb = xkb_state_new (xkb_keymap);
-
-      seat->caps_lock_led =
-        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
-      seat->num_lock_led =
-        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
-      seat->scroll_lock_led =
-        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
-    }
-
-  return seat;
-}
-
 void
 meta_seat_native_clear_repeat_timer (MetaSeatNative *seat)
 {
@@ -212,6 +218,13 @@ meta_seat_native_clear_repeat_timer (MetaSeatNative *seat)
     }
 }
 
+static void
+dispatch_libinput (MetaSeatNative *seat)
+{
+  libinput_dispatch (seat->libinput);
+  process_events (seat);
+}
+
 static gboolean
 keyboard_repeat (gpointer data)
 {
@@ -220,7 +233,7 @@ keyboard_repeat (gpointer data)
 
   /* There might be events queued in libinput that could cancel the
      repeat timer. */
-  meta_device_manager_native_dispatch (seat->manager_evdev);
+  dispatch_libinput (seat);
   if (!seat->repeat_timer)
     return G_SOURCE_REMOVE;
 
@@ -385,12 +398,12 @@ new_absolute_motion_event (MetaSeatNative     *seat,
 
   if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE)
     {
-      meta_device_manager_native_constrain_pointer (seat->manager_evdev,
-                                                    seat->core_pointer,
-                                                    time_us,
-                                                    seat->pointer_x,
-                                                    seat->pointer_y,
-                                                    &x, &y);
+      meta_seat_native_constrain_pointer (seat,
+                                          seat->core_pointer,
+                                          time_us,
+                                          seat->pointer_x,
+                                          seat->pointer_y,
+                                          &x, &y);
     }
 
   meta_event_native_set_time_usec (event, time_us);
@@ -444,12 +457,12 @@ meta_seat_native_notify_relative_motion (MetaSeatNative     *seat,
   if (!_clutter_input_device_get_stage (input_device))
     return;
 
-  meta_device_manager_native_filter_relative_motion (seat->manager_evdev,
-                                                     input_device,
-                                                     seat->pointer_x,
-                                                     seat->pointer_y,
-                                                     &dx,
-                                                     &dy);
+  meta_seat_native_filter_relative_motion (seat,
+                                           input_device,
+                                           seat->pointer_x,
+                                           seat->pointer_y,
+                                           &dx,
+                                           &dy);
 
   new_x = seat->pointer_x + dx;
   new_y = seat->pointer_y + dy;
@@ -843,61 +856,2079 @@ meta_seat_native_notify_touch_event (MetaSeatNative     *seat,
   queue_event (event);
 }
 
-void
-meta_seat_native_free (MetaSeatNative *seat)
+
+/*
+ * MetaEventSource for reading input devices
+ */
+static gboolean
+meta_event_prepare (GSource *source,
+                    gint    *timeout)
 {
-  GSList *iter;
+  gboolean retval;
 
-  for (iter = seat->devices; iter; iter = g_slist_next (iter))
+  _clutter_threads_acquire_lock ();
+
+  *timeout = -1;
+  retval = clutter_events_pending ();
+
+  _clutter_threads_release_lock ();
+
+  return retval;
+}
+
+static gboolean
+meta_event_check (GSource *source)
+{
+  MetaEventSource *event_source = (MetaEventSource *) source;
+  gboolean retval;
+
+  _clutter_threads_acquire_lock ();
+
+  retval = ((event_source->event_poll_fd.revents & G_IO_IN) ||
+            clutter_events_pending ());
+
+  _clutter_threads_release_lock ();
+
+  return retval;
+}
+
+void
+meta_seat_native_constrain_pointer (MetaSeatNative     *seat,
+                                    ClutterInputDevice *core_pointer,
+                                    uint64_t            time_us,
+                                    float               x,
+                                    float               y,
+                                    float              *new_x,
+                                    float              *new_y)
+{
+  if (seat->constrain_callback)
     {
-      ClutterInputDevice *device = iter->data;
+      seat->constrain_callback (core_pointer,
+                                us2ms (time_us),
+                                x, y,
+                                new_x, new_y,
+                                seat->constrain_data);
+    }
+  else
+    {
+      ClutterActor *stage = CLUTTER_ACTOR (meta_seat_native_get_stage (seat));
+      float stage_width = clutter_actor_get_width (stage);
+      float stage_height = clutter_actor_get_height (stage);
 
-      g_object_unref (device);
+      *new_x = CLAMP (x, 0.f, stage_width - 1);
+      *new_y = CLAMP (y, 0.f, stage_height - 1);
     }
-  g_slist_free (seat->devices);
-  g_free (seat->touch_states);
+}
 
-  xkb_state_unref (seat->xkb);
+void
+meta_seat_native_filter_relative_motion (MetaSeatNative     *seat,
+                                         ClutterInputDevice *device,
+                                         float               x,
+                                         float               y,
+                                         float              *dx,
+                                         float              *dy)
+{
+  if (!seat->relative_motion_filter)
+    return;
 
-  meta_seat_native_clear_repeat_timer (seat);
+  seat->relative_motion_filter (device, x, y, dx, dy,
+                                seat->relative_motion_filter_user_data);
+}
 
-  if (seat->libinput_seat)
-    libinput_seat_unref (seat->libinput_seat);
+static void
+notify_absolute_motion (ClutterInputDevice *input_device,
+                        uint64_t            time_us,
+                        float               x,
+                        float               y,
+                        double             *axes)
+{
+  MetaSeatNative *seat;
+  ClutterEvent *event;
+
+  seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (input_device));
+  event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes);
 
-  g_free (seat);
+  queue_event (event);
 }
 
-ClutterInputDevice *
-meta_seat_native_get_device (MetaSeatNative *seat,
-                             int             id)
+static void
+notify_relative_tool_motion (ClutterInputDevice *input_device,
+                             uint64_t            time_us,
+                             float               dx,
+                             float               dy,
+                             double             *axes)
 {
-  ClutterInputDevice *device;
-  GSList *l;
+  MetaInputDeviceNative *device_evdev;
+  ClutterEvent *event;
+  MetaSeatNative *seat;
+  gfloat x, y;
 
-  for (l = seat->devices; l; l = l->next)
-    {
-      device = l->data;
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+  x = input_device->current_x + dx;
+  y = input_device->current_y + dy;
 
-      if (clutter_input_device_get_device_id (device) == id)
-        return device;
-    }
+  meta_seat_native_filter_relative_motion (seat,
+                                           input_device,
+                                           seat->pointer_x,
+                                           seat->pointer_y,
+                                           &dx,
+                                           &dy);
 
-  return NULL;
+  event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes);
+  meta_event_native_set_relative_motion (event, dx, dy, 0, 0);
+
+  queue_event (event);
 }
 
-void
-meta_seat_native_set_stage (MetaSeatNative *seat,
-                            ClutterStage   *stage)
+static void
+notify_pinch_gesture_event (ClutterInputDevice          *input_device,
+                            ClutterTouchpadGesturePhase  phase,
+                            uint64_t                     time_us,
+                            double                       dx,
+                            double                       dy,
+                            double                       angle_delta,
+                            double                       scale,
+                            uint32_t                     n_fingers)
 {
-  GSList *l;
+  MetaInputDeviceNative *device_evdev;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event = NULL;
+  graphene_point_t pos;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH);
+
+  clutter_input_device_get_coords (seat->core_pointer, NULL, &pos);
+
+  meta_event_native_set_time_usec (event, time_us);
+  event->touchpad_pinch.phase = phase;
+  event->touchpad_pinch.time = us2ms (time_us);
+  event->touchpad_pinch.stage = CLUTTER_STAGE (stage);
+  event->touchpad_pinch.x = pos.x;
+  event->touchpad_pinch.y = pos.y;
+  event->touchpad_pinch.dx = dx;
+  event->touchpad_pinch.dy = dy;
+  event->touchpad_pinch.angle_delta = angle_delta;
+  event->touchpad_pinch.scale = scale;
+  event->touchpad_pinch.n_fingers = n_fingers;
+
+  meta_xkb_translate_state (event, seat->xkb, seat->button_state);
+
+  clutter_event_set_device (event, seat->core_pointer);
+  clutter_event_set_source_device (event, input_device);
+
+  queue_event (event);
+}
+
+static void
+notify_swipe_gesture_event (ClutterInputDevice          *input_device,
+                            ClutterTouchpadGesturePhase  phase,
+                            uint64_t                     time_us,
+                            uint32_t                     n_fingers,
+                            double                       dx,
+                            double                       dy)
+{
+  MetaInputDeviceNative *device_evdev;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event = NULL;
+  graphene_point_t pos;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE);
+
+  meta_event_native_set_time_usec (event, time_us);
+  event->touchpad_swipe.phase = phase;
+  event->touchpad_swipe.time = us2ms (time_us);
+  event->touchpad_swipe.stage = CLUTTER_STAGE (stage);
+
+  clutter_input_device_get_coords (seat->core_pointer, NULL, &pos);
+  event->touchpad_swipe.x = pos.x;
+  event->touchpad_swipe.y = pos.y;
+  event->touchpad_swipe.dx = dx;
+  event->touchpad_swipe.dy = dy;
+  event->touchpad_swipe.n_fingers = n_fingers;
+
+  meta_xkb_translate_state (event, seat->xkb, seat->button_state);
+
+  clutter_event_set_device (event, seat->core_pointer);
+  clutter_event_set_source_device (event, input_device);
+
+  queue_event (event);
+}
+
+static void
+notify_proximity (ClutterInputDevice *input_device,
+                  uint64_t            time_us,
+                  gboolean            in)
+{
+  MetaInputDeviceNative *device_evdev;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event = NULL;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  if (in)
+    event = clutter_event_new (CLUTTER_PROXIMITY_IN);
+  else
+    event = clutter_event_new (CLUTTER_PROXIMITY_OUT);
+
+  meta_event_native_set_time_usec (event, time_us);
+
+  event->proximity.time = us2ms (time_us);
+  event->proximity.stage = CLUTTER_STAGE (stage);
+  clutter_event_set_device_tool (event, device_evdev->last_tool);
+  clutter_event_set_device (event, seat->core_pointer);
+  clutter_event_set_source_device (event, input_device);
 
   _clutter_input_device_set_stage (seat->core_pointer, stage);
-  _clutter_input_device_set_stage (seat->core_keyboard, stage);
 
-  for (l = seat->devices; l; l = l->next)
-    {
-      ClutterInputDevice *device = l->data;
+  queue_event (event);
+}
 
-      _clutter_input_device_set_stage (device, stage);
-    }
+static void
+notify_pad_button (ClutterInputDevice *input_device,
+                   uint64_t            time_us,
+                   uint32_t            button,
+                   uint32_t            mode_group,
+                   uint32_t            mode,
+                   uint32_t            pressed)
+{
+  MetaInputDeviceNative *device_evdev;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  if (pressed)
+    event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS);
+  else
+    event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE);
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  meta_event_native_set_time_usec (event, time_us);
+  event->pad_button.stage = stage;
+  event->pad_button.button = button;
+  event->pad_button.group = mode_group;
+  event->pad_button.mode = mode;
+  clutter_event_set_device (event, input_device);
+  clutter_event_set_source_device (event, input_device);
+  clutter_event_set_time (event, us2ms (time_us));
+
+  _clutter_input_device_set_stage (seat->core_pointer, stage);
+
+  queue_event (event);
+}
+
+static void
+notify_pad_strip (ClutterInputDevice *input_device,
+                  uint64_t            time_us,
+                  uint32_t            strip_number,
+                  uint32_t            strip_source,
+                  uint32_t            mode_group,
+                  uint32_t            mode,
+                  double              value)
+{
+  MetaInputDeviceNative *device_evdev;
+  ClutterInputDevicePadSource source;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  if (strip_source == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER)
+    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER;
+  else
+    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN;
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  event = clutter_event_new (CLUTTER_PAD_STRIP);
+  meta_event_native_set_time_usec (event, time_us);
+  event->pad_strip.strip_source = source;
+  event->pad_strip.stage = stage;
+  event->pad_strip.strip_number = strip_number;
+  event->pad_strip.value = value;
+  event->pad_strip.group = mode_group;
+  event->pad_strip.mode = mode;
+  clutter_event_set_device (event, input_device);
+  clutter_event_set_source_device (event, input_device);
+  clutter_event_set_time (event, us2ms (time_us));
+
+  _clutter_input_device_set_stage (seat->core_pointer, stage);
+
+  queue_event (event);
+}
+
+static void
+notify_pad_ring (ClutterInputDevice *input_device,
+                 uint64_t            time_us,
+                 uint32_t            ring_number,
+                 uint32_t            ring_source,
+                 uint32_t            mode_group,
+                 uint32_t            mode,
+                 double              angle)
+{
+  MetaInputDeviceNative *device_evdev;
+  ClutterInputDevicePadSource source;
+  MetaSeatNative *seat;
+  ClutterStage *stage;
+  ClutterEvent *event;
+
+  /* We can drop the event on the floor if no stage has been
+   * associated with the device yet. */
+  stage = _clutter_input_device_get_stage (input_device);
+  if (stage == NULL)
+    return;
+
+  if (ring_source == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER)
+    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER;
+  else
+    source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN;
+
+  device_evdev = META_INPUT_DEVICE_NATIVE (input_device);
+  seat = meta_input_device_native_get_seat (device_evdev);
+
+  event = clutter_event_new (CLUTTER_PAD_RING);
+  meta_event_native_set_time_usec (event, time_us);
+  event->pad_ring.ring_source = source;
+  event->pad_ring.stage = stage;
+  event->pad_ring.ring_number = ring_number;
+  event->pad_ring.angle = angle;
+  event->pad_ring.group = mode_group;
+  event->pad_ring.mode = mode;
+  clutter_event_set_device (event, input_device);
+  clutter_event_set_source_device (event, input_device);
+  clutter_event_set_time (event, us2ms (time_us));
+
+  _clutter_input_device_set_stage (seat->core_pointer, stage);
+
+  queue_event (event);
+}
+
+static gboolean
+meta_event_dispatch (GSource     *g_source,
+                     GSourceFunc  callback,
+                     gpointer     user_data)
+{
+  MetaEventSource *source = (MetaEventSource *) g_source;
+  MetaSeatNative *seat;
+  ClutterEvent *event;
+
+  _clutter_threads_acquire_lock ();
+
+  seat = source->seat;
+
+  /* Don't queue more events if we haven't finished handling the previous batch
+   */
+  if (clutter_events_pending ())
+    goto queue_event;
+
+  dispatch_libinput (seat);
+
+ queue_event:
+  event = clutter_event_get ();
+
+  if (event)
+    {
+      ClutterModifierType event_state;
+      ClutterInputDevice *input_device =
+        clutter_event_get_source_device (event);
+      MetaInputDeviceNative *device_evdev =
+        META_INPUT_DEVICE_NATIVE (input_device);
+      MetaSeatNative *seat =
+        meta_input_device_native_get_seat (device_evdev);
+
+      /* Drop events if we don't have any stage to forward them to */
+      if (!_clutter_input_device_get_stage (input_device))
+        goto out;
+
+      /* update the device states *before* the event */
+      event_state = seat->button_state |
+        xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_EFFECTIVE);
+      _clutter_input_device_set_state (seat->core_pointer, event_state);
+      _clutter_input_device_set_state (seat->core_keyboard, event_state);
+
+      /* forward the event into clutter for emission etc. */
+      _clutter_stage_queue_event (event->any.stage, event, FALSE);
+    }
+
+out:
+  _clutter_threads_release_lock ();
+
+  return TRUE;
+}
+static GSourceFuncs event_funcs = {
+  meta_event_prepare,
+  meta_event_check,
+  meta_event_dispatch,
+  NULL
+};
+
+static MetaEventSource *
+meta_event_source_new (MetaSeatNative *seat)
+{
+  GSource *source;
+  MetaEventSource *event_source;
+  gint fd;
+
+  source = g_source_new (&event_funcs, sizeof (MetaEventSource));
+  event_source = (MetaEventSource *) source;
+
+  /* setup the source */
+  event_source->seat = seat;
+
+  fd = libinput_get_fd (seat->libinput);
+  event_source->event_poll_fd.fd = fd;
+  event_source->event_poll_fd.events = G_IO_IN;
+
+  /* and finally configure and attach the GSource */
+  g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
+  g_source_add_poll (source, &event_source->event_poll_fd);
+  g_source_set_can_recurse (source, TRUE);
+  g_source_attach (source, NULL);
+
+  return event_source;
+}
+
+static void
+meta_event_source_free (MetaEventSource *source)
+{
+  GSource *g_source = (GSource *) source;
+
+  /* ignore the return value of close, it's not like we can do something
+   * about it */
+  close (source->event_poll_fd.fd);
+
+  g_source_destroy (g_source);
+  g_source_unref (g_source);
+}
+
+static void
+evdev_add_device (MetaSeatNative         *seat,
+                  struct libinput_device *libinput_device)
+{
+  ClutterInputDeviceType type;
+  ClutterInputDevice *device, *master = NULL;
+  ClutterActor *stage;
+
+  device = meta_input_device_native_new (seat, libinput_device);
+  stage = CLUTTER_ACTOR (meta_seat_native_get_stage (seat));
+  _clutter_input_device_set_stage (device, CLUTTER_STAGE (stage));
+
+  seat->devices = g_slist_prepend (seat->devices, device);
+
+  /* Clutter assumes that device types are exclusive in the
+   * ClutterInputDevice API */
+  type = meta_input_device_native_determine_type (libinput_device);
+
+  if (type == CLUTTER_KEYBOARD_DEVICE)
+    master = seat->core_keyboard;
+  else if (type == CLUTTER_POINTER_DEVICE)
+    master = seat->core_pointer;
+
+  if (master)
+    {
+      _clutter_input_device_set_associated_device (device, master);
+      _clutter_input_device_add_slave (master, device);
+    }
+
+  g_signal_emit_by_name (seat, "device-added", device);
+}
+
+static void
+evdev_remove_device (MetaSeatNative        *seat,
+                     MetaInputDeviceNative *device_evdev)
+{
+  ClutterInputDevice *device;
+
+  device = CLUTTER_INPUT_DEVICE (device_evdev);
+  seat->devices = g_slist_remove (seat->devices, device);
+
+  g_signal_emit_by_name (seat, "device-removed", device);
+
+  if (seat->repeat_timer && seat->repeat_device == device)
+    meta_seat_native_clear_repeat_timer (seat);
+
+  g_object_run_dispose (G_OBJECT (device));
+  g_object_unref (device);
+}
+
+static void
+flush_event_queue (void)
+{
+  ClutterEvent *event;
+
+  while ((event = clutter_event_get ()) != NULL)
+    {
+      _clutter_process_event (event);
+      clutter_event_free (event);
+    }
+}
+
+static gboolean
+process_base_event (MetaSeatNative        *seat,
+                    struct libinput_event *event)
+{
+  ClutterInputDevice *device;
+  struct libinput_device *libinput_device;
+  gboolean handled = TRUE;
+
+  switch (libinput_event_get_type (event))
+    {
+    case LIBINPUT_EVENT_DEVICE_ADDED:
+      libinput_device = libinput_event_get_device (event);
+
+      evdev_add_device (seat, libinput_device);
+      break;
+
+    case LIBINPUT_EVENT_DEVICE_REMOVED:
+      /* Flush all queued events, there
+       * might be some from this device.
+       */
+      flush_event_queue ();
+
+      libinput_device = libinput_event_get_device (event);
+
+      device = libinput_device_get_user_data (libinput_device);
+      evdev_remove_device (seat,
+                           META_INPUT_DEVICE_NATIVE (device));
+      break;
+
+    default:
+      handled = FALSE;
+    }
+
+  return handled;
+}
+
+static ClutterScrollSource
+translate_scroll_source (enum libinput_pointer_axis_source source)
+{
+  switch (source)
+    {
+    case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+      return CLUTTER_SCROLL_SOURCE_WHEEL;
+    case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+      return CLUTTER_SCROLL_SOURCE_FINGER;
+    case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+      return CLUTTER_SCROLL_SOURCE_CONTINUOUS;
+    default:
+      return CLUTTER_SCROLL_SOURCE_UNKNOWN;
+    }
+}
+
+static ClutterInputDeviceToolType
+translate_tool_type (struct libinput_tablet_tool *libinput_tool)
+{
+  enum libinput_tablet_tool_type tool;
+
+  tool = libinput_tablet_tool_get_type (libinput_tool);
+
+  switch (tool)
+    {
+    case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+      return CLUTTER_INPUT_DEVICE_TOOL_PEN;
+    case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+      return CLUTTER_INPUT_DEVICE_TOOL_ERASER;
+    case LIBINPUT_TABLET_TOOL_TYPE_BRUSH:
+      return CLUTTER_INPUT_DEVICE_TOOL_BRUSH;
+    case LIBINPUT_TABLET_TOOL_TYPE_PENCIL:
+      return CLUTTER_INPUT_DEVICE_TOOL_PENCIL;
+    case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH:
+      return CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH;
+    case LIBINPUT_TABLET_TOOL_TYPE_MOUSE:
+      return CLUTTER_INPUT_DEVICE_TOOL_MOUSE;
+    case LIBINPUT_TABLET_TOOL_TYPE_LENS:
+      return CLUTTER_INPUT_DEVICE_TOOL_LENS;
+    default:
+      return CLUTTER_INPUT_DEVICE_TOOL_NONE;
+    }
+}
+
+static void
+input_device_update_tool (ClutterInputDevice          *input_device,
+                          struct libinput_tablet_tool *libinput_tool)
+{
+  MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device);
+  MetaSeatNative *seat = meta_input_device_native_get_seat (evdev_device);
+  ClutterInputDeviceTool *tool = NULL;
+  ClutterInputDeviceToolType tool_type;
+  uint64_t tool_serial;
+
+  if (libinput_tool)
+    {
+      tool_serial = libinput_tablet_tool_get_serial (libinput_tool);
+      tool_type = translate_tool_type (libinput_tool);
+      tool = clutter_input_device_lookup_tool (input_device,
+                                               tool_serial, tool_type);
+
+      if (!tool)
+        {
+          tool = meta_input_device_tool_native_new (libinput_tool,
+                                                    tool_serial, tool_type);
+          clutter_input_device_add_tool (input_device, tool);
+        }
+    }
+
+  if (evdev_device->last_tool != tool)
+    {
+      evdev_device->last_tool = tool;
+      g_signal_emit_by_name (seat, "tool-changed", input_device, tool);
+    }
+}
+
+static gdouble *
+translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event,
+                       ClutterInputDeviceTool            *tool)
+{
+  GArray *axes = g_array_new (FALSE, FALSE, sizeof (gdouble));
+  struct libinput_tablet_tool *libinput_tool;
+  gdouble value;
+
+  libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event);
+
+  value = libinput_event_tablet_tool_get_x (tablet_event);
+  g_array_append_val (axes, value);
+  value = libinput_event_tablet_tool_get_y (tablet_event);
+  g_array_append_val (axes, value);
+
+  if (libinput_tablet_tool_has_distance (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_distance (tablet_event);
+      g_array_append_val (axes, value);
+    }
+
+  if (libinput_tablet_tool_has_pressure (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_pressure (tablet_event);
+      value = meta_input_device_tool_native_translate_pressure (tool, value);
+      g_array_append_val (axes, value);
+    }
+
+  if (libinput_tablet_tool_has_tilt (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_tilt_x (tablet_event);
+      g_array_append_val (axes, value);
+      value = libinput_event_tablet_tool_get_tilt_y (tablet_event);
+      g_array_append_val (axes, value);
+    }
+
+  if (libinput_tablet_tool_has_rotation (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_rotation (tablet_event);
+      g_array_append_val (axes, value);
+    }
+
+  if (libinput_tablet_tool_has_slider (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_slider_position (tablet_event);
+      g_array_append_val (axes, value);
+    }
+
+  if (libinput_tablet_tool_has_wheel (libinput_tool))
+    {
+      value = libinput_event_tablet_tool_get_wheel_delta (tablet_event);
+      g_array_append_val (axes, value);
+    }
+
+  if (axes->len == 0)
+    {
+      g_array_free (axes, TRUE);
+      return NULL;
+    }
+  else
+    return (gdouble *) g_array_free (axes, FALSE);
+}
+
+static MetaSeatNative *
+seat_from_device (ClutterInputDevice *device)
+{
+  MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device);
+
+  return meta_input_device_native_get_seat (device_evdev);
+}
+
+static void
+notify_continuous_axis (MetaSeatNative                *seat,
+                        ClutterInputDevice            *device,
+                        uint64_t                       time_us,
+                        ClutterScrollSource            scroll_source,
+                        struct libinput_event_pointer *axis_event)
+{
+  gdouble dx = 0.0, dy = 0.0;
+  ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE;
+
+  if (libinput_event_pointer_has_axis (axis_event,
+                                       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
+    {
+      dx = libinput_event_pointer_get_axis_value (
+          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+
+      if (fabs (dx) < DBL_EPSILON)
+        finish_flags |= CLUTTER_SCROLL_FINISHED_HORIZONTAL;
+    }
+  if (libinput_event_pointer_has_axis (axis_event,
+                                       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
+    {
+      dy = libinput_event_pointer_get_axis_value (
+          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+
+      if (fabs (dy) < DBL_EPSILON)
+        finish_flags |= CLUTTER_SCROLL_FINISHED_VERTICAL;
+    }
+
+  meta_seat_native_notify_scroll_continuous (seat, device, time_us,
+                                             dx, dy,
+                                             scroll_source, finish_flags);
+}
+
+static void
+notify_discrete_axis (MetaSeatNative                *seat,
+                      ClutterInputDevice            *device,
+                      uint64_t                       time_us,
+                      ClutterScrollSource            scroll_source,
+                      struct libinput_event_pointer *axis_event)
+{
+  gdouble discrete_dx = 0.0, discrete_dy = 0.0;
+
+  if (libinput_event_pointer_has_axis (axis_event,
+                                       LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
+    {
+      discrete_dx = libinput_event_pointer_get_axis_value_discrete (
+          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+    }
+  if (libinput_event_pointer_has_axis (axis_event,
+                                       LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
+    {
+      discrete_dy = libinput_event_pointer_get_axis_value_discrete (
+          axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+    }
+
+  meta_seat_native_notify_discrete_scroll (seat, device,
+                                           time_us,
+                                           discrete_dx, discrete_dy,
+                                           scroll_source);
+}
+
+static void
+process_tablet_axis (MetaSeatNative        *seat,
+                     struct libinput_event *event)
+{
+  struct libinput_device *libinput_device = libinput_event_get_device (event);
+  uint64_t time;
+  double x, y, dx, dy, *axes;
+  float stage_width, stage_height;
+  ClutterStage *stage;
+  ClutterInputDevice *device;
+  struct libinput_event_tablet_tool *tablet_event =
+    libinput_event_get_tablet_tool_event (event);
+  MetaInputDeviceNative *evdev_device;
+
+  device = libinput_device_get_user_data (libinput_device);
+  evdev_device = META_INPUT_DEVICE_NATIVE (device);
+
+  stage = _clutter_input_device_get_stage (device);
+  if (!stage)
+    return;
+
+  axes = translate_tablet_axes (tablet_event,
+                                evdev_device->last_tool);
+  if (!axes)
+    return;
+
+  stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
+  stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
+
+  time = libinput_event_tablet_tool_get_time_usec (tablet_event);
+
+  if (clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE ||
+      clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_MOUSE ||
+      clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_LENS)
+    {
+      dx = libinput_event_tablet_tool_get_dx (tablet_event);
+      dy = libinput_event_tablet_tool_get_dy (tablet_event);
+      notify_relative_tool_motion (device, time, dx, dy, axes);
+    }
+  else
+    {
+      x = libinput_event_tablet_tool_get_x_transformed (tablet_event, stage_width);
+      y = libinput_event_tablet_tool_get_y_transformed (tablet_event, stage_height);
+      notify_absolute_motion (device, time, x, y, axes);
+    }
+}
+
+static gboolean
+process_device_event (MetaSeatNative        *seat,
+                      struct libinput_event *event)
+{
+  gboolean handled = TRUE;
+  struct libinput_device *libinput_device = libinput_event_get_device(event);
+  ClutterInputDevice *device;
+  MetaInputDeviceNative *device_evdev;
+
+  switch (libinput_event_get_type (event))
+    {
+    case LIBINPUT_EVENT_KEYBOARD_KEY:
+      {
+        uint32_t key, key_state, seat_key_count;
+        uint64_t time_us;
+        struct libinput_event_keyboard *key_event =
+          libinput_event_get_keyboard_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_keyboard_get_time_usec (key_event);
+        key = libinput_event_keyboard_get_key (key_event);
+        key_state = libinput_event_keyboard_get_key_state (key_event) ==
+                    LIBINPUT_KEY_STATE_PRESSED;
+        seat_key_count =
+          libinput_event_keyboard_get_seat_key_count (key_event);
+
+       /* Ignore key events that are not seat wide state changes. */
+       if ((key_state == LIBINPUT_KEY_STATE_PRESSED &&
+            seat_key_count != 1) ||
+           (key_state == LIBINPUT_KEY_STATE_RELEASED &&
+            seat_key_count != 0))
+          break;
+
+        meta_seat_native_notify_key (seat_from_device (device),
+                                     device,
+                                     time_us, key, key_state, TRUE);
+
+        break;
+      }
+
+    case LIBINPUT_EVENT_POINTER_MOTION:
+      {
+        struct libinput_event_pointer *pointer_event =
+          libinput_event_get_pointer_event (event);
+        uint64_t time_us;
+        double dx;
+        double dy;
+        double dx_unaccel;
+        double dy_unaccel;
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_pointer_get_time_usec (pointer_event);
+        dx = libinput_event_pointer_get_dx (pointer_event);
+        dy = libinput_event_pointer_get_dy (pointer_event);
+        dx_unaccel = libinput_event_pointer_get_dx_unaccelerated (pointer_event);
+        dy_unaccel = libinput_event_pointer_get_dy_unaccelerated (pointer_event);
+
+        meta_seat_native_notify_relative_motion (seat_from_device (device),
+                                                 device,
+                                                 time_us,
+                                                 dx, dy,
+                                                 dx_unaccel, dy_unaccel);
+
+        break;
+      }
+
+    case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
+      {
+        uint64_t time_us;
+        double x, y;
+        float stage_width, stage_height;
+        ClutterStage *stage;
+        struct libinput_event_pointer *motion_event =
+          libinput_event_get_pointer_event (event);
+        device = libinput_device_get_user_data (libinput_device);
+
+        stage = _clutter_input_device_get_stage (device);
+        if (stage == NULL)
+          break;
+
+        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
+        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
+
+        time_us = libinput_event_pointer_get_time_usec (motion_event);
+        x = libinput_event_pointer_get_absolute_x_transformed (motion_event,
+                                                               stage_width);
+        y = libinput_event_pointer_get_absolute_y_transformed (motion_event,
+                                                               stage_height);
+
+        meta_seat_native_notify_absolute_motion (seat_from_device (device),
+                                                 device,
+                                                 time_us,
+                                                 x, y,
+                                                 NULL);
+
+        break;
+      }
+
+    case LIBINPUT_EVENT_POINTER_BUTTON:
+      {
+        uint32_t button, button_state, seat_button_count;
+        uint64_t time_us;
+        struct libinput_event_pointer *button_event =
+          libinput_event_get_pointer_event (event);
+        device = libinput_device_get_user_data (libinput_device);
+
+        time_us = libinput_event_pointer_get_time_usec (button_event);
+        button = libinput_event_pointer_get_button (button_event);
+        button_state = libinput_event_pointer_get_button_state (button_event) ==
+                       LIBINPUT_BUTTON_STATE_PRESSED;
+        seat_button_count =
+          libinput_event_pointer_get_seat_button_count (button_event);
+
+        /* Ignore button events that are not seat wide state changes. */
+        if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED &&
+             seat_button_count != 1) ||
+            (button_state == LIBINPUT_BUTTON_STATE_RELEASED &&
+             seat_button_count != 0))
+          break;
+
+        meta_seat_native_notify_button (seat_from_device (device), device,
+                                        time_us, button, button_state);
+        break;
+      }
+
+    case LIBINPUT_EVENT_POINTER_AXIS:
+      {
+        uint64_t time_us;
+        enum libinput_pointer_axis_source source;
+        struct libinput_event_pointer *axis_event =
+          libinput_event_get_pointer_event (event);
+        MetaSeatNative *seat;
+        ClutterScrollSource scroll_source;
+
+        device = libinput_device_get_user_data (libinput_device);
+        seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (device));
+
+        time_us = libinput_event_pointer_get_time_usec (axis_event);
+        source = libinput_event_pointer_get_axis_source (axis_event);
+        scroll_source = translate_scroll_source (source);
+
+        /* libinput < 0.8 sent wheel click events with value 10. Since 0.8
+           the value is the angle of the click in degrees. To keep
+           backwards-compat with existing clients, we just send multiples of
+           the click count. */
+
+        switch (scroll_source)
+          {
+          case CLUTTER_SCROLL_SOURCE_WHEEL:
+            notify_discrete_axis (seat, device, time_us, scroll_source,
+                                  axis_event);
+            break;
+          case CLUTTER_SCROLL_SOURCE_FINGER:
+          case CLUTTER_SCROLL_SOURCE_CONTINUOUS:
+          case CLUTTER_SCROLL_SOURCE_UNKNOWN:
+            notify_continuous_axis (seat, device, time_us, scroll_source,
+                                    axis_event);
+            break;
+          }
+        break;
+      }
+
+    case LIBINPUT_EVENT_TOUCH_DOWN:
+      {
+        int device_slot;
+        uint64_t time_us;
+        double x, y;
+        float stage_width, stage_height;
+        MetaSeatNative *seat;
+        ClutterStage *stage;
+        MetaTouchState *touch_state;
+        struct libinput_event_touch *touch_event =
+          libinput_event_get_touch_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        device_evdev = META_INPUT_DEVICE_NATIVE (device);
+        seat = meta_input_device_native_get_seat (device_evdev);
+
+        stage = _clutter_input_device_get_stage (device);
+        if (stage == NULL)
+          break;
+
+        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
+        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
+
+        device_slot = libinput_event_touch_get_slot (touch_event);
+        time_us = libinput_event_touch_get_time_usec (touch_event);
+        x = libinput_event_touch_get_x_transformed (touch_event,
+                                                    stage_width);
+        y = libinput_event_touch_get_y_transformed (touch_event,
+                                                    stage_height);
+
+        touch_state =
+          meta_input_device_native_acquire_touch_state (device_evdev,
+                                                        device_slot);
+        touch_state->coords.x = x;
+        touch_state->coords.y = y;
+
+        meta_seat_native_notify_touch_event (seat, device,
+                                             CLUTTER_TOUCH_BEGIN,
+                                             time_us,
+                                             touch_state->seat_slot,
+                                             touch_state->coords.x,
+                                             touch_state->coords.y);
+        break;
+      }
+
+    case LIBINPUT_EVENT_TOUCH_UP:
+      {
+        int device_slot;
+        uint64_t time_us;
+        MetaSeatNative *seat;
+        MetaTouchState *touch_state;
+        struct libinput_event_touch *touch_event =
+          libinput_event_get_touch_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        device_evdev = META_INPUT_DEVICE_NATIVE (device);
+        seat = meta_input_device_native_get_seat (device_evdev);
+
+        device_slot = libinput_event_touch_get_slot (touch_event);
+        time_us = libinput_event_touch_get_time_usec (touch_event);
+        touch_state =
+          meta_input_device_native_lookup_touch_state (device_evdev,
+                                                       device_slot);
+        if (!touch_state)
+          break;
+
+        meta_seat_native_notify_touch_event (seat, device,
+                                             CLUTTER_TOUCH_END, time_us,
+                                             touch_state->seat_slot,
+                                             touch_state->coords.x,
+                                             touch_state->coords.y);
+        meta_input_device_native_release_touch_state (device_evdev,
+                                                      touch_state);
+        break;
+      }
+
+    case LIBINPUT_EVENT_TOUCH_MOTION:
+      {
+        int device_slot;
+        uint64_t time_us;
+        double x, y;
+        float stage_width, stage_height;
+        MetaSeatNative *seat;
+        ClutterStage *stage;
+        MetaTouchState *touch_state;
+        struct libinput_event_touch *touch_event =
+          libinput_event_get_touch_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        device_evdev = META_INPUT_DEVICE_NATIVE (device);
+        seat = meta_input_device_native_get_seat (device_evdev);
+
+        stage = _clutter_input_device_get_stage (device);
+        if (stage == NULL)
+          break;
+
+        stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage));
+        stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage));
+
+        device_slot = libinput_event_touch_get_slot (touch_event);
+        time_us = libinput_event_touch_get_time_usec (touch_event);
+        x = libinput_event_touch_get_x_transformed (touch_event,
+                                                    stage_width);
+        y = libinput_event_touch_get_y_transformed (touch_event,
+                                                    stage_height);
+
+        touch_state =
+          meta_input_device_native_lookup_touch_state (device_evdev,
+                                                       device_slot);
+        if (!touch_state)
+          break;
+
+        touch_state->coords.x = x;
+        touch_state->coords.y = y;
+
+        meta_seat_native_notify_touch_event (seat, device,
+                                             CLUTTER_TOUCH_UPDATE,
+                                             time_us,
+                                             touch_state->seat_slot,
+                                             touch_state->coords.x,
+                                             touch_state->coords.y);
+        break;
+      }
+    case LIBINPUT_EVENT_TOUCH_CANCEL:
+      {
+        uint64_t time_us;
+        struct libinput_event_touch *touch_event =
+          libinput_event_get_touch_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        device_evdev = META_INPUT_DEVICE_NATIVE (device);
+        time_us = libinput_event_touch_get_time_usec (touch_event);
+
+        meta_input_device_native_release_touch_slots (device_evdev, time_us);
+
+        break;
+      }
+    case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN:
+    case LIBINPUT_EVENT_GESTURE_PINCH_END:
+      {
+        struct libinput_event_gesture *gesture_event =
+          libinput_event_get_gesture_event (event);
+        ClutterTouchpadGesturePhase phase;
+        uint32_t n_fingers;
+        uint64_t time_us;
+
+        if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_PINCH_BEGIN)
+          phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN;
+        else
+          phase = libinput_event_gesture_get_cancelled (gesture_event) ?
+            CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END;
+
+        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_gesture_get_time_usec (gesture_event);
+        notify_pinch_gesture_event (device, phase, time_us, 0, 0, 0, 0, n_fingers);
+        break;
+      }
+    case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE:
+      {
+        struct libinput_event_gesture *gesture_event =
+          libinput_event_get_gesture_event (event);
+        gdouble angle_delta, scale, dx, dy;
+        uint32_t n_fingers;
+        uint64_t time_us;
+
+        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_gesture_get_time_usec (gesture_event);
+        angle_delta = libinput_event_gesture_get_angle_delta (gesture_event);
+        scale = libinput_event_gesture_get_scale (gesture_event);
+        dx = libinput_event_gesture_get_dx (gesture_event);
+        dy = libinput_event_gesture_get_dy (gesture_event);
+
+        notify_pinch_gesture_event (device,
+                                    CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
+                                    time_us, dx, dy, angle_delta, scale, n_fingers);
+        break;
+      }
+    case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN:
+    case LIBINPUT_EVENT_GESTURE_SWIPE_END:
+      {
+        struct libinput_event_gesture *gesture_event =
+          libinput_event_get_gesture_event (event);
+        ClutterTouchpadGesturePhase phase;
+        uint32_t n_fingers;
+        uint64_t time_us;
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_gesture_get_time_usec (gesture_event);
+        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
+
+        if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN)
+          phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN;
+        else
+          phase = libinput_event_gesture_get_cancelled (gesture_event) ?
+            CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END;
+
+        notify_swipe_gesture_event (device, phase, time_us, n_fingers, 0, 0);
+        break;
+      }
+    case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE:
+      {
+        struct libinput_event_gesture *gesture_event =
+          libinput_event_get_gesture_event (event);
+        uint32_t n_fingers;
+        uint64_t time_us;
+        double dx, dy;
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_gesture_get_time_usec (gesture_event);
+        n_fingers = libinput_event_gesture_get_finger_count (gesture_event);
+        dx = libinput_event_gesture_get_dx (gesture_event);
+        dy = libinput_event_gesture_get_dy (gesture_event);
+
+        notify_swipe_gesture_event (device,
+                                    CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE,
+                                    time_us, n_fingers, dx, dy);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
+      {
+        process_tablet_axis (seat, event);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
+      {
+        uint64_t time;
+        struct libinput_event_tablet_tool *tablet_event =
+          libinput_event_get_tablet_tool_event (event);
+        struct libinput_tablet_tool *libinput_tool = NULL;
+        enum libinput_tablet_tool_proximity_state state;
+
+        state = libinput_event_tablet_tool_get_proximity_state (tablet_event);
+        time = libinput_event_tablet_tool_get_time_usec (tablet_event);
+        device = libinput_device_get_user_data (libinput_device);
+
+        libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event);
+
+        if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN)
+          input_device_update_tool (device, libinput_tool);
+        notify_proximity (device, time, state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN);
+        if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT)
+          input_device_update_tool (device, NULL);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_TOOL_BUTTON:
+      {
+        uint64_t time_us;
+        uint32_t button_state;
+        struct libinput_event_tablet_tool *tablet_event =
+          libinput_event_get_tablet_tool_event (event);
+        uint32_t tablet_button;
+
+        process_tablet_axis (seat, event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_tablet_tool_get_time_usec (tablet_event);
+        tablet_button = libinput_event_tablet_tool_get_button (tablet_event);
+
+        button_state = libinput_event_tablet_tool_get_button_state (tablet_event) ==
+                       LIBINPUT_BUTTON_STATE_PRESSED;
+
+        meta_seat_native_notify_button (seat_from_device (device), device,
+                                        time_us, tablet_button, button_state);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_TOOL_TIP:
+      {
+        uint64_t time_us;
+        uint32_t button_state;
+        struct libinput_event_tablet_tool *tablet_event =
+          libinput_event_get_tablet_tool_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        time_us = libinput_event_tablet_tool_get_time_usec (tablet_event);
+
+        button_state = libinput_event_tablet_tool_get_tip_state (tablet_event) ==
+                       LIBINPUT_TABLET_TOOL_TIP_DOWN;
+
+        /* To avoid jumps on tip, notify axes before the tip down event
+           but after the tip up event */
+        if (button_state)
+          process_tablet_axis (seat, event);
+
+        meta_seat_native_notify_button (seat_from_device (device), device,
+                                        time_us, BTN_TOUCH, button_state);
+        if (!button_state)
+          process_tablet_axis (seat, event);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_PAD_BUTTON:
+      {
+        uint64_t time;
+        uint32_t button_state, button, group, mode;
+        struct libinput_tablet_pad_mode_group *mode_group;
+        struct libinput_event_tablet_pad *pad_event =
+          libinput_event_get_tablet_pad_event (event);
+
+        device = libinput_device_get_user_data (libinput_device);
+        time = libinput_event_tablet_pad_get_time_usec (pad_event);
+
+        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
+        group = libinput_tablet_pad_mode_group_get_index (mode_group);
+        mode = libinput_event_tablet_pad_get_mode (pad_event);
+
+        button = libinput_event_tablet_pad_get_button_number (pad_event);
+        button_state = libinput_event_tablet_pad_get_button_state (pad_event) ==
+                       LIBINPUT_BUTTON_STATE_PRESSED;
+        notify_pad_button (device, time, button, group, mode, button_state);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_PAD_STRIP:
+      {
+        uint64_t time;
+        uint32_t number, source, group, mode;
+        struct libinput_tablet_pad_mode_group *mode_group;
+        struct libinput_event_tablet_pad *pad_event =
+          libinput_event_get_tablet_pad_event (event);
+        double value;
+
+        device = libinput_device_get_user_data (libinput_device);
+        time = libinput_event_tablet_pad_get_time_usec (pad_event);
+        number = libinput_event_tablet_pad_get_strip_number (pad_event);
+        value = libinput_event_tablet_pad_get_strip_position (pad_event);
+        source = libinput_event_tablet_pad_get_strip_source (pad_event);
+
+        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
+        group = libinput_tablet_pad_mode_group_get_index (mode_group);
+        mode = libinput_event_tablet_pad_get_mode (pad_event);
+
+        notify_pad_strip (device, time, number, source, group, mode, value);
+        break;
+      }
+    case LIBINPUT_EVENT_TABLET_PAD_RING:
+      {
+        uint64_t time;
+        uint32_t number, source, group, mode;
+        struct libinput_tablet_pad_mode_group *mode_group;
+        struct libinput_event_tablet_pad *pad_event =
+          libinput_event_get_tablet_pad_event (event);
+        double angle;
+
+        device = libinput_device_get_user_data (libinput_device);
+        time = libinput_event_tablet_pad_get_time_usec (pad_event);
+        number = libinput_event_tablet_pad_get_ring_number (pad_event);
+        angle = libinput_event_tablet_pad_get_ring_position (pad_event);
+        source = libinput_event_tablet_pad_get_ring_source (pad_event);
+
+        mode_group = libinput_event_tablet_pad_get_mode_group (pad_event);
+        group = libinput_tablet_pad_mode_group_get_index (mode_group);
+        mode = libinput_event_tablet_pad_get_mode (pad_event);
+
+        notify_pad_ring (device, time, number, source, group, mode, angle);
+        break;
+      }
+    default:
+      handled = FALSE;
+    }
+
+  return handled;
+}
+
+static gboolean
+filter_event (MetaSeatNative        *seat,
+              struct libinput_event *event)
+{
+  gboolean retval = CLUTTER_EVENT_PROPAGATE;
+  MetaEventFilter *filter;
+  GSList *tmp_list;
+
+  tmp_list = seat->event_filters;
+
+  while (tmp_list)
+    {
+      filter = tmp_list->data;
+      retval = filter->func (event, filter->data);
+      tmp_list = tmp_list->next;
+
+      if (retval != CLUTTER_EVENT_PROPAGATE)
+        break;
+    }
+
+  return retval;
+}
+
+static void
+process_event (MetaSeatNative        *seat,
+               struct libinput_event *event)
+{
+  gboolean retval;
+
+  retval = filter_event (seat, event);
+
+  if (retval != CLUTTER_EVENT_PROPAGATE)
+    return;
+
+  if (process_base_event (seat, event))
+    return;
+  if (process_device_event (seat, event))
+    return;
+}
+
+static void
+process_events (MetaSeatNative *seat)
+{
+  struct libinput_event *event;
+
+  while ((event = libinput_get_event (seat->libinput)))
+    {
+      process_event(seat, event);
+      libinput_event_destroy(event);
+    }
+}
+
+static int
+open_restricted (const char *path,
+                 int         flags,
+                 void       *user_data)
+{
+  gint fd;
+
+  if (device_open_callback)
+    {
+      GError *error = NULL;
+
+      fd = device_open_callback (path, flags, device_callback_data, &error);
+
+      if (fd < 0)
+        {
+          g_warning ("Could not open device %s: %s", path, error->message);
+          g_error_free (error);
+        }
+    }
+  else
+    {
+      fd = open (path, O_RDWR | O_NONBLOCK);
+      if (fd < 0)
+        {
+          g_warning ("Could not open device %s: %s", path, strerror (errno));
+        }
+    }
+
+  return fd;
+}
+
+static void
+close_restricted (int   fd,
+                  void *user_data)
+{
+  if (device_close_callback)
+    device_close_callback (fd, device_callback_data);
+  else
+    close (fd);
+}
+
+static const struct libinput_interface libinput_interface = {
+  open_restricted,
+  close_restricted
+};
+
+static void
+meta_seat_native_constructed (GObject *object)
+{
+  MetaSeatNative *seat = META_SEAT_NATIVE (object);
+  ClutterInputDevice *device;
+  ClutterStage *stage;
+  MetaEventSource *source;
+  struct udev *udev;
+
+  device = meta_input_device_native_new_virtual (
+      seat, CLUTTER_POINTER_DEVICE,
+      CLUTTER_INPUT_MODE_MASTER);
+  stage = meta_seat_native_get_stage (seat);
+  _clutter_input_device_set_stage (device, stage);
+  seat->pointer_x = INITIAL_POINTER_X;
+  seat->pointer_y = INITIAL_POINTER_Y;
+  _clutter_input_device_set_coords (device, NULL,
+                                    seat->pointer_x, seat->pointer_y,
+                                    NULL);
+  seat->core_pointer = device;
+
+  device = meta_input_device_native_new_virtual (
+      seat, CLUTTER_KEYBOARD_DEVICE,
+      CLUTTER_INPUT_MODE_MASTER);
+  _clutter_input_device_set_stage (device, stage);
+  seat->core_keyboard = device;
+
+  udev = udev_new ();
+  if (G_UNLIKELY (udev == NULL))
+    {
+      g_warning ("Failed to create udev object");
+      return;
+    }
+
+  seat->libinput = libinput_udev_create_context (&libinput_interface,
+                                                 seat, udev);
+  if (seat->libinput == NULL)
+    {
+      g_critical ("Failed to create the libinput object.");
+      return;
+    }
+
+  if (libinput_udev_assign_seat (seat->libinput, seat->seat_id) == -1)
+    {
+      g_critical ("Failed to assign a seat to the libinput object.");
+      libinput_unref (seat->libinput);
+      seat->libinput = NULL;
+      return;
+    }
+
+  udev_unref (udev);
+
+  dispatch_libinput (seat);
+
+  source = meta_event_source_new (seat);
+  seat->event_source = source;
+
+  if (G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed)
+    G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed (object);
+}
+
+static void
+meta_seat_native_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  MetaSeatNative *seat_native = META_SEAT_NATIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_SEAT_ID:
+      seat_native->seat_id = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_seat_native_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  MetaSeatNative *seat_native = META_SEAT_NATIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_SEAT_ID:
+      g_value_set_string (value, seat_native->seat_id);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+meta_seat_native_dispose (GObject *object)
+{
+  MetaSeatNative *seat = META_SEAT_NATIVE (object);
+
+  g_clear_signal_handler (&seat->stage_added_handler, seat->stage_manager);
+  g_clear_signal_handler (&seat->stage_removed_handler, seat->stage_manager);
+
+  if (seat->stage_manager)
+    {
+      g_object_unref (seat->stage_manager);
+      seat->stage_manager = NULL;
+    }
+
+  if (seat->libinput)
+    {
+      libinput_unref (seat->libinput);
+      seat->libinput = NULL;
+    }
+
+  G_OBJECT_CLASS (meta_seat_native_parent_class)->dispose (object);
+}
+
+static void
+meta_seat_native_finalize (GObject *object)
+{
+  MetaSeatNative *seat = META_SEAT_NATIVE (object);
+  GSList *iter;
+
+  for (iter = seat->devices; iter; iter = g_slist_next (iter))
+    {
+      ClutterInputDevice *device = iter->data;
+
+      g_object_unref (device);
+    }
+  g_slist_free (seat->devices);
+  g_free (seat->touch_states);
+
+  meta_event_source_free (seat->event_source);
+
+  xkb_state_unref (seat->xkb);
+
+  meta_seat_native_clear_repeat_timer (seat);
+
+  if (seat->libinput_seat)
+    libinput_seat_unref (seat->libinput_seat);
+
+  g_list_free (seat->free_device_ids);
+
+  if (seat->constrain_data_notify != NULL)
+    seat->constrain_data_notify (seat->constrain_data);
+
+  g_free (seat->seat_id);
+
+  G_OBJECT_CLASS (meta_seat_native_parent_class)->finalize (object);
+}
+
+static ClutterInputDevice *
+meta_seat_native_get_pointer (ClutterSeat *seat)
+{
+  MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
+
+  return seat_native->core_pointer;
+}
+
+static ClutterInputDevice *
+meta_seat_native_get_keyboard (ClutterSeat *seat)
+{
+  MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
+
+  return seat_native->core_keyboard;
+}
+
+static GList *
+meta_seat_native_list_devices (ClutterSeat *seat)
+{
+  MetaSeatNative *seat_native = META_SEAT_NATIVE (seat);
+  GList *devices = NULL;
+  GSList *l;
+
+  for (l = seat_native->devices; l; l = l->next)
+    devices = g_list_prepend (devices, l->data);
+
+  return devices;
+}
+
+static void
+meta_seat_native_class_init (MetaSeatNativeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass);
+
+  object_class->constructed = meta_seat_native_constructed;
+  object_class->set_property = meta_seat_native_set_property;
+  object_class->get_property = meta_seat_native_get_property;
+  object_class->dispose = meta_seat_native_dispose;
+  object_class->finalize = meta_seat_native_finalize;
+
+  seat_class->get_pointer = meta_seat_native_get_pointer;
+  seat_class->get_keyboard = meta_seat_native_get_keyboard;
+  seat_class->list_devices = meta_seat_native_list_devices;
+
+  props[PROP_SEAT_ID] =
+    g_param_spec_string ("seat-id",
+                         "Seat ID",
+                         "Seat ID",
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+meta_seat_native_stage_added_cb (ClutterStageManager *manager,
+                                 ClutterStage        *stage,
+                                 MetaSeatNative      *seat)
+{
+  /* NB: Currently we can only associate a single stage with all evdev
+   * devices.
+   *
+   * We save a pointer to the stage so if we release/reclaim input
+   * devices due to switching virtual terminals then we know what
+   * stage to re associate the devices with.
+   */
+  meta_seat_native_set_stage (seat, stage);
+
+  /* We only want to do this once so we can catch the default
+     stage. If the application has multiple stages then it will need
+     to manage the stage of the input devices itself */
+  g_clear_signal_handler (&seat->stage_added_handler, seat->stage_manager);
+}
+
+static void
+meta_seat_native_stage_removed_cb (ClutterStageManager *manager,
+                                   ClutterStage        *stage,
+                                   MetaSeatNative      *seat)
+{
+  meta_seat_native_set_stage (seat, NULL);
+}
+
+static void
+meta_seat_native_init (MetaSeatNative *seat)
+{
+  ClutterKeymap *keymap;
+  struct xkb_keymap *xkb_keymap;
+
+  seat->stage_manager = clutter_stage_manager_get_default ();
+  g_object_ref (seat->stage_manager);
+
+  /* evdev doesn't have any way to link an event to a particular stage
+     so we'll have to leave it up to applications to set the
+     corresponding stage for an input device. However to make it
+     easier for applications that are only using one fullscreen stage
+     (which is probably the most frequent use-case for the evdev
+     backend) we'll associate any input devices that don't have a
+     stage with the first stage created. */
+  seat->stage_added_handler =
+    g_signal_connect (seat->stage_manager,
+                      "stage-added",
+                      G_CALLBACK (meta_seat_native_stage_added_cb),
+                      seat);
+  seat->stage_removed_handler =
+    g_signal_connect (seat->stage_manager,
+                      "stage-removed",
+                      G_CALLBACK (meta_seat_native_stage_removed_cb),
+                      seat);
+
+  seat->device_id_next = INITIAL_DEVICE_ID;
+
+  seat->repeat = TRUE;
+  seat->repeat_delay = 250;     /* ms */
+  seat->repeat_interval = 33;   /* ms */
+
+  keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
+  xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap));
+
+  if (xkb_keymap)
+    {
+      seat->xkb = xkb_state_new (xkb_keymap);
+
+      seat->caps_lock_led =
+        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
+      seat->num_lock_led =
+        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
+      seat->scroll_lock_led =
+        xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
+    }
+}
+
+ClutterInputDevice *
+meta_seat_native_get_device (MetaSeatNative *seat,
+                             int             id)
+{
+  ClutterInputDevice *device;
+  GSList *l;
+
+  for (l = seat->devices; l; l = l->next)
+    {
+      device = l->data;
+
+      if (clutter_input_device_get_device_id (device) == id)
+        return device;
+    }
+
+  return NULL;
+}
+
+void
+meta_seat_native_set_stage (MetaSeatNative *seat,
+                            ClutterStage   *stage)
+{
+  GSList *l;
+
+  if (seat->stage == stage)
+    return;
+
+  seat->stage = stage;
+  _clutter_input_device_set_stage (seat->core_pointer, stage);
+  _clutter_input_device_set_stage (seat->core_keyboard, stage);
+
+  for (l = seat->devices; l; l = l->next)
+    {
+      ClutterInputDevice *device = l->data;
+
+      _clutter_input_device_set_stage (device, stage);
+    }
+}
+
+ClutterStage *
+meta_seat_native_get_stage (MetaSeatNative *seat)
+{
+  return seat->stage;
+}
+
+/**
+ * meta_seat_native_set_device_callbacks: (skip)
+ * @open_callback: the user replacement for open()
+ * @close_callback: the user replacement for close()
+ * @user_data: user data for @callback
+ *
+ * Through this function, the application can set a custom callback
+ * to be invoked when Clutter is about to open an evdev device. It can do
+ * so if special handling is needed, for example to circumvent permission
+ * problems.
+ *
+ * Setting @callback to %NULL will reset the default behavior.
+ *
+ * For reliable effects, this function must be called before clutter_init().
+ */
+void
+meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback  open_callback,
+                                       MetaCloseDeviceCallback close_callback,
+                                       gpointer                user_data)
+{
+  device_open_callback = open_callback;
+  device_close_callback = close_callback;
+  device_callback_data = user_data;
+}
+
+/**
+ * meta_seat_native_set_pointer_constrain_callback:
+ * @seat: the #ClutterSeat created by the evdev backend
+ * @callback: the callback
+ * @user_data: data to pass to the callback
+ * @user_data_notify: function to be called when removing the callback
+ *
+ * Sets a callback to be invoked for every pointer motion. The callback
+ * can then modify the new pointer coordinates to constrain movement within
+ * a specific region.
+ */
+void
+meta_seat_native_set_pointer_constrain_callback (MetaSeatNative               *seat,
+                                                 MetaPointerConstrainCallback  callback,
+                                                 gpointer                      user_data,
+                                                 GDestroyNotify                user_data_notify)
+{
+  g_return_if_fail (META_IS_SEAT_NATIVE (seat));
+
+  if (seat->constrain_data_notify)
+    seat->constrain_data_notify (seat->constrain_data);
+
+  seat->constrain_callback = callback;
+  seat->constrain_data = user_data;
+  seat->constrain_data_notify = user_data_notify;
+}
+
+void
+meta_seat_native_set_relative_motion_filter (MetaSeatNative           *seat,
+                                             MetaRelativeMotionFilter  filter,
+                                             gpointer                  user_data)
+{
+  g_return_if_fail (META_IS_SEAT_NATIVE (seat));
+
+  seat->relative_motion_filter = filter;
+  seat->relative_motion_filter_user_data = user_data;
+}
+
+/**
+ * meta_seat_native_add_filter: (skip)
+ * @func: (closure data): a filter function
+ * @data: (allow-none): user data to be passed to the filter function, or %NULL
+ * @destroy_notify: (allow-none): function to call on @data when the filter is removed, or %NULL
+ *
+ * Adds an event filter function.
+ */
+void
+meta_seat_native_add_filter (MetaSeatNative      *seat,
+                             MetaEvdevFilterFunc  func,
+                             gpointer             data,
+                             GDestroyNotify       destroy_notify)
+{
+  MetaEventFilter *filter;
+
+  g_return_if_fail (func != NULL);
+
+  filter = g_new0 (MetaEventFilter, 1);
+  filter->func = func;
+  filter->data = data;
+  filter->destroy_notify = destroy_notify;
+
+  seat->event_filters = g_slist_append (seat->event_filters, filter);
+}
+
+/**
+ * meta_seat_native_remove_filter: (skip)
+ * @func: a filter function
+ * @data: (allow-none): user data to be passed to the filter function, or %NULL
+ *
+ * Removes the given filter function.
+ */
+void
+meta_seat_native_remove_filter (MetaSeatNative      *seat,
+                                MetaEvdevFilterFunc  func,
+                                gpointer             data)
+{
+  MetaEventFilter *filter;
+  GSList *tmp_list;
+
+  g_return_if_fail (func != NULL);
+
+  tmp_list = seat->event_filters;
+
+  while (tmp_list)
+    {
+      filter = tmp_list->data;
+
+      if (filter->func == func && filter->data == data)
+        {
+          if (filter->destroy_notify)
+            filter->destroy_notify (filter->data);
+          g_free (filter);
+          seat->event_filters =
+            g_slist_delete_link (seat->event_filters, tmp_list);
+          return;
+        }
+
+      tmp_list = tmp_list->next;
+    }
+}
+
+void
+meta_seat_native_update_xkb_state (MetaSeatNative *seat)
+{
+  xkb_mod_mask_t latched_mods;
+  xkb_mod_mask_t locked_mods;
+  struct xkb_keymap *xkb_keymap;
+  ClutterKeymap *keymap;
+
+  keymap = clutter_backend_get_keymap (clutter_get_default_backend ());
+  xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap));
+
+  latched_mods = xkb_state_serialize_mods (seat->xkb,
+                                           XKB_STATE_MODS_LATCHED);
+  locked_mods = xkb_state_serialize_mods (seat->xkb,
+                                          XKB_STATE_MODS_LOCKED);
+  xkb_state_unref (seat->xkb);
+  seat->xkb = xkb_state_new (xkb_keymap);
+
+  xkb_state_update_mask (seat->xkb,
+                         0, /* depressed */
+                         latched_mods,
+                         locked_mods,
+                         0, 0, seat->layout_idx);
+
+  seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS);
+  seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM);
+  seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL);
+
+  meta_seat_native_sync_leds (seat);
+}
+
+/**
+ * meta_seat_native_warp_pointer:
+ * @pointer_device: the pointer device to warp
+ * @time: the timestamp for the warp event
+ * @x: the new X position of the pointer
+ * @y: the new Y position of the pointer
+ *
+ * Warps the pointer to a new location. Technically, this is
+ * processed the same way as an absolute motion event from
+ * libinput: it simply generates an absolute motion event that
+ * will be processed on the next iteration of the mainloop.
+ *
+ * The intended use for this is for display servers that need
+ * to warp cursor the cursor to a new location.
+ */
+void
+meta_seat_native_warp_pointer (ClutterInputDevice   *pointer_device,
+                               uint32_t              time_,
+                               int                   x,
+                               int                   y)
+{
+  notify_absolute_motion (pointer_device, ms2us(time_), x, y, NULL);
+}
+
+gint
+meta_seat_native_acquire_device_id (MetaSeatNative *seat)
+{
+  GList *first;
+  gint next_id;
+
+  if (seat->free_device_ids == NULL)
+    {
+      gint i;
+
+      /* We ran out of free ID's, so append 10 new ones. */
+      for (i = 0; i < 10; i++)
+        seat->free_device_ids =
+          g_list_append (seat->free_device_ids,
+                         GINT_TO_POINTER (seat->device_id_next++));
+    }
+
+  first = g_list_first (seat->free_device_ids);
+  next_id = GPOINTER_TO_INT (first->data);
+  seat->free_device_ids = g_list_delete_link (seat->free_device_ids, first);
+
+  return next_id;
+}
+
+static int
+compare_ids (gconstpointer a,
+             gconstpointer b)
+{
+  return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
+}
+
+void
+meta_seat_native_release_device_id (MetaSeatNative     *seat,
+                                    ClutterInputDevice *device)
+{
+  gint device_id;
+
+  device_id = clutter_input_device_get_device_id (device);
+  seat->free_device_ids = g_list_insert_sorted (seat->free_device_ids,
+                                                GINT_TO_POINTER (device_id),
+                                                compare_ids);
+}
+
+/**
+ * meta_seat_native_release_devices:
+ *
+ * Releases all the evdev devices that Clutter is currently managing. This api
+ * is typically used when switching away from the Clutter application when
+ * switching tty. The devices can be reclaimed later with a call to
+ * meta_seat_native_reclaim_devices().
+ *
+ * This function should only be called after clutter has been initialized.
+ */
+void
+meta_seat_native_release_devices (MetaSeatNative *seat)
+{
+  g_return_if_fail (META_IS_SEAT_NATIVE (seat));
+
+  if (seat->released)
+    {
+      g_warning ("meta_seat_native_release_devices() shouldn't be called "
+                 "multiple times without a corresponding call to "
+                 "meta_seat_native_reclaim_devices() first");
+      return;
+    }
+
+  libinput_suspend (seat->libinput);
+  process_events (seat);
+
+  seat->released = TRUE;
+}
+
+/**
+ * meta_seat_native_reclaim_devices:
+ *
+ * This causes Clutter to re-probe for evdev devices. This is must only be
+ * called after a corresponding call to meta_seat_native_release_devices()
+ * was previously used to release all evdev devices. This API is typically
+ * used when a clutter application using evdev has regained focus due to
+ * switching ttys.
+ *
+ * This function should only be called after clutter has been initialized.
+ */
+void
+meta_seat_native_reclaim_devices (MetaSeatNative *seat)
+{
+  if (!seat->released)
+    {
+      g_warning ("Spurious call to meta_seat_native_reclaim_devices() without "
+                 "previous call to meta_seat_native_release_devices");
+      return;
+    }
+
+  libinput_resume (seat->libinput);
+  meta_seat_native_update_xkb_state (seat);
+  process_events (seat);
+
+  seat->released = FALSE;
 }
diff --git a/src/backends/native/meta-seat-native.h b/src/backends/native/meta-seat-native.h
index 9dd2fbdad..c15cab8d9 100644
--- a/src/backends/native/meta-seat-native.h
+++ b/src/backends/native/meta-seat-native.h
@@ -26,12 +26,42 @@
 #include <libinput.h>
 #include <linux/input-event-codes.h>
 
-#include "backends/native/meta-device-manager-native.h"
 #include "backends/native/meta-xkb-utils.h"
 #include "clutter/clutter.h"
 
 typedef struct _MetaTouchState MetaTouchState;
 typedef struct _MetaSeatNative MetaSeatNative;
+typedef struct _MetaEventSource  MetaEventSource;
+
+/**
+ * MetaPointerConstrainCallback:
+ * @device: the core pointer device
+ * @time: the event time in milliseconds
+ * @x: (inout): the new X coordinate
+ * @y: (inout): the new Y coordinate
+ * @user_data: user data passed to this function
+ *
+ * This callback will be called for all pointer motion events, and should
+ * update (@x, @y) to constrain the pointer position appropriately.
+ * The subsequent motion event will use the updated values as the new coordinates.
+ * Note that the coordinates are not clamped to the stage size, and the callback
+ * must make sure that this happens before it returns.
+ * Also note that the event will be emitted even if the pointer is constrained
+ * to be in the same position.
+ */
+typedef void (* MetaPointerConstrainCallback) (ClutterInputDevice *device,
+                                               uint32_t            time,
+                                               float               prev_x,
+                                               float               prev_y,
+                                               float              *x,
+                                               float              *y,
+                                               gpointer            user_data);
+typedef void (* MetaRelativeMotionFilter) (ClutterInputDevice *device,
+                                           float               x,
+                                           float               y,
+                                           float              *dx,
+                                           float              *dy,
+                                           gpointer            user_data);
 
 struct _MetaTouchState
 {
@@ -44,8 +74,12 @@ struct _MetaTouchState
 
 struct _MetaSeatNative
 {
+  ClutterSeat parent_instance;
+
+  char *seat_id;
+  MetaEventSource *event_source;
+  struct libinput *libinput;
   struct libinput_seat *libinput_seat;
-  MetaDeviceManagerNative *manager_evdev;
 
   GSList *devices;
 
@@ -63,6 +97,23 @@ struct _MetaSeatNative
   uint32_t button_state;
   int button_count[KEY_CNT];
 
+  ClutterStage *stage;
+  ClutterStageManager *stage_manager;
+  gulong stage_added_handler;
+  gulong stage_removed_handler;
+
+  int device_id_next;
+  GList *free_device_ids;
+
+  MetaPointerConstrainCallback constrain_callback;
+  gpointer constrain_data;
+  GDestroyNotify constrain_data_notify;
+
+  MetaRelativeMotionFilter relative_motion_filter;
+  gpointer relative_motion_filter_user_data;
+
+  GSList *event_filters;
+
   /* keyboard repeat */
   gboolean repeat;
   uint32_t repeat_delay;
@@ -78,8 +129,32 @@ struct _MetaSeatNative
   /* Emulation of discrete scroll events out of smooth ones */
   float accum_scroll_dx;
   float accum_scroll_dy;
+
+  gboolean released;
 };
 
+#define META_TYPE_SEAT_NATIVE meta_seat_native_get_type ()
+G_DECLARE_FINAL_TYPE (MetaSeatNative, meta_seat_native,
+                      META, SEAT_NATIVE, ClutterSeat)
+
+static inline uint64_t
+us (uint64_t us)
+{
+  return us;
+}
+
+static inline uint64_t
+ms2us (uint64_t ms)
+{
+  return us (ms * 1000);
+}
+
+static inline uint32_t
+us2ms (uint64_t us)
+{
+  return (uint32_t) (us / 1000);
+}
+
 void meta_seat_native_notify_key (MetaSeatNative     *seat,
                                   ClutterInputDevice *device,
                                   uint64_t            time_us,
@@ -145,16 +220,81 @@ MetaTouchState * meta_seat_native_acquire_touch_state (MetaSeatNative *seat,
 void meta_seat_native_release_touch_state (MetaSeatNative *seat,
                                            MetaTouchState *touch_state);
 
-MetaTouchState * meta_seat_native_get_touch (MetaSeatNative *seat,
-                                             uint32_t        id);
-
 void meta_seat_native_set_stage (MetaSeatNative *seat,
                                  ClutterStage   *stage);
+ClutterStage * meta_seat_native_get_stage (MetaSeatNative *seat);
 
 void meta_seat_native_clear_repeat_timer (MetaSeatNative *seat);
 
-MetaSeatNative * meta_seat_native_new (MetaDeviceManagerNative *manager_evdev);
+gint meta_seat_native_acquire_device_id (MetaSeatNative     *seat);
+void meta_seat_native_release_device_id (MetaSeatNative     *seat,
+                                         ClutterInputDevice *device);
+
+void meta_seat_native_update_xkb_state (MetaSeatNative *seat);
+
+void meta_seat_native_constrain_pointer (MetaSeatNative     *seat,
+                                         ClutterInputDevice *core_pointer,
+                                         uint64_t            time_us,
+                                         float               x,
+                                         float               y,
+                                         float              *new_x,
+                                         float              *new_y);
 
-void meta_seat_native_free (MetaSeatNative *seat);
+void meta_seat_native_filter_relative_motion (MetaSeatNative     *seat,
+                                              ClutterInputDevice *device,
+                                              float               x,
+                                              float               y,
+                                              float              *dx,
+                                              float              *dy);
+
+void meta_seat_native_dispatch (MetaSeatNative *seat);
+
+/**
+ * MetaOpenDeviceCallback:
+ * @path: the device path
+ * @flags: flags to be passed to open
+ *
+ * This callback will be called when Clutter needs to access an input
+ * device. It should return an open file descriptor for the file at @path,
+ * or -1 if opening failed.
+ */
+typedef int (* MetaOpenDeviceCallback) (const char  *path,
+                                        int          flags,
+                                        gpointer     user_data,
+                                        GError     **error);
+typedef void (* MetaCloseDeviceCallback) (int          fd,
+                                          gpointer     user_data);
+
+void  meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback  open_callback,
+                                             MetaCloseDeviceCallback close_callback,
+                                             gpointer                user_data);
+
+void  meta_seat_native_release_devices (MetaSeatNative *seat);
+void  meta_seat_native_reclaim_devices (MetaSeatNative *seat);
+
+void  meta_seat_native_set_pointer_constrain_callback (MetaSeatNative               *seat,
+                                                       MetaPointerConstrainCallback  callback,
+                                                       gpointer                      user_data,
+                                                       GDestroyNotify                user_data_notify);
+
+void meta_seat_native_set_relative_motion_filter (MetaSeatNative           *seat,
+                                                  MetaRelativeMotionFilter  filter,
+                                                  gpointer                  user_data);
+
+typedef gboolean (* MetaEvdevFilterFunc) (struct libinput_event *event,
+                                          gpointer               data);
+
+void meta_seat_native_add_filter    (MetaSeatNative        *seat,
+                                     MetaEvdevFilterFunc    func,
+                                     gpointer               data,
+                                     GDestroyNotify         destroy_notify);
+void meta_seat_native_remove_filter (MetaSeatNative        *seat,
+                                     MetaEvdevFilterFunc    func,
+                                     gpointer               data);
+
+void meta_seat_native_warp_pointer (ClutterInputDevice   *pointer_device,
+                                    uint32_t              time_,
+                                    int                   x,
+                                    int                   y);
 
 #endif /* META_SEAT_NATIVE_H */
diff --git a/src/backends/native/meta-virtual-input-device-native.c 
b/src/backends/native/meta-virtual-input-device-native.c
index c45d75d79..48856677c 100644
--- a/src/backends/native/meta-virtual-input-device-native.c
+++ b/src/backends/native/meta-virtual-input-device-native.c
@@ -630,20 +630,17 @@ meta_virtual_input_device_native_constructed (GObject *object)
     CLUTTER_VIRTUAL_INPUT_DEVICE (object);
   MetaVirtualInputDeviceNative *virtual_evdev =
     META_VIRTUAL_INPUT_DEVICE_NATIVE (object);
-  ClutterDeviceManager *manager;
   ClutterInputDeviceType device_type;
   ClutterStage *stage;
 
-  manager = clutter_virtual_input_device_get_manager (virtual_device);
   device_type = clutter_virtual_input_device_get_device_type (virtual_device);
 
   virtual_evdev->device =
-    meta_input_device_native_new_virtual (manager,
-                                          virtual_evdev->seat,
+    meta_input_device_native_new_virtual (virtual_evdev->seat,
                                           device_type,
                                           CLUTTER_INPUT_MODE_SLAVE);
 
-  stage = meta_device_manager_native_get_stage (META_DEVICE_MANAGER_NATIVE (manager));
+  stage = meta_seat_native_get_stage (virtual_evdev->seat);
   _clutter_input_device_set_stage (virtual_evdev->device, stage);
 }
 
diff --git a/src/wayland/meta-wayland-touch.c b/src/wayland/meta-wayland-touch.c
index 80c3b57e0..9f7f9f7e0 100644
--- a/src/wayland/meta-wayland-touch.c
+++ b/src/wayland/meta-wayland-touch.c
@@ -34,6 +34,7 @@
 #include "backends/native/meta-backend-native.h"
 #include "backends/native/meta-device-manager-native.h"
 #include "backends/native/meta-event-native.h"
+#include "backends/native/meta-seat-native.h"
 #endif
 
 G_DEFINE_TYPE (MetaWaylandTouch, meta_wayland_touch,
@@ -570,7 +571,13 @@ meta_wayland_touch_enable (MetaWaylandTouch *touch)
 #ifdef HAVE_NATIVE_BACKEND
   MetaBackend *backend = meta_get_backend ();
   if (META_IS_BACKEND_NATIVE (backend))
-    meta_device_manager_native_add_filter (evdev_filter_func, touch, NULL);
+    {
+      ClutterBackend *backend = clutter_get_default_backend ();
+      ClutterSeat *seat = clutter_backend_get_default_seat (backend);
+
+      meta_seat_native_add_filter (META_SEAT_NATIVE (seat),
+                                   evdev_filter_func, touch, NULL);
+    }
 #endif
 }
 
@@ -580,7 +587,13 @@ meta_wayland_touch_disable (MetaWaylandTouch *touch)
 #ifdef HAVE_NATIVE_BACKEND
   MetaBackend *backend = meta_get_backend ();
   if (META_IS_BACKEND_NATIVE (backend))
-    meta_device_manager_native_remove_filter (evdev_filter_func, touch);
+    {
+      ClutterBackend *backend = clutter_get_default_backend ();
+      ClutterSeat *seat = clutter_backend_get_default_seat (backend);
+
+      meta_seat_native_remove_filter (META_SEAT_NATIVE (seat),
+                                      evdev_filter_func, touch);
+    }
 #endif
 
   meta_wayland_touch_cancel (touch);


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