OSDN Git Service

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