From d45f5b2493bc0a2882bf972849b5c9c50cd533ca Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Wed, 25 May 2011 05:54:35 -0400 Subject: fixes: Add support for pointer barriers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: Peter Hutterer Signed-off-by: Adam Jackson Signed-off-by: Peter Hutterer --- 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 655c0e4ba..ba1d1768f 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 8692ded8a..7b7a9f53c 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 b7ee070a1..5574e7d1e 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 000000000..8c804ba26 --- /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 +#endif + +#include +#include +#include +#include +#include + +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 fb608f694..01eb70d92 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 54f0df341..e0ebedd80 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 1638350c2..5765e64b5 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 d00536968..6ba276e8c 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); -- cgit v1.2.3