OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkFocus.c] - Blame information for rev 1780

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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.1.1.1 2002-01-16 10:25:51 markom 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
    /* CYGNUS LOCAL: We can't just return if force is set.  */
554
    if (winPtr == displayFocusPtr->focusWinPtr && ! force) {
555
        return;
556
    }
557
 
558
    /*
559
     * Find the top-level window for winPtr, then find (or create)
560
     * a record for the top-level.  Also see whether winPtr and all its
561
     * ancestors are mapped.
562
     */
563
 
564
    allMapped = 1;
565
    for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr)  {
566
        if (topLevelPtr == NULL) {
567
            /*
568
             * The window is being deleted.  No point in worrying about
569
             * giving it the focus.
570
             */
571
            return;
572
        }
573
        if (!(topLevelPtr->flags & TK_MAPPED)) {
574
            allMapped = 0;
575
        }
576
        if (topLevelPtr->flags & TK_TOP_LEVEL) {
577
            break;
578
        }
579
    }
580
 
581
    /*
582
     * If the new focus window isn't mapped, then we can't focus on it
583
     * (X will generate an error, for example).  Instead, create an
584
     * event handler that will set the focus to this window once it gets
585
     * mapped.  At the same time, delete any old handler that might be
586
     * around;  it's no longer relevant.
587
     */
588
 
589
    if (displayFocusPtr->focusOnMapPtr != NULL) {
590
        Tk_DeleteEventHandler(
591
                (Tk_Window) displayFocusPtr->focusOnMapPtr,
592
                StructureNotifyMask, FocusMapProc,
593
                (ClientData) displayFocusPtr->focusOnMapPtr);
594
        displayFocusPtr->focusOnMapPtr = NULL;
595
    }
596
    if (!allMapped) {
597
        Tk_CreateEventHandler((Tk_Window) winPtr,
598
                VisibilityChangeMask, FocusMapProc,
599
                (ClientData) winPtr);
600
        displayFocusPtr->focusOnMapPtr = winPtr;
601
        displayFocusPtr->forceFocus = force;
602
        return;
603
    }
604
 
605
    for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
606
            tlFocusPtr = tlFocusPtr->nextPtr) {
607
        if (tlFocusPtr->topLevelPtr == topLevelPtr) {
608
            break;
609
        }
610
    }
611
    if (tlFocusPtr == NULL) {
612
        tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo));
613
        tlFocusPtr->topLevelPtr = topLevelPtr;
614
        tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr;
615
        winPtr->mainPtr->tlFocusPtr = tlFocusPtr;
616
    }
617
    tlFocusPtr->focusWinPtr = winPtr;
618
 
619
    /*
620
     * Reset the window system's focus window and generate focus events,
621
     * with two special cases:
622
     *
623
     * 1. If the application is embedded and doesn't currently have the
624
     *    focus, don't set the focus directly.  Instead, see if the
625
     *    embedding code can claim the focus from the enclosing
626
     *    container.
627
     * 2. Otherwise, if the application doesn't currently have the
628
     *    focus, don't change the window system's focus unless it was
629
     *    already in this application or "force" was specified.
630
     */
631
 
632
    if ((topLevelPtr->flags & TK_EMBEDDED)
633
            && (displayFocusPtr->focusWinPtr == NULL)) {
634
        TkpClaimFocus(topLevelPtr, force);
635
    } else if ((displayFocusPtr->focusWinPtr != NULL) || force) {
636
        /*
637
         * Generate events to shift focus between Tk windows.
638
         * We do this regardless of what TkpChangeFocus does with
639
         * the real X focus so that Tk widgets track focus commands
640
         * when there is no window manager.  GenerateFocusEvents will
641
         * set up a serial number marker so we discard focus events
642
         * that are triggered by the ChangeFocus.
643
         */
644
 
645
        serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force);
646
        if (serial != 0) {
647
            displayFocusPtr->focusSerial = serial;
648
        }
649
        GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr);
650
        displayFocusPtr->focusWinPtr = winPtr;
651
        winPtr->dispPtr->focusPtr = winPtr;
652
    }
653
}
654
 
655
/*
656
 *----------------------------------------------------------------------
657
 *
658
 * TkGetFocusWin --
659
 *
660
 *      Given a window, this procedure returns the current focus
661
 *      window for its application and display.
662
 *
663
 * Results:
664
 *      The return value is a pointer to the window that currently
665
 *      has the input focus for the specified application and
666
 *      display, or NULL if none.
667
 *
668
 * Side effects:
669
 *      None.
670
 *
671
 *----------------------------------------------------------------------
672
 */
673
 
674
TkWindow *
675
TkGetFocusWin(winPtr)
676
    TkWindow *winPtr;           /* Window that selects an application
677
                                 * and a display. */
678
{
679
    DisplayFocusInfo *displayFocusPtr;
680
 
681
    if (winPtr == NULL) {
682
        return (TkWindow *) NULL;
683
    }
684
 
685
    displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
686
    return displayFocusPtr->focusWinPtr;
687
}
688
 
689
/*
690
 *----------------------------------------------------------------------
691
 *
692
 * TkFocusKeyEvent --
693
 *
694
 *      Given a window and a key press or release event that arrived for
695
 *      the window, use information about the keyboard focus to compute
696
 *      which window should really get the event.  In addition, update
697
 *      the event to refer to its new window.
698
 *
699
 * Results:
700
 *      The return value is a pointer to the window that has the input
701
 *      focus in winPtr's application, or NULL if winPtr's application
702
 *      doesn't have the input focus.  If a non-NULL value is returned,
703
 *      eventPtr will be updated to refer properly to the focus window.
704
 *
705
 * Side effects:
706
 *      None.
707
 *
708
 *----------------------------------------------------------------------
709
 */
710
 
711
TkWindow *
712
TkFocusKeyEvent(winPtr, eventPtr)
713
    TkWindow *winPtr;           /* Window that selects an application
714
                                 * and a display. */
715
    XEvent *eventPtr;           /* X event to redirect (should be KeyPress
716
                                 * or KeyRelease). */
717
{
718
    DisplayFocusInfo *displayFocusPtr;
719
    TkWindow *focusWinPtr;
720
    int focusX, focusY, vRootX, vRootY, vRootWidth, vRootHeight;
721
 
722
    displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
723
    focusWinPtr = displayFocusPtr->focusWinPtr;
724
 
725
    /*
726
     * The code below is a debugging aid to make sure that dispPtr->focusPtr
727
     * is kept properly in sync with the "truth", which is the value in
728
     * displayFocusPtr->focusWinPtr.
729
     */
730
 
731
#ifdef TCL_MEM_DEBUG
732
    if (focusWinPtr != winPtr->dispPtr->focusPtr) {
733
        printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n");
734
        printf("expected %s, got %s\n",
735
                (focusWinPtr != NULL) ? focusWinPtr->pathName : "??",
736
                (winPtr->dispPtr->focusPtr != NULL) ?
737
                winPtr->dispPtr->focusPtr->pathName : "??");
738
    }
739
#endif
740
 
741
    if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
742
        /*
743
         * Map the x and y coordinates to make sense in the context of
744
         * the focus window, if possible (make both -1 if the map-from
745
         * and map-to windows don't share the same screen).
746
         */
747
 
748
        if ((focusWinPtr->display != winPtr->display)
749
                || (focusWinPtr->screenNum != winPtr->screenNum)) {
750
            eventPtr->xkey.x = -1;
751
            eventPtr->xkey.y = -1;
752
        } else {
753
            Tk_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY,
754
                    &vRootWidth, &vRootHeight);
755
            Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY);
756
            eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX;
757
            eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - focusY;
758
        }
759
        eventPtr->xkey.window = focusWinPtr->window;
760
        return focusWinPtr;
761
    }
762
 
763
    /*
764
     * The event doesn't belong to us.  Perhaps, due to embedding, it
765
     * really belongs to someone else.  Give the embedding code a chance
766
     * to redirect the event.
767
     */
768
 
769
    TkpRedirectKeyEvent(winPtr, eventPtr);
770
    return (TkWindow *) NULL;
771
}
772
 
773
/*
774
 *----------------------------------------------------------------------
775
 *
776
 * TkFocusDeadWindow --
777
 *
778
 *      This procedure is invoked when it is determined that
779
 *      a window is dead.  It cleans up focus-related information
780
 *      about the window.
781
 *
782
 * Results:
783
 *      None.
784
 *
785
 * Side effects:
786
 *      Various things get cleaned up and recycled.
787
 *
788
 *----------------------------------------------------------------------
789
 */
790
 
791
void
792
TkFocusDeadWindow(winPtr)
793
    register TkWindow *winPtr;          /* Information about the window
794
                                         * that is being deleted. */
795
{
796
    ToplevelFocusInfo *tlFocusPtr, *prevPtr;
797
    DisplayFocusInfo *displayFocusPtr;
798
    TkDisplay *dispPtr = winPtr->dispPtr;
799
 
800
    /*
801
     * Search for focus records that refer to this window either as
802
     * the top-level window or the current focus window.
803
     */
804
 
805
    displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
806
    for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr;
807
            tlFocusPtr != NULL;
808
            prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) {
809
        if (winPtr == tlFocusPtr->topLevelPtr) {
810
            /*
811
             * The top-level window is the one being deleted: free
812
             * the focus record and release the focus back to PointerRoot
813
             * if we acquired it implicitly.
814
             */
815
 
816
            if (dispPtr->implicitWinPtr == winPtr) {
817
                if (tclFocusDebug) {
818
                    printf("releasing focus to root after %s died\n",
819
                            tlFocusPtr->topLevelPtr->pathName);
820
                }
821
                dispPtr->implicitWinPtr = NULL;
822
                displayFocusPtr->focusWinPtr = NULL;
823
                dispPtr->focusPtr = NULL;
824
            }
825
            if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) {
826
                displayFocusPtr->focusWinPtr = NULL;
827
                dispPtr->focusPtr = NULL;
828
            }
829
            if (prevPtr == NULL) {
830
                winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr;
831
            } else {
832
                prevPtr->nextPtr = tlFocusPtr->nextPtr;
833
            }
834
            ckfree((char *) tlFocusPtr);
835
            break;
836
        } else if (winPtr == tlFocusPtr->focusWinPtr) {
837
            /*
838
             * The deleted window had the focus for its top-level:
839
             * move the focus to the top-level itself.
840
             */
841
 
842
            tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
843
            if ((displayFocusPtr->focusWinPtr == winPtr)
844
                    && !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
845
                if (tclFocusDebug) {
846
                    printf("forwarding focus to %s after %s died\n",
847
                            tlFocusPtr->topLevelPtr->pathName,
848
                            winPtr->pathName);
849
                }
850
                GenerateFocusEvents(displayFocusPtr->focusWinPtr,
851
                        tlFocusPtr->topLevelPtr);
852
                displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
853
                dispPtr->focusPtr = tlFocusPtr->topLevelPtr;
854
            }
855
            break;
856
        }
857
    }
858
 
859
    if (displayFocusPtr->focusOnMapPtr == winPtr) {
860
        displayFocusPtr->focusOnMapPtr = NULL;
861
    }
862
}
863
 
864
/*
865
 *----------------------------------------------------------------------
866
 *
867
 * GenerateFocusEvents --
868
 *
869
 *      This procedure is called to create FocusIn and FocusOut events to
870
 *      move the input focus from one window to another.
871
 *
872
 * Results:
873
 *      None.
874
 *
875
 * Side effects:
876
 *      FocusIn and FocusOut events are generated.
877
 *
878
 *----------------------------------------------------------------------
879
 */
880
 
881
static void
882
GenerateFocusEvents(sourcePtr, destPtr)
883
    TkWindow *sourcePtr;        /* Window that used to have the focus (may
884
                                 * be NULL). */
885
    TkWindow *destPtr;          /* New window to have the focus (may be
886
                                 * NULL). */
887
 
888
{
889
    XEvent event;
890
    TkWindow *winPtr;
891
 
892
    winPtr = sourcePtr;
893
    if (winPtr == NULL) {
894
        winPtr = destPtr;
895
        if (winPtr == NULL) {
896
            return;
897
        }
898
    }
899
 
900
    event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
901
    event.xfocus.send_event = GENERATED_EVENT_MAGIC;
902
    event.xfocus.display = winPtr->display;
903
    event.xfocus.mode = NotifyNormal;
904
    TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
905
            TCL_QUEUE_MARK);
906
}
907
 
908
/*
909
 *----------------------------------------------------------------------
910
 *
911
 * FocusMapProc --
912
 *
913
 *      This procedure is called as an event handler for VisibilityNotify
914
 *      events, if a window receives the focus at a time when its
915
 *      toplevel isn't mapped.  The procedure is needed because X
916
 *      won't allow the focus to be set to an unmapped window;  we
917
 *      detect when the toplevel is mapped and set the focus to it then.
918
 *
919
 * Results:
920
 *      None.
921
 *
922
 * Side effects:
923
 *      If this is a map event, the focus gets set to the toplevel
924
 *      given by clientData.
925
 *
926
 *----------------------------------------------------------------------
927
 */
928
 
929
static void
930
FocusMapProc(clientData, eventPtr)
931
    ClientData clientData;      /* Toplevel window. */
932
    XEvent *eventPtr;           /* Information about event. */
933
{
934
    TkWindow *winPtr = (TkWindow *) clientData;
935
    DisplayFocusInfo *displayFocusPtr;
936
 
937
    if (eventPtr->type == VisibilityNotify) {
938
        displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr,
939
                winPtr->dispPtr);
940
        if (tclFocusDebug) {
941
            printf("auto-focussing on %s, force %d\n", winPtr->pathName,
942
                    displayFocusPtr->forceFocus);
943
        }
944
        Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask,
945
                FocusMapProc, clientData);
946
        displayFocusPtr->focusOnMapPtr = NULL;
947
        SetFocus(winPtr, displayFocusPtr->forceFocus);
948
    }
949
}
950
 
951
/*
952
 *----------------------------------------------------------------------
953
 *
954
 * FindDisplayFocusInfo --
955
 *
956
 *      Given an application and a display, this procedure locate the
957
 *      focus record for that combination.  If no such record exists,
958
 *      it creates a new record and initializes it.
959
 *
960
 * Results:
961
 *      The return value is a pointer to the record.
962
 *
963
 * Side effects:
964
 *      A new record will be allocated if there wasn't one already.
965
 *
966
 *----------------------------------------------------------------------
967
 */
968
 
969
static DisplayFocusInfo *
970
FindDisplayFocusInfo(mainPtr, dispPtr)
971
    TkMainInfo *mainPtr;        /* Record that identifies a particular
972
                                 * application. */
973
    TkDisplay *dispPtr;         /* Display whose focus information is
974
                                 * needed. */
975
{
976
    DisplayFocusInfo *displayFocusPtr;
977
 
978
    for (displayFocusPtr = mainPtr->displayFocusPtr;
979
            displayFocusPtr != NULL;
980
            displayFocusPtr = displayFocusPtr->nextPtr) {
981
        if (displayFocusPtr->dispPtr == dispPtr) {
982
            return displayFocusPtr;
983
        }
984
    }
985
 
986
    /*
987
     * The record doesn't exist yet.  Make a new one.
988
     */
989
 
990
    displayFocusPtr = (DisplayFocusInfo *) ckalloc(sizeof(DisplayFocusInfo));
991
    displayFocusPtr->dispPtr = dispPtr;
992
    displayFocusPtr->focusWinPtr = NULL;
993
    displayFocusPtr->focusOnMapPtr = NULL;
994
    displayFocusPtr->forceFocus = 0;
995
    displayFocusPtr->focusSerial = 0;
996
    displayFocusPtr->nextPtr = mainPtr->displayFocusPtr;
997
    mainPtr->displayFocusPtr = displayFocusPtr;
998
    return displayFocusPtr;
999
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.