diff --git a/dix/events.c b/dix/events.c index faf8eedd5..7627f4ebe 100644 --- a/dix/events.c +++ b/dix/events.c @@ -1685,17 +1685,45 @@ AllowSome(ClientPtr client, * Server-side protocol handling for AllowEvents request. * * Release some events from a frozen device. + * + * In some cases, the grab the client has is not on the ClientPointer but on + * some other device (see ProcGrabPointer comments). To cover this case, we + * need to run through all devices to ensure we don't forget the device we + * actually have a grab on. */ int ProcAllowEvents(ClientPtr client) { TimeStamp time; - DeviceIntPtr mouse = PickPointer(client); - DeviceIntPtr keybd = PickKeyboard(client); + DeviceIntPtr mouse = NULL, + grabbed; + DeviceIntPtr keybd = NULL; + GrabPtr grab; REQUEST(xAllowEventsReq); REQUEST_SIZE_MATCH(xAllowEventsReq); time = ClientTimeToServerTime(stuff->time); + + for (grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + grab = grabbed->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + if (IsPointerDevice(grabbed)) + mouse = grabbed; + else if (IsKeyboardDevice(grabbed)) + keybd = grabbed; + + if (mouse && keybd) + break; + } + } + + if (!mouse) + mouse = PickPointer(client); + if (!keybd) + keybd = PickKeyboard(client); + switch (stuff->mode) { case ReplayPointer: @@ -3104,7 +3132,8 @@ CheckPassiveGrabsOnWindow( #else grab->modifierDevice->key->state; #endif - if (GrabMatchesSecond(&tempGrab, grab) && + /* ignore the device for core events when comparing grabs */ + if (GrabMatchesSecond(&tempGrab, grab, (xE->u.u.type < LASTEvent)) && (!grab->confineTo || (grab->confineTo->realized && BorderSizeNotEmpty(device, grab->confineTo)))) @@ -3119,6 +3148,23 @@ CheckPassiveGrabsOnWindow( } #endif grabinfo = &device->deviceGrab; + /* A passive grab may have been created for a different device + than it is assigned to at this point in time. + Update the grab's device and modifier device to reflect the + current state. + XXX: Since XGrabDeviceButton requires to specify the + modifierDevice explicitly, we don't override this choice. + This essentially requires a client to re-create all + passiveGrabs when the pairing changes... oh well. + */ + if (xE->u.u.type < LASTEVENT) + { + grab->device = device; + grab->modifierDevice = GetPairedKeyboard(device); + if (!grab->modifierDevice) + grab->modifierDevice = inputInfo.keyboard; + } + (*grabinfo->ActivateGrab)(device, grab, currentTime, TRUE); FixUpEventFromWindow(device, xE, grab->window, None, TRUE); @@ -4607,10 +4653,16 @@ ProcGetInputFocus(ClientPtr client) } /** - * Server-side protocol handling for Grabpointer request. + * Server-side protocol handling for GrabPointer request. * * Sets an active grab on the client's ClientPointer and returns success * status to client. + * + * A special case of GrabPointer is when there is already a grab on some + * device (by the same client). In this case, this grab is overwritten, and + * the device stays as it is. This case can happen when a client has a passive + * grab and then grabs the pointer, or when the client already has an active + * grab and the ClientPointer was changed since. */ int ProcGrabPointer(ClientPtr client) @@ -4623,6 +4675,7 @@ ProcGrabPointer(ClientPtr client) REQUEST(xGrabPointerReq); TimeStamp time; int rc; + DeviceIntPtr grabbed = NULL; REQUEST_SIZE_MATCH(xGrabPointerReq); UpdateCurrentTime(); @@ -4677,6 +4730,21 @@ ProcGrabPointer(ClientPtr client) rep.type = X_Reply; rep.sequenceNumber = client->sequence; rep.length = 0; + + /* Check if a the client already has a grab on a device */ + for(grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + if (IsPointerDevice(grabbed)) + { + grab = grabbed->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + device = grabbed; + break; + } + } + } + grab = device->deviceGrab.grab; if ((grab) && !SameClient(grab, client)) rep.status = AlreadyGrabbed; @@ -4720,6 +4788,7 @@ ProcGrabPointer(ClientPtr client) FreeCursor (oldCursor, (Cursor)0); rep.status = GrabSuccess; + /* guarantee only one core pointer grab at a time by this client */ RemoveOtherCoreGrabs(client, device); } WriteReplyToClient(client, sizeof(xGrabPointerReply), &rep); @@ -4732,12 +4801,14 @@ ProcGrabPointer(ClientPtr client) * Changes properties of the grab hold by the client. If the client does not * hold an active grab on the device, nothing happens. * - * Works on the client's ClientPointer. + * Works on the client's ClientPointer, but in some cases the client may have + * a grab on a device that isn't the ClientPointer (see ProcGrabPointer + * comments). */ int ProcChangeActivePointerGrab(ClientPtr client) { - DeviceIntPtr device = PickPointer(client); + DeviceIntPtr device, grabbed; GrabPtr grab = device->deviceGrab.grab; CursorPtr newCursor, oldCursor; REQUEST(xChangeActivePointerGrabReq); @@ -4761,6 +4832,31 @@ ProcChangeActivePointerGrab(ClientPtr client) return BadCursor; } } + if (!grab && !SameClient(grab, client)) + { + /* no grab on ClientPointer, or some other client has a grab on our + * ClientPointer, let's check if we have a pointer grab on some other + * device. */ + for(grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + if (IsPointerDevice(grabbed)) + { + grab = grabbed->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + device = grabbed; + break; + } + } + } + /* nope. no grab on any actual device */ + if (!grabbed) + { + device = inputInfo.pointer; + grab = inputInfo.pointer->deviceGrab.grab; + } + } + if (!grab) return Success; if (!SameClient(grab, client)) @@ -4783,12 +4879,17 @@ ProcChangeActivePointerGrab(ClientPtr client) /** * Server-side protocol handling for UngrabPointer request. * - * Deletes the pointer grab on the client's ClientPointer device. + * Deletes a pointer grab on a device the client has grabbed. This should be + * the ClientPointer, but may not be. So we search the device list for a + * device we have a pointer grab on and then ungrab this device. (see + * ProcGrabPointer comments). We are guaranteed that the client doesn't have + * more than one core pointer grab at a time. */ int ProcUngrabPointer(ClientPtr client) { - DeviceIntPtr device = PickPointer(client); + DeviceIntPtr device = PickPointer(client), + grabbed; GrabPtr grab; TimeStamp time; REQUEST(xResourceReq); @@ -4796,6 +4897,31 @@ ProcUngrabPointer(ClientPtr client) REQUEST_SIZE_MATCH(xResourceReq); UpdateCurrentTime(); grab = device->deviceGrab.grab; + + if (!grab || !grab->coreGrab || !SameClient(grab, client)) + { + /* No pointer grab on ClientPointer. May be a pointer grab on some + * other device */ + for(grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + if (IsPointerDevice(grabbed)) + { + grab = grabbed->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + device = grabbed; + break; + } + } + } + /* nope. no grab on any actual device */ + if (!grabbed) + { + device = inputInfo.pointer; + grab = inputInfo.pointer->deviceGrab.grab; + } + } + time = ClientTimeToServerTime(stuff->id); if ((CompareTimeStamps(time, currentTime) != LATER) && (CompareTimeStamps(time, device->deviceGrab.grabTime) != EARLIER) && @@ -4935,6 +5061,15 @@ RemoveOtherCoreGrabs(ClientPtr client, DeviceIntPtr dev) * Server-side protocol handling for GrabKeyboard request. * * Grabs the client's keyboard and returns success status to client. + * + * In some special cases the client may already have a grab on a keyboard that + * is not the one that is paired with the ClientPointer. This can happen when + * the client alreay has a passive grab on some keyboard device, or when the + * client actively grabbed the keyboard and the ClientPointer or keyboard + * pairing was changed since. + * Therefore, we need to run through all the keyboards available and check if + * there's already a grab on it from our client. The client will only ever + * have one core keyboard grab at a time. */ int ProcGrabKeyboard(ClientPtr client) @@ -4942,16 +5077,32 @@ ProcGrabKeyboard(ClientPtr client) xGrabKeyboardReply rep; REQUEST(xGrabKeyboardReq); int result; - DeviceIntPtr keyboard = PickKeyboard(client); + DeviceIntPtr keyboard = PickKeyboard(client), + grabbed; + GrabPtr grab; REQUEST_SIZE_MATCH(xGrabKeyboardReq); + for (grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + if (IsKeyboardDevice(grabbed)) + { + grab = grabbed->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + keyboard = grabbed; + break; + } + } + } + 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); + /* ensure only one core keyboard grab by this client */ RemoveOtherCoreGrabs(client, keyboard); } else { @@ -4972,18 +5123,40 @@ ProcGrabKeyboard(ClientPtr client) * Server-side protocol handling for UngrabKeyboard request. * * Deletes a possible grab on the client's keyboard. + * + * We may have a grab on a keyboard that isn't the ClientPointer's keyboard. + * Thus we need to check all keyboar devices for a grab. (see ProcGrabKeyboard + * comments) */ int ProcUngrabKeyboard(ClientPtr client) { - DeviceIntPtr device = PickKeyboard(client); + DeviceIntPtr device = PickKeyboard(client), + grabbed; GrabPtr grab; TimeStamp time; REQUEST(xResourceReq); REQUEST_SIZE_MATCH(xResourceReq); UpdateCurrentTime(); + + if (!grab || !grab->coreGrab || !SameClient(grab, client)) + { + for (grabbed = inputInfo.devices; grabbed; grabbed = grabbed->next) + { + if (IsKeyboardDevice(grabbed)) + { + grab = device->deviceGrab.grab; + if (grab && grab->coreGrab && SameClient(grab, client)) + { + device = grabbed; + break; + } + } + } + } grab = device->deviceGrab.grab; + time = ClientTimeToServerTime(stuff->id); if ((CompareTimeStamps(time, currentTime) != LATER) && (CompareTimeStamps(time, device->deviceGrab.grabTime) != EARLIER) && diff --git a/dix/grabs.c b/dix/grabs.c index 9150c3c14..01f4b6439 100644 --- a/dix/grabs.c +++ b/dix/grabs.c @@ -240,12 +240,28 @@ GrabSupersedesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab) return FALSE; } +/** + * Compares two grabs and returns TRUE if the first grab matches the second + * grab. + * + * A match is when + * - the devices set for the grab are equal (this is optional). + * - the event types for both grabs are equal. + * - XXX + * + * @param ignoreDevice TRUE if the device settings on the grabs are to be + * ignored. + * @return TRUE if the grabs match or FALSE otherwise. + */ Bool -GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab) +GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab, Bool ignoreDevice) { - if ((pFirstGrab->device != pSecondGrab->device) || - (pFirstGrab->modifierDevice != pSecondGrab->modifierDevice) || - (pFirstGrab->type != pSecondGrab->type)) + if (!ignoreDevice && + ((pFirstGrab->device != pSecondGrab->device) || + (pFirstGrab->modifierDevice != pSecondGrab->modifierDevice))) + return FALSE; + + if (pFirstGrab->type != pSecondGrab->type) return FALSE; if (GrabSupersedesSecond(pFirstGrab, pSecondGrab) || @@ -278,7 +294,7 @@ AddPassiveGrabToList(GrabPtr pGrab) for (grab = wPassiveGrabs(pGrab->window); grab; grab = grab->next) { - if (GrabMatchesSecond(pGrab, grab)) + if (GrabMatchesSecond(pGrab, grab, FALSE)) { if (CLIENT_BITS(pGrab->resource) != CLIENT_BITS(grab->resource)) { @@ -343,7 +359,7 @@ DeletePassiveGrabFromList(GrabPtr pMinuendGrab) grab = grab->next) { if ((CLIENT_BITS(grab->resource) != CLIENT_BITS(pMinuendGrab->resource)) || - !GrabMatchesSecond(grab, pMinuendGrab)) + !GrabMatchesSecond(grab, pMinuendGrab, FALSE)) continue; if (GrabSupersedesSecond(pMinuendGrab, grab)) { diff --git a/include/dixgrabs.h b/include/dixgrabs.h index 2d66d6ba1..b237ab3ad 100644 --- a/include/dixgrabs.h +++ b/include/dixgrabs.h @@ -47,7 +47,8 @@ extern int DeletePassiveGrab( extern Bool GrabMatchesSecond( GrabPtr /* pFirstGrab */, - GrabPtr /* pSecondGrab */); + GrabPtr /* pSecondGrab */, + Bool /*ignoreDevice*/); extern int AddPassiveGrabToList( GrabPtr /* pGrab */);