/* * 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. * * SCCS: @(#) tkFocus.c 1.48 97/10/31 09:55:22 */ #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; /* * Global used for debugging. */ int tclFocusDebug = 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 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)); 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; ToplevelFocusInfo *tlFocusPtr; char c; size_t length; /* * If invoked with no arguments, just return the current focus window. */ if (argc == 1) { focusWinPtr = TkGetFocusWin(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 = TkGetFocusWin(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 (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == topLevelPtr) { #ifdef STk_CODE STk_sharp_dot_result(interp, tlFocusPtr->focusWinPtr->pathName); #else interp->result = tlFocusPtr->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, 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)) { SetFocus(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 (tclFocusDebug) { 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 (tclFocusDebug) { printf("Defocussed implicit Async\n"); } GenerateFocusEvents(displayFocusPtr->focusWinPtr, (TkWindow *) NULL); XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, CurrentTime); displayFocusPtr->focusWinPtr = NULL; dispPtr->implicitWinPtr = NULL; } } 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. */ { ToplevelFocusInfo *tlFocusPtr; DisplayFocusInfo *displayFocusPtr; TkWindow *topLevelPtr; int allMapped, serial; displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); if (winPtr == displayFocusPtr->focusWinPtr) { 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 (tclFocusDebug) { 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 (tclFocusDebug) { 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 (tclFocusDebug) { printf("auto-focussing on %s, force %d\n", winPtr->pathName, displayFocusPtr->forceFocus); } Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask, FocusMapProc, clientData); displayFocusPtr->focusOnMapPtr = NULL; SetFocus(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; }