[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