Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtk/generic/tkFocus.c
1810 views
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
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
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
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 *
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
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
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
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
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
803