|
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkfocus.c,v 1.1.1.1 2001/06/13 05:00:25 dtashley Exp $ */ |
|
|
|
|
|
/* |
|
|
* tkFocus.c -- |
|
|
* |
|
|
* This file contains procedures 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. |
|
|
* |
|
|
* RCS: @(#) $Id: tkfocus.c,v 1.1.1.1 2001/06/13 05:00:25 dtashley Exp $ |
|
|
*/ |
|
|
|
|
|
#include "tkInt.h" |
|
|
#include "tkPort.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; |
|
|
|
|
|
/* |
|
|
* The following magic value is stored in the "send_event" field of |
|
|
* FocusIn and FocusOut events that are generated in this file. This |
|
|
* allows us to separate "real" events coming from the server from |
|
|
* those that we generated. |
|
|
*/ |
|
|
|
|
|
#define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac) |
|
|
|
|
|
/* |
|
|
* Forward declarations for procedures defined in this file: |
|
|
*/ |
|
|
|
|
|
|
|
|
static DisplayFocusInfo *FindDisplayFocusInfo _ANSI_ARGS_((TkMainInfo *mainPtr, |
|
|
TkDisplay *dispPtr)); |
|
|
static void FocusMapProc _ANSI_ARGS_((ClientData clientData, |
|
|
XEvent *eventPtr)); |
|
|
static void GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr, |
|
|
TkWindow *destPtr)); |
|
|
|
|
|
/* |
|
|
*-------------------------------------------------------------- |
|
|
* |
|
|
* Tk_FocusObjCmd -- |
|
|
* |
|
|
* This procedure 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, interp, objc, objv) |
|
|
ClientData clientData; /* Main window associated with |
|
|
* interpreter. */ |
|
|
Tcl_Interp *interp; /* Current interpreter. */ |
|
|
int objc; /* Number of arguments. */ |
|
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
|
|
{ |
|
|
static char *focusOptions[] = {"-displayof", "-force", "-lastfor", |
|
|
(char *) NULL}; |
|
|
Tk_Window tkwin = (Tk_Window) clientData; |
|
|
TkWindow *winPtr = (TkWindow *) clientData; |
|
|
TkWindow *newPtr, *focusWinPtr, *topLevelPtr; |
|
|
ToplevelFocusInfo *tlFocusPtr; |
|
|
char *windowName; |
|
|
int index; |
|
|
|
|
|
/* |
|
|
* If invoked with no arguments, just return the current focus window. |
|
|
*/ |
|
|
|
|
|
if (objc == 1) { |
|
|
focusWinPtr = TkGetFocusWin(winPtr); |
|
|
if (focusWinPtr != NULL) { |
|
|
Tcl_SetResult(interp, focusWinPtr->pathName, TCL_STATIC); |
|
|
} |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
/* |
|
|
* If invoked with a single argument beginning with "." then focus |
|
|
* on that window. |
|
|
*/ |
|
|
|
|
|
if (objc == 2) { |
|
|
windowName = Tcl_GetStringFromObj(objv[1], (int *) NULL); |
|
|
|
|
|
/* |
|
|
* 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; |
|
|
} |
|
|
if (!(newPtr->flags & TK_ALREADY_DEAD)) { |
|
|
TkSetFocusWin(newPtr, 0); |
|
|
} |
|
|
return TCL_OK; |
|
|
} |
|
|
} |
|
|
|
|
|
if (Tcl_GetIndexFromObj(interp, objv[1], focusOptions, "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_GetStringFromObj(objv[2], (int *) NULL); |
|
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); |
|
|
if (newPtr == NULL) { |
|
|
return TCL_ERROR; |
|
|
} |
|
|
newPtr = TkGetFocusWin(newPtr); |
|
|
if (newPtr != NULL) { |
|
|
Tcl_SetResult(interp, newPtr->pathName, TCL_STATIC); |
|
|
} |
|
|
break; |
|
|
} |
|
|
case 1: { /* -force */ |
|
|
windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL); |
|
|
|
|
|
/* |
|
|
* 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_GetStringFromObj(objv[2], (int *) NULL); |
|
|
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_LEVEL) { |
|
|
for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; |
|
|
tlFocusPtr != NULL; |
|
|
tlFocusPtr = tlFocusPtr->nextPtr) { |
|
|
if (tlFocusPtr->topLevelPtr == topLevelPtr) { |
|
|
Tcl_SetResult(interp, |
|
|
tlFocusPtr->focusWinPtr->pathName, |
|
|
TCL_STATIC); |
|
|
return TCL_OK; |
|
|
} |
|
|
} |
|
|
Tcl_SetResult(interp, topLevelPtr->pathName, TCL_STATIC); |
|
|
return TCL_OK; |
|
|
} |
|
|
} |
|
|
break; |
|
|
} |
|
|
default: { |
|
|
panic("bad const entries to focusOptions in focus command"); |
|
|
} |
|
|
} |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
/* |
|
|
*-------------------------------------------------------------- |
|
|
* |
|
|
* TkFocusFilterEvent -- |
|
|
* |
|
|
* This procedure 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(winPtr, eventPtr) |
|
|
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 procedure 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 procedure; 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_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; |
|
|
|
|
|
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, (TkWindow *) 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)) { |
|
|
if (dispPtr->focusDebug) { |
|
|
printf("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)) { |
|
|
if (dispPtr->focusDebug) { |
|
|
printf("Defocussed implicit Async\n"); |
|
|
} |
|
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, |
|
|
(TkWindow *) NULL); |
|
|
XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, |
|
|
CurrentTime); |
|
|
displayFocusPtr->focusWinPtr = NULL; |
|
|
dispPtr->implicitWinPtr = NULL; |
|
|
} |
|
|
} |
|
|
return retValue; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* TkSetFocusWin -- |
|
|
* |
|
|
* This procedure 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(winPtr, force) |
|
|
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; |
|
|
|
|
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
|
|
|
|
|
/* |
|
|
* If force is set, we should make sure we grab the focus regardless |
|
|
* of the current focus window since under Windows, we may need to |
|
|
* take control away from another application. |
|
|
*/ |
|
|
|
|
|
if (winPtr == displayFocusPtr->focusWinPtr && !force) { |
|
|
return; |
|
|
} |
|
|
|
|
|
/* |
|
|
* 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. |
|
|
*/ |
|
|
|
|
|
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_LEVEL) { |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
* If the new focus window isn't mapped, then we can't focus on 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, |
|
|
(ClientData) displayFocusPtr->focusOnMapPtr); |
|
|
displayFocusPtr->focusOnMapPtr = NULL; |
|
|
} |
|
|
if (!allMapped) { |
|
|
Tk_CreateEventHandler((Tk_Window) winPtr, |
|
|
VisibilityChangeMask, FocusMapProc, |
|
|
(ClientData) 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 = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); |
|
|
tlFocusPtr->topLevelPtr = topLevelPtr; |
|
|
tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; |
|
|
winPtr->mainPtr->tlFocusPtr = tlFocusPtr; |
|
|
} |
|
|
tlFocusPtr->focusWinPtr = winPtr; |
|
|
|
|
|
/* |
|
|
* Reset the window system's focus window and generate focus events, |
|
|
* with two special cases: |
|
|
* |
|
|
* 1. If the application is embedded and doesn't currently have the |
|
|
* focus, don't set the focus directly. Instead, see if the |
|
|
* embedding code can claim the focus from the enclosing |
|
|
* container. |
|
|
* 2. Otherwise, if the application doesn't currently have the |
|
|
* focus, don't change the window system's focus unless it was |
|
|
* already in this application or "force" was specified. |
|
|
*/ |
|
|
|
|
|
if ((topLevelPtr->flags & TK_EMBEDDED) |
|
|
&& (displayFocusPtr->focusWinPtr == NULL)) { |
|
|
TkpClaimFocus(topLevelPtr, force); |
|
|
} else if ((displayFocusPtr->focusWinPtr != NULL) || force) { |
|
|
/* |
|
|
* Generate events to shift focus between Tk windows. |
|
|
* We do this regardless of what TkpChangeFocus does with |
|
|
* the real X focus so that Tk widgets track focus commands |
|
|
* when there is no window manager. GenerateFocusEvents will |
|
|
* set up a serial number marker so we discard focus events |
|
|
* that are triggered by the ChangeFocus. |
|
|
*/ |
|
|
|
|
|
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 procedure 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(winPtr) |
|
|
TkWindow *winPtr; /* Window that selects an application |
|
|
* and a display. */ |
|
|
{ |
|
|
DisplayFocusInfo *displayFocusPtr; |
|
|
|
|
|
if (winPtr == NULL) { |
|
|
return (TkWindow *) 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(winPtr, eventPtr) |
|
|
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, vRootX, vRootY, vRootWidth, vRootHeight; |
|
|
|
|
|
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_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY, |
|
|
&vRootWidth, &vRootHeight); |
|
|
Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY); |
|
|
eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX; |
|
|
eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - 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 (TkWindow *) NULL; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* TkFocusDeadWindow -- |
|
|
* |
|
|
* This procedure 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(winPtr) |
|
|
register TkWindow *winPtr; /* Information about the window |
|
|
* that is being deleted. */ |
|
|
{ |
|
|
ToplevelFocusInfo *tlFocusPtr, *prevPtr; |
|
|
DisplayFocusInfo *displayFocusPtr; |
|
|
TkDisplay *dispPtr = winPtr->dispPtr; |
|
|
|
|
|
/* |
|
|
* 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) { |
|
|
if (dispPtr->focusDebug) { |
|
|
printf("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((char *) 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)) { |
|
|
if (dispPtr->focusDebug) { |
|
|
printf("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; |
|
|
} |
|
|
} |
|
|
|
|
|
if (displayFocusPtr->focusOnMapPtr == winPtr) { |
|
|
displayFocusPtr->focusOnMapPtr = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* GenerateFocusEvents -- |
|
|
* |
|
|
* This procedure 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(sourcePtr, destPtr) |
|
|
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_EVENT_MAGIC; |
|
|
event.xfocus.display = winPtr->display; |
|
|
event.xfocus.mode = NotifyNormal; |
|
|
TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn, |
|
|
TCL_QUEUE_MARK); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* FocusMapProc -- |
|
|
* |
|
|
* This procedure 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 procedure 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, eventPtr) |
|
|
ClientData clientData; /* Toplevel window. */ |
|
|
XEvent *eventPtr; /* Information about event. */ |
|
|
{ |
|
|
TkWindow *winPtr = (TkWindow *) clientData; |
|
|
DisplayFocusInfo *displayFocusPtr; |
|
|
|
|
|
if (eventPtr->type == VisibilityNotify) { |
|
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, |
|
|
winPtr->dispPtr); |
|
|
if (winPtr->dispPtr->focusDebug) { |
|
|
printf("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 procedure 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(mainPtr, dispPtr) |
|
|
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 = (DisplayFocusInfo *) 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; |
|
|
} |
|
|
|
|
|
|
|
|
/* $History: tkFocus.c $ |
|
|
* |
|
|
* ***************** Version 1 ***************** |
|
|
* User: Dtashley Date: 1/02/01 Time: 2:49a |
|
|
* Created in $/IjuScripter, IjuConsole/Source/Tk Base |
|
|
* Initial check-in. |
|
|
*/ |
|
|
|
|
|
/* End of TKFOCUS.C */ |
|
1 |
|
/* $Header$ */ |
2 |
|
|
3 |
|
/* |
4 |
|
* tkFocus.c -- |
5 |
|
* |
6 |
|
* This file contains procedures that manage the input |
7 |
|
* focus for Tk. |
8 |
|
* |
9 |
|
* Copyright (c) 1990-1994 The Regents of the University of California. |
10 |
|
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
11 |
|
* |
12 |
|
* See the file "license.terms" for information on usage and redistribution |
13 |
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
14 |
|
* |
15 |
|
* RCS: @(#) $Id: tkfocus.c,v 1.1.1.1 2001/06/13 05:00:25 dtashley Exp $ |
16 |
|
*/ |
17 |
|
|
18 |
|
#include "tkInt.h" |
19 |
|
#include "tkPort.h" |
20 |
|
|
21 |
|
|
22 |
|
/* |
23 |
|
* For each top-level window that has ever received the focus, there |
24 |
|
* is a record of the following type: |
25 |
|
*/ |
26 |
|
|
27 |
|
typedef struct TkToplevelFocusInfo { |
28 |
|
TkWindow *topLevelPtr; /* Information about top-level window. */ |
29 |
|
TkWindow *focusWinPtr; /* The next time the focus comes to this |
30 |
|
* top-level, it will be given to this |
31 |
|
* window. */ |
32 |
|
struct TkToplevelFocusInfo *nextPtr; |
33 |
|
/* Next in list of all toplevel focus records |
34 |
|
* for a given application. */ |
35 |
|
} ToplevelFocusInfo; |
36 |
|
|
37 |
|
/* |
38 |
|
* One of the following structures exists for each display used by |
39 |
|
* each application. These are linked together from the TkMainInfo |
40 |
|
* structure. These structures are needed because it isn't |
41 |
|
* sufficient to store a single piece of focus information in each |
42 |
|
* display or in each application: we need the cross-product. |
43 |
|
* There needs to be separate information for each display, because |
44 |
|
* it's possible to have multiple focus windows active simultaneously |
45 |
|
* on different displays. There also needs to be separate information |
46 |
|
* for each application, because of embedding: if an embedded |
47 |
|
* application has the focus, its container application also has |
48 |
|
* the focus. Thus we keep a list of structures for each application: |
49 |
|
* the same display can appear in structures for several applications |
50 |
|
* at once. |
51 |
|
*/ |
52 |
|
|
53 |
|
typedef struct TkDisplayFocusInfo { |
54 |
|
TkDisplay *dispPtr; /* Display that this information pertains |
55 |
|
* to. */ |
56 |
|
struct TkWindow *focusWinPtr; |
57 |
|
/* Window that currently has the focus for |
58 |
|
* this application on this display, or NULL |
59 |
|
* if none. */ |
60 |
|
struct TkWindow *focusOnMapPtr; |
61 |
|
/* This points to a toplevel window that is |
62 |
|
* supposed to receive the X input focus as |
63 |
|
* soon as it is mapped (needed to handle the |
64 |
|
* fact that X won't allow the focus on an |
65 |
|
* unmapped window). NULL means no delayed |
66 |
|
* focus op in progress for this display. */ |
67 |
|
int forceFocus; /* Associated with focusOnMapPtr: non-zero |
68 |
|
* means claim the focus even if some other |
69 |
|
* application currently has it. */ |
70 |
|
unsigned long focusSerial; /* Serial number of last request this |
71 |
|
* application made to change the focus on |
72 |
|
* this display. Used to identify stale |
73 |
|
* focus notifications coming from the |
74 |
|
* X server. */ |
75 |
|
struct TkDisplayFocusInfo *nextPtr; |
76 |
|
/* Next in list of all display focus |
77 |
|
* records for a given application. */ |
78 |
|
} DisplayFocusInfo; |
79 |
|
|
80 |
|
/* |
81 |
|
* The following magic value is stored in the "send_event" field of |
82 |
|
* FocusIn and FocusOut events that are generated in this file. This |
83 |
|
* allows us to separate "real" events coming from the server from |
84 |
|
* those that we generated. |
85 |
|
*/ |
86 |
|
|
87 |
|
#define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac) |
88 |
|
|
89 |
|
/* |
90 |
|
* Forward declarations for procedures defined in this file: |
91 |
|
*/ |
92 |
|
|
93 |
|
|
94 |
|
static DisplayFocusInfo *FindDisplayFocusInfo _ANSI_ARGS_((TkMainInfo *mainPtr, |
95 |
|
TkDisplay *dispPtr)); |
96 |
|
static void FocusMapProc _ANSI_ARGS_((ClientData clientData, |
97 |
|
XEvent *eventPtr)); |
98 |
|
static void GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr, |
99 |
|
TkWindow *destPtr)); |
100 |
|
|
101 |
|
/* |
102 |
|
*-------------------------------------------------------------- |
103 |
|
* |
104 |
|
* Tk_FocusObjCmd -- |
105 |
|
* |
106 |
|
* This procedure is invoked to process the "focus" Tcl command. |
107 |
|
* See the user documentation for details on what it does. |
108 |
|
* |
109 |
|
* Results: |
110 |
|
* A standard Tcl result. |
111 |
|
* |
112 |
|
* Side effects: |
113 |
|
* See the user documentation. |
114 |
|
* |
115 |
|
*-------------------------------------------------------------- |
116 |
|
*/ |
117 |
|
|
118 |
|
int |
119 |
|
Tk_FocusObjCmd(clientData, interp, objc, objv) |
120 |
|
ClientData clientData; /* Main window associated with |
121 |
|
* interpreter. */ |
122 |
|
Tcl_Interp *interp; /* Current interpreter. */ |
123 |
|
int objc; /* Number of arguments. */ |
124 |
|
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
125 |
|
{ |
126 |
|
static char *focusOptions[] = {"-displayof", "-force", "-lastfor", |
127 |
|
(char *) NULL}; |
128 |
|
Tk_Window tkwin = (Tk_Window) clientData; |
129 |
|
TkWindow *winPtr = (TkWindow *) clientData; |
130 |
|
TkWindow *newPtr, *focusWinPtr, *topLevelPtr; |
131 |
|
ToplevelFocusInfo *tlFocusPtr; |
132 |
|
char *windowName; |
133 |
|
int index; |
134 |
|
|
135 |
|
/* |
136 |
|
* If invoked with no arguments, just return the current focus window. |
137 |
|
*/ |
138 |
|
|
139 |
|
if (objc == 1) { |
140 |
|
focusWinPtr = TkGetFocusWin(winPtr); |
141 |
|
if (focusWinPtr != NULL) { |
142 |
|
Tcl_SetResult(interp, focusWinPtr->pathName, TCL_STATIC); |
143 |
|
} |
144 |
|
return TCL_OK; |
145 |
|
} |
146 |
|
|
147 |
|
/* |
148 |
|
* If invoked with a single argument beginning with "." then focus |
149 |
|
* on that window. |
150 |
|
*/ |
151 |
|
|
152 |
|
if (objc == 2) { |
153 |
|
windowName = Tcl_GetStringFromObj(objv[1], (int *) NULL); |
154 |
|
|
155 |
|
/* |
156 |
|
* The empty string case exists for backwards compatibility. |
157 |
|
*/ |
158 |
|
|
159 |
|
if (windowName[0] == '\0') { |
160 |
|
return TCL_OK; |
161 |
|
} |
162 |
|
if (windowName[0] == '.') { |
163 |
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); |
164 |
|
if (newPtr == NULL) { |
165 |
|
return TCL_ERROR; |
166 |
|
} |
167 |
|
if (!(newPtr->flags & TK_ALREADY_DEAD)) { |
168 |
|
TkSetFocusWin(newPtr, 0); |
169 |
|
} |
170 |
|
return TCL_OK; |
171 |
|
} |
172 |
|
} |
173 |
|
|
174 |
|
if (Tcl_GetIndexFromObj(interp, objv[1], focusOptions, "option", 0, |
175 |
|
&index) != TCL_OK) { |
176 |
|
return TCL_ERROR; |
177 |
|
} |
178 |
|
if (objc != 3) { |
179 |
|
Tcl_WrongNumArgs(interp, 2, objv, "window"); |
180 |
|
return TCL_ERROR; |
181 |
|
} |
182 |
|
switch (index) { |
183 |
|
case 0: { /* -displayof */ |
184 |
|
windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL); |
185 |
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); |
186 |
|
if (newPtr == NULL) { |
187 |
|
return TCL_ERROR; |
188 |
|
} |
189 |
|
newPtr = TkGetFocusWin(newPtr); |
190 |
|
if (newPtr != NULL) { |
191 |
|
Tcl_SetResult(interp, newPtr->pathName, TCL_STATIC); |
192 |
|
} |
193 |
|
break; |
194 |
|
} |
195 |
|
case 1: { /* -force */ |
196 |
|
windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL); |
197 |
|
|
198 |
|
/* |
199 |
|
* The empty string case exists for backwards compatibility. |
200 |
|
*/ |
201 |
|
|
202 |
|
if (windowName[0] == '\0') { |
203 |
|
return TCL_OK; |
204 |
|
} |
205 |
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); |
206 |
|
if (newPtr == NULL) { |
207 |
|
return TCL_ERROR; |
208 |
|
} |
209 |
|
TkSetFocusWin(newPtr, 1); |
210 |
|
break; |
211 |
|
} |
212 |
|
case 2: { /* -lastfor */ |
213 |
|
windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL); |
214 |
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); |
215 |
|
if (newPtr == NULL) { |
216 |
|
return TCL_ERROR; |
217 |
|
} |
218 |
|
for (topLevelPtr = newPtr; topLevelPtr != NULL; |
219 |
|
topLevelPtr = topLevelPtr->parentPtr) { |
220 |
|
if (topLevelPtr->flags & TK_TOP_LEVEL) { |
221 |
|
for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; |
222 |
|
tlFocusPtr != NULL; |
223 |
|
tlFocusPtr = tlFocusPtr->nextPtr) { |
224 |
|
if (tlFocusPtr->topLevelPtr == topLevelPtr) { |
225 |
|
Tcl_SetResult(interp, |
226 |
|
tlFocusPtr->focusWinPtr->pathName, |
227 |
|
TCL_STATIC); |
228 |
|
return TCL_OK; |
229 |
|
} |
230 |
|
} |
231 |
|
Tcl_SetResult(interp, topLevelPtr->pathName, TCL_STATIC); |
232 |
|
return TCL_OK; |
233 |
|
} |
234 |
|
} |
235 |
|
break; |
236 |
|
} |
237 |
|
default: { |
238 |
|
panic("bad const entries to focusOptions in focus command"); |
239 |
|
} |
240 |
|
} |
241 |
|
return TCL_OK; |
242 |
|
} |
243 |
|
|
244 |
|
/* |
245 |
|
*-------------------------------------------------------------- |
246 |
|
* |
247 |
|
* TkFocusFilterEvent -- |
248 |
|
* |
249 |
|
* This procedure is invoked by Tk_HandleEvent when it encounters |
250 |
|
* a FocusIn, FocusOut, Enter, or Leave event. |
251 |
|
* |
252 |
|
* Results: |
253 |
|
* A return value of 1 means that Tk_HandleEvent should process |
254 |
|
* the event normally (i.e. event handlers should be invoked). |
255 |
|
* A return value of 0 means that this event should be ignored. |
256 |
|
* |
257 |
|
* Side effects: |
258 |
|
* Additional events may be generated, and the focus may switch. |
259 |
|
* |
260 |
|
*-------------------------------------------------------------- |
261 |
|
*/ |
262 |
|
|
263 |
|
int |
264 |
|
TkFocusFilterEvent(winPtr, eventPtr) |
265 |
|
TkWindow *winPtr; /* Window that focus event is directed to. */ |
266 |
|
XEvent *eventPtr; /* FocusIn, FocusOut, Enter, or Leave |
267 |
|
* event. */ |
268 |
|
{ |
269 |
|
/* |
270 |
|
* Design notes: the window manager and X server work together to |
271 |
|
* transfer the focus among top-level windows. This procedure takes |
272 |
|
* care of transferring the focus from a top-level or wrapper window |
273 |
|
* to the actual window within that top-level that has the focus. |
274 |
|
* We do this by synthesizing X events to move the focus around. |
275 |
|
* None of the FocusIn and FocusOut events generated by X are ever |
276 |
|
* used outside of this procedure; only the synthesized events get |
277 |
|
* through to the rest of the application. At one point (e.g. |
278 |
|
* Tk4.0b1) Tk used to call X to move the focus from a top-level to |
279 |
|
* one of its descendants, then just pass through the events |
280 |
|
* generated by X. This approach didn't work very well, for a |
281 |
|
* variety of reasons. For example, if X generates the events they |
282 |
|
* go at the back of the event queue, which could cause problems if |
283 |
|
* other things have already happened, such as moving the focus to |
284 |
|
* yet another window. |
285 |
|
*/ |
286 |
|
|
287 |
|
ToplevelFocusInfo *tlFocusPtr; |
288 |
|
DisplayFocusInfo *displayFocusPtr; |
289 |
|
TkDisplay *dispPtr = winPtr->dispPtr; |
290 |
|
TkWindow *newFocusPtr; |
291 |
|
int retValue, delta; |
292 |
|
|
293 |
|
/* |
294 |
|
* If this was a generated event, just turn off the generated |
295 |
|
* flag and pass the event through to Tk bindings. |
296 |
|
*/ |
297 |
|
|
298 |
|
if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) { |
299 |
|
eventPtr->xfocus.send_event = 0; |
300 |
|
return 1; |
301 |
|
} |
302 |
|
|
303 |
|
/* |
304 |
|
* Check for special events generated by embedded applications to |
305 |
|
* request the input focus. If this is one of those events, make |
306 |
|
* the change in focus and return without any additional processing |
307 |
|
* of the event (note: the "detail" field of the event indicates |
308 |
|
* whether to claim the focus even if we don't already have it). |
309 |
|
*/ |
310 |
|
|
311 |
|
if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) |
312 |
|
&& (eventPtr->type == FocusIn)) { |
313 |
|
TkSetFocusWin(winPtr, eventPtr->xfocus.detail); |
314 |
|
return 0; |
315 |
|
} |
316 |
|
|
317 |
|
/* |
318 |
|
* This was not a generated event. We'll return 1 (so that the |
319 |
|
* event will be processed) if it's an Enter or Leave event, and |
320 |
|
* 0 (so that the event won't be processed) if it's a FocusIn or |
321 |
|
* FocusOut event. |
322 |
|
*/ |
323 |
|
|
324 |
|
retValue = 0; |
325 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
326 |
|
if (eventPtr->type == FocusIn) { |
327 |
|
/* |
328 |
|
* Skip FocusIn events that cause confusion |
329 |
|
* NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur |
330 |
|
* on windows in between the origin and destination of the |
331 |
|
* focus change. For FocusIn we may see this when focus |
332 |
|
* goes into an embedded child. We don't care about this, |
333 |
|
* although we may end up getting a NotifyPointer later. |
334 |
|
* NotifyInferior - focus is coming to us from an embedded child. |
335 |
|
* When focus is on an embeded focus, we still think we have |
336 |
|
* the focus, too, so this message doesn't change our state. |
337 |
|
* NotifyPointerRoot - should never happen because this is sent |
338 |
|
* to the root window. |
339 |
|
* |
340 |
|
* Interesting FocusIn events are |
341 |
|
* NotifyAncestor - focus is coming from our parent, probably the root. |
342 |
|
* NotifyNonlinear - focus is coming from a different branch, probably |
343 |
|
* another toplevel. |
344 |
|
* NotifyPointer - implicit focus because of the mouse position. |
345 |
|
* This is only interesting on toplevels, when it means that the |
346 |
|
* focus has been set to the root window but the mouse is over |
347 |
|
* this toplevel. We take the focus implicitly (probably no |
348 |
|
* window manager) |
349 |
|
*/ |
350 |
|
|
351 |
|
if ((eventPtr->xfocus.detail == NotifyVirtual) |
352 |
|
|| (eventPtr->xfocus.detail == NotifyNonlinearVirtual) |
353 |
|
|| (eventPtr->xfocus.detail == NotifyPointerRoot) |
354 |
|
|| (eventPtr->xfocus.detail == NotifyInferior)) { |
355 |
|
return retValue; |
356 |
|
} |
357 |
|
} else if (eventPtr->type == FocusOut) { |
358 |
|
/* |
359 |
|
* Skip FocusOut events that cause confusion. |
360 |
|
* NotifyPointer - the pointer is in us or a child, and we are losing |
361 |
|
* focus because of an XSetInputFocus. Other focus events |
362 |
|
* will set our state properly. |
363 |
|
* NotifyPointerRoot - should never happen because this is sent |
364 |
|
* to the root window. |
365 |
|
* NotifyInferior - focus leaving us for an embedded child. We |
366 |
|
* retain a notion of focus when an embedded child has focus. |
367 |
|
* |
368 |
|
* Interesting events are: |
369 |
|
* NotifyAncestor - focus is going to root. |
370 |
|
* NotifyNonlinear - focus is going to another branch, probably |
371 |
|
* another toplevel. |
372 |
|
* NotifyVirtual, NotifyNonlinearVirtual - focus is passing through, |
373 |
|
* and we need to make sure we track this. |
374 |
|
*/ |
375 |
|
|
376 |
|
if ((eventPtr->xfocus.detail == NotifyPointer) |
377 |
|
|| (eventPtr->xfocus.detail == NotifyPointerRoot) |
378 |
|
|| (eventPtr->xfocus.detail == NotifyInferior)) { |
379 |
|
return retValue; |
380 |
|
} |
381 |
|
} else { |
382 |
|
retValue = 1; |
383 |
|
if (eventPtr->xcrossing.detail == NotifyInferior) { |
384 |
|
return retValue; |
385 |
|
} |
386 |
|
} |
387 |
|
|
388 |
|
/* |
389 |
|
* If winPtr isn't a top-level window than just ignore the event. |
390 |
|
*/ |
391 |
|
|
392 |
|
winPtr = TkWmFocusToplevel(winPtr); |
393 |
|
if (winPtr == NULL) { |
394 |
|
return retValue; |
395 |
|
} |
396 |
|
|
397 |
|
/* |
398 |
|
* If there is a grab in effect and this window is outside the |
399 |
|
* grabbed tree, then ignore the event. |
400 |
|
*/ |
401 |
|
|
402 |
|
if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) { |
403 |
|
return retValue; |
404 |
|
} |
405 |
|
|
406 |
|
/* |
407 |
|
* It is possible that there were outstanding FocusIn and FocusOut |
408 |
|
* events on their way to us at the time the focus was changed |
409 |
|
* internally with the "focus" command. If so, these events could |
410 |
|
* potentially cause us to lose the focus (switch it to the window |
411 |
|
* of the last FocusIn event) even though the focus change occurred |
412 |
|
* after those events. The following code detects this and ignores |
413 |
|
* the stale events. |
414 |
|
* |
415 |
|
* Note: the focusSerial is only generated by TkpChangeFocus, |
416 |
|
* whereas in Tk 4.2 there was always a nop marker generated. |
417 |
|
*/ |
418 |
|
|
419 |
|
delta = eventPtr->xfocus.serial - displayFocusPtr->focusSerial; |
420 |
|
if (delta < 0) { |
421 |
|
return retValue; |
422 |
|
} |
423 |
|
|
424 |
|
/* |
425 |
|
* Find the ToplevelFocusInfo structure for the window, and make a new one |
426 |
|
* if there isn't one already. |
427 |
|
*/ |
428 |
|
|
429 |
|
for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; |
430 |
|
tlFocusPtr = tlFocusPtr->nextPtr) { |
431 |
|
if (tlFocusPtr->topLevelPtr == winPtr) { |
432 |
|
break; |
433 |
|
} |
434 |
|
} |
435 |
|
if (tlFocusPtr == NULL) { |
436 |
|
tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); |
437 |
|
tlFocusPtr->topLevelPtr = tlFocusPtr->focusWinPtr = winPtr; |
438 |
|
tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; |
439 |
|
winPtr->mainPtr->tlFocusPtr = tlFocusPtr; |
440 |
|
} |
441 |
|
newFocusPtr = tlFocusPtr->focusWinPtr; |
442 |
|
|
443 |
|
if (eventPtr->type == FocusIn) { |
444 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); |
445 |
|
displayFocusPtr->focusWinPtr = newFocusPtr; |
446 |
|
dispPtr->focusPtr = newFocusPtr; |
447 |
|
|
448 |
|
/* |
449 |
|
* NotifyPointer gets set when the focus has been set to the root window |
450 |
|
* but we have the pointer. We'll treat this like an implicit |
451 |
|
* focus in event so that upon Leave events we release focus. |
452 |
|
*/ |
453 |
|
|
454 |
|
if (!(winPtr->flags & TK_EMBEDDED)) { |
455 |
|
if (eventPtr->xfocus.detail == NotifyPointer) { |
456 |
|
dispPtr->implicitWinPtr = winPtr; |
457 |
|
} else { |
458 |
|
dispPtr->implicitWinPtr = NULL; |
459 |
|
} |
460 |
|
} |
461 |
|
} else if (eventPtr->type == FocusOut) { |
462 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, (TkWindow *) NULL); |
463 |
|
|
464 |
|
/* |
465 |
|
* Reset dispPtr->focusPtr, but only if it currently is the same |
466 |
|
* as this application's focusWinPtr: this check is needed to |
467 |
|
* handle embedded applications in the same process. |
468 |
|
*/ |
469 |
|
|
470 |
|
if (dispPtr->focusPtr == displayFocusPtr->focusWinPtr) { |
471 |
|
dispPtr->focusPtr = NULL; |
472 |
|
} |
473 |
|
displayFocusPtr->focusWinPtr = NULL; |
474 |
|
} else if (eventPtr->type == EnterNotify) { |
475 |
|
/* |
476 |
|
* If there is no window manager, or if the window manager isn't |
477 |
|
* moving the focus around (e.g. the disgusting "NoTitleFocus" |
478 |
|
* option has been selected in twm), then we won't get FocusIn |
479 |
|
* or FocusOut events. Instead, the "focus" field will be set |
480 |
|
* in an Enter event to indicate that we've already got the focus |
481 |
|
* when the mouse enters the window (even though we didn't get |
482 |
|
* a FocusIn event). Watch for this and grab the focus when it |
483 |
|
* happens. Note: if this is an embedded application then don't |
484 |
|
* accept the focus implicitly like this; the container |
485 |
|
* application will give us the focus explicitly if it wants us |
486 |
|
* to have it. |
487 |
|
*/ |
488 |
|
|
489 |
|
if (eventPtr->xcrossing.focus && |
490 |
|
(displayFocusPtr->focusWinPtr == NULL) |
491 |
|
&& !(winPtr->flags & TK_EMBEDDED)) { |
492 |
|
if (dispPtr->focusDebug) { |
493 |
|
printf("Focussed implicitly on %s\n", |
494 |
|
newFocusPtr->pathName); |
495 |
|
} |
496 |
|
|
497 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); |
498 |
|
displayFocusPtr->focusWinPtr = newFocusPtr; |
499 |
|
dispPtr->implicitWinPtr = winPtr; |
500 |
|
dispPtr->focusPtr = newFocusPtr; |
501 |
|
} |
502 |
|
} else if (eventPtr->type == LeaveNotify) { |
503 |
|
/* |
504 |
|
* If the pointer just left a window for which we automatically |
505 |
|
* claimed the focus on enter, move the focus back to the root |
506 |
|
* window, where it was before we claimed it above. Note: |
507 |
|
* dispPtr->implicitWinPtr may not be the same as |
508 |
|
* displayFocusPtr->focusWinPtr (e.g. because the "focus" |
509 |
|
* command was used to redirect the focus after it arrived at |
510 |
|
* dispPtr->implicitWinPtr)!! In addition, we generate events |
511 |
|
* because the window manager won't give us a FocusOut event when |
512 |
|
* we focus on the root. |
513 |
|
*/ |
514 |
|
|
515 |
|
if ((dispPtr->implicitWinPtr != NULL) |
516 |
|
&& !(winPtr->flags & TK_EMBEDDED)) { |
517 |
|
if (dispPtr->focusDebug) { |
518 |
|
printf("Defocussed implicit Async\n"); |
519 |
|
} |
520 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, |
521 |
|
(TkWindow *) NULL); |
522 |
|
XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, |
523 |
|
CurrentTime); |
524 |
|
displayFocusPtr->focusWinPtr = NULL; |
525 |
|
dispPtr->implicitWinPtr = NULL; |
526 |
|
} |
527 |
|
} |
528 |
|
return retValue; |
529 |
|
} |
530 |
|
|
531 |
|
/* |
532 |
|
*---------------------------------------------------------------------- |
533 |
|
* |
534 |
|
* TkSetFocusWin -- |
535 |
|
* |
536 |
|
* This procedure is invoked to change the focus window for a |
537 |
|
* given display in a given application. |
538 |
|
* |
539 |
|
* Results: |
540 |
|
* None. |
541 |
|
* |
542 |
|
* Side effects: |
543 |
|
* Event handlers may be invoked to process the change of |
544 |
|
* focus. |
545 |
|
* |
546 |
|
*---------------------------------------------------------------------- |
547 |
|
*/ |
548 |
|
|
549 |
|
void |
550 |
|
TkSetFocusWin(winPtr, force) |
551 |
|
TkWindow *winPtr; /* Window that is to be the new focus for |
552 |
|
* its display and application. */ |
553 |
|
int force; /* If non-zero, set the X focus to this |
554 |
|
* window even if the application doesn't |
555 |
|
* currently have the X focus. */ |
556 |
|
{ |
557 |
|
ToplevelFocusInfo *tlFocusPtr; |
558 |
|
DisplayFocusInfo *displayFocusPtr; |
559 |
|
TkWindow *topLevelPtr; |
560 |
|
int allMapped, serial; |
561 |
|
|
562 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
563 |
|
|
564 |
|
/* |
565 |
|
* If force is set, we should make sure we grab the focus regardless |
566 |
|
* of the current focus window since under Windows, we may need to |
567 |
|
* take control away from another application. |
568 |
|
*/ |
569 |
|
|
570 |
|
if (winPtr == displayFocusPtr->focusWinPtr && !force) { |
571 |
|
return; |
572 |
|
} |
573 |
|
|
574 |
|
/* |
575 |
|
* Find the top-level window for winPtr, then find (or create) |
576 |
|
* a record for the top-level. Also see whether winPtr and all its |
577 |
|
* ancestors are mapped. |
578 |
|
*/ |
579 |
|
|
580 |
|
allMapped = 1; |
581 |
|
for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) { |
582 |
|
if (topLevelPtr == NULL) { |
583 |
|
/* |
584 |
|
* The window is being deleted. No point in worrying about |
585 |
|
* giving it the focus. |
586 |
|
*/ |
587 |
|
return; |
588 |
|
} |
589 |
|
if (!(topLevelPtr->flags & TK_MAPPED)) { |
590 |
|
allMapped = 0; |
591 |
|
} |
592 |
|
if (topLevelPtr->flags & TK_TOP_LEVEL) { |
593 |
|
break; |
594 |
|
} |
595 |
|
} |
596 |
|
|
597 |
|
/* |
598 |
|
* If the new focus window isn't mapped, then we can't focus on it |
599 |
|
* (X will generate an error, for example). Instead, create an |
600 |
|
* event handler that will set the focus to this window once it gets |
601 |
|
* mapped. At the same time, delete any old handler that might be |
602 |
|
* around; it's no longer relevant. |
603 |
|
*/ |
604 |
|
|
605 |
|
if (displayFocusPtr->focusOnMapPtr != NULL) { |
606 |
|
Tk_DeleteEventHandler( |
607 |
|
(Tk_Window) displayFocusPtr->focusOnMapPtr, |
608 |
|
StructureNotifyMask, FocusMapProc, |
609 |
|
(ClientData) displayFocusPtr->focusOnMapPtr); |
610 |
|
displayFocusPtr->focusOnMapPtr = NULL; |
611 |
|
} |
612 |
|
if (!allMapped) { |
613 |
|
Tk_CreateEventHandler((Tk_Window) winPtr, |
614 |
|
VisibilityChangeMask, FocusMapProc, |
615 |
|
(ClientData) winPtr); |
616 |
|
displayFocusPtr->focusOnMapPtr = winPtr; |
617 |
|
displayFocusPtr->forceFocus = force; |
618 |
|
return; |
619 |
|
} |
620 |
|
|
621 |
|
for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; |
622 |
|
tlFocusPtr = tlFocusPtr->nextPtr) { |
623 |
|
if (tlFocusPtr->topLevelPtr == topLevelPtr) { |
624 |
|
break; |
625 |
|
} |
626 |
|
} |
627 |
|
if (tlFocusPtr == NULL) { |
628 |
|
tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo)); |
629 |
|
tlFocusPtr->topLevelPtr = topLevelPtr; |
630 |
|
tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; |
631 |
|
winPtr->mainPtr->tlFocusPtr = tlFocusPtr; |
632 |
|
} |
633 |
|
tlFocusPtr->focusWinPtr = winPtr; |
634 |
|
|
635 |
|
/* |
636 |
|
* Reset the window system's focus window and generate focus events, |
637 |
|
* with two special cases: |
638 |
|
* |
639 |
|
* 1. If the application is embedded and doesn't currently have the |
640 |
|
* focus, don't set the focus directly. Instead, see if the |
641 |
|
* embedding code can claim the focus from the enclosing |
642 |
|
* container. |
643 |
|
* 2. Otherwise, if the application doesn't currently have the |
644 |
|
* focus, don't change the window system's focus unless it was |
645 |
|
* already in this application or "force" was specified. |
646 |
|
*/ |
647 |
|
|
648 |
|
if ((topLevelPtr->flags & TK_EMBEDDED) |
649 |
|
&& (displayFocusPtr->focusWinPtr == NULL)) { |
650 |
|
TkpClaimFocus(topLevelPtr, force); |
651 |
|
} else if ((displayFocusPtr->focusWinPtr != NULL) || force) { |
652 |
|
/* |
653 |
|
* Generate events to shift focus between Tk windows. |
654 |
|
* We do this regardless of what TkpChangeFocus does with |
655 |
|
* the real X focus so that Tk widgets track focus commands |
656 |
|
* when there is no window manager. GenerateFocusEvents will |
657 |
|
* set up a serial number marker so we discard focus events |
658 |
|
* that are triggered by the ChangeFocus. |
659 |
|
*/ |
660 |
|
|
661 |
|
serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force); |
662 |
|
if (serial != 0) { |
663 |
|
displayFocusPtr->focusSerial = serial; |
664 |
|
} |
665 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr); |
666 |
|
displayFocusPtr->focusWinPtr = winPtr; |
667 |
|
winPtr->dispPtr->focusPtr = winPtr; |
668 |
|
} |
669 |
|
} |
670 |
|
|
671 |
|
/* |
672 |
|
*---------------------------------------------------------------------- |
673 |
|
* |
674 |
|
* TkGetFocusWin -- |
675 |
|
* |
676 |
|
* Given a window, this procedure returns the current focus |
677 |
|
* window for its application and display. |
678 |
|
* |
679 |
|
* Results: |
680 |
|
* The return value is a pointer to the window that currently |
681 |
|
* has the input focus for the specified application and |
682 |
|
* display, or NULL if none. |
683 |
|
* |
684 |
|
* Side effects: |
685 |
|
* None. |
686 |
|
* |
687 |
|
*---------------------------------------------------------------------- |
688 |
|
*/ |
689 |
|
|
690 |
|
TkWindow * |
691 |
|
TkGetFocusWin(winPtr) |
692 |
|
TkWindow *winPtr; /* Window that selects an application |
693 |
|
* and a display. */ |
694 |
|
{ |
695 |
|
DisplayFocusInfo *displayFocusPtr; |
696 |
|
|
697 |
|
if (winPtr == NULL) { |
698 |
|
return (TkWindow *) NULL; |
699 |
|
} |
700 |
|
|
701 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
702 |
|
return displayFocusPtr->focusWinPtr; |
703 |
|
} |
704 |
|
|
705 |
|
/* |
706 |
|
*---------------------------------------------------------------------- |
707 |
|
* |
708 |
|
* TkFocusKeyEvent -- |
709 |
|
* |
710 |
|
* Given a window and a key press or release event that arrived for |
711 |
|
* the window, use information about the keyboard focus to compute |
712 |
|
* which window should really get the event. In addition, update |
713 |
|
* the event to refer to its new window. |
714 |
|
* |
715 |
|
* Results: |
716 |
|
* The return value is a pointer to the window that has the input |
717 |
|
* focus in winPtr's application, or NULL if winPtr's application |
718 |
|
* doesn't have the input focus. If a non-NULL value is returned, |
719 |
|
* eventPtr will be updated to refer properly to the focus window. |
720 |
|
* |
721 |
|
* Side effects: |
722 |
|
* None. |
723 |
|
* |
724 |
|
*---------------------------------------------------------------------- |
725 |
|
*/ |
726 |
|
|
727 |
|
TkWindow * |
728 |
|
TkFocusKeyEvent(winPtr, eventPtr) |
729 |
|
TkWindow *winPtr; /* Window that selects an application |
730 |
|
* and a display. */ |
731 |
|
XEvent *eventPtr; /* X event to redirect (should be KeyPress |
732 |
|
* or KeyRelease). */ |
733 |
|
{ |
734 |
|
DisplayFocusInfo *displayFocusPtr; |
735 |
|
TkWindow *focusWinPtr; |
736 |
|
int focusX, focusY, vRootX, vRootY, vRootWidth, vRootHeight; |
737 |
|
|
738 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
739 |
|
focusWinPtr = displayFocusPtr->focusWinPtr; |
740 |
|
|
741 |
|
/* |
742 |
|
* The code below is a debugging aid to make sure that dispPtr->focusPtr |
743 |
|
* is kept properly in sync with the "truth", which is the value in |
744 |
|
* displayFocusPtr->focusWinPtr. |
745 |
|
*/ |
746 |
|
|
747 |
|
#ifdef TCL_MEM_DEBUG |
748 |
|
if (focusWinPtr != winPtr->dispPtr->focusPtr) { |
749 |
|
printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n"); |
750 |
|
printf("expected %s, got %s\n", |
751 |
|
(focusWinPtr != NULL) ? focusWinPtr->pathName : "??", |
752 |
|
(winPtr->dispPtr->focusPtr != NULL) ? |
753 |
|
winPtr->dispPtr->focusPtr->pathName : "??"); |
754 |
|
} |
755 |
|
#endif |
756 |
|
|
757 |
|
if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) { |
758 |
|
/* |
759 |
|
* Map the x and y coordinates to make sense in the context of |
760 |
|
* the focus window, if possible (make both -1 if the map-from |
761 |
|
* and map-to windows don't share the same screen). |
762 |
|
*/ |
763 |
|
|
764 |
|
if ((focusWinPtr->display != winPtr->display) |
765 |
|
|| (focusWinPtr->screenNum != winPtr->screenNum)) { |
766 |
|
eventPtr->xkey.x = -1; |
767 |
|
eventPtr->xkey.y = -1; |
768 |
|
} else { |
769 |
|
Tk_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY, |
770 |
|
&vRootWidth, &vRootHeight); |
771 |
|
Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY); |
772 |
|
eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX; |
773 |
|
eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - focusY; |
774 |
|
} |
775 |
|
eventPtr->xkey.window = focusWinPtr->window; |
776 |
|
return focusWinPtr; |
777 |
|
} |
778 |
|
|
779 |
|
/* |
780 |
|
* The event doesn't belong to us. Perhaps, due to embedding, it |
781 |
|
* really belongs to someone else. Give the embedding code a chance |
782 |
|
* to redirect the event. |
783 |
|
*/ |
784 |
|
|
785 |
|
TkpRedirectKeyEvent(winPtr, eventPtr); |
786 |
|
return (TkWindow *) NULL; |
787 |
|
} |
788 |
|
|
789 |
|
/* |
790 |
|
*---------------------------------------------------------------------- |
791 |
|
* |
792 |
|
* TkFocusDeadWindow -- |
793 |
|
* |
794 |
|
* This procedure is invoked when it is determined that |
795 |
|
* a window is dead. It cleans up focus-related information |
796 |
|
* about the window. |
797 |
|
* |
798 |
|
* Results: |
799 |
|
* None. |
800 |
|
* |
801 |
|
* Side effects: |
802 |
|
* Various things get cleaned up and recycled. |
803 |
|
* |
804 |
|
*---------------------------------------------------------------------- |
805 |
|
*/ |
806 |
|
|
807 |
|
void |
808 |
|
TkFocusDeadWindow(winPtr) |
809 |
|
register TkWindow *winPtr; /* Information about the window |
810 |
|
* that is being deleted. */ |
811 |
|
{ |
812 |
|
ToplevelFocusInfo *tlFocusPtr, *prevPtr; |
813 |
|
DisplayFocusInfo *displayFocusPtr; |
814 |
|
TkDisplay *dispPtr = winPtr->dispPtr; |
815 |
|
|
816 |
|
/* |
817 |
|
* Search for focus records that refer to this window either as |
818 |
|
* the top-level window or the current focus window. |
819 |
|
*/ |
820 |
|
|
821 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); |
822 |
|
for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr; |
823 |
|
tlFocusPtr != NULL; |
824 |
|
prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) { |
825 |
|
if (winPtr == tlFocusPtr->topLevelPtr) { |
826 |
|
/* |
827 |
|
* The top-level window is the one being deleted: free |
828 |
|
* the focus record and release the focus back to PointerRoot |
829 |
|
* if we acquired it implicitly. |
830 |
|
*/ |
831 |
|
|
832 |
|
if (dispPtr->implicitWinPtr == winPtr) { |
833 |
|
if (dispPtr->focusDebug) { |
834 |
|
printf("releasing focus to root after %s died\n", |
835 |
|
tlFocusPtr->topLevelPtr->pathName); |
836 |
|
} |
837 |
|
dispPtr->implicitWinPtr = NULL; |
838 |
|
displayFocusPtr->focusWinPtr = NULL; |
839 |
|
dispPtr->focusPtr = NULL; |
840 |
|
} |
841 |
|
if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) { |
842 |
|
displayFocusPtr->focusWinPtr = NULL; |
843 |
|
dispPtr->focusPtr = NULL; |
844 |
|
} |
845 |
|
if (prevPtr == NULL) { |
846 |
|
winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr; |
847 |
|
} else { |
848 |
|
prevPtr->nextPtr = tlFocusPtr->nextPtr; |
849 |
|
} |
850 |
|
ckfree((char *) tlFocusPtr); |
851 |
|
break; |
852 |
|
} else if (winPtr == tlFocusPtr->focusWinPtr) { |
853 |
|
/* |
854 |
|
* The deleted window had the focus for its top-level: |
855 |
|
* move the focus to the top-level itself. |
856 |
|
*/ |
857 |
|
|
858 |
|
tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; |
859 |
|
if ((displayFocusPtr->focusWinPtr == winPtr) |
860 |
|
&& !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) { |
861 |
|
if (dispPtr->focusDebug) { |
862 |
|
printf("forwarding focus to %s after %s died\n", |
863 |
|
tlFocusPtr->topLevelPtr->pathName, |
864 |
|
winPtr->pathName); |
865 |
|
} |
866 |
|
GenerateFocusEvents(displayFocusPtr->focusWinPtr, |
867 |
|
tlFocusPtr->topLevelPtr); |
868 |
|
displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; |
869 |
|
dispPtr->focusPtr = tlFocusPtr->topLevelPtr; |
870 |
|
} |
871 |
|
break; |
872 |
|
} |
873 |
|
} |
874 |
|
|
875 |
|
if (displayFocusPtr->focusOnMapPtr == winPtr) { |
876 |
|
displayFocusPtr->focusOnMapPtr = NULL; |
877 |
|
} |
878 |
|
} |
879 |
|
|
880 |
|
/* |
881 |
|
*---------------------------------------------------------------------- |
882 |
|
* |
883 |
|
* GenerateFocusEvents -- |
884 |
|
* |
885 |
|
* This procedure is called to create FocusIn and FocusOut events to |
886 |
|
* move the input focus from one window to another. |
887 |
|
* |
888 |
|
* Results: |
889 |
|
* None. |
890 |
|
* |
891 |
|
* Side effects: |
892 |
|
* FocusIn and FocusOut events are generated. |
893 |
|
* |
894 |
|
*---------------------------------------------------------------------- |
895 |
|
*/ |
896 |
|
|
897 |
|
static void |
898 |
|
GenerateFocusEvents(sourcePtr, destPtr) |
899 |
|
TkWindow *sourcePtr; /* Window that used to have the focus (may |
900 |
|
* be NULL). */ |
901 |
|
TkWindow *destPtr; /* New window to have the focus (may be |
902 |
|
* NULL). */ |
903 |
|
|
904 |
|
{ |
905 |
|
XEvent event; |
906 |
|
TkWindow *winPtr; |
907 |
|
|
908 |
|
winPtr = sourcePtr; |
909 |
|
if (winPtr == NULL) { |
910 |
|
winPtr = destPtr; |
911 |
|
if (winPtr == NULL) { |
912 |
|
return; |
913 |
|
} |
914 |
|
} |
915 |
|
|
916 |
|
event.xfocus.serial = LastKnownRequestProcessed(winPtr->display); |
917 |
|
event.xfocus.send_event = GENERATED_EVENT_MAGIC; |
918 |
|
event.xfocus.display = winPtr->display; |
919 |
|
event.xfocus.mode = NotifyNormal; |
920 |
|
TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn, |
921 |
|
TCL_QUEUE_MARK); |
922 |
|
} |
923 |
|
|
924 |
|
/* |
925 |
|
*---------------------------------------------------------------------- |
926 |
|
* |
927 |
|
* FocusMapProc -- |
928 |
|
* |
929 |
|
* This procedure is called as an event handler for VisibilityNotify |
930 |
|
* events, if a window receives the focus at a time when its |
931 |
|
* toplevel isn't mapped. The procedure is needed because X |
932 |
|
* won't allow the focus to be set to an unmapped window; we |
933 |
|
* detect when the toplevel is mapped and set the focus to it then. |
934 |
|
* |
935 |
|
* Results: |
936 |
|
* None. |
937 |
|
* |
938 |
|
* Side effects: |
939 |
|
* If this is a map event, the focus gets set to the toplevel |
940 |
|
* given by clientData. |
941 |
|
* |
942 |
|
*---------------------------------------------------------------------- |
943 |
|
*/ |
944 |
|
|
945 |
|
static void |
946 |
|
FocusMapProc(clientData, eventPtr) |
947 |
|
ClientData clientData; /* Toplevel window. */ |
948 |
|
XEvent *eventPtr; /* Information about event. */ |
949 |
|
{ |
950 |
|
TkWindow *winPtr = (TkWindow *) clientData; |
951 |
|
DisplayFocusInfo *displayFocusPtr; |
952 |
|
|
953 |
|
if (eventPtr->type == VisibilityNotify) { |
954 |
|
displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, |
955 |
|
winPtr->dispPtr); |
956 |
|
if (winPtr->dispPtr->focusDebug) { |
957 |
|
printf("auto-focussing on %s, force %d\n", winPtr->pathName, |
958 |
|
displayFocusPtr->forceFocus); |
959 |
|
} |
960 |
|
Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask, |
961 |
|
FocusMapProc, clientData); |
962 |
|
displayFocusPtr->focusOnMapPtr = NULL; |
963 |
|
TkSetFocusWin(winPtr, displayFocusPtr->forceFocus); |
964 |
|
} |
965 |
|
} |
966 |
|
|
967 |
|
/* |
968 |
|
*---------------------------------------------------------------------- |
969 |
|
* |
970 |
|
* FindDisplayFocusInfo -- |
971 |
|
* |
972 |
|
* Given an application and a display, this procedure locate the |
973 |
|
* focus record for that combination. If no such record exists, |
974 |
|
* it creates a new record and initializes it. |
975 |
|
* |
976 |
|
* Results: |
977 |
|
* The return value is a pointer to the record. |
978 |
|
* |
979 |
|
* Side effects: |
980 |
|
* A new record will be allocated if there wasn't one already. |
981 |
|
* |
982 |
|
*---------------------------------------------------------------------- |
983 |
|
*/ |
984 |
|
|
985 |
|
static DisplayFocusInfo * |
986 |
|
FindDisplayFocusInfo(mainPtr, dispPtr) |
987 |
|
TkMainInfo *mainPtr; /* Record that identifies a particular |
988 |
|
* application. */ |
989 |
|
TkDisplay *dispPtr; /* Display whose focus information is |
990 |
|
* needed. */ |
991 |
|
{ |
992 |
|
DisplayFocusInfo *displayFocusPtr; |
993 |
|
|
994 |
|
for (displayFocusPtr = mainPtr->displayFocusPtr; |
995 |
|
displayFocusPtr != NULL; |
996 |
|
displayFocusPtr = displayFocusPtr->nextPtr) { |
997 |
|
if (displayFocusPtr->dispPtr == dispPtr) { |
998 |
|
return displayFocusPtr; |
999 |
|
} |
1000 |
|
} |
1001 |
|
|
1002 |
|
/* |
1003 |
|
* The record doesn't exist yet. Make a new one. |
1004 |
|
*/ |
1005 |
|
|
1006 |
|
displayFocusPtr = (DisplayFocusInfo *) ckalloc(sizeof(DisplayFocusInfo)); |
1007 |
|
displayFocusPtr->dispPtr = dispPtr; |
1008 |
|
displayFocusPtr->focusWinPtr = NULL; |
1009 |
|
displayFocusPtr->focusOnMapPtr = NULL; |
1010 |
|
displayFocusPtr->forceFocus = 0; |
1011 |
|
displayFocusPtr->focusSerial = 0; |
1012 |
|
displayFocusPtr->nextPtr = mainPtr->displayFocusPtr; |
1013 |
|
mainPtr->displayFocusPtr = displayFocusPtr; |
1014 |
|
return displayFocusPtr; |
1015 |
|
} |
1016 |
|
|
1017 |
|
/* End of tkfocus.c */ |