From 9e257029c760883c4ea0715d4fd06476f3fe8053 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 19 Jun 2007 11:28:07 +0930 Subject: [PATCH] Add implicitGrab field to GrabInfoRec. Is set when passive grab is implicit as result of a ButtonPress event. If this is the case, we need to store the XI mask as well as the core mask to ensure delivery of XI events during the grab's lifetime. Remove all core grabs on other devices when client issues a GrabPointer or GrabKeyboard request. Let's assume that the client really only wants one device to interact, so this seems like a reasonable solution. --- dix/devices.c | 1 + dix/events.c | 92 ++++++++++++++++++++++++++++++++++++++++++---- include/dix.h | 4 ++ include/inputstr.h | 6 ++- 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/dix/devices.c b/dix/devices.c index 4672b2ac1..b56423a6d 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -135,6 +135,7 @@ AddInputDevice(DeviceProc deviceProc, Bool autoStart) dev->deviceGrab.grab = NullGrab; dev->deviceGrab.grabTime = currentTime; dev->deviceGrab.fromPassiveGrab = FALSE; + dev->deviceGrab.implicitGrab = FALSE; dev->key = (KeyClassPtr)NULL; dev->valuator = (ValuatorClassPtr)NULL; diff --git a/dix/events.c b/dix/events.c index 4c5f5b9e2..f6e90214d 100644 --- a/dix/events.c +++ b/dix/events.c @@ -209,6 +209,12 @@ static xEvent *xeviexE; ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | \ Mod3Mask | Mod4Mask | Mod5Mask ) #define AllEventMasks (lastEventMask|(lastEventMask-1)) + +/** + * Used to indicate a implicit passive grab created by a ButtonPress event. + * See DeliverEventsToWindow(). + */ +#define ImplicitGrabMask (1 << 7) /* * The following relies on the fact that the ButtonMotionMasks are equal * to the corresponding ButtonMasks from the current modifier/button state. @@ -1238,6 +1244,14 @@ PlayReleasedEvents(void) } } +/** + * Freeze or thaw the given devices. The device's processing proc is + * switched to either the real processing proc (in case of thawing) or an + * enqueuing processing proc (usually EnqueueEvent()). + * + * @param dev The device to freeze/thaw + * @param frozen True to freeze or false to thaw. + */ static void FreezeThaw(DeviceIntPtr dev, Bool frozen) { @@ -1248,6 +1262,14 @@ FreezeThaw(DeviceIntPtr dev, Bool frozen) dev->public.processInputProc = dev->public.realInputProc; } +/** + * Unfreeze devices and replay all events to the respective clients. + * + * ComputeFreezes takes the first event in the device's frozen event queue. It + * runs up the sprite tree (spriteTrace) and searches for the window to replay + * the events from. If it is found, it checks for passive grabs one down from + * the window or delivers the events. + */ void ComputeFreezes(void) { @@ -1394,7 +1416,8 @@ CheckGrabForSyncs(DeviceIntPtr thisDev, Bool thisMode, Bool otherMode) * on, but core events will be sent to other clients. * Can cause the cursor to change if a grab cursor is set. * - * Extension devices are set up for ActivateKeyboardGrab(). + * Note that parameter autoGrab may be (True & ImplicitGrabMask) if the grab + * is an implicit grab caused by a ButtonPress event. * * @param mouse The device to grab. * @param grab The grab structure, needs to be setup. @@ -1428,7 +1451,8 @@ ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab, grab->cursor->refcnt++; grabinfo->activeGrab = *grab; grabinfo->grab = &grabinfo->activeGrab; - grabinfo->fromPassiveGrab = autoGrab; + grabinfo->fromPassiveGrab = autoGrab & ~ImplicitGrabMask; + grabinfo->implicitGrab = autoGrab & ImplicitGrabMask; PostNewCursor(mouse); CheckGrabForSyncs(mouse,(Bool)grab->pointerMode, (Bool)grab->keyboardMode); } @@ -1987,7 +2011,8 @@ DeliverEventsToWindow(DeviceIntPtr pDev, WindowPtr pWin, xEvent tempGrab.genericMasks = NULL; (*inputInfo.pointer->deviceGrab.ActivateGrab)(pDev, &tempGrab, - currentTime, TRUE); + currentTime, + TRUE | ImplicitGrabMask); } else if ((type == MotionNotify) && deliveries) pDev->valuator->motionHintWindow = pWin; @@ -3243,13 +3268,19 @@ DeliverGrabbedEvent(xEvent *xE, DeviceIntPtr thisDev, } else { Mask mask = grab->eventMask; - if (grabinfo->fromPassiveGrab && (xE->u.u.type & - EXTENSION_EVENT_BASE)) + if (grabinfo->fromPassiveGrab && + grabinfo->implicitGrab && + (xE->u.u.type & EXTENSION_EVENT_BASE)) mask = grab->deviceMask; FixUpEventFromWindow(thisDev, xE, grab->window, None, TRUE); - deliveries = TryClientEvents(rClient(grab), xE, count, - mask, filters[xE->u.u.type], grab); + + if (!(!(xE->u.u.type & EXTENSION_EVENT_BASE) && + IsInterferingGrab(rClient(grab), thisDev, xE))) + { + deliveries = TryClientEvents(rClient(grab), xE, count, + mask, filters[xE->u.u.type], grab); + } } if (deliveries && (xE->u.u.type == MotionNotify #ifdef XINPUT @@ -4629,6 +4660,8 @@ ProcGrabPointer(ClientPtr client) if (oldCursor) FreeCursor (oldCursor, (Cursor)0); rep.status = GrabSuccess; + + RemoveOtherCoreGrabs(client, device); } WriteReplyToClient(client, sizeof(xGrabPointerReply), &rep); return Success; @@ -4797,6 +4830,47 @@ GrabDevice(ClientPtr client, DeviceIntPtr dev, return Success; } +/** + * Deactivate any core grabs on the given client except the given device. + * + * This fixes race conditions where clients deal with implicit passive grabs + * on one device, but then actively grab their client pointer, which is + * another device. + * + * Grabs are only removed if the other device matches the type of device. If + * dev is a pointer device, only other pointer grabs are removed. Likewise, if + * dev is a keyboard device, only keyboard grabs are removed. + * + * If dev doesn't have a grab, do nothing and go for a beer. + * + * @param client The client that is to be limited. + * @param dev The only device allowed to have a grab on the client. + */ + +_X_EXPORT void +RemoveOtherCoreGrabs(ClientPtr client, DeviceIntPtr dev) +{ + if (!dev || !dev->deviceGrab.grab) + return; + + DeviceIntPtr it = inputInfo.devices; + for (; it; it = it->next) + { + if (it == dev) + continue; + /* check for IsPointer Device */ + + if (it->deviceGrab.grab && + it->deviceGrab.grab->coreGrab && + SameClient(it->deviceGrab.grab, client)) + { + if ((IsPointerDevice(dev) && IsPointerDevice(it)) || + (IsKeyboardDevice(dev) && IsKeyboardDevice(it))) + (*it->deviceGrab.DeactivateGrab)(it); + } + } +} + /** * Server-side protocol handling for GrabKeyboard request. * @@ -4813,10 +4887,13 @@ ProcGrabKeyboard(ClientPtr client) REQUEST_SIZE_MATCH(xGrabKeyboardReq); if (XaceHook(XACE_DEVICE_ACCESS, client, keyboard, TRUE)) + { result = GrabDevice(client, keyboard, stuff->keyboardMode, stuff->pointerMode, stuff->grabWindow, stuff->ownerEvents, stuff->time, KeyPressMask | KeyReleaseMask, &rep.status, TRUE); + RemoveOtherCoreGrabs(client, keyboard); + } else { result = Success; rep.status = AlreadyGrabbed; @@ -5857,3 +5934,4 @@ ExtUngrabDevice(ClientPtr client, DeviceIntPtr dev) } + diff --git a/include/dix.h b/include/dix.h index f0e2f2ae6..be1cb95f2 100644 --- a/include/dix.h +++ b/include/dix.h @@ -494,6 +494,10 @@ extern int GrabDevice( CARD8 * /* status */, Bool /* coreGrab */); +extern void RemoveOtherCoreGrabs( + ClientPtr /* client */, + DeviceIntPtr /* dev */); + extern void InitEvents(void); extern void InitSprite( DeviceIntPtr /* pDev */, diff --git a/include/inputstr.h b/include/inputstr.h index 986232c79..d9128cce8 100644 --- a/include/inputstr.h +++ b/include/inputstr.h @@ -333,9 +333,11 @@ typedef struct { #define FROZEN_NO_EVENT 5 #define FROZEN_WITH_EVENT 6 #define THAW_OTHERS 7 + typedef struct _GrabInfoRec { - TimeStamp grabTime; - Bool fromPassiveGrab; + TimeStamp grabTime; + Bool fromPassiveGrab; /* true if from passive grab */ + Bool implicitGrab; /* implicit from ButtonPress */ GrabRec activeGrab; GrabPtr grab; CARD8 activatingKey;