Tk_Window GetGrabWindowForWindow( Tk_Window tkwin) { Tk_Window grabWin = TkMacOSXGetCapture(); if (!grabWin) { int grabState = TkGrabState((TkWindow*)tkwin); if (grabState != TK_GRAB_NONE && grabState != TK_GRAB_IN_TREE) { grabWin = (Tk_Window) (((TkWindow*)tkwin)->dispPtr->grabWinPtr); } } return grabWin; }
int TkPointerEvent( register XEvent *eventPtr, /* Pointer to the event. */ TkWindow *winPtr) /* Tk's information for window where event was * reported. */ { register TkWindow *winPtr2; TkDisplay *dispPtr = winPtr->dispPtr; unsigned int serial; int outsideGrabTree = 0; int ancestorOfGrab = 0; int appGrabbed = 0; /* Non-zero means event is being reported to * an application that is affected by the * grab. */ /* * Collect information about the grab (if any). */ switch (TkGrabState(winPtr)) { case TK_GRAB_IN_TREE: appGrabbed = 1; break; case TK_GRAB_ANCESTOR: appGrabbed = 1; outsideGrabTree = 1; ancestorOfGrab = 1; break; case TK_GRAB_EXCLUDED: appGrabbed = 1; outsideGrabTree = 1; break; } if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { /* * Keep track of what window the mouse is *really* over. Any events * that we generate have a special send_event value, which is detected * below and used to ignore the event for purposes of setting * serverWinPtr. */ if (eventPtr->xcrossing.send_event != GENERATED_GRAB_EVENT_MAGIC) { if ((eventPtr->type == LeaveNotify) && (winPtr->flags & TK_TOP_HIERARCHY)) { dispPtr->serverWinPtr = NULL; } else { dispPtr->serverWinPtr = winPtr; } } /* * When a grab is active, X continues to report enter and leave events * for windows outside the tree of the grab window: * 1. Detect these events and ignore them except for windows above the * grab window. * 2. Allow Enter and Leave events to pass through the windows above * the grab window, but never let them end up with the pointer *in* * one of those windows. */ if (dispPtr->grabWinPtr != NULL) { if (outsideGrabTree && appGrabbed) { if (!ancestorOfGrab) { return 0; } switch (eventPtr->xcrossing.detail) { case NotifyInferior: return 0; case NotifyAncestor: eventPtr->xcrossing.detail = NotifyVirtual; break; case NotifyNonlinear: eventPtr->xcrossing.detail = NotifyNonlinearVirtual; break; } } /* * Make buttons have the same grab-like behavior inside a grab as * they do outside a grab: do this by ignoring enter and leave * events except for the window in which the button was pressed. */ if ((dispPtr->buttonWinPtr != NULL) && (winPtr != dispPtr->buttonWinPtr)) { return 0; } } return 1; } if (!appGrabbed) { return 1; } if (eventPtr->type == MotionNotify) { /* * When grabs are active, X reports motion events relative to the * window under the pointer. Instead, it should report the events * relative to the window the button went down in, if there is a * button down. Otherwise, if the pointer window is outside the * subtree of the grab window, the events should be reported relative * to the grab window. Otherwise, the event should be reported to the * pointer window. */ winPtr2 = winPtr; if (dispPtr->buttonWinPtr != NULL) { winPtr2 = dispPtr->buttonWinPtr; } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) { winPtr2 = dispPtr->grabWinPtr; } if (winPtr2 != winPtr) { TkChangeEventWindow(eventPtr, winPtr2); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; } return 1; } /* * Process ButtonPress and ButtonRelease events: * 1. Keep track of whether a button is down and what window it went down * in. * 2. If the first button goes down outside the grab tree, pretend it went * down in the grab window. Note: it's important to redirect events to * the grab window like this in order to make things like menus work, * where button presses outside the grabbed menu need to be seen. An * application can always ignore the events if they occur outside its * window. * 3. If a button press or release occurs outside the window where the * first button was pressed, retarget the event so it's reported to the * window where the first button was pressed. * 4. If the last button is released in a window different than where the * first button was pressed, generate Enter/Leave events to move the * mouse from the button window to its current window. * 5. If the grab is set at a time when a button is already down, or if * the window where the button was pressed was deleted, then * dispPtr->buttonWinPtr will stay NULL. Just forget about the * auto-grab for the button press; events will go to whatever window * contains the pointer. If this window isn't in the grab tree then * redirect events to the grab window. * 6. When a button is pressed during a local grab, the X server sets a * grab of its own, since it doesn't even know about our local grab. * This causes enter and leave events no longer to be generated in the * same way as for global grabs. To eliminate this problem, set a * temporary global grab when the first button goes down and release it * when the last button comes up. */ if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) { winPtr2 = dispPtr->buttonWinPtr; if (winPtr2 == NULL) { if (outsideGrabTree) { winPtr2 = dispPtr->grabWinPtr; /* Note 5. */ } else { winPtr2 = winPtr; /* Note 5. */ } } if (eventPtr->type == ButtonPress) { if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) { if (outsideGrabTree) { TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; /* Note 2. */ } if (!(dispPtr->grabFlags & GRAB_GLOBAL)) { /* Note 6. */ serial = NextRequest(dispPtr->display); if (XGrabPointer(dispPtr->display, dispPtr->grabWinPtr->window, True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == 0) { EatGrabEvents(dispPtr, serial); if (XGrabKeyboard(dispPtr->display, winPtr->window, False, GrabModeAsync, GrabModeAsync, CurrentTime) == 0) { dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; } else { XUngrabPointer(dispPtr->display, CurrentTime); } } } dispPtr->buttonWinPtr = winPtr; return 1; } } else { if ((eventPtr->xbutton.state & ALL_BUTTONS) == buttonStates[eventPtr->xbutton.button - Button1]) { ReleaseButtonGrab(dispPtr); /* Note 4. */ } } if (winPtr2 != winPtr) { TkChangeEventWindow(eventPtr, winPtr2); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; /* Note 3. */ } } return 1; }
int TkFocusFilterEvent( TkWindow *winPtr, /* Window that focus event is directed to. */ XEvent *eventPtr) /* FocusIn, FocusOut, Enter, or Leave * event. */ { /* * Design notes: the window manager and X server work together to transfer * the focus among top-level windows. This function takes care of * transferring the focus from a top-level or wrapper window to the actual * window within that top-level that has the focus. We do this by * synthesizing X events to move the focus around. None of the FocusIn and * FocusOut events generated by X are ever used outside of this function; * only the synthesized events get through to the rest of the application. * At one point (e.g. Tk4.0b1) Tk used to call X to move the focus from a * top-level to one of its descendants, then just pass through the events * generated by X. This approach didn't work very well, for a variety of * reasons. For example, if X generates the events they go at the back of * the event queue, which could cause problems if other things have * already happened, such as moving the focus to yet another window. */ ToplevelFocusInfo *tlFocusPtr; DisplayFocusInfo *displayFocusPtr; TkDisplay *dispPtr = winPtr->dispPtr; TkWindow *newFocusPtr; int retValue, delta; /* * If this was a generated event, just turn off the generated flag and * pass the event through to Tk bindings. */ if (eventPtr->xfocus.send_event == GENERATED_FOCUS_EVENT_MAGIC) { eventPtr->xfocus.send_event = 0; return 1; } /* * Check for special events generated by embedded applications to request * the input focus. If this is one of those events, make the change in * focus and return without any additional processing of the event (note: * the "detail" field of the event indicates whether to claim the focus * even if we don't already have it). */ if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) && (eventPtr->type == FocusIn)) { TkSetFocusWin(winPtr, eventPtr->xfocus.detail); return 0; } /* * This was not a generated event. We'll return 1 (so that the event will * be processed) if it's an Enter or Leave event, and 0 (so that the event * won't be processed) if it's a FocusIn or FocusOut event. */ retValue = 0; displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); if (eventPtr->type == FocusIn) { /* * Skip FocusIn events that cause confusion * NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur on * windows in between the origin and destination of the focus * change. For FocusIn we may see this when focus goes into an * embedded child. We don't care about this, although we may end * up getting a NotifyPointer later. * NotifyInferior - focus is coming to us from an embedded child. When * focus is on an embeded focus, we still think we have the * focus, too, so this message doesn't change our state. * NotifyPointerRoot - should never happen because this is sent to the * root window. * * Interesting FocusIn events are * NotifyAncestor - focus is coming from our parent, probably the root. * NotifyNonlinear - focus is coming from a different branch, probably * another toplevel. * NotifyPointer - implicit focus because of the mouse position. This * is only interesting on toplevels, when it means that the focus * has been set to the root window but the mouse is over this * toplevel. We take the focus implicitly (probably no window * manager) */ if ((eventPtr->xfocus.detail == NotifyVirtual) || (eventPtr->xfocus.detail == NotifyNonlinearVirtual) || (eventPtr->xfocus.detail == NotifyPointerRoot) || (eventPtr->xfocus.detail == NotifyInferior)) { return retValue; } } else if (eventPtr->type == FocusOut) { /* * Skip FocusOut events that cause confusion. * NotifyPointer - the pointer is in us or a child, and we are losing * focus because of an XSetInputFocus. Other focus events will * set our state properly. * NotifyPointerRoot - should never happen because this is sent to the * root window. * NotifyInferior - focus leaving us for an embedded child. We retain * a notion of focus when an embedded child has focus. * * Interesting events are: * NotifyAncestor - focus is going to root. * NotifyNonlinear - focus is going to another branch, probably * another toplevel. * NotifyVirtual, NotifyNonlinearVirtual - focus is passing through, * and we need to make sure we track this. */ if ((eventPtr->xfocus.detail == NotifyPointer) || (eventPtr->xfocus.detail == NotifyPointerRoot) || (eventPtr->xfocus.detail == NotifyInferior)) { return retValue; } } else { retValue = 1; if (eventPtr->xcrossing.detail == NotifyInferior) { return retValue; } } /* * If winPtr isn't a top-level window than just ignore the event. */ winPtr = TkWmFocusToplevel(winPtr); if (winPtr == NULL) { return retValue; } /* * If there is a grab in effect and this window is outside the grabbed * tree, then ignore the event. */ if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) { return retValue; } /* * It is possible that there were outstanding FocusIn and FocusOut events * on their way to us at the time the focus was changed internally with * the "focus" command. If so, these events could potentially cause us to * lose the focus (switch it to the window of the last FocusIn event) even * though the focus change occurred after those events. The following code * detects this and ignores the stale events. * * Note: the focusSerial is only generated by TkpChangeFocus, whereas in * Tk 4.2 there was always a nop marker generated. */ delta = eventPtr->xfocus.serial - displayFocusPtr->focusSerial; if (delta < 0) { return retValue; } /* * Find the ToplevelFocusInfo structure for the window, and make a new one * if there isn't one already. */ for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == winPtr) { break; } } if (tlFocusPtr == NULL) { tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); tlFocusPtr->topLevelPtr = tlFocusPtr->focusWinPtr = winPtr; tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; winPtr->mainPtr->tlFocusPtr = tlFocusPtr; } newFocusPtr = tlFocusPtr->focusWinPtr; /* * Ignore event if newFocus window is already dead! */ if (newFocusPtr->flags & TK_ALREADY_DEAD) { return retValue; } if (eventPtr->type == FocusIn) { GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); displayFocusPtr->focusWinPtr = newFocusPtr; dispPtr->focusPtr = newFocusPtr; /* * NotifyPointer gets set when the focus has been set to the root * window but we have the pointer. We'll treat this like an implicit * focus in event so that upon Leave events we release focus. */ if (!(winPtr->flags & TK_EMBEDDED)) { if (eventPtr->xfocus.detail == NotifyPointer) { dispPtr->implicitWinPtr = winPtr; } else { dispPtr->implicitWinPtr = NULL; } } } else if (eventPtr->type == FocusOut) { GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); /* * Reset dispPtr->focusPtr, but only if it currently is the same as * this application's focusWinPtr: this check is needed to handle * embedded applications in the same process. */ if (dispPtr->focusPtr == displayFocusPtr->focusWinPtr) { dispPtr->focusPtr = NULL; } displayFocusPtr->focusWinPtr = NULL; } else if (eventPtr->type == EnterNotify) { /* * If there is no window manager, or if the window manager isn't * moving the focus around (e.g. the disgusting "NoTitleFocus" option * has been selected in twm), then we won't get FocusIn or FocusOut * events. Instead, the "focus" field will be set in an Enter event to * indicate that we've already got the focus when the mouse enters the * window (even though we didn't get a FocusIn event). Watch for this * and grab the focus when it happens. Note: if this is an embedded * application then don't accept the focus implicitly like this; the * container application will give us the focus explicitly if it wants * us to have it. */ if (eventPtr->xcrossing.focus && (displayFocusPtr->focusWinPtr == NULL) && !(winPtr->flags & TK_EMBEDDED)) { DEBUG(dispPtr, ("Focussed implicitly on %s\n", newFocusPtr->pathName)); GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); displayFocusPtr->focusWinPtr = newFocusPtr; dispPtr->implicitWinPtr = winPtr; dispPtr->focusPtr = newFocusPtr; } } else if (eventPtr->type == LeaveNotify) { /* * If the pointer just left a window for which we automatically * claimed the focus on enter, move the focus back to the root window, * where it was before we claimed it above. Note: * dispPtr->implicitWinPtr may not be the same as * displayFocusPtr->focusWinPtr (e.g. because the "focus" command was * used to redirect the focus after it arrived at * dispPtr->implicitWinPtr)!! In addition, we generate events because * the window manager won't give us a FocusOut event when we focus on * the root. */ if ((dispPtr->implicitWinPtr != NULL) && !(winPtr->flags & TK_EMBEDDED)) { DEBUG(dispPtr, ("Defocussed implicit Async\n")); GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, CurrentTime); displayFocusPtr->focusWinPtr = NULL; dispPtr->implicitWinPtr = NULL; } } return retValue; }