diff --git a/Xi/Makefile.am b/Xi/Makefile.am index 69c7886b9..af85bd049 100644 --- a/Xi/Makefile.am +++ b/Xi/Makefile.am @@ -78,6 +78,8 @@ libXi_la_SOURCES = \ ungrdevk.h \ xiallowev.c \ xiallowev.h \ + xibarriers.c \ + xibarriers.h \ xichangecursor.c \ xichangecursor.h \ xichangehierarchy.c \ diff --git a/Xi/extinit.c b/Xi/extinit.c index 7e3075551..1074b237c 100644 --- a/Xi/extinit.c +++ b/Xi/extinit.c @@ -122,6 +122,7 @@ SOFTWARE. #include "xiqueryversion.h" #include "xisetclientpointer.h" #include "xiwarppointer.h" +#include "xibarriers.h" /* Masks for XI events have to be aligned with core event (partially anyway). * If DeviceButtonMotionMask is != ButtonMotionMask, event delivery @@ -1263,6 +1264,9 @@ XInputExtensionInit(void) if (!AddCallback(&ClientStateCallback, XIClientCallback, 0)) FatalError("Failed to add callback to XI.\n"); + if (!XIBarrierInit()) + FatalError("Could not initialize barriers.\n"); + extEntry = AddExtension(INAME, IEVENTS, IERRORS, ProcIDispatch, SProcIDispatch, IResetProc, StandardMinorOpcode); if (extEntry) { diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c new file mode 100644 index 000000000..ba9fd063e --- /dev/null +++ b/Xi/xibarriers.c @@ -0,0 +1,548 @@ +/* + * Copyright 2012 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. + * + * Copyright © 2002 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Keith Packard not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Keith Packard makes no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "xibarriers.h" +#include "scrnintstr.h" +#include "cursorstr.h" +#include "dixevents.h" +#include "servermd.h" +#include "mipointer.h" +#include "inputstr.h" +#include "windowstr.h" +#include "xace.h" +#include "list.h" +#include "exglobals.h" + +RESTYPE PointerBarrierType; + +static DevPrivateKeyRec BarrierScreenPrivateKeyRec; + +#define BarrierScreenPrivateKey (&BarrierScreenPrivateKeyRec) + +typedef struct PointerBarrierClient *PointerBarrierClientPtr; + +struct PointerBarrierClient { + ScreenPtr screen; + struct PointerBarrier barrier; + struct xorg_list entry; + int num_devices; + int *device_ids; /* num_devices */ +}; + +typedef struct _BarrierScreen { + CloseScreenProcPtr CloseScreen; + ConstrainCursorHarderProcPtr ConstrainCursorHarder; + struct xorg_list barriers; +} BarrierScreenRec, *BarrierScreenPtr; + +#define GetBarrierScreen(s) ((BarrierScreenPtr)dixLookupPrivate(&(s)->devPrivates, BarrierScreenPrivateKey)) +#define GetBarrierScreenIfSet(s) GetBarrierScreen(s) +#define SetBarrierScreen(s,p) dixSetPrivate(&(s)->devPrivates, BarrierScreenPrivateKey, p) +#define Wrap(as,s,elt,func) (((as)->elt = (s)->elt), (s)->elt = func) +#define Unwrap(as,s,elt,backup) (((backup) = (s)->elt), (s)->elt = (as)->elt) + +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 (dir & BarrierPositiveX && x1 == (barrier->x1 - 1) && y1 >= barrier->y1 && y1 <= barrier->y2) { + *distance = 0; + return TRUE; + } + if (dir & BarrierNegativeX && 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 (dir & BarrierPositiveY && y1 == (barrier->y1 - 1) && x1 >= barrier->x1 && x1 <= barrier->x2) { + *distance = 0; + return TRUE; + } + if (dir & BarrierNegativeY && 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; +} + +static BOOL +barrier_blocks_device(struct PointerBarrierClient *client, + DeviceIntPtr dev) +{ + int i; + int master_id; + + /* Clients with no devices are treated as + * if they specified XIAllDevices. */ + if (client->num_devices == 0) + return TRUE; + + master_id = GetMaster(dev, POINTER_OR_FLOAT)->id; + + for (i = 0; i < client->num_devices; i++) { + int device_id = client->device_ids[i]; + if (device_id == XIAllDevices || + device_id == XIAllMasterDevices || + device_id == master_id) + return TRUE; + } + + return FALSE; +} + +/** + * 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(BarrierScreenPtr cs, DeviceIntPtr dev, + 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 */ + + xorg_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_blocks_device(c, dev)) + 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 +BarrierConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode, + int *x, int *y) +{ + BarrierScreenPtr cs = GetBarrierScreen(screen); + + if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev) && + mode == Relative) { + int ox, oy; + int dir; + int i; + 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); + +#define MAX_BARRIERS 2 + for (i = 0; i < MAX_BARRIERS; i++) { + nearest = barrier_find_nearest(cs, dev, dir, ox, oy, *x, *y); + if (!nearest) + break; + + 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; + } + } + } + + if (cs->ConstrainCursorHarder) { + screen->ConstrainCursorHarder = cs->ConstrainCursorHarder; + screen->ConstrainCursorHarder(dev, screen, mode, x, y); + screen->ConstrainCursorHarder = BarrierConstrainCursorHarder; + } +} + +static int +CreatePointerBarrierClient(ClientPtr client, + xXFixesCreatePointerBarrierReq * stuff, + PointerBarrierClientPtr *client_out) +{ + WindowPtr pWin; + ScreenPtr screen; + BarrierScreenPtr cs; + int err; + int size; + int i; + struct PointerBarrierClient *ret; + CARD16 *in_devices; + + size = sizeof(*ret) + sizeof(DeviceIntPtr) * stuff->num_devices; + ret = malloc(size); + + if (!ret) { + return BadAlloc; + } + + err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess); + if (err != Success) { + client->errorValue = stuff->window; + goto error; + } + + screen = pWin->drawable.pScreen; + cs = GetBarrierScreen(screen); + + ret->screen = screen; + ret->num_devices = stuff->num_devices; + if (ret->num_devices > 0) + ret->device_ids = (int*)&ret[1]; + else + ret->device_ids = NULL; + + in_devices = (CARD16 *) &stuff[1]; + for (i = 0; i < stuff->num_devices; i++) { + int device_id = in_devices[i]; + DeviceIntPtr device; + + if ((err = dixLookupDevice (&device, device_id, + client, DixReadAccess))) { + client->errorValue = device_id; + goto error; + } + + if (!IsMaster (device)) { + client->errorValue = device_id; + err = BadDevice; + goto error; + } + + ret->device_ids[i] = device_id; + } + + 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); + xorg_list_add(&ret->entry, &cs->barriers); + + *client_out = ret; + return Success; + + error: + *client_out = NULL; + free(ret); + return err; +} + +static int +BarrierFreeBarrier(void *data, XID id) +{ + struct PointerBarrierClient *b = NULL, *barrier; + ScreenPtr screen; + BarrierScreenPtr cs; + + barrier = container_of(data, struct PointerBarrierClient, barrier); + + screen = barrier->screen; + cs = GetBarrierScreen(screen); + + /* find and unlink from the screen private */ + xorg_list_for_each_entry(b, &cs->barriers, entry) { + if (b == barrier) { + xorg_list_del(&b->entry); + break; + } + } + + free(barrier); + return Success; +} + +int +XICreatePointerBarrier(ClientPtr client, + xXFixesCreatePointerBarrierReq * stuff) +{ + int err; + struct PointerBarrierClient *barrier; + struct PointerBarrier b; + + 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 ((err = CreatePointerBarrierClient(client, stuff, &barrier))) + return err; + + if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier)) + return BadAlloc; + + return Success; +} + +int +XIDestroyPointerBarrier(ClientPtr client, + xXFixesDestroyPointerBarrierReq * stuff) +{ + int err; + void *barrier; + + 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; +} + +static Bool +BarrierCloseScreen(ScreenPtr pScreen) +{ + BarrierScreenPtr cs = GetBarrierScreen(pScreen); + Bool ret; + _X_UNUSED CloseScreenProcPtr close_proc; + _X_UNUSED ConstrainCursorHarderProcPtr constrain_proc; + + Unwrap(cs, pScreen, CloseScreen, close_proc); + Unwrap(cs, pScreen, ConstrainCursorHarder, constrain_proc); + ret = (*pScreen->CloseScreen) (pScreen); + free(cs); + return ret; +} + +Bool +XIBarrierInit(void) +{ + int i; + + if (!dixRegisterPrivateKey(&BarrierScreenPrivateKeyRec, PRIVATE_SCREEN, 0)) + return FALSE; + + for (i = 0; i < screenInfo.numScreens; i++) { + ScreenPtr pScreen = screenInfo.screens[i]; + BarrierScreenPtr cs; + + cs = (BarrierScreenPtr) calloc(1, sizeof(BarrierScreenRec)); + if (!cs) + return FALSE; + xorg_list_init(&cs->barriers); + Wrap(cs, pScreen, CloseScreen, BarrierCloseScreen); + Wrap(cs, pScreen, ConstrainCursorHarder, BarrierConstrainCursorHarder); + SetBarrierScreen(pScreen, cs); + } + + PointerBarrierType = CreateNewResourceType(BarrierFreeBarrier, + "XIPointerBarrier"); + + return PointerBarrierType; +} diff --git a/Xi/xibarriers.h b/Xi/xibarriers.h new file mode 100644 index 000000000..8f2993f85 --- /dev/null +++ b/Xi/xibarriers.h @@ -0,0 +1,47 @@ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#ifndef _XIBARRIERS_H_ +#define _XIBARRIERS_H_ + +#include "resource.h" + +extern _X_EXPORT RESTYPE PointerBarrierType; + +struct PointerBarrier { + CARD16 x1, x2, y1, y2; + CARD32 directions; +}; + +int +barrier_get_direction(int, int, int, int); +BOOL +barrier_is_blocking(const struct PointerBarrier *, int, int, int, int, + double *); +BOOL +barrier_is_blocking_direction(const struct PointerBarrier *, int); +void +barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, + int *y); + +#include + +int +XICreatePointerBarrier(ClientPtr client, + xXFixesCreatePointerBarrierReq * stuff); + +int +XIDestroyPointerBarrier(ClientPtr client, + xXFixesDestroyPointerBarrierReq * stuff); + +Bool +XIBarrierInit(void); + +#endif /* _XIBARRIERS_H_ */ + + + + + diff --git a/xfixes/cursor.c b/xfixes/cursor.c index ffee4d6ab..568e717fa 100644 --- a/xfixes/cursor.c +++ b/xfixes/cursor.c @@ -56,12 +56,11 @@ #include "windowstr.h" #include "xace.h" #include "list.h" -#include "exglobals.h" +#include "xibarriers.h" static RESTYPE CursorClientType; static RESTYPE CursorHideCountType; static RESTYPE CursorWindowType; -RESTYPE PointerBarrierType; static CursorPtr CursorCurrent[MAXDEVICES]; static DevPrivateKeyRec CursorScreenPrivateKeyRec; @@ -113,16 +112,6 @@ typedef struct _CursorHideCountRec { XID resource; } CursorHideCountRec; -typedef struct PointerBarrierClient *PointerBarrierClientPtr; - -struct PointerBarrierClient { - ScreenPtr screen; - struct PointerBarrier barrier; - struct xorg_list entry; - int num_devices; - int *device_ids; /* num_devices */ -}; - /* * Wrap DisplayCursor to catch cursor change events */ @@ -130,9 +119,7 @@ struct PointerBarrierClient { typedef struct _CursorScreen { DisplayCursorProcPtr DisplayCursor; CloseScreenProcPtr CloseScreen; - ConstrainCursorHarderProcPtr ConstrainCursorHarder; CursorHideCountPtr pCursorHideCounts; - struct xorg_list barriers; } CursorScreenRec, *CursorScreenPtr; #define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey)) @@ -199,11 +186,9 @@ CursorCloseScreen(ScreenPtr pScreen) Bool ret; _X_UNUSED CloseScreenProcPtr close_proc; _X_UNUSED DisplayCursorProcPtr display_proc; - _X_UNUSED 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) (pScreen); free(cs); @@ -1013,384 +998,15 @@ 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 (dir & BarrierPositiveX && x1 == (barrier->x1 - 1) && y1 >= barrier->y1 && y1 <= barrier->y2) { - *distance = 0; - return TRUE; - } - if (dir & BarrierNegativeX && 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 (dir & BarrierPositiveY && y1 == (barrier->y1 - 1) && x1 >= barrier->x1 && x1 <= barrier->x2) { - *distance = 0; - return TRUE; - } - if (dir & BarrierNegativeY && 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; -} - -static BOOL -barrier_blocks_device(struct PointerBarrierClient *client, - DeviceIntPtr dev) -{ - int i; - int master_id; - - /* Clients with no devices are treated as - * if they specified XIAllDevices. */ - if (client->num_devices == 0) - return TRUE; - - master_id = GetMaster(dev, POINTER_OR_FLOAT)->id; - - for (i = 0; i < client->num_devices; i++) { - int device_id = client->device_ids[i]; - if (device_id == XIAllDevices || - device_id == XIAllMasterDevices || - device_id == master_id) - return TRUE; - } - - return FALSE; -} - -/** - * 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, DeviceIntPtr dev, - 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 */ - - xorg_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_blocks_device(c, dev)) - 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 (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev) && - mode == Relative) { - int ox, oy; - int dir; - int i; - 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); - -#define MAX_BARRIERS 2 - for (i = 0; i < MAX_BARRIERS; i++) { - nearest = barrier_find_nearest(cs, dev, dir, ox, oy, *x, *y); - if (!nearest) - break; - - 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; - } - } - } - - if (cs->ConstrainCursorHarder) { - screen->ConstrainCursorHarder = cs->ConstrainCursorHarder; - screen->ConstrainCursorHarder(dev, screen, mode, x, y); - screen->ConstrainCursorHarder = CursorConstrainCursorHarder; - } -} - -static int -CreatePointerBarrierClient(ScreenPtr screen, ClientPtr client, - xXFixesCreatePointerBarrierReq * stuff, - PointerBarrierClientPtr *client_out) -{ - CursorScreenPtr cs = GetCursorScreen(screen); - int err; - int size; - int i; - CARD16 *in_devices; - struct PointerBarrierClient *ret; - - size = sizeof(*ret) + sizeof(int) * stuff->num_devices; - ret = malloc(size); - - *client_out = NULL; - - if (!ret) { - return BadAlloc; - } - - ret->screen = screen; - ret->num_devices = stuff->num_devices; - if (ret->num_devices > 0) - ret->device_ids = (int*)&ret[1]; - else - ret->device_ids = NULL; - - in_devices = (CARD16 *) &stuff[1]; - for (i = 0; i < stuff->num_devices; i++) { - int device_id = in_devices[i]; - DeviceIntPtr device; - - if ((err = dixLookupDevice (&device, device_id, - client, DixReadAccess))) { - client->errorValue = device_id; - goto error; - } - - if (!IsMaster (device)) { - client->errorValue = device_id; - err = BadDevice; - goto error; - } - - ret->device_ids[i] = device_id; - } - - 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); - xorg_list_add(&ret->entry, &cs->barriers); - - *client_out = ret; - return Success; - - error: - free(ret); - return err; -} - int ProcXFixesCreatePointerBarrier(ClientPtr client) { - int err; - WindowPtr pWin; - struct PointerBarrierClient *barrier; - struct PointerBarrier b; - REQUEST(xXFixesCreatePointerBarrierReq); REQUEST_FIXED_SIZE(xXFixesCreatePointerBarrierReq, pad_to_int32(stuff->num_devices)); LEGAL_NEW_RESOURCE(stuff->barrier, client); - err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess); - if (err != Success) { - client->errorValue = stuff->window; - return err; - } - - 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 ((err = CreatePointerBarrierClient(pWin->drawable.pScreen, - client, stuff, &barrier))) - return err; - - if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier)) - return BadAlloc; - - return Success; + return XICreatePointerBarrier(client, stuff); } int @@ -1418,49 +1034,14 @@ SProcXFixesCreatePointerBarrier(ClientPtr client) 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 */ - xorg_list_for_each_entry(b, &cs->barriers, entry) { - if (b == barrier) { - xorg_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; + return XIDestroyPointerBarrier(client, stuff); } int @@ -1492,10 +1073,8 @@ XFixesCursorInit(void) cs = (CursorScreenPtr) calloc(1, sizeof(CursorScreenRec)); if (!cs) return FALSE; - xorg_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); } @@ -1505,9 +1084,6 @@ XFixesCursorInit(void) "XFixesCursorHideCount"); CursorWindowType = CreateNewResourceType(CursorFreeWindow, "XFixesCursorWindow"); - PointerBarrierType = CreateNewResourceType(CursorFreeBarrier, - "XFixesPointerBarrier"); - return CursorClientType && CursorHideCountType && CursorWindowType && - PointerBarrierType; + return CursorClientType && CursorHideCountType && CursorWindowType; } diff --git a/xfixes/xfixes.h b/xfixes/xfixes.h index 19af09f7d..98828710f 100644 --- a/xfixes/xfixes.h +++ b/xfixes/xfixes.h @@ -30,7 +30,6 @@ #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) \ @@ -52,20 +51,6 @@ 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); +#include "xibarriers.h" #endif /* _XFIXES_H_ */