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