[mutter] Implement support for the wp_pointer_constraints protocol



commit 495c89401a35f97266018fe9938945861d970dbd
Author: Jonas Ådahl <jadahl gmail com>
Date:   Wed Jun 17 12:10:52 2015 +0800

    Implement support for the wp_pointer_constraints protocol
    
    The wp_pointer_constraints protocol is a protocol which enables clients
    to manipulate the behavior of the pointer cursor associated with a seat.
    
    Currently available constraints are locking the pointer to a static
    position, and confining the pointer to a given region.
    
    Currently locking is fully implemented, and confining is implemented for
    rectangular confinement regions.
    
    What else is lacking is less troublesome semantics for enabling the lock
    or confinement; currently the only requirement implemented is that the
    window that appears focused is the one that may aquire the lock.
    
    This means that a pointer could be 'stolen' by creating a new window that
    receives active focus, or when using focus-follows-mouse, a pointer
    passes a window that has requested a lock. This semantics can be changed
    and the protocol itself allows any semantics as seems fit.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=744104

 .gitignore                                     |    2 +
 src/Makefile.am                                |   10 +
 src/backends/meta-backend-private.h            |    6 +
 src/backends/meta-backend.c                    |   11 +
 src/backends/meta-pointer-constraint.c         |   57 ++
 src/backends/meta-pointer-constraint.h         |   60 ++
 src/backends/native/meta-backend-native.c      |   22 +
 src/wayland/meta-pointer-confinement-wayland.c |  200 ++++++
 src/wayland/meta-pointer-confinement-wayland.h |   45 ++
 src/wayland/meta-pointer-lock-wayland.c        |   72 ++
 src/wayland/meta-pointer-lock-wayland.h        |   42 ++
 src/wayland/meta-wayland-pointer-constraints.c |  818 ++++++++++++++++++++++++
 src/wayland/meta-wayland-pointer-constraints.h |   60 ++
 src/wayland/meta-wayland-pointer.c             |    9 +-
 src/wayland/meta-wayland-pointer.h             |    6 +
 src/wayland/meta-wayland-surface.c             |   77 +++
 src/wayland/meta-wayland-surface.h             |   18 +
 src/wayland/meta-wayland.c                     |    1 +
 src/wayland/meta-window-wayland.c              |    8 +
 19 files changed, 1523 insertions(+), 1 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 62a2743..66df2b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,6 +70,8 @@ src/pointer-gestures-unstable-v*-protocol.c
 src/pointer-gestures-unstable-v*-server-protocol.h
 src/relative-pointer-unstable-v*-protocol.c
 src/relative-pointer-unstable-v*-server-protocol.h
+src/pointer-constraints-unstable-v*-protocol.c
+src/pointer-constraints-unstable-v*-server-protocol.h
 src/meta/meta-version.h
 doc/reference/*.args
 doc/reference/*.bak
diff --git a/src/Makefile.am b/src/Makefile.am
index fb0ccbb..ca2483b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,6 +53,8 @@ mutter_built_sources += \
        xdg-shell-unstable-v5-server-protocol.h                         \
        relative-pointer-unstable-v1-protocol.c                         \
        relative-pointer-unstable-v1-server-protocol.h                  \
+       pointer-constraints-unstable-v1-protocol.c                      \
+       pointer-constraints-unstable-v1-server-protocol.h               \
        $(NULL)
 endif
 
@@ -86,6 +88,8 @@ libmutter_la_SOURCES =                                \
        backends/meta-monitor-manager-private.h \
        backends/meta-monitor-manager-dummy.c   \
        backends/meta-monitor-manager-dummy.h   \
+       backends/meta-pointer-constraint.c      \
+       backends/meta-pointer-constraint.h      \
        backends/meta-stage.h                   \
        backends/meta-stage.c                   \
        backends/edid-parse.c                   \
@@ -268,6 +272,12 @@ libmutter_la_SOURCES +=                            \
        wayland/meta-wayland-keyboard.h         \
        wayland/meta-wayland-pointer.c          \
        wayland/meta-wayland-pointer.h          \
+       wayland/meta-wayland-pointer-constraints.c      \
+       wayland/meta-wayland-pointer-constraints.h      \
+       wayland/meta-pointer-lock-wayland.c             \
+       wayland/meta-pointer-lock-wayland.h             \
+       wayland/meta-pointer-confinement-wayland.c      \
+       wayland/meta-pointer-confinement-wayland.h      \
        wayland/meta-wayland-popup.c            \
        wayland/meta-wayland-popup.h            \
        wayland/meta-wayland-seat.c             \
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 6fa468d..73123ce 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -34,6 +34,7 @@
 #include <meta/meta-idle-monitor.h>
 #include "meta-cursor-renderer.h"
 #include "meta-monitor-manager-private.h"
+#include "backends/meta-pointer-constraint.h"
 
 #define DEFAULT_XKB_RULES_FILE "evdev"
 #define DEFAULT_XKB_MODEL "pc105+inet"
@@ -51,6 +52,8 @@ struct _MetaBackend
 
   GHashTable *device_monitors;
   gint current_device_id;
+
+  MetaPointerConstraint *client_pointer_constraint;
 };
 
 struct _MetaBackendClass
@@ -124,4 +127,7 @@ gboolean meta_backend_get_relative_motion_deltas (MetaBackend *backend,
                                                   double       *dx_unaccel,
                                                   double       *dy_unaccel);
 
+void meta_backend_set_client_pointer_constraint (MetaBackend *backend,
+                                                 MetaPointerConstraint *constraint);
+
 #endif /* META_BACKEND_PRIVATE_H */
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 05d160d..e4ef6e3 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -571,6 +571,17 @@ meta_backend_get_relative_motion_deltas (MetaBackend *backend,
                                             dx_unaccel, dy_unaccel);
 }
 
+void
+meta_backend_set_client_pointer_constraint (MetaBackend           *backend,
+                                            MetaPointerConstraint *constraint)
+{
+  g_assert (!constraint || (constraint && !backend->client_pointer_constraint));
+
+  g_clear_object (&backend->client_pointer_constraint);
+  if (constraint)
+    backend->client_pointer_constraint = g_object_ref (constraint);
+}
+
 static GType
 get_backend_type (void)
 {
diff --git a/src/backends/meta-pointer-constraint.c b/src/backends/meta-pointer-constraint.c
new file mode 100644
index 0000000..40ee3d3
--- /dev/null
+++ b/src/backends/meta-pointer-constraint.c
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#include "config.h"
+
+#include "backends/meta-pointer-constraint.h"
+
+#include <glib-object.h>
+
+G_DEFINE_TYPE (MetaPointerConstraint, meta_pointer_constraint, G_TYPE_OBJECT);
+
+static void
+meta_pointer_constraint_init (MetaPointerConstraint *constraint)
+{
+}
+
+static void
+meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass)
+{
+}
+
+void
+meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
+                                   ClutterInputDevice    *device,
+                                   guint32                time,
+                                   float                  prev_x,
+                                   float                  prev_y,
+                                   float                  *x,
+                                   float                  *y)
+{
+  META_POINTER_CONSTRAINT_GET_CLASS (constraint)->constrain (constraint,
+                                                             device,
+                                                             time,
+                                                             prev_x, prev_y,
+                                                             x, y);
+}
diff --git a/src/backends/meta-pointer-constraint.h b/src/backends/meta-pointer-constraint.h
new file mode 100644
index 0000000..fd60816
--- /dev/null
+++ b/src/backends/meta-pointer-constraint.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#ifndef META_POINTER_CONSTRAINT_H
+#define META_POINTER_CONSTRAINT_H
+
+#include <glib-object.h>
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+#define META_TYPE_POINTER_CONSTRAINT (meta_pointer_constraint_get_type ())
+G_DECLARE_DERIVABLE_TYPE (MetaPointerConstraint, meta_pointer_constraint,
+                          META, POINTER_CONSTRAINT, GObject);
+
+struct _MetaPointerConstraintClass
+{
+  GObjectClass parent_class;
+
+  void (*constrain) (MetaPointerConstraint *constraint,
+                     ClutterInputDevice *device,
+                     guint32 time,
+                     float prev_x,
+                     float prev_y,
+                     float *x,
+                     float *y);
+};
+
+void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
+                                        ClutterInputDevice    *device,
+                                        guint32                time,
+                                        float                  prev_x,
+                                        float                  prev_y,
+                                        float                 *x,
+                                        float                 *y);
+
+G_END_DECLS
+
+#endif /* META_POINTER_CONSTRAINT_H */
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 16d0bac..768b96b 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -36,6 +36,7 @@
 #include "meta-monitor-manager-kms.h"
 #include "meta-cursor-renderer-native.h"
 #include "meta-launcher.h"
+#include "backends/meta-pointer-constraint.h"
 
 #include <stdlib.h>
 
@@ -139,6 +140,24 @@ constrain_to_barriers (ClutterInputDevice *device,
                                        new_x, new_y);
 }
 
+static void
+constrain_to_client_constraint (ClutterInputDevice *device,
+                                guint32             time,
+                                float               prev_x,
+                                float               prev_y,
+                                float              *x,
+                                float              *y)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaPointerConstraint *constraint = backend->client_pointer_constraint;
+
+  if (!constraint)
+    return;
+
+  meta_pointer_constraint_constrain (constraint, device,
+                                     time, prev_x, prev_y, x, y);
+}
+
 /*
  * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg.
  * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder)
@@ -207,6 +226,9 @@ pointer_constrain_callback (ClutterInputDevice *device,
   /* Constrain to barriers */
   constrain_to_barriers (device, time, new_x, new_y);
 
+  /* Constrain to pointer lock */
+  constrain_to_client_constraint (device, time, prev_x, prev_y, new_x, new_y);
+
   monitor_manager = meta_monitor_manager_get ();
   monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors);
 
diff --git a/src/wayland/meta-pointer-confinement-wayland.c b/src/wayland/meta-pointer-confinement-wayland.c
new file mode 100644
index 0000000..ec7433d
--- /dev/null
+++ b/src/wayland/meta-pointer-confinement-wayland.c
@@ -0,0 +1,200 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#include "config.h"
+
+#include "wayland/meta-pointer-confinement-wayland.h"
+
+#include <glib-object.h>
+#include <cairo.h>
+
+#include "backends/meta-backend-private.h"
+#include "wayland/meta-wayland-seat.h"
+#include "wayland/meta-wayland-pointer.h"
+#include "wayland/meta-wayland-pointer-constraints.h"
+#include "wayland/meta-wayland-surface.h"
+#include "backends/meta-pointer-constraint.h"
+#include "compositor/meta-surface-actor-wayland.h"
+
+struct _MetaPointerConfinementWayland
+{
+  MetaPointerConstraint parent;
+
+  MetaWaylandPointerConstraint *constraint;
+};
+
+G_DEFINE_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland,
+               META_TYPE_POINTER_CONSTRAINT);
+
+static void
+meta_pointer_confinement_wayland_constrain (MetaPointerConstraint *constraint,
+                                            ClutterInputDevice    *device,
+                                            guint32                time,
+                                            float                  prev_x,
+                                            float                  prev_y,
+                                            float                  *x,
+                                            float                  *y)
+{
+  MetaPointerConfinementWayland *self =
+    META_POINTER_CONFINEMENT_WAYLAND (constraint);
+  MetaWaylandSurface *surface;
+  cairo_region_t *region;
+  cairo_rectangle_int_t extents;
+  float sx, sy;
+  float min_sx, max_sx;
+  float min_sy, max_sy;
+
+  region =
+    meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
+  cairo_region_get_extents (region, &extents);
+  cairo_region_destroy (region);
+
+  min_sx = extents.x;
+  max_sx = extents.x + extents.width - 1;
+  max_sy = extents.y + extents.height - 1;
+  min_sy = extents.y;
+
+  surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
+  meta_wayland_surface_get_relative_coordinates (surface, *x, *y, &sx, &sy);
+
+  if (sx < min_sx)
+    sx = min_sx;
+  else if (sx > max_sx)
+    sx = max_sx;
+
+  if (sy < min_sy)
+    sy = min_sy;
+  else if (sy > max_sy)
+    sy = max_sy;
+
+  meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, x, y);
+}
+
+static void
+meta_pointer_confinement_wayland_maybe_warp (MetaPointerConfinementWayland *self)
+{
+  MetaWaylandSeat *seat;
+  MetaWaylandSurface *surface;
+  wl_fixed_t sx;
+  wl_fixed_t sy;
+  cairo_region_t *region;
+
+  seat = meta_wayland_pointer_constraint_get_seat (self->constraint);
+  surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
+  meta_wayland_pointer_get_relative_coordinates (&seat->pointer,
+                                                 surface,
+                                                 &sx, &sy);
+
+  region =
+    meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
+
+  if (!cairo_region_contains_point (region,
+                                    wl_fixed_to_int (sx),
+                                    wl_fixed_to_int (sy)))
+    {
+      cairo_rectangle_int_t extents;
+      wl_fixed_t min_sx;
+      wl_fixed_t max_sx;
+      wl_fixed_t max_sy;
+      wl_fixed_t min_sy;
+      gboolean x_changed = TRUE, y_changed = TRUE;
+
+      cairo_region_get_extents (region, &extents);
+
+      min_sx = wl_fixed_from_int (extents.x);
+      max_sx = wl_fixed_from_int (extents.x + extents.width - 1);
+      max_sy = wl_fixed_from_int (extents.y + extents.height - 1);
+      min_sy = wl_fixed_from_int (extents.y);
+
+      if (sx < min_sx)
+        sx = min_sx;
+      else if (sx > max_sx)
+        sx = max_sx;
+      else
+        x_changed = FALSE;
+
+      if (sy < min_sy)
+        sy = min_sy;
+      else if (sy > max_sy)
+        sy = max_sy;
+      else
+        y_changed = FALSE;
+
+      if (x_changed || y_changed)
+        {
+          float x, y;
+
+          meta_wayland_surface_get_absolute_coordinates (surface,
+                                                         wl_fixed_to_double (sx),
+                                                         wl_fixed_to_double (sy),
+                                                         &x, &y);
+          meta_backend_warp_pointer (meta_get_backend (), (int)x, (int)y);
+        }
+    }
+
+  cairo_region_destroy (region);
+}
+
+static void
+surface_actor_painting (MetaSurfaceActorWayland       *surface_actor,
+                        MetaPointerConfinementWayland *self)
+{
+  meta_pointer_confinement_wayland_maybe_warp (self);
+}
+
+MetaPointerConstraint *
+meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint)
+{
+  GObject *object;
+  MetaPointerConfinementWayland *confinement;
+  MetaWaylandSurface *surface;
+
+  object = g_object_new (META_TYPE_POINTER_CONFINEMENT_WAYLAND, NULL);
+  confinement = META_POINTER_CONFINEMENT_WAYLAND (object);
+
+  confinement->constraint = constraint;
+
+  surface = meta_wayland_pointer_constraint_get_surface (constraint);
+  g_signal_connect_object (surface->surface_actor,
+                           "painting",
+                           G_CALLBACK (surface_actor_painting),
+                           confinement,
+                           0);
+
+  return META_POINTER_CONSTRAINT (confinement);
+}
+
+static void
+meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland)
+{
+}
+
+static void
+meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass)
+{
+  MetaPointerConstraintClass *pointer_constraint_class =
+    META_POINTER_CONSTRAINT_CLASS (klass);
+
+  pointer_constraint_class->constrain = meta_pointer_confinement_wayland_constrain;
+}
diff --git a/src/wayland/meta-pointer-confinement-wayland.h b/src/wayland/meta-pointer-confinement-wayland.h
new file mode 100644
index 0000000..482a25a
--- /dev/null
+++ b/src/wayland/meta-pointer-confinement-wayland.h
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#ifndef META_POINTER_CONFINEMENT_WAYLAND_H
+#define META_POINTER_CONFINEMENT_WAYLAND_H
+
+#include <glib-object.h>
+
+#include "backends/meta-pointer-constraint.h"
+#include "wayland/meta-wayland-pointer-constraints.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_POINTER_CONFINEMENT_WAYLAND (meta_pointer_confinement_wayland_get_type ())
+G_DECLARE_FINAL_TYPE (MetaPointerConfinementWayland,
+                      meta_pointer_confinement_wayland,
+                      META, POINTER_CONFINEMENT_WAYLAND,
+                      MetaPointerConstraint);
+
+MetaPointerConstraint *meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint);
+
+G_END_DECLS
+
+#endif /* META_CONFINEMENT_WAYLAND_H */
diff --git a/src/wayland/meta-pointer-lock-wayland.c b/src/wayland/meta-pointer-lock-wayland.c
new file mode 100644
index 0000000..a9098d5
--- /dev/null
+++ b/src/wayland/meta-pointer-lock-wayland.c
@@ -0,0 +1,72 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#include "config.h"
+
+#include "wayland/meta-pointer-lock-wayland.h"
+
+#include <glib-object.h>
+
+#include "backends/meta-pointer-constraint.h"
+
+struct _MetaPointerLockWayland
+{
+  MetaPointerConstraint parent;
+};
+
+G_DEFINE_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland,
+               META_TYPE_POINTER_CONSTRAINT);
+
+static void
+meta_pointer_lock_wayland_constrain (MetaPointerConstraint *constraint,
+                                     ClutterInputDevice    *device,
+                                     guint32                time,
+                                     float                  prev_x,
+                                     float                  prev_y,
+                                     float                 *x,
+                                     float                 *y)
+{
+  *x = prev_x;
+  *y = prev_y;
+}
+
+MetaPointerConstraint *
+meta_pointer_lock_wayland_new (void)
+{
+  return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND, NULL);
+}
+
+static void
+meta_pointer_lock_wayland_init (MetaPointerLockWayland *lock_wayland)
+{
+}
+
+static void
+meta_pointer_lock_wayland_class_init (MetaPointerLockWaylandClass *klass)
+{
+  MetaPointerConstraintClass *pointer_constraint_class =
+    META_POINTER_CONSTRAINT_CLASS (klass);
+
+  pointer_constraint_class->constrain = meta_pointer_lock_wayland_constrain;
+}
diff --git a/src/wayland/meta-pointer-lock-wayland.h b/src/wayland/meta-pointer-lock-wayland.h
new file mode 100644
index 0000000..787b432
--- /dev/null
+++ b/src/wayland/meta-pointer-lock-wayland.h
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#ifndef META_POINTER_LOCK_WAYLAND_H
+#define META_POINTER_LOCK_WAYLAND_H
+
+#include <glib-object.h>
+
+#include "backends/meta-pointer-constraint.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_POINTER_LOCK_WAYLAND (meta_pointer_lock_wayland_get_type ())
+G_DECLARE_FINAL_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland,
+                      META, POINTER_LOCK_WAYLAND, MetaPointerConstraint);
+
+MetaPointerConstraint *meta_pointer_lock_wayland_new (void);
+
+G_END_DECLS
+
+#endif /* META_LOCK_WAYLAND_H */
diff --git a/src/wayland/meta-wayland-pointer-constraints.c b/src/wayland/meta-wayland-pointer-constraints.c
new file mode 100644
index 0000000..49fbb0c
--- /dev/null
+++ b/src/wayland/meta-wayland-pointer-constraints.c
@@ -0,0 +1,818 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#include "config.h"
+
+#include "meta-wayland-pointer-constraints.h"
+
+#include <glib.h>
+
+#include "meta/meta-backend.h"
+#include "meta-wayland-private.h"
+#include "meta-wayland-seat.h"
+#include "meta-wayland-pointer.h"
+#include "meta-wayland-surface.h"
+#include "meta-wayland-region.h"
+#include "meta-pointer-lock-wayland.h"
+#include "meta-pointer-confinement-wayland.h"
+#include "window-private.h"
+#include "backends/meta-backend-private.h"
+#include "backends/native/meta-backend-native.h"
+#include "backends/meta-pointer-constraint.h"
+
+#include "pointer-constraints-unstable-v1-server-protocol.h"
+
+static GQuark quark_pending_constraint_state = 0;
+
+struct _MetaWaylandPointerConstraint
+{
+  GObject parent;
+
+  MetaWaylandSurface *surface;
+  gboolean is_enabled;
+  cairo_region_t *region;
+  struct wl_resource *resource;
+  MetaWaylandPointerGrab grab;
+  MetaWaylandSeat *seat;
+  enum zwp_pointer_constraints_v1_lifetime lifetime;
+
+  gboolean hint_set;
+  wl_fixed_t x_hint;
+  wl_fixed_t y_hint;
+
+  MetaPointerConstraint *constraint;
+};
+
+typedef struct
+{
+  MetaWaylandPointerConstraint *constraint;
+  cairo_region_t *region;
+  gulong applied_handler_id;
+} MetaWaylandPendingConstraintState;
+
+typedef struct
+{
+  GList *pending_constraint_states;
+} MetaWaylandPendingConstraintStateContainer;
+
+G_DEFINE_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint,
+               G_TYPE_OBJECT);
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_interface;
+static const struct zwp_confined_pointer_v1_interface confined_pointer_interface;
+static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface;
+static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface;
+
+static cairo_region_t *
+create_infinite_constraint_region (void)
+{
+  return cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
+                                          .x = INT_MIN / 2,
+                                          .y = INT_MIN / 2,
+                                          .width = INT_MAX,
+                                          .height = INT_MAX,
+                                        });
+}
+
+static MetaWaylandPointerConstraint *
+meta_wayland_pointer_constraint_new (MetaWaylandSurface                      *surface,
+                                     MetaWaylandSeat                         *seat,
+                                     MetaWaylandRegion                       *region,
+                                     enum zwp_pointer_constraints_v1_lifetime lifetime,
+                                     struct wl_resource                      *resource,
+                                     const MetaWaylandPointerGrabInterface   *grab_interface)
+{
+  MetaWaylandPointerConstraint *constraint;
+
+  constraint = g_object_new (META_TYPE_WAYLAND_POINTER_CONSTRAINT, NULL);
+  if (!constraint)
+    return NULL;
+
+  constraint->surface = surface;
+  constraint->seat = seat;
+  constraint->lifetime = lifetime;
+  constraint->resource = resource;
+  constraint->grab.interface = grab_interface;
+
+  if (region)
+    {
+      constraint->region =
+        cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
+    }
+  else
+    {
+      constraint->region = create_infinite_constraint_region ();
+    }
+
+  return constraint;
+}
+
+static gboolean
+meta_wayland_pointer_constraint_is_enabled (MetaWaylandPointerConstraint *constraint)
+{
+  return constraint->is_enabled;
+}
+
+static void
+meta_wayland_pointer_constraint_notify_activated (MetaWaylandPointerConstraint *constraint)
+{
+  struct wl_resource *resource = constraint->resource;
+
+  if (wl_resource_instance_of (resource,
+                               &zwp_locked_pointer_v1_interface,
+                               &locked_pointer_interface))
+    {
+      zwp_locked_pointer_v1_send_locked (resource);
+    }
+  else if (wl_resource_instance_of (resource,
+                                    &zwp_confined_pointer_v1_interface,
+                                    &confined_pointer_interface))
+    {
+      zwp_confined_pointer_v1_send_confined (resource);
+    }
+}
+
+static void
+meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint *constraint)
+{
+  struct wl_resource *resource = constraint->resource;
+
+  if (wl_resource_instance_of (resource,
+                               &zwp_locked_pointer_v1_interface,
+                               &locked_pointer_interface))
+    zwp_locked_pointer_v1_send_unlocked (resource);
+  else if (wl_resource_instance_of (resource,
+                                    &zwp_confined_pointer_v1_interface,
+                                    &confined_pointer_interface))
+    zwp_confined_pointer_v1_send_unconfined (resource);
+}
+
+static MetaPointerConstraint *
+meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint)
+{
+  struct wl_resource *resource = constraint->resource;
+
+  if (wl_resource_instance_of (resource,
+                               &zwp_locked_pointer_v1_interface,
+                               &locked_pointer_interface))
+    {
+      return meta_pointer_lock_wayland_new ();
+    }
+  else if (wl_resource_instance_of (resource,
+                                    &zwp_confined_pointer_v1_interface,
+                                    &confined_pointer_interface))
+    {
+      return meta_pointer_confinement_wayland_new (constraint);
+    }
+  g_assert_not_reached ();
+  return NULL;
+}
+
+static void
+meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint)
+{
+  MetaBackend *backend = meta_get_backend ();
+
+  g_assert (!constraint->is_enabled);
+
+  constraint->is_enabled = TRUE;
+  meta_wayland_pointer_constraint_notify_activated (constraint);
+  meta_wayland_pointer_start_grab (&constraint->seat->pointer,
+                                   &constraint->grab);
+
+  constraint->constraint =
+    meta_wayland_pointer_constraint_create_pointer_constraint (constraint);
+  meta_backend_set_client_pointer_constraint (backend, constraint->constraint);
+  g_object_add_weak_pointer (G_OBJECT (constraint->constraint),
+                             (gpointer *) &constraint->constraint);
+  g_object_unref (constraint->constraint);
+}
+
+static void
+meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint)
+{
+  meta_wayland_pointer_constraint_notify_deactivated (constraint);
+  meta_wayland_pointer_end_grab (constraint->grab.pointer);
+  meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL);
+}
+
+void
+meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint)
+{
+  if (meta_wayland_pointer_constraint_is_enabled (constraint))
+    meta_wayland_pointer_constraint_disable (constraint);
+
+  wl_resource_set_user_data (constraint->resource, NULL);
+  cairo_region_destroy (constraint->region);
+  g_object_unref (constraint);
+}
+
+static gboolean
+is_within_constraint_region (MetaWaylandPointerConstraint *constraint,
+                             wl_fixed_t                    sx,
+                             wl_fixed_t                    sy)
+{
+  cairo_region_t *region;
+  gboolean is_within;
+
+  region = meta_wayland_pointer_constraint_calculate_effective_region (constraint);
+  is_within = cairo_region_contains_point (constraint->region,
+                                           wl_fixed_to_int (sx),
+                                           wl_fixed_to_int (sy));
+  cairo_region_destroy (region);
+
+  return is_within;
+}
+
+void
+meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint)
+{
+  MetaWaylandSeat *seat = constraint->seat;
+  wl_fixed_t sx, sy;
+
+  if (constraint->is_enabled)
+    return;
+
+  if (seat->keyboard.focus_surface != constraint->surface)
+    return;
+
+  meta_wayland_pointer_get_relative_coordinates (&constraint->seat->pointer,
+                                                 constraint->surface,
+                                                 &sx, &sy);
+  if (!is_within_constraint_region (constraint, sx, sy))
+    return;
+
+  meta_wayland_pointer_constraint_enable (constraint);
+}
+
+static void
+meta_wayland_pointer_constraint_remove (MetaWaylandPointerConstraint *constraint)
+{
+  MetaWaylandSurface *surface = constraint->surface;
+
+  meta_wayland_surface_remove_pointer_constraint (surface, constraint);
+  meta_wayland_pointer_constraint_destroy (constraint);
+}
+
+void
+meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
+                                                       MetaWindow      *focus_window)
+{
+  MetaWaylandPointer *pointer = &seat->pointer;
+
+  if ((pointer->grab->interface == &confined_pointer_grab_interface ||
+       pointer->grab->interface == &locked_pointer_grab_interface) &&
+      pointer->focus_surface &&
+      pointer->focus_surface->window != focus_window)
+    {
+      MetaWaylandPointerConstraint *constraint =
+        wl_container_of (pointer->grab, constraint, grab);
+
+      switch (constraint->lifetime)
+        {
+        case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
+          meta_wayland_pointer_constraint_remove (constraint);
+          break;
+
+        case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
+          meta_wayland_pointer_constraint_disable (constraint);
+          break;
+
+        default:
+          g_assert_not_reached ();
+        }
+    }
+}
+
+void
+meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window)
+{
+  MetaWaylandSurface *surface = window->surface;
+  GList *it;
+
+  for (it = surface->pointer_constraints; it; it = it->next)
+    {
+      MetaWaylandPointerConstraint *constraint = it->data;
+
+      meta_wayland_pointer_constraint_maybe_enable (constraint);
+    }
+}
+
+MetaWaylandSeat *
+meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint)
+{
+  return constraint->seat;
+}
+
+cairo_region_t *
+meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint)
+{
+  cairo_region_t *region;
+
+  region = cairo_region_copy (constraint->surface->input_region);
+  cairo_region_intersect (region, constraint->region);
+
+  return region;
+}
+
+cairo_region_t *
+meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint)
+{
+  return constraint->region;
+}
+
+MetaWaylandSurface *
+meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint)
+{
+  return constraint->surface;
+}
+
+static void
+pointer_constraint_resource_destroyed (struct wl_resource *resource)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_resource_get_user_data (resource);
+
+  if (!constraint)
+    return;
+
+  meta_wayland_pointer_constraint_remove (constraint);
+}
+
+static void
+pending_constraint_state_free (MetaWaylandPendingConstraintState *constraint_pending)
+{
+  g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
+  if (constraint_pending->constraint)
+    g_object_remove_weak_pointer (G_OBJECT (constraint_pending->constraint),
+                                  (gpointer *) &constraint_pending->constraint);
+}
+
+static MetaWaylandPendingConstraintStateContainer *
+get_pending_constraint_state_container (MetaWaylandPendingState *pending)
+{
+  return g_object_get_qdata (G_OBJECT (pending),
+                             quark_pending_constraint_state);
+}
+
+static MetaWaylandPendingConstraintState *
+get_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
+{
+  MetaWaylandPendingState *pending = constraint->surface->pending;
+  MetaWaylandPendingConstraintStateContainer *container;
+  GList *l;
+
+  container = get_pending_constraint_state_container (pending);
+  for (l = container->pending_constraint_states; l; l = l->next)
+    {
+      MetaWaylandPendingConstraintState *constraint_pending = l->data;
+
+      if (constraint_pending->constraint == constraint)
+        return constraint_pending;
+    }
+
+  return NULL;
+}
+
+static void
+pending_constraint_state_container_free (MetaWaylandPendingConstraintStateContainer *container)
+{
+  g_list_free_full (container->pending_constraint_states,
+                    (GDestroyNotify) pending_constraint_state_free);
+  g_free (container);
+}
+
+static MetaWaylandPendingConstraintStateContainer *
+ensure_pending_constraint_state_container (MetaWaylandPendingState *pending)
+{
+  MetaWaylandPendingConstraintStateContainer *container;
+
+  container = get_pending_constraint_state_container (pending);
+  if (!container)
+    {
+      container = g_new0 (MetaWaylandPendingConstraintStateContainer, 1);
+      g_object_set_qdata_full (G_OBJECT (pending),
+                               quark_pending_constraint_state,
+                               container,
+                               (GDestroyNotify) pending_constraint_state_container_free);
+
+    }
+
+  return container;
+}
+
+static void
+remove_pending_constraint_state (MetaWaylandPointerConstraint *constraint,
+                                 MetaWaylandPendingState      *pending)
+{
+  MetaWaylandPendingConstraintStateContainer *container;
+  GList *l;
+
+  container = get_pending_constraint_state_container (pending);
+  for (l = container->pending_constraint_states; l; l = l->next)
+    {
+      MetaWaylandPendingConstraintState *constraint_pending = l->data;
+      if (constraint_pending->constraint != constraint)
+        continue;
+
+      pending_constraint_state_free (l->data);
+      container->pending_constraint_states =
+        g_list_remove_link (container->pending_constraint_states, l);
+      break;
+    }
+}
+
+static void
+pending_constraint_state_applied (MetaWaylandPendingState           *pending,
+                                  MetaWaylandPendingConstraintState *constraint_pending)
+{
+  MetaWaylandPointerConstraint *constraint = constraint_pending->constraint;
+
+  if (!constraint)
+    return;
+
+  g_clear_pointer (&constraint->region, cairo_region_destroy);
+  if (constraint_pending->region)
+    {
+      constraint->region = constraint_pending->region;
+      constraint_pending->region = NULL;
+    }
+  else
+    {
+      constraint->region = create_infinite_constraint_region ();
+    }
+
+  g_signal_handler_disconnect (pending,
+                               constraint_pending->applied_handler_id);
+  remove_pending_constraint_state (constraint, pending);
+
+  /* The pointer is potentially warped by the actor paint signal callback if
+   * the new region proved it necessary.
+   */
+}
+
+static MetaWaylandPendingConstraintState *
+ensure_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
+{
+  MetaWaylandPendingState *pending = constraint->surface->pending;
+  MetaWaylandPendingConstraintStateContainer *container;
+  MetaWaylandPendingConstraintState *constraint_pending;
+
+  container = ensure_pending_constraint_state_container (pending);
+  constraint_pending = get_pending_constraint_state (constraint);
+  if (!constraint_pending)
+    {
+      constraint_pending = g_new0 (MetaWaylandPendingConstraintState, 1);
+      constraint_pending->constraint = constraint;
+      constraint_pending->applied_handler_id =
+        g_signal_connect (pending, "applied",
+                          G_CALLBACK (pending_constraint_state_applied),
+                          constraint_pending);
+      g_object_add_weak_pointer (G_OBJECT (constraint),
+                                 (gpointer *) &constraint_pending->constraint);
+
+      container->pending_constraint_states =
+        g_list_append (container->pending_constraint_states,
+                       constraint_pending);
+    }
+
+  return constraint_pending;
+}
+
+static void
+meta_wayland_pointer_constraint_set_pending_region (MetaWaylandPointerConstraint *constraint,
+                                                    MetaWaylandRegion            *region)
+{
+  MetaWaylandPendingConstraintState *constraint_pending;
+
+  constraint_pending = ensure_pending_constraint_state (constraint);
+
+  g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
+  if (region)
+    {
+      constraint_pending->region =
+        cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
+    }
+}
+
+static void
+init_pointer_constraint (struct wl_resource                      *resource,
+                         uint32_t                                 id,
+                         MetaWaylandSurface                      *surface,
+                         MetaWaylandSeat                         *seat,
+                         MetaWaylandRegion                       *region,
+                         enum zwp_pointer_constraints_v1_lifetime lifetime,
+                         const struct wl_interface               *interface,
+                         const void                              *implementation,
+                         const MetaWaylandPointerGrabInterface   *grab_interface)
+{
+  struct wl_client *client = wl_resource_get_client (resource);
+  struct wl_resource *cr;
+  MetaWaylandPointerConstraint *constraint;
+
+  if (meta_wayland_surface_get_pointer_constraint_for_seat (surface, seat))
+    {
+      wl_resource_post_error (resource,
+                              WL_DISPLAY_ERROR_INVALID_OBJECT,
+                              "the pointer as already requested to be "
+                              "locked or confined on that surface");
+      return;
+    }
+
+  cr = wl_resource_create (client, interface,
+                           wl_resource_get_version (resource),
+                           id);
+  if (cr == NULL)
+    {
+      wl_client_post_no_memory (client);
+      return;
+    }
+
+  constraint = meta_wayland_pointer_constraint_new (surface, seat,
+                                                    region,
+                                                    lifetime,
+                                                    cr, grab_interface);
+  if (constraint == NULL)
+    {
+      wl_client_post_no_memory (client);
+      return;
+    }
+
+  meta_wayland_surface_add_pointer_constraint (surface, constraint);
+
+  wl_resource_set_implementation (cr, implementation, constraint,
+                                  pointer_constraint_resource_destroyed);
+
+  meta_wayland_pointer_constraint_maybe_enable (constraint);
+}
+
+static void
+locked_pointer_destroy (struct wl_client   *client,
+                        struct wl_resource *resource)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_resource_get_user_data (resource);
+  gboolean warp_pointer = FALSE;
+  int warp_x, warp_y;
+
+  if (constraint && constraint->is_enabled && constraint->hint_set &&
+      is_within_constraint_region (constraint,
+                                   constraint->x_hint,
+                                   constraint->y_hint))
+    {
+      float sx, sy;
+      float x, y;
+
+      sx = (float)wl_fixed_to_double (constraint->x_hint);
+      sy = (float)wl_fixed_to_double (constraint->y_hint);
+      meta_wayland_surface_get_absolute_coordinates (constraint->surface,
+                                                     sx, sy,
+                                                     &x, &y);
+      warp_pointer = TRUE;
+      warp_x = (int) x;
+      warp_y = (int) y;
+    }
+  wl_resource_destroy (resource);
+
+  if (warp_pointer)
+    meta_backend_warp_pointer (meta_get_backend (), warp_x, warp_y);
+}
+
+static void
+locked_pointer_set_cursor_position_hint (struct wl_client   *client,
+                                         struct wl_resource *resource,
+                                         wl_fixed_t          surface_x,
+                                         wl_fixed_t          surface_y)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_resource_get_user_data (resource);
+
+  /* Ignore a set cursor hint that was already sent after the constraint
+   * was cancelled. */
+  if (!constraint->resource || constraint->resource != resource)
+    return;
+
+  constraint->hint_set = TRUE;
+  constraint->x_hint = surface_x;
+  constraint->y_hint = surface_y;
+}
+
+static void
+locked_pointer_set_region (struct wl_client   *client,
+                           struct wl_resource *resource,
+                           struct wl_resource *region_resource)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_resource_get_user_data (resource);
+  MetaWaylandRegion *region =
+    region_resource ? wl_resource_get_user_data (region_resource) : NULL;
+
+  meta_wayland_pointer_constraint_set_pending_region (constraint, region);
+}
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = {
+  locked_pointer_destroy,
+  locked_pointer_set_cursor_position_hint,
+  locked_pointer_set_region,
+};
+
+static void
+locked_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
+                                   MetaWaylandSurface     *surface)
+{
+}
+
+static void
+locked_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
+                                    const ClutterEvent     *event)
+{
+  meta_wayland_pointer_send_relative_motion (grab->pointer, event);
+}
+
+static void
+locked_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
+                                    const ClutterEvent     *event)
+{
+  meta_wayland_pointer_send_button (grab->pointer, event);
+}
+
+static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface = {
+  locked_pointer_grab_pointer_focus,
+  locked_pointer_grab_pointer_motion,
+  locked_pointer_grab_pointer_button,
+};
+
+static void
+pointer_constraints_destroy (struct wl_client   *client,
+                             struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+pointer_constraints_lock_pointer (struct wl_client   *client,
+                                  struct wl_resource *resource,
+                                  uint32_t            id,
+                                  struct wl_resource *surface_resource,
+                                  struct wl_resource *pointer_resource,
+                                  struct wl_resource *region_resource,
+                                  uint32_t            lifetime)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
+  MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
+  MetaWaylandRegion *region =
+    region_resource ? wl_resource_get_user_data (region_resource) : NULL;
+
+  init_pointer_constraint (resource, id, surface, seat, region, lifetime,
+                           &zwp_locked_pointer_v1_interface,
+                           &locked_pointer_interface,
+                           &locked_pointer_grab_interface);
+}
+
+static void
+confined_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
+                                     MetaWaylandSurface *surface)
+{
+}
+
+static void
+confined_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
+                                      const ClutterEvent     *event)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_container_of (grab, constraint, grab);
+  MetaWaylandPointer *pointer = grab->pointer;
+
+  g_assert (pointer->focus_surface);
+  g_assert (pointer->focus_surface == constraint->surface);
+
+  meta_wayland_pointer_send_motion (pointer, event);
+}
+
+static void
+confined_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
+                                      const ClutterEvent     *event)
+{
+  meta_wayland_pointer_send_button (grab->pointer, event);
+}
+
+static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface = {
+  confined_pointer_grab_pointer_focus,
+  confined_pointer_grab_pointer_motion,
+  confined_pointer_grab_pointer_button,
+};
+
+static void
+confined_pointer_destroy (struct wl_client   *client,
+                          struct wl_resource *resource)
+{
+  wl_resource_destroy (resource);
+}
+
+static void
+confined_pointer_set_region (struct wl_client   *client,
+                             struct wl_resource *resource,
+                             struct wl_resource *region_resource)
+{
+  MetaWaylandPointerConstraint *constraint =
+    wl_resource_get_user_data (resource);
+  MetaWaylandRegion *region =
+    region_resource ? wl_resource_get_user_data (region_resource) : NULL;
+
+  meta_wayland_pointer_constraint_set_pending_region (constraint, region);
+}
+
+static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = {
+  confined_pointer_destroy,
+  confined_pointer_set_region,
+};
+
+static void
+pointer_constraints_confine_pointer (struct wl_client   *client,
+                                     struct wl_resource *resource,
+                                     uint32_t            id,
+                                     struct wl_resource *surface_resource,
+                                     struct wl_resource *pointer_resource,
+                                     struct wl_resource *region_resource,
+                                     uint32_t            lifetime)
+{
+  MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+  MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
+  MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
+  MetaWaylandRegion *region =
+    region_resource ? wl_resource_get_user_data (region_resource) : NULL;
+
+  init_pointer_constraint (resource, id, surface, seat, region, lifetime,
+                           &zwp_confined_pointer_v1_interface,
+                           &confined_pointer_interface,
+                           &confined_pointer_grab_interface);
+
+}
+
+static const struct zwp_pointer_constraints_v1_interface pointer_constraints = {
+  pointer_constraints_destroy,
+  pointer_constraints_lock_pointer,
+  pointer_constraints_confine_pointer,
+};
+
+static void
+bind_pointer_constraints (struct wl_client *client,
+                          void             *data,
+                          uint32_t          version,
+                          uint32_t          id)
+{
+  MetaWaylandCompositor *compositor = data;
+  struct wl_resource *resource;
+
+  resource = wl_resource_create (client,
+                                 &zwp_pointer_constraints_v1_interface,
+                                 1, id);
+
+  wl_resource_set_implementation (resource,
+                                  &pointer_constraints,
+                                  compositor,
+                                  NULL);
+}
+
+void
+meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor)
+{
+  if (!wl_global_create (compositor->wayland_display,
+                         &zwp_pointer_constraints_v1_interface, 1,
+                         compositor, bind_pointer_constraints))
+    g_error ("Could not create wp_pointer_constraints global");
+}
+
+static void
+meta_wayland_pointer_constraint_init (MetaWaylandPointerConstraint *constraint)
+{
+}
+
+static void
+meta_wayland_pointer_constraint_class_init (MetaWaylandPointerConstraintClass *klass)
+{
+  quark_pending_constraint_state =
+    g_quark_from_static_string ("-meta-wayland-pointer-constraint-pending_state");
+}
diff --git a/src/wayland/meta-wayland-pointer-constraints.h b/src/wayland/meta-wayland-pointer-constraints.h
new file mode 100644
index 0000000..c467c83
--- /dev/null
+++ b/src/wayland/meta-wayland-pointer-constraints.h
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jonas Ådahl <jadahl gmail com>
+ */
+
+#ifndef META_WAYLAND_POINTER_CONSTRAINTS_H
+#define META_WAYLAND_POINTER_CONSTRAINTS_H
+
+#include "meta-wayland-types.h"
+#include "meta/window.h"
+
+#include <wayland-server.h>
+
+#define META_TYPE_WAYLAND_POINTER_CONSTRAINT (meta_wayland_pointer_constraint_get_type ())
+G_DECLARE_FINAL_TYPE (MetaWaylandPointerConstraint,
+                      meta_wayland_pointer_constraint,
+                      META, WAYLAND_POINTER_CONSTRAINT,
+                      GObject);
+
+typedef struct _MetaWaylandPointerConstraint MetaWaylandPointerConstraint;
+
+void meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor);
+
+void meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint);
+
+void meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint);
+
+MetaWaylandSeat * meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint);
+
+cairo_region_t * meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint 
*constraint);
+
+cairo_region_t * meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint);
+
+MetaWaylandSurface * meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint);
+
+void meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
+                                                            MetaWindow      *focus_window);
+
+void meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window);
+
+#endif /* META_WAYLAND_POINTER_CONSTRAINTS_H */
diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
index 663e35f..09d0e98 100644
--- a/src/wayland/meta-wayland-pointer.c
+++ b/src/wayland/meta-wayland-pointer.c
@@ -273,7 +273,7 @@ meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer)
     }
 }
 
-static void
+void
 meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer,
                                            const ClutterEvent *event)
 {
@@ -1249,6 +1249,13 @@ meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor)
     g_error ("Could not create relative pointer manager global");
 }
 
+MetaWaylandSeat *
+meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
+{
+  MetaWaylandSeat *seat = wl_container_of (pointer, seat, pointer);
+  return seat;
+}
+
 static void
 cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
 {
diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h
index cfe1b46..c016bc1 100644
--- a/src/wayland/meta-wayland-pointer.h
+++ b/src/wayland/meta-wayland-pointer.h
@@ -28,6 +28,7 @@
 #include "meta-wayland-pointer-gesture-swipe.h"
 #include "meta-wayland-pointer-gesture-pinch.h"
 #include "meta-wayland-surface.h"
+#include "meta-wayland-pointer-constraints.h"
 
 #include <meta/meta-cursor-tracker.h>
 
@@ -102,6 +103,9 @@ gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer,
 void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer,
                                        const ClutterEvent *event);
 
+void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer,
+                                                const ClutterEvent *event);
+
 void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer,
                                        const ClutterEvent *event);
 
@@ -145,4 +149,6 @@ void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *re
 
 void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor);
 
+MetaWaylandSeat *meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer);
+
 #endif /* META_WAYLAND_POINTER_H */
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
index 7fe7ab7..129486c 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -55,6 +55,14 @@
 #include "meta-surface-actor-wayland.h"
 #include "meta-xwayland-private.h"
 
+enum {
+  PENDING_STATE_SIGNAL_APPLIED,
+
+  PENDING_STATE_SIGNAL_LAST_SIGNAL
+};
+
+static guint pending_state_signals[PENDING_STATE_SIGNAL_LAST_SIGNAL];
+
 typedef struct _MetaWaylandSurfaceRolePrivate
 {
   MetaWaylandSurface *surface;
@@ -516,6 +524,14 @@ meta_wayland_pending_state_class_init (MetaWaylandPendingStateClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = meta_wayland_pending_state_finalize;
+
+  pending_state_signals[PENDING_STATE_SIGNAL_APPLIED] =
+    g_signal_new ("applied",
+                  G_TYPE_FROM_CLASS (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
 }
 
 static void
@@ -689,6 +705,10 @@ apply_pending_state (MetaWaylandSurface      *surface,
       wl_list_init (&pending->frame_callback_list);
     }
 
+  g_signal_emit (pending,
+                 pending_state_signals[PENDING_STATE_SIGNAL_APPLIED],
+                 0);
+
   meta_surface_actor_wayland_sync_state (
     META_SURFACE_ACTOR_WAYLAND (surface->surface_actor));
 
@@ -1072,6 +1092,9 @@ wl_surface_destructor (struct wl_resource *resource)
   if (surface->window)
     destroy_window (surface);
 
+  g_list_free_full (surface->pointer_constraints,
+                    (GDestroyNotify) meta_wayland_pointer_constraint_destroy);
+
   surface_set_buffer (surface, NULL);
   g_clear_object (&surface->pending);
 
@@ -2469,6 +2492,39 @@ meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface)
 }
 
 void
+meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface           *surface,
+                                             MetaWaylandPointerConstraint *constraint)
+{
+  surface->pointer_constraints = g_list_append (surface->pointer_constraints,
+                                                constraint);
+}
+
+void
+meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface           *surface,
+                                                MetaWaylandPointerConstraint *constraint)
+{
+  surface->pointer_constraints = g_list_remove (surface->pointer_constraints,
+                                                constraint);
+}
+
+MetaWaylandPointerConstraint *
+meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface,
+                                                      MetaWaylandSeat    *seat)
+{
+  GList *iter;
+
+  for (iter = surface->pointer_constraints; iter; iter = iter->next)
+    {
+      MetaWaylandPointerConstraint *constraint = iter->data;
+
+      if (seat == meta_wayland_pointer_constraint_get_seat (constraint))
+        return constraint;
+    }
+
+  return NULL;
+}
+
+void
 meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
                                                float               abs_x,
                                                float               abs_y,
@@ -2483,6 +2539,27 @@ meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
   *sy /= surface->scale;
 }
 
+void
+meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
+                                               float               sx,
+                                               float               sy,
+                                               float               *x,
+                                               float               *y)
+{
+  ClutterActor *actor =
+    CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor));
+  ClutterVertex sv = {
+    .x = sx * surface->scale,
+    .y = sy * surface->scale,
+  };
+  ClutterVertex v = { 0 };
+
+  clutter_actor_apply_relative_transform_to_point (actor, NULL, &sv, &v);
+
+  *x = v.x;
+  *y = v.y;
+}
+
 static void
 meta_wayland_surface_init (MetaWaylandSurface *surface)
 {
diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h
index 3105eec..7200bbf 100644
--- a/src/wayland/meta-wayland-surface.h
+++ b/src/wayland/meta-wayland-surface.h
@@ -31,6 +31,7 @@
 #include "meta-wayland-types.h"
 #include "meta-surface-actor.h"
 #include "backends/meta-monitor-manager-private.h"
+#include "meta-wayland-pointer-constraints.h"
 
 typedef struct _MetaWaylandPendingState MetaWaylandPendingState;
 
@@ -217,6 +218,8 @@ struct _MetaWaylandSurface
     gboolean pending_pos;
     GSList *pending_placement_ops;
   } sub;
+
+  GList *pointer_constraints;
 };
 
 void                meta_wayland_shell_init     (MetaWaylandCompositor *compositor);
@@ -261,12 +264,27 @@ void                meta_wayland_surface_queue_pending_frame_callbacks (MetaWayl
 void                meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface      
*surface,
                                                                               MetaWaylandPendingState 
*pending);
 
+void                meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface           *surface,
+                                                                 MetaWaylandPointerConstraint *constraint);
+
+void                meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface           *surface,
+                                                                    MetaWaylandPointerConstraint 
*constraint);
+MetaWaylandPointerConstraint *
+                    meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface,
+                                                                          MetaWaylandSeat    *seat);
+
 void                meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
                                                                    float               abs_x,
                                                                    float               abs_y,
                                                                    float              *sx,
                                                                    float              *sy);
 
+void                meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
+                                                                   float               sx,
+                                                                   float               sy,
+                                                                   float              *x,
+                                                                   float              *y);
+
 MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role);
 
 #endif
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index a878f32..3199c99 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -329,6 +329,7 @@ meta_wayland_init (void)
   meta_wayland_pointer_gestures_init (compositor);
   meta_wayland_seat_init (compositor);
   meta_wayland_relative_pointer_init (compositor);
+  meta_wayland_pointer_constraints_init (compositor);
 
   if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
     g_error ("Failed to start X Wayland");
diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
index 8129cb8..46865ed 100644
--- a/src/wayland/meta-window-wayland.c
+++ b/src/wayland/meta-window-wayland.c
@@ -30,6 +30,7 @@
 #include "window-private.h"
 #include "boxes-private.h"
 #include "stack-tracker.h"
+#include "meta-wayland-private.h"
 #include "meta-wayland-surface.h"
 #include "compositor/meta-surface-actor-wayland.h"
 
@@ -402,6 +403,11 @@ appears_focused_changed (GObject    *object,
                          gpointer    user_data)
 {
   MetaWindow *window = META_WINDOW (object);
+  MetaWaylandCompositor *wayland_compositor;
+
+  wayland_compositor = meta_wayland_compositor_get_default ();
+  meta_wayland_pointer_constraint_maybe_remove_for_seat (wayland_compositor->seat,
+                                                         window);
 
   /* When we're unmanaging, we remove focus from the window,
    * causing this to fire. Don't do anything in that case. */
@@ -409,6 +415,8 @@ appears_focused_changed (GObject    *object,
     return;
 
   surface_state_changed (window);
+
+  meta_wayland_pointer_constraint_maybe_enable_for_window (window);
 }
 
 static void


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