tkFocus.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:33k
源码类别:

通讯编程

开发平台:

Visual C++

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