diff --git a/Xi/exevents.c b/Xi/exevents.c index 83891f87f..91a346138 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -962,7 +962,7 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device) void ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device) { - GrabPtr grab = device->deviceGrab.grab; + GrabPtr grab; Bool deactivateDeviceGrab = FALSE; int key = 0, rootX, rootY; ButtonClassPtr b; @@ -1060,6 +1060,7 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device) CallCallbacks(&DeviceEventCallback, (pointer) & eventinfo); } #endif + grab = device->deviceGrab.grab; switch(event->type) { @@ -1509,6 +1510,52 @@ GrabKey(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr modifier_device, return AddPassiveGrabToList(client, grab); } +/* Enter/FocusIn grab */ +int +GrabWindow(ClientPtr client, DeviceIntPtr dev, int type, + GrabParameters *param, GrabMask *mask) +{ + WindowPtr pWin; + CursorPtr cursor; + GrabPtr grab; + Mask access_mode = DixGrabAccess; + int rc; + + rc = CheckGrabValues(client, param); + if (rc != Success) + return rc; + + rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess); + if (rc != Success) + return rc; + if (param->cursor == None) + cursor = NullCursor; + else { + rc = dixLookupResourceByType((pointer *)&cursor, param->cursor, + RT_CURSOR, client, DixUseAccess); + if (rc != Success) + { + client->errorValue = param->cursor; + return (rc == BadValue) ? BadCursor : rc; + } + access_mode |= DixForceAccess; + } + if (param->this_device_mode == GrabModeSync || param->other_devices_mode == GrabModeSync) + access_mode |= DixFreezeAccess; + rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, access_mode); + if (rc != Success) + return rc; + + grab = CreateGrab(client->index, dev, dev, pWin, GRABTYPE_XI2, + mask, param, (type == XIGrabtypeEnter) ? XI_Enter : XI_FocusIn, + 0, NULL, cursor); + + if (!grab) + return BadAlloc; + + return AddPassiveGrabToList(client, grab); +} + int SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client, Mask mask, Mask exclusivemasks) @@ -1825,7 +1872,8 @@ DeleteDeviceFromAnyExtEvents(WindowPtr pWin, DeviceIntPtr dev) switch (dev->focus->revert) { case RevertToNone: - DoFocusEvents(dev, pWin, NoneWin, focusEventMode); + if (!ActivateFocusInGrab(dev, NoneWin)) + DoFocusEvents(dev, pWin, NoneWin, focusEventMode); dev->focus->win = NoneWin; dev->focus->traceGood = 0; break; @@ -1836,12 +1884,14 @@ DeleteDeviceFromAnyExtEvents(WindowPtr pWin, DeviceIntPtr dev) dev->focus->traceGood--; } while (!parent->realized); - DoFocusEvents(dev, pWin, parent, focusEventMode); + if (!ActivateFocusInGrab(dev, parent)) + DoFocusEvents(dev, pWin, parent, focusEventMode); dev->focus->win = parent; dev->focus->revert = RevertToNone; break; case RevertToPointerRoot: - DoFocusEvents(dev, pWin, PointerRootWin, focusEventMode); + if (!ActivateFocusInGrab(dev, PointerRootWin)) + DoFocusEvents(dev, pWin, PointerRootWin, focusEventMode); dev->focus->win = PointerRootWin; dev->focus->traceGood = 0; break; @@ -1851,11 +1901,13 @@ DeleteDeviceFromAnyExtEvents(WindowPtr pWin, DeviceIntPtr dev) if (!kbd || (kbd == dev && kbd != inputInfo.keyboard)) kbd = inputInfo.keyboard; if (kbd->focus->win) { - DoFocusEvents(dev, pWin, kbd->focus->win, focusEventMode); + if (!ActivateFocusInGrab(dev, kbd->focus->win)) + DoFocusEvents(dev, pWin, kbd->focus->win, focusEventMode); dev->focus->win = FollowKeyboardWin; dev->focus->traceGood = 0; } else { - DoFocusEvents(dev, pWin, NoneWin, focusEventMode); + if (!ActivateFocusInGrab(dev, NoneWin)) + DoFocusEvents(dev, pWin, NoneWin, focusEventMode); dev->focus->win = NoneWin; dev->focus->traceGood = 0; } diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c index f53b0504c..a8807bd32 100644 --- a/Xi/xipassivegrab.c +++ b/Xi/xipassivegrab.c @@ -95,12 +95,21 @@ ProcXIPassiveGrabDevice(ClientPtr client) return ret; if (stuff->grab_type != XIGrabtypeButton && - stuff->grab_type != XIGrabtypeKeysym) + stuff->grab_type != XIGrabtypeKeysym && + stuff->grab_type != XIGrabtypeEnter && + stuff->grab_type != XIGrabtypeFocusIn) { client->errorValue = stuff->grab_type; return BadValue; } + if ((stuff->grab_type == XIGrabtypeEnter || + stuff->grab_type == XIGrabtypeFocusIn) && stuff->detail != 0) + { + client->errorValue = stuff->detail; + return BadValue; + } + /* Can't grab for modifiers on an attached slave device */ if (!IsMaster(dev)) { @@ -175,6 +184,11 @@ ProcXIPassiveGrabDevice(ClientPtr client) status = GrabKey(client, dev, mod_dev, stuff->detail, ¶m, GRABTYPE_XI2, &mask); break; + case XIGrabtypeEnter: + case XIGrabtypeFocusIn: + status = GrabWindow(client, dev, stuff->grab_type, + ¶m, &mask); + break; } if (status != GrabSuccess) @@ -251,12 +265,21 @@ ProcXIPassiveUngrabDevice(ClientPtr client) return rc; if (stuff->grab_type != XIGrabtypeButton && - stuff->grab_type != XIGrabtypeKeysym) + stuff->grab_type != XIGrabtypeKeysym && + stuff->grab_type != XIGrabtypeEnter && + stuff->grab_type != XIGrabtypeFocusIn) { client->errorValue = stuff->grab_type; return BadValue; } + if ((stuff->grab_type == XIGrabtypeEnter || + stuff->grab_type == XIGrabtypeFocusIn) && stuff->detail != 0) + { + client->errorValue = stuff->detail; + return BadValue; + } + rc = dixLookupWindow(&win, stuff->grab_window, client, DixSetAttrAccess); if (rc != Success) return rc; @@ -269,8 +292,13 @@ ProcXIPassiveUngrabDevice(ClientPtr client) tempGrab.resource = client->clientAsMask; tempGrab.device = dev; tempGrab.window = win; - tempGrab.type = - (stuff->grab_type == XIGrabtypeButton) ? XI_ButtonPress : XI_KeyPress; + switch(stuff->grab_type) + { + case XIGrabtypeButton: tempGrab.type = XI_ButtonPress; break; + case XIGrabtypeKeysym: tempGrab.type = XI_KeyPress; break; + case XIGrabtypeEnter: tempGrab.type = XI_Enter; break; + case XIGrabtypeFocusIn: tempGrab.type = XI_FocusIn; break; + } tempGrab.grabtype = GRABTYPE_XI2; tempGrab.modifierDevice = mod_dev; tempGrab.modifiersDetail.pMask = NULL; diff --git a/dix/eventconvert.c b/dix/eventconvert.c index 403282cb3..507289179 100644 --- a/dix/eventconvert.c +++ b/dix/eventconvert.c @@ -155,6 +155,12 @@ EventToXI2(InternalEvent *ev, xEvent **xi) { switch (ev->any.type) { + /* Enter/FocusIn are for grabs. We don't need an actual event, since + * the real events delivered are triggered elsewhere */ + case ET_Enter: + case ET_FocusIn: + *xi = NULL; + return Success; case ET_Motion: case ET_ButtonPress: case ET_ButtonRelease: @@ -525,6 +531,8 @@ GetXI2Type(InternalEvent *event) case ET_Hierarchy: xi2type = XI_HierarchyChanged; break; case ET_DeviceChanged: xi2type = XI_DeviceChanged; break; case ET_Raw: xi2type = XI_RawEvent; break; + case ET_FocusIn: xi2type = XI_FocusIn; break; + case ET_FocusOut: xi2type = XI_FocusOut; break; default: break; } diff --git a/dix/events.c b/dix/events.c index 5e1ab1fa5..5d22016f7 100644 --- a/dix/events.c +++ b/dix/events.c @@ -224,6 +224,10 @@ static void CheckPhysLimits(DeviceIntPtr pDev, Bool generateEvents, Bool confineToScreen, ScreenPtr pScreen); +static Bool CheckPassiveGrabsOnWindow(WindowPtr pWin, + DeviceIntPtr device, + DeviceEvent *event, + BOOL checkCore); /** * Main input device struct. @@ -2623,6 +2627,74 @@ XYToWindow(DeviceIntPtr pDev, int x, int y) return pSprite->spriteTrace[pSprite->spriteTraceGood-1]; } +/** + * Ungrab a currently FocusIn grabbed device and grab the device on the + * given window. If the win given is the NoneWin, the device is ungrabbed if + * applicable and FALSE is returned. + * + * @returns TRUE if the device has been grabbed, or FALSE otherwise. + */ +BOOL +ActivateFocusInGrab(DeviceIntPtr dev, WindowPtr win) +{ + DeviceEvent event; + + if (dev->deviceGrab.grab && + dev->deviceGrab.fromPassiveGrab && + dev->deviceGrab.grab->type == XI_Enter) + { + if (dev->deviceGrab.grab->window == win || + IsParent(dev->deviceGrab.grab->window, win)) + return FALSE; + (*dev->deviceGrab.DeactivateGrab)(dev); + } + + if (win == NoneWin || win == PointerRootWin) + return FALSE; + + memset(&event, 0, sizeof(DeviceEvent)); + event.header = ET_Internal; + event.type = ET_FocusIn; + event.length = sizeof(DeviceEvent); + event.time = GetTimeInMillis(); + event.deviceid = dev->id; + event.sourceid = dev->id; + event.detail.button = 0; + return CheckPassiveGrabsOnWindow(win, dev, &event, FALSE); +} + +/** + * Ungrab a currently Enter grabbed device and grab the deice for the given + * window. + * + * @returns TRUE if the device has been grabbed, or FALSE otherwise. + */ +static BOOL +ActivateEnterGrab(DeviceIntPtr dev, WindowPtr win) +{ + DeviceEvent event; + + if (dev->deviceGrab.grab && + dev->deviceGrab.fromPassiveGrab && + dev->deviceGrab.grab->type == XI_Enter) + { + if (dev->deviceGrab.grab->window == win || + IsParent(dev->deviceGrab.grab->window, win)) + return FALSE; + (*dev->deviceGrab.DeactivateGrab)(dev); + } + + memset(&event, 0, sizeof(DeviceEvent)); + event.header = ET_Internal; + event.type = ET_Enter; + event.length = sizeof(DeviceEvent); + event.time = GetTimeInMillis(); + event.deviceid = dev->id; + event.sourceid = dev->id; + event.detail.button = 0; + return CheckPassiveGrabsOnWindow(win, dev, &event, FALSE); +} + /** * Update the sprite coordinates based on the event. Update the cursor * position, then update the event with the new coordinates that may have been @@ -2637,7 +2709,7 @@ XYToWindow(DeviceIntPtr pDev, int x, int y) Bool CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev) { - WindowPtr prevSpriteWin; + WindowPtr prevSpriteWin, newSpriteWin; SpritePtr pSprite = pDev->spriteInfo->sprite; CHECKEVENT(ev); @@ -2715,17 +2787,23 @@ CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev) ev->root_y = pSprite->hot.y; } - pSprite->win = XYToWindow(pDev, pSprite->hot.x, pSprite->hot.y); + newSpriteWin = XYToWindow(pDev, pSprite->hot.x, pSprite->hot.y); - if (pSprite->win != prevSpriteWin) + if (newSpriteWin != prevSpriteWin) { + if (!ev) + UpdateCurrentTimeIf(); + if (prevSpriteWin != NullWindow) { - if (!ev) - UpdateCurrentTimeIf(); - DoEnterLeaveEvents(pDev, prevSpriteWin, pSprite->win, - NotifyNormal); + if (!ActivateEnterGrab(pDev, newSpriteWin)) + DoEnterLeaveEvents(pDev, prevSpriteWin, + newSpriteWin, NotifyNormal); } - PostNewCursor(pDev); + /* set pSprite->win after ActivateEnterGrab, otherwise + sprite window == grab_window and no enter/leave events are + sent. */ + pSprite->win = newSpriteWin; + PostNewCursor(pDev); return FALSE; } return TRUE; @@ -3443,11 +3521,14 @@ CheckPassiveGrabsOnWindow( (*grabinfo->ActivateGrab)(device, grab, currentTime, TRUE); - FixUpEventFromWindow(device, xE, grab->window, None, TRUE); + if (xE) + { + FixUpEventFromWindow(device, xE, grab->window, None, TRUE); - TryClientEvents(rClient(grab), device, xE, count, - GetEventFilter(device, xE), - GetEventFilter(device, xE), grab); + TryClientEvents(rClient(grab), device, xE, count, + GetEventFilter(device, xE), + GetEventFilter(device, xE), grab); + } if (grabinfo->sync.state == FROZEN_NO_EVENT) { @@ -4344,9 +4425,14 @@ SetInputFocus( return Success; mode = (dev->deviceGrab.grab) ? NotifyWhileGrabbed : NotifyNormal; if (focus->win == FollowKeyboardWin) - DoFocusEvents(dev, keybd->focus->win, focusWin, mode); - else - DoFocusEvents(dev, focus->win, focusWin, mode); + { + if (!ActivateFocusInGrab(dev, focusWin)) + DoFocusEvents(dev, keybd->focus->win, focusWin, mode); + } else + { + if (!ActivateFocusInGrab(dev, focusWin)) + DoFocusEvents(dev, focus->win, focusWin, mode); + } focus->time = time; focus->revert = revertTo; if (focusID == FollowKeyboard) @@ -5327,12 +5413,14 @@ DeleteWindowFromAnyEvents(WindowPtr pWin, Bool freeResources) || clients[CLIENT_ID(parent->drawable.id)]->clientGone #endif ); - DoFocusEvents(keybd, pWin, parent, focusEventMode); + if (!ActivateFocusInGrab(keybd, parent)) + DoFocusEvents(keybd, pWin, parent, focusEventMode); focus->win = parent; focus->revert = RevertToNone; break; case RevertToPointerRoot: - DoFocusEvents(keybd, pWin, PointerRootWin, focusEventMode); + if (!ActivateFocusInGrab(keybd, PointerRootWin)) + DoFocusEvents(keybd, pWin, PointerRootWin, focusEventMode); focus->win = PointerRootWin; focus->traceGood = 0; break; diff --git a/include/dix.h b/include/dix.h index d4bec5f20..bfb036933 100644 --- a/include/dix.h +++ b/include/dix.h @@ -350,6 +350,10 @@ extern void ActivateKeyboardGrab( extern void DeactivateKeyboardGrab( DeviceIntPtr /* keybd */); +extern BOOL ActivateFocusInGrab( + DeviceIntPtr /* dev */, + WindowPtr /* win */); + extern void AllowSome( ClientPtr /* client */, TimeStamp /* time */, @@ -582,6 +586,7 @@ extern Bool IsKeyboardDevice(DeviceIntPtr dev); extern Bool IsPointerEvent(InternalEvent *event); extern Bool IsMaster(DeviceIntPtr dev); + /* * These are deprecated compatibility functions and will be removed soon! * Please use the noted replacements instead. diff --git a/include/exevents.h b/include/exevents.h index 95d08cc7f..5878413c2 100644 --- a/include/exevents.h +++ b/include/exevents.h @@ -107,6 +107,13 @@ extern int GrabKey( GrabType /* grabtype */, GrabMask* /* eventMask */); +extern int GrabWindow( + ClientPtr /* client */, + DeviceIntPtr /* dev */, + int /* type */, + GrabParameters* /* param */, + GrabMask* /* eventMask */); + extern int SelectForWindow( DeviceIntPtr /* dev */, WindowPtr /* pWin */,