[mutter] backends/native: Implement support for pointer barriers



commit db384a656cf635ad0a6650d7e3354ac9271839d4
Author: Jonas Ådahl <jadahl gmail com>
Date:   Fri Jan 16 16:50:51 2015 +0800

    backends/native: Implement support for pointer barriers
    
    When running as a dispay server pointer barriers are a server side
    feature and requires no client interaction of any sort. This patch
    implements pointer barriers that can be used when running as a display
    server on the native backend. Running as a display server using the X11
    backend is currently not supported.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=706655

 src/Makefile.am                                   |    3 +
 src/backends/meta-barrier.c                       |    6 +
 src/backends/native/meta-backend-native-private.h |   32 +
 src/backends/native/meta-backend-native.c         |   39 +-
 src/backends/native/meta-barrier-native.c         |  744 +++++++++++++++++++++
 src/backends/native/meta-barrier-native.h         |   68 ++
 src/core/display.c                                |   22 +-
 7 files changed, 909 insertions(+), 5 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 458d77a..d397ee2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -271,6 +271,9 @@ if HAVE_NATIVE_BACKEND
 libmutter_la_SOURCES +=                                        \
        backends/native/meta-backend-native.c           \
        backends/native/meta-backend-native.h           \
+       backends/native/meta-backend-native-private.h   \
+       backends/native/meta-barrier-native.c           \
+       backends/native/meta-barrier-native.h           \
        backends/native/meta-cursor-renderer-native.c   \
        backends/native/meta-cursor-renderer-native.h   \
        backends/native/meta-idle-monitor-native.c      \
diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c
index ab4fdbe..20bc4ef 100644
--- a/src/backends/meta-barrier.c
+++ b/src/backends/meta-barrier.c
@@ -12,6 +12,8 @@
 
 #include <meta/util.h>
 #include <meta/barrier.h>
+#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-barrier-native.h"
 #include "backends/x11/meta-backend-x11.h"
 #include "backends/x11/meta-barrier-x11.h"
 #include "mutter-enum-types.h"
@@ -166,6 +168,10 @@ meta_barrier_constructed (GObject *object)
 
   g_return_if_fail (priv->x1 == priv->x2 || priv->y1 == priv->y2);
 
+#if defined(HAVE_NATIVE_BACKEND)
+  if (META_IS_BACKEND_NATIVE (meta_get_backend ()))
+    priv->impl = meta_barrier_impl_native_new (barrier);
+#endif
 #if defined(HAVE_XI23)
   if (META_IS_BACKEND_X11 (meta_get_backend ()) &&
       !meta_is_wayland_compositor ())
diff --git a/src/backends/native/meta-backend-native-private.h 
b/src/backends/native/meta-backend-native-private.h
new file mode 100644
index 0000000..0458325
--- /dev/null
+++ b/src/backends/native/meta-backend-native-private.h
@@ -0,0 +1,32 @@
+/* -*- 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_BACKEND_NATIVE_PRIVATE_H
+#define META_BACKEND_NATIVE_PRIVATE_H
+
+#include "backends/native/meta-barrier-native.h"
+
+MetaBarrierManagerNative *meta_backend_native_get_barrier_manager (MetaBackendNative *native);
+
+#endif /* META_BACKEND_NATIVE_PRIVATE_H */
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 22328f0..6d0e5db 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -24,10 +24,13 @@
 
 #include "config.h"
 
+#include "meta-backend-native.h"
+#include "meta-backend-native-private.h"
+
 #include <meta/main.h>
 #include <clutter/evdev/clutter-evdev.h>
-#include "meta-backend-native.h"
 
+#include "meta-barrier-native.h"
 #include "meta-idle-monitor-native.h"
 #include "meta-monitor-manager-kms.h"
 #include "meta-cursor-renderer-native.h"
@@ -36,6 +39,10 @@
 struct _MetaBackendNativePrivate
 {
   MetaLauncher *launcher;
+
+  MetaBarrierManagerNative *barrier_manager;
+
+  GSettings *keyboard_settings;
 };
 typedef struct _MetaBackendNativePrivate MetaBackendNativePrivate;
 
@@ -52,6 +59,22 @@ meta_backend_native_finalize (GObject *object)
   G_OBJECT_CLASS (meta_backend_native_parent_class)->finalize (object);
 }
 
+static void
+constrain_to_barriers (ClutterInputDevice *device,
+                       guint32             time,
+                       float              *new_x,
+                       float              *new_y)
+{
+  MetaBackendNative *native = META_BACKEND_NATIVE (meta_get_backend ());
+  MetaBackendNativePrivate *priv =
+    meta_backend_native_get_instance_private (native);
+
+  meta_barrier_manager_native_process (priv->barrier_manager,
+                                       device,
+                                       time,
+                                       new_x, new_y);
+}
+
 /*
  * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg.
  * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder)
@@ -141,6 +164,9 @@ pointer_constrain_callback (ClutterInputDevice *device,
   unsigned int n_monitors;
   gboolean ret;
 
+  /* Constrain to barriers */
+  constrain_to_barriers (device, time, new_x, new_y);
+
   monitor_manager = meta_monitor_manager_get ();
   monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors);
 
@@ -269,6 +295,8 @@ meta_backend_native_init (MetaBackendNative *native)
 
   /* We're a display server, so start talking to weston-launch. */
   priv->launcher = meta_launcher_new ();
+
+  priv->barrier_manager = meta_barrier_manager_native_new ();
 }
 
 gboolean
@@ -281,6 +309,15 @@ meta_activate_vt (int vt, GError **error)
   return meta_launcher_activate_vt (priv->launcher, vt, error);
 }
 
+MetaBarrierManagerNative *
+meta_backend_native_get_barrier_manager (MetaBackendNative *native)
+{
+  MetaBackendNativePrivate *priv =
+    meta_backend_native_get_instance_private (native);
+
+  return priv->barrier_manager;
+}
+
 /**
  * meta_activate_session:
  *
diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c
new file mode 100644
index 0000000..8f81d34
--- /dev/null
+++ b/src/backends/native/meta-barrier-native.c
@@ -0,0 +1,744 @@
+/* -*- 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>
+ */
+
+/**
+ * SECTION:barrier-native
+ * @Title: MetaBarrierImplNative
+ * @Short_Description: Pointer barriers implementation for the native backend
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <meta/barrier.h>
+#include <meta/util.h>
+#include "backends/meta-backend-private.h"
+#include "backends/meta-barrier-private.h"
+#include "backends/native/meta-backend-native.h"
+#include "backends/native/meta-backend-native-private.h"
+#include "backends/native/meta-barrier-native.h"
+
+struct _MetaBarrierManagerNative
+{
+  GHashTable *barriers;
+};
+
+typedef enum {
+  /* The barrier is active and responsive to pointer motion. */
+  META_BARRIER_STATE_ACTIVE,
+
+  /* An intermediate state after a pointer hit the pointer barrier. */
+  META_BARRIER_STATE_HIT,
+
+  /* The barrier was hit by a pointer and is still within the hit box and
+   * has not been released.*/
+  META_BARRIER_STATE_HELD,
+
+  /* The pointer was released by the user. If the following motion hits
+   * the barrier, it will pass through. */
+  META_BARRIER_STATE_RELEASE,
+
+  /* An intermediate state when the pointer has left the barrier. */
+  META_BARRIER_STATE_LEFT,
+} MetaBarrierState;
+
+struct _MetaBarrierImplNativePrivate
+{
+  MetaBarrier              *barrier;
+  MetaBarrierManagerNative *manager;
+
+  gboolean                  is_active;
+  MetaBarrierState          state;
+  int                       trigger_serial;
+  guint32                   last_event_time;
+  MetaBarrierDirection      blocked_dir;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrierImplNative, meta_barrier_impl_native,
+                            META_TYPE_BARRIER_IMPL)
+
+static int
+next_serial (void)
+{
+  static int barrier_serial = 1;
+
+  barrier_serial++;
+
+  /* If it wraps, avoid 0 as it's not a valid serial. */
+  if (barrier_serial == 0)
+    barrier_serial++;
+
+  return barrier_serial;
+}
+
+typedef struct _Vector2
+{
+  float x, y;
+} Vector2;
+
+static float
+vector2_cross_product (Vector2 a, Vector2 b)
+{
+  return a.x * b.y - a.y * b.x;
+}
+
+static Vector2
+vector2_add (Vector2 a, Vector2 b)
+{
+  return (Vector2) {
+    .x = a.x + b.x,
+    .y = a.y + b.y,
+  };
+}
+
+static Vector2
+vector2_subtract (Vector2 a, Vector2 b)
+{
+  return (Vector2) {
+    .x = a.x - b.x,
+    .y = a.y - b.y,
+  };
+}
+
+static Vector2
+vector2_multiply_constant (float c, Vector2 a)
+{
+  return (Vector2) {
+    .x = c * a.x,
+    .y = c * a.y,
+  };
+}
+
+typedef struct _Line2
+{
+  Vector2 a;
+  Vector2 b;
+} Line2;
+
+static gboolean
+lines_intersect (Line2 *line1, Line2 *line2, Vector2 *intersection)
+{
+  Vector2 p = line1->a;
+  Vector2 r = vector2_subtract (line1->b, line1->a);
+  Vector2 q = line2->a;
+  Vector2 s = vector2_subtract (line2->b, line2->a);
+  float rxs;
+  float sxr;
+  float t;
+  float u;
+
+  /*
+   * The line (p, r) and (q, s) intersects where
+   *
+   *   p + t r = q + u s
+   *
+   * Calculate t:
+   *
+   *   (p + t r) × s = (q + u s) × s
+   *   p × s + t (r × s) = q × s + u (s × s)
+   *   p × s + t (r × s) = q × s
+   *   t (r × s) = q × s - p × s
+   *   t (r × s) = (q - p) × s
+   *   t = ((q - p) × s) / (r × s)
+   *
+   * Using the same method, for u we get:
+   *
+   *   u = ((p - q) × r) / (s × r)
+   */
+
+  rxs = vector2_cross_product (r, s);
+  sxr = vector2_cross_product (s, r);
+
+  /* If r × s = 0 then the lines are either parallel or collinear. */
+  if (fabs ( rxs) < DBL_MIN)
+    return FALSE;
+
+  t = vector2_cross_product (vector2_subtract (q, p), s) / rxs;
+  u = vector2_cross_product (vector2_subtract (p, q), r) / sxr;
+
+
+  /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */
+  if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0)
+    return FALSE;
+
+  *intersection = vector2_add (p, vector2_multiply_constant (t, r));
+
+  return TRUE;
+}
+
+static gboolean
+is_barrier_horizontal (MetaBarrier *barrier)
+{
+  return barrier->priv->y1 == barrier->priv->y2;
+}
+
+static gboolean
+is_barrier_blocking_directions (MetaBarrier         *barrier,
+                                MetaBarrierDirection directions)
+{
+  /* Barriers doesn't block parallel motions. */
+  if (is_barrier_horizontal (barrier))
+    {
+      if ((directions & (META_BARRIER_DIRECTION_POSITIVE_Y |
+                         META_BARRIER_DIRECTION_NEGATIVE_Y)) == 0)
+        return FALSE;
+    }
+  else
+    {
+      if ((directions & (META_BARRIER_DIRECTION_POSITIVE_X |
+                         META_BARRIER_DIRECTION_NEGATIVE_X)) == 0)
+        return FALSE;
+    }
+
+  return (barrier->priv->directions & directions) != directions;
+}
+
+static void
+dismiss_pointer (MetaBarrierImplNative *self)
+{
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+
+  priv->state = META_BARRIER_STATE_LEFT;
+}
+
+static Line2
+barrier_to_line (MetaBarrier *barrier)
+{
+  return (Line2) {
+    .a = (Vector2) {
+      .x = MIN (barrier->priv->x1, barrier->priv->x2),
+      .y = MIN (barrier->priv->y1, barrier->priv->y2),
+    },
+    .b = (Vector2) {
+      .x = MAX (barrier->priv->x1, barrier->priv->x2),
+      .y = MAX (barrier->priv->y1, barrier->priv->y2),
+    },
+  };
+}
+
+/*
+ * Calculate the hit box for a held motion. The hit box is a 2 px wide region
+ * in the opposite direction of every direction the barrier blocks. The purpose
+ * of this is to allow small movements without receiving a "left" signal. This
+ * heuristic comes from the X.org pointer barrier implementation.
+ */
+static Line2
+calculate_barrier_hit_box (MetaBarrier *barrier)
+{
+  Line2 hit_box = barrier_to_line (barrier);
+
+  if (is_barrier_horizontal (barrier))
+    {
+      if (is_barrier_blocking_directions (barrier,
+                                          META_BARRIER_DIRECTION_POSITIVE_Y))
+        hit_box.a.y -= 2.0f;
+      if (is_barrier_blocking_directions (barrier,
+                                          META_BARRIER_DIRECTION_NEGATIVE_Y))
+        hit_box.b.y += 2.0f;
+    }
+  else
+    {
+      if (is_barrier_blocking_directions (barrier,
+                                          META_BARRIER_DIRECTION_POSITIVE_X))
+        hit_box.a.x -= 2.0f;
+      if (is_barrier_blocking_directions (barrier,
+                                          META_BARRIER_DIRECTION_NEGATIVE_X))
+        hit_box.b.x += 2.0f;
+    }
+
+  return hit_box;
+}
+
+static gboolean
+is_within_box (Line2 box, Vector2 point)
+{
+  return (point.x >= box.a.x && point.x < box.b.x &&
+          point.y >= box.a.y && point.y < box.b.y);
+}
+
+static void
+maybe_release_barrier (gpointer key,
+                       gpointer value,
+                       gpointer user_data)
+{
+  MetaBarrierImplNative *self = key;
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+  MetaBarrier *barrier = priv->barrier;
+  Line2 *motion = user_data;
+  Line2 hit_box;
+
+  if (priv->state != META_BARRIER_STATE_HELD)
+    return;
+
+  /* Release if we end up outside barrier end points. */
+  if (is_barrier_horizontal (barrier))
+    {
+      if (motion->b.x > MAX (barrier->priv->x1, barrier->priv->x2) ||
+          motion->b.x < MIN (barrier->priv->x1, barrier->priv->x2))
+        {
+          dismiss_pointer (self);
+          return;
+        }
+    }
+  else
+    {
+      if (motion->b.y > MAX (barrier->priv->y1, barrier->priv->y2) ||
+          motion->b.y < MIN (barrier->priv->y1, barrier->priv->y2))
+        {
+          dismiss_pointer (self);
+          return;
+        }
+    }
+
+  /* Release if we don't intersect and end up outside of hit box. */
+  hit_box = calculate_barrier_hit_box (barrier);
+  if (!is_within_box (hit_box, motion->b))
+    {
+      dismiss_pointer (self);
+      return;
+    }
+}
+
+static void
+maybe_release_barriers (MetaBarrierManagerNative *manager,
+                        float                     prev_x,
+                        float                     prev_y,
+                        float                     x,
+                        float                     y)
+{
+  Line2 motion = {
+    .a = {
+      .x = prev_x,
+      .y = prev_y,
+    },
+    .b = {
+      .x = x,
+      .y = y,
+    },
+  };
+
+  g_hash_table_foreach (manager->barriers,
+                        maybe_release_barrier,
+                        &motion);
+}
+
+typedef struct _MetaClosestBarrierData
+{
+  struct
+  {
+    Line2                       motion;
+    MetaBarrierDirection        directions;
+  } in;
+
+  struct
+  {
+    float                       closest_distance_2;
+    MetaBarrierImplNative      *barrier_impl;
+  } out;
+} MetaClosestBarrierData;
+
+static void
+update_closest_barrier (gpointer key,
+                        gpointer value,
+                        gpointer user_data)
+{
+  MetaBarrierImplNative *self = key;
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+  MetaBarrier *barrier = priv->barrier;
+  MetaClosestBarrierData *data = user_data;
+  Line2 barrier_line;
+  Vector2 intersection;
+  float dx, dy;
+  float distance_2;
+
+  /* Ignore if the barrier is not blocking in any of the motions directions. */
+  if (!is_barrier_blocking_directions (barrier, data->in.directions))
+    return;
+
+  /* Ignore if the barrier released the pointer. */
+  if (priv->state == META_BARRIER_STATE_RELEASE)
+    return;
+
+  /* Ignore if we are moving away from barrier. */
+  if (priv->state == META_BARRIER_STATE_HELD &&
+      (data->in.directions & priv->blocked_dir) == 0)
+    return;
+
+  /* Check if the motion intersects with the barrier, and retrieve the
+   * intersection point if any. */
+  barrier_line = (Line2) {
+    .a = {
+      .x = barrier->priv->x1,
+      .y = barrier->priv->y1
+    },
+    .b = {
+      .x = barrier->priv->x2,
+      .y = barrier->priv->y2
+    },
+  };
+  if (!lines_intersect (&barrier_line, &data->in.motion, &intersection))
+    return;
+
+  /* Calculate the distance to the barrier and keep track of the closest
+   * barrier. */
+  dx = intersection.x - data->in.motion.a.x;
+  dy = intersection.y - data->in.motion.a.y;
+  distance_2 = dx*dx + dy*dy;
+  if (data->out.barrier_impl == NULL ||
+      distance_2 < data->out.closest_distance_2)
+    {
+      data->out.barrier_impl = self;
+      data->out.closest_distance_2 = distance_2;
+    }
+}
+
+static gboolean
+get_closest_barrier (MetaBarrierManagerNative *manager,
+                     float                     prev_x,
+                     float                     prev_y,
+                     float                     x,
+                     float                     y,
+                     MetaBarrierDirection      motion_dir,
+                     MetaBarrierImplNative   **barrier_impl)
+{
+  MetaClosestBarrierData closest_barrier_data;
+
+  closest_barrier_data = (MetaClosestBarrierData) {
+    .in = {
+      .motion = {
+        .a = {
+          .x = prev_x,
+          .y = prev_y,
+        },
+        .b = {
+          .x = x,
+          .y = y,
+        },
+      },
+      .directions = motion_dir,
+    },
+  };
+
+  g_hash_table_foreach (manager->barriers,
+                        update_closest_barrier,
+                        &closest_barrier_data);
+
+  if (closest_barrier_data.out.barrier_impl != NULL)
+    {
+      *barrier_impl = closest_barrier_data.out.barrier_impl;
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+typedef struct _MetaBarrierEventData
+{
+  guint32             time;
+  float               prev_x;
+  float               prev_y;
+  float               x;
+  float               y;
+  float               dx;
+  float               dy;
+} MetaBarrierEventData;
+
+static void
+emit_barrier_event (MetaBarrierImplNative *self,
+                    guint32                time,
+                    float                  prev_x,
+                    float                  prev_y,
+                    float                  x,
+                    float                  y,
+                    float                  dx,
+                    float                  dy)
+{
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+  MetaBarrier *barrier = priv->barrier;
+  MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent);
+  MetaBarrierState old_state = priv->state;
+
+  switch (priv->state)
+    {
+    case META_BARRIER_STATE_HIT:
+      priv->state = META_BARRIER_STATE_HELD;
+      priv->trigger_serial = next_serial ();
+      event->dt = 0;
+
+      break;
+    case META_BARRIER_STATE_RELEASE:
+    case META_BARRIER_STATE_LEFT:
+      priv->state = META_BARRIER_STATE_ACTIVE;
+
+      /* Intentional fall-through. */
+    case META_BARRIER_STATE_HELD:
+      event->dt = time - priv->last_event_time;
+
+      break;
+    case META_BARRIER_STATE_ACTIVE:
+      g_assert_not_reached (); /* Invalid state. */
+    }
+
+  event->ref_count = 1;
+  event->event_id = priv->trigger_serial;
+  event->time = time;
+
+  event->x = x;
+  event->y = y;
+  event->dx = dx;
+  event->dy = dy;
+
+  event->grabbed = priv->state == META_BARRIER_STATE_HELD;
+  event->released = old_state == META_BARRIER_STATE_RELEASE;
+
+  priv->last_event_time = time;
+
+  if (priv->state == META_BARRIER_STATE_HELD)
+    _meta_barrier_emit_hit_signal (barrier, event);
+  else
+    _meta_barrier_emit_left_signal (barrier, event);
+
+  meta_barrier_event_unref (event);
+}
+
+static void
+maybe_emit_barrier_event (gpointer key, gpointer value, gpointer user_data)
+{
+  MetaBarrierImplNative *self = key;
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+  MetaBarrierEventData *data = user_data;
+
+  switch (priv->state) {
+    case META_BARRIER_STATE_ACTIVE:
+      break;
+    case META_BARRIER_STATE_HIT:
+    case META_BARRIER_STATE_HELD:
+    case META_BARRIER_STATE_RELEASE:
+    case META_BARRIER_STATE_LEFT:
+      emit_barrier_event (self,
+                          data->time,
+                          data->prev_x,
+                          data->prev_y,
+                          data->x,
+                          data->y,
+                          data->dx,
+                          data->dy);
+      break;
+    }
+}
+
+/* Clamp (x, y) to the barrier and remove clamped direction from motion_dir. */
+static void
+clamp_to_barrier (MetaBarrierImplNative *self,
+                  MetaBarrierDirection *motion_dir,
+                  float *x,
+                  float *y)
+{
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+  MetaBarrier *barrier = priv->barrier;
+
+  if (is_barrier_horizontal (barrier))
+    {
+      if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y)
+        *y = barrier->priv->y1;
+      else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y)
+        *y = barrier->priv->y1;
+
+      priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y |
+                                         META_BARRIER_DIRECTION_NEGATIVE_Y);
+      *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_Y |
+                       META_BARRIER_DIRECTION_NEGATIVE_Y);
+    }
+  else
+    {
+      if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X)
+        *x = barrier->priv->x1;
+      else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X)
+        *x = barrier->priv->x1;
+
+      priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X |
+                                         META_BARRIER_DIRECTION_NEGATIVE_X);
+      *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_X |
+                       META_BARRIER_DIRECTION_NEGATIVE_X);
+    }
+
+  priv->state = META_BARRIER_STATE_HIT;
+}
+
+void
+meta_barrier_manager_native_process (MetaBarrierManagerNative *manager,
+                                     ClutterInputDevice       *device,
+                                     guint32                   time,
+                                     float                    *x,
+                                     float                    *y)
+{
+  ClutterPoint prev_pos;
+  float prev_x;
+  float prev_y;
+  float orig_x = *x;
+  float orig_y = *y;
+  MetaBarrierDirection motion_dir = 0;
+  MetaBarrierEventData barrier_event_data;
+  MetaBarrierImplNative *barrier_impl;
+
+  if (!clutter_input_device_get_coords (device, NULL, &prev_pos))
+    return;
+
+  prev_x = prev_pos.x;
+  prev_y = prev_pos.y;
+
+  /* Get the direction of the motion vector. */
+  if (prev_x < *x)
+    motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X;
+  else if (prev_x > *x)
+    motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_X;
+  if (prev_y < *y)
+    motion_dir |= META_BARRIER_DIRECTION_POSITIVE_Y;
+  else if (prev_y > *y)
+    motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_Y;
+
+  /* Clamp to the closest barrier in any direction until either there are no
+   * more barriers to clamp to or all directions have been clamped. */
+  while (motion_dir != 0)
+    {
+      if (get_closest_barrier (manager,
+                               prev_x, prev_y,
+                               *x, *y,
+                               motion_dir,
+                               &barrier_impl))
+        clamp_to_barrier (barrier_impl, &motion_dir, x, y);
+      else
+        break;
+    }
+
+  /* Potentially release active barrier movements. */
+  maybe_release_barriers (manager, prev_x, prev_y, *x, *y);
+
+  /* Initiate or continue barrier interaction. */
+  barrier_event_data = (MetaBarrierEventData) {
+    .time = time,
+    .prev_x = prev_x,
+    .prev_y = prev_y,
+    .x = *x,
+    .y = *y,
+    .dx = orig_x - prev_x,
+    .dy = orig_y - prev_y,
+  };
+
+  g_hash_table_foreach (manager->barriers,
+                        maybe_emit_barrier_event,
+                        &barrier_event_data);
+}
+
+static gboolean
+_meta_barrier_impl_native_is_active (MetaBarrierImpl *impl)
+{
+  MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl);
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+
+  return priv->is_active;
+}
+
+static void
+_meta_barrier_impl_native_release (MetaBarrierImpl  *impl,
+                                   MetaBarrierEvent *event)
+{
+  MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl);
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+
+  if (priv->state == META_BARRIER_STATE_HELD &&
+      event->event_id == priv->trigger_serial)
+    priv->state = META_BARRIER_STATE_RELEASE;
+}
+
+static void
+_meta_barrier_impl_native_destroy (MetaBarrierImpl *impl)
+{
+  MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl);
+  MetaBarrierImplNativePrivate *priv =
+    meta_barrier_impl_native_get_instance_private (self);
+
+  g_hash_table_remove (priv->manager->barriers, self);
+  priv->is_active = FALSE;
+}
+
+MetaBarrierImpl *
+meta_barrier_impl_native_new (MetaBarrier *barrier)
+{
+  MetaBarrierImplNative *self;
+  MetaBarrierImplNativePrivate *priv;
+  MetaBackendNative *native;
+  MetaBarrierManagerNative *manager;
+
+  self = g_object_new (META_TYPE_BARRIER_IMPL_NATIVE, NULL);
+  priv = meta_barrier_impl_native_get_instance_private (self);
+
+  priv->barrier = barrier;
+  priv->is_active = TRUE;
+
+  native = META_BACKEND_NATIVE (meta_get_backend ());
+  manager = meta_backend_native_get_barrier_manager (native);
+  priv->manager = manager;
+  g_hash_table_add (manager->barriers, self);
+
+  return META_BARRIER_IMPL (self);
+}
+
+static void
+meta_barrier_impl_native_class_init (MetaBarrierImplNativeClass *klass)
+{
+  MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass);
+
+  impl_class->is_active = _meta_barrier_impl_native_is_active;
+  impl_class->release = _meta_barrier_impl_native_release;
+  impl_class->destroy = _meta_barrier_impl_native_destroy;
+}
+
+static void
+meta_barrier_impl_native_init (MetaBarrierImplNative *self)
+{
+}
+
+MetaBarrierManagerNative *
+meta_barrier_manager_native_new (void)
+{
+  MetaBarrierManagerNative *manager;
+
+  manager = g_new0 (MetaBarrierManagerNative, 1);
+
+  manager->barriers = g_hash_table_new (NULL, NULL);
+
+  return manager;
+}
diff --git a/src/backends/native/meta-barrier-native.h b/src/backends/native/meta-barrier-native.h
new file mode 100644
index 0000000..0fc414a
--- /dev/null
+++ b/src/backends/native/meta-barrier-native.h
@@ -0,0 +1,68 @@
+/* -*- 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_BARRIER_NATIVE_H
+#define META_BARRIER_NATIVE_H
+
+#include "backends/meta-barrier-private.h"
+
+G_BEGIN_DECLS
+
+#define META_TYPE_BARRIER_IMPL_NATIVE            (meta_barrier_impl_native_get_type ())
+#define META_BARRIER_IMPL_NATIVE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNative))
+#define META_BARRIER_IMPL_NATIVE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass))
+#define META_IS_BARRIER_IMPL_NATIVE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
META_TYPE_BARRIER_IMPL_NATIVE))
+#define META_IS_BARRIER_IMPL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
META_TYPE_BARRIER_IMPL_NATIVE))
+#define META_BARRIER_IMPL_NATIVE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass))
+
+typedef struct _MetaBarrierImplNative        MetaBarrierImplNative;
+typedef struct _MetaBarrierImplNativeClass   MetaBarrierImplNativeClass;
+typedef struct _MetaBarrierImplNativePrivate MetaBarrierImplNativePrivate;
+
+typedef struct _MetaBarrierManagerNative     MetaBarrierManagerNative;
+
+struct _MetaBarrierImplNative
+{
+  MetaBarrierImpl parent;
+};
+
+struct _MetaBarrierImplNativeClass
+{
+  MetaBarrierImplClass parent_class;
+};
+
+GType meta_barrier_impl_native_get_type (void) G_GNUC_CONST;
+
+MetaBarrierImpl *meta_barrier_impl_native_new (MetaBarrier *barrier);
+
+MetaBarrierManagerNative *meta_barrier_manager_native_new (void);
+void meta_barrier_manager_native_process (MetaBarrierManagerNative *manager,
+                                          ClutterInputDevice       *device,
+                                          guint32                   time,
+                                          float                    *x,
+                                          float                    *y);
+
+G_END_DECLS
+
+#endif /* META_BARRIER_NATIVE_H */
diff --git a/src/core/display.c b/src/core/display.c
index 3c0e2ab..f3bd73f 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -50,6 +50,7 @@
 #include "meta-idle-monitor-dbus.h"
 #include "meta-cursor-tracker-private.h"
 #include <meta/meta-backend.h>
+#include "backends/native/meta-backend-native.h"
 #include "backends/x11/meta-backend-x11.h"
 #include <clutter/x11/clutter-x11.h>
 
@@ -2906,9 +2907,11 @@ meta_display_get_xinput_opcode (MetaDisplay *display)
  * meta_display_supports_extended_barriers:
  * @display: a #MetaDisplay
  *
- * Returns: whether the X server supports extended barrier
- * features as defined in version 2.3 of the XInput 2
- * specification.
+ * Returns: whether pointer barriers can be supported.
+ *
+ * When running as an X compositor the X server needs XInput 2
+ * version 2.3. When running as a display server it is supported
+ * when running on the native backend.
  *
  * Clients should use this method to determine whether their
  * interfaces should depend on new barrier features.
@@ -2916,7 +2919,18 @@ meta_display_get_xinput_opcode (MetaDisplay *display)
 gboolean
 meta_display_supports_extended_barriers (MetaDisplay *display)
 {
-  return META_DISPLAY_HAS_XINPUT_23 (display) && !meta_is_wayland_compositor ();
+#ifdef HAVE_NATIVE_BACKEND
+  if (META_IS_BACKEND_NATIVE (meta_get_backend ()))
+    return TRUE;
+#endif
+
+  if (META_IS_BACKEND_X11 (meta_get_backend ()))
+    {
+      return (META_DISPLAY_HAS_XINPUT_23 (display) &&
+              !meta_is_wayland_compositor());
+    }
+
+  g_assert_not_reached ();
 }
 
 /**


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