diff options
author | Adam Jackson <ajax@redhat.com> | 2011-05-25 05:54:35 -0400 |
---|---|---|
committer | Adam Jackson <ajax@redhat.com> | 2011-05-31 15:10:51 -0400 |
commit | d45f5b2493bc0a2882bf972849b5c9c50cd533ca (patch) | |
tree | 08ee6bc366efad2b49299a6f6b350042c8d5af0b /xfixes | |
parent | 4621bb270a36d35d4ab67f1d7fb47674683dfc5b (diff) |
fixes: Add support for pointer barriers
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@who-t.net>
Tested-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Adam Jackson <ajax@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'xfixes')
-rw-r--r-- | xfixes/cursor.c | 399 | ||||
-rw-r--r-- | xfixes/xfixes.c | 24 | ||||
-rw-r--r-- | xfixes/xfixes.h | 17 | ||||
-rw-r--r-- | xfixes/xfixesint.h | 16 |
4 files changed, 446 insertions, 10 deletions
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); |