Exemple #1
0
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;
}
Exemple #2
0
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;
}
Exemple #3
0
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;
}