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
This commit is contained in:
parent
1dea1813b1
commit
db384a656c
@ -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 \
|
||||
|
@ -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 ())
|
||||
|
32
src/backends/native/meta-backend-native-private.h
Normal file
32
src/backends/native/meta-backend-native-private.h
Normal file
@ -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 */
|
@ -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:
|
||||
*
|
||||
|
744
src/backends/native/meta-barrier-native.c
Normal file
744
src/backends/native/meta-barrier-native.c
Normal file
@ -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;
|
||||
}
|
68
src/backends/native/meta-barrier-native.h
Normal file
68
src/backends/native/meta-barrier-native.h
Normal file
@ -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 */
|
@ -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 ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user