From: Adam Jackson <a...@redhat.com>

Pointer barriers block the movement of a pointer past a certain line,
specified by the barrier. Movement through the barrier may be permitted for
certain directions of movement.

Algorithm used:
For each movement, find the nearest-to-origin-of-movement barrier that
blocks this movement. If found, clamp to that barrier and re-evaluate from
this new origin.

Co-authored-by: Peter Hutterer <peter.hutte...@who-t.net>
Signed-off-by: Adam Jackson <a...@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutte...@who-t.net>
---
 include/protocol-versions.h |    2 +-
 test/Makefile.am            |    3 +-
 test/fixes.c                |  304 ++++++++++++++++++++++++++++++++++
 xfixes/cursor.c             |  386 ++++++++++++++++++++++++++++++++++++++++++-
 xfixes/xfixes.c             |   22 ++-
 xfixes/xfixes.h             |   16 ++
 xfixes/xfixesint.h          |   16 ++
 7 files changed, 737 insertions(+), 12 deletions(-)
 create mode 100644 test/fixes.c

diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index c8c7f5f..7be1af3 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -126,7 +126,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 be54e5f..ccdb859 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,6 +1,6 @@
 if UNITTESTS
 SUBDIRS= . xi2
-check_PROGRAMS = xkb input xtest list
+check_PROGRAMS = xkb input xtest list fixes
 check_LTLIBRARIES = libxservertest.la
 
 TESTS=$(check_PROGRAMS)
@@ -17,6 +17,7 @@ xkb_LDADD=$(TEST_LDADD)
 input_LDADD=$(TEST_LDADD)
 xtest_LDADD=$(TEST_LDADD)
 list_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..d319dab
--- /dev/null
+++ b/test/fixes.c
@@ -0,0 +1,304 @@
+/**
+ * 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>
+
+#include <glib.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);
+    g_assert(blocking);
+
+    for (j = 0; j <= BarrierNegativeY; j++)
+    {
+        barrier->directions = 1 << i;
+        for (i = 0; i <= BarrierNegativeY; i++)
+        {
+            barrier->directions |= 1 << i;
+            blocking = barrier_is_blocking_direction(barrier, dir);
+            g_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;
+    g_assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
+    g_assert(distance == 1);
+
+    /* definitely not across */
+    x1 = x + 10;
+    x2 = x + 5;
+    g_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;
+    g_assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
+
+    /* across, diagonally */
+    x1 = x + 5;
+    x2 = x - 5;
+    y1 = y + 5;
+    y2 = y - 5;
+    g_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;
+    g_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;
+    g_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;
+    g_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;
+    g_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;
+    g_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;
+    g_assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
+
+    /* definitely not across */
+    y1 = y + 10;
+    y2 = y + 5;
+    g_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;
+    g_assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance));
+
+    /* across, diagonally */
+    y1 = y + 5;
+    y2 = y - 5;
+    x1 = x + 5;
+    x2 = x - 5;
+    g_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;
+    g_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;
+    g_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;
+    g_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;
+    g_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;
+    g_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);
+    g_assert(cx <= barrier.x1);
+    g_assert(cy == INT_MAX);
+
+    cx = 0;
+    cy = INT_MAX;
+    barrier_clamp_to_barrier(&barrier, BarrierNegativeX, &cx, &cy);
+    g_assert(cx >= barrier.x1);
+    g_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);
+    g_assert(cx == INT_MAX);
+    g_assert(cy <= barrier.y1);
+
+    cx = INT_MAX;
+    cy = 0;
+    barrier_clamp_to_barrier(&barrier, BarrierNegativeY, &cx, &cy);
+    g_assert(cx == INT_MAX);
+    g_assert(cy >= barrier.y1);
+}
+
+int main(int argc, char** argv)
+{
+    g_test_init(&argc, &argv,NULL);
+    g_test_bug_base("https://bugzilla.freedesktop.org/show_bug.cgi?id=";);
+
+    g_test_add_func("/fixes/pointer-barrier", fixes_pointer_barriers_test);
+    g_test_add_func("/fixes/pointer-barrier-direction", 
fixes_pointer_barrier_direction_test);
+    g_test_add_func("/fixes/pointer-barrier-clamp", 
fixes_pointer_barrier_clamp_test);
+
+    return g_test_run();
+}
diff --git a/xfixes/cursor.c b/xfixes/cursor.c
index fb608f6..3cafc24 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;
+static 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;
+    PointerBarrierClientPtr next;
+};
+
 /*
  * 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;
+    PointerBarrierClientPtr    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,370 @@ 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 on 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;
+
+    /* Algorithm below doesn't handle edge cases well, hence the extra
+     * checks. */
+    if (barrier_is_vertical(barrier)) {
+       /* Startpoint or endpoint on barrier? */
+       if ((x1 == barrier->x1 && y1 >= barrier->y1 && y1 <= barrier->y2) ||
+           (x2 == barrier->x1 && y2 >= barrier->y1 && y2 <= barrier->y2)) {
+           *distance = 0;
+           return TRUE;
+        }
+    } else {
+       if ((y1 == barrier->y1 && x1 >= barrier->x1 && x1 <= barrier->x2) ||
+           (y2 == barrier->y1 && x2 >= barrier->x1 && x2 <= barrier->x2)) {
+           *distance = 0;
+           return TRUE;
+        }
+    }
+
+    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
+ * @param exclude Exclude this barrier from any checks
+ * @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 PointerBarrier *exclude)
+{
+    struct PointerBarrierClient *c;
+    struct PointerBarrier *nearest = NULL;
+    double min_distance = INT_MAX; /* can't get higher than that in X anyway */
+
+    for (c = cs->barriers; c; c = c->next) {
+       struct PointerBarrier *b = &c->barrier;
+       double distance;
+
+       if (b == exclude)
+           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.
+ */
+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 + 1;
+       if ((dir & BarrierPositiveX) & ~barrier->directions)
+           *x = barrier->x1 - 1;
+    }
+    if (barrier_is_horizontal(barrier))
+    {
+       if ((dir & BarrierNegativeY) & ~barrier->directions)
+           *y = barrier->y1 + 1;
+       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 (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 position 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, NULL);
+       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, nearest);
+           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 = stuff->x1;
+       ret->barrier.x2 = stuff->x2;
+       ret->barrier.y1 = stuff->y1;
+       ret->barrier.y2 = stuff->y2;
+       ret->barrier.directions = stuff->directions & 0x0f;
+       ret->next = cs->barriers;
+       cs->barriers = ret;
+    }
+
+    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;
+
+    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 */
+    if (cs->barriers == barrier)
+       cs->barriers = cs->barriers->next;
+    else for (b = cs->barriers; b; b = b->next) {
+       if (b->next == barrier) {
+           b->next = b->next->next;
+           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)
 {
@@ -1050,6 +1430,7 @@ XFixesCursorInit (void)
            return FALSE;
        Wrap (cs, pScreen, CloseScreen, CursorCloseScreen);
        Wrap (cs, pScreen, DisplayCursor, CursorDisplayCursor);
+       Wrap (cs, pScreen, ConstrainCursorHarder, CursorConstrainCursorHarder);
        cs->pCursorHideCounts = NULL;
        SetCursorScreen (pScreen, cs);
     }
@@ -1059,7 +1440,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 e8c7bf1..97f2ba6 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_XFixesCreatePointerBarrier,   /* 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
diff --git a/xfixes/xfixes.h b/xfixes/xfixes.h
index 1638350..7f6ef26 100644
--- a/xfixes/xfixes.h
+++ b/xfixes/xfixes.h
@@ -51,5 +51,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.4

_______________________________________________
xorg-devel@lists.x.org: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to