[PATCH] fixes: Add support for pointer barriers
Peter Hutterer
peter.hutterer at who-t.net
Wed May 25 23:05:34 PDT 2011
On Wed, May 25, 2011 at 05:54:35AM -0400, Adam Jackson wrote:
> Implements pointer barriers as specified by version 5 of the XFIXES
> protocol. Barriers are axis-aligned, zero-width lines that block pointer
> movement for relative input devices. Barriers may block motion in either
> the positive or negative direction, or both.
>
> v3:
> - Fix off-by-one in version_requests array
> - Port to non-glib test harness
> - Fix review notes from Søren Sandmann Pedersen, add tests to match
>
> Co-authored-by: Peter Hutterer <peter.hutterer at who-t.net>
> Signed-off-by: Adam Jackson <ajax at redhat.com>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
well, with this in the tree gnome shell is significantly nicer to use on a
dualhead setup.
Tested-by: Peter Hutterer <peter.hutterer at who-t.net>
Cheers,
Peter
> ---
> configure.ac | 2 +-
> include/protocol-versions.h | 2 +-
> test/Makefile.am | 3 +-
> test/fixes.c | 327 +++++++++++++++++++++++++++++++++++
> xfixes/cursor.c | 399 ++++++++++++++++++++++++++++++++++++++++++-
> xfixes/xfixes.c | 24 ++-
> xfixes/xfixes.h | 17 ++
> xfixes/xfixesint.h | 16 ++
> 8 files changed, 777 insertions(+), 13 deletions(-)
> create mode 100644 test/fixes.c
>
> diff --git a/configure.ac b/configure.ac
> index 655c0e4..ba1d176 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -810,7 +810,7 @@ dnl specific modules against it
> PKG_CHECK_MODULES(PIXMAN, $LIBPIXMAN)
> REQUIRED_LIBS="$REQUIRED_LIBS $LIBPIXMAN $LIBXFONT xau"
>
> -REQUIRED_MODULES="[fixesproto >= 4.1] [damageproto >= 1.1] [xcmiscproto >= 1.2.0] [xtrans >= 1.2.2] [bigreqsproto >= 1.1.0] $SDK_REQUIRED_MODULES"
> +REQUIRED_MODULES="[fixesproto >= 5.0] [damageproto >= 1.1] [xcmiscproto >= 1.2.0] [xtrans >= 1.2.2] [bigreqsproto >= 1.1.0] $SDK_REQUIRED_MODULES"
>
> if test "x$CONFIG_UDEV" = xyes &&
> { test "x$CONFIG_DBUS_API" = xyes || test "x$CONFIG_HAL" = xyes; }; then
> diff --git a/include/protocol-versions.h b/include/protocol-versions.h
> index 8692ded..7b7a9f5 100644
> --- a/include/protocol-versions.h
> +++ b/include/protocol-versions.h
> @@ -122,7 +122,7 @@
> #define SERVER_XF86VIDMODE_MINOR_VERSION 2
>
> /* Fixes */
> -#define SERVER_XFIXES_MAJOR_VERSION 4
> +#define SERVER_XFIXES_MAJOR_VERSION 5
> #define SERVER_XFIXES_MINOR_VERSION 0
>
> /* X Input */
> diff --git a/test/Makefile.am b/test/Makefile.am
> index b7ee070..5574e7d 100644
> --- a/test/Makefile.am
> +++ b/test/Makefile.am
> @@ -1,7 +1,7 @@
> if ENABLE_UNIT_TESTS
> if HAVE_LD_WRAP
> SUBDIRS= . xi2
> -noinst_PROGRAMS = xkb input xtest list misc
> +noinst_PROGRAMS = xkb input xtest list misc fixes
> check_LTLIBRARIES = libxservertest.la
>
> TESTS=$(noinst_PROGRAMS)
> @@ -19,6 +19,7 @@ input_LDADD=$(TEST_LDADD)
> xtest_LDADD=$(TEST_LDADD)
> list_LDADD=$(TEST_LDADD)
> misc_LDADD=$(TEST_LDADD)
> +fixes_LDADD=$(TEST_LDADD)
>
> libxservertest_la_LIBADD = \
> $(XSERVER_LIBS) \
> diff --git a/test/fixes.c b/test/fixes.c
> new file mode 100644
> index 0000000..8c804ba
> --- /dev/null
> +++ b/test/fixes.c
> @@ -0,0 +1,327 @@
> +/**
> + * Copyright © 2011 Red Hat, Inc.
> + *
> + * 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.
> + */
> +
> +#ifdef HAVE_DIX_CONFIG_H
> +#include <dix-config.h>
> +#endif
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <X11/X.h>
> +#include <xfixesint.h>
> +#include <X11/extensions/xfixeswire.h>
> +
> +static void
> +_fixes_test_direction(struct PointerBarrier *barrier, int d[4], int permitted)
> +{
> + BOOL blocking;
> + int i, j;
> + int dir = barrier_get_direction(d[0], d[1], d[2], d[3]);
> +
> + barrier->directions = 0;
> + blocking = barrier_is_blocking_direction(barrier, dir);
> + assert(blocking);
> +
> + for (j = 0; j <= BarrierNegativeY; j++)
> + {
> + for (i = 0; i <= BarrierNegativeY; i++)
> + {
> + barrier->directions |= 1 << i;
> + blocking = barrier_is_blocking_direction(barrier, dir);
> + assert((barrier->directions & permitted) == permitted ? !blocking : blocking);
> + }
> + }
> +
> +}
> +
> +static void
> +fixes_pointer_barrier_direction_test(void)
> +{
> + struct PointerBarrier barrier;
> +
> + int x = 100;
> + int y = 100;
> +
> + int directions[8][4] = {
> + { x, y, x, y + 100}, /* S */
> + { x + 50, y, x - 50, y + 100}, /* SW */
> + { x + 100, y, x, y}, /* W */
> + { x + 100, y + 50, x, y - 50}, /* NW */
> + { x, y + 100, x, y}, /* N */
> + { x - 50, y + 100, x + 50, y}, /* NE */
> + { x, y, x + 100, y}, /* E */
> + { x, y - 50, x + 100, y + 50}, /* SE */
> + };
> +
> + barrier.x1 = x;
> + barrier.x2 = x;
> + barrier.y1 = y - 50;
> + barrier.y2 = y + 49;
> +
> +
> + _fixes_test_direction(&barrier, directions[0], BarrierPositiveY);
> + _fixes_test_direction(&barrier, directions[1], BarrierPositiveY | BarrierNegativeX);
> + _fixes_test_direction(&barrier, directions[2], BarrierNegativeX);
> + _fixes_test_direction(&barrier, directions[3], BarrierNegativeY | BarrierNegativeX);
> + _fixes_test_direction(&barrier, directions[4], BarrierNegativeY);
> + _fixes_test_direction(&barrier, directions[5], BarrierPositiveX | BarrierNegativeY);
> + _fixes_test_direction(&barrier, directions[6], BarrierPositiveX);
> + _fixes_test_direction(&barrier, directions[7], BarrierPositiveY | BarrierPositiveX);
> +
> +
> +}
> +
> +
> +static void
> +fixes_pointer_barriers_test(void)
> +{
> + struct PointerBarrier barrier;
> + int x1, y1, x2, y2;
> + double distance;
> +
> + int x = 100;
> + int y = 100;
> +
> + /* vert barrier */
> + barrier.x1 = x;
> + barrier.x2 = x;
> + barrier.y1 = y - 50;
> + barrier.y2 = y + 50;
> +
> + /* across at half-way */
> + x1 = x + 1;
> + x2 = x - 1;
> + y1 = y;
> + y2 = y;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> + assert(distance == 1);
> +
> + /* definitely not across */
> + x1 = x + 10;
> + x2 = x + 5;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across, but outside of y range */
> + x1 = x + 1;
> + x2 = x -1;
> + y1 = y + 100;
> + y2 = y + 100;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across, diagonally */
> + x1 = x + 5;
> + x2 = x - 5;
> + y1 = y + 5;
> + y2 = y - 5;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across but outside boundary, diagonally */
> + x1 = x + 5;
> + x2 = x - 5;
> + y1 = y + 100;
> + y2 = y + 50;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → blocking */
> + x1 = x;
> + x2 = x - 1;
> + y1 = y;
> + y2 = y;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → not blocking, positive */
> + x1 = x;
> + x2 = x + 1;
> + y1 = y;
> + y2 = y;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → not blocking, negative */
> + x1 = x - 1;
> + x2 = x - 2;
> + y1 = y;
> + y2 = y;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: endpoint of movement on barrier → blocking */
> + x1 = x + 1;
> + x2 = x;
> + y1 = y;
> + y2 = y;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* startpoint on barrier but outside y range */
> + x1 = x;
> + x2 = x - 1;
> + y1 = y + 100;
> + y2 = y + 100;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* endpoint on barrier but outside y range */
> + x1 = x + 1;
> + x2 = x;
> + y1 = y + 100;
> + y2 = y + 100;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> +
> + /* horizontal barrier */
> + barrier.x1 = x - 50;
> + barrier.x2 = x + 50;
> + barrier.y1 = y;
> + barrier.y2 = y;
> +
> + /* across at half-way */
> + x1 = x;
> + x2 = x;
> + y1 = y - 1;
> + y2 = y + 1;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* definitely not across */
> + y1 = y + 10;
> + y2 = y + 5;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across, but outside of y range */
> + x1 = x + 100;
> + x2 = x + 100;
> + y1 = y + 1;
> + y2 = y -1;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across, diagonally */
> + y1 = y + 5;
> + y2 = y - 5;
> + x1 = x + 5;
> + x2 = x - 5;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* across but outside boundary, diagonally */
> + y1 = y + 5;
> + y2 = y - 5;
> + x1 = x + 100;
> + x2 = x + 50;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → blocking */
> + y1 = y;
> + y2 = y - 1;
> + x1 = x;
> + x2 = x;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → not blocking, positive */
> + y1 = y;
> + y2 = y + 1;
> + x1 = x;
> + x2 = x;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: startpoint of movement on barrier → not blocking, negative */
> + y1 = y - 1;
> + y2 = y - 2;
> + x1 = x;
> + x2 = x;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* edge case: endpoint of movement on barrier → blocking */
> + y1 = y + 1;
> + y2 = y;
> + x1 = x;
> + x2 = x;
> + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* startpoint on barrier but outside y range */
> + y1 = y;
> + y2 = y - 1;
> + x1 = x + 100;
> + x2 = x + 100;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> + /* endpoint on barrier but outside y range */
> + y1 = y + 1;
> + y2 = y;
> + x1 = x + 100;
> + x2 = x + 100;
> + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
> +
> +}
> +
> +static void fixes_pointer_barrier_clamp_test(void)
> +{
> + struct PointerBarrier barrier;
> +
> + int x = 100;
> + int y = 100;
> +
> + int cx, cy; /* clamped */
> +
> + /* vert barrier */
> + barrier.x1 = x;
> + barrier.x2 = x;
> + barrier.y1 = y - 50;
> + barrier.y2 = y + 49;
> + barrier.directions = 0;
> +
> + cx = INT_MAX;
> + cy = INT_MAX;
> + barrier_clamp_to_barrier(&barrier, BarrierPositiveX, &cx, &cy);
> + assert(cx == barrier.x1 - 1);
> + assert(cy == INT_MAX);
> +
> + cx = 0;
> + cy = INT_MAX;
> + barrier_clamp_to_barrier(&barrier, BarrierNegativeX, &cx, &cy);
> + assert(cx == barrier.x1);
> + assert(cy == INT_MAX);
> +
> + /* horiz barrier */
> + barrier.x1 = x - 50;
> + barrier.x2 = x + 49;
> + barrier.y1 = y;
> + barrier.y2 = y;
> + barrier.directions = 0;
> +
> + cx = INT_MAX;
> + cy = INT_MAX;
> + barrier_clamp_to_barrier(&barrier, BarrierPositiveY, &cx, &cy);
> + assert(cx == INT_MAX);
> + assert(cy == barrier.y1 - 1);
> +
> + cx = INT_MAX;
> + cy = 0;
> + barrier_clamp_to_barrier(&barrier, BarrierNegativeY, &cx, &cy);
> + assert(cx == INT_MAX);
> + assert(cy == barrier.y1);
> +}
> +
> +int main(int argc, char** argv)
> +{
> +
> + fixes_pointer_barriers_test();
> + fixes_pointer_barrier_direction_test();
> + fixes_pointer_barrier_clamp_test();
> +
> + return 0;
> +}
> diff --git a/xfixes/cursor.c b/xfixes/cursor.c
> index fb608f6..01eb70d 100644
> --- a/xfixes/cursor.c
> +++ b/xfixes/cursor.c
> @@ -1,5 +1,6 @@
> /*
> * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
> + * Copyright 2010 Red Hat, Inc.
> *
> * Permission is hereby granted, free of charge, to any person obtaining a
> * copy of this software and associated documentation files (the "Software"),
> @@ -50,13 +51,16 @@
> #include "cursorstr.h"
> #include "dixevents.h"
> #include "servermd.h"
> +#include "mipointer.h"
> #include "inputstr.h"
> #include "windowstr.h"
> #include "xace.h"
> +#include "list.h"
>
> static RESTYPE CursorClientType;
> static RESTYPE CursorHideCountType;
> static RESTYPE CursorWindowType;
> +RESTYPE PointerBarrierType;
> static CursorPtr CursorCurrent[MAXDEVICES];
>
> static DevPrivateKeyRec CursorScreenPrivateKeyRec;
> @@ -107,6 +111,14 @@ typedef struct _CursorHideCountRec {
> XID resource;
> } CursorHideCountRec;
>
> +typedef struct PointerBarrierClient *PointerBarrierClientPtr;
> +
> +struct PointerBarrierClient {
> + ScreenPtr screen;
> + struct PointerBarrier barrier;
> + struct list entry;
> +};
> +
> /*
> * Wrap DisplayCursor to catch cursor change events
> */
> @@ -114,7 +126,9 @@ typedef struct _CursorHideCountRec {
> typedef struct _CursorScreen {
> DisplayCursorProcPtr DisplayCursor;
> CloseScreenProcPtr CloseScreen;
> + ConstrainCursorHarderProcPtr ConstrainCursorHarder;
> CursorHideCountPtr pCursorHideCounts;
> + struct list barriers;
> } CursorScreenRec, *CursorScreenPtr;
>
> #define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey))
> @@ -184,9 +198,11 @@ CursorCloseScreen (int index, ScreenPtr pScreen)
> Bool ret;
> CloseScreenProcPtr close_proc;
> DisplayCursorProcPtr display_proc;
> + ConstrainCursorHarderProcPtr constrain_proc;
>
> Unwrap (cs, pScreen, CloseScreen, close_proc);
> Unwrap (cs, pScreen, DisplayCursor, display_proc);
> + Unwrap (cs, pScreen, ConstrainCursorHarder, constrain_proc);
> deleteCursorHideCountsForScreen(pScreen);
> ret = (*pScreen->CloseScreen) (index, pScreen);
> free(cs);
> @@ -1029,6 +1045,382 @@ CursorFreeWindow (pointer data, XID id)
> return 1;
> }
>
> +static BOOL
> +barrier_is_horizontal(const struct PointerBarrier *barrier)
> +{
> + return barrier->y1 == barrier->y2;
> +}
> +
> +static BOOL
> +barrier_is_vertical(const struct PointerBarrier *barrier)
> +{
> + return barrier->x1 == barrier->x2;
> +}
> +
> +/**
> + * @return The set of barrier movement directions the movement vector
> + * x1/y1 → x2/y2 represents.
> + */
> +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 |= BarrierPositiveX;
> + if (x2 < x1)
> + direction |= BarrierNegativeX;
> + if (y2 > y1)
> + direction |= BarrierPositiveY;
> + if (y2 < y1)
> + direction |= BarrierNegativeY;
> +
> + 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.
> + */
> +BOOL
> +barrier_is_blocking_direction(const struct PointerBarrier *barrier, int direction)
> +{
> + /* Barriers define which way is ok, not which way is blocking */
> + return (barrier->directions & direction) != direction;
> +}
> +
> +/**
> + * 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
> + */
> +BOOL
> +barrier_is_blocking(const struct PointerBarrier *barrier,
> + int x1, int y1, int x2, int y2,
> + double *distance)
> +{
> + BOOL rc = FALSE;
> + float ua, ub, ud;
> + int dir = barrier_get_direction(x1, y1, x2, y2);
> +
> + /* Algorithm below doesn't handle edge cases well, hence the extra
> + * checks. */
> + if (barrier_is_vertical(barrier)) {
> + /* handle immediate barrier adjacency, moving away */
> + if (dir & BarrierPositiveX && x1 == barrier->x1)
> + return FALSE;
> + if (dir & BarrierNegativeX && x1 == (barrier->x1 - 1))
> + return FALSE;
> + /* startpoint adjacent to barrier, moving towards -> block */
> + if (x1 == barrier->x1 && y1 >= barrier->y1 && y1 <= barrier->y2) {
> + *distance = 0;
> + return TRUE;
> + }
> + } else {
> + /* handle immediate barrier adjacency, moving away */
> + if (dir & BarrierPositiveY && y1 == barrier->y1)
> + return FALSE;
> + if (dir & BarrierNegativeY && y1 == (barrier->y1 - 1))
> + return FALSE;
> + /* startpoint adjacent to barrier, moving towards -> block */
> + if (y1 == barrier->y1 && x1 >= barrier->x1 && x1 <= barrier->x2) {
> + *distance = 0;
> + return TRUE;
> + }
> + }
> +
> + /* not an edge case, compute distance */
> + ua = 0;
> + ud = (barrier->y2 - barrier->y1) * (x2 - x1) - (barrier->x2 - barrier->x1) * (y2 - y1);
> + if (ud != 0) {
> + ua = ((barrier->x2 - barrier->x1) * (y1 - barrier->y1) -
> + (barrier->y2 - barrier->y1) * (x1 - barrier->x1)) / ud;
> + ub = ((x2 - x1) * (y1 - barrier->y1) -
> + (y2 - y1) * (x1 - barrier->x1)) / ud;
> + if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
> + ua = 0;
> + }
> +
> + if (ua > 0 && ua <= 1)
> + {
> + double ix = barrier->x1 + ua * (barrier->x2 - barrier->x1);
> + double iy = barrier->y1 + ua * (barrier->y2 - barrier->y1);
> +
> + *distance = sqrt(pow(x1 - ix, 2) + pow(y1 - iy, 2));
> + rc = TRUE;
> + }
> +
> + return rc;
> +}
> +
> +/**
> + * Find the nearest barrier 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 struct PointerBarrier*
> +barrier_find_nearest(CursorScreenPtr cs, int dir,
> + int x1, int y1, int x2, int y2)
> +{
> + struct PointerBarrierClient *c;
> + struct PointerBarrier *nearest = NULL;
> + double min_distance = INT_MAX; /* can't get higher than that in X anyway */
> +
> + list_for_each_entry(c, &cs->barriers, entry) {
> + struct PointerBarrier *b = &c->barrier;
> + double distance;
> +
> + 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.
> + */
> +void
> +barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, int *y)
> +{
> + if (barrier_is_vertical(barrier))
> + {
> + if ((dir & BarrierNegativeX) & ~barrier->directions)
> + *x = barrier->x1;
> + if ((dir & BarrierPositiveX) & ~barrier->directions)
> + *x = barrier->x1 - 1;
> + }
> + if (barrier_is_horizontal(barrier))
> + {
> + if ((dir & BarrierNegativeY) & ~barrier->directions)
> + *y = barrier->y1;
> + if ((dir & BarrierPositiveY) & ~barrier->directions)
> + *y = barrier->y1 - 1;
> + }
> +}
> +
> +static void
> +CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode, int *x, int *y)
> +{
> + CursorScreenPtr cs = GetCursorScreen(screen);
> +
> + if (!list_is_empty(&cs->barriers) && !IsFloating(dev) && mode == Relative) {
> + int ox, oy;
> + int dir;
> + struct PointerBarrier *nearest = NULL;
> +
> + /* where are we coming from */
> + miPointerGetPosition(dev, &ox, &oy);
> +
> + /* 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(ox, oy, *x, *y);
> +
> + nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
> + if (nearest) {
> + barrier_clamp_to_barrier(nearest, dir, x, y);
> +
> + if (barrier_is_vertical(nearest)) {
> + dir &= ~(BarrierNegativeX | BarrierPositiveX);
> + ox = *x;
> + } else if (barrier_is_horizontal(nearest)) {
> + dir &= ~(BarrierNegativeY | BarrierPositiveY);
> + oy = *y;
> + }
> +
> + nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
> + if (nearest) {
> + barrier_clamp_to_barrier(nearest, dir, x, y);
> + }
> + }
> + }
> +
> + if (cs->ConstrainCursorHarder) {
> + screen->ConstrainCursorHarder = cs->ConstrainCursorHarder;
> + screen->ConstrainCursorHarder(dev, screen, mode, x, y);
> + screen->ConstrainCursorHarder = CursorConstrainCursorHarder;
> + }
> +}
> +
> +static struct PointerBarrierClient *
> +CreatePointerBarrierClient(ScreenPtr screen, ClientPtr client,
> + xXFixesCreatePointerBarrierReq *stuff)
> +{
> + CursorScreenPtr cs = GetCursorScreen(screen);
> + struct PointerBarrierClient *ret = malloc(sizeof(*ret));
> +
> + if (ret) {
> + ret->screen = screen;
> + ret->barrier.x1 = min(stuff->x1, stuff->x2);
> + ret->barrier.x2 = max(stuff->x1, stuff->x2);
> + ret->barrier.y1 = min(stuff->y1, stuff->y2);
> + ret->barrier.y2 = max(stuff->y1, stuff->y2);
> + ret->barrier.directions = stuff->directions & 0x0f;
> + if (barrier_is_horizontal(&ret->barrier))
> + ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
> + if (barrier_is_vertical(&ret->barrier))
> + ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
> + list_add(&ret->entry, &cs->barriers);
> + }
> +
> + return ret;
> +}
> +
> +int
> +ProcXFixesCreatePointerBarrier (ClientPtr client)
> +{
> + int err;
> + WindowPtr pWin;
> + struct PointerBarrierClient *barrier;
> + struct PointerBarrier b;
> + REQUEST (xXFixesCreatePointerBarrierReq);
> +
> + REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
> + LEGAL_NEW_RESOURCE(stuff->barrier, client);
> +
> + err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
> + if (err != Success) {
> + client->errorValue = stuff->window;
> + return err;
> + }
> +
> + /* This sure does need fixing. */
> + if (stuff->num_devices)
> + return BadImplementation;
> +
> + b.x1 = stuff->x1;
> + b.x2 = stuff->x2;
> + b.y1 = stuff->y1;
> + b.y2 = stuff->y2;
> +
> + if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
> + return BadValue;
> +
> + /* no 0-sized barriers */
> + if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
> + return BadValue;
> +
> + if (!(barrier = CreatePointerBarrierClient(pWin->drawable.pScreen,
> + client, stuff)))
> + return BadAlloc;
> +
> + if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
> + return BadAlloc;
> +
> + return Success;
> +}
> +
> +int
> +SProcXFixesCreatePointerBarrier (ClientPtr client)
> +{
> + int n;
> + REQUEST(xXFixesCreatePointerBarrierReq);
> +
> + swaps(&stuff->length, n);
> + REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
> + swapl(&stuff->barrier, n);
> + swapl(&stuff->window, n);
> + swaps(&stuff->x1, n);
> + swaps(&stuff->y1, n);
> + swaps(&stuff->x2, n);
> + swaps(&stuff->y2, n);
> + swapl(&stuff->directions, n);
> + return ProcXFixesVector[stuff->xfixesReqType](client);
> +}
> +
> +static int
> +CursorFreeBarrier(void *data, XID id)
> +{
> + struct PointerBarrierClient *b = NULL, *barrier;
> + ScreenPtr screen;
> + CursorScreenPtr cs;
> +
> + barrier = container_of(data, struct PointerBarrierClient, barrier);
> + screen = barrier->screen;
> + cs = GetCursorScreen(screen);
> +
> + /* find and unlink from the screen private */
> + list_for_each_entry(b, &cs->barriers, entry) {
> + if (b == barrier) {
> + list_del(&b->entry);
> + break;
> + }
> + }
> +
> + free(barrier);
> + return Success;
> +}
> +
> +int
> +ProcXFixesDestroyPointerBarrier (ClientPtr client)
> +{
> + int err;
> + void *barrier;
> + REQUEST (xXFixesDestroyPointerBarrierReq);
> +
> + REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
> +
> + err = dixLookupResourceByType((void **)&barrier, stuff->barrier,
> + PointerBarrierType, client,
> + DixDestroyAccess);
> + if (err != Success) {
> + client->errorValue = stuff->barrier;
> + return err;
> + }
> +
> + FreeResource(stuff->barrier, RT_NONE);
> + return Success;
> +}
> +
> +int
> +SProcXFixesDestroyPointerBarrier (ClientPtr client)
> +{
> + int n;
> + REQUEST(xXFixesDestroyPointerBarrierReq);
> +
> + swaps(&stuff->length, n);
> + REQUEST_SIZE_MATCH(xXFixesDestroyPointerBarrierReq);
> + swapl(&stuff->barrier, n);
> + return ProcXFixesVector[stuff->xfixesReqType](client);
> +}
> +
> Bool
> XFixesCursorInit (void)
> {
> @@ -1048,8 +1440,10 @@ XFixesCursorInit (void)
> cs = (CursorScreenPtr) calloc(1, sizeof (CursorScreenRec));
> if (!cs)
> return FALSE;
> + list_init(&cs->barriers);
> Wrap (cs, pScreen, CloseScreen, CursorCloseScreen);
> Wrap (cs, pScreen, DisplayCursor, CursorDisplayCursor);
> + Wrap (cs, pScreen, ConstrainCursorHarder, CursorConstrainCursorHarder);
> cs->pCursorHideCounts = NULL;
> SetCursorScreen (pScreen, cs);
> }
> @@ -1059,7 +1453,10 @@ XFixesCursorInit (void)
> "XFixesCursorHideCount");
> CursorWindowType = CreateNewResourceType(CursorFreeWindow,
> "XFixesCursorWindow");
> + PointerBarrierType = CreateNewResourceType(CursorFreeBarrier,
> + "XFixesPointerBarrier");
>
> - return CursorClientType && CursorHideCountType && CursorWindowType;
> + return CursorClientType && CursorHideCountType && CursorWindowType &&
> + PointerBarrierType;
> }
>
> diff --git a/xfixes/xfixes.c b/xfixes/xfixes.c
> index 54f0df3..e0ebedd 100644
> --- a/xfixes/xfixes.c
> +++ b/xfixes/xfixes.c
> @@ -1,5 +1,6 @@
> /*
> * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
> + * Copyright 2010 Red Hat, Inc.
> *
> * Permission is hereby granted, free of charge, to any person obtaining a
> * copy of this software and associated documentation files (the "Software"),
> @@ -47,10 +48,6 @@
>
> #include "xfixesint.h"
> #include "protocol-versions.h"
> -/*
> - * Must use these instead of the constants from xfixeswire.h. They advertise
> - * what we implement, not what the protocol headers define.
> - */
>
> static unsigned char XFixesReqCode;
> int XFixesEventBase;
> @@ -97,11 +94,12 @@ ProcXFixesQueryVersion(ClientPtr client)
>
> /* Major version controls available requests */
> static const int version_requests[] = {
> - X_XFixesQueryVersion, /* before client sends QueryVersion */
> - X_XFixesGetCursorImage, /* Version 1 */
> - X_XFixesChangeCursorByName, /* Version 2 */
> - X_XFixesExpandRegion, /* Version 3 */
> - X_XFixesShowCursor, /* Version 4 */
> + X_XFixesQueryVersion, /* before client sends QueryVersion */
> + X_XFixesGetCursorImage, /* Version 1 */
> + X_XFixesChangeCursorByName, /* Version 2 */
> + X_XFixesExpandRegion, /* Version 3 */
> + X_XFixesShowCursor, /* Version 4 */
> + X_XFixesDestroyPointerBarrier, /* Version 5 */
> };
>
> #define NUM_VERSION_REQUESTS (sizeof (version_requests) / sizeof (version_requests[0]))
> @@ -142,6 +140,9 @@ int (*ProcXFixesVector[XFixesNumberRequests])(ClientPtr) = {
> /*************** Version 4 ****************/
> ProcXFixesHideCursor,
> ProcXFixesShowCursor,
> +/*************** Version 5 ****************/
> + ProcXFixesCreatePointerBarrier,
> + ProcXFixesDestroyPointerBarrier,
> };
>
> static int
> @@ -205,6 +206,9 @@ static int (*SProcXFixesVector[XFixesNumberRequests])(ClientPtr) = {
> /*************** Version 4 ****************/
> SProcXFixesHideCursor,
> SProcXFixesShowCursor,
> +/*************** Version 5 ****************/
> + SProcXFixesCreatePointerBarrier,
> + SProcXFixesDestroyPointerBarrier,
> };
>
> static int
> @@ -260,6 +264,8 @@ XFixesExtensionInit(void)
> EventSwapVector[XFixesEventBase + XFixesCursorNotify] =
> (EventSwapPtr) SXFixesCursorNotifyEvent;
> SetResourceTypeErrorValue(RegionResType, XFixesErrorBase + BadRegion);
> + SetResourceTypeErrorValue(PointerBarrierType,
> + XFixesErrorBase + BadBarrier);
> }
> }
>
> diff --git a/xfixes/xfixes.h b/xfixes/xfixes.h
> index 1638350..5765e64 100644
> --- a/xfixes/xfixes.h
> +++ b/xfixes/xfixes.h
> @@ -30,6 +30,7 @@
> #include "resource.h"
>
> extern _X_EXPORT RESTYPE RegionResType;
> +extern _X_EXPORT RESTYPE PointerBarrierType;
> extern _X_EXPORT int XFixesErrorBase;
>
> #define VERIFY_REGION(pRegion, rid, client, mode) \
> @@ -51,5 +52,21 @@ extern _X_EXPORT int XFixesErrorBase;
> extern _X_EXPORT RegionPtr
> XFixesRegionCopy (RegionPtr pRegion);
>
> +struct PointerBarrier {
> + CARD16 x1, x2, y1, y2;
> + CARD32 directions;
> +};
> +
> +
> +extern int
> +barrier_get_direction(int, int, int, int);
> +extern BOOL
> +barrier_is_blocking(const struct PointerBarrier*, int, int, int, int, double*);
> +extern BOOL
> +barrier_is_blocking_direction(const struct PointerBarrier*, int);
> +extern void
> +barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, int *y);
> +
> +
>
> #endif /* _XFIXES_H_ */
> diff --git a/xfixes/xfixesint.h b/xfixes/xfixesint.h
> index d005369..6ba276e 100644
> --- a/xfixes/xfixesint.h
> +++ b/xfixes/xfixesint.h
> @@ -1,5 +1,6 @@
> /*
> * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
> + * Copyright 2010 Red Hat, Inc.
> *
> * Permission is hereby granted, free of charge, to any person obtaining a
> * copy of this software and associated documentation files (the "Software"),
> @@ -278,6 +279,21 @@ ProcXFixesShowCursor (ClientPtr client);
> int
> SProcXFixesShowCursor (ClientPtr client);
>
> +/* Version 5 */
> +
> +int
> +ProcXFixesCreatePointerBarrier (ClientPtr client);
> +
> +int
> +SProcXFixesCreatePointerBarrier (ClientPtr client);
> +
> +int
> +ProcXFixesDestroyPointerBarrier (ClientPtr client);
> +
> +int
> +SProcXFixesDestroyPointerBarrier (ClientPtr client);
> +
> +/* Xinerama */
> extern int (*PanoramiXSaveXFixesVector[XFixesNumberRequests])(ClientPtr);
> void PanoramiXFixesInit (void);
> void PanoramiXFixesReset (void);
> --
> 1.7.5.1
>
More information about the xorg-devel
mailing list