[mutter/wip/carlosg/input-thread: 28/101] backends: Delegate pointer confinements to an impl object




commit 8f8ea8a7166a85b8398d32d254b9a6bf01597d5b
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Jul 8 18:17:13 2020 +0200

    backends: Delegate pointer confinements to an impl object
    
    Split pointer confinements in 2 objects, one set from the upper layers
    containing its definition, and another managed by the backend that
    applies it.

 src/backends/meta-backend-private.h                |   2 +
 src/backends/meta-backend.c                        |   7 +-
 src/backends/meta-pointer-constraint.c             |  91 ++-
 src/backends/meta-pointer-constraint.h             |  49 +-
 src/backends/native/meta-backend-native.c          |  60 +-
 .../native/meta-pointer-constraint-native.c        | 693 +++++++++++++++++++
 .../native/meta-pointer-constraint-native.h        |  46 ++
 src/backends/native/meta-seat-native.c             |  57 +-
 src/backends/native/meta-seat-native.h             |  38 +-
 src/meson.build                                    |   2 +
 src/wayland/meta-pointer-confinement-wayland.c     | 755 ++++-----------------
 src/wayland/meta-pointer-confinement-wayland.h     |  23 +-
 src/wayland/meta-pointer-lock-wayland.c            |  61 +-
 src/wayland/meta-pointer-lock-wayland.h            |   6 +-
 src/wayland/meta-wayland-pointer-constraints.c     |  25 +-
 15 files changed, 1118 insertions(+), 797 deletions(-)
---
diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h
index 0b62ed4bf2..afce64fd7a 100644
--- a/src/backends/meta-backend-private.h
+++ b/src/backends/meta-backend-private.h
@@ -101,6 +101,8 @@ struct _MetaBackendClass
   void (* set_numlock) (MetaBackend *backend,
                         gboolean     numlock_state);
 
+  void (* set_pointer_constraint) (MetaBackend           *backend,
+                                   MetaPointerConstraint *constraint);
 };
 
 void meta_init_backend (GType backend_gtype);
diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c
index 1770ad795a..1e4f00a75a 100644
--- a/src/backends/meta-backend.c
+++ b/src/backends/meta-backend.c
@@ -1313,11 +1313,8 @@ meta_backend_set_client_pointer_constraint (MetaBackend           *backend,
 {
   MetaBackendPrivate *priv = meta_backend_get_instance_private (backend);
 
-  g_assert (!constraint || !priv->client_pointer_constraint);
-
-  g_clear_object (&priv->client_pointer_constraint);
-  if (constraint)
-    priv->client_pointer_constraint = g_object_ref (constraint);
+  META_BACKEND_GET_CLASS (backend)->set_pointer_constraint (backend, constraint);
+  g_set_object (&priv->client_pointer_constraint, constraint);
 }
 
 ClutterBackend *
diff --git a/src/backends/meta-pointer-constraint.c b/src/backends/meta-pointer-constraint.c
index 55ca9f0239..7682b14b05 100644
--- a/src/backends/meta-pointer-constraint.c
+++ b/src/backends/meta-pointer-constraint.c
@@ -39,10 +39,34 @@
 
 #include "backends/meta-pointer-constraint.h"
 
+#ifdef HAVE_NATIVE_BACKEND
+#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-pointer-constraint-native.h"
+#endif
+
 #include <glib-object.h>
 
+struct _MetaPointerConstraint
+{
+  GObject parent_instance;
+  cairo_region_t *region;
+};
+
 G_DEFINE_TYPE (MetaPointerConstraint, meta_pointer_constraint, G_TYPE_OBJECT);
 
+G_DEFINE_TYPE (MetaPointerConstraintImpl, meta_pointer_constraint_impl,
+               G_TYPE_OBJECT);
+
+static void
+meta_pointer_constraint_finalize (GObject *object)
+{
+  MetaPointerConstraint *constraint = META_POINTER_CONSTRAINT (object);
+
+  g_clear_pointer (&constraint->region, cairo_region_destroy);
+
+  G_OBJECT_CLASS (meta_pointer_constraint_parent_class)->finalize (object);
+}
+
 static void
 meta_pointer_constraint_init (MetaPointerConstraint *constraint)
 {
@@ -50,12 +74,43 @@ meta_pointer_constraint_init (MetaPointerConstraint *constraint)
 
 static void
 meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_pointer_constraint_finalize;
+}
+
+
+MetaPointerConstraint *
+meta_pointer_constraint_new (const cairo_region_t *region)
+{
+  MetaPointerConstraint *constraint;
+
+  constraint = g_object_new (META_TYPE_POINTER_CONSTRAINT, NULL);
+  constraint->region = cairo_region_copy (region);
+
+  return constraint;
+}
+
+cairo_region_t *
+meta_pointer_constraint_get_region (MetaPointerConstraint *constraint)
+{
+  return constraint->region;
+}
+
+static void
+meta_pointer_constraint_impl_init (MetaPointerConstraintImpl *constraint_impl)
+{
+}
+
+static void
+meta_pointer_constraint_impl_class_init (MetaPointerConstraintImplClass *klass)
 {
 }
 
 /**
- * meta_pointer_constraint_constrain:
- * @constraint: a #MetaPointerConstraint.
+ * meta_pointer_constraint_impl_constrain:
+ * @constraint_impl: a #MetaPointerConstraintImpl.
  * @device; the device of the pointer.
  * @time: the timestamp (in ms) of the event.
  * @prev_x: X-coordinate of the previous pointer position.
@@ -67,17 +122,25 @@ meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass)
  * if needed.
  */
 void
-meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
-                                   ClutterInputDevice    *device,
-                                   guint32                time,
-                                   float                  prev_x,
-                                   float                  prev_y,
-                                   float                  *x,
-                                   float                  *y)
+meta_pointer_constraint_impl_constrain (MetaPointerConstraintImpl *constraint_impl,
+                                        ClutterInputDevice        *device,
+                                        uint32_t                   time,
+                                        float                      prev_x,
+                                        float                      prev_y,
+                                        float                     *x,
+                                        float                     *y)
+{
+  META_POINTER_CONSTRAINT_IMPL_GET_CLASS (constraint_impl)->constrain (constraint_impl,
+                                                                       device,
+                                                                       time,
+                                                                       prev_x, prev_y,
+                                                                       x, y);
+}
+
+void
+meta_pointer_constraint_impl_ensure_constrained (MetaPointerConstraintImpl *constraint_impl,
+                                                 ClutterInputDevice        *device)
 {
-  META_POINTER_CONSTRAINT_GET_CLASS (constraint)->constrain (constraint,
-                                                             device,
-                                                             time,
-                                                             prev_x, prev_y,
-                                                             x, y);
+  META_POINTER_CONSTRAINT_IMPL_GET_CLASS (constraint_impl)->ensure_constrained (constraint_impl,
+                                                                                device);
 }
diff --git a/src/backends/meta-pointer-constraint.h b/src/backends/meta-pointer-constraint.h
index ed0b025b56..b47eda4903 100644
--- a/src/backends/meta-pointer-constraint.h
+++ b/src/backends/meta-pointer-constraint.h
@@ -32,34 +32,45 @@
 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);
+G_DECLARE_FINAL_TYPE (MetaPointerConstraint, meta_pointer_constraint,
+                      META, POINTER_CONSTRAINT, GObject);
+
+MetaPointerConstraint * meta_pointer_constraint_new (const cairo_region_t *region);
+cairo_region_t * meta_pointer_constraint_get_region (MetaPointerConstraint *constraint);
+
+#define META_TYPE_POINTER_CONSTRAINT_IMPL (meta_pointer_constraint_impl_get_type ())
+G_DECLARE_DERIVABLE_TYPE (MetaPointerConstraintImpl, meta_pointer_constraint_impl,
+                          META, POINTER_CONSTRAINT_IMPL, GObject);
 
 /**
- * MetaPointerConstraintClass:
+ * MetaPointerConstraintImplClass:
  * @constrain: the virtual function pointer for
- *             meta_pointer_constraint_constrain().
+ *             meta_pointer_constraint_impl_constrain().
  */
-struct _MetaPointerConstraintClass
+struct _MetaPointerConstraintImplClass
 {
   GObjectClass parent_class;
 
-  void (*constrain) (MetaPointerConstraint *constraint,
-                     ClutterInputDevice *device,
-                     guint32 time,
-                     float prev_x,
-                     float prev_y,
-                     float *x,
-                     float *y);
+  void (* constrain) (MetaPointerConstraintImpl *constraint_impl,
+                      ClutterInputDevice        *device,
+                      uint32_t                   time,
+                      float                      prev_x,
+                      float                      prev_y,
+                      float                     *x,
+                      float                     *y);
+  void (* ensure_constrained) (MetaPointerConstraintImpl *constraint_impl,
+                               ClutterInputDevice        *device);
 };
 
-void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
-                                        ClutterInputDevice    *device,
-                                        guint32                time,
-                                        float                  prev_x,
-                                        float                  prev_y,
-                                        float                 *x,
-                                        float                 *y);
+void meta_pointer_constraint_impl_constrain (MetaPointerConstraintImpl *constraint_impl,
+                                             ClutterInputDevice        *device,
+                                             uint32_t                   time,
+                                             float                      prev_x,
+                                             float                      prev_y,
+                                             float                     *x,
+                                             float                     *y);
+void meta_pointer_constraint_impl_ensure_constrained (MetaPointerConstraintImpl *constraint_impl,
+                                                      ClutterInputDevice        *device);
 
 G_END_DECLS
 
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 18709d5a58..94fce30b38 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -105,38 +105,6 @@ meta_backend_native_finalize (GObject *object)
   G_OBJECT_CLASS (meta_backend_native_parent_class)->finalize (object);
 }
 
-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 =
-    meta_backend_get_client_pointer_constraint (backend);
-
-  if (!constraint)
-    return;
-
-  meta_pointer_constraint_constrain (constraint, device,
-                                     time, prev_x, prev_y, x, y);
-}
-
-static void
-pointer_constrain_callback (ClutterInputDevice *device,
-                            guint32             time,
-                            float               prev_x,
-                            float               prev_y,
-                            float              *new_x,
-                            float              *new_y,
-                            gpointer            user_data)
-{
-  /* Constrain to pointer lock */
-  constrain_to_client_constraint (device, time, prev_x, prev_y, new_x, new_y);
-}
-
 static ClutterBackend *
 meta_backend_native_create_clutter_backend (MetaBackend *backend)
 {
@@ -181,14 +149,8 @@ maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native)
 static void
 meta_backend_native_post_init (MetaBackend *backend)
 {
-  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
-  ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
   MetaSettings *settings = meta_backend_get_settings (backend);
 
-  meta_seat_native_set_pointer_constrain_callback (META_SEAT_NATIVE (seat),
-                                                   pointer_constrain_callback,
-                                                   NULL, NULL);
-
   META_BACKEND_CLASS (meta_backend_native_parent_class)->post_init (backend);
 
   if (meta_settings_is_experimental_feature_enabled (settings,
@@ -341,6 +303,26 @@ meta_backend_native_set_numlock (MetaBackend *backend,
                                          numlock_state);
 }
 
+static void
+meta_backend_native_set_pointer_constraint (MetaBackend           *backend,
+                                            MetaPointerConstraint *constraint)
+{
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
+  MetaPointerConstraintImpl *constraint_impl = NULL;
+  cairo_region_t *region;
+
+  if (constraint)
+    {
+      region = meta_pointer_constraint_get_region (constraint);
+      constraint_impl = meta_pointer_constraint_impl_native_new (constraint,
+                                                                 region);
+    }
+
+  meta_seat_native_set_pointer_constraint (META_SEAT_NATIVE (seat),
+                                           constraint_impl);
+}
+
 static void
 meta_backend_native_update_screen_size (MetaBackend *backend,
                                         int width, int height)
@@ -554,6 +536,8 @@ meta_backend_native_class_init (MetaBackendNativeClass *klass)
   backend_class->lock_layout_group = meta_backend_native_lock_layout_group;
   backend_class->update_screen_size = meta_backend_native_update_screen_size;
   backend_class->set_numlock = meta_backend_native_set_numlock;
+
+  backend_class->set_pointer_constraint = meta_backend_native_set_pointer_constraint;
 }
 
 static void
diff --git a/src/backends/native/meta-pointer-constraint-native.c 
b/src/backends/native/meta-pointer-constraint-native.c
new file mode 100644
index 0000000000..8ce678ae4a
--- /dev/null
+++ b/src/backends/native/meta-pointer-constraint-native.c
@@ -0,0 +1,693 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2015-2020 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 <glib-object.h>
+#include <wayland-server.h>
+
+#include "core/meta-border.h"
+#include "meta-pointer-constraint-native.h"
+
+struct _MetaPointerConstraintImplNative
+{
+  MetaPointerConstraintImpl parent;
+  MetaPointerConstraint *constraint;
+  cairo_region_t *region;
+};
+
+G_DEFINE_TYPE (MetaPointerConstraintImplNative,
+               meta_pointer_constraint_impl_native,
+               META_TYPE_POINTER_CONSTRAINT_IMPL);
+
+typedef struct _MetaBox
+{
+  int x1;
+  int y1;
+  int x2;
+  int y2;
+} MetaBox;
+
+static MetaBorder *
+add_border (GArray                    *borders,
+            float                      x1,
+            float                      y1,
+            float                      x2,
+            float                      y2,
+            MetaBorderMotionDirection  blocking_directions)
+{
+  MetaBorder border;
+
+  border = (MetaBorder) {
+    .line = (MetaLine2) {
+      .a = (MetaVector2) {
+        .x = x1,
+        .y = y1,
+      },
+      .b = (MetaVector2) {
+        .x = x2,
+        .y = y2,
+      },
+    },
+    .blocking_directions = blocking_directions,
+  };
+
+  g_array_append_val (borders, border);
+
+  return &g_array_index (borders, MetaBorder, borders->len - 1);
+}
+
+static gint
+compare_lines_x (gconstpointer a,
+                 gconstpointer b)
+{
+  const MetaBorder *border_a = a;
+  const MetaBorder *border_b = b;
+
+  if (border_a->line.a.x == border_b->line.a.x)
+    return border_a->line.b.x < border_b->line.b.x;
+  else
+    return border_a->line.a.x > border_b->line.a.x;
+}
+
+static void
+add_non_overlapping_edges (MetaBox      *boxes,
+                           unsigned int  band_above_start,
+                           unsigned int  band_below_start,
+                           unsigned int  band_below_end,
+                           GArray       *borders)
+{
+  unsigned int i;
+  GArray *band_merge;
+  MetaBorder *border;
+  MetaBorder *prev_border;
+  MetaBorder *new_border;
+
+  band_merge = g_array_new (FALSE, FALSE, sizeof *border);
+
+  /* Add bottom band of previous row, and top band of current row, and
+   * sort them so lower left x coordinate comes first. If there are two
+   * borders with the same left x coordinate, the wider one comes first.
+   */
+  for (i = band_above_start; i < band_below_start; i++)
+    {
+      MetaBox *box = &boxes[i];
+      add_border (band_merge, box->x1, box->y2, box->x2, box->y2,
+                  META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
+    }
+  for (i = band_below_start; i < band_below_end; i++)
+    {
+      MetaBox *box= &boxes[i];
+      add_border (band_merge, box->x1, box->y1, box->x2, box->y1,
+                  META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
+    }
+  g_array_sort (band_merge, compare_lines_x);
+
+  /* Combine the two combined bands so that any overlapping border is
+   * eliminated. */
+  prev_border = NULL;
+  for (i = 0; i < band_merge->len; i++)
+    {
+      border = &g_array_index (band_merge, MetaBorder, i);
+
+      g_assert (border->line.a.y == border->line.b.y);
+      g_assert (!prev_border ||
+                prev_border->line.a.y == border->line.a.y);
+      g_assert (!prev_border ||
+                (prev_border->line.a.x != border->line.a.x ||
+                 prev_border->line.b.x != border->line.b.x));
+      g_assert (!prev_border ||
+                prev_border->line.a.x <= border->line.a.x);
+
+      if (prev_border &&
+          prev_border->line.a.x == border->line.a.x)
+        {
+          /*
+           * ------------ +
+           * -------      =
+           * [     ]-----
+           */
+          prev_border->line.a.x = border->line.b.x;
+        }
+      else if (prev_border &&
+               prev_border->line.b.x == border->line.b.x)
+        {
+          /*
+           * ------------ +
+           *       ------ =
+           * ------[    ]
+           */
+          prev_border->line.b.x = border->line.a.x;
+        }
+      else if (prev_border &&
+               prev_border->line.b.x == border->line.a.x)
+        {
+          /*
+           * --------        +
+           *         ------  =
+           * --------------
+           */
+          prev_border->line.b.x = border->line.b.x;
+        }
+      else if (prev_border &&
+               prev_border->line.b.x >= border->line.a.x)
+        {
+          /*
+           * --------------- +
+           *      ------     =
+           * -----[    ]----
+           */
+          new_border = add_border (borders,
+                                   border->line.b.x,
+                                   border->line.b.y,
+                                   prev_border->line.b.x,
+                                   prev_border->line.b.y,
+                                   prev_border->blocking_directions);
+          prev_border->line.b.x = border->line.a.x;
+          prev_border = new_border;
+        }
+      else
+        {
+          g_assert (!prev_border ||
+                    prev_border->line.b.x < border->line.a.x);
+          /*
+           * First border or non-overlapping.
+           *
+           * -----           +
+           *        -----    =
+           * -----  -----
+           */
+          g_array_append_val (borders, *border);
+          prev_border = &g_array_index (borders, MetaBorder, borders->len - 1);
+        }
+    }
+
+  g_array_free (band_merge, FALSE);
+}
+
+static void
+add_band_bottom_edges (MetaBox *boxes,
+                       int      band_start,
+                       int      band_end,
+                       GArray  *borders)
+{
+  int i;
+
+  for (i = band_start; i < band_end; i++)
+    {
+      add_border (borders,
+                  boxes[i].x1, boxes[i].y2,
+                  boxes[i].x2, boxes[i].y2,
+                  META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
+    }
+}
+
+static void
+region_to_outline (cairo_region_t *region,
+                   GArray         *borders)
+{
+  MetaBox *boxes;
+  int num_boxes;
+  int i;
+  int top_most, bottom_most;
+  int current_roof;
+  int prev_top;
+  int band_start, prev_band_start;
+
+  /*
+   * Remove any overlapping lines from the set of rectangles. Note that
+   * pixman regions are grouped as rows of rectangles, where rectangles
+   * in one row never touch or overlap and are all of the same height.
+   *
+   *             -------- ---                   -------- ---
+   *             |      | | |                   |      | | |
+   *   ----------====---- ---         -----------  ----- ---
+   *   |            |            =>   |            |
+   *   ----==========---------        -----        ----------
+   *       |                 |            |                 |
+   *       -------------------            -------------------
+   *
+   */
+
+  num_boxes  = cairo_region_num_rectangles (region);
+  boxes = g_new (MetaBox, num_boxes);
+  for (i = 0; i < num_boxes; i++)
+    {
+      cairo_rectangle_int_t rect;
+      cairo_region_get_rectangle (region, i, &rect);
+      boxes[i] = (MetaBox) {
+        .x1 = rect.x,
+        .y1 = rect.y,
+        .x2 = rect.x + rect.width,
+        .y2 = rect.y + rect.height,
+      };
+    }
+  prev_top = 0;
+  top_most = boxes[0].y1;
+  current_roof = top_most;
+  bottom_most = boxes[num_boxes - 1].y2;
+  band_start = 0;
+  prev_band_start = 0;
+  for (i = 0; i < num_boxes; i++)
+    {
+      /* Detect if there is a vertical empty space, and add the lower
+       * level of the previous band if so was the case. */
+      if (i > 0 &&
+          boxes[i].y1 != prev_top &&
+          boxes[i].y1 != boxes[i - 1].y2)
+        {
+          current_roof = boxes[i].y1;
+          add_band_bottom_edges (boxes,
+                                 band_start,
+                                 i,
+                                 borders);
+        }
+
+      /* Special case adding the last band, since it won't be handled
+       * by the band change detection below. */
+      if (boxes[i].y1 != current_roof && i == num_boxes - 1)
+        {
+          if (boxes[i].y1 != prev_top)
+            {
+              /* The last band is a single box, so we don't
+               * have a prev_band_start to tell us when the
+               * previous band started. */
+              add_non_overlapping_edges (boxes,
+                                         band_start,
+                                         i,
+                                         i + 1,
+                                         borders);
+            }
+          else
+            {
+              add_non_overlapping_edges (boxes,
+                                         prev_band_start,
+                                         band_start,
+                                         i + 1,
+                                         borders);
+            }
+        }
+
+      /* Detect when passing a band and combine the top border of the
+       * just passed band with the bottom band of the previous band.
+       */
+      if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top)
+        {
+          /* Combine the two passed bands. */
+          if (prev_top != current_roof)
+            {
+              add_non_overlapping_edges (boxes,
+                                         prev_band_start,
+                                         band_start,
+                                         i,
+                                         borders);
+            }
+
+          prev_band_start = band_start;
+          band_start = i;
+        }
+
+      /* Add the top border if the box is part of the current roof. */
+      if (boxes[i].y1 == current_roof)
+        {
+          add_border (borders,
+                      boxes[i].x1, boxes[i].y1,
+                      boxes[i].x2, boxes[i].y1,
+                      META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
+        }
+
+      /* Add the bottom border of the last band. */
+      if (boxes[i].y2 == bottom_most)
+        {
+          add_border (borders,
+                      boxes[i].x1, boxes[i].y2,
+                      boxes[i].x2, boxes[i].y2,
+                      META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
+        }
+
+      /* Always add the left border. */
+      add_border (borders,
+                  boxes[i].x1, boxes[i].y1,
+                  boxes[i].x1, boxes[i].y2,
+                  META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
+
+      /* Always add the right border. */
+      add_border (borders,
+                  boxes[i].x2, boxes[i].y1,
+                  boxes[i].x2, boxes[i].y2,
+                  META_BORDER_MOTION_DIRECTION_POSITIVE_X);
+
+      prev_top = boxes[i].y1;
+    }
+
+  g_free (boxes);
+}
+
+static MetaBorder *
+get_closest_border (GArray    *borders,
+                    MetaLine2 *motion,
+                    uint32_t   directions)
+{
+  MetaBorder *border;
+  MetaVector2 intersection;
+  MetaVector2 delta;
+  float distance_2;
+  MetaBorder *closest_border = NULL;
+  float closest_distance_2 = DBL_MAX;
+  unsigned int i;
+
+  for (i = 0; i < borders->len; i++)
+    {
+      border = &g_array_index (borders, MetaBorder, i);
+
+      if (!meta_border_is_blocking_directions (border, directions))
+        continue;
+
+      if (!meta_line2_intersects_with (&border->line, motion, &intersection))
+        continue;
+
+      delta = meta_vector2_subtract (intersection, motion->a);
+      distance_2 = delta.x*delta.x + delta.y*delta.y;
+      if (distance_2 < closest_distance_2)
+        {
+          closest_border = border;
+          closest_distance_2 = distance_2;
+        }
+    }
+
+  return closest_border;
+}
+
+static void
+clamp_to_border (MetaBorder *border,
+                 MetaLine2  *motion,
+                 uint32_t   *motion_dir)
+{
+  /*
+   * When clamping either rightward or downward motions, the motion needs to be
+   * clamped so that the destination coordinate does not end up on the border
+   * (see weston_pointer_clamp_event_to_region). Do this by clamping such
+   * motions to the border minus the smallest possible wl_fixed_t value.
+   *
+   * When clamping in either leftward or upward motion, the resulting coordinate
+   * needs to be clamped so that it is enough on the inside to avoid the
+   * inaccuracies of clutter's stage to actor transformation algorithm (the one
+   * used in clutter_actor_transform_stage_point) to make it end up outside the
+   * next motion. It also needs to be clamped so that to the wl_fixed_t
+   * coordinate may still be right on the border (i.e. at .0). Testing shows
+   * that the smallest wl_fixed_t value divided by 10 is small enough to make
+   * the wl_fixed_t coordinate .0 and large enough to avoid the inaccuracies of
+   * clutters transform algorithm.
+   */
+  if (meta_border_is_horizontal (border))
+    {
+      if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
+        motion->b.y = border->line.a.y - wl_fixed_to_double (1);
+      else
+        motion->b.y = border->line.a.y + wl_fixed_to_double (1) / 10;
+      *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
+                       META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
+    }
+  else
+    {
+      if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_X)
+        motion->b.x = border->line.a.x - wl_fixed_to_double (1);
+      else
+        motion->b.x = border->line.a.x + wl_fixed_to_double (1) / 10;
+      *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_X |
+                       META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
+    }
+}
+
+static uint32_t
+get_motion_directions (MetaLine2 *motion)
+{
+  uint32_t directions = 0;
+
+  if (motion->a.x < motion->b.x)
+    directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_X;
+  else if (motion->a.x > motion->b.x)
+    directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_X;
+  if (motion->a.y < motion->b.y)
+    directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_Y;
+  else if (motion->a.y > motion->b.y)
+    directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_Y;
+
+  return directions;
+}
+
+static void
+meta_pointer_constraint_impl_native_constraint (MetaPointerConstraintImpl *constraint_impl,
+                                                ClutterInputDevice        *device,
+                                                uint32_t                   time,
+                                                float                      prev_x,
+                                                float                      prev_y,
+                                                float                     *x_inout,
+                                                float                     *y_inout)
+{
+  MetaPointerConstraintImplNative *constraint_impl_native;
+  cairo_region_t *region;
+  float x, y;
+  GArray *borders;
+  MetaLine2 motion;
+  MetaBorder *closest_border;
+  uint32_t directions;
+
+  constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (constraint_impl);
+
+  region = cairo_region_reference (constraint_impl_native->region);
+  x = *x_inout;
+  y = *y_inout;
+
+  /* For motions in a positive direction on any axis, append the smallest
+   * possible value representable in a Wayland absolute coordinate.  This is
+   * in order to avoid not clamping motion that as a floating point number
+   * won't be clamped, but will be rounded up to be outside of the range
+   * of wl_fixed_t. */
+  if (x > prev_x)
+    x += (float) wl_fixed_to_double(1);
+  if (y > prev_y)
+    y += (float) wl_fixed_to_double(1);
+
+  borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
+
+  /*
+   * Generate borders given the confine region we are to use. The borders
+   * are defined to be the outer region of the allowed area. This means
+   * top/left borders are "within" the allowed area, while bottom/right
+   * borders are outside. This needs to be considered when clamping
+   * confined motion vectors.
+   */
+  region_to_outline (region, borders);
+  cairo_region_destroy (region);
+
+  motion = (MetaLine2) {
+    .a = (MetaVector2) {
+      .x = prev_x,
+      .y = prev_y,
+    },
+    .b = (MetaVector2) {
+      .x = x,
+      .y = y,
+    },
+  };
+  directions = get_motion_directions (&motion);
+
+  while (directions)
+    {
+      closest_border = get_closest_border (borders,
+                                           &motion,
+                                           directions);
+      if (closest_border)
+        clamp_to_border (closest_border, &motion, &directions);
+      else
+        break;
+    }
+
+  *x_inout = motion.b.x;
+  *y_inout = motion.b.y;
+  g_array_free (borders, FALSE);
+}
+
+static float
+point_to_border_distance_2 (MetaBorder *border,
+                            float       x,
+                            float       y)
+{
+  float orig_x, orig_y;
+  float dx, dy;
+
+  if (meta_border_is_horizontal (border))
+    {
+      if (x < border->line.a.x)
+        orig_x = border->line.a.x;
+      else if (x > border->line.b.x)
+        orig_x = border->line.b.x;
+      else
+        orig_x = x;
+      orig_y = border->line.a.y;
+    }
+  else
+    {
+      if (y < border->line.a.y)
+        orig_y = border->line.a.y;
+      else if (y > border->line.b.y)
+        orig_y = border->line.b.y;
+      else
+        orig_y = y;
+      orig_x = border->line.a.x;
+    }
+
+  dx = fabsf (orig_x - x);
+  dy = fabsf (orig_y - y);
+  return dx*dx + dy*dy;
+}
+
+static void
+closest_point_behind_border (MetaBorder *border,
+                             float      *sx,
+                             float      *sy)
+{
+  switch (border->blocking_directions)
+    {
+    case META_BORDER_MOTION_DIRECTION_POSITIVE_X:
+    case META_BORDER_MOTION_DIRECTION_NEGATIVE_X:
+      if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_X)
+        *sx = border->line.a.x - wl_fixed_to_double (1);
+      else
+        *sx = border->line.a.x + wl_fixed_to_double (1);
+      if (*sy < border->line.a.y)
+        *sy = border->line.a.y + wl_fixed_to_double (1);
+      else if (*sy > border->line.b.y)
+        *sy = border->line.b.y - wl_fixed_to_double (1);
+      break;
+    case META_BORDER_MOTION_DIRECTION_POSITIVE_Y:
+    case META_BORDER_MOTION_DIRECTION_NEGATIVE_Y:
+      if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
+        *sy = border->line.a.y - wl_fixed_to_double (1);
+      else
+        *sy = border->line.a.y + wl_fixed_to_double (1);
+      if (*sx < border->line.a.x)
+        *sx = border->line.a.x + wl_fixed_to_double (1);
+      else if (*sx > (border->line.b.x))
+        *sx = border->line.b.x - wl_fixed_to_double (1);
+      break;
+    }
+}
+
+static void
+meta_pointer_constraint_impl_native_ensure_constrained (MetaPointerConstraintImpl *constraint_impl,
+                                                        ClutterInputDevice        *device)
+{
+  MetaPointerConstraintImplNative *constraint_impl_native;
+  graphene_point_t point;
+  cairo_region_t *region;
+  float x;
+  float y;
+
+  constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (constraint_impl);
+  region = cairo_region_reference (constraint_impl_native->region);
+
+  clutter_input_device_get_coords (device, NULL, &point);
+  x = point.x;
+  y = point.y;
+
+  if (!cairo_region_contains_point (region, (int) x, (int) y))
+    {
+      GArray *borders;
+      float closest_distance_2 = FLT_MAX;
+      MetaBorder *closest_border = NULL;
+      ClutterSeat *seat;
+      unsigned int i;
+
+      borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
+
+      region_to_outline (region, borders);
+
+      for (i = 0; i < borders->len; i++)
+        {
+          MetaBorder *border = &g_array_index (borders, MetaBorder, i);
+          float distance_2;
+
+          distance_2 = point_to_border_distance_2 (border, x, y);
+          if (distance_2 < closest_distance_2)
+            {
+              closest_border = border;
+              closest_distance_2 = distance_2;
+            }
+        }
+
+      closest_point_behind_border (closest_border, &x, &y);
+
+      seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
+      clutter_seat_warp_pointer (seat, x, y);
+    }
+
+  cairo_region_destroy (region);
+}
+
+static void
+meta_pointer_constraint_impl_native_finalize (GObject *object)
+{
+  MetaPointerConstraintImplNative *constraint_impl_native;
+
+  constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (object);
+  g_clear_pointer (&constraint_impl_native->region, cairo_region_destroy);
+
+  G_OBJECT_CLASS (meta_pointer_constraint_impl_native_parent_class)->finalize (object);
+}
+
+static void
+meta_pointer_constraint_impl_native_init (MetaPointerConstraintImplNative *constraint_impl_native)
+{
+}
+
+static void
+meta_pointer_constraint_impl_native_class_init (MetaPointerConstraintImplNativeClass *klass)
+{
+  MetaPointerConstraintImplClass *constraint_impl_class;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_pointer_constraint_impl_native_finalize;
+
+  constraint_impl_class = META_POINTER_CONSTRAINT_IMPL_CLASS (klass);
+  constraint_impl_class->constrain = meta_pointer_constraint_impl_native_constraint;
+  constraint_impl_class->ensure_constrained =
+    meta_pointer_constraint_impl_native_ensure_constrained;
+}
+
+
+MetaPointerConstraintImpl *
+meta_pointer_constraint_impl_native_new (MetaPointerConstraint *constraint,
+                                         const cairo_region_t  *region)
+{
+  MetaPointerConstraintImplNative *constraint_impl;
+
+  constraint_impl = g_object_new (META_TYPE_POINTER_CONSTRAINT_IMPL_NATIVE,
+                                  NULL);
+  constraint_impl->constraint = constraint;
+  constraint_impl->region = cairo_region_copy (region);
+
+  return META_POINTER_CONSTRAINT_IMPL (constraint_impl);
+}
diff --git a/src/backends/native/meta-pointer-constraint-native.h 
b/src/backends/native/meta-pointer-constraint-native.h
new file mode 100644
index 0000000000..83a2e575d5
--- /dev/null
+++ b/src/backends/native/meta-pointer-constraint-native.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2020 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:
+ *     Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef META_POINTER_CONSTRAINT_NATIVE_H
+#define META_POINTER_CONSTRAINT_NATIVE_H
+
+#include <glib-object.h>
+
+#include "clutter/clutter.h"
+#include "backends/meta-pointer-constraint.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_POINTER_CONSTRAINT_IMPL_NATIVE (meta_pointer_constraint_impl_native_get_type ())
+G_DECLARE_FINAL_TYPE (MetaPointerConstraintImplNative,
+                      meta_pointer_constraint_impl_native,
+                      META, POINTER_CONSTRAINT_IMPL_NATIVE,
+                      MetaPointerConstraintImpl)
+
+MetaPointerConstraintImpl * meta_pointer_constraint_impl_native_new (MetaPointerConstraint *constraint_impl,
+                                                                     const cairo_region_t  *region);
+
+G_END_DECLS
+
+#endif /* META_POINTER_CONSTRAINT_NATIVE_H */
diff --git a/src/backends/native/meta-seat-native.c b/src/backends/native/meta-seat-native.c
index 6d7db96cd1..bf62788789 100644
--- a/src/backends/native/meta-seat-native.c
+++ b/src/backends/native/meta-seat-native.c
@@ -934,13 +934,14 @@ meta_seat_native_constrain_pointer (MetaSeatNative     *seat,
                          us2ms (time_us),
                          new_x, new_y);
 
-  if (seat->constrain_callback)
+  /* Bar to constraints */
+  if (seat->pointer_constraint)
     {
-      seat->constrain_callback (core_pointer,
-                                us2ms (time_us),
-                                x, y,
-                                new_x, new_y,
-                                seat->constrain_data);
+      meta_pointer_constraint_impl_constrain (seat->pointer_constraint,
+                                              core_pointer,
+                                              us2ms (time_us),
+                                              x, y,
+                                              new_x, new_y);
     }
 
   /* if we're moving inside a monitor, we're fine */
@@ -2642,9 +2643,6 @@ meta_seat_native_finalize (GObject *object)
 
   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);
@@ -3028,33 +3026,6 @@ meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback  open_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_update_xkb_state (MetaSeatNative *seat)
 {
@@ -3302,3 +3273,17 @@ meta_seat_native_get_barrier_manager (MetaSeatNative *seat)
 {
   return seat->barrier_manager;
 }
+
+void
+meta_seat_native_set_pointer_constraint (MetaSeatNative            *seat,
+                                         MetaPointerConstraintImpl *constraint_impl)
+{
+  if (!g_set_object (&seat->pointer_constraint, constraint_impl))
+    return;
+
+  if (constraint_impl)
+    {
+      meta_pointer_constraint_impl_ensure_constrained (constraint_impl,
+                                                       seat->core_pointer);
+    }
+}
diff --git a/src/backends/native/meta-seat-native.h b/src/backends/native/meta-seat-native.h
index 0cc353e7d8..2a1faaee00 100644
--- a/src/backends/native/meta-seat-native.h
+++ b/src/backends/native/meta-seat-native.h
@@ -29,6 +29,7 @@
 
 #include "backends/native/meta-barrier-native.h"
 #include "backends/native/meta-keymap-native.h"
+#include "backends/native/meta-pointer-constraint-native.h"
 #include "backends/native/meta-xkb-utils.h"
 #include "clutter/clutter.h"
 
@@ -36,30 +37,6 @@ 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);
-
 struct _MetaTouchState
 {
   MetaSeatNative *seat;
@@ -104,10 +81,7 @@ struct _MetaSeatNative
   GList *free_device_ids;
 
   MetaBarrierManagerNative *barrier_manager;
-
-  MetaPointerConstrainCallback constrain_callback;
-  gpointer constrain_data;
-  GDestroyNotify constrain_data_notify;
+  MetaPointerConstraintImpl *pointer_constraint;
 
   MetaKeymapNative *keymap;
 
@@ -256,11 +230,6 @@ void  meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback  open_callba
 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);
-
 struct xkb_state * meta_seat_native_get_xkb_state (MetaSeatNative *seat);
 
 void               meta_seat_native_set_keyboard_map   (MetaSeatNative    *seat,
@@ -286,4 +255,7 @@ void meta_seat_native_release_touch_slots (MetaSeatNative *seat,
 
 MetaBarrierManagerNative * meta_seat_native_get_barrier_manager (MetaSeatNative *seat);
 
+void meta_seat_native_set_pointer_constraint (MetaSeatNative            *seat,
+                                              MetaPointerConstraintImpl *constraint_impl);
+
 #endif /* META_SEAT_NATIVE_H */
diff --git a/src/meson.build b/src/meson.build
index cc8527beab..df8667f5c2 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -697,6 +697,8 @@ if have_native_backend
     'backends/native/meta-kms-utils.h',
     'backends/native/meta-kms.c',
     'backends/native/meta-kms.h',
+    'backends/native/meta-pointer-constraint-native.c',
+    'backends/native/meta-pointer-constraint-native.h',
     'backends/native/meta-renderer-native-gles3.c',
     'backends/native/meta-renderer-native-gles3.h',
     'backends/native/meta-renderer-native.h',
diff --git a/src/wayland/meta-pointer-confinement-wayland.c b/src/wayland/meta-pointer-confinement-wayland.c
index 7f980054c7..3df6ed0ab3 100644
--- a/src/wayland/meta-pointer-confinement-wayland.c
+++ b/src/wayland/meta-pointer-confinement-wayland.c
@@ -41,698 +41,227 @@
 
 #include "backends/meta-backend-private.h"
 #include "backends/meta-pointer-constraint.h"
-#include "compositor/meta-surface-actor-wayland.h"
-#include "core/meta-border.h"
 #include "wayland/meta-wayland-pointer-constraints.h"
 #include "wayland/meta-wayland-pointer.h"
 #include "wayland/meta-wayland-seat.h"
 #include "wayland/meta-wayland-surface.h"
 
-struct _MetaPointerConfinementWayland
-{
-  MetaPointerConstraint parent;
+typedef struct _MetaPointerConfinementWaylandPrivate MetaPointerConfinementWaylandPrivate;
 
+struct _MetaPointerConfinementWaylandPrivate
+{
   MetaWaylandPointerConstraint *constraint;
+  gboolean enabled;
 };
 
-typedef struct _MetaBox
-{
-  int x1;
-  int y1;
-  int x2;
-  int y2;
-} MetaBox;
-
-G_DEFINE_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland,
-               META_TYPE_POINTER_CONSTRAINT);
-
-static MetaBorder *
-add_border (GArray *borders,
-            float x1, float y1,
-            float x2, float y2,
-            MetaBorderMotionDirection blocking_directions)
+enum
 {
-  MetaBorder border;
-
-  border = (MetaBorder) {
-    .line = (MetaLine2) {
-      .a = (MetaVector2) {
-        .x = x1,
-        .y = y1,
-      },
-      .b = (MetaVector2) {
-        .x = x2,
-        .y = y2,
-      },
-    },
-    .blocking_directions = blocking_directions,
-  };
-
-  g_array_append_val (borders, border);
-
-  return &g_array_index (borders, MetaBorder, borders->len - 1);
-}
+  PROP_0,
+  PROP_WAYLAND_POINTER_CONSTRAINT,
+  N_PROPS,
+};
 
-static gint
-compare_lines_x (gconstpointer a, gconstpointer b)
-{
-  const MetaBorder *border_a = a;
-  const MetaBorder *border_b = b;
+static GParamSpec *props[N_PROPS] = { 0 };
 
-  if (border_a->line.a.x == border_b->line.a.x)
-    return border_a->line.b.x < border_b->line.b.x;
-  else
-    return border_a->line.a.x > border_b->line.a.x;
-}
+G_DEFINE_TYPE_WITH_PRIVATE (MetaPointerConfinementWayland,
+                            meta_pointer_confinement_wayland,
+                            G_TYPE_OBJECT)
 
 static void
-add_non_overlapping_edges (MetaBox     *boxes,
-                           unsigned int band_above_start,
-                           unsigned int band_below_start,
-                           unsigned int band_below_end,
-                           GArray      *borders)
+meta_pointer_confinement_wayland_update (MetaPointerConfinementWayland *self)
 {
-  unsigned int i;
-  GArray *band_merge;
-  MetaBorder *border;
-  MetaBorder *prev_border;
-  MetaBorder *new_border;
-
-  band_merge = g_array_new (FALSE, FALSE, sizeof *border);
-
-  /* Add bottom band of previous row, and top band of current row, and
-   * sort them so lower left x coordinate comes first. If there are two
-   * borders with the same left x coordinate, the wider one comes first.
-   */
-  for (i = band_above_start; i < band_below_start; i++)
-    {
-      MetaBox *box = &boxes[i];
-      add_border (band_merge, box->x1, box->y2, box->x2, box->y2,
-                  META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
-    }
-  for (i = band_below_start; i < band_below_end; i++)
-    {
-      MetaBox *box= &boxes[i];
-      add_border (band_merge, box->x1, box->y1, box->x2, box->y1,
-                  META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
-    }
-  g_array_sort (band_merge, compare_lines_x);
-
-  /* Combine the two combined bands so that any overlapping border is
-   * eliminated. */
-  prev_border = NULL;
-  for (i = 0; i < band_merge->len; i++)
-    {
-      border = &g_array_index (band_merge, MetaBorder, i);
-
-      g_assert (border->line.a.y == border->line.b.y);
-      g_assert (!prev_border ||
-                prev_border->line.a.y == border->line.a.y);
-      g_assert (!prev_border ||
-                (prev_border->line.a.x != border->line.a.x ||
-                 prev_border->line.b.x != border->line.b.x));
-      g_assert (!prev_border ||
-                prev_border->line.a.x <= border->line.a.x);
-
-      if (prev_border &&
-          prev_border->line.a.x == border->line.a.x)
-        {
-          /*
-           * ------------ +
-           * -------      =
-           * [     ]-----
-           */
-          prev_border->line.a.x = border->line.b.x;
-        }
-      else if (prev_border &&
-               prev_border->line.b.x == border->line.b.x)
-        {
-          /*
-           * ------------ +
-           *       ------ =
-           * ------[    ]
-           */
-          prev_border->line.b.x = border->line.a.x;
-        }
-      else if (prev_border &&
-               prev_border->line.b.x == border->line.a.x)
-        {
-          /*
-           * --------        +
-           *         ------  =
-           * --------------
-           */
-          prev_border->line.b.x = border->line.b.x;
-        }
-      else if (prev_border &&
-               prev_border->line.b.x >= border->line.a.x)
-        {
-          /*
-           * --------------- +
-           *      ------     =
-           * -----[    ]----
-           */
-          new_border = add_border (borders,
-                                   border->line.b.x,
-                                   border->line.b.y,
-                                   prev_border->line.b.x,
-                                   prev_border->line.b.y,
-                                   prev_border->blocking_directions);
-          prev_border->line.b.x = border->line.a.x;
-          prev_border = new_border;
-        }
-      else
-        {
-          g_assert (!prev_border ||
-                    prev_border->line.b.x < border->line.a.x);
-          /*
-           * First border or non-overlapping.
-           *
-           * -----           +
-           *        -----    =
-           * -----  -----
-           */
-          g_array_append_val (borders, *border);
-          prev_border = &g_array_index (borders, MetaBorder, borders->len - 1);
-        }
-    }
+  MetaPointerConstraint *constraint;
 
-  g_array_free (band_merge, FALSE);
+  constraint =
+    META_POINTER_CONFINEMENT_WAYLAND_GET_CLASS (self)->create_constraint (self);
+  meta_backend_set_client_pointer_constraint (meta_get_backend (), constraint);
+  g_object_unref (constraint);
 }
 
 static void
-add_band_bottom_edges (MetaBox *boxes,
-                       int      band_start,
-                       int      band_end,
-                       GArray  *borders)
+surface_geometry_changed (MetaWaylandSurface            *surface,
+                          MetaPointerConfinementWayland *self)
 {
-  int i;
-
-  for (i = band_start; i < band_end; i++)
-    {
-      add_border (borders,
-                  boxes[i].x1, boxes[i].y2,
-                  boxes[i].x2, boxes[i].y2,
-                  META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
-    }
+  meta_pointer_confinement_wayland_update (self);
 }
 
 static void
-region_to_outline (cairo_region_t *region,
-                   GArray         *borders)
+window_position_changed (MetaWindow                    *window,
+                         MetaPointerConfinementWayland *self)
 {
-  MetaBox *boxes;
-  int num_boxes;
-  int i;
-  int top_most, bottom_most;
-  int current_roof;
-  int prev_top;
-  int band_start, prev_band_start;
-
-  /*
-   * Remove any overlapping lines from the set of rectangles. Note that
-   * pixman regions are grouped as rows of rectangles, where rectangles
-   * in one row never touch or overlap and are all of the same height.
-   *
-   *             -------- ---                   -------- ---
-   *             |      | | |                   |      | | |
-   *   ----------====---- ---         -----------  ----- ---
-   *   |            |            =>   |            |
-   *   ----==========---------        -----        ----------
-   *       |                 |            |                 |
-   *       -------------------            -------------------
-   *
-   */
-
-  num_boxes  = cairo_region_num_rectangles (region);
-  boxes = g_new (MetaBox, num_boxes);
-  for (i = 0; i < num_boxes; i++)
-    {
-      cairo_rectangle_int_t rect;
-      cairo_region_get_rectangle (region, i, &rect);
-      boxes[i] = (MetaBox) {
-        .x1 = rect.x,
-        .y1 = rect.y,
-        .x2 = rect.x + rect.width,
-        .y2 = rect.y + rect.height,
-      };
-    }
-  prev_top = 0;
-  top_most = boxes[0].y1;
-  current_roof = top_most;
-  bottom_most = boxes[num_boxes - 1].y2;
-  band_start = 0;
-  prev_band_start = 0;
-  for (i = 0; i < num_boxes; i++)
-    {
-      /* Detect if there is a vertical empty space, and add the lower
-       * level of the previous band if so was the case. */
-      if (i > 0 &&
-          boxes[i].y1 != prev_top &&
-          boxes[i].y1 != boxes[i - 1].y2)
-        {
-          current_roof = boxes[i].y1;
-          add_band_bottom_edges (boxes,
-                                 band_start,
-                                 i,
-                                 borders);
-        }
-
-      /* Special case adding the last band, since it won't be handled
-       * by the band change detection below. */
-      if (boxes[i].y1 != current_roof && i == num_boxes - 1)
-        {
-          if (boxes[i].y1 != prev_top)
-            {
-              /* The last band is a single box, so we don't
-               * have a prev_band_start to tell us when the
-               * previous band started. */
-              add_non_overlapping_edges (boxes,
-                                         band_start,
-                                         i,
-                                         i + 1,
-                                         borders);
-            }
-          else
-            {
-              add_non_overlapping_edges (boxes,
-                                         prev_band_start,
-                                         band_start,
-                                         i + 1,
-                                         borders);
-            }
-        }
-
-      /* Detect when passing a band and combine the top border of the
-       * just passed band with the bottom band of the previous band.
-       */
-      if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top)
-        {
-          /* Combine the two passed bands. */
-          if (prev_top != current_roof)
-            {
-              add_non_overlapping_edges (boxes,
-                                         prev_band_start,
-                                         band_start,
-                                         i,
-                                         borders);
-            }
-
-          prev_band_start = band_start;
-          band_start = i;
-        }
-
-      /* Add the top border if the box is part of the current roof. */
-      if (boxes[i].y1 == current_roof)
-        {
-          add_border (borders,
-                      boxes[i].x1, boxes[i].y1,
-                      boxes[i].x2, boxes[i].y1,
-                      META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
-        }
-
-      /* Add the bottom border of the last band. */
-      if (boxes[i].y2 == bottom_most)
-        {
-          add_border (borders,
-                      boxes[i].x1, boxes[i].y2,
-                      boxes[i].x2, boxes[i].y2,
-                      META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
-        }
-
-      /* Always add the left border. */
-      add_border (borders,
-                  boxes[i].x1, boxes[i].y1,
-                  boxes[i].x1, boxes[i].y2,
-                  META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
-
-      /* Always add the right border. */
-      add_border (borders,
-                  boxes[i].x2, boxes[i].y1,
-                  boxes[i].x2, boxes[i].y2,
-                  META_BORDER_MOTION_DIRECTION_POSITIVE_X);
-
-      prev_top = boxes[i].y1;
-    }
-
-  g_free (boxes);
+  meta_pointer_confinement_wayland_update (self);
 }
 
-static MetaBorder *
-get_closest_border (GArray    *borders,
-                    MetaLine2 *motion,
-                    uint32_t   directions)
+void
+meta_pointer_confinement_wayland_enable (MetaPointerConfinementWayland *confinement)
 {
-  MetaBorder *border;
-  MetaVector2 intersection;
-  MetaVector2 delta;
-  float distance_2;
-  MetaBorder *closest_border = NULL;
-  float closest_distance_2 = DBL_MAX;
-  unsigned int i;
-
-  for (i = 0; i < borders->len; i++)
-    {
-      border = &g_array_index (borders, MetaBorder, i);
-
-      if (!meta_border_is_blocking_directions (border, directions))
-        continue;
+  MetaPointerConfinementWaylandPrivate *priv;
+  MetaWaylandPointerConstraint *constraint;
+  MetaWaylandSurface *surface;
+  MetaWindow *window;
 
-      if (!meta_line2_intersects_with (&border->line, motion, &intersection))
-        continue;
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
+  g_assert (!priv->enabled);
 
-      delta = meta_vector2_subtract (intersection, motion->a);
-      distance_2 = delta.x*delta.x + delta.y*delta.y;
-      if (distance_2 < closest_distance_2)
-        {
-          closest_border = border;
-          closest_distance_2 = distance_2;
-        }
-    }
+  priv->enabled = TRUE;
+  constraint = priv->constraint;
 
-  return closest_border;
-}
+  surface = meta_wayland_pointer_constraint_get_surface (constraint);
+  g_signal_connect_object (surface,
+                           "geometry-changed",
+                           G_CALLBACK (surface_geometry_changed),
+                           confinement,
+                           0);
 
-static void
-clamp_to_border (MetaBorder *border,
-                 MetaLine2  *motion,
-                 uint32_t   *motion_dir)
-{
-  /*
-   * When clamping either rightward or downward motions, the motion needs to be
-   * clamped so that the destination coordinate does not end up on the border
-   * (see weston_pointer_clamp_event_to_region). Do this by clamping such
-   * motions to the border minus the smallest possible wl_fixed_t value.
-   *
-   * When clamping in either leftward or upward motion, the resulting coordinate
-   * needs to be clamped so that it is enough on the inside to avoid the
-   * inaccuracies of clutter's stage to actor transformation algorithm (the one
-   * used in clutter_actor_transform_stage_point) to make it end up outside the
-   * next motion. It also needs to be clamped so that to the wl_fixed_t
-   * coordinate may still be right on the border (i.e. at .0). Testing shows
-   * that the smallest wl_fixed_t value divided by 10 is small enough to make
-   * the wl_fixed_t coordinate .0 and large enough to avoid the inaccuracies of
-   * clutters transform algorithm.
-   */
-  if (meta_border_is_horizontal (border))
-    {
-      if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
-        motion->b.y = border->line.a.y - wl_fixed_to_double (1);
-      else
-        motion->b.y = border->line.a.y + wl_fixed_to_double (1) / 10;
-      *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
-                       META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
-    }
-  else
+  window = meta_wayland_surface_get_window (surface);
+  if (window)
     {
-      if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_X)
-        motion->b.x = border->line.a.x - wl_fixed_to_double (1);
-      else
-        motion->b.x = border->line.a.x + wl_fixed_to_double (1) / 10;
-      *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_X |
-                       META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
+      g_signal_connect_object (window,
+                               "position-changed",
+                               G_CALLBACK (window_position_changed),
+                               confinement,
+                               0);
     }
-}
 
-static uint32_t
-get_motion_directions (MetaLine2 *motion)
-{
-  uint32_t directions = 0;
-
-  if (motion->a.x < motion->b.x)
-    directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_X;
-  else if (motion->a.x > motion->b.x)
-    directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_X;
-  if (motion->a.y < motion->b.y)
-    directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_Y;
-  else if (motion->a.y > motion->b.y)
-    directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_Y;
-
-  return directions;
+  meta_pointer_confinement_wayland_update (confinement);
 }
 
-static void
-meta_pointer_confinement_wayland_constrain (MetaPointerConstraint *constraint,
-                                            ClutterInputDevice    *device,
-                                            guint32                time,
-                                            float                  prev_x,
-                                            float                  prev_y,
-                                            float                  *x,
-                                            float                  *y)
+void
+meta_pointer_confinement_wayland_disable (MetaPointerConfinementWayland *confinement)
 {
-  MetaPointerConfinementWayland *self =
-    META_POINTER_CONFINEMENT_WAYLAND (constraint);
+  MetaPointerConfinementWaylandPrivate *priv;
+  MetaWaylandPointerConstraint *constraint;
   MetaWaylandSurface *surface;
-  cairo_region_t *region;
-  float sx, sy;
-  float prev_sx, prev_sy;
-  GArray *borders;
-  MetaLine2 motion;
-  MetaBorder *closest_border;
-  uint32_t directions;
-
-  surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
-
-  meta_wayland_surface_get_relative_coordinates (surface, *x, *y, &sx, &sy);
-  meta_wayland_surface_get_relative_coordinates (surface, prev_x, prev_y,
-                                                 &prev_sx, &prev_sy);
-
-  /* For motions in a positive direction on any axis, append the smallest
-   * possible value representable in a Wayland absolute coordinate.  This is
-   * in order to avoid not clamping motion that as a floating point number
-   * won't be clamped, but will be rounded up to be outside of the range
-   * of wl_fixed_t. */
-  if (sx > prev_sx)
-    sx += (float)wl_fixed_to_double(1);
-  if (sy > prev_sy)
-    sy += (float)wl_fixed_to_double(1);
-
-  borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
-
-  /*
-   * Generate borders given the confine region we are to use. The borders
-   * are defined to be the outer region of the allowed area. This means
-   * top/left borders are "within" the allowed area, while bottom/right
-   * borders are outside. This needs to be considered when clamping
-   * confined motion vectors.
-   */
-  region =
-    meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
-  region_to_outline (region, borders);
-  cairo_region_destroy (region);
+  MetaWindow *window;
 
-  motion = (MetaLine2) {
-    .a = (MetaVector2) {
-      .x = prev_sx,
-      .y = prev_sy,
-    },
-    .b = (MetaVector2) {
-      .x = sx,
-      .y = sy,
-    },
-  };
-  directions = get_motion_directions (&motion);
-
-  while (directions)
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
+  constraint = priv->constraint;
+  g_assert (priv->enabled);
+
+  priv->enabled = FALSE;
+  surface = meta_wayland_pointer_constraint_get_surface (constraint);
+  g_signal_handlers_disconnect_by_func (surface, surface_geometry_changed,
+                                        confinement);
+
+  window = meta_wayland_surface_get_window (surface);
+  if (window)
     {
-      closest_border = get_closest_border (borders,
-                                           &motion,
-                                           directions);
-      if (closest_border)
-        clamp_to_border (closest_border, &motion, &directions);
-      else
-        break;
+      g_signal_handlers_disconnect_by_func (window, window_position_changed,
+                                            confinement);
     }
 
-  meta_wayland_surface_get_absolute_coordinates (surface,
-                                                 motion.b.x, motion.b.y,
-                                                 x, y);
+  meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL);
+}
 
-  g_array_free (borders, FALSE);
+static void
+meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland)
+{
 }
 
-static float
-point_to_border_distance_2 (MetaBorder *border,
-                            float       x,
-                            float       y)
+static void
+meta_pointer_confinement_wayland_get_property (GObject    *object,
+                                               guint       prop_id,
+                                               GValue     *value,
+                                               GParamSpec *pspec)
 {
-  float orig_x, orig_y;
-  float dx, dy;
+  MetaPointerConfinementWayland *confinement;
+  MetaPointerConfinementWaylandPrivate *priv;
 
-  if (meta_border_is_horizontal (border))
-    {
-      if (x < border->line.a.x)
-        orig_x = border->line.a.x;
-      else if (x > border->line.b.x)
-        orig_x = border->line.b.x;
-      else
-        orig_x = x;
-      orig_y = border->line.a.y;
-    }
-  else
+  confinement = META_POINTER_CONFINEMENT_WAYLAND (object);
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
+
+  switch (prop_id)
     {
-      if (y < border->line.a.y)
-        orig_y = border->line.a.y;
-      else if (y > border->line.b.y)
-        orig_y = border->line.b.y;
-      else
-        orig_y = y;
-      orig_x = border->line.a.x;
+    case PROP_WAYLAND_POINTER_CONSTRAINT:
+      g_value_set_object (value, priv->constraint);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
     }
-
-  dx = fabsf (orig_x - x);
-  dy = fabsf (orig_y - y);
-  return dx*dx + dy*dy;
 }
 
 static void
-warp_to_behind_border (MetaBorder *border,
-                       float      *sx,
-                       float      *sy)
+meta_pointer_confinement_wayland_set_property (GObject      *object,
+                                               guint         prop_id,
+                                               const GValue *value,
+                                               GParamSpec   *pspec)
 {
-  switch (border->blocking_directions)
+  MetaPointerConfinementWayland *confinement;
+  MetaPointerConfinementWaylandPrivate *priv;
+
+  confinement = META_POINTER_CONFINEMENT_WAYLAND (object);
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
+
+  switch (prop_id)
     {
-    case META_BORDER_MOTION_DIRECTION_POSITIVE_X:
-    case META_BORDER_MOTION_DIRECTION_NEGATIVE_X:
-      if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_X)
-        *sx = border->line.a.x - wl_fixed_to_double (1);
-      else
-        *sx = border->line.a.x + wl_fixed_to_double (1);
-      if (*sy < border->line.a.y)
-        *sy = border->line.a.y + wl_fixed_to_double (1);
-      else if (*sy > border->line.b.y)
-        *sy = border->line.b.y - wl_fixed_to_double (1);
+    case PROP_WAYLAND_POINTER_CONSTRAINT:
+      priv->constraint = g_value_get_object (value);
       break;
-    case META_BORDER_MOTION_DIRECTION_POSITIVE_Y:
-    case META_BORDER_MOTION_DIRECTION_NEGATIVE_Y:
-      if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
-        *sy = border->line.a.y - wl_fixed_to_double (1);
-      else
-        *sy = border->line.a.y + wl_fixed_to_double (1);
-      if (*sx < border->line.a.x)
-        *sx = border->line.a.x + wl_fixed_to_double (1);
-      else if (*sx > (border->line.b.x))
-        *sx = border->line.b.x - wl_fixed_to_double (1);
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
 
-static void
-meta_pointer_confinement_wayland_maybe_warp (MetaPointerConfinementWayland *self)
+static MetaPointerConstraint *
+meta_pointer_confinement_wayland_create_constraint (MetaPointerConfinementWayland *confinement)
 {
-  MetaWaylandSeat *seat;
+  MetaPointerConfinementWaylandPrivate *priv;
+  MetaPointerConstraint *constraint;
   MetaWaylandSurface *surface;
-  graphene_point_t point;
-  float sx;
-  float sy;
   cairo_region_t *region;
+  float dx, dy;
 
-  seat = meta_wayland_pointer_constraint_get_seat (self->constraint);
-  surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
-
-  clutter_input_device_get_coords (seat->pointer->device, NULL, &point);
-  meta_wayland_surface_get_relative_coordinates (surface,
-                                                 point.x, point.y,
-                                                 &sx, &sy);
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
 
+  surface = meta_wayland_pointer_constraint_get_surface (priv->constraint);
   region =
-    meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
-
-  if (!cairo_region_contains_point (region, (int)sx, (int)sy))
-    {
-      GArray *borders;
-      float closest_distance_2 = FLT_MAX;
-      MetaBorder *closest_border = NULL;
-      ClutterSeat *seat;
-      unsigned int i;
-      float x;
-      float y;
-
-      borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
-
-      region_to_outline (region, borders);
-
-      for (i = 0; i < borders->len; i++)
-        {
-          MetaBorder *border = &g_array_index (borders, MetaBorder, i);
-          float distance_2;
-
-          distance_2 = point_to_border_distance_2 (border, sx, sy);
-          if (distance_2 < closest_distance_2)
-            {
-              closest_border = border;
-              closest_distance_2 = distance_2;
-            }
-        }
-
-      warp_to_behind_border (closest_border, &sx, &sy);
+    meta_wayland_pointer_constraint_calculate_effective_region (priv->constraint);
 
-      meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, &x, &y);
-
-      seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
-      clutter_seat_warp_pointer (seat, (int)x, (int)y);
-    }
+  meta_wayland_surface_get_absolute_coordinates (surface, 0, 0, &dx, &dy);
+  cairo_region_translate (region, dx, dy);
 
+  constraint = meta_pointer_constraint_new (region);
   cairo_region_destroy (region);
-}
 
-static void
-surface_geometry_changed (MetaWaylandSurface            *surface,
-                          MetaPointerConfinementWayland *self)
-{
-  meta_pointer_confinement_wayland_maybe_warp (self);
+  return constraint;
 }
 
 static void
-window_position_changed (MetaWindow                    *window,
-                         MetaPointerConfinementWayland *self)
+meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass)
 {
-  meta_pointer_confinement_wayland_maybe_warp (self);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = meta_pointer_confinement_wayland_set_property;
+  object_class->get_property = meta_pointer_confinement_wayland_get_property;
+
+  klass->create_constraint = meta_pointer_confinement_wayland_create_constraint;
+
+  props[PROP_WAYLAND_POINTER_CONSTRAINT] =
+    g_param_spec_object ("wayland-pointer-constraint",
+                         "Wayland pointer constraint",
+                         "Wayland pointer constraint",
+                         META_TYPE_WAYLAND_POINTER_CONSTRAINT,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+  g_object_class_install_properties (object_class, N_PROPS, props);
 }
 
-MetaPointerConstraint *
+MetaPointerConfinementWayland *
 meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint)
 {
-  GObject *object;
-  MetaPointerConfinementWayland *confinement;
-  MetaWaylandSurface *surface;
-  MetaWindow *window;
-
-  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,
-                           "geometry-changed",
-                           G_CALLBACK (surface_geometry_changed),
-                           confinement,
-                           0);
-
-  window = meta_wayland_surface_get_window (surface);
-  if (window)
-    {
-      g_signal_connect_object (window,
-                               "position-changed",
-                               G_CALLBACK (window_position_changed),
-                               confinement,
-                               0);
-    }
-
-  return META_POINTER_CONSTRAINT (confinement);
-}
-
-static void
-meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland)
-{
+  return g_object_new (META_TYPE_POINTER_CONFINEMENT_WAYLAND,
+                       "wayland-pointer-constraint", constraint,
+                       NULL);
 }
 
-static void
-meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass)
+MetaWaylandPointerConstraint *
+meta_pointer_confinement_wayland_get_wayland_pointer_constraint (MetaPointerConfinementWayland *confinement)
 {
-  MetaPointerConstraintClass *pointer_constraint_class =
-    META_POINTER_CONSTRAINT_CLASS (klass);
+  MetaPointerConfinementWaylandPrivate *priv;
 
-  pointer_constraint_class->constrain = meta_pointer_confinement_wayland_constrain;
+  priv = meta_pointer_confinement_wayland_get_instance_private (confinement);
+  return priv->constraint;
 }
diff --git a/src/wayland/meta-pointer-confinement-wayland.h b/src/wayland/meta-pointer-confinement-wayland.h
index 482a25a9ae..4f265779c4 100644
--- a/src/wayland/meta-pointer-confinement-wayland.h
+++ b/src/wayland/meta-pointer-confinement-wayland.h
@@ -33,12 +33,23 @@
 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_DECLARE_DERIVABLE_TYPE (MetaPointerConfinementWayland,
+                          meta_pointer_confinement_wayland,
+                          META, POINTER_CONFINEMENT_WAYLAND,
+                          GObject)
+
+struct _MetaPointerConfinementWaylandClass
+{
+  GObjectClass parent_class;
+
+  MetaPointerConstraint * (*create_constraint) (MetaPointerConfinementWayland *confinement);
+};
+
+MetaPointerConfinementWayland *meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint 
*constraint);
+MetaWaylandPointerConstraint *
+  meta_pointer_confinement_wayland_get_wayland_pointer_constraint (MetaPointerConfinementWayland 
*confinement);
+void meta_pointer_confinement_wayland_enable (MetaPointerConfinementWayland *confinement);
+void meta_pointer_confinement_wayland_disable (MetaPointerConfinementWayland *confinement);
 
 G_END_DECLS
 
diff --git a/src/wayland/meta-pointer-lock-wayland.c b/src/wayland/meta-pointer-lock-wayland.c
index 08c2789bc4..e872247323 100644
--- a/src/wayland/meta-pointer-lock-wayland.c
+++ b/src/wayland/meta-pointer-lock-wayland.c
@@ -37,33 +37,55 @@
 
 #include <glib-object.h>
 
-#include "backends/meta-pointer-constraint.h"
+#include "backends/meta-backend-private.h"
+#include "compositor/meta-surface-actor-wayland.h"
 
 struct _MetaPointerLockWayland
 {
-  MetaPointerConstraint parent;
+  GObject parent;
+  MetaWaylandPointerConstraint *constraint;
 };
 
 G_DEFINE_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland,
-               META_TYPE_POINTER_CONSTRAINT);
+               META_TYPE_POINTER_CONFINEMENT_WAYLAND)
 
-static void
-meta_pointer_lock_wayland_constrain (MetaPointerConstraint *constraint,
-                                     ClutterInputDevice    *device,
-                                     guint32                time,
-                                     float                  prev_x,
-                                     float                  prev_y,
-                                     float                 *x,
-                                     float                 *y)
+static MetaPointerConstraint *
+meta_pointer_lock_wayland_create_constraint (MetaPointerConfinementWayland *confinement)
 {
-  *x = prev_x;
-  *y = prev_y;
+  MetaBackend *backend = meta_get_backend ();
+  ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
+  ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
+  ClutterInputDevice *pointer = clutter_seat_get_pointer (seat);
+  MetaWaylandPointerConstraint *wayland_constraint;
+  MetaPointerConstraint *constraint;
+  MetaWaylandSurface *surface;
+  graphene_point_t point;
+  cairo_region_t *region;
+  float sx, sy, x, y;
+
+  clutter_input_device_get_coords (pointer, NULL, &point);
+  wayland_constraint =
+    meta_pointer_confinement_wayland_get_wayland_pointer_constraint (confinement);
+  surface = meta_wayland_pointer_constraint_get_surface (wayland_constraint);
+  meta_wayland_surface_get_relative_coordinates (surface,
+                                                 point.x, point.y,
+                                                 &sx, &sy);
+
+  meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, &x, &y);
+  region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { (int) x, (int) y, 1 , 1 });
+
+  constraint = meta_pointer_constraint_new (region);
+  cairo_region_destroy (region);
+
+  return constraint;
 }
 
-MetaPointerConstraint *
-meta_pointer_lock_wayland_new (void)
+MetaPointerConfinementWayland *
+meta_pointer_lock_wayland_new (MetaWaylandPointerConstraint *constraint)
 {
-  return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND, NULL);
+  return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND,
+                       "wayland-pointer-constraint", constraint,
+                       NULL);
 }
 
 static void
@@ -74,8 +96,9 @@ 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);
+  MetaPointerConfinementWaylandClass *confinement_class =
+    META_POINTER_CONFINEMENT_WAYLAND_CLASS (klass);
 
-  pointer_constraint_class->constrain = meta_pointer_lock_wayland_constrain;
+  confinement_class->create_constraint =
+    meta_pointer_lock_wayland_create_constraint;
 }
diff --git a/src/wayland/meta-pointer-lock-wayland.h b/src/wayland/meta-pointer-lock-wayland.h
index 787b43269d..d52aaa30dd 100644
--- a/src/wayland/meta-pointer-lock-wayland.h
+++ b/src/wayland/meta-pointer-lock-wayland.h
@@ -27,15 +27,15 @@
 
 #include <glib-object.h>
 
-#include "backends/meta-pointer-constraint.h"
+#include "wayland/meta-pointer-confinement-wayland.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);
+                      META, POINTER_LOCK_WAYLAND, MetaPointerConfinementWayland)
 
-MetaPointerConstraint *meta_pointer_lock_wayland_new (void);
+MetaPointerConfinementWayland *meta_pointer_lock_wayland_new (MetaWaylandPointerConstraint *constraint);
 
 G_END_DECLS
 
diff --git a/src/wayland/meta-wayland-pointer-constraints.c b/src/wayland/meta-wayland-pointer-constraints.c
index 7547d7e13f..3a8b8e095d 100644
--- a/src/wayland/meta-wayland-pointer-constraints.c
+++ b/src/wayland/meta-wayland-pointer-constraints.c
@@ -68,7 +68,7 @@ struct _MetaWaylandPointerConstraint
   wl_fixed_t x_hint;
   wl_fixed_t y_hint;
 
-  MetaPointerConstraint *constraint;
+  MetaPointerConfinementWayland *confinement;
 };
 
 typedef struct _MetaWaylandSurfacePointerConstraintsData
@@ -375,7 +375,7 @@ meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint
     zwp_confined_pointer_v1_send_unconfined (resource);
 }
 
-static MetaPointerConstraint *
+static MetaPointerConfinementWayland *
 meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint)
 {
   struct wl_resource *resource = constraint->resource;
@@ -384,7 +384,7 @@ meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerCon
                                &zwp_locked_pointer_v1_interface,
                                &locked_pointer_interface))
     {
-      return meta_pointer_lock_wayland_new ();
+      return meta_pointer_lock_wayland_new (constraint);
     }
   else if (wl_resource_instance_of (resource,
                                     &zwp_confined_pointer_v1_interface,
@@ -399,8 +399,6 @@ meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerCon
 static void
 meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint)
 {
-  MetaBackend *backend = meta_get_backend ();
-
   g_assert (!constraint->is_enabled);
 
   constraint->is_enabled = TRUE;
@@ -408,20 +406,25 @@ meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint
   meta_wayland_pointer_start_grab (constraint->seat->pointer,
                                    &constraint->grab);
 
-  constraint->constraint =
+  constraint->confinement =
     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);
+  meta_pointer_confinement_wayland_enable (constraint->confinement);
+  g_object_add_weak_pointer (G_OBJECT (constraint->confinement),
+                             (gpointer *) &constraint->confinement);
 }
 
 static void
 meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint)
 {
   constraint->is_enabled = FALSE;
+
+  if (constraint->confinement)
+    {
+      meta_pointer_confinement_wayland_disable (constraint->confinement);
+      g_object_unref (constraint->confinement);
+    }
+
   meta_wayland_pointer_constraint_notify_deactivated (constraint);
-  meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL);
   meta_wayland_pointer_end_grab (constraint->grab.pointer);
 }
 


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