836 lines
24 KiB
C
836 lines
24 KiB
C
|
/*
|
|||
|
* 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-1995 Sun Microsystems, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tkFocus.c 1.27 96/02/15 18:53:29
|
|||
|
*/
|
|||
|
|
|||
|
#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 TkFocusInfo {
|
|||
|
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 TkFocusInfo *nextPtr;/* Next in list of all focus records for
|
|||
|
* a given application. */
|
|||
|
} FocusInfo;
|
|||
|
|
|||
|
static int focusDebug = 0;
|
|||
|
|
|||
|
/*
|
|||
|
* 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 void ChangeXFocus _ANSI_ARGS_((TkWindow *topLevelPtr,
|
|||
|
int focus));
|
|||
|
static void FocusMapProc _ANSI_ARGS_((ClientData clientData,
|
|||
|
XEvent *eventPtr));
|
|||
|
static void GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
|
|||
|
TkWindow *destPtr));
|
|||
|
static void SetFocus _ANSI_ARGS_((TkWindow *winPtr, int force));
|
|||
|
|
|||
|
/*
|
|||
|
*--------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_FocusCmd --
|
|||
|
*
|
|||
|
* 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_FocusCmd(clientData, interp, argc, argv)
|
|||
|
ClientData clientData; /* Main window associated with
|
|||
|
* interpreter. */
|
|||
|
Tcl_Interp *interp; /* Current interpreter. */
|
|||
|
int argc; /* Number of arguments. */
|
|||
|
char **argv; /* Argument strings. */
|
|||
|
{
|
|||
|
Tk_Window tkwin = (Tk_Window) clientData;
|
|||
|
TkWindow *winPtr = (TkWindow *) clientData;
|
|||
|
TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
|
|||
|
FocusInfo *focusPtr;
|
|||
|
char c;
|
|||
|
size_t length;
|
|||
|
|
|||
|
/*
|
|||
|
* If invoked with no arguments, just return the current focus window.
|
|||
|
*/
|
|||
|
|
|||
|
if (argc == 1) {
|
|||
|
focusWinPtr = TkGetFocus(winPtr);
|
|||
|
if (focusWinPtr != NULL) {
|
|||
|
#ifdef STk_CODE
|
|||
|
STk_sharp_dot_result(interp, focusWinPtr->pathName);
|
|||
|
#else
|
|||
|
interp->result = focusWinPtr->pathName;
|
|||
|
#endif
|
|||
|
}
|
|||
|
#ifdef STk_CODE
|
|||
|
else
|
|||
|
interp->result = "#f";
|
|||
|
#endif
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If invoked with a single argument beginning with "." then focus
|
|||
|
* on that window.
|
|||
|
*/
|
|||
|
|
|||
|
if (argc == 2) {
|
|||
|
if (argv[1][0] == 0) {
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
if (argv[1][0] == '.') {
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (!(newPtr->flags & TK_ALREADY_DEAD)) {
|
|||
|
SetFocus(newPtr, 0);
|
|||
|
}
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
length = strlen(argv[1]);
|
|||
|
c = argv[1][1];
|
|||
|
if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
|
|||
|
if (argc != 3) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
#ifdef STk_CODE
|
|||
|
argv[0], " :displayof window\"", (char *) NULL);
|
|||
|
#else
|
|||
|
argv[0], " -displayof window\"", (char *) NULL);
|
|||
|
#endif
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
newPtr = TkGetFocus(newPtr);
|
|||
|
if (newPtr != NULL) {
|
|||
|
interp->result = newPtr->pathName;
|
|||
|
}
|
|||
|
} else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
|
|||
|
if (argc != 3) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
#ifdef STk_CODE
|
|||
|
argv[0], " :force window\"", (char *) NULL);
|
|||
|
#else
|
|||
|
argv[0], " -force window\"", (char *) NULL);
|
|||
|
#endif
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
if (argv[2][0] == 0) {
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
SetFocus(newPtr, 1);
|
|||
|
} else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
|
|||
|
if (argc != 3) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
#ifdef STk_CODE
|
|||
|
argv[0], " :lastfor window\"", (char *) NULL);
|
|||
|
#else
|
|||
|
argv[0], " -lastfor window\"", (char *) NULL);
|
|||
|
#endif
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
|
|||
|
if (newPtr == NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
for (topLevelPtr = newPtr; topLevelPtr != NULL;
|
|||
|
topLevelPtr = topLevelPtr->parentPtr) {
|
|||
|
if (topLevelPtr->flags & TK_TOP_LEVEL) {
|
|||
|
for (focusPtr = newPtr->mainPtr->focusPtr; focusPtr != NULL;
|
|||
|
focusPtr = focusPtr->nextPtr) {
|
|||
|
if (focusPtr->topLevelPtr == topLevelPtr) {
|
|||
|
#ifdef STk_CODE
|
|||
|
STk_sharp_dot_result(interp,
|
|||
|
focusPtr->focusWinPtr->pathName);
|
|||
|
#else
|
|||
|
interp->result = focusPtr->focusWinPtr->pathName;
|
|||
|
#endif
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
#ifdef STk_CODE
|
|||
|
STk_sharp_dot_result(interp, topLevelPtr->pathName);
|
|||
|
#else
|
|||
|
interp->result = topLevelPtr->pathName;
|
|||
|
#endif
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
Tcl_AppendResult(interp, "bad option \"", argv[1],
|
|||
|
#ifdef STk_CODE
|
|||
|
"\": must be :displayof, :force, or :lastfor", (char *) NULL);
|
|||
|
#else
|
|||
|
"\": must be -displayof, -force, or -lastfor", (char *) NULL);
|
|||
|
#endif
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
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 or FocusOut 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 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.
|
|||
|
*/
|
|||
|
|
|||
|
FocusInfo *focusPtr;
|
|||
|
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.
|
|||
|
*/
|
|||
|
|
|||
|
if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
|
|||
|
eventPtr->xfocus.send_event = 0;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
/*
|
|||
|
* 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. Also, skip NotifyPointer, NotifyPointerRoot,
|
|||
|
* and NotifyInferior focus events immediately; they're not
|
|||
|
* useful and tend to cause confusion.
|
|||
|
*/
|
|||
|
|
|||
|
if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
|
|||
|
retValue = 0;
|
|||
|
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.
|
|||
|
*/
|
|||
|
|
|||
|
if (!(winPtr->flags & TK_TOP_LEVEL)) {
|
|||
|
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;
|
|||
|
|
|||
|
}
|
|||
|
/*
|
|||
|
* Find the FocusInfo structure for the window, and make a new one
|
|||
|
* if there isn't one already.
|
|||
|
*/
|
|||
|
|
|||
|
for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
|
|||
|
focusPtr = focusPtr->nextPtr) {
|
|||
|
if (focusPtr->topLevelPtr == winPtr) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (focusPtr == NULL) {
|
|||
|
focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
|
|||
|
focusPtr->topLevelPtr = focusPtr->focusWinPtr = winPtr;
|
|||
|
focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
|
|||
|
winPtr->mainPtr->focusPtr = focusPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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 puts
|
|||
|
* the focus back to the place where it was rightfully set.
|
|||
|
*/
|
|||
|
|
|||
|
newFocusPtr = focusPtr->focusWinPtr;
|
|||
|
|
|||
|
delta = eventPtr->xfocus.serial - winPtr->mainPtr->focusSerial;
|
|||
|
if (focusDebug) {
|
|||
|
printf("check event serial %d, delta %d\n",
|
|||
|
(int) eventPtr->xfocus.serial, delta);
|
|||
|
}
|
|||
|
if ((delta < 0) && (winPtr->mainPtr->lastFocusPtr != NULL)) {
|
|||
|
newFocusPtr = winPtr->mainPtr->lastFocusPtr;
|
|||
|
if (focusDebug) {
|
|||
|
printf("reverting to %s instead of %s\n", newFocusPtr->pathName,
|
|||
|
focusPtr->focusWinPtr->pathName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (eventPtr->type == FocusIn) {
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
|
|||
|
dispPtr->focusWinPtr = newFocusPtr;
|
|||
|
dispPtr->implicitWinPtr = NULL;
|
|||
|
if (focusDebug) {
|
|||
|
printf("Focussed on %s\n", newFocusPtr->pathName);
|
|||
|
}
|
|||
|
} else if (eventPtr->type == FocusOut) {
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
|
|||
|
dispPtr->focusWinPtr = NULL;
|
|||
|
dispPtr->implicitWinPtr = NULL;
|
|||
|
if (focusDebug) {
|
|||
|
printf("Unfocussed from %s, detail %d\n", winPtr->pathName,
|
|||
|
eventPtr->xfocus.detail);
|
|||
|
}
|
|||
|
} 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 then mouse enters the window (even though we didn't get
|
|||
|
* a FocusIn event). Watch for this and grab the focus when it
|
|||
|
* happens.
|
|||
|
*/
|
|||
|
|
|||
|
if (eventPtr->xcrossing.focus && (dispPtr->focusWinPtr == NULL)) {
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
|
|||
|
dispPtr->focusWinPtr = newFocusPtr;
|
|||
|
dispPtr->implicitWinPtr = winPtr;
|
|||
|
if (focusDebug) {
|
|||
|
printf("Focussed implicitly on %s\n",
|
|||
|
newFocusPtr->pathName);
|
|||
|
}
|
|||
|
}
|
|||
|
} else if (eventPtr->type == LeaveNotify) {
|
|||
|
/*
|
|||
|
* If the pointer just left a window for which we automatically
|
|||
|
* claimed the focus on enter, generate FocusOut events. Note:
|
|||
|
* dispPtr->implicitWinPtr may not be the same as
|
|||
|
* dispPtr->focusWinPtr (e.g. because the "focus" command was
|
|||
|
* used to redirect the focus after it arrived at
|
|||
|
* dispPtr->implicitWinPtr)!!
|
|||
|
*/
|
|||
|
|
|||
|
if (dispPtr->implicitWinPtr == winPtr) {
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
|
|||
|
dispPtr->focusWinPtr = NULL;
|
|||
|
dispPtr->implicitWinPtr = NULL;
|
|||
|
if (focusDebug) {
|
|||
|
printf("Defocussed implicitly\n");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return retValue;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* SetFocus --
|
|||
|
*
|
|||
|
* 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.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
SetFocus(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. */
|
|||
|
{
|
|||
|
TkDisplay *dispPtr = winPtr->dispPtr;
|
|||
|
FocusInfo *focusPtr;
|
|||
|
TkWindow *topLevelPtr, *topLevelPtr2;
|
|||
|
|
|||
|
if (winPtr == dispPtr->focusWinPtr) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Find the top-level window for winPtr, then find (or create)
|
|||
|
* a record for the top-level.
|
|||
|
*/
|
|||
|
|
|||
|
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_LEVEL) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
|
|||
|
focusPtr = focusPtr->nextPtr) {
|
|||
|
if (focusPtr->topLevelPtr == topLevelPtr) {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (focusPtr == NULL) {
|
|||
|
focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
|
|||
|
focusPtr->topLevelPtr = topLevelPtr;
|
|||
|
focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
|
|||
|
winPtr->mainPtr->focusPtr = focusPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Reset the focus, but only if the application already has the
|
|||
|
* input focus or "force" has been specified.
|
|||
|
*/
|
|||
|
|
|||
|
focusPtr->focusWinPtr = winPtr;
|
|||
|
Tk_MakeWindowExist((Tk_Window) winPtr);
|
|||
|
if (force || ((dispPtr->focusWinPtr != NULL)
|
|||
|
&& (dispPtr->focusWinPtr->mainPtr == winPtr->mainPtr))) {
|
|||
|
/*
|
|||
|
* Reset the focus in X if it has changed top-levels and if the
|
|||
|
* new top-level isn't override-redirect (the only reason to
|
|||
|
* change the X focus is so that the window manager can redecorate
|
|||
|
* the focus window, but if it's override-redirect then it won't
|
|||
|
* be decorated anyway; also, changing the focus to menus causes
|
|||
|
* all sorts of problems with olvwm: the focus gets lost if
|
|||
|
* keyboard traversal is used to move among menus.
|
|||
|
*/
|
|||
|
|
|||
|
if (dispPtr->focusWinPtr != NULL) {
|
|||
|
for (topLevelPtr2 = dispPtr->focusWinPtr;
|
|||
|
(topLevelPtr2 != NULL)
|
|||
|
&& !(topLevelPtr2->flags & TK_TOP_LEVEL);
|
|||
|
topLevelPtr2 = topLevelPtr2->parentPtr) {
|
|||
|
/* Empty loop body. */
|
|||
|
}
|
|||
|
} else {
|
|||
|
topLevelPtr2 = NULL;
|
|||
|
}
|
|||
|
if ((topLevelPtr2 != topLevelPtr)
|
|||
|
&& !(topLevelPtr->atts.override_redirect)) {
|
|||
|
if (dispPtr->focusOnMapPtr != NULL) {
|
|||
|
Tk_DeleteEventHandler((Tk_Window) dispPtr->focusOnMapPtr,
|
|||
|
StructureNotifyMask, FocusMapProc,
|
|||
|
(ClientData) dispPtr->focusOnMapPtr);
|
|||
|
dispPtr->focusOnMapPtr = NULL;
|
|||
|
}
|
|||
|
if (topLevelPtr->flags & TK_MAPPED) {
|
|||
|
ChangeXFocus(topLevelPtr, force);
|
|||
|
} else {
|
|||
|
/*
|
|||
|
* The window isn't mapped, so we can't give it the focus
|
|||
|
* right now. Create an event handler that will give it
|
|||
|
* the focus as soon as it is mapped.
|
|||
|
*/
|
|||
|
|
|||
|
Tk_CreateEventHandler((Tk_Window) topLevelPtr,
|
|||
|
StructureNotifyMask, FocusMapProc,
|
|||
|
(ClientData) topLevelPtr);
|
|||
|
dispPtr->focusOnMapPtr = topLevelPtr;
|
|||
|
dispPtr->forceFocus = force;
|
|||
|
}
|
|||
|
}
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr, winPtr);
|
|||
|
dispPtr->focusWinPtr = winPtr;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Remember the current serial number for the X server and issue
|
|||
|
* a dummy server request. This marks the position at which we
|
|||
|
* changed the focus, so we can distinguish FocusIn and FocusOut
|
|||
|
* events on either side of the mark.
|
|||
|
*/
|
|||
|
|
|||
|
winPtr->mainPtr->lastFocusPtr = winPtr;
|
|||
|
winPtr->mainPtr->focusSerial = NextRequest(winPtr->display);
|
|||
|
XNoOp(winPtr->display);
|
|||
|
if (focusDebug) {
|
|||
|
printf("focus marking for %s at %d\n", winPtr->pathName,
|
|||
|
(int) winPtr->mainPtr->focusSerial);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkGetFocus --
|
|||
|
*
|
|||
|
* 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 *
|
|||
|
TkGetFocus(winPtr)
|
|||
|
TkWindow *winPtr; /* Window that selects an application
|
|||
|
* and a display. */
|
|||
|
{
|
|||
|
TkWindow *focusWinPtr;
|
|||
|
|
|||
|
focusWinPtr = winPtr->dispPtr->focusWinPtr;
|
|||
|
if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
|
|||
|
return focusWinPtr;
|
|||
|
}
|
|||
|
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. */
|
|||
|
{
|
|||
|
FocusInfo *focusPtr, *prevPtr;
|
|||
|
TkDisplay *dispPtr = winPtr->dispPtr;
|
|||
|
|
|||
|
/*
|
|||
|
* Search for focus records that refer to this window either as
|
|||
|
* the top-level window or the current focus window.
|
|||
|
*/
|
|||
|
|
|||
|
for (prevPtr = NULL, focusPtr = winPtr->mainPtr->focusPtr;
|
|||
|
focusPtr != NULL;
|
|||
|
prevPtr = focusPtr, focusPtr = focusPtr->nextPtr) {
|
|||
|
if (winPtr == focusPtr->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 (focusDebug) {
|
|||
|
printf("releasing focus to root after %s died\n",
|
|||
|
focusPtr->topLevelPtr->pathName);
|
|||
|
}
|
|||
|
dispPtr->implicitWinPtr = NULL;
|
|||
|
dispPtr->focusWinPtr = NULL;
|
|||
|
}
|
|||
|
if (dispPtr->focusWinPtr == focusPtr->focusWinPtr) {
|
|||
|
dispPtr->focusWinPtr = NULL;
|
|||
|
}
|
|||
|
if (dispPtr->focusOnMapPtr == focusPtr->topLevelPtr) {
|
|||
|
dispPtr->focusOnMapPtr = NULL;
|
|||
|
}
|
|||
|
if (prevPtr == NULL) {
|
|||
|
winPtr->mainPtr->focusPtr = focusPtr->nextPtr;
|
|||
|
} else {
|
|||
|
prevPtr->nextPtr = focusPtr->nextPtr;
|
|||
|
}
|
|||
|
ckfree((char *) focusPtr);
|
|||
|
break;
|
|||
|
} else if (winPtr == focusPtr->focusWinPtr) {
|
|||
|
/*
|
|||
|
* The deleted window had the focus for its top-level:
|
|||
|
* move the focus to the top-level itself.
|
|||
|
*/
|
|||
|
|
|||
|
focusPtr->focusWinPtr = focusPtr->topLevelPtr;
|
|||
|
if ((dispPtr->focusWinPtr == winPtr)
|
|||
|
&& !(focusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
|
|||
|
if (focusDebug) {
|
|||
|
printf("forwarding focus to %s after %s died\n",
|
|||
|
focusPtr->topLevelPtr->pathName, winPtr->pathName);
|
|||
|
}
|
|||
|
GenerateFocusEvents(dispPtr->focusWinPtr,
|
|||
|
focusPtr->topLevelPtr);
|
|||
|
dispPtr->focusWinPtr = focusPtr->topLevelPtr;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (winPtr->mainPtr->lastFocusPtr == winPtr) {
|
|||
|
winPtr->mainPtr->lastFocusPtr = 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);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* ChangeXFocus --
|
|||
|
*
|
|||
|
* This procedure is invoked to move the official X focus from
|
|||
|
* one top-level to another. We do this when the application
|
|||
|
* changes the focus window from one top-level to another, in
|
|||
|
* order to notify the window manager so that it can highlight
|
|||
|
* the new focus top-level.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* The official X focus window changes; the application's focus
|
|||
|
* window isn't changed by this procedure.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
ChangeXFocus(topLevelPtr, force)
|
|||
|
TkWindow *topLevelPtr; /* Top-level window that is to receive
|
|||
|
* the X focus. */
|
|||
|
int force; /* Non-zero means claim the focus even
|
|||
|
* if it didn't originally belong to
|
|||
|
* topLevelPtr's application. */
|
|||
|
{
|
|||
|
TkDisplay *dispPtr = topLevelPtr->dispPtr;
|
|||
|
TkWindow *winPtr;
|
|||
|
Window focusWindow;
|
|||
|
int dummy;
|
|||
|
Tk_ErrorHandler errHandler;
|
|||
|
|
|||
|
/*
|
|||
|
* If the focus was received implicitly, then there's no advantage
|
|||
|
* in setting an explicit focus; just return.
|
|||
|
*/
|
|||
|
|
|||
|
if (dispPtr->implicitWinPtr != NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Check to make sure that the focus is still in one of the
|
|||
|
* windows of this application. Furthermore, grab the server
|
|||
|
* to make sure that the focus doesn't change in the middle
|
|||
|
* of this operation.
|
|||
|
*/
|
|||
|
|
|||
|
if (!focusDebug) {
|
|||
|
XGrabServer(dispPtr->display);
|
|||
|
}
|
|||
|
if (!force) {
|
|||
|
XGetInputFocus(dispPtr->display, &focusWindow, &dummy);
|
|||
|
winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow);
|
|||
|
if ((winPtr == NULL) || (winPtr->mainPtr != topLevelPtr->mainPtr)) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Tell X to change the focus. Ignore errors that occur when changing
|
|||
|
* the focus: it is still possible that the window we're focussing
|
|||
|
* to could have gotten unmapped, which will generate an error.
|
|||
|
*/
|
|||
|
|
|||
|
errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
|
|||
|
(Tk_ErrorProc *) NULL, (ClientData) NULL);
|
|||
|
XSetInputFocus(dispPtr->display, topLevelPtr->window, RevertToParent,
|
|||
|
CurrentTime);
|
|||
|
Tk_DeleteErrorHandler(errHandler);
|
|||
|
if (focusDebug) {
|
|||
|
printf("Set X focus to %s\n", topLevelPtr->pathName);
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
if (!focusDebug) {
|
|||
|
XUngrabServer(dispPtr->display);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* FocusMapProc --
|
|||
|
*
|
|||
|
* This procedure is called as an event handler for StructureNotify
|
|||
|
* 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;
|
|||
|
TkDisplay *dispPtr = winPtr->dispPtr;
|
|||
|
|
|||
|
if (eventPtr->type == MapNotify) {
|
|||
|
ChangeXFocus(winPtr, dispPtr->forceFocus);
|
|||
|
Tk_DeleteEventHandler((Tk_Window) winPtr, StructureNotifyMask,
|
|||
|
FocusMapProc, clientData);
|
|||
|
dispPtr->focusOnMapPtr = NULL;
|
|||
|
}
|
|||
|
}
|