~ubuntu-branches/ubuntu/dapper/tk8.0/dapper-updates

« back to all changes in this revision

Viewing changes to generic/tkFocus.c

  • Committer: Bazaar Package Importer
  • Author(s): Mike Markley
  • Date: 2001-07-24 21:57:40 UTC
  • Revision ID: james.westby@ubuntu.com-20010724215740-r70t25rtmbqjil2h
Tags: upstream-8.0.5
ImportĀ upstreamĀ versionĀ 8.0.5

Show diffs side-by-side

added added

removed removed

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