From 482e0cb35225dc3b0664f9429db19c61b0c98ef6 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 1 Nov 2012 14:14:13 -0400 Subject: cursor: Move pointer barrier code over to XI In order to send events to specific windows associated with the barrier, we need to move the code that handles barriers to somewhere where it's easier to construct and send events. Rather than duplicating XSync with its XSyncSelectAlarm, re-use the existing XI infrastructure. For now, just move a bunch of code over, rename some things, and initialize the new structures, but still consider it a separate codebase. Pointer barrier requests are still handled by XFixes, so this is a weird intermediate state. It's unknown whether we'll add explicit requests to pointer barriers inside XI. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/Makefile.am | 2 + Xi/extinit.c | 4 + Xi/xibarriers.c | 548 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Xi/xibarriers.h | 47 +++++ 4 files changed, 601 insertions(+) create mode 100644 Xi/xibarriers.c create mode 100644 Xi/xibarriers.h (limited to 'Xi') 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_ */ + + + + + -- cgit v1.2.3 From 2868a93945f043e1efd897d56543fe673e341faf Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 9 Dec 2012 18:41:52 -0500 Subject: barriers: Don't loop over the server to destroy a barrier This is completely pointless as far as I can tell. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index ba9fd063e..9e43196f6 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -437,22 +437,10 @@ CreatePointerBarrierClient(ClientPtr client, static int BarrierFreeBarrier(void *data, XID id) { - struct PointerBarrierClient *b = NULL, *barrier; - ScreenPtr screen; - BarrierScreenPtr cs; + struct PointerBarrierClient *barrier; 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; - } - } + xorg_list_del(&barrier->entry); free(barrier); return Success; -- cgit v1.2.3 From 7e16dd3628334a5991b6713d778a46c1ce3b8b78 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sat, 24 Nov 2012 19:39:47 -0500 Subject: barriers: Switch to an explicit hook for barrier constrainment Rather than riding on the ConstrainCursorHarder hook, which has several issues, move to an explicit hook, which will help us with some RANDR interaction issues. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 56 ++++++++++++++++---------------------------------------- include/input.h | 5 +++++ mi/mipointer.c | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 40 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 9e43196f6..9c7affd4c 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -74,16 +74,12 @@ struct PointerBarrierClient { }; 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) @@ -306,22 +302,22 @@ barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x, } } -static void -BarrierConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode, - int *x, int *y) +void +input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, + int current_x, int current_y, + int dest_x, int dest_y, + int *out_x, int *out_y) { + /* Clamped coordinates here refer to screen edge clamping. */ BarrierScreenPtr cs = GetBarrierScreen(screen); + int x = dest_x, + y = dest_y; - if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev) && - mode == Relative) { - int ox, oy; + if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev)) { 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. @@ -329,32 +325,29 @@ BarrierConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode, * 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); + dir = barrier_get_direction(current_x, current_y, x, y); #define MAX_BARRIERS 2 for (i = 0; i < MAX_BARRIERS; i++) { - nearest = barrier_find_nearest(cs, dev, dir, ox, oy, *x, *y); + nearest = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); if (!nearest) break; - barrier_clamp_to_barrier(nearest, dir, x, y); + barrier_clamp_to_barrier(nearest, dir, &x, &y); if (barrier_is_vertical(nearest)) { dir &= ~(BarrierNegativeX | BarrierPositiveX); - ox = *x; + current_x = x; } else if (barrier_is_horizontal(nearest)) { dir &= ~(BarrierNegativeY | BarrierPositiveY); - oy = *y; + current_y = y; } } } - if (cs->ConstrainCursorHarder) { - screen->ConstrainCursorHarder = cs->ConstrainCursorHarder; - screen->ConstrainCursorHarder(dev, screen, mode, x, y); - screen->ConstrainCursorHarder = BarrierConstrainCursorHarder; - } + *out_x = x; + *out_y = y; } static int @@ -493,21 +486,6 @@ XIDestroyPointerBarrier(ClientPtr client, 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) { @@ -524,8 +502,6 @@ XIBarrierInit(void) if (!cs) return FALSE; xorg_list_init(&cs->barriers); - Wrap(cs, pScreen, CloseScreen, BarrierCloseScreen); - Wrap(cs, pScreen, ConstrainCursorHarder, BarrierConstrainCursorHarder); SetBarrierScreen(pScreen, cs); } diff --git a/include/input.h b/include/input.h index 2387dbf4a..a5d0462c9 100644 --- a/include/input.h +++ b/include/input.h @@ -678,4 +678,9 @@ extern _X_EXPORT void input_option_set_value(InputOption *opt, extern _X_HIDDEN Bool point_on_screen(ScreenPtr pScreen, int x, int y); extern _X_HIDDEN void update_desktop_dimensions(void); +extern _X_HIDDEN void input_constrain_cursor(DeviceIntPtr pDev, ScreenPtr screen, + int current_x, int current_y, + int dest_x, int dest_y, + int *out_x, int *out_y); + #endif /* INPUT_H */ diff --git a/mi/mipointer.c b/mi/mipointer.c index f34506326..d1708249f 100644 --- a/mi/mipointer.c +++ b/mi/mipointer.c @@ -588,6 +588,22 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, x -= pScreen->x; y -= pScreen->y; + if (mode == Relative) { + /* coordinates after clamped to a barrier */ + int constrained_x, constrained_y; + int current_x, current_y; /* current position in per-screen coord */ + + current_x = MIPOINTER(pDev)->x - pScreen->y; + current_y = MIPOINTER(pDev)->y - pScreen->x; + + input_constrain_cursor(pDev, pScreen, + current_x, current_y, x, y, + &constrained_x, &constrained_y); + + x = constrained_x; + y = constrained_y; + } + if (switch_screen) { pScreenPriv = GetScreenPrivate(pScreen); if (!pPointer->confined) { -- cgit v1.2.3 From 97da74c80e8b8eb03f7c1e34180cf5cd343bd608 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 9 Dec 2012 22:19:14 -0500 Subject: barriers: Switch to finding the nearest barrier client When we add events, we eventually want to add more state to the PointerBarrierClient, so return one of these instead of the dummy public structure that's not very interesting. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 9c7affd4c..21ad6c74f 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -237,7 +237,7 @@ barrier_blocks_device(struct PointerBarrierClient *client, } /** - * Find the nearest barrier that is blocking movement from x1/y1 to x2/y2. + * Find the nearest barrier client 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 @@ -246,13 +246,12 @@ barrier_blocks_device(struct PointerBarrierClient *client, * @param y2 Y end coordinate of movement vector * @return The barrier nearest to the movement origin that blocks this movement. */ -static struct PointerBarrier * +static struct PointerBarrierClient * barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, int dir, int x1, int y1, int x2, int y2) { - struct PointerBarrierClient *c; - struct PointerBarrier *nearest = NULL; + struct PointerBarrierClient *c, *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) { @@ -268,7 +267,7 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, if (barrier_is_blocking(b, x1, y1, x2, y2, &distance)) { if (min_distance > distance) { min_distance = distance; - nearest = b; + nearest = c; } } } @@ -317,6 +316,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, int dir; int i; struct PointerBarrier *nearest = NULL; + PointerBarrierClientPtr c; /* How this works: * Given the origin and the movement vector, get the nearest barrier @@ -329,10 +329,12 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, #define MAX_BARRIERS 2 for (i = 0; i < MAX_BARRIERS; i++) { - nearest = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); - if (!nearest) + c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); + if (!c) break; + nearest = &c->barrier; + barrier_clamp_to_barrier(nearest, dir, &x, &y); if (barrier_is_vertical(nearest)) { -- cgit v1.2.3 From 85a37ddcc2ff49adaec4075532b60e7d015f901c Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 9 Dec 2012 22:48:18 -0500 Subject: barriers: Reindent the constrainment hook This is to make future diffs much cleaner. Best viewed with -w. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 57 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 21ad6c74f..0affde68d 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -311,43 +311,44 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, BarrierScreenPtr cs = GetBarrierScreen(screen); int x = dest_x, y = dest_y; + int dir; + int i; + struct PointerBarrier *nearest = NULL; + PointerBarrierClientPtr c; + + if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev)) + goto out; - if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev)) { - int dir; - int i; - struct PointerBarrier *nearest = NULL; - PointerBarrierClientPtr c; - - /* 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(current_x, current_y, x, y); + /* 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(current_x, current_y, x, y); #define MAX_BARRIERS 2 - for (i = 0; i < MAX_BARRIERS; i++) { - c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); - if (!c) - break; + for (i = 0; i < MAX_BARRIERS; i++) { + c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); + if (!c) + break; - nearest = &c->barrier; + nearest = &c->barrier; - barrier_clamp_to_barrier(nearest, dir, &x, &y); + barrier_clamp_to_barrier(nearest, dir, &x, &y); - if (barrier_is_vertical(nearest)) { - dir &= ~(BarrierNegativeX | BarrierPositiveX); - current_x = x; - } - else if (barrier_is_horizontal(nearest)) { - dir &= ~(BarrierNegativeY | BarrierPositiveY); - current_y = y; - } + if (barrier_is_vertical(nearest)) { + dir &= ~(BarrierNegativeX | BarrierPositiveX); + current_x = x; + } + else if (barrier_is_horizontal(nearest)) { + dir &= ~(BarrierNegativeY | BarrierPositiveY); + current_y = y; } } + out: *out_x = x; *out_y = y; } -- cgit v1.2.3 From e130a46ab45839f91b29ee75bf19a597dfe3da28 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 1 Nov 2012 16:06:40 -0400 Subject: Add support for XI2.3: Pointer barrier events and releases. This adds support for clients that would like to get a notification every time a barrier is hit, and allows clients to temporarily release a barrier so that pointers can go through them, without having to destroy and recreate barriers. Based on work by Chris Halse Rogers Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer Signed-off-by: Peter Hutterer --- Xi/exevents.c | 32 ++++++++++++++++ Xi/extinit.c | 38 ++++++++++++++++++- Xi/xibarriers.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Xi/xibarriers.h | 8 ++-- dix/eventconvert.c | 39 +++++++++++++++++++ include/events.h | 1 + include/eventstr.h | 22 +++++++++++ include/inputstr.h | 2 +- mi/mieq.c | 4 ++ 9 files changed, 245 insertions(+), 8 deletions(-) (limited to 'Xi') diff --git a/Xi/exevents.c b/Xi/exevents.c index 2caf98c25..e606d9ea5 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1639,6 +1639,34 @@ ProcessTouchEvent(InternalEvent *ev, DeviceIntPtr dev) UpdateDeviceState(dev, &ev->device_event); } +static void +ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev) +{ + Mask filter; + WindowPtr pWin; + BarrierEvent *be = &e->barrier_event; + xEvent *ev; + int rc; + + if (!IsMaster(dev)) + return; + + if (dixLookupWindow(&pWin, be->window, serverClient, DixReadAccess) != Success) + return; + + rc = EventToXI2(e, &ev); + if (rc != Success) { + ErrorF("[Xi] event conversion from %s failed with code %d\n", __func__, rc); + return; + } + + filter = GetEventFilter(dev, ev); + + DeliverEventsToWindow(dev, pWin, ev, 1, + filter, NullGrab); + free(ev); +} + /** * Process DeviceEvents and DeviceChangedEvents. */ @@ -1788,6 +1816,10 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device) case ET_TouchEnd: ProcessTouchEvent(ev, device); break; + case ET_BarrierHit: + case ET_BarrierLeave: + ProcessBarrierEvent(ev, device); + break; default: ProcessDeviceEvent(ev, device); break; diff --git a/Xi/extinit.c b/Xi/extinit.c index 1074b237c..175d89b00 100644 --- a/Xi/extinit.c +++ b/Xi/extinit.c @@ -252,7 +252,8 @@ static int (*ProcIVector[]) (ClientPtr) = { ProcXIChangeProperty, /* 57 */ ProcXIDeleteProperty, /* 58 */ ProcXIGetProperty, /* 59 */ - ProcXIGetSelectedEvents /* 60 */ + ProcXIGetSelectedEvents, /* 60 */ + ProcXIBarrierReleasePointer /* 61 */ }; /* For swapped clients */ @@ -317,7 +318,8 @@ static int (*SProcIVector[]) (ClientPtr) = { SProcXIChangeProperty, /* 57 */ SProcXIDeleteProperty, /* 58 */ SProcXIGetProperty, /* 59 */ - SProcXIGetSelectedEvents /* 60 */ + SProcXIGetSelectedEvents, /* 60 */ + SProcXIBarrierReleasePointer /* 61 */ }; /***************************************************************** @@ -840,6 +842,33 @@ STouchOwnershipEvent(xXITouchOwnershipEvent * from, xXITouchOwnershipEvent * to) swapl(&to->child); } +static void +SBarrierEvent(xXIBarrierEvent * from, + xXIBarrierEvent * to) { + to->type = from->type; + + cpswapl(from->length, to->length); + cpswapl(from->time, to->time); + cpswaps(from->deviceid, to->deviceid); + cpswaps(from->sourceid, to->sourceid); + cpswapl(from->event, to->event); + cpswapl(from->root, to->root); + cpswapl(from->root_x, to->root_x); + cpswapl(from->root_y, to->root_y); + +#define SWAP_FP3232(x, y) \ + do { \ + cpswapl((x).integral, (y).integral); \ + cpswapl((x).frac, (y).frac); \ + } while(0) + + SWAP_FP3232(from->dx, to->dx); + SWAP_FP3232(from->dy, to->dy); + cpswapl(from->dtime, to->dtime); + cpswapl(from->barrier, to->barrier); + cpswapl(from->eventid, to->eventid); +} + /** Event swapping function for XI2 events. */ void XI2EventSwap(xGenericEvent *from, xGenericEvent *to) @@ -886,6 +915,11 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to) case XI_RawTouchEnd: SRawEvent((xXIRawEvent *) from, (xXIRawEvent *) to); break; + case XI_BarrierHit: + case XI_BarrierLeave: + SBarrierEvent((xXIBarrierEvent *) from, + (xXIBarrierEvent *) to); + break; default: ErrorF("[Xi] Unknown event type to swap. This is a bug.\n"); break; diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 0affde68d..f16094d45 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -56,6 +56,8 @@ #include "xace.h" #include "list.h" #include "exglobals.h" +#include "eventstr.h" +#include "mi.h" RESTYPE PointerBarrierType; @@ -66,11 +68,18 @@ static DevPrivateKeyRec BarrierScreenPrivateKeyRec; typedef struct PointerBarrierClient *PointerBarrierClientPtr; struct PointerBarrierClient { + XID id; ScreenPtr screen; + WindowPtr window; struct PointerBarrier barrier; struct xorg_list entry; int num_devices; int *device_ids; /* num_devices */ + Time last_timestamp; + int barrier_event_id; + int release_event_id; + Bool hit; + Bool last_hit; }; typedef struct _BarrierScreen { @@ -315,6 +324,18 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, int i; struct PointerBarrier *nearest = NULL; PointerBarrierClientPtr c; + Time ms = GetTimeInMillis(); + BarrierEvent ev = { + .header = ET_Internal, + .type = 0, + .length = sizeof (BarrierEvent), + .time = ms, + .deviceid = dev->id, + .sourceid = dev->id, + .dx = dest_x - current_x, + .dy = dest_y - current_y, + .root = screen->root->drawable.id, + }; if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev)) goto out; @@ -336,6 +357,13 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, nearest = &c->barrier; + c->seen = TRUE; + c->hit = TRUE; + + if (c->barrier_event_id == c->release_event_id) + continue; + + ev.type = ET_BarrierHit; barrier_clamp_to_barrier(nearest, dir, &x, &y); if (barrier_is_vertical(nearest)) { @@ -346,6 +374,21 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, dir &= ~(BarrierNegativeY | BarrierPositiveY); current_y = y; } + + ev.flags = 0; + ev.event_id = c->barrier_event_id; + ev.barrierid = c->id; + + ev.dt = ms - c->last_timestamp; + ev.window = c->window->drawable.id; + c->last_timestamp = ms; + + mieqEnqueue(dev, (InternalEvent *) &ev); + } + + xorg_list_for_each_entry(c, &cs->barriers, entry) { + c->last_hit = c->hit; + c->hit = FALSE; } out: @@ -384,6 +427,7 @@ CreatePointerBarrierClient(ClientPtr client, cs = GetBarrierScreen(screen); ret->screen = screen; + ret->window = pWin; ret->num_devices = stuff->num_devices; if (ret->num_devices > 0) ret->device_ids = (int*)&ret[1]; @@ -410,6 +454,11 @@ CreatePointerBarrierClient(ClientPtr client, ret->device_ids[i] = device_id; } + ret->id = stuff->barrier; + ret->barrier_event_id = 0; + ret->release_event_id = 0; + ret->hit = FALSE; + ret->last_hit = FALSE; ret->barrier.x1 = min(stuff->x1, stuff->x2); ret->barrier.x2 = max(stuff->x1, stuff->x2); ret->barrier.y1 = min(stuff->y1, stuff->y2); @@ -489,6 +538,64 @@ XIDestroyPointerBarrier(ClientPtr client, return Success; } +int +SProcXIBarrierReleasePointer(ClientPtr client) +{ + xXIBarrierReleasePointerInfo *info; + REQUEST(xXIBarrierReleasePointerReq); + int i; + + info = (xXIBarrierReleasePointerInfo*) &stuff[1]; + + swaps(&stuff->length); + swapl(&stuff->num_barriers); + for (i = 0; i < stuff->num_barriers; i++, info++) { + swaps(&info->deviceid); + swapl(&info->barrier); + swapl(&info->eventid); + } + + return (ProcXIBarrierReleasePointer(client)); +} + +int +ProcXIBarrierReleasePointer(ClientPtr client) +{ + int i; + int err; + struct PointerBarrierClient *barrier; + struct PointerBarrier *b; + xXIBarrierReleasePointerInfo *info; + + REQUEST(xXIBarrierReleasePointerReq); + REQUEST_AT_LEAST_SIZE(xXIBarrierReleasePointerReq); + + info = (xXIBarrierReleasePointerInfo*) &stuff[1]; + for (i = 0; i < stuff->num_barriers; i++, info++) { + CARD32 barrier_id, event_id; + _X_UNUSED CARD32 device_id; + + barrier_id = info->barrier; + event_id = info->eventid; + + /* FIXME: per-device releases */ + device_id = info->deviceid; + + err = dixLookupResourceByType((void **) &b, barrier_id, + PointerBarrierType, client, DixReadAccess); + if (err != Success) { + client->errorValue = barrier_id; + return err; + } + + barrier = container_of(b, struct PointerBarrierClient, barrier); + if (barrier->barrier_event_id == event_id) + barrier->release_event_id = event_id; + } + + return Success; +} + Bool XIBarrierInit(void) { diff --git a/Xi/xibarriers.h b/Xi/xibarriers.h index 8f2993f85..f29bb6c8f 100644 --- a/Xi/xibarriers.h +++ b/Xi/xibarriers.h @@ -39,9 +39,7 @@ XIDestroyPointerBarrier(ClientPtr client, Bool XIBarrierInit(void); -#endif /* _XIBARRIERS_H_ */ - - - - +int SProcXIBarrierReleasePointer(ClientPtr client); +int ProcXIBarrierReleasePointer(ClientPtr client); +#endif /* _XIBARRIERS_H_ */ diff --git a/dix/eventconvert.c b/dix/eventconvert.c index 2e422d7a1..bb5c8d30d 100644 --- a/dix/eventconvert.c +++ b/dix/eventconvert.c @@ -57,6 +57,7 @@ static int eventToKeyButtonPointer(DeviceEvent *ev, xEvent **xi, int *count); static int eventToDeviceChanged(DeviceChangedEvent *ev, xEvent **dcce); static int eventToDeviceEvent(DeviceEvent *ev, xEvent **xi); static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi); +static int eventToBarrierEvent(BarrierEvent *ev, xEvent **xi); static int eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi); /* Do not use, read comments below */ @@ -277,6 +278,9 @@ EventToXI2(InternalEvent *ev, xEvent **xi) case ET_RawTouchUpdate: case ET_RawTouchEnd: return eventToRawEvent(&ev->raw_event, xi); + case ET_BarrierHit: + case ET_BarrierLeave: + return eventToBarrierEvent(&ev->barrier_event, xi); default: break; } @@ -782,6 +786,35 @@ eventToRawEvent(RawDeviceEvent *ev, xEvent **xi) return Success; } +static int +eventToBarrierEvent(BarrierEvent *ev, xEvent **xi) +{ + xXIBarrierEvent *barrier; + int len = sizeof(xXIBarrierEvent); + + *xi = calloc(1, len); + barrier = (xXIBarrierEvent*) *xi; + barrier->type = GenericEvent; + barrier->extension = IReqCode; + barrier->evtype = GetXI2Type(ev->type); + barrier->length = bytes_to_int32(len - sizeof(xEvent)); + barrier->deviceid = ev->deviceid; + barrier->sourceid = ev->sourceid; + barrier->time = ev->time; + barrier->event = ev->window; + barrier->root = ev->root; + barrier->dx = double_to_fp3232(ev->dx); + barrier->dy = double_to_fp3232(ev->dy); + barrier->dtime = ev->dt; + barrier->flags = ev->flags; + barrier->eventid = ev->event_id; + barrier->barrier = ev->barrierid; + barrier->root_x = double_to_fp1616(ev->root_x); + barrier->root_y = double_to_fp1616(ev->root_y); + + return Success; +} + /** * Return the corresponding core type for the given event or 0 if no core * equivalent exists. @@ -929,6 +962,12 @@ GetXI2Type(enum EventType type) case ET_TouchOwnership: xi2type = XI_TouchOwnership; break; + case ET_BarrierHit: + xi2type = XI_BarrierHit; + break; + case ET_BarrierLeave: + xi2type = XI_BarrierLeave; + break; default: break; } diff --git a/include/events.h b/include/events.h index 222bf328b..c0ef45d5f 100644 --- a/include/events.h +++ b/include/events.h @@ -27,6 +27,7 @@ typedef struct _DeviceEvent DeviceEvent; typedef struct _DeviceChangedEvent DeviceChangedEvent; typedef struct _TouchOwnershipEvent TouchOwnershipEvent; +typedef struct _BarrierEvent BarrierEvent; #if XFreeXDGA typedef struct _DGAEvent DGAEvent; diff --git a/include/eventstr.h b/include/eventstr.h index eddba8775..38fab4f3c 100644 --- a/include/eventstr.h +++ b/include/eventstr.h @@ -72,6 +72,8 @@ enum EventType { ET_RawTouchUpdate, ET_RawTouchEnd, ET_XQuartz, + ET_BarrierHit, + ET_BarrierLeave, ET_Internal = 0xFF /* First byte */ }; @@ -227,6 +229,25 @@ struct _RawDeviceEvent { uint32_t flags; /**< Flags to be copied into the generated event */ }; +struct _BarrierEvent { + unsigned char header; /**< Always ET_Internal */ + enum EventType type; /**< ET_BarrierHit, ET_BarrierLeave */ + int length; /**< Length in bytes */ + Time time; /**< Time in ms */ + int deviceid; /**< Device to post this event for */ + int sourceid; /**< The physical source device */ + int barrierid; + Window window; + Window root; + double dx; + double dy; + double root_x; + double root_y; + int16_t dt; + int32_t event_id; + uint32_t flags; +}; + #ifdef XQUARTZ #define XQUARTZ_EVENT_MAXARGS 5 struct _XQuartzEvent { @@ -253,6 +274,7 @@ union _InternalEvent { DeviceEvent device_event; DeviceChangedEvent changed_event; TouchOwnershipEvent touch_ownership_event; + BarrierEvent barrier_event; #if XFreeXDGA DGAEvent dga_event; #endif diff --git a/include/inputstr.h b/include/inputstr.h index bb0a77963..17cee9854 100644 --- a/include/inputstr.h +++ b/include/inputstr.h @@ -71,7 +71,7 @@ extern _X_EXPORT int CountBits(const uint8_t * mask, int len); * events to the protocol, the server will not support these events until * this number here is bumped. */ -#define XI2LASTEVENT XI_RawTouchEnd +#define XI2LASTEVENT XI_BarrierLeave #define XI2MASKSIZE ((XI2LASTEVENT >> 3) + 1) /* no of bytes for masks */ /** diff --git a/mi/mieq.c b/mi/mieq.c index b2c7769ec..22f8c91bb 100644 --- a/mi/mieq.c +++ b/mi/mieq.c @@ -407,6 +407,10 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent *event) case ET_RawTouchUpdate: event->raw_event.deviceid = dev->id; break; + case ET_BarrierHit: + case ET_BarrierLeave: + event->barrier_event.deviceid = dev->id; + break; default: ErrorF("[mi] Unknown event type (%d), cannot change id.\n", event->any.type); -- cgit v1.2.3 From 2884b249294e3e8fc749bbb81e2a897314fa0818 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 6 Dec 2012 11:59:53 +1000 Subject: barriers: Don't allow destroying other client's barriers Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index f16094d45..86e811ea1 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -534,6 +534,9 @@ XIDestroyPointerBarrier(ClientPtr client, return err; } + if (CLIENT_ID(stuff->barrier) != client->index) + return BadAccess; + FreeResource(stuff->barrier, RT_NONE); return Success; } -- cgit v1.2.3 From a1ee26e624e6a7030ecb37a608190cb8e3d1f65b Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 6 Dec 2012 12:06:28 +1000 Subject: barriers: Don't allow releasing the pointer on other client's barriers Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 86e811ea1..8f122537a 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -591,6 +591,9 @@ ProcXIBarrierReleasePointer(ClientPtr client) return err; } + if (CLIENT_ID(barrier_id) != client->index) + return BadAccess; + barrier = container_of(b, struct PointerBarrierClient, barrier); if (barrier->barrier_event_id == event_id) barrier->release_event_id = event_id; -- cgit v1.2.3 From 3b2fbcfa6c75ab072926ab9659adf785b324ce28 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 8 Nov 2012 01:17:40 -0500 Subject: barriers: Add support for edge cases when releasing barriers If we release a barrier, we want to ensure that we block all other barriers afterwards, rather than capping the limit to the two nearest barriers. Signed-off-by: Jasper St. Pierre Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 8f122537a..ac94927bf 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -267,6 +267,9 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, struct PointerBarrier *b = &c->barrier; double distance; + if (c->hit) + continue; + if (!barrier_is_blocking_direction(b, dir)) continue; @@ -321,7 +324,6 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, int x = dest_x, y = dest_y; int dir; - int i; struct PointerBarrier *nearest = NULL; PointerBarrierClientPtr c; Time ms = GetTimeInMillis(); @@ -349,8 +351,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, */ dir = barrier_get_direction(current_x, current_y, x, y); -#define MAX_BARRIERS 2 - for (i = 0; i < MAX_BARRIERS; i++) { + while (dir != 0) { c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); if (!c) break; -- cgit v1.2.3 From e3a734d081ebda4460e6c1eb7dcdd0f7c10ba9b4 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sat, 24 Nov 2012 21:42:56 -0500 Subject: barriers: Add a couple pixels of elbow room for the hit detection Pointers (and the hands that drive them) aren't very precise, and the slightest amount of nudging to either side might be enough to reset the event ID, making clients think they have an entirely new hit. Allow for a slightly bigger "hit box" before these barriers get reset. Signed-off-by: Jasper St. Pierre Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index ac94927bf..95a62ae32 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -79,7 +79,7 @@ struct PointerBarrierClient { int barrier_event_id; int release_event_id; Bool hit; - Bool last_hit; + Bool seen; }; typedef struct _BarrierScreen { @@ -220,6 +220,35 @@ barrier_is_blocking(const struct PointerBarrier * barrier, return rc; } +#define HIT_EDGE_EXTENTS 2 +static BOOL +barrier_inside_hit_box(struct PointerBarrier *barrier, int x, int y) +{ + int x1, x2, y1, y2; + int dir; + + x1 = barrier->x1; + x2 = barrier->x2; + y1 = barrier->y1; + y2 = barrier->y2; + dir = ~(barrier->directions); + + if (barrier_is_vertical(barrier)) { + if (dir & BarrierPositiveX) + x1 -= HIT_EDGE_EXTENTS; + if (dir & BarrierNegativeX) + x2 += HIT_EDGE_EXTENTS; + } + if (barrier_is_horizontal(barrier)) { + if (dir & BarrierPositiveY) + y1 -= HIT_EDGE_EXTENTS; + if (dir & BarrierNegativeY) + y2 += HIT_EDGE_EXTENTS; + } + + return x >= x1 && x <= x2 && y >= y1 && y <= y2; +} + static BOOL barrier_blocks_device(struct PointerBarrierClient *client, DeviceIntPtr dev) @@ -267,7 +296,7 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, struct PointerBarrier *b = &c->barrier; double distance; - if (c->hit) + if (c->seen) continue; if (!barrier_is_blocking_direction(b, dir)) @@ -388,8 +417,9 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, } xorg_list_for_each_entry(c, &cs->barriers, entry) { - c->last_hit = c->hit; - c->hit = FALSE; + c->seen = FALSE; + if (c->hit && !barrier_inside_hit_box(&c->barrier, x, y)) + c->hit = FALSE; } out: @@ -459,7 +489,7 @@ CreatePointerBarrierClient(ClientPtr client, ret->barrier_event_id = 0; ret->release_event_id = 0; ret->hit = FALSE; - ret->last_hit = FALSE; + ret->seen = FALSE; ret->barrier.x1 = min(stuff->x1, stuff->x2); ret->barrier.x2 = max(stuff->x1, stuff->x2); ret->barrier.y1 = min(stuff->y1, stuff->y2); -- cgit v1.2.3 From dac9e13a6c874f3b99ba4cf9085ed0e63beb0871 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Mon, 26 Nov 2012 20:37:56 -0500 Subject: barriers: Increment event ID on hit box leave We eventually want to send a new notify event on hitbox leave, which signifies the dawn of a new barrier event ID, so it's convenient if we can put the code here. Signed-off-by: Jasper St. Pierre Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 95a62ae32..45e97cf1d 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -418,8 +418,16 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, xorg_list_for_each_entry(c, &cs->barriers, entry) { c->seen = FALSE; - if (c->hit && !barrier_inside_hit_box(&c->barrier, x, y)) - c->hit = FALSE; + if (!c->hit) + continue; + + if (barrier_inside_hit_box(&c->barrier, x, y)) + continue; + + c->hit = FALSE; + /* If we've left the hit box, this is the + * start of a new event ID. */ + c->barrier_event_id++; } out: @@ -486,7 +494,7 @@ CreatePointerBarrierClient(ClientPtr client, } ret->id = stuff->barrier; - ret->barrier_event_id = 0; + ret->barrier_event_id = 1; ret->release_event_id = 0; ret->hit = FALSE; ret->seen = FALSE; -- cgit v1.2.3 From 7fabecd8e3efe76caeb740d905a02ae4256e3246 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 5 Dec 2012 19:58:05 -0500 Subject: barriers: Send a BarrierLeave event when we leave the hitbox Additionally, add flags when the pointer is released. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 45e97cf1d..75ea4f7b7 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -417,6 +417,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, } xorg_list_for_each_entry(c, &cs->barriers, entry) { + int flags = 0; c->seen = FALSE; if (!c->hit) continue; @@ -425,6 +426,22 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, continue; c->hit = FALSE; + + ev.type = ET_BarrierLeave; + + if (c->barrier_event_id == c->release_event_id) + flags |= XIBarrierPointerReleased; + + ev.flags = flags; + ev.event_id = c->barrier_event_id; + ev.barrierid = c->id; + + ev.dt = ms - c->last_timestamp; + ev.window = c->window->drawable.id; + c->last_timestamp = ms; + + mieqEnqueue(dev, (InternalEvent *) &ev); + /* If we've left the hit box, this is the * start of a new event ID. */ c->barrier_event_id++; -- cgit v1.2.3 From 207e8dee00dd26ff4363edb72058b48b8a76b824 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 9 Dec 2012 19:44:13 -0500 Subject: barriers: Clean up code Rename a variable. This is to make the diff in the next commit cleaner. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 75ea4f7b7..b62a18ee5 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -538,12 +538,12 @@ CreatePointerBarrierClient(ClientPtr client, static int BarrierFreeBarrier(void *data, XID id) { - struct PointerBarrierClient *barrier; + struct PointerBarrierClient *c; - barrier = container_of(data, struct PointerBarrierClient, barrier); - xorg_list_del(&barrier->entry); + c = container_of(data, struct PointerBarrierClient, barrier); + xorg_list_del(&c->entry); - free(barrier); + free(c); return Success; } -- cgit v1.2.3 From 6401317bdcf58b2717214ac65c8f47b0c384a7db Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sun, 9 Dec 2012 19:45:48 -0500 Subject: barriers: Send an XI_BarrierLeave event when a barrier is destroyed This ensures that we always complete an event sequence. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index b62a18ee5..2e7f2cc9d 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -539,8 +539,52 @@ static int BarrierFreeBarrier(void *data, XID id) { struct PointerBarrierClient *c; + Time ms = GetTimeInMillis(); c = container_of(data, struct PointerBarrierClient, barrier); + + /* FIXME: this is really broken for multidevice */ + if (c->hit) { + DeviceIntPtr dev = NULL; + ScreenPtr screen = c->screen; + BarrierEvent ev = { + .header = ET_Internal, + .type = ET_BarrierLeave, + .length = sizeof (BarrierEvent), + .time = ms, + /* .deviceid */ + .sourceid = 0, + .barrierid = c->id, + .window = c->window->drawable.id, + .root = screen->root->drawable.id, + .dx = 0, + .dy = 0, + /* .root_x */ + /* .root_y */ + .dt = ms - c->last_timestamp, + .event_id = c->barrier_event_id, + .flags = XIBarrierPointerReleased, + }; + + for (dev = inputInfo.devices; dev; dev = dev->next) { + int root_x, root_y; + + if (dev->type != MASTER_POINTER) + continue; + + if (!barrier_blocks_device(c, dev)) + continue; + + ev.deviceid = dev->id; + + GetSpritePosition(dev, &root_x, &root_y); + ev.root_x = root_x; + ev.root_y = root_y; + + mieqEnqueue(dev, (InternalEvent *) &ev); + } + } + xorg_list_del(&c->entry); free(c); -- cgit v1.2.3 From 0a5a0d7c242add4d79721c1b90073b603a475534 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Tue, 27 Nov 2012 14:29:52 -0500 Subject: barriers: Replace complex intersection test with simpler math Since barriers are axis-aligned, we can do the intersection test with simple interpolation rather than line-segment intersection. This also helps us out in the future when we want the barriers to extend to be rays and lines rather than just segments. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 78 ++++++++++++++++++++------------------------------------- 1 file changed, 27 insertions(+), 51 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 2e7f2cc9d..a65d2080b 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -140,6 +140,9 @@ barrier_is_blocking_direction(const struct PointerBarrier * barrier, return (barrier->directions & direction) != direction; } +#define T(v, a, b) (((float)v) - (a)) / ((b) - (a)) +#define F(t, a, b) ((t) * ((a) - (b)) + (a)) + /** * Test if the movement vector x1/y1 → x2/y2 is intersecting with the * barrier. A movement vector with the startpoint or endpoint adjacent to @@ -157,67 +160,40 @@ 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) + float t, y; + t = T(barrier->x1, x1, x2); + if (t < 0 || t > 1) return FALSE; - if (dir & BarrierNegativeX && x1 == (barrier->x1 - 1)) + + /* Edge case: moving away from barrier. */ + if (x2 > x1 && t == 0) 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; - } + + y = F(t, y1, y2); + if (y < barrier->y1 || y > barrier->y2) + return FALSE; + + *distance = sqrt((pow(y - y1, 2) + pow(barrier->x1 - x1, 2))); + return TRUE; } else { - /* handle immediate barrier adjacency, moving away */ - if (dir & BarrierPositiveY && y1 == barrier->y1) + float t, x; + t = T(barrier->y1, y1, y2); + if (t < 0 || t > 1) 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; - } + /* Edge case: moving away from barrier. */ + if (y2 > y1 && t == 0) + return FALSE; - if (ua > 0 && ua <= 1) { - double ix = barrier->x1 + ua * (barrier->x2 - barrier->x1); - double iy = barrier->y1 + ua * (barrier->y2 - barrier->y1); + x = F(t, x1, x2); + if (x < barrier->x1 || x > barrier->x2) + return FALSE; - *distance = sqrt(pow(x1 - ix, 2) + pow(y1 - iy, 2)); - rc = TRUE; + *distance = sqrt((pow(x - x1, 2) + pow(barrier->y1 - y1, 2))); + return TRUE; } - - return rc; } #define HIT_EDGE_EXTENTS 2 -- cgit v1.2.3 From 707b4dc61f18960611409ef5ad8947be189f7296 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Tue, 27 Nov 2012 15:31:52 -0500 Subject: barriers: Support line and ray barriers This allows clients to add barriers that extend to the edge of the screen. Clients are encouraged to use these instead of precise coordinates in these cases to help prevent pointer leaks. Signed-off-by: Jasper St. Pierre Reviewed-by: Peter Hutterer --- Xi/xibarriers.c | 46 ++++++++++++++++++++++++++++++++++++++++------ Xi/xibarriers.h | 2 +- test/fixes.c | 26 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index a65d2080b..27b21eedb 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -140,6 +140,19 @@ barrier_is_blocking_direction(const struct PointerBarrier * barrier, return (barrier->directions & direction) != direction; } +static BOOL +inside_segment(int v, int v1, int v2) +{ + if (v1 < 0 && v2 < 0) /* line */ + return TRUE; + else if (v1 < 0) /* ray */ + return v <= v2; + else if (v2 < 0) /* ray */ + return v >= v1; + else /* line segment */ + return v >= v1 && v <= v2; +} + #define T(v, a, b) (((float)v) - (a)) / ((b) - (a)) #define F(t, a, b) ((t) * ((a) - (b)) + (a)) @@ -171,7 +184,7 @@ barrier_is_blocking(const struct PointerBarrier * barrier, return FALSE; y = F(t, y1, y2); - if (y < barrier->y1 || y > barrier->y2) + if (!inside_segment(y, barrier->y1, barrier->y2)) return FALSE; *distance = sqrt((pow(y - y1, 2) + pow(barrier->x1 - x1, 2))); @@ -188,7 +201,7 @@ barrier_is_blocking(const struct PointerBarrier * barrier, return FALSE; x = F(t, x1, x2); - if (x < barrier->x1 || x > barrier->x2) + if (!inside_segment(x, barrier->x1, barrier->x2)) return FALSE; *distance = sqrt((pow(x - x1, 2) + pow(barrier->y1 - y1, 2))); @@ -428,6 +441,18 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, *out_y = y; } +static void +sort_min_max(INT16 *a, INT16 *b) +{ + INT16 A, B; + if (*a < 0 || *b < 0) + return; + A = *a; + B = *b; + *a = min(A, B); + *b = max(A, B); +} + static int CreatePointerBarrierClient(ClientPtr client, xXFixesCreatePointerBarrierReq * stuff, @@ -491,10 +516,12 @@ CreatePointerBarrierClient(ClientPtr client, ret->release_event_id = 0; ret->hit = FALSE; ret->seen = FALSE; - 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.x1 = stuff->x1; + ret->barrier.x2 = stuff->x2; + ret->barrier.y1 = stuff->y1; + ret->barrier.y2 = stuff->y2; + sort_min_max(&ret->barrier.x1, &ret->barrier.x2); + sort_min_max(&ret->barrier.y1, &ret->barrier.y2); ret->barrier.directions = stuff->directions & 0x0f; if (barrier_is_horizontal(&ret->barrier)) ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX); @@ -587,6 +614,13 @@ XICreatePointerBarrier(ClientPtr client, if (barrier_is_horizontal(&b) && barrier_is_vertical(&b)) return BadValue; + /* no infinite barriers on the wrong axis */ + if (barrier_is_horizontal(&b) && (b.y1 < 0 || b.y2 < 0)) + return BadValue; + + if (barrier_is_vertical(&b) && (b.x1 < 0 || b.x2 < 0)) + return BadValue; + if ((err = CreatePointerBarrierClient(client, stuff, &barrier))) return err; diff --git a/Xi/xibarriers.h b/Xi/xibarriers.h index f29bb6c8f..bdcb0b289 100644 --- a/Xi/xibarriers.h +++ b/Xi/xibarriers.h @@ -11,7 +11,7 @@ extern _X_EXPORT RESTYPE PointerBarrierType; struct PointerBarrier { - CARD16 x1, x2, y1, y2; + INT16 x1, x2, y1, y2; CARD32 directions; }; diff --git a/test/fixes.c b/test/fixes.c index 7807c73ce..4ac6750e4 100644 --- a/test/fixes.c +++ b/test/fixes.c @@ -265,6 +265,32 @@ fixes_pointer_barriers_test(void) x2 = x + 100; assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance)); + /* ray vert barrier */ + barrier.x1 = x; + barrier.x2 = x; + barrier.y1 = -1; + barrier.y2 = y + 100; + + /* ray barrier simple case */ + y1 = y; + y2 = y; + x1 = x + 50; + x2 = x - 50; + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance)); + + /* endpoint outside y range; should be blocked */ + y1 = y - 1000; + y2 = y - 1000; + x1 = x + 50; + x2 = x - 50; + assert(barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance)); + + /* endpoint outside y range */ + y1 = y + 150; + y2 = y + 150; + x1 = x + 50; + x2 = x - 50; + assert(!barrier_is_blocking(&barrier, x1, y1, x2, y2, &distance)); } static void -- cgit v1.2.3 From 21a15f9a04ec0a6c8f654eef943561e98db2475d Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 3 Dec 2012 10:41:16 +1000 Subject: Pass the event list through to the pointer barrier code to return it Instead of having the pointer barrier code enqueue events separately from GetPointerEvents, pass the event list through and let it add to it. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 15 ++++++++++++--- dix/getevents.c | 15 +++++++++++---- include/input.h | 3 ++- mi/mipointer.c | 6 ++++-- mi/mipointer.h | 3 ++- 5 files changed, 31 insertions(+), 11 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 27b21eedb..104280c62 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -335,7 +335,8 @@ void input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, int current_x, int current_y, int dest_x, int dest_y, - int *out_x, int *out_y) + int *out_x, int *out_y, + int *nevents, InternalEvent* events) { /* Clamped coordinates here refer to screen edge clamping. */ BarrierScreenPtr cs = GetBarrierScreen(screen); @@ -356,6 +357,10 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, .dy = dest_y - current_y, .root = screen->root->drawable.id, }; + InternalEvent *barrier_events = events; + + if (nevents) + *nevents = 0; if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev)) goto out; @@ -402,7 +407,9 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.window = c->window->drawable.id; c->last_timestamp = ms; - mieqEnqueue(dev, (InternalEvent *) &ev); + barrier_events->barrier_event = ev; + barrier_events++; + *nevents += 1; } xorg_list_for_each_entry(c, &cs->barriers, entry) { @@ -429,7 +436,9 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.window = c->window->drawable.id; c->last_timestamp = ms; - mieqEnqueue(dev, (InternalEvent *) &ev); + barrier_events->barrier_event = ev; + barrier_events++; + *nevents += 1; /* If we've left the hit box, this is the * start of a new event ID. */ diff --git a/dix/getevents.c b/dix/getevents.c index fa538d9f4..3d41e1e5a 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -916,10 +916,13 @@ scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask, * @param[in,out] devy y desktop-wide coordinate in device coordinate system * @param[in,out] screenx x coordinate in desktop coordinate system * @param[in,out] screeny y coordinate in desktop coordinate system + * @param[out] nevents Number of barrier events added to events + * @param[in,out] events List of events barrier events are added to */ static ScreenPtr positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, - double *devx, double *devy, double *screenx, double *screeny) + double *devx, double *devy, double *screenx, double *screeny, + int *nevents, InternalEvent* events) { ScreenPtr scr = miPointerGetScreen(dev); double tmpx, tmpy; @@ -933,7 +936,7 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, /* miPointerSetPosition takes care of crossing screens for us, as well as * clipping to the current screen. Coordinates returned are in desktop * coord system */ - scr = miPointerSetPosition(dev, mode, screenx, screeny); + scr = miPointerSetPosition(dev, mode, screenx, screeny, nevents, events); /* If we were constrained, rescale x/y from the screen coordinates so * the device valuators reflect the correct position. For screen @@ -1319,6 +1322,7 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, int sx, sy; /* for POINTER_SCREEN */ ValuatorMask mask; ScreenPtr scr; + int num_barrier_events = 0; switch (type) { case MotionNotify: @@ -1395,7 +1399,10 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, } scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, - &mask, &devx, &devy, &screenx, &screeny); + &mask, &devx, &devy, &screenx, &screeny, + &num_barrier_events, events); + num_events += num_barrier_events; + events += num_barrier_events; /* screenx, screeny are in desktop coordinates, mask is in device coordinates per-screen (the event data) @@ -1945,7 +1952,7 @@ GetTouchEvents(InternalEvent *events, DeviceIntPtr dev, uint32_t ddx_touchid, scr = scale_to_desktop(dev, &mask, &devx, &devy, &screenx, &screeny); if (emulate_pointer) scr = positionSprite(dev, Absolute, &mask, - &devx, &devy, &screenx, &screeny); + &devx, &devy, &screenx, &screeny, NULL, NULL); /* see fill_pointer_events for coordinate systems */ if (emulate_pointer) diff --git a/include/input.h b/include/input.h index a5d0462c9..23a20b59d 100644 --- a/include/input.h +++ b/include/input.h @@ -681,6 +681,7 @@ extern _X_HIDDEN void update_desktop_dimensions(void); extern _X_HIDDEN void input_constrain_cursor(DeviceIntPtr pDev, ScreenPtr screen, int current_x, int current_y, int dest_x, int dest_y, - int *out_x, int *out_y); + int *out_x, int *out_y, + int *nevents, InternalEvent* events); #endif /* INPUT_H */ diff --git a/mi/mipointer.c b/mi/mipointer.c index d1708249f..f4fbd2912 100644 --- a/mi/mipointer.c +++ b/mi/mipointer.c @@ -565,7 +565,8 @@ miPointerMoveNoEvent(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y) */ ScreenPtr miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, - double *screeny) + double *screeny, + int *nevents, InternalEvent* events) { miPointerScreenPtr pScreenPriv; ScreenPtr pScreen; @@ -598,7 +599,8 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, input_constrain_cursor(pDev, pScreen, current_x, current_y, x, y, - &constrained_x, &constrained_y); + &constrained_x, &constrained_y, + nevents, events); x = constrained_x; y = constrained_y; diff --git a/mi/mipointer.h b/mi/mipointer.h index 1500e216a..f89dff31e 100644 --- a/mi/mipointer.h +++ b/mi/mipointer.h @@ -115,7 +115,8 @@ miPointerGetPosition(DeviceIntPtr pDev, int *x, int *y); /* Moves the cursor to the specified position. May clip the co-ordinates: * x and y are modified in-place. */ extern _X_EXPORT ScreenPtr -miPointerSetPosition(DeviceIntPtr pDev, int mode, double *x, double *y); +miPointerSetPosition(DeviceIntPtr pDev, int mode, double *x, double *y, + int *nevents, InternalEvent *events); extern _X_EXPORT void miPointerUpdateSprite(DeviceIntPtr pDev); -- cgit v1.2.3 From c50db6faba4ee1c27b735c6e9c98a4276ba3c7ff Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 6 Dec 2012 12:58:55 +1000 Subject: Xi: fill in barrier root x/y after clamping to RandR outputs x/y for barrier events should contain the actual pointer position. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 4 ++++ mi/mipointer.c | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 104280c62..048af62a2 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -407,6 +407,8 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.window = c->window->drawable.id; c->last_timestamp = ms; + /* root x/y is filled in later */ + barrier_events->barrier_event = ev; barrier_events++; *nevents += 1; @@ -436,6 +438,8 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.window = c->window->drawable.id; c->last_timestamp = ms; + /* root x/y is filled in later */ + barrier_events->barrier_event = ev; barrier_events++; *nevents += 1; diff --git a/mi/mipointer.c b/mi/mipointer.c index f4fbd2912..7bc09830a 100644 --- a/mi/mipointer.c +++ b/mi/mipointer.c @@ -573,6 +573,8 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, ScreenPtr newScreen; int x, y; Bool switch_screen = FALSE; + Bool should_constrain_barriers = FALSE; + int i; miPointerPtr pPointer; @@ -589,7 +591,9 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, x -= pScreen->x; y -= pScreen->y; - if (mode == Relative) { + should_constrain_barriers = (mode == Relative); + + if (should_constrain_barriers) { /* coordinates after clamped to a barrier */ int constrained_x, constrained_y; int current_x, current_y; /* current position in per-screen coord */ @@ -637,6 +641,18 @@ miPointerSetPosition(DeviceIntPtr pDev, int mode, double *screenx, if (pPointer->x != x || pPointer->y != y || pPointer->pScreen != pScreen) miPointerMoveNoEvent(pDev, pScreen, x, y); + /* check if we generated any barrier events and if so, update root x/y + * to the fully constrained coords */ + if (should_constrain_barriers) { + for (i = 0; i < *nevents; i++) { + if (events[i].any.type == ET_BarrierHit || + events[i].any.type == ET_BarrierLeave) { + events[i].barrier_event.root_x = x; + events[i].barrier_event.root_y = y; + } + } + } + /* Convert to desktop coordinates again */ x += pScreen->x; y += pScreen->y; -- cgit v1.2.3 From 353aa515922e1095047161ec47a2722772218f20 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 6 Dec 2012 15:09:27 +1000 Subject: Xi: deliver barrier events as grabbed events where necessary If the grab_window is the barrier window and the client owns the grab, deliver as normal grabbed event (respecting owner_events). Otherwise, deliver as usual. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/exevents.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'Xi') diff --git a/Xi/exevents.c b/Xi/exevents.c index e606d9ea5..a7ec0c415 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1647,6 +1647,7 @@ ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev) BarrierEvent *be = &e->barrier_event; xEvent *ev; int rc; + GrabPtr grab = dev->deviceGrab.grab; if (!IsMaster(dev)) return; @@ -1660,10 +1661,21 @@ ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev) return; } - filter = GetEventFilter(dev, ev); + /* A client has a grab, deliver to this client if the grab_window is the + barrier window. + + Otherwise, deliver normally to the client. + */ + if (grab && + CLIENT_ID(be->barrierid) == CLIENT_ID(grab->resource) && + grab->window->drawable.id == be->window) { + DeliverGrabbedEvent(e, dev, FALSE); + } else { + filter = GetEventFilter(dev, ev); - DeliverEventsToWindow(dev, pWin, ev, 1, - filter, NullGrab); + DeliverEventsToWindow(dev, pWin, ev, 1, + filter, NullGrab); + } free(ev); } -- cgit v1.2.3 From 88a2cccc37cac4e171f9bfc020017ddfa66ae417 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 7 Dec 2012 09:11:13 +1000 Subject: Xi: if the device is currently grabbed, flag the barrier event Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/exevents.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Xi') diff --git a/Xi/exevents.c b/Xi/exevents.c index a7ec0c415..f025dd1ec 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -1655,6 +1655,9 @@ ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev) if (dixLookupWindow(&pWin, be->window, serverClient, DixReadAccess) != Success) return; + if (grab) + be->flags |= XIBarrierDeviceIsGrabbed; + rc = EventToXI2(e, &ev); if (rc != Success) { ErrorF("[Xi] event conversion from %s failed with code %d\n", __func__, rc); -- cgit v1.2.3 From 151d44149a09dd125e25b3d94f22a609f0221548 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 10 Dec 2012 10:53:49 +1000 Subject: Xi: swap sequence number and evtype in barrier events Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/extinit.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Xi') diff --git a/Xi/extinit.c b/Xi/extinit.c index 175d89b00..606d61c88 100644 --- a/Xi/extinit.c +++ b/Xi/extinit.c @@ -847,7 +847,9 @@ SBarrierEvent(xXIBarrierEvent * from, xXIBarrierEvent * to) { to->type = from->type; + cpswaps(from->sequenceNumber, to->sequenceNumber); cpswapl(from->length, to->length); + cpswaps(from->evtype, to->evtype); cpswapl(from->time, to->time); cpswaps(from->deviceid, to->deviceid); cpswaps(from->sourceid, to->sourceid); -- cgit v1.2.3 From cc10ac8f0e07854647e1fd7cc70b7e9f8d919fd1 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 10 Dec 2012 11:00:46 +1000 Subject: Xi: fix swapping for barrier events Protocol events don't contain pointers, so it's easier to copy everything over, then swap in-place. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/extinit.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'Xi') diff --git a/Xi/extinit.c b/Xi/extinit.c index 606d61c88..73b084cd9 100644 --- a/Xi/extinit.c +++ b/Xi/extinit.c @@ -845,30 +845,27 @@ STouchOwnershipEvent(xXITouchOwnershipEvent * from, xXITouchOwnershipEvent * to) static void SBarrierEvent(xXIBarrierEvent * from, xXIBarrierEvent * to) { - to->type = from->type; - - cpswaps(from->sequenceNumber, to->sequenceNumber); - cpswapl(from->length, to->length); - cpswaps(from->evtype, to->evtype); - cpswapl(from->time, to->time); - cpswaps(from->deviceid, to->deviceid); - cpswaps(from->sourceid, to->sourceid); - cpswapl(from->event, to->event); - cpswapl(from->root, to->root); - cpswapl(from->root_x, to->root_x); - cpswapl(from->root_y, to->root_y); - -#define SWAP_FP3232(x, y) \ - do { \ - cpswapl((x).integral, (y).integral); \ - cpswapl((x).frac, (y).frac); \ - } while(0) - - SWAP_FP3232(from->dx, to->dx); - SWAP_FP3232(from->dy, to->dy); - cpswapl(from->dtime, to->dtime); - cpswapl(from->barrier, to->barrier); - cpswapl(from->eventid, to->eventid); + + *to = *from; + + swaps(&from->sequenceNumber); + swapl(&from->length); + swaps(&from->evtype); + swapl(&from->time); + swaps(&from->deviceid); + swaps(&from->sourceid); + swapl(&from->event); + swapl(&from->root); + swapl(&from->root_x); + swapl(&from->root_y); + + swapl(&from->dx.integral); + swapl(&from->dx.frac); + swapl(&from->dy.integral); + swapl(&from->dy.frac); + swapl(&from->dtime); + swapl(&from->barrier); + swapl(&from->eventid); } /** Event swapping function for XI2 events. */ -- cgit v1.2.3 From f71c2f895c9e2f9d0d42feaac2a3e1d2deb71f67 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 12 Dec 2012 15:41:32 +1000 Subject: Xi: fix per-device barrier handling Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 235 ++++++++++++++++++++++++++++++++++++++----------- Xi/xibarriers.h | 3 + Xi/xichangehierarchy.c | 6 ++ 3 files changed, 195 insertions(+), 49 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 048af62a2..7e8b2e6cd 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -67,19 +67,28 @@ static DevPrivateKeyRec BarrierScreenPrivateKeyRec; typedef struct PointerBarrierClient *PointerBarrierClientPtr; +struct PointerBarrierDevice { + struct xorg_list entry; + int deviceid; + Time last_timestamp; + int barrier_event_id; + int release_event_id; + Bool hit; + Bool seen; +}; + struct PointerBarrierClient { XID id; ScreenPtr screen; WindowPtr window; struct PointerBarrier barrier; struct xorg_list entry; + /* num_devices/device_ids are devices the barrier applies to */ int num_devices; int *device_ids; /* num_devices */ - Time last_timestamp; - int barrier_event_id; - int release_event_id; - Bool hit; - Bool seen; + + /* per_device keeps track of devices actually blocked by barriers */ + struct xorg_list per_device; }; typedef struct _BarrierScreen { @@ -90,6 +99,47 @@ typedef struct _BarrierScreen { #define GetBarrierScreenIfSet(s) GetBarrierScreen(s) #define SetBarrierScreen(s,p) dixSetPrivate(&(s)->devPrivates, BarrierScreenPrivateKey, p) +static struct PointerBarrierDevice *AllocBarrierDevice(void) +{ + struct PointerBarrierDevice *pbd = NULL; + + pbd = malloc(sizeof(struct PointerBarrierDevice)); + if (!pbd) + return NULL; + + pbd->deviceid = -1; /* must be set by caller */ + pbd->barrier_event_id = 1; + pbd->release_event_id = 0; + pbd->hit = FALSE; + pbd->seen = FALSE; + xorg_list_init(&pbd->entry); + + return pbd; +} + +static void FreePointerBarrierClient(struct PointerBarrierClient *c) +{ + struct PointerBarrierDevice *pbd = NULL, *tmp = NULL; + + xorg_list_for_each_entry_safe(pbd, tmp, &c->per_device, entry) { + free(pbd); + } + free(c); +} + +static struct PointerBarrierDevice *GetBarrierDevice(struct PointerBarrierClient *c, int deviceid) +{ + struct PointerBarrierDevice *pbd = NULL; + + xorg_list_for_each_entry(pbd, &c->per_device, entry) { + if (pbd->deviceid == deviceid) + break; + } + + BUG_WARN(!pbd); + return pbd; +} + static BOOL barrier_is_horizontal(const struct PointerBarrier *barrier) { @@ -283,9 +333,11 @@ barrier_find_nearest(BarrierScreenPtr cs, DeviceIntPtr dev, xorg_list_for_each_entry(c, &cs->barriers, entry) { struct PointerBarrier *b = &c->barrier; + struct PointerBarrierDevice *pbd; double distance; - if (c->seen) + pbd = GetBarrierDevice(c, dev->id); + if (pbd->seen) continue; if (!barrier_is_blocking_direction(b, dir)) @@ -358,6 +410,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, .root = screen->root->drawable.id, }; InternalEvent *barrier_events = events; + DeviceIntPtr master; if (nevents) *nevents = 0; @@ -365,6 +418,13 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, if (xorg_list_is_empty(&cs->barriers) || IsFloating(dev)) goto out; + /** + * This function is only called for slave devices, but pointer-barriers + * are for master-devices only. Flip the device to the master here, + * continue with that. + */ + master = GetMaster(dev, MASTER_POINTER); + /* How this works: * Given the origin and the movement vector, get the nearest barrier * to the origin that is blocking the movement. @@ -375,16 +435,19 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, dir = barrier_get_direction(current_x, current_y, x, y); while (dir != 0) { - c = barrier_find_nearest(cs, dev, dir, current_x, current_y, x, y); + struct PointerBarrierDevice *pbd; + + c = barrier_find_nearest(cs, master, dir, current_x, current_y, x, y); if (!c) break; nearest = &c->barrier; - c->seen = TRUE; - c->hit = TRUE; + pbd = GetBarrierDevice(c, master->id); + pbd->seen = TRUE; + pbd->hit = TRUE; - if (c->barrier_event_id == c->release_event_id) + if (pbd->barrier_event_id == pbd->release_event_id) continue; ev.type = ET_BarrierHit; @@ -400,12 +463,12 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, } ev.flags = 0; - ev.event_id = c->barrier_event_id; + ev.event_id = pbd->barrier_event_id; ev.barrierid = c->id; - ev.dt = ms - c->last_timestamp; + ev.dt = ms - pbd->last_timestamp; ev.window = c->window->drawable.id; - c->last_timestamp = ms; + pbd->last_timestamp = ms; /* root x/y is filled in later */ @@ -415,28 +478,31 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, } xorg_list_for_each_entry(c, &cs->barriers, entry) { + struct PointerBarrierDevice *pbd; int flags = 0; - c->seen = FALSE; - if (!c->hit) + + pbd = GetBarrierDevice(c, master->id); + pbd->seen = FALSE; + if (!pbd->hit) continue; if (barrier_inside_hit_box(&c->barrier, x, y)) continue; - c->hit = FALSE; + pbd->hit = FALSE; ev.type = ET_BarrierLeave; - if (c->barrier_event_id == c->release_event_id) + if (pbd->barrier_event_id == pbd->release_event_id) flags |= XIBarrierPointerReleased; ev.flags = flags; - ev.event_id = c->barrier_event_id; + ev.event_id = pbd->barrier_event_id; ev.barrierid = c->id; - ev.dt = ms - c->last_timestamp; + ev.dt = ms - pbd->last_timestamp; ev.window = c->window->drawable.id; - c->last_timestamp = ms; + pbd->last_timestamp = ms; /* root x/y is filled in later */ @@ -446,7 +512,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, /* If we've left the hit box, this is the * start of a new event ID. */ - c->barrier_event_id++; + pbd->barrier_event_id++; } out: @@ -479,6 +545,7 @@ CreatePointerBarrierClient(ClientPtr client, int i; struct PointerBarrierClient *ret; CARD16 *in_devices; + DeviceIntPtr dev; size = sizeof(*ret) + sizeof(DeviceIntPtr) * stuff->num_devices; ret = malloc(size); @@ -487,6 +554,8 @@ CreatePointerBarrierClient(ClientPtr client, return BadAlloc; } + xorg_list_init(&ret->per_device); + err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess); if (err != Success) { client->errorValue = stuff->window; @@ -524,11 +593,25 @@ CreatePointerBarrierClient(ClientPtr client, ret->device_ids[i] = device_id; } + /* Alloc one per master pointer, they're the ones that can be blocked */ + xorg_list_init(&ret->per_device); + nt_list_for_each_entry(dev, inputInfo.devices, next) { + struct PointerBarrierDevice *pbd; + + if (dev->type != MASTER_POINTER) + continue; + + pbd = AllocBarrierDevice(); + if (!pbd) { + err = BadAlloc; + goto error; + } + pbd->deviceid = dev->id; + + xorg_list_add(&pbd->entry, &ret->per_device); + } + ret->id = stuff->barrier; - ret->barrier_event_id = 1; - ret->release_event_id = 0; - ret->hit = FALSE; - ret->seen = FALSE; ret->barrier.x1 = stuff->x1; ret->barrier.x2 = stuff->x2; ret->barrier.y1 = stuff->y1; @@ -547,7 +630,7 @@ CreatePointerBarrierClient(ClientPtr client, error: *client_out = NULL; - free(ret); + FreePointerBarrierClient(ret); return err; } @@ -556,13 +639,15 @@ BarrierFreeBarrier(void *data, XID id) { struct PointerBarrierClient *c; Time ms = GetTimeInMillis(); + DeviceIntPtr dev = NULL; + ScreenPtr screen; c = container_of(data, struct PointerBarrierClient, barrier); + screen = c->screen; - /* FIXME: this is really broken for multidevice */ - if (c->hit) { - DeviceIntPtr dev = NULL; - ScreenPtr screen = c->screen; + for (dev = inputInfo.devices; dev; dev = dev->next) { + struct PointerBarrierDevice *pbd; + int root_x, root_y; BarrierEvent ev = { .header = ET_Internal, .type = ET_BarrierLeave, @@ -577,36 +662,79 @@ BarrierFreeBarrier(void *data, XID id) .dy = 0, /* .root_x */ /* .root_y */ - .dt = ms - c->last_timestamp, - .event_id = c->barrier_event_id, + /* .dt */ + /* .event_id */ .flags = XIBarrierPointerReleased, }; - for (dev = inputInfo.devices; dev; dev = dev->next) { - int root_x, root_y; - if (dev->type != MASTER_POINTER) - continue; + if (dev->type != MASTER_POINTER) + continue; - if (!barrier_blocks_device(c, dev)) - continue; + pbd = GetBarrierDevice(c, dev->id); + if (!pbd->hit) + continue; - ev.deviceid = dev->id; + ev.deviceid = dev->id; + ev.event_id = pbd->barrier_event_id, + ev.dt = ms - pbd->last_timestamp, - GetSpritePosition(dev, &root_x, &root_y); - ev.root_x = root_x; - ev.root_y = root_y; + GetSpritePosition(dev, &root_x, &root_y); + ev.root_x = root_x; + ev.root_y = root_y; - mieqEnqueue(dev, (InternalEvent *) &ev); - } + mieqEnqueue(dev, (InternalEvent *) &ev); } xorg_list_del(&c->entry); - free(c); + FreePointerBarrierClient(c); return Success; } +static void add_master_func(pointer res, XID id, pointer devid) +{ + struct PointerBarrier *b; + struct PointerBarrierClient *barrier; + struct PointerBarrierDevice *pbd; + int *deviceid = devid; + + b = res; + barrier = container_of(b, struct PointerBarrierClient, barrier); + + + pbd = AllocBarrierDevice(); + pbd->deviceid = *deviceid; + + xorg_list_add(&pbd->entry, &barrier->per_device); +} + +static void remove_master_func(pointer res, XID id, pointer devid) +{ + struct PointerBarrierDevice *pbd; + struct PointerBarrierClient *barrier; + struct PointerBarrier *b; + int *deviceid = devid; + + b = res; + barrier = container_of(b, struct PointerBarrierClient, barrier); + + pbd = GetBarrierDevice(barrier, *deviceid); + xorg_list_del(&pbd->entry); + free(pbd); +} + +void XIBarrierNewMasterDevice(ClientPtr client, int deviceid) +{ + FindClientResourcesByType(client, PointerBarrierType, add_master_func, &deviceid); +} + +void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid) +{ + /* FIXME: send LeaveNotify */ + FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid); +} + int XICreatePointerBarrier(ClientPtr client, xXFixesCreatePointerBarrierReq * stuff) @@ -698,14 +826,19 @@ ProcXIBarrierReleasePointer(ClientPtr client) info = (xXIBarrierReleasePointerInfo*) &stuff[1]; for (i = 0; i < stuff->num_barriers; i++, info++) { + struct PointerBarrierDevice *pbd; + DeviceIntPtr dev; CARD32 barrier_id, event_id; _X_UNUSED CARD32 device_id; barrier_id = info->barrier; event_id = info->eventid; - /* FIXME: per-device releases */ - device_id = info->deviceid; + err = dixLookupDevice(&dev, info->deviceid, client, DixReadAccess); + if (err != Success) { + client->errorValue = BadDevice; + return err; + } err = dixLookupResourceByType((void **) &b, barrier_id, PointerBarrierType, client, DixReadAccess); @@ -717,9 +850,13 @@ ProcXIBarrierReleasePointer(ClientPtr client) if (CLIENT_ID(barrier_id) != client->index) return BadAccess; + barrier = container_of(b, struct PointerBarrierClient, barrier); - if (barrier->barrier_event_id == event_id) - barrier->release_event_id = event_id; + + pbd = GetBarrierDevice(barrier, dev->id); + + if (pbd->barrier_event_id == event_id) + pbd->release_event_id = event_id; } return Success; diff --git a/Xi/xibarriers.h b/Xi/xibarriers.h index bdcb0b289..11e84ec9f 100644 --- a/Xi/xibarriers.h +++ b/Xi/xibarriers.h @@ -42,4 +42,7 @@ XIBarrierInit(void); int SProcXIBarrierReleasePointer(ClientPtr client); int ProcXIBarrierReleasePointer(ClientPtr client); +void XIBarrierNewMasterDevice(ClientPtr client, int deviceid); +void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid); + #endif /* _XIBARRIERS_H_ */ diff --git a/Xi/xichangehierarchy.c b/Xi/xichangehierarchy.c index 89f16d8be..a2cb832c4 100644 --- a/Xi/xichangehierarchy.c +++ b/Xi/xichangehierarchy.c @@ -52,6 +52,7 @@ #include "xkbsrv.h" #include "xichangehierarchy.h" +#include "xibarriers.h" /** * Send the current state of the device hierarchy to all clients. @@ -189,6 +190,8 @@ add_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES]) flags[XTestptr->id] |= XISlaveAttached; flags[XTestkeybd->id] |= XISlaveAttached; + XIBarrierNewMasterDevice(client, ptr->id); + unwind: free(name); return rc; @@ -293,6 +296,8 @@ remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES]) } } + XIBarrierRemoveMasterDevice(client, ptr->id); + /* disable the remove the devices, XTest devices must be done first else the sprites they rely on will be destroyed */ DisableDevice(XTestptr, FALSE); @@ -313,6 +318,7 @@ remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES]) flags[keybd->id] |= XIMasterRemoved; flags[ptr->id] |= XIMasterRemoved; + unwind: return rc; } -- cgit v1.2.3 From e2423b627ef7e4d08ea0685af5e771ddd2b511ce Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 13 Dec 2012 14:04:13 +1000 Subject: Xi: don't store the window pointer in barriers, store the window ID When a client shuts down and resources are being freed, the window may have been freed already, so accessing it to get the window ID is bad. Plus, we never care about the window anyway other than for stuffing it into the event. Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 7e8b2e6cd..171463455 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -80,7 +80,7 @@ struct PointerBarrierDevice { struct PointerBarrierClient { XID id; ScreenPtr screen; - WindowPtr window; + Window window; struct PointerBarrier barrier; struct xorg_list entry; /* num_devices/device_ids are devices the barrier applies to */ @@ -467,7 +467,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.barrierid = c->id; ev.dt = ms - pbd->last_timestamp; - ev.window = c->window->drawable.id; + ev.window = c->window; pbd->last_timestamp = ms; /* root x/y is filled in later */ @@ -501,7 +501,7 @@ input_constrain_cursor(DeviceIntPtr dev, ScreenPtr screen, ev.barrierid = c->id; ev.dt = ms - pbd->last_timestamp; - ev.window = c->window->drawable.id; + ev.window = c->window; pbd->last_timestamp = ms; /* root x/y is filled in later */ @@ -566,7 +566,7 @@ CreatePointerBarrierClient(ClientPtr client, cs = GetBarrierScreen(screen); ret->screen = screen; - ret->window = pWin; + ret->window = stuff->window; ret->num_devices = stuff->num_devices; if (ret->num_devices > 0) ret->device_ids = (int*)&ret[1]; @@ -656,7 +656,7 @@ BarrierFreeBarrier(void *data, XID id) /* .deviceid */ .sourceid = 0, .barrierid = c->id, - .window = c->window->drawable.id, + .window = c->window, .root = screen->root->drawable.id, .dx = 0, .dy = 0, -- cgit v1.2.3 From 2eefa5d6e870c57ac6a5930883d8cfe3a3882a43 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 13 Dec 2012 14:42:32 +1000 Subject: Xi: if a MD is removed, send a barrier leave event (if applicable) Signed-off-by: Peter Hutterer Reviewed-by: Jasper St. Pierre --- Xi/xibarriers.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'Xi') diff --git a/Xi/xibarriers.c b/Xi/xibarriers.c index 171463455..7b7b83f1c 100644 --- a/Xi/xibarriers.c +++ b/Xi/xibarriers.c @@ -714,12 +714,41 @@ static void remove_master_func(pointer res, XID id, pointer devid) struct PointerBarrierDevice *pbd; struct PointerBarrierClient *barrier; struct PointerBarrier *b; + DeviceIntPtr dev; int *deviceid = devid; + int rc; + Time ms = GetTimeInMillis(); + + rc = dixLookupDevice(&dev, *deviceid, serverClient, DixSendAccess); + if (rc != Success) + return; b = res; barrier = container_of(b, struct PointerBarrierClient, barrier); pbd = GetBarrierDevice(barrier, *deviceid); + + if (pbd->hit) { + BarrierEvent ev = { + .header = ET_Internal, + .type =ET_BarrierLeave, + .length = sizeof (BarrierEvent), + .time = ms, + .deviceid = *deviceid, + .sourceid = 0, + .dx = 0, + .dy = 0, + .root = barrier->screen->root->drawable.id, + .window = barrier->window, + .dt = ms - pbd->last_timestamp, + .flags = XIBarrierPointerReleased, + .event_id = pbd->barrier_event_id, + .barrierid = barrier->id, + }; + + mieqEnqueue(dev, (InternalEvent *) &ev); + } + xorg_list_del(&pbd->entry); free(pbd); } @@ -731,7 +760,6 @@ void XIBarrierNewMasterDevice(ClientPtr client, int deviceid) void XIBarrierRemoveMasterDevice(ClientPtr client, int deviceid) { - /* FIXME: send LeaveNotify */ FindClientResourcesByType(client, PointerBarrierType, remove_master_func, &deviceid); } -- cgit v1.2.3