1217 lines
36 KiB
C
1217 lines
36 KiB
C
/*
|
||
* tkFocus.c --
|
||
*
|
||
* This file contains functions that manage the input focus for Tk.
|
||
*
|
||
* Copyright (c) 1990-1994 The Regents of the University of California.
|
||
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution of
|
||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*/
|
||
|
||
#include "tkInt.h"
|
||
|
||
/*
|
||
* For each top-level window that has ever received the focus, there is a
|
||
* record of the following type:
|
||
*/
|
||
|
||
typedef struct TkToplevelFocusInfo {
|
||
TkWindow *topLevelPtr; /* Information about top-level window. */
|
||
TkWindow *focusWinPtr; /* The next time the focus comes to this
|
||
* top-level, it will be given to this
|
||
* window. */
|
||
struct TkToplevelFocusInfo *nextPtr;
|
||
/* Next in list of all toplevel focus records
|
||
* for a given application. */
|
||
} ToplevelFocusInfo;
|
||
|
||
/*
|
||
* One of the following structures exists for each display used by each
|
||
* application. These are linked together from the TkMainInfo structure.
|
||
* These structures are needed because it isn't sufficient to store a single
|
||
* piece of focus information in each display or in each application: we need
|
||
* the cross-product. There needs to be separate information for each display,
|
||
* because it's possible to have multiple focus windows active simultaneously
|
||
* on different displays. There also needs to be separate information for each
|
||
* application, because of embedding: if an embedded application has the
|
||
* focus, its container application also has the focus. Thus we keep a list of
|
||
* structures for each application: the same display can appear in structures
|
||
* for several applications at once.
|
||
*/
|
||
|
||
typedef struct TkDisplayFocusInfo {
|
||
TkDisplay *dispPtr; /* Display that this information pertains
|
||
* to. */
|
||
struct TkWindow *focusWinPtr;
|
||
/* Window that currently has the focus for
|
||
* this application on this display, or NULL
|
||
* if none. */
|
||
struct TkWindow *focusOnMapPtr;
|
||
/* This points to a toplevel window that is
|
||
* supposed to receive the X input focus as
|
||
* soon as it is mapped (needed to handle the
|
||
* fact that X won't allow the focus on an
|
||
* unmapped window). NULL means no delayed
|
||
* focus op in progress for this display. */
|
||
int forceFocus; /* Associated with focusOnMapPtr: non-zero
|
||
* means claim the focus even if some other
|
||
* application currently has it. */
|
||
unsigned long focusSerial; /* Serial number of last request this
|
||
* application made to change the focus on
|
||
* this display. Used to identify stale focus
|
||
* notifications coming from the X server. */
|
||
struct TkDisplayFocusInfo *nextPtr;
|
||
/* Next in list of all display focus records
|
||
* for a given application. */
|
||
} DisplayFocusInfo;
|
||
|
||
/*
|
||
* Debugging support...
|
||
*/
|
||
|
||
#define DEBUG(dispPtr, arguments) \
|
||
if ((dispPtr)->focusDebug) { \
|
||
printf arguments; \
|
||
}
|
||
|
||
/*
|
||
* Forward declarations for functions defined in this file:
|
||
*/
|
||
|
||
static DisplayFocusInfo*FindDisplayFocusInfo(TkMainInfo *mainPtr,
|
||
TkDisplay *dispPtr);
|
||
static void FocusMapProc(ClientData clientData, XEvent *eventPtr);
|
||
static void GenerateFocusEvents(TkWindow *sourcePtr,
|
||
TkWindow *destPtr);
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* Tk_FocusObjCmd --
|
||
*
|
||
* This function is invoked to process the "focus" Tcl command. See the
|
||
* user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
Tk_FocusObjCmd(
|
||
ClientData clientData, /* Main window associated with interpreter. */
|
||
Tcl_Interp *interp, /* Current interpreter. */
|
||
int objc, /* Number of arguments. */
|
||
Tcl_Obj *const objv[]) /* Argument objects. */
|
||
{
|
||
static const char *const focusOptions[] = {
|
||
"-displayof", "-force", "-lastfor", NULL
|
||
};
|
||
Tk_Window tkwin = clientData;
|
||
TkWindow *winPtr = clientData;
|
||
TkWindow *newPtr, *topLevelPtr;
|
||
ToplevelFocusInfo *tlFocusPtr;
|
||
const char *windowName;
|
||
int index;
|
||
|
||
/*
|
||
* If invoked with no arguments, just return the current focus window.
|
||
*/
|
||
|
||
if (objc == 1) {
|
||
Tk_Window focusWin = (Tk_Window) TkGetFocusWin(winPtr);
|
||
|
||
if (focusWin != NULL) {
|
||
Tcl_SetObjResult(interp, TkNewWindowObj(focusWin));
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
* If invoked with a single argument beginning with "." then focus on that
|
||
* window.
|
||
*/
|
||
|
||
if (objc == 2) {
|
||
windowName = Tcl_GetString(objv[1]);
|
||
|
||
/*
|
||
* The empty string case exists for backwards compatibility.
|
||
*/
|
||
|
||
if (windowName[0] == '\0') {
|
||
return TCL_OK;
|
||
}
|
||
if (windowName[0] == '.') {
|
||
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
|
||
if (newPtr == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
TkSetFocusWin(newPtr, 0);
|
||
return TCL_OK;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* We have a subcommand to parse and act upon.
|
||
*/
|
||
|
||
if (Tcl_GetIndexFromObjStruct(interp, objv[1], focusOptions,
|
||
sizeof(char *), "option", 0, &index) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
if (objc != 3) {
|
||
Tcl_WrongNumArgs(interp, 2, objv, "window");
|
||
return TCL_ERROR;
|
||
}
|
||
switch (index) {
|
||
case 0: /* -displayof */
|
||
windowName = Tcl_GetString(objv[2]);
|
||
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
|
||
if (newPtr == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
newPtr = TkGetFocusWin(newPtr);
|
||
if (newPtr != NULL) {
|
||
Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) newPtr));
|
||
}
|
||
break;
|
||
case 1: /* -force */
|
||
windowName = Tcl_GetString(objv[2]);
|
||
|
||
/*
|
||
* The empty string case exists for backwards compatibility.
|
||
*/
|
||
|
||
if (windowName[0] == '\0') {
|
||
return TCL_OK;
|
||
}
|
||
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
|
||
if (newPtr == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
TkSetFocusWin(newPtr, 1);
|
||
break;
|
||
case 2: /* -lastfor */
|
||
windowName = Tcl_GetString(objv[2]);
|
||
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
|
||
if (newPtr == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
for (topLevelPtr = newPtr; topLevelPtr != NULL;
|
||
topLevelPtr = topLevelPtr->parentPtr) {
|
||
if (!(topLevelPtr->flags & TK_TOP_HIERARCHY)) {
|
||
continue;
|
||
}
|
||
for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
|
||
tlFocusPtr = tlFocusPtr->nextPtr) {
|
||
if (tlFocusPtr->topLevelPtr == topLevelPtr) {
|
||
Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window)
|
||
tlFocusPtr->focusWinPtr));
|
||
return TCL_OK;
|
||
}
|
||
}
|
||
Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) topLevelPtr));
|
||
return TCL_OK;
|
||
}
|
||
break;
|
||
default:
|
||
Tcl_Panic("bad const entries to focusOptions in focus command");
|
||
}
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkFocusFilterEvent --
|
||
*
|
||
* This function is invoked by Tk_HandleEvent when it encounters a
|
||
* FocusIn, FocusOut, Enter, or Leave event.
|
||
*
|
||
* Results:
|
||
* A return value of 1 means that Tk_HandleEvent should process the event
|
||
* normally (i.e. event handlers should be invoked). A return value of 0
|
||
* means that this event should be ignored.
|
||
*
|
||
* Side effects:
|
||
* Additional events may be generated, and the focus may switch.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
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) == GENERATED_FOCUS_EVENT_MAGIC) {
|
||
eventPtr->xfocus.send_event &= ~GENERATED_FOCUS_EVENT_MAGIC;
|
||
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 = 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;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkSetFocusWin --
|
||
*
|
||
* This function is invoked to change the focus window for a given
|
||
* display in a given application.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Event handlers may be invoked to process the change of
|
||
* focus.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkSetFocusWin(
|
||
TkWindow *winPtr, /* Window that is to be the new focus for its
|
||
* display and application. */
|
||
int force) /* If non-zero, set the X focus to this window
|
||
* even if the application doesn't currently
|
||
* have the X focus. */
|
||
{
|
||
ToplevelFocusInfo *tlFocusPtr;
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
TkWindow *topLevelPtr;
|
||
int allMapped, serial;
|
||
|
||
/*
|
||
* Don't set focus if window is already dead. [Bug 3574708]
|
||
*/
|
||
|
||
if (winPtr->flags & TK_ALREADY_DEAD) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Get the current focus window with the same display and application
|
||
* as winPtr.
|
||
*/
|
||
|
||
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
|
||
|
||
/*
|
||
* Do nothing if the window already has focus and force is not set. If
|
||
* force is set, we need to grab the focus, since under Windows or macOS
|
||
* this may involve taking control away from another application.
|
||
*/
|
||
|
||
if (winPtr == displayFocusPtr->focusWinPtr && !force) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Find the toplevel window for winPtr, then find (or create) a record
|
||
* for the toplevel. Also see whether winPtr and all its ancestors are
|
||
* mapped.
|
||
*/
|
||
|
||
allMapped = 1;
|
||
for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) {
|
||
if (topLevelPtr == NULL) {
|
||
|
||
/*
|
||
* The window is being deleted. No point in worrying about giving
|
||
* it the focus.
|
||
*/
|
||
|
||
return;
|
||
}
|
||
if (!(topLevelPtr->flags & TK_MAPPED)) {
|
||
allMapped = 0;
|
||
}
|
||
if (topLevelPtr->flags & TK_TOP_HIERARCHY) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* If any ancestor of the new focus window isn't mapped, then we can't set
|
||
* focus for it (X will generate an error, for example). Instead, create
|
||
* an event handler that will set the focus to this window once it gets
|
||
* mapped. At the same time, delete any old handler that might be around;
|
||
* it's no longer relevant.
|
||
*/
|
||
|
||
if (displayFocusPtr->focusOnMapPtr != NULL) {
|
||
Tk_DeleteEventHandler((Tk_Window) displayFocusPtr->focusOnMapPtr,
|
||
StructureNotifyMask, FocusMapProc,
|
||
displayFocusPtr->focusOnMapPtr);
|
||
displayFocusPtr->focusOnMapPtr = NULL;
|
||
}
|
||
if (!allMapped) {
|
||
Tk_CreateEventHandler((Tk_Window) winPtr, VisibilityChangeMask,
|
||
FocusMapProc, winPtr);
|
||
displayFocusPtr->focusOnMapPtr = winPtr;
|
||
displayFocusPtr->forceFocus = force;
|
||
return;
|
||
}
|
||
|
||
for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
|
||
tlFocusPtr = tlFocusPtr->nextPtr) {
|
||
if (tlFocusPtr->topLevelPtr == topLevelPtr) {
|
||
break;
|
||
}
|
||
}
|
||
if (tlFocusPtr == NULL) {
|
||
tlFocusPtr = ckalloc(sizeof(ToplevelFocusInfo));
|
||
tlFocusPtr->topLevelPtr = topLevelPtr;
|
||
tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr;
|
||
winPtr->mainPtr->tlFocusPtr = tlFocusPtr;
|
||
}
|
||
tlFocusPtr->focusWinPtr = winPtr;
|
||
|
||
if (topLevelPtr->flags & TK_EMBEDDED &&
|
||
(displayFocusPtr->focusWinPtr == NULL)) {
|
||
|
||
/*
|
||
* We are assigning focus to an embedded toplevel. The platform
|
||
* specific function TkpClaimFocus needs to handle the job of
|
||
* assigning focus to the container, since we have no way to find the
|
||
* contaiuner.
|
||
*/
|
||
|
||
TkpClaimFocus(topLevelPtr, force);
|
||
} else if ((displayFocusPtr->focusWinPtr != NULL) || force) {
|
||
|
||
/*
|
||
* If we are forcing removal of focus from a container hosting a
|
||
* toplevel from a different application, clear the focus in that
|
||
* application.
|
||
*/
|
||
|
||
if (force) {
|
||
TkWindow *focusPtr = winPtr->dispPtr->focusPtr;
|
||
if (focusPtr && focusPtr->mainPtr != winPtr->mainPtr) {
|
||
DisplayFocusInfo *displayFocusPtr2 = FindDisplayFocusInfo(
|
||
focusPtr->mainPtr, focusPtr->dispPtr);
|
||
displayFocusPtr2->focusWinPtr = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Call the platform specific function TkpChangeFocus to move the
|
||
* window manager's focus to a new toplevel.
|
||
*/
|
||
|
||
serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force);
|
||
if (serial != 0) {
|
||
displayFocusPtr->focusSerial = serial;
|
||
}
|
||
GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr);
|
||
displayFocusPtr->focusWinPtr = winPtr;
|
||
winPtr->dispPtr->focusPtr = winPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkGetFocusWin --
|
||
*
|
||
* Given a window, this function returns the current focus window for its
|
||
* application and display.
|
||
*
|
||
* Results:
|
||
* The return value is a pointer to the window that currently has the
|
||
* input focus for the specified application and display, or NULL if
|
||
* none.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
TkWindow *
|
||
TkGetFocusWin(
|
||
TkWindow *winPtr) /* Window that selects an application and a
|
||
* display. */
|
||
{
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
|
||
if (winPtr == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
|
||
return displayFocusPtr->focusWinPtr;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFocusKeyEvent --
|
||
*
|
||
* Given a window and a key press or release event that arrived for the
|
||
* window, use information about the keyboard focus to compute which
|
||
* window should really get the event. In addition, update the event to
|
||
* refer to its new window.
|
||
*
|
||
* Results:
|
||
* The return value is a pointer to the window that has the input focus
|
||
* in winPtr's application, or NULL if winPtr's application doesn't have
|
||
* the input focus. If a non-NULL value is returned, eventPtr will be
|
||
* updated to refer properly to the focus window.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
TkWindow *
|
||
TkFocusKeyEvent(
|
||
TkWindow *winPtr, /* Window that selects an application and a
|
||
* display. */
|
||
XEvent *eventPtr) /* X event to redirect (should be KeyPress or
|
||
* KeyRelease). */
|
||
{
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
TkWindow *focusWinPtr;
|
||
int focusX, focusY;
|
||
|
||
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
|
||
focusWinPtr = displayFocusPtr->focusWinPtr;
|
||
|
||
/*
|
||
* The code below is a debugging aid to make sure that dispPtr->focusPtr
|
||
* is kept properly in sync with the "truth", which is the value in
|
||
* displayFocusPtr->focusWinPtr.
|
||
*/
|
||
|
||
#ifdef TCL_MEM_DEBUG
|
||
if (focusWinPtr != winPtr->dispPtr->focusPtr) {
|
||
printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n");
|
||
printf("expected %s, got %s\n",
|
||
(focusWinPtr != NULL) ? focusWinPtr->pathName : "??",
|
||
(winPtr->dispPtr->focusPtr != NULL) ?
|
||
winPtr->dispPtr->focusPtr->pathName : "??");
|
||
}
|
||
#endif
|
||
|
||
if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
|
||
/*
|
||
* Map the x and y coordinates to make sense in the context of the
|
||
* focus window, if possible (make both -1 if the map-from and map-to
|
||
* windows don't share the same screen).
|
||
*/
|
||
|
||
if ((focusWinPtr->display != winPtr->display)
|
||
|| (focusWinPtr->screenNum != winPtr->screenNum)) {
|
||
eventPtr->xkey.x = -1;
|
||
eventPtr->xkey.y = -1;
|
||
} else {
|
||
Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY);
|
||
eventPtr->xkey.x = eventPtr->xkey.x_root - focusX;
|
||
eventPtr->xkey.y = eventPtr->xkey.y_root - focusY;
|
||
}
|
||
eventPtr->xkey.window = focusWinPtr->window;
|
||
return focusWinPtr;
|
||
}
|
||
|
||
/*
|
||
* The event doesn't belong to us. Perhaps, due to embedding, it really
|
||
* belongs to someone else. Give the embedding code a chance to redirect
|
||
* the event.
|
||
*/
|
||
|
||
TkpRedirectKeyEvent(winPtr, eventPtr);
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFocusDeadWindow --
|
||
*
|
||
* This function is invoked when it is determined that a window is dead.
|
||
* It cleans up focus-related information about the window.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Various things get cleaned up and recycled.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkFocusDeadWindow(
|
||
register TkWindow *winPtr) /* Information about the window that is being
|
||
* deleted. */
|
||
{
|
||
ToplevelFocusInfo *tlFocusPtr, *prevPtr;
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
TkDisplay *dispPtr = winPtr->dispPtr;
|
||
|
||
/*
|
||
* Certain special windows like those used for send and clipboard have no
|
||
* mainPtr.
|
||
*/
|
||
|
||
if (winPtr->mainPtr == NULL) {
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Search for focus records that refer to this window either as the
|
||
* top-level window or the current focus window.
|
||
*/
|
||
|
||
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
|
||
for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr;
|
||
tlFocusPtr != NULL;
|
||
prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) {
|
||
if (winPtr == tlFocusPtr->topLevelPtr) {
|
||
/*
|
||
* The top-level window is the one being deleted: free the focus
|
||
* record and release the focus back to PointerRoot if we acquired
|
||
* it implicitly.
|
||
*/
|
||
|
||
if (dispPtr->implicitWinPtr == winPtr) {
|
||
DEBUG(dispPtr, ("releasing focus to root after %s died\n",
|
||
tlFocusPtr->topLevelPtr->pathName));
|
||
dispPtr->implicitWinPtr = NULL;
|
||
displayFocusPtr->focusWinPtr = NULL;
|
||
dispPtr->focusPtr = NULL;
|
||
}
|
||
if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) {
|
||
displayFocusPtr->focusWinPtr = NULL;
|
||
dispPtr->focusPtr = NULL;
|
||
}
|
||
if (prevPtr == NULL) {
|
||
winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr;
|
||
} else {
|
||
prevPtr->nextPtr = tlFocusPtr->nextPtr;
|
||
}
|
||
ckfree(tlFocusPtr);
|
||
break;
|
||
} else if (winPtr == tlFocusPtr->focusWinPtr) {
|
||
/*
|
||
* The deleted window had the focus for its top-level: move the
|
||
* focus to the top-level itself.
|
||
*/
|
||
|
||
tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
|
||
if ((displayFocusPtr->focusWinPtr == winPtr)
|
||
&& !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
|
||
DEBUG(dispPtr, ("forwarding focus to %s after %s died\n",
|
||
tlFocusPtr->topLevelPtr->pathName, winPtr->pathName));
|
||
GenerateFocusEvents(displayFocusPtr->focusWinPtr,
|
||
tlFocusPtr->topLevelPtr);
|
||
displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
|
||
dispPtr->focusPtr = tlFocusPtr->topLevelPtr;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Occasionally, things can become unsynchronized. Move them back into
|
||
* synch now. [Bug 2496114]
|
||
*/
|
||
|
||
if (displayFocusPtr->focusWinPtr == winPtr) {
|
||
DEBUG(dispPtr, ("focus cleared after %s died\n", winPtr->pathName));
|
||
displayFocusPtr->focusWinPtr = NULL;
|
||
}
|
||
|
||
if (displayFocusPtr->focusOnMapPtr == winPtr) {
|
||
displayFocusPtr->focusOnMapPtr = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* GenerateFocusEvents --
|
||
*
|
||
* This function is called to create FocusIn and FocusOut events to move
|
||
* the input focus from one window to another.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* FocusIn and FocusOut events are generated.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
GenerateFocusEvents(
|
||
TkWindow *sourcePtr, /* Window that used to have the focus (may be
|
||
* NULL). */
|
||
TkWindow *destPtr) /* New window to have the focus (may be
|
||
* NULL). */
|
||
{
|
||
XEvent event;
|
||
TkWindow *winPtr;
|
||
|
||
winPtr = sourcePtr;
|
||
if (winPtr == NULL) {
|
||
winPtr = destPtr;
|
||
if (winPtr == NULL) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
|
||
event.xfocus.send_event = GENERATED_FOCUS_EVENT_MAGIC;
|
||
event.xfocus.display = winPtr->display;
|
||
event.xfocus.mode = NotifyNormal;
|
||
TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
|
||
TCL_QUEUE_MARK);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* FocusMapProc --
|
||
*
|
||
* This function is called as an event handler for VisibilityNotify
|
||
* events, if a window receives the focus at a time when its toplevel
|
||
* isn't mapped. The function is needed because X won't allow the focus
|
||
* to be set to an unmapped window; we detect when the toplevel is mapped
|
||
* and set the focus to it then.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* If this is a map event, the focus gets set to the toplevel given by
|
||
* clientData.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
FocusMapProc(
|
||
ClientData clientData, /* Toplevel window. */
|
||
XEvent *eventPtr) /* Information about event. */
|
||
{
|
||
TkWindow *winPtr = clientData;
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
|
||
if (eventPtr->type == VisibilityNotify) {
|
||
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr,
|
||
winPtr->dispPtr);
|
||
DEBUG(winPtr->dispPtr, ("auto-focussing on %s, force %d\n",
|
||
winPtr->pathName, displayFocusPtr->forceFocus));
|
||
Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask,
|
||
FocusMapProc, clientData);
|
||
displayFocusPtr->focusOnMapPtr = NULL;
|
||
TkSetFocusWin(winPtr, displayFocusPtr->forceFocus);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* FindDisplayFocusInfo --
|
||
*
|
||
* Given an application and a display, this function locate the focus
|
||
* record for that combination. If no such record exists, it creates a
|
||
* new record and initializes it.
|
||
*
|
||
* Results:
|
||
* The return value is a pointer to the record.
|
||
*
|
||
* Side effects:
|
||
* A new record will be allocated if there wasn't one already.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static DisplayFocusInfo *
|
||
FindDisplayFocusInfo(
|
||
TkMainInfo *mainPtr, /* Record that identifies a particular
|
||
* application. */
|
||
TkDisplay *dispPtr) /* Display whose focus information is
|
||
* needed. */
|
||
{
|
||
DisplayFocusInfo *displayFocusPtr;
|
||
|
||
for (displayFocusPtr = mainPtr->displayFocusPtr;
|
||
displayFocusPtr != NULL;
|
||
displayFocusPtr = displayFocusPtr->nextPtr) {
|
||
if (displayFocusPtr->dispPtr == dispPtr) {
|
||
return displayFocusPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* The record doesn't exist yet. Make a new one.
|
||
*/
|
||
|
||
displayFocusPtr = ckalloc(sizeof(DisplayFocusInfo));
|
||
displayFocusPtr->dispPtr = dispPtr;
|
||
displayFocusPtr->focusWinPtr = NULL;
|
||
displayFocusPtr->focusOnMapPtr = NULL;
|
||
displayFocusPtr->forceFocus = 0;
|
||
displayFocusPtr->focusSerial = 0;
|
||
displayFocusPtr->nextPtr = mainPtr->displayFocusPtr;
|
||
mainPtr->displayFocusPtr = displayFocusPtr;
|
||
return displayFocusPtr;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFocusFree --
|
||
*
|
||
* Free resources associated with maintaining the focus.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* This mainPtr should no long access focus information.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkFocusFree(
|
||
TkMainInfo *mainPtr) /* Record that identifies a particular
|
||
* application. */
|
||
{
|
||
while (mainPtr->displayFocusPtr != NULL) {
|
||
DisplayFocusInfo *displayFocusPtr = mainPtr->displayFocusPtr;
|
||
|
||
mainPtr->displayFocusPtr = mainPtr->displayFocusPtr->nextPtr;
|
||
ckfree(displayFocusPtr);
|
||
}
|
||
while (mainPtr->tlFocusPtr != NULL) {
|
||
ToplevelFocusInfo *tlFocusPtr = mainPtr->tlFocusPtr;
|
||
|
||
mainPtr->tlFocusPtr = mainPtr->tlFocusPtr->nextPtr;
|
||
ckfree(tlFocusPtr);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFocusSplit --
|
||
*
|
||
* Adjust focus window for a newly managed toplevel, thus splitting the
|
||
* toplevel into two toplevels.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* A new record is allocated for the new toplevel window.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkFocusSplit(
|
||
TkWindow *winPtr) /* Window is the new toplevel. Any focus point
|
||
* at or below window must be moved to this
|
||
* new toplevel. */
|
||
{
|
||
ToplevelFocusInfo *tlFocusPtr;
|
||
TkWindow *topLevelPtr, *subWinPtr;
|
||
|
||
FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
|
||
|
||
/*
|
||
* Find the top-level window for winPtr, then find (or create) a record
|
||
* for the top-level. Also see whether winPtr and all its ancestors are
|
||
* mapped.
|
||
*/
|
||
|
||
for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) {
|
||
if (topLevelPtr == NULL) {
|
||
/*
|
||
* The window is being deleted. No point in worrying about giving
|
||
* it the focus.
|
||
*/
|
||
|
||
return;
|
||
}
|
||
if (topLevelPtr->flags & TK_TOP_HIERARCHY) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Search all focus records to find child windows of winPtr.
|
||
*/
|
||
|
||
for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
|
||
tlFocusPtr = tlFocusPtr->nextPtr) {
|
||
if (tlFocusPtr->topLevelPtr == topLevelPtr) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (tlFocusPtr == NULL) {
|
||
/*
|
||
* No focus record for this toplevel, nothing to do.
|
||
*/
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* See if current focusWin is child of the new toplevel.
|
||
*/
|
||
|
||
for (subWinPtr = tlFocusPtr->focusWinPtr;
|
||
subWinPtr && subWinPtr != winPtr && subWinPtr != topLevelPtr;
|
||
subWinPtr = subWinPtr->parentPtr) {
|
||
/* EMPTY */
|
||
}
|
||
|
||
if (subWinPtr == winPtr) {
|
||
/*
|
||
* Move focus to new toplevel.
|
||
*/
|
||
|
||
ToplevelFocusInfo *newTlFocusPtr = ckalloc(sizeof(ToplevelFocusInfo));
|
||
|
||
newTlFocusPtr->topLevelPtr = winPtr;
|
||
newTlFocusPtr->focusWinPtr = tlFocusPtr->focusWinPtr;
|
||
newTlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr;
|
||
winPtr->mainPtr->tlFocusPtr = newTlFocusPtr;
|
||
|
||
/*
|
||
* Move old toplevel's focus to the toplevel itself.
|
||
*/
|
||
|
||
tlFocusPtr->focusWinPtr = topLevelPtr;
|
||
}
|
||
|
||
/*
|
||
* If it's not, then let focus progress naturally.
|
||
*/
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkFocusJoin --
|
||
*
|
||
* Remove the focus record for this window that is nolonger managed
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* A tlFocusPtr record is removed
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkFocusJoin(
|
||
TkWindow *winPtr) /* Window is no longer a toplevel. */
|
||
{
|
||
ToplevelFocusInfo *tlFocusPtr, *tmpPtr;
|
||
|
||
/*
|
||
* Remove old toplevel record
|
||
*/
|
||
|
||
if (winPtr && winPtr->mainPtr && winPtr->mainPtr->tlFocusPtr
|
||
&& winPtr->mainPtr->tlFocusPtr->topLevelPtr == winPtr) {
|
||
tmpPtr = winPtr->mainPtr->tlFocusPtr;
|
||
winPtr->mainPtr->tlFocusPtr = tmpPtr->nextPtr;
|
||
ckfree(tmpPtr);
|
||
} else if (winPtr && winPtr->mainPtr) {
|
||
for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
|
||
tlFocusPtr = tlFocusPtr->nextPtr) {
|
||
if (tlFocusPtr->nextPtr &&
|
||
tlFocusPtr->nextPtr->topLevelPtr == winPtr) {
|
||
tmpPtr = tlFocusPtr->nextPtr;
|
||
tlFocusPtr->nextPtr = tmpPtr->nextPtr;
|
||
ckfree(tmpPtr);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode: c
|
||
* c-basic-offset: 4
|
||
* fill-column: 78
|
||
* End:
|
||
*/
|