/* * tkFocus.c -- * * This file contains functions that manage the input focus for Tk. * * Copyright (c) 1990-1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkInt.h" /* * For each top-level window that has ever received the focus, there is a * record of the following type: */ typedef struct TkToplevelFocusInfo { TkWindow *topLevelPtr; /* Information about top-level window. */ TkWindow *focusWinPtr; /* The next time the focus comes to this * top-level, it will be given to this * window. */ struct TkToplevelFocusInfo *nextPtr; /* Next in list of all toplevel focus records * for a given application. */ } ToplevelFocusInfo; /* * One of the following structures exists for each display used by each * application. These are linked together from the TkMainInfo structure. * These structures are needed because it isn't sufficient to store a single * piece of focus information in each display or in each application: we need * the cross-product. There needs to be separate information for each display, * because it's possible to have multiple focus windows active simultaneously * on different displays. There also needs to be separate information for each * application, because of embedding: if an embedded application has the * focus, its container application also has the focus. Thus we keep a list of * structures for each application: the same display can appear in structures * for several applications at once. */ typedef struct TkDisplayFocusInfo { TkDisplay *dispPtr; /* Display that this information pertains * to. */ struct TkWindow *focusWinPtr; /* Window that currently has the focus for * this application on this display, or NULL * if none. */ struct TkWindow *focusOnMapPtr; /* This points to a toplevel window that is * supposed to receive the X input focus as * soon as it is mapped (needed to handle the * fact that X won't allow the focus on an * unmapped window). NULL means no delayed * focus op in progress for this display. */ int forceFocus; /* Associated with focusOnMapPtr: non-zero * means claim the focus even if some other * application currently has it. */ unsigned long focusSerial; /* Serial number of last request this * application made to change the focus on * this display. Used to identify stale focus * notifications coming from the X server. */ struct TkDisplayFocusInfo *nextPtr; /* Next in list of all display focus records * for a given application. */ } DisplayFocusInfo; /* * Debugging support... */ #define DEBUG(dispPtr, arguments) \ if ((dispPtr)->focusDebug) { \ printf arguments; fflush(stdout); \ } /* * Forward declarations for functions defined in this file: */ static DisplayFocusInfo*FindDisplayFocusInfo(TkMainInfo *mainPtr, TkDisplay *dispPtr); static void FocusMapProc(ClientData clientData, XEvent *eventPtr); static void GenerateFocusEvents(TkWindow *sourcePtr, TkWindow *destPtr); /* *-------------------------------------------------------------- * * Tk_FocusObjCmd -- * * This function is invoked to process the "focus" Tcl command. See the * user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ int Tk_FocusObjCmd( ClientData clientData, /* Main window associated with interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { static const char *const focusOptions[] = { "-displayof", "-force", "-lastfor", NULL }; Tk_Window tkwin = (Tk_Window)clientData; TkWindow *winPtr = (TkWindow *)clientData; TkWindow *newPtr, *topLevelPtr; ToplevelFocusInfo *tlFocusPtr; const char *windowName; int index; /* * If invoked with no arguments, just return the current focus window. */ if (objc == 1) { Tk_Window focusWin = (Tk_Window) TkGetFocusWin(winPtr); if (focusWin != NULL) { Tcl_SetObjResult(interp, TkNewWindowObj(focusWin)); } return TCL_OK; } /* * If invoked with a single argument beginning with "." then focus on that * window. */ if (objc == 2) { windowName = Tcl_GetString(objv[1]); /* * The empty string case exists for backwards compatibility. */ if (windowName[0] == '\0') { return TCL_OK; } if (windowName[0] == '.') { newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); if (newPtr == NULL) { return TCL_ERROR; } TkSetFocusWin(newPtr, 0); return TCL_OK; } } /* * We have a subcommand to parse and act upon. */ if (Tcl_GetIndexFromObjStruct(interp, objv[1], focusOptions, sizeof(char *), "option", 0, &index) != TCL_OK) { return TCL_ERROR; } if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "window"); return TCL_ERROR; } switch (index) { case 0: /* -displayof */ windowName = Tcl_GetString(objv[2]); newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); if (newPtr == NULL) { return TCL_ERROR; } newPtr = TkGetFocusWin(newPtr); if (newPtr != NULL) { Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) newPtr)); } break; case 1: /* -force */ windowName = Tcl_GetString(objv[2]); /* * The empty string case exists for backwards compatibility. */ if (windowName[0] == '\0') { return TCL_OK; } newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); if (newPtr == NULL) { return TCL_ERROR; } TkSetFocusWin(newPtr, 1); break; case 2: /* -lastfor */ windowName = Tcl_GetString(objv[2]); newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin); if (newPtr == NULL) { return TCL_ERROR; } for (topLevelPtr = newPtr; topLevelPtr != NULL; topLevelPtr = topLevelPtr->parentPtr) { if (!(topLevelPtr->flags & TK_TOP_HIERARCHY)) { continue; } for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == topLevelPtr) { Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) tlFocusPtr->focusWinPtr)); return TCL_OK; } } Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) topLevelPtr)); return TCL_OK; } break; default: Tcl_Panic("bad const entries to focusOptions in focus command"); } return TCL_OK; } /* *-------------------------------------------------------------- * * TkFocusFilterEvent -- * * This function is invoked by Tk_HandleEvent when it encounters a * FocusIn, FocusOut, Enter, or Leave event. * * Results: * A return value of 1 means that Tk_HandleEvent should process the event * normally (i.e. event handlers should be invoked). A return value of 0 * means that this event should be ignored. * * Side effects: * Additional events may be generated, and the focus may switch. * *-------------------------------------------------------------- */ int TkFocusFilterEvent( TkWindow *winPtr, /* Window that focus event is directed to. */ XEvent *eventPtr) /* FocusIn, FocusOut, Enter, or Leave * event. */ { /* * Design notes: the window manager and X server work together to transfer * the focus among top-level windows. This function takes care of * transferring the focus from a top-level or wrapper window to the actual * window within that top-level that has the focus. We do this by * synthesizing X events to move the focus around. None of the FocusIn and * FocusOut events generated by X are ever used outside of this function; * only the synthesized events get through to the rest of the application. * At one point (e.g. Tk4.0b1) Tk used to call X to move the focus from a * top-level to one of its descendants, then just pass through the events * generated by X. This approach didn't work very well, for a variety of * reasons. For example, if X generates the events they go at the back of * the event queue, which could cause problems if other things have * already happened, such as moving the focus to yet another window. */ ToplevelFocusInfo *tlFocusPtr; DisplayFocusInfo *displayFocusPtr; TkDisplay *dispPtr = winPtr->dispPtr; TkWindow *newFocusPtr; int retValue, delta; /* * If this was a generated event, just turn off the generated flag and * pass the event through to Tk bindings. */ if ((eventPtr->xfocus.send_event & GENERATED_FOCUS_EVENT_MAGIC) == GENERATED_FOCUS_EVENT_MAGIC) { eventPtr->xfocus.send_event &= ~GENERATED_FOCUS_EVENT_MAGIC; return 1; } /* * Check for special events generated by embedded applications to request * the input focus. If this is one of those events, make the change in * focus and return without any additional processing of the event (note: * the "detail" field of the event indicates whether to claim the focus * even if we don't already have it). */ if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS) && (eventPtr->type == FocusIn)) { TkSetFocusWin(winPtr, eventPtr->xfocus.detail); return 0; } /* * This was not a generated event. We'll return 1 (so that the event will * be processed) if it's an Enter or Leave event, and 0 (so that the event * won't be processed) if it's a FocusIn or FocusOut event. */ retValue = 0; displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); if (eventPtr->type == FocusIn) { /* * Skip FocusIn events that cause confusion * NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur on * windows in between the origin and destination of the focus * change. For FocusIn we may see this when focus goes into an * embedded child. We don't care about this, although we may end * up getting a NotifyPointer later. * NotifyInferior - focus is coming to us from an embedded child. When * focus is on an embedded 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 then just ignore the event. */ winPtr = TkWmFocusToplevel(winPtr); if (winPtr == NULL) { return retValue; } /* * If there is a grab in effect and this window is outside the grabbed * tree, then ignore the event. */ if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) { return retValue; } /* * It is possible that there were outstanding FocusIn and FocusOut events * on their way to us at the time the focus was changed internally with * the "focus" command. If so, these events could potentially cause us to * lose the focus (switch it to the window of the last FocusIn event) even * though the focus change occurred after those events. The following code * detects this and ignores the stale events. * * Note: the focusSerial is only generated by TkpChangeFocus, whereas in * Tk 4.2 there was always a nop marker generated. */ delta = eventPtr->xfocus.serial - displayFocusPtr->focusSerial; if (delta < 0) { return retValue; } /* * Find the ToplevelFocusInfo structure for the window, and make a new one * if there isn't one already. */ for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == winPtr) { break; } } if (tlFocusPtr == NULL) { tlFocusPtr = (ToplevelFocusInfo *)ckalloc(sizeof(ToplevelFocusInfo)); tlFocusPtr->topLevelPtr = tlFocusPtr->focusWinPtr = winPtr; tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; winPtr->mainPtr->tlFocusPtr = tlFocusPtr; } newFocusPtr = tlFocusPtr->focusWinPtr; /* * Ignore event if newFocus window is already dead! */ if (newFocusPtr->flags & TK_ALREADY_DEAD) { return retValue; } if (eventPtr->type == FocusIn) { GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); displayFocusPtr->focusWinPtr = newFocusPtr; dispPtr->focusPtr = newFocusPtr; /* * NotifyPointer gets set when the focus has been set to the root * window but we have the pointer. We'll treat this like an implicit * focus in event so that upon Leave events we release focus. */ if (!(winPtr->flags & TK_EMBEDDED)) { if (eventPtr->xfocus.detail == NotifyPointer) { dispPtr->implicitWinPtr = winPtr; } else { dispPtr->implicitWinPtr = NULL; } } } else if (eventPtr->type == FocusOut) { GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); /* * Reset dispPtr->focusPtr, but only if it currently is the same as * this application's focusWinPtr: this check is needed to handle * embedded applications in the same process. */ if (dispPtr->focusPtr == displayFocusPtr->focusWinPtr) { dispPtr->focusPtr = NULL; } displayFocusPtr->focusWinPtr = NULL; } else if (eventPtr->type == EnterNotify) { /* * If there is no window manager, or if the window manager isn't * moving the focus around (e.g. the disgusting "NoTitleFocus" option * has been selected in twm), then we won't get FocusIn or FocusOut * events. Instead, the "focus" field will be set in an Enter event to * indicate that we've already got the focus when the mouse enters the * window (even though we didn't get a FocusIn event). Watch for this * and grab the focus when it happens. Note: if this is an embedded * application then don't accept the focus implicitly like this; the * container application will give us the focus explicitly if it wants * us to have it. */ if (eventPtr->xcrossing.focus && (displayFocusPtr->focusWinPtr == NULL) && !(winPtr->flags & TK_EMBEDDED)) { DEBUG(dispPtr, ("Focussed implicitly on %s\n", newFocusPtr->pathName)); GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr); displayFocusPtr->focusWinPtr = newFocusPtr; dispPtr->implicitWinPtr = winPtr; dispPtr->focusPtr = newFocusPtr; } } else if (eventPtr->type == LeaveNotify) { /* * If the pointer just left a window for which we automatically * claimed the focus on enter, move the focus back to the root window, * where it was before we claimed it above. Note: * dispPtr->implicitWinPtr may not be the same as * displayFocusPtr->focusWinPtr (e.g. because the "focus" command was * used to redirect the focus after it arrived at * dispPtr->implicitWinPtr)!! In addition, we generate events because * the window manager won't give us a FocusOut event when we focus on * the root. */ if ((dispPtr->implicitWinPtr != NULL) && !(winPtr->flags & TK_EMBEDDED)) { DEBUG(dispPtr, ("Defocussed implicit Async from %s\n", displayFocusPtr->focusWinPtr->pathName)); GenerateFocusEvents(displayFocusPtr->focusWinPtr, NULL); XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot, CurrentTime); displayFocusPtr->focusWinPtr = NULL; dispPtr->implicitWinPtr = NULL; dispPtr->focusPtr = NULL; } } return retValue; } /* *---------------------------------------------------------------------- * * TkSetFocusWin -- * * This function is invoked to change the focus window for a given * display in a given application. * * Results: * None. * * Side effects: * Event handlers may be invoked to process the change of * focus. * *---------------------------------------------------------------------- */ void TkSetFocusWin( TkWindow *winPtr, /* Window that is to be the new focus for its * display and application. */ int force) /* If non-zero, set the X focus to this window * even if the application doesn't currently * have the X focus. */ { ToplevelFocusInfo *tlFocusPtr; DisplayFocusInfo *displayFocusPtr; TkWindow *topLevelPtr; int allMapped, serial; /* * Don't set focus if window is already dead. [Bug 3574708] */ if (winPtr->flags & TK_ALREADY_DEAD) { return; } /* * Get the current focus window with the same display and application * as winPtr. */ displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); /* * Do nothing if the window already has focus and force is not set. If * force is set, we need to grab the focus, since under Windows or macOS * this may involve taking control away from another application. */ if (winPtr == displayFocusPtr->focusWinPtr && !force) { return; } /* * Find the toplevel window for winPtr, then find (or create) a record * for the toplevel. Also see whether winPtr and all its ancestors are * mapped. */ allMapped = 1; for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) { if (topLevelPtr == NULL) { /* * The window is being deleted. No point in worrying about giving * it the focus. */ return; } if (!(topLevelPtr->flags & TK_MAPPED)) { allMapped = 0; } if (topLevelPtr->flags & TK_TOP_HIERARCHY) { break; } } /* * If any ancestor of the new focus window isn't mapped, then we can't set * focus for it (X will generate an error, for example). Instead, create * an event handler that will set the focus to this window once it gets * mapped. At the same time, delete any old handler that might be around; * it's no longer relevant. */ if (displayFocusPtr->focusOnMapPtr != NULL) { Tk_DeleteEventHandler((Tk_Window) displayFocusPtr->focusOnMapPtr, VisibilityChangeMask, FocusMapProc, displayFocusPtr->focusOnMapPtr); displayFocusPtr->focusOnMapPtr = NULL; } if (!allMapped) { Tk_CreateEventHandler((Tk_Window) winPtr, VisibilityChangeMask, FocusMapProc, winPtr); displayFocusPtr->focusOnMapPtr = winPtr; displayFocusPtr->forceFocus = force; return; } for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == topLevelPtr) { break; } } if (tlFocusPtr == NULL) { tlFocusPtr = (ToplevelFocusInfo *)ckalloc(sizeof(ToplevelFocusInfo)); tlFocusPtr->topLevelPtr = topLevelPtr; tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; winPtr->mainPtr->tlFocusPtr = tlFocusPtr; } tlFocusPtr->focusWinPtr = winPtr; if (topLevelPtr->flags & TK_EMBEDDED && (displayFocusPtr->focusWinPtr == NULL)) { /* * We are assigning focus to an embedded toplevel. The platform * specific function TkpClaimFocus needs to handle the job of * assigning focus to the container, since we have no way to find the * container. */ TkpClaimFocus(topLevelPtr, force); } else if ((displayFocusPtr->focusWinPtr != NULL) || force) { /* * If we are forcing removal of focus from a container hosting a * toplevel from a different application, clear the focus in that * application. */ if (force) { TkWindow *focusPtr = winPtr->dispPtr->focusPtr; if (focusPtr && focusPtr->mainPtr != winPtr->mainPtr) { DisplayFocusInfo *displayFocusPtr2 = FindDisplayFocusInfo( focusPtr->mainPtr, focusPtr->dispPtr); displayFocusPtr2->focusWinPtr = NULL; } } /* * Call the platform specific function TkpChangeFocus to move the * window manager's focus to a new toplevel. */ serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force); if (serial != 0) { displayFocusPtr->focusSerial = serial; } GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr); displayFocusPtr->focusWinPtr = winPtr; winPtr->dispPtr->focusPtr = winPtr; } } /* *---------------------------------------------------------------------- * * TkGetFocusWin -- * * Given a window, this function returns the current focus window for its * application and display. * * Results: * The return value is a pointer to the window that currently has the * input focus for the specified application and display, or NULL if * none. * * Side effects: * None. * *---------------------------------------------------------------------- */ TkWindow * TkGetFocusWin( TkWindow *winPtr) /* Window that selects an application and a * display. */ { DisplayFocusInfo *displayFocusPtr; if (winPtr == NULL) { return NULL; } displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); return displayFocusPtr->focusWinPtr; } /* *---------------------------------------------------------------------- * * TkFocusKeyEvent -- * * Given a window and a key press or release event that arrived for the * window, use information about the keyboard focus to compute which * window should really get the event. In addition, update the event to * refer to its new window. * * Results: * The return value is a pointer to the window that has the input focus * in winPtr's application, or NULL if winPtr's application doesn't have * the input focus. If a non-NULL value is returned, eventPtr will be * updated to refer properly to the focus window. * * Side effects: * None. * *---------------------------------------------------------------------- */ TkWindow * TkFocusKeyEvent( TkWindow *winPtr, /* Window that selects an application and a * display. */ XEvent *eventPtr) /* X event to redirect (should be KeyPress or * KeyRelease). */ { DisplayFocusInfo *displayFocusPtr; TkWindow *focusWinPtr; int focusX, focusY; displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); focusWinPtr = displayFocusPtr->focusWinPtr; /* * The code below is a debugging aid to make sure that dispPtr->focusPtr * is kept properly in sync with the "truth", which is the value in * displayFocusPtr->focusWinPtr. */ #ifdef TCL_MEM_DEBUG if (focusWinPtr != winPtr->dispPtr->focusPtr) { printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n"); printf("expected %s, got %s\n", (focusWinPtr != NULL) ? focusWinPtr->pathName : "??", (winPtr->dispPtr->focusPtr != NULL) ? winPtr->dispPtr->focusPtr->pathName : "??"); } #endif if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) { /* * Map the x and y coordinates to make sense in the context of the * focus window, if possible (make both -1 if the map-from and map-to * windows don't share the same screen). */ if ((focusWinPtr->display != winPtr->display) || (focusWinPtr->screenNum != winPtr->screenNum)) { eventPtr->xkey.x = -1; eventPtr->xkey.y = -1; } else { Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY); eventPtr->xkey.x = eventPtr->xkey.x_root - focusX; eventPtr->xkey.y = eventPtr->xkey.y_root - focusY; } eventPtr->xkey.window = focusWinPtr->window; return focusWinPtr; } /* * The event doesn't belong to us. Perhaps, due to embedding, it really * belongs to someone else. Give the embedding code a chance to redirect * the event. */ TkpRedirectKeyEvent(winPtr, eventPtr); return NULL; } /* *---------------------------------------------------------------------- * * TkFocusDeadWindow -- * * This function is invoked when it is determined that a window is dead. * It cleans up focus-related information about the window. * * Results: * None. * * Side effects: * Various things get cleaned up and recycled. * *---------------------------------------------------------------------- */ void TkFocusDeadWindow( TkWindow *winPtr) /* Information about the window that is being * deleted. */ { ToplevelFocusInfo *tlFocusPtr, *prevPtr; DisplayFocusInfo *displayFocusPtr; TkDisplay *dispPtr = winPtr->dispPtr; int noMatch = 1; /* * Certain special windows like those used for send and clipboard have no * mainPtr. */ if (winPtr->mainPtr == NULL) { return; } /* * Search for focus records that refer to this window either as the * top-level window or the current focus window. */ displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) { if (winPtr == tlFocusPtr->topLevelPtr) { /* * The top-level window is the one being deleted: free the focus * record and release the focus back to PointerRoot if we acquired * it implicitly. */ if (dispPtr->implicitWinPtr == winPtr) { DEBUG(dispPtr, ("releasing focus to root after %s died\n", tlFocusPtr->topLevelPtr->pathName)); dispPtr->implicitWinPtr = NULL; displayFocusPtr->focusWinPtr = NULL; dispPtr->focusPtr = NULL; } if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) { displayFocusPtr->focusWinPtr = NULL; dispPtr->focusPtr = NULL; } if (prevPtr == NULL) { winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr; } else { prevPtr->nextPtr = tlFocusPtr->nextPtr; } ckfree(tlFocusPtr); noMatch = 0; break; } else if (winPtr == tlFocusPtr->focusWinPtr) { /* * The deleted window had the focus for its top-level: move the * focus to the top-level itself. */ tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; if ((displayFocusPtr->focusWinPtr == winPtr) && !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) { DEBUG(dispPtr, ("forwarding focus to %s after %s died\n", tlFocusPtr->topLevelPtr->pathName, winPtr->pathName)); GenerateFocusEvents(displayFocusPtr->focusWinPtr, tlFocusPtr->topLevelPtr); displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr; dispPtr->focusPtr = tlFocusPtr->topLevelPtr; } noMatch = 0; break; } } /* * Occasionally, things can become unsynchronized. Move them back into * synch now. [Bug 2496114] */ if (displayFocusPtr->focusWinPtr == winPtr) { DEBUG(dispPtr, ("focus cleared after %s died\n", winPtr->pathName)); displayFocusPtr->focusWinPtr = NULL; } if (displayFocusPtr->focusOnMapPtr == winPtr) { displayFocusPtr->focusOnMapPtr = NULL; } /* * It may happen that the search above for focus records that refer * to this window did not find any match. In such a case, when the * dead window had the focus, release it. */ if (noMatch && (dispPtr->focusPtr == winPtr)) { dispPtr->focusPtr = NULL; } } /* *---------------------------------------------------------------------- * * GenerateFocusEvents -- * * This function is called to create FocusIn and FocusOut events to move * the input focus from one window to another. * * Results: * None. * * Side effects: * FocusIn and FocusOut events are generated. * *---------------------------------------------------------------------- */ static void GenerateFocusEvents( TkWindow *sourcePtr, /* Window that used to have the focus (may be * NULL). */ TkWindow *destPtr) /* New window to have the focus (may be * NULL). */ { XEvent event; TkWindow *winPtr; winPtr = sourcePtr; if (winPtr == NULL) { winPtr = destPtr; if (winPtr == NULL) { return; } } event.xfocus.serial = LastKnownRequestProcessed(winPtr->display); event.xfocus.send_event = GENERATED_FOCUS_EVENT_MAGIC; event.xfocus.display = winPtr->display; event.xfocus.mode = NotifyNormal; TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn, TCL_QUEUE_MARK); } /* *---------------------------------------------------------------------- * * FocusMapProc -- * * This function is called as an event handler for VisibilityNotify * events, if a window receives the focus at a time when its toplevel * isn't mapped. The function is needed because X won't allow the focus * to be set to an unmapped window; we detect when the toplevel is mapped * and set the focus to it then. * * Results: * None. * * Side effects: * If this is a map event, the focus gets set to the toplevel given by * clientData. * *---------------------------------------------------------------------- */ static void FocusMapProc( ClientData clientData, /* Toplevel window. */ XEvent *eventPtr) /* Information about event. */ { TkWindow *winPtr = (TkWindow *)clientData; DisplayFocusInfo *displayFocusPtr; if (eventPtr->type == VisibilityNotify) { displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); DEBUG(winPtr->dispPtr, ("auto-focussing on %s, force %d\n", winPtr->pathName, displayFocusPtr->forceFocus)); Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask, FocusMapProc, clientData); displayFocusPtr->focusOnMapPtr = NULL; TkSetFocusWin(winPtr, displayFocusPtr->forceFocus); } } /* *---------------------------------------------------------------------- * * FindDisplayFocusInfo -- * * Given an application and a display, this function locate the focus * record for that combination. If no such record exists, it creates a * new record and initializes it. * * Results: * The return value is a pointer to the record. * * Side effects: * A new record will be allocated if there wasn't one already. * *---------------------------------------------------------------------- */ static DisplayFocusInfo * FindDisplayFocusInfo( TkMainInfo *mainPtr, /* Record that identifies a particular * application. */ TkDisplay *dispPtr) /* Display whose focus information is * needed. */ { DisplayFocusInfo *displayFocusPtr; for (displayFocusPtr = mainPtr->displayFocusPtr; displayFocusPtr != NULL; displayFocusPtr = displayFocusPtr->nextPtr) { if (displayFocusPtr->dispPtr == dispPtr) { return displayFocusPtr; } } /* * The record doesn't exist yet. Make a new one. */ displayFocusPtr = (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; } /* *---------------------------------------------------------------------- * * TkFocusFree -- * * Free resources associated with maintaining the focus. * * Results: * None. * * Side effects: * This mainPtr should no longer access focus information. * *---------------------------------------------------------------------- */ void TkFocusFree( TkMainInfo *mainPtr) /* Record that identifies a particular * application. */ { while (mainPtr->displayFocusPtr != NULL) { DisplayFocusInfo *displayFocusPtr = mainPtr->displayFocusPtr; mainPtr->displayFocusPtr = mainPtr->displayFocusPtr->nextPtr; ckfree(displayFocusPtr); } while (mainPtr->tlFocusPtr != NULL) { ToplevelFocusInfo *tlFocusPtr = mainPtr->tlFocusPtr; mainPtr->tlFocusPtr = mainPtr->tlFocusPtr->nextPtr; ckfree(tlFocusPtr); } } /* *---------------------------------------------------------------------- * * TkFocusSplit -- * * Adjust focus window for a newly managed toplevel, thus splitting the * toplevel into two toplevels. * * Results: * None. * * Side effects: * A new record is allocated for the new toplevel window. * *---------------------------------------------------------------------- */ void TkFocusSplit( TkWindow *winPtr) /* Window is the new toplevel. Any focus point * at or below window must be moved to this * new toplevel. */ { ToplevelFocusInfo *tlFocusPtr; TkWindow *topLevelPtr, *subWinPtr; FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr); /* * Find the top-level window for winPtr, then find (or create) a record * for the top-level. Also see whether winPtr and all its ancestors are * mapped. */ for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) { if (topLevelPtr == NULL) { /* * The window is being deleted. No point in worrying about giving * it the focus. */ return; } if (topLevelPtr->flags & TK_TOP_HIERARCHY) { break; } } /* * Search all focus records to find child windows of winPtr. */ for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->topLevelPtr == topLevelPtr) { break; } } if (tlFocusPtr == NULL) { /* * No focus record for this toplevel, nothing to do. */ return; } /* * See if current focusWin is child of the new toplevel. */ for (subWinPtr = tlFocusPtr->focusWinPtr; subWinPtr && subWinPtr != winPtr && subWinPtr != topLevelPtr; subWinPtr = subWinPtr->parentPtr) { /* EMPTY */ } if (subWinPtr == winPtr) { /* * Move focus to new toplevel. */ ToplevelFocusInfo *newTlFocusPtr = (ToplevelFocusInfo *)ckalloc(sizeof(ToplevelFocusInfo)); newTlFocusPtr->topLevelPtr = winPtr; newTlFocusPtr->focusWinPtr = tlFocusPtr->focusWinPtr; newTlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr; winPtr->mainPtr->tlFocusPtr = newTlFocusPtr; /* * Move old toplevel's focus to the toplevel itself. */ tlFocusPtr->focusWinPtr = topLevelPtr; } /* * If it's not, then let focus progress naturally. */ } /* *---------------------------------------------------------------------- * * TkFocusJoin -- * * Remove the focus record for this window that is no longer managed * * Results: * None. * * Side effects: * A tlFocusPtr record is removed * *---------------------------------------------------------------------- */ void TkFocusJoin( TkWindow *winPtr) /* Window is no longer a toplevel. */ { ToplevelFocusInfo *tlFocusPtr, *tmpPtr; /* * Remove old toplevel record */ if (winPtr && winPtr->mainPtr && winPtr->mainPtr->tlFocusPtr && winPtr->mainPtr->tlFocusPtr->topLevelPtr == winPtr) { tmpPtr = winPtr->mainPtr->tlFocusPtr; winPtr->mainPtr->tlFocusPtr = tmpPtr->nextPtr; ckfree(tmpPtr); } else if (winPtr && winPtr->mainPtr) { for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL; tlFocusPtr = tlFocusPtr->nextPtr) { if (tlFocusPtr->nextPtr && tlFocusPtr->nextPtr->topLevelPtr == winPtr) { tmpPtr = tlFocusPtr->nextPtr; tlFocusPtr->nextPtr = tmpPtr->nextPtr; ckfree(tmpPtr); break; } } } } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */