mirror of
https://github.com/brl/mutter.git
synced 2024-11-21 23:50:41 -05:00
wayland: add support for pointer barriers
Use the clutter pointer constrain callback and a lot of copypasted code from Xorg to implement reactive pointer barriers and pointer barrier events. https://bugzilla.gnome.org/show_bug.cgi?id=706655
This commit is contained in:
parent
00a73c5bdc
commit
34398b273c
@ -52,6 +52,7 @@ libmutter_wayland_la_SOURCES = \
|
|||||||
core/async-getprop.h \
|
core/async-getprop.h \
|
||||||
core/barrier.c \
|
core/barrier.c \
|
||||||
meta/barrier.h \
|
meta/barrier.h \
|
||||||
|
core/barrier-private.h \
|
||||||
core/bell.c \
|
core/bell.c \
|
||||||
core/bell.h \
|
core/bell.h \
|
||||||
core/boxes.c \
|
core/boxes.c \
|
||||||
|
39
src/core/barrier-private.h
Normal file
39
src/core/barrier-private.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012, 2013 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Authors: Jaster St. Pierre <jstpierr@redhat.com>
|
||||||
|
* Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BARRIER_PRIVATE_H
|
||||||
|
#define BARRIER_PRIVATE_H
|
||||||
|
|
||||||
|
typedef struct _MetaBarrierManager MetaBarrierManager;
|
||||||
|
|
||||||
|
MetaBarrierManager *meta_barrier_manager_get (void);
|
||||||
|
|
||||||
|
void meta_barrier_manager_constrain_cursor (MetaBarrierManager *manager,
|
||||||
|
guint32 time,
|
||||||
|
float current_x,
|
||||||
|
float current_y,
|
||||||
|
float *new_x,
|
||||||
|
float *new_y);
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,27 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012, 2013 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Authors: Jaster St. Pierre <jstpierr@redhat.com>
|
||||||
|
* Giovanni Campagna <gcampagn@redhat.com>
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:barrier
|
* SECTION:barrier
|
||||||
* @Title: MetaBarrier
|
* @Title: MetaBarrier
|
||||||
@ -9,6 +31,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include <X11/extensions/XInput2.h>
|
#include <X11/extensions/XInput2.h>
|
||||||
#include <X11/extensions/Xfixes.h>
|
#include <X11/extensions/Xfixes.h>
|
||||||
@ -16,6 +39,7 @@
|
|||||||
#include <meta/barrier.h>
|
#include <meta/barrier.h>
|
||||||
#include "display-private.h"
|
#include "display-private.h"
|
||||||
#include "mutter-enum-types.h"
|
#include "mutter-enum-types.h"
|
||||||
|
#include "barrier-private.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
G_DEFINE_TYPE (MetaBarrier, meta_barrier, G_TYPE_OBJECT)
|
G_DEFINE_TYPE (MetaBarrier, meta_barrier, G_TYPE_OBJECT)
|
||||||
@ -56,9 +80,23 @@ struct _MetaBarrierPrivate
|
|||||||
|
|
||||||
MetaBarrierDirection directions;
|
MetaBarrierDirection directions;
|
||||||
|
|
||||||
|
/* x11 */
|
||||||
PointerBarrier xbarrier;
|
PointerBarrier xbarrier;
|
||||||
|
|
||||||
|
/* wayland */
|
||||||
|
gboolean active;
|
||||||
|
gboolean seen, hit;
|
||||||
|
|
||||||
|
int barrier_event_id;
|
||||||
|
int release_event_id;
|
||||||
|
guint32 last_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _MetaBarrierManager
|
||||||
|
{
|
||||||
|
GList *barriers;
|
||||||
|
} *global_barrier_manager;
|
||||||
|
|
||||||
static void meta_barrier_event_unref (MetaBarrierEvent *event);
|
static void meta_barrier_event_unref (MetaBarrierEvent *event);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -148,7 +186,10 @@ meta_barrier_dispose (GObject *object)
|
|||||||
gboolean
|
gboolean
|
||||||
meta_barrier_is_active (MetaBarrier *barrier)
|
meta_barrier_is_active (MetaBarrier *barrier)
|
||||||
{
|
{
|
||||||
return barrier->priv->xbarrier != 0;
|
if (meta_is_wayland_compositor ())
|
||||||
|
return barrier->priv->active;
|
||||||
|
else
|
||||||
|
return barrier->priv->xbarrier != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -165,15 +206,25 @@ void
|
|||||||
meta_barrier_release (MetaBarrier *barrier,
|
meta_barrier_release (MetaBarrier *barrier,
|
||||||
MetaBarrierEvent *event)
|
MetaBarrierEvent *event)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_XI23
|
MetaBarrierPrivate *priv;
|
||||||
MetaBarrierPrivate *priv = barrier->priv;
|
|
||||||
if (META_DISPLAY_HAS_XINPUT_23 (priv->display))
|
priv = barrier->priv;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
{
|
{
|
||||||
XIBarrierReleasePointer (priv->display->xdisplay,
|
priv->release_event_id = event->event_id;
|
||||||
META_VIRTUAL_CORE_POINTER_ID,
|
|
||||||
priv->xbarrier, event->event_id);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef HAVE_XI23
|
||||||
|
if (META_DISPLAY_HAS_XINPUT_23 (priv->display))
|
||||||
|
{
|
||||||
|
XIBarrierReleasePointer (priv->display->xdisplay,
|
||||||
|
META_VIRTUAL_CORE_POINTER_ID,
|
||||||
|
priv->xbarrier, event->event_id);
|
||||||
|
}
|
||||||
#endif /* HAVE_XI23 */
|
#endif /* HAVE_XI23 */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -192,19 +243,29 @@ meta_barrier_constructed (GObject *object)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dpy = priv->display->xdisplay;
|
if (meta_is_wayland_compositor ())
|
||||||
root = DefaultRootWindow (dpy);
|
{
|
||||||
|
MetaBarrierManager *manager = meta_barrier_manager_get ();
|
||||||
|
|
||||||
priv->xbarrier = XFixesCreatePointerBarrier (dpy, root,
|
manager->barriers = g_list_prepend (manager->barriers, g_object_ref (barrier));
|
||||||
priv->x1, priv->y1,
|
priv->active = TRUE;
|
||||||
priv->x2, priv->y2,
|
}
|
||||||
priv->directions, 0, NULL);
|
else
|
||||||
|
{
|
||||||
|
dpy = priv->display->xdisplay;
|
||||||
|
root = DefaultRootWindow (dpy);
|
||||||
|
|
||||||
/* Take a ref that we'll release when the XID dies inside destroy(),
|
priv->xbarrier = XFixesCreatePointerBarrier (dpy, root,
|
||||||
* so that the object stays alive and doesn't get GC'd. */
|
priv->x1, priv->y1,
|
||||||
g_object_ref (barrier);
|
priv->x2, priv->y2,
|
||||||
|
priv->directions, 0, NULL);
|
||||||
|
|
||||||
g_hash_table_insert (priv->display->xids, &priv->xbarrier, barrier);
|
/* Take a ref that we'll release when the XID dies inside destroy(),
|
||||||
|
* so that the object stays alive and doesn't get GC'd. */
|
||||||
|
g_object_ref (barrier);
|
||||||
|
|
||||||
|
g_hash_table_insert (priv->display->xids, &priv->xbarrier, barrier);
|
||||||
|
}
|
||||||
|
|
||||||
G_OBJECT_CLASS (meta_barrier_parent_class)->constructed (object);
|
G_OBJECT_CLASS (meta_barrier_parent_class)->constructed (object);
|
||||||
}
|
}
|
||||||
@ -312,16 +373,26 @@ meta_barrier_destroy (MetaBarrier *barrier)
|
|||||||
if (priv->display == NULL)
|
if (priv->display == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dpy = priv->display->xdisplay;
|
if (meta_is_wayland_compositor ())
|
||||||
|
{
|
||||||
|
MetaBarrierManager *manager = meta_barrier_manager_get ();
|
||||||
|
|
||||||
if (!meta_barrier_is_active (barrier))
|
manager->barriers = g_list_remove (manager->barriers, barrier);
|
||||||
return;
|
g_object_unref (barrier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dpy = priv->display->xdisplay;
|
||||||
|
|
||||||
XFixesDestroyPointerBarrier (dpy, priv->xbarrier);
|
if (!meta_barrier_is_active (barrier))
|
||||||
g_hash_table_remove (priv->display->xids, &priv->xbarrier);
|
return;
|
||||||
priv->xbarrier = 0;
|
|
||||||
|
|
||||||
g_object_unref (barrier);
|
XFixesDestroyPointerBarrier (dpy, priv->xbarrier);
|
||||||
|
g_hash_table_remove (priv->display->xids, &priv->xbarrier);
|
||||||
|
priv->xbarrier = 0;
|
||||||
|
|
||||||
|
g_object_unref (barrier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -371,6 +442,9 @@ meta_display_process_barrier_event (MetaDisplay *display,
|
|||||||
{
|
{
|
||||||
MetaBarrier *barrier;
|
MetaBarrier *barrier;
|
||||||
|
|
||||||
|
if (meta_is_wayland_compositor ())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
barrier = g_hash_table_lookup (display->xids, &xev->barrier);
|
barrier = g_hash_table_lookup (display->xids, &xev->barrier);
|
||||||
if (barrier != NULL)
|
if (barrier != NULL)
|
||||||
{
|
{
|
||||||
@ -382,6 +456,405 @@ meta_display_process_barrier_event (MetaDisplay *display,
|
|||||||
}
|
}
|
||||||
#endif /* HAVE_XI23 */
|
#endif /* HAVE_XI23 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following code was copied and adapted from the X server (Xi/xibarriers.c)
|
||||||
|
*
|
||||||
|
* Copyright 2012 Red Hat, Inc.
|
||||||
|
* Copyright © 2002 Keith Packard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
barrier_is_horizontal(MetaBarrier *barrier)
|
||||||
|
{
|
||||||
|
return barrier->priv->y1 == barrier->priv->y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
barrier_is_vertical(MetaBarrier *barrier)
|
||||||
|
{
|
||||||
|
return barrier->priv->x1 == barrier->priv->x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @return The set of barrier movement directions the movement vector
|
||||||
|
* x1/y1 → x2/y2 represents.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
barrier_get_direction(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
int direction = 0;
|
||||||
|
|
||||||
|
/* which way are we trying to go */
|
||||||
|
if (x2 > x1)
|
||||||
|
direction |= META_BARRIER_DIRECTION_POSITIVE_X;
|
||||||
|
if (x2 < x1)
|
||||||
|
direction |= META_BARRIER_DIRECTION_NEGATIVE_X;
|
||||||
|
if (y2 > y1)
|
||||||
|
direction |= META_BARRIER_DIRECTION_POSITIVE_Y;
|
||||||
|
if (y2 < y1)
|
||||||
|
direction |= META_BARRIER_DIRECTION_NEGATIVE_Y;
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test if the barrier may block movement in the direction defined by
|
||||||
|
* x1/y1 → x2/y2. This function only tests whether the directions could be
|
||||||
|
* blocked, it does not test if the barrier actually blocks the movement.
|
||||||
|
*
|
||||||
|
* @return TRUE if the barrier blocks the direction of movement or FALSE
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
barrier_is_blocking_direction(MetaBarrier *barrier,
|
||||||
|
MetaBarrierDirection direction)
|
||||||
|
{
|
||||||
|
/* Barriers define which way is ok, not which way is blocking */
|
||||||
|
return (barrier->priv->directions & direction) != direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
inside_segment(int v, int v1, int v2)
|
||||||
|
{
|
||||||
|
if (v1 < 0 && v2 < 0) /* line */
|
||||||
|
return TRUE;
|
||||||
|
else if (v1 < 0) /* ray */
|
||||||
|
return v <= v2;
|
||||||
|
else if (v2 < 0) /* ray */
|
||||||
|
return v >= v1;
|
||||||
|
else /* line segment */
|
||||||
|
return v >= v1 && v <= v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define T(v, a, b) (((float)v) - (a)) / ((b) - (a))
|
||||||
|
#define F(t, a, b) ((t) * ((a) - (b)) + (a))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test if the movement vector x1/y1 → x2/y2 is intersecting with the
|
||||||
|
* barrier. A movement vector with the startpoint or endpoint adjacent to
|
||||||
|
* the barrier itself counts as intersecting.
|
||||||
|
*
|
||||||
|
* @param x1 X start coordinate of movement vector
|
||||||
|
* @param y1 Y start coordinate of movement vector
|
||||||
|
* @param x2 X end coordinate of movement vector
|
||||||
|
* @param y2 Y end coordinate of movement vector
|
||||||
|
* @param[out] distance The distance between the start point and the
|
||||||
|
* intersection with the barrier (if applicable).
|
||||||
|
* @return TRUE if the barrier intersects with the given vector
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
barrier_is_blocking(MetaBarrier *barrier,
|
||||||
|
int x1, int y1, int x2, int y2, double *distance)
|
||||||
|
{
|
||||||
|
if (barrier_is_vertical (barrier))
|
||||||
|
{
|
||||||
|
float t, y;
|
||||||
|
t = T (barrier->priv->x1, x1, x2);
|
||||||
|
if (t < 0 || t > 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Edge case: moving away from barrier. */
|
||||||
|
if (x2 > x1 && t == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
y = F (t, y1, y2);
|
||||||
|
if (!inside_segment (y, barrier->priv->y1, barrier->priv->y2))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*distance = sqrt ((y - y1) * (y - y1) + (barrier->priv->x1 - x1) * (barrier->priv->x1 - x1));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float t, x;
|
||||||
|
t = T (barrier->priv->y1, y1, y2);
|
||||||
|
if (t < 0 || t > 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Edge case: moving away from barrier. */
|
||||||
|
if (y2 > y1 && t == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
x = F(t, x1, x2);
|
||||||
|
if (!inside_segment (x, barrier->priv->x1, barrier->priv->x2))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*distance = sqrt ((x - x1) * (x - x1) + (barrier->priv->y1 - y1) * (barrier->priv->y1 - y1));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HIT_EDGE_EXTENTS 2
|
||||||
|
static gboolean
|
||||||
|
barrier_inside_hit_box(MetaBarrier *barrier, int x, int y)
|
||||||
|
{
|
||||||
|
int x1, x2, y1, y2;
|
||||||
|
int dir;
|
||||||
|
|
||||||
|
x1 = barrier->priv->x1;
|
||||||
|
x2 = barrier->priv->x2;
|
||||||
|
y1 = barrier->priv->y1;
|
||||||
|
y2 = barrier->priv->y2;
|
||||||
|
dir = ~(barrier->priv->directions);
|
||||||
|
|
||||||
|
if (barrier_is_vertical (barrier))
|
||||||
|
{
|
||||||
|
if (dir & META_BARRIER_DIRECTION_POSITIVE_X)
|
||||||
|
x1 -= HIT_EDGE_EXTENTS;
|
||||||
|
if (dir & META_BARRIER_DIRECTION_NEGATIVE_X)
|
||||||
|
x2 += HIT_EDGE_EXTENTS;
|
||||||
|
}
|
||||||
|
if (barrier_is_horizontal (barrier))
|
||||||
|
{
|
||||||
|
if (dir & META_BARRIER_DIRECTION_POSITIVE_Y)
|
||||||
|
y1 -= HIT_EDGE_EXTENTS;
|
||||||
|
if (dir & META_BARRIER_DIRECTION_NEGATIVE_Y)
|
||||||
|
y2 += HIT_EDGE_EXTENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the nearest barrier client that is blocking movement from x1/y1 to x2/y2.
|
||||||
|
*
|
||||||
|
* @param dir Only barriers blocking movement in direction dir are checked
|
||||||
|
* @param x1 X start coordinate of movement vector
|
||||||
|
* @param y1 Y start coordinate of movement vector
|
||||||
|
* @param x2 X end coordinate of movement vector
|
||||||
|
* @param y2 Y end coordinate of movement vector
|
||||||
|
* @return The barrier nearest to the movement origin that blocks this movement.
|
||||||
|
*/
|
||||||
|
static MetaBarrier *
|
||||||
|
barrier_find_nearest(MetaBarrierManager *manager,
|
||||||
|
int dir,
|
||||||
|
int x1,
|
||||||
|
int y1,
|
||||||
|
int x2,
|
||||||
|
int y2)
|
||||||
|
{
|
||||||
|
GList *iter;
|
||||||
|
MetaBarrier *nearest = NULL;
|
||||||
|
double min_distance = INT_MAX; /* can't get higher than that in X anyway */
|
||||||
|
|
||||||
|
for (iter = manager->barriers; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
MetaBarrier *b = iter->data;
|
||||||
|
double distance;
|
||||||
|
|
||||||
|
if (b->priv->seen || !b->priv->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!barrier_is_blocking_direction (b, dir))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (barrier_is_blocking (b, x1, y1, x2, y2, &distance))
|
||||||
|
{
|
||||||
|
if (min_distance > distance)
|
||||||
|
{
|
||||||
|
min_distance = distance;
|
||||||
|
nearest = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clamp to the given barrier given the movement direction specified in dir.
|
||||||
|
*
|
||||||
|
* @param barrier The barrier to clamp to
|
||||||
|
* @param dir The movement direction
|
||||||
|
* @param[out] x The clamped x coordinate.
|
||||||
|
* @param[out] y The clamped x coordinate.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
barrier_clamp_to_barrier(MetaBarrier *barrier,
|
||||||
|
int dir,
|
||||||
|
float *x,
|
||||||
|
float *y)
|
||||||
|
{
|
||||||
|
if (barrier_is_vertical (barrier))
|
||||||
|
{
|
||||||
|
if ((dir & META_BARRIER_DIRECTION_NEGATIVE_X) & ~barrier->priv->directions)
|
||||||
|
*x = barrier->priv->x1;
|
||||||
|
if ((dir & META_BARRIER_DIRECTION_POSITIVE_X) & ~barrier->priv->directions)
|
||||||
|
*x = barrier->priv->x1 - 1;
|
||||||
|
}
|
||||||
|
if (barrier_is_horizontal (barrier))
|
||||||
|
{
|
||||||
|
if ((dir & META_BARRIER_DIRECTION_NEGATIVE_Y) & ~barrier->priv->directions)
|
||||||
|
*y = barrier->priv->y1;
|
||||||
|
if ((dir & META_BARRIER_DIRECTION_POSITIVE_Y) & ~barrier->priv->directions)
|
||||||
|
*y = barrier->priv->y1 - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_hit_event (gpointer data)
|
||||||
|
{
|
||||||
|
MetaBarrierEvent *event = data;
|
||||||
|
|
||||||
|
g_signal_emit (event->barrier, obj_signals[HIT], 0, event);
|
||||||
|
|
||||||
|
meta_barrier_event_unref (event);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
emit_left_event (gpointer data)
|
||||||
|
{
|
||||||
|
MetaBarrierEvent *event = data;
|
||||||
|
|
||||||
|
g_signal_emit (event->barrier, obj_signals[LEFT], 0, event);
|
||||||
|
|
||||||
|
meta_barrier_event_unref (event);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_barrier_manager_constrain_cursor (MetaBarrierManager *manager,
|
||||||
|
guint32 time,
|
||||||
|
float current_x,
|
||||||
|
float current_y,
|
||||||
|
float *new_x,
|
||||||
|
float *new_y)
|
||||||
|
{
|
||||||
|
float x = *new_x;
|
||||||
|
float y = *new_y;
|
||||||
|
int dir;
|
||||||
|
MetaBarrier *nearest = NULL;
|
||||||
|
GList *iter;
|
||||||
|
float dx = x - current_x;
|
||||||
|
float dy = y - current_y;
|
||||||
|
|
||||||
|
/* How this works:
|
||||||
|
* Given the origin and the movement vector, get the nearest barrier
|
||||||
|
* to the origin that is blocking the movement.
|
||||||
|
* Clamp to that barrier.
|
||||||
|
* Then, check from the clamped intersection to the original
|
||||||
|
* destination, again finding the nearest barrier and clamping.
|
||||||
|
*/
|
||||||
|
dir = barrier_get_direction (current_x, current_y, x, y);
|
||||||
|
|
||||||
|
while (dir != 0)
|
||||||
|
{
|
||||||
|
MetaBarrierEvent *event;
|
||||||
|
gboolean new_sequence;
|
||||||
|
|
||||||
|
nearest = barrier_find_nearest (manager, dir, current_x, current_y, x, y);
|
||||||
|
if (!nearest)
|
||||||
|
break;
|
||||||
|
|
||||||
|
new_sequence = !nearest->priv->hit;
|
||||||
|
|
||||||
|
nearest->priv->seen = TRUE;
|
||||||
|
nearest->priv->hit = TRUE;
|
||||||
|
|
||||||
|
if (nearest->priv->barrier_event_id == nearest->priv->release_event_id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
barrier_clamp_to_barrier (nearest, dir, &x, &y);
|
||||||
|
|
||||||
|
if (barrier_is_vertical (nearest))
|
||||||
|
{
|
||||||
|
dir &= ~(META_BARRIER_DIRECTION_NEGATIVE_X | META_BARRIER_DIRECTION_POSITIVE_X);
|
||||||
|
current_x = x;
|
||||||
|
}
|
||||||
|
else if (barrier_is_horizontal (nearest))
|
||||||
|
{
|
||||||
|
dir &= ~(META_BARRIER_DIRECTION_NEGATIVE_Y | META_BARRIER_DIRECTION_POSITIVE_Y);
|
||||||
|
current_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = g_slice_new0 (MetaBarrierEvent);
|
||||||
|
|
||||||
|
event->ref_count = 1;
|
||||||
|
event->barrier = g_object_ref (nearest);
|
||||||
|
event->event_id = nearest->priv->barrier_event_id;
|
||||||
|
event->time = time;
|
||||||
|
event->dt = new_sequence ? 0 : time - nearest->priv->last_timestamp;
|
||||||
|
event->x = current_x;
|
||||||
|
event->y = current_y;
|
||||||
|
event->dx = dx;
|
||||||
|
event->dy = dy;
|
||||||
|
event->released = FALSE;
|
||||||
|
event->grabbed = FALSE;
|
||||||
|
|
||||||
|
g_idle_add (emit_hit_event, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (iter = manager->barriers; iter; iter = iter->next)
|
||||||
|
{
|
||||||
|
MetaBarrierEvent *event;
|
||||||
|
MetaBarrier *barrier = iter->data;
|
||||||
|
|
||||||
|
if (!barrier->priv->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
barrier->priv->seen = FALSE;
|
||||||
|
if (!barrier->priv->hit)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (barrier_inside_hit_box (barrier, x, y))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
barrier->priv->hit = FALSE;
|
||||||
|
|
||||||
|
event = g_slice_new0 (MetaBarrierEvent);
|
||||||
|
|
||||||
|
event->ref_count = 1;
|
||||||
|
event->barrier = g_object_ref (barrier);
|
||||||
|
event->event_id = barrier->priv->barrier_event_id;
|
||||||
|
event->time = time;
|
||||||
|
event->dt = time - barrier->priv->last_timestamp;
|
||||||
|
event->x = current_x;
|
||||||
|
event->y = current_y;
|
||||||
|
event->dx = dx;
|
||||||
|
event->dy = dy;
|
||||||
|
event->released = barrier->priv->barrier_event_id == barrier->priv->release_event_id;
|
||||||
|
event->grabbed = FALSE;
|
||||||
|
|
||||||
|
g_idle_add (emit_left_event, event);
|
||||||
|
|
||||||
|
/* If we've left the hit box, this is the
|
||||||
|
* start of a new event ID. */
|
||||||
|
barrier->priv->barrier_event_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*new_x = x;
|
||||||
|
*new_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaBarrierManager *
|
||||||
|
meta_barrier_manager_get (void)
|
||||||
|
{
|
||||||
|
if (!global_barrier_manager)
|
||||||
|
global_barrier_manager = g_new0 (MetaBarrierManager, 1);
|
||||||
|
|
||||||
|
return global_barrier_manager;
|
||||||
|
}
|
||||||
|
|
||||||
static MetaBarrierEvent *
|
static MetaBarrierEvent *
|
||||||
meta_barrier_event_ref (MetaBarrierEvent *event)
|
meta_barrier_event_ref (MetaBarrierEvent *event)
|
||||||
{
|
{
|
||||||
@ -399,7 +872,12 @@ meta_barrier_event_unref (MetaBarrierEvent *event)
|
|||||||
g_return_if_fail (event->ref_count > 0);
|
g_return_if_fail (event->ref_count > 0);
|
||||||
|
|
||||||
if (g_atomic_int_dec_and_test ((volatile int *)&event->ref_count))
|
if (g_atomic_int_dec_and_test ((volatile int *)&event->ref_count))
|
||||||
g_slice_free (MetaBarrierEvent, event);
|
{
|
||||||
|
if (event->barrier)
|
||||||
|
g_object_unref (event->barrier);
|
||||||
|
|
||||||
|
g_slice_free (MetaBarrierEvent, event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
G_DEFINE_BOXED_TYPE (MetaBarrierEvent,
|
G_DEFINE_BOXED_TYPE (MetaBarrierEvent,
|
||||||
|
@ -6078,7 +6078,7 @@ meta_display_get_xinput_opcode (MetaDisplay *display)
|
|||||||
gboolean
|
gboolean
|
||||||
meta_display_supports_extended_barriers (MetaDisplay *display)
|
meta_display_supports_extended_barriers (MetaDisplay *display)
|
||||||
{
|
{
|
||||||
return META_DISPLAY_HAS_XINPUT_23 (display) && !meta_is_wayland_compositor ();
|
return meta_is_wayland_compositor () || META_DISPLAY_HAS_XINPUT_23 (display);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,6 +94,7 @@ typedef enum {
|
|||||||
struct _MetaBarrierEvent {
|
struct _MetaBarrierEvent {
|
||||||
/* < private > */
|
/* < private > */
|
||||||
volatile guint ref_count;
|
volatile guint ref_count;
|
||||||
|
MetaBarrier *barrier;
|
||||||
|
|
||||||
/* < public > */
|
/* < public > */
|
||||||
int event_id;
|
int event_id;
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
|
|
||||||
#include "meta-wayland-pointer.h"
|
#include "meta-wayland-pointer.h"
|
||||||
#include "meta-wayland-private.h"
|
#include "meta-wayland-private.h"
|
||||||
|
#include "barrier-private.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -165,10 +166,10 @@ static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
check_all_screen_monitors(MetaMonitorInfo *monitors,
|
check_all_screen_monitors (MetaMonitorInfo *monitors,
|
||||||
unsigned n_monitors,
|
unsigned n_monitors,
|
||||||
float x,
|
float x,
|
||||||
float y)
|
float y)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
@ -193,14 +194,13 @@ static void
|
|||||||
constrain_all_screen_monitors (ClutterInputDevice *device,
|
constrain_all_screen_monitors (ClutterInputDevice *device,
|
||||||
MetaMonitorInfo *monitors,
|
MetaMonitorInfo *monitors,
|
||||||
unsigned n_monitors,
|
unsigned n_monitors,
|
||||||
|
float current_x,
|
||||||
|
float current_y,
|
||||||
float *x,
|
float *x,
|
||||||
float *y)
|
float *y)
|
||||||
{
|
{
|
||||||
ClutterPoint current;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
clutter_input_device_get_coords (device, NULL, ¤t);
|
|
||||||
|
|
||||||
/* if we're trying to escape, clamp to the CRTC we're coming from */
|
/* if we're trying to escape, clamp to the CRTC we're coming from */
|
||||||
for (i = 0; i < n_monitors; i++)
|
for (i = 0; i < n_monitors; i++)
|
||||||
{
|
{
|
||||||
@ -213,8 +213,8 @@ constrain_all_screen_monitors (ClutterInputDevice *device,
|
|||||||
top = monitor->rect.y;
|
top = monitor->rect.y;
|
||||||
bottom = left + monitor->rect.height;
|
bottom = left + monitor->rect.height;
|
||||||
|
|
||||||
nx = current.x;
|
nx = current_x;
|
||||||
ny = current.y;
|
ny = current_y;
|
||||||
|
|
||||||
if ((nx >= left) && (nx < right) && (ny >= top) && (ny < bottom))
|
if ((nx >= left) && (nx < right) && (ny >= top) && (ny < bottom))
|
||||||
{
|
{
|
||||||
@ -239,21 +239,32 @@ pointer_constrain_callback (ClutterInputDevice *device,
|
|||||||
float *new_y,
|
float *new_y,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
MetaBarrierManager *barrier_manager;
|
||||||
MetaMonitorManager *monitor_manager;
|
MetaMonitorManager *monitor_manager;
|
||||||
MetaMonitorInfo *monitors;
|
MetaMonitorInfo *monitors;
|
||||||
unsigned int n_monitors;
|
unsigned int n_monitors;
|
||||||
gboolean ret;
|
gboolean ret;
|
||||||
|
ClutterPoint current;
|
||||||
|
|
||||||
|
clutter_input_device_get_coords (device, NULL, ¤t);
|
||||||
|
|
||||||
|
barrier_manager = meta_barrier_manager_get ();
|
||||||
monitor_manager = meta_monitor_manager_get ();
|
monitor_manager = meta_monitor_manager_get ();
|
||||||
monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors);
|
monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors);
|
||||||
|
|
||||||
|
meta_barrier_manager_constrain_cursor (barrier_manager, time,
|
||||||
|
current.x, current.y,
|
||||||
|
new_x, new_y);
|
||||||
|
|
||||||
/* if we're moving inside a monitor, we're fine */
|
/* if we're moving inside a monitor, we're fine */
|
||||||
ret = check_all_screen_monitors(monitors, n_monitors, *new_x, *new_y);
|
ret = check_all_screen_monitors(monitors, n_monitors, *new_x, *new_y);
|
||||||
if (ret == TRUE)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* if we're trying to escape, clamp to the CRTC we're coming from */
|
/* if we're trying to escape, clamp to the CRTC we're coming from */
|
||||||
constrain_all_screen_monitors(device, monitors, n_monitors, new_x, new_y);
|
constrain_all_screen_monitors(device, monitors, n_monitors,
|
||||||
|
current.x, current.y,
|
||||||
|
new_x, new_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
Loading…
Reference in New Issue
Block a user