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-1995 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 * SCCS: @(#) tkFocus.c 1.27 96/02/15 18:53:29
14 */
15
16 #include "tkInt.h"
17
18 /*
19 * For each top-level window that has ever received the focus, there
20 * is a record of the following type:
21 */
22
23 typedef struct TkFocusInfo {
24 TkWindow *topLevelPtr; /* Information about top-level window. */
25 TkWindow *focusWinPtr; /* The next time the focus comes to this
26 * top-level, it will be given to this
27 * window. */
28 struct TkFocusInfo *nextPtr;/* Next in list of all focus records for
29 * a given application. */
30 } FocusInfo;
31
32 static int focusDebug = 0;
33
34 /*
35 * The following magic value is stored in the "send_event" field of
36 * FocusIn and FocusOut events that are generated in this file. This
37 * allows us to separate "real" events coming from the server from
38 * those that we generated.
39 */
40
41 #define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac)
42
43 /*
44 * Forward declarations for procedures defined in this file:
45 */
46
47
48 static void ChangeXFocus _ANSI_ARGS_((TkWindow *topLevelPtr,
49 int focus));
50 static void FocusMapProc _ANSI_ARGS_((ClientData clientData,
51 XEvent *eventPtr));
52 static void GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
53 TkWindow *destPtr));
54 static void SetFocus _ANSI_ARGS_((TkWindow *winPtr, int force));
55
56 /*
57 *--------------------------------------------------------------
58 *
59 * Tk_FocusCmd --
60 *
61 * This procedure is invoked to process the "focus" Tcl command.
62 * See the user documentation for details on what it does.
63 *
64 * Results:
65 * A standard Tcl result.
66 *
67 * Side effects:
68 * See the user documentation.
69 *
70 *--------------------------------------------------------------
71 */
72
73 int
Tk_FocusCmd(clientData,interp,argc,argv)74 Tk_FocusCmd(clientData, interp, argc, argv)
75 ClientData clientData; /* Main window associated with
76 * interpreter. */
77 Tcl_Interp *interp; /* Current interpreter. */
78 int argc; /* Number of arguments. */
79 char **argv; /* Argument strings. */
80 {
81 Tk_Window tkwin = (Tk_Window) clientData;
82 TkWindow *winPtr = (TkWindow *) clientData;
83 TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
84 FocusInfo *focusPtr;
85 char c;
86 size_t length;
87
88 /*
89 * If invoked with no arguments, just return the current focus window.
90 */
91
92 if (argc == 1) {
93 focusWinPtr = TkGetFocus(winPtr);
94 if (focusWinPtr != NULL) {
95 interp->result = focusWinPtr->pathName;
96 }
97 return TCL_OK;
98 }
99
100 /*
101 * If invoked with a single argument beginning with "." then focus
102 * on that window.
103 */
104
105 if (argc == 2) {
106 if (argv[1][0] == 0) {
107 return TCL_OK;
108 }
109 if (argv[1][0] == '.') {
110 newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
111 if (newPtr == NULL) {
112 return TCL_ERROR;
113 }
114 if (!(newPtr->flags & TK_ALREADY_DEAD)) {
115 SetFocus(newPtr, 0);
116 }
117 return TCL_OK;
118 }
119 }
120
121 length = strlen(argv[1]);
122 c = argv[1][1];
123 if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
124 if (argc != 3) {
125 Tcl_AppendResult(interp, "wrong # args: should be \"",
126 argv[0], " -displayof window\"", (char *) NULL);
127 return TCL_ERROR;
128 }
129 newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
130 if (newPtr == NULL) {
131 return TCL_ERROR;
132 }
133 newPtr = TkGetFocus(newPtr);
134 if (newPtr != NULL) {
135 interp->result = newPtr->pathName;
136 }
137 } else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
138 if (argc != 3) {
139 Tcl_AppendResult(interp, "wrong # args: should be \"",
140 argv[0], " -force window\"", (char *) NULL);
141 return TCL_ERROR;
142 }
143 if (argv[2][0] == 0) {
144 return TCL_OK;
145 }
146 newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
147 if (newPtr == NULL) {
148 return TCL_ERROR;
149 }
150 SetFocus(newPtr, 1);
151 } else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
152 if (argc != 3) {
153 Tcl_AppendResult(interp, "wrong # args: should be \"",
154 argv[0], " -lastfor window\"", (char *) NULL);
155 return TCL_ERROR;
156 }
157 newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
158 if (newPtr == NULL) {
159 return TCL_ERROR;
160 }
161 for (topLevelPtr = newPtr; topLevelPtr != NULL;
162 topLevelPtr = topLevelPtr->parentPtr) {
163 if (topLevelPtr->flags & TK_TOP_LEVEL) {
164 for (focusPtr = newPtr->mainPtr->focusPtr; focusPtr != NULL;
165 focusPtr = focusPtr->nextPtr) {
166 if (focusPtr->topLevelPtr == topLevelPtr) {
167 interp->result = focusPtr->focusWinPtr->pathName;
168 return TCL_OK;
169 }
170 }
171 interp->result = topLevelPtr->pathName;
172 return TCL_OK;
173 }
174 }
175 } else {
176 Tcl_AppendResult(interp, "bad option \"", argv[1],
177 "\": must be -displayof, -force, or -lastfor", (char *) NULL);
178 return TCL_ERROR;
179 }
180 return TCL_OK;
181 }
182
183 /*
184 *--------------------------------------------------------------
185 *
186 * TkFocusFilterEvent --
187 *
188 * This procedure is invoked by Tk_HandleEvent when it encounters
189 * a FocusIn, FocusOut, Enter, or Leave event.
190 *
191 * Results:
192 * A return value of 1 means that Tk_HandleEvent should process
193 * the event normally (i.e. event handlers should be invoked).
194 * A return value of 0 means that this event should be ignored.
195 *
196 * Side effects:
197 * Additional events may be generated, and the focus may switch.
198 *
199 *--------------------------------------------------------------
200 */
201
202 int
TkFocusFilterEvent(winPtr,eventPtr)203 TkFocusFilterEvent(winPtr, eventPtr)
204 TkWindow *winPtr; /* Window that focus event is directed to. */
205 XEvent *eventPtr; /* FocusIn or FocusOut event. */
206 {
207 /*
208 * Design notes: the window manager and X server work together to
209 * transfer the focus among top-level windows. This procedure takes
210 * care of transferring the focus from a top-level window to the
211 * actual window within that top-level that has the focus. We
212 * do this by synthesizing X events to move the focus around. None
213 * of the FocusIn and FocusOut events generated by X are ever used
214 * outside of this procedure; only the synthesized events get through
215 * to the rest of the application. At one point (e.g. Tk4.0b1) Tk
216 * used to call X to move the focus from a top-level to one of its
217 * descendants, then just pass through the events generated by X.
218 * This approach didn't work very well, for a variety of reasons.
219 * For example, if X generates the events they go at the back of
220 * the event queue, which could cause problems if other things
221 * have already happened, such as moving the focus to yet another
222 * window.
223 */
224
225 FocusInfo *focusPtr;
226 TkDisplay *dispPtr = winPtr->dispPtr;
227 TkWindow *newFocusPtr;
228 int retValue, delta;
229
230 /*
231 * If this was a generated event, just turn off the generated
232 * flag and pass the event through.
233 */
234
235 if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
236 eventPtr->xfocus.send_event = 0;
237 return 1;
238 }
239
240 /*
241 * This was not a generated event. We'll return 1 (so that the
242 * event will be processed) if it's an Enter or Leave event, and
243 * 0 (so that the event won't be processed) if it's a FocusIn or
244 * FocusOut event. Also, skip NotifyPointer, NotifyPointerRoot,
245 * and NotifyInferior focus events immediately; they're not
246 * useful and tend to cause confusion.
247 */
248
249 if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
250 retValue = 0;
251 if ((eventPtr->xfocus.detail == NotifyPointer)
252 || (eventPtr->xfocus.detail == NotifyPointerRoot)
253 || (eventPtr->xfocus.detail == NotifyInferior)) {
254 return retValue;
255 }
256 } else {
257 retValue = 1;
258 if (eventPtr->xcrossing.detail == NotifyInferior) {
259 return retValue;
260 }
261 }
262
263 /*
264 * If winPtr isn't a top-level window than just ignore the event.
265 */
266
267 if (!(winPtr->flags & TK_TOP_LEVEL)) {
268 return retValue;
269 }
270
271 /*
272 * If there is a grab in effect and this window is outside the
273 * grabbed tree, then ignore the event.
274 */
275
276 if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) {
277 return retValue;
278 }
279
280 /*
281 * Find the FocusInfo structure for the window, and make a new one
282 * if there isn't one already.
283 */
284
285 for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
286 focusPtr = focusPtr->nextPtr) {
287 if (focusPtr->topLevelPtr == winPtr) {
288 break;
289 }
290 }
291 if (focusPtr == NULL) {
292 focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
293 focusPtr->topLevelPtr = focusPtr->focusWinPtr = winPtr;
294 focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
295 winPtr->mainPtr->focusPtr = focusPtr;
296 }
297
298 /*
299 * It is possible that there were outstanding FocusIn and FocusOut
300 * events on their way to us at the time the focus was changed
301 * internally with the "focus" command. If so, these events could
302 * potentially cause us to lose the focus (switch it to the window
303 * of the last FocusIn event) even though the focus change occurred
304 * after those events. The following code detects this and puts
305 * the focus back to the place where it was rightfully set.
306 */
307
308 newFocusPtr = focusPtr->focusWinPtr;
309 delta = eventPtr->xfocus.serial - winPtr->mainPtr->focusSerial;
310 if (focusDebug) {
311 printf("check event serial %d, delta %d\n",
312 (int) eventPtr->xfocus.serial, delta);
313 }
314 if ((delta < 0) && (winPtr->mainPtr->lastFocusPtr != NULL)) {
315 newFocusPtr = winPtr->mainPtr->lastFocusPtr;
316 if (focusDebug) {
317 printf("reverting to %s instead of %s\n", newFocusPtr->pathName,
318 focusPtr->focusWinPtr->pathName);
319 }
320 }
321
322 if (eventPtr->type == FocusIn) {
323 GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
324 dispPtr->focusWinPtr = newFocusPtr;
325 dispPtr->implicitWinPtr = NULL;
326 if (focusDebug) {
327 printf("Focussed on %s\n", newFocusPtr->pathName);
328 }
329 } else if (eventPtr->type == FocusOut) {
330 GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
331 dispPtr->focusWinPtr = NULL;
332 dispPtr->implicitWinPtr = NULL;
333 if (focusDebug) {
334 printf("Unfocussed from %s, detail %d\n", winPtr->pathName,
335 eventPtr->xfocus.detail);
336 }
337 } else if (eventPtr->type == EnterNotify) {
338 /*
339 * If there is no window manager, or if the window manager isn't
340 * moving the focus around (e.g. the disgusting "NoTitleFocus"
341 * option has been selected in twm), then we won't get FocusIn
342 * or FocusOut events. Instead, the "focus" field will be set
343 * in an Enter event to indicate that we've already got the focus
344 * when then mouse enters the window (even though we didn't get
345 * a FocusIn event). Watch for this and grab the focus when it
346 * happens.
347 */
348
349 if (eventPtr->xcrossing.focus && (dispPtr->focusWinPtr == NULL)) {
350 GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
351 dispPtr->focusWinPtr = newFocusPtr;
352 dispPtr->implicitWinPtr = winPtr;
353 if (focusDebug) {
354 printf("Focussed implicitly on %s\n",
355 newFocusPtr->pathName);
356 }
357 }
358 } else if (eventPtr->type == LeaveNotify) {
359 /*
360 * If the pointer just left a window for which we automatically
361 * claimed the focus on enter, generate FocusOut events. Note:
362 * dispPtr->implicitWinPtr may not be the same as
363 * dispPtr->focusWinPtr (e.g. because the "focus" command was
364 * used to redirect the focus after it arrived at
365 * dispPtr->implicitWinPtr)!!
366 */
367
368 if (dispPtr->implicitWinPtr == winPtr) {
369 GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
370 dispPtr->focusWinPtr = NULL;
371 dispPtr->implicitWinPtr = NULL;
372 if (focusDebug) {
373 printf("Defocussed implicitly\n");
374 }
375 }
376 }
377 return retValue;
378 }
379
380 /*
381 *----------------------------------------------------------------------
382 *
383 * SetFocus --
384 *
385 * This procedure is invoked to change the focus window for a
386 * given display in a given application.
387 *
388 * Results:
389 * None.
390 *
391 * Side effects:
392 * Event handlers may be invoked to process the change of
393 * focus.
394 *
395 *----------------------------------------------------------------------
396 */
397
398 static void
SetFocus(winPtr,force)399 SetFocus(winPtr, force)
400 TkWindow *winPtr; /* Window that is to be the new focus for
401 * its display and application. */
402 int force; /* If non-zero, set the X focus to this
403 * window even if the application doesn't
404 * currently have the X focus. */
405 {
406 TkDisplay *dispPtr = winPtr->dispPtr;
407 FocusInfo *focusPtr;
408 TkWindow *topLevelPtr, *topLevelPtr2;
409
410 if (winPtr == dispPtr->focusWinPtr) {
411 return;
412 }
413
414 /*
415 * Find the top-level window for winPtr, then find (or create)
416 * a record for the top-level.
417 */
418
419 for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) {
420 if (topLevelPtr == NULL) {
421 /*
422 * The window is being deleted. No point in worrying about
423 * giving it the focus.
424 */
425
426 return;
427 }
428 if (topLevelPtr->flags & TK_TOP_LEVEL) {
429 break;
430 }
431 }
432 for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
433 focusPtr = focusPtr->nextPtr) {
434 if (focusPtr->topLevelPtr == topLevelPtr) {
435 break;
436 }
437 }
438 if (focusPtr == NULL) {
439 focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
440 focusPtr->topLevelPtr = topLevelPtr;
441 focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
442 winPtr->mainPtr->focusPtr = focusPtr;
443 }
444
445 /*
446 * Reset the focus, but only if the application already has the
447 * input focus or "force" has been specified.
448 */
449
450 focusPtr->focusWinPtr = winPtr;
451 Tk_MakeWindowExist((Tk_Window) winPtr);
452 if (force || ((dispPtr->focusWinPtr != NULL)
453 && (dispPtr->focusWinPtr->mainPtr == winPtr->mainPtr))) {
454 /*
455 * Reset the focus in X if it has changed top-levels and if the
456 * new top-level isn't override-redirect (the only reason to
457 * change the X focus is so that the window manager can redecorate
458 * the focus window, but if it's override-redirect then it won't
459 * be decorated anyway; also, changing the focus to menus causes
460 * all sorts of problems with olvwm: the focus gets lost if
461 * keyboard traversal is used to move among menus.
462 */
463
464 if (dispPtr->focusWinPtr != NULL) {
465 for (topLevelPtr2 = dispPtr->focusWinPtr;
466 (topLevelPtr2 != NULL)
467 && !(topLevelPtr2->flags & TK_TOP_LEVEL);
468 topLevelPtr2 = topLevelPtr2->parentPtr) {
469 /* Empty loop body. */
470 }
471 } else {
472 topLevelPtr2 = NULL;
473 }
474 if ((topLevelPtr2 != topLevelPtr)
475 && !(topLevelPtr->atts.override_redirect)) {
476 if (dispPtr->focusOnMapPtr != NULL) {
477 Tk_DeleteEventHandler((Tk_Window) dispPtr->focusOnMapPtr,
478 StructureNotifyMask, FocusMapProc,
479 (ClientData) dispPtr->focusOnMapPtr);
480 dispPtr->focusOnMapPtr = NULL;
481 }
482 if (topLevelPtr->flags & TK_MAPPED) {
483 ChangeXFocus(topLevelPtr, force);
484 } else {
485 /*
486 * The window isn't mapped, so we can't give it the focus
487 * right now. Create an event handler that will give it
488 * the focus as soon as it is mapped.
489 */
490
491 Tk_CreateEventHandler((Tk_Window) topLevelPtr,
492 StructureNotifyMask, FocusMapProc,
493 (ClientData) topLevelPtr);
494 dispPtr->focusOnMapPtr = topLevelPtr;
495 dispPtr->forceFocus = force;
496 }
497 }
498 GenerateFocusEvents(dispPtr->focusWinPtr, winPtr);
499 dispPtr->focusWinPtr = winPtr;
500 }
501
502 /*
503 * Remember the current serial number for the X server and issue
504 * a dummy server request. This marks the position at which we
505 * changed the focus, so we can distinguish FocusIn and FocusOut
506 * events on either side of the mark.
507 */
508
509 winPtr->mainPtr->lastFocusPtr = winPtr;
510 winPtr->mainPtr->focusSerial = NextRequest(winPtr->display);
511 XNoOp(winPtr->display);
512 if (focusDebug) {
513 printf("focus marking for %s at %d\n", winPtr->pathName,
514 (int) winPtr->mainPtr->focusSerial);
515 }
516 }
517
518 /*
519 *----------------------------------------------------------------------
520 *
521 * TkGetFocus --
522 *
523 * Given a window, this procedure returns the current focus
524 * window for its application and display.
525 *
526 * Results:
527 * The return value is a pointer to the window that currently
528 * has the input focus for the specified application and
529 * display, or NULL if none.
530 *
531 * Side effects:
532 * None.
533 *
534 *----------------------------------------------------------------------
535 */
536
537 TkWindow *
TkGetFocus(winPtr)538 TkGetFocus(winPtr)
539 TkWindow *winPtr; /* Window that selects an application
540 * and a display. */
541 {
542 TkWindow *focusWinPtr;
543
544 focusWinPtr = winPtr->dispPtr->focusWinPtr;
545 if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
546 return focusWinPtr;
547 }
548 return (TkWindow *) NULL;
549 }
550
551 /*
552 *----------------------------------------------------------------------
553 *
554 * TkFocusDeadWindow --
555 *
556 * This procedure is invoked when it is determined that
557 * a window is dead. It cleans up focus-related information
558 * about the window.
559 *
560 * Results:
561 * None.
562 *
563 * Side effects:
564 * Various things get cleaned up and recycled.
565 *
566 *----------------------------------------------------------------------
567 */
568
569 void
TkFocusDeadWindow(winPtr)570 TkFocusDeadWindow(winPtr)
571 register TkWindow *winPtr; /* Information about the window
572 * that is being deleted. */
573 {
574 FocusInfo *focusPtr, *prevPtr;
575 TkDisplay *dispPtr = winPtr->dispPtr;
576
577 /*
578 * Search for focus records that refer to this window either as
579 * the top-level window or the current focus window.
580 */
581
582 for (prevPtr = NULL, focusPtr = winPtr->mainPtr->focusPtr;
583 focusPtr != NULL;
584 prevPtr = focusPtr, focusPtr = focusPtr->nextPtr) {
585 if (winPtr == focusPtr->topLevelPtr) {
586 /*
587 * The top-level window is the one being deleted: free
588 * the focus record and release the focus back to PointerRoot
589 * if we acquired it implicitly.
590 */
591
592 if (dispPtr->implicitWinPtr == winPtr) {
593 if (focusDebug) {
594 printf("releasing focus to root after %s died\n",
595 focusPtr->topLevelPtr->pathName);
596 }
597 dispPtr->implicitWinPtr = NULL;
598 dispPtr->focusWinPtr = NULL;
599 }
600 if (dispPtr->focusWinPtr == focusPtr->focusWinPtr) {
601 dispPtr->focusWinPtr = NULL;
602 }
603 if (dispPtr->focusOnMapPtr == focusPtr->topLevelPtr) {
604 dispPtr->focusOnMapPtr = NULL;
605 }
606 if (prevPtr == NULL) {
607 winPtr->mainPtr->focusPtr = focusPtr->nextPtr;
608 } else {
609 prevPtr->nextPtr = focusPtr->nextPtr;
610 }
611 ckfree((char *) focusPtr);
612 break;
613 } else if (winPtr == focusPtr->focusWinPtr) {
614 /*
615 * The deleted window had the focus for its top-level:
616 * move the focus to the top-level itself.
617 */
618
619 focusPtr->focusWinPtr = focusPtr->topLevelPtr;
620 if ((dispPtr->focusWinPtr == winPtr)
621 && !(focusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
622 if (focusDebug) {
623 printf("forwarding focus to %s after %s died\n",
624 focusPtr->topLevelPtr->pathName, winPtr->pathName);
625 }
626 GenerateFocusEvents(dispPtr->focusWinPtr,
627 focusPtr->topLevelPtr);
628 dispPtr->focusWinPtr = focusPtr->topLevelPtr;
629 }
630 break;
631 }
632 }
633
634 if (winPtr->mainPtr->lastFocusPtr == winPtr) {
635 winPtr->mainPtr->lastFocusPtr = NULL;
636 }
637 }
638
639 /*
640 *----------------------------------------------------------------------
641 *
642 * GenerateFocusEvents --
643 *
644 * This procedure is called to create FocusIn and FocusOut events to
645 * move the input focus from one window to another.
646 *
647 * Results:
648 * None.
649 *
650 * Side effects:
651 * FocusIn and FocusOut events are generated.
652 *
653 *----------------------------------------------------------------------
654 */
655
656 static void
GenerateFocusEvents(sourcePtr,destPtr)657 GenerateFocusEvents(sourcePtr, destPtr)
658 TkWindow *sourcePtr; /* Window that used to have the focus (may
659 * be NULL). */
660 TkWindow *destPtr; /* New window to have the focus (may be
661 * NULL). */
662
663 {
664 XEvent event;
665 TkWindow *winPtr;
666
667 winPtr = sourcePtr;
668 if (winPtr == NULL) {
669 winPtr = destPtr;
670 if (winPtr == NULL) {
671 return;
672 }
673 }
674
675 event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
676 event.xfocus.send_event = GENERATED_EVENT_MAGIC;
677 event.xfocus.display = winPtr->display;
678 event.xfocus.mode = NotifyNormal;
679 TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
680 TCL_QUEUE_MARK);
681 }
682
683 /*
684 *----------------------------------------------------------------------
685 *
686 * ChangeXFocus --
687 *
688 * This procedure is invoked to move the official X focus from
689 * one top-level to another. We do this when the application
690 * changes the focus window from one top-level to another, in
691 * order to notify the window manager so that it can highlight
692 * the new focus top-level.
693 *
694 * Results:
695 * None.
696 *
697 * Side effects:
698 * The official X focus window changes; the application's focus
699 * window isn't changed by this procedure.
700 *
701 *----------------------------------------------------------------------
702 */
703
704 static void
ChangeXFocus(topLevelPtr,force)705 ChangeXFocus(topLevelPtr, force)
706 TkWindow *topLevelPtr; /* Top-level window that is to receive
707 * the X focus. */
708 int force; /* Non-zero means claim the focus even
709 * if it didn't originally belong to
710 * topLevelPtr's application. */
711 {
712 TkDisplay *dispPtr = topLevelPtr->dispPtr;
713 TkWindow *winPtr;
714 Window focusWindow;
715 int dummy;
716 Tk_ErrorHandler errHandler;
717
718 /*
719 * If the focus was received implicitly, then there's no advantage
720 * in setting an explicit focus; just return.
721 */
722
723 if (dispPtr->implicitWinPtr != NULL) {
724 return;
725 }
726
727 /*
728 * Check to make sure that the focus is still in one of the
729 * windows of this application. Furthermore, grab the server
730 * to make sure that the focus doesn't change in the middle
731 * of this operation.
732 */
733
734 if (!focusDebug) {
735 XGrabServer(dispPtr->display);
736 }
737 if (!force) {
738 XGetInputFocus(dispPtr->display, &focusWindow, &dummy);
739 winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow);
740 if ((winPtr == NULL) || (winPtr->mainPtr != topLevelPtr->mainPtr)) {
741 goto done;
742 }
743 }
744
745 /*
746 * Tell X to change the focus. Ignore errors that occur when changing
747 * the focus: it is still possible that the window we're focussing
748 * to could have gotten unmapped, which will generate an error.
749 */
750
751 errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
752 (Tk_ErrorProc *) NULL, (ClientData) NULL);
753 XSetInputFocus(dispPtr->display, topLevelPtr->window, RevertToParent,
754 CurrentTime);
755 Tk_DeleteErrorHandler(errHandler);
756 if (focusDebug) {
757 printf("Set X focus to %s\n", topLevelPtr->pathName);
758 }
759
760 done:
761 if (!focusDebug) {
762 XUngrabServer(dispPtr->display);
763 }
764 }
765
766 /*
767 *----------------------------------------------------------------------
768 *
769 * FocusMapProc --
770 *
771 * This procedure is called as an event handler for StructureNotify
772 * events, if a window receives the focus at a time when its
773 * toplevel isn't mapped. The procedure is needed because X
774 * won't allow the focus to be set to an unmapped window; we
775 * detect when the toplevel is mapped and set the focus to it then.
776 *
777 * Results:
778 * None.
779 *
780 * Side effects:
781 * If this is a map event, the focus gets set to the toplevel
782 * given by clientData.
783 *
784 *----------------------------------------------------------------------
785 */
786
787 static void
FocusMapProc(clientData,eventPtr)788 FocusMapProc(clientData, eventPtr)
789 ClientData clientData; /* Toplevel window. */
790 XEvent *eventPtr; /* Information about event. */
791 {
792 TkWindow *winPtr = (TkWindow *) clientData;
793 TkDisplay *dispPtr = winPtr->dispPtr;
794
795 if (eventPtr->type == MapNotify) {
796 ChangeXFocus(winPtr, dispPtr->forceFocus);
797 Tk_DeleteEventHandler((Tk_Window) winPtr, StructureNotifyMask,
798 FocusMapProc, clientData);
799 dispPtr->focusOnMapPtr = NULL;
800 }
801 }
802