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