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

Subversion Repositories or1k

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tkGrab.c --
3
 *
4
 *      This file provides procedures that implement grabs for Tk.
5
 *
6
 * Copyright (c) 1992-1994 The Regents of the University of California.
7
 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tkGrab.c,v 1.1.1.1 2002-01-16 10:25:51 markom Exp $
13
 */
14
 
15
#include "tkPort.h"
16
#include "tkInt.h"
17
 
18
/*
19
 * The grab state machine has four states: ungrabbed, button pressed,
20
 * grabbed, and button pressed while grabbed.  In addition, there are
21
 * three pieces of grab state information: the current grab window,
22
 * the current restrict window, and whether the mouse is captured.
23
 *
24
 * The current grab window specifies the point in the Tk window
25
 * heirarchy above which pointer events will not be reported.  Any
26
 * window within the subtree below the grab window will continue to
27
 * receive events as normal.  Events outside of the grab tree will be
28
 * reported to the grab window.
29
 *
30
 * If the current restrict window is set, then all pointer events will
31
 * be reported only to the restrict window.  The restrict window is
32
 * normally set during an automatic button grab.
33
 *
34
 * The mouse capture state specifies whether the window system will
35
 * report mouse events outside of any Tk toplevels.  This is set
36
 * during a global grab or an automatic button grab.
37
 *
38
 * The transitions between different states is given in the following
39
 * table:
40
 *
41
 * Event\State  U       B       G       GB
42
 * -----------  --      --      --      --
43
 * FirstPress   B       B       GB      GB
44
 * Press        B       B       G       GB
45
 * Release      U       B       G       GB
46
 * LastRelease  U       U       G       G
47
 * Grab         G       G       G       G
48
 * Ungrab       U       B       U       U
49
 *
50
 * Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button
51
 *
52
 * In addition, the following conditions are always true:
53
 *
54
 * State\Variable       Grab         Restrict        Capture
55
 * --------------       ----         --------        -------
56
 * Ungrabbed             0              0                0
57
 * Button                0              1               1
58
 * Grabbed               1              0                b/g
59
 * Grab and Button       1              1               1
60
 *
61
 * Note: 0 means variable is set to NULL, 1 means variable is set to
62
 * some window, b/g means the variable is set to a window if a button
63
 * is currently down or a global grab is in effect.
64
 *
65
 * The final complication to all of this is enter and leave events.
66
 * In order to correctly handle all of the various cases, Tk cannot
67
 * rely on X enter/leave events in all situations.  The following
68
 * describes the correct sequence of enter and leave events that
69
 * should be observed by Tk scripts:
70
 *
71
 * Event(state)         Enter/Leave From -> To
72
 * ------------         ----------------------
73
 * LastRelease(B | GB): restrict window -> anc(grab window, event window)
74
 * Grab(U | B):         event window -> anc(grab window, event window)
75
 * Grab(G):             anc(old grab window, event window) ->
76
 *                              anc(new grab window, event window)
77
 * Grab(GB):            restrict window -> anc(new grab window, event window)
78
 * Ungrab(G):           anc(grab window, event window) -> event window
79
 * Ungrab(GB):          restrict window -> event window
80
 *
81
 * Note: anc(x,y) returns the least ancestor of y that is in the tree
82
 * of x, terminating at toplevels.
83
 */
84
 
85
/*
86
 * The following structure is used to pass information to
87
 * GrabRestrictProc from EatGrabEvents.
88
 */
89
 
90
typedef struct {
91
    Display *display;           /* Display from which to discard events. */
92
    unsigned int serial;        /* Serial number with which to compare. */
93
} GrabInfo;
94
 
95
/*
96
 * Bit definitions for grabFlags field of TkDisplay structures:
97
 *
98
 * GRAB_GLOBAL                  1 means this is a global grab (we grabbed via
99
 *                              the server so all applications are locked out).
100
 *                              0 means this is a local grab that affects
101
 *                              only this application.
102
 * GRAB_TEMP_GLOBAL             1 means we've temporarily grabbed via the
103
 *                              server because a button is down and we want
104
 *                              to make sure that we get the button-up
105
 *                              event.  The grab will be released when the
106
 *                              last mouse button goes up.
107
 */
108
 
109
#define GRAB_GLOBAL             1
110
#define GRAB_TEMP_GLOBAL        4
111
 
112
/*
113
 * The following structure is a Tcl_Event that triggers a change in
114
 * the grabWinPtr field of a display.  This event guarantees that
115
 * the change occurs in the proper order relative to enter and leave
116
 * events.
117
 */
118
 
119
typedef struct NewGrabWinEvent {
120
    Tcl_Event header;           /* Standard information for all Tcl events. */
121
    TkDisplay *dispPtr;         /* Display whose grab window is to change. */
122
    Window grabWindow;          /* New grab window for display.  This is
123
                                 * recorded instead of a (TkWindow *) because
124
                                 * it will allow us to detect cases where
125
                                 * the window is destroyed before this event
126
                                 * is processed. */
127
} NewGrabWinEvent;
128
 
129
/*
130
 * The following magic value is stored in the "send_event" field of
131
 * EnterNotify and LeaveNotify events that are generated in this
132
 * file.  This allows us to separate "real" events coming from the
133
 * server from those that we generated.
134
 */
135
 
136
#define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac)
137
 
138
/*
139
 * Mask that selects any of the state bits corresponding to buttons,
140
 * plus masks that select individual buttons' bits:
141
 */
142
 
143
#define ALL_BUTTONS \
144
        (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
145
static unsigned int buttonStates[] = {
146
    Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
147
};
148
 
149
/*
150
 * Forward declarations for procedures declared later in this file:
151
 */
152
 
153
static void             EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr,
154
                            unsigned int serial));
155
static TkWindow *       FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1,
156
                            TkWindow *winPtr2, int *countPtr1,
157
                            int *countPtr2));
158
static Tk_RestrictAction GrabRestrictProc _ANSI_ARGS_((ClientData arg,
159
                            XEvent *eventPtr));
160
static int              GrabWinEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
161
                            int flags));
162
static void             MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
163
                            TkWindow *destPtr, int mode, int leaveEvents,
164
                            int EnterEvents));
165
static void             QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr,
166
                            TkWindow *grabWinPtr));
167
static void             ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr));
168
 
169
/*
170
 *----------------------------------------------------------------------
171
 *
172
 * Tk_GrabCmd --
173
 *
174
 *      This procedure is invoked to process the "grab" Tcl command.
175
 *      See the user documentation for details on what it does.
176
 *
177
 * Results:
178
 *      A standard Tcl result.
179
 *
180
 * Side effects:
181
 *      See the user documentation.
182
 *
183
 *----------------------------------------------------------------------
184
 */
185
 
186
        /* ARGSUSED */
187
int
188
Tk_GrabCmd(clientData, interp, argc, argv)
189
    ClientData clientData;      /* Main window associated with
190
                                 * interpreter. */
191
    Tcl_Interp *interp;         /* Current interpreter. */
192
    int argc;                   /* Number of arguments. */
193
    char **argv;                /* Argument strings. */
194
{
195
    int globalGrab, c;
196
    Tk_Window tkwin;
197
    TkDisplay *dispPtr;
198
    size_t length;
199
 
200
    if (argc < 2) {
201
        badArgs:
202
        Tcl_AppendResult(interp, "wrong # args: should be \"",
203
                argv[0], " ?-global? window\" or \"", argv[0],
204
                " option ?arg arg ...?\"", (char *) NULL);
205
        return TCL_ERROR;
206
    }
207
    c = argv[1][0];
208
    length = strlen(argv[1]);
209
    if (c == '.') {
210
        if (argc != 2) {
211
            goto badArgs;
212
        }
213
        tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
214
        if (tkwin == NULL) {
215
            return TCL_ERROR;
216
        }
217
        return Tk_Grab(interp, tkwin, 0);
218
    } else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0)
219
            && (length >= 2)) {
220
        if (argc != 3) {
221
            goto badArgs;
222
        }
223
        tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
224
        if (tkwin == NULL) {
225
            return TCL_ERROR;
226
        }
227
        return Tk_Grab(interp, tkwin, 1);
228
    } else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) {
229
        if (argc > 3) {
230
            Tcl_AppendResult(interp, "wrong # args: should be \"",
231
                    argv[0], " current ?window?\"", (char *) NULL);
232
            return TCL_ERROR;
233
        }
234
        if (argc == 3) {
235
            tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
236
            if (tkwin == NULL) {
237
                return TCL_ERROR;
238
            }
239
            dispPtr = ((TkWindow *) tkwin)->dispPtr;
240
            if (dispPtr->eventualGrabWinPtr != NULL) {
241
                interp->result = dispPtr->eventualGrabWinPtr->pathName;
242
            }
243
        } else {
244
            for (dispPtr = tkDisplayList; dispPtr != NULL;
245
                    dispPtr = dispPtr->nextPtr) {
246
                if (dispPtr->eventualGrabWinPtr != NULL) {
247
                    Tcl_AppendElement(interp,
248
                            dispPtr->eventualGrabWinPtr->pathName);
249
                }
250
            }
251
        }
252
        return TCL_OK;
253
    } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
254
        if (argc != 3) {
255
            Tcl_AppendResult(interp, "wrong # args: should be \"",
256
                    argv[0], " release window\"", (char *) NULL);
257
            return TCL_ERROR;
258
        }
259
        tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
260
        if (tkwin == NULL) {
261
            Tcl_ResetResult(interp);
262
        } else {
263
            Tk_Ungrab(tkwin);
264
        }
265
    } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)
266
            && (length >= 2)) {
267
        if ((argc != 3) && (argc != 4)) {
268
            Tcl_AppendResult(interp, "wrong # args: should be \"",
269
                    argv[0], " set ?-global? window\"", (char *) NULL);
270
            return TCL_ERROR;
271
        }
272
        if (argc == 3) {
273
            globalGrab = 0;
274
            tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
275
        } else {
276
            globalGrab = 1;
277
            length = strlen(argv[2]);
278
            if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) {
279
                Tcl_AppendResult(interp, "bad argument \"", argv[2],
280
                        "\": must be \"", argv[0], " set ?-global? window\"",
281
                        (char *) NULL);
282
                return TCL_ERROR;
283
            }
284
            tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData);
285
        }
286
        if (tkwin == NULL) {
287
            return TCL_ERROR;
288
        }
289
        return Tk_Grab(interp, tkwin, globalGrab);
290
    } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)
291
            && (length >= 2)) {
292
        TkWindow *winPtr;
293
 
294
        if (argc != 3) {
295
            Tcl_AppendResult(interp, "wrong # args: should be \"",
296
                    argv[0], " status window\"", (char *) NULL);
297
            return TCL_ERROR;
298
        }
299
        winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2],
300
                (Tk_Window) clientData);
301
        if (winPtr == NULL) {
302
            return TCL_ERROR;
303
        }
304
        dispPtr = winPtr->dispPtr;
305
        if (dispPtr->eventualGrabWinPtr != winPtr) {
306
            interp->result = "none";
307
        } else if (dispPtr->grabFlags & GRAB_GLOBAL) {
308
            interp->result = "global";
309
        } else {
310
            interp->result = "local";
311
        }
312
    } else {
313
        Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
314
                "\": must be current, release, set, or status",
315
                (char *) NULL);
316
        return TCL_ERROR;
317
    }
318
    return TCL_OK;
319
}
320
 
321
/*
322
 *----------------------------------------------------------------------
323
 *
324
 * Tk_Grab --
325
 *
326
 *      Grabs the pointer and keyboard, so that mouse-related events are
327
 *      only reported relative to a given window and its descendants.
328
 *
329
 * Results:
330
 *      A standard Tcl result is returned.  TCL_OK is the normal return
331
 *      value;  if the grab could not be set then TCL_ERROR is returned
332
 *      and interp->result will hold an error message.
333
 *
334
 * Side effects:
335
 *      Once this call completes successfully, no window outside the
336
 *      tree rooted at tkwin will receive pointer- or keyboard-related
337
 *      events until the next call to Tk_Ungrab.  If a previous grab was
338
 *      in effect within this application, then it is replaced with a new
339
 *      one.
340
 *
341
 *----------------------------------------------------------------------
342
 */
343
 
344
int
345
Tk_Grab(interp, tkwin, grabGlobal)
346
    Tcl_Interp *interp;                 /* Used for error reporting. */
347
    Tk_Window tkwin;                    /* Window on whose behalf the pointer
348
                                         * is to be grabbed. */
349
    int grabGlobal;                     /* Non-zero means issue a grab to the
350
                                         * server so that no other application
351
                                         * gets mouse or keyboard events.
352
                                         * Zero means the grab only applies
353
                                         * within this application. */
354
{
355
    int grabResult, numTries;
356
    TkWindow *winPtr = (TkWindow *) tkwin;
357
    TkDisplay *dispPtr = winPtr->dispPtr;
358
    TkWindow *winPtr2;
359
    unsigned int serial;
360
 
361
    ReleaseButtonGrab(dispPtr);
362
    if (dispPtr->eventualGrabWinPtr != NULL) {
363
        if ((dispPtr->eventualGrabWinPtr == winPtr)
364
                && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
365
            return TCL_OK;
366
        }
367
        if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
368
            alreadyGrabbed:
369
            interp->result = "grab failed: another application has grab";
370
            return TCL_ERROR;
371
        }
372
        Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
373
    }
374
 
375
    Tk_MakeWindowExist(tkwin);
376
    if (!grabGlobal) {
377
        Window dummy1, dummy2;
378
        int dummy3, dummy4, dummy5, dummy6;
379
        unsigned int state;
380
 
381
        /*
382
         * Local grab.  However, if any mouse buttons are down, turn
383
         * it into a global grab temporarily, until the last button
384
         * goes up.  This does two things: (a) it makes sure that we
385
         * see the button-up event;  and (b) it allows us to track mouse
386
         * motion among all of the windows of this application.
387
         */
388
 
389
        dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
390
        XQueryPointer(dispPtr->display, winPtr->window, &dummy1,
391
                &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state);
392
        if ((state & ALL_BUTTONS) != 0) {
393
            dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
394
            goto setGlobalGrab;
395
        }
396
    } else {
397
        dispPtr->grabFlags |= GRAB_GLOBAL;
398
        setGlobalGrab:
399
 
400
        /*
401
         * Tricky point: must ungrab before grabbing.  This is needed
402
         * in case there is a button auto-grab already in effect.  If
403
         * there is, and the mouse has moved to a different window, X
404
         * won't generate enter and leave events to move the mouse if
405
         * we grab without ungrabbing.
406
         */
407
 
408
        XUngrabPointer(dispPtr->display, CurrentTime);
409
        serial = NextRequest(dispPtr->display);
410
 
411
        /*
412
         * Another tricky point: there are races with some window
413
         * managers that can cause grabs to fail because the window
414
         * manager hasn't released its grab quickly enough.  To work
415
         * around this problem, retry a few times after AlreadyGrabbed
416
         * errors to give the grab release enough time to register with
417
         * the server.
418
         */
419
 
420
        grabResult = 0;                  /* Needed only to prevent gcc
421
                                         * compiler warnings. */
422
        for (numTries = 0; numTries < 10; numTries++) {
423
            grabResult = XGrabPointer(dispPtr->display, winPtr->window,
424
                    True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
425
                    |PointerMotionMask, GrabModeAsync, GrabModeAsync, None,
426
                    None, CurrentTime);
427
            if (grabResult != AlreadyGrabbed) {
428
                break;
429
            }
430
            Tcl_Sleep(100);
431
        }
432
        if (grabResult != 0) {
433
            grabError:
434
            if (grabResult == GrabNotViewable) {
435
                interp->result = "grab failed: window not viewable";
436
            } else if (grabResult == AlreadyGrabbed) {
437
                goto alreadyGrabbed;
438
            } else if (grabResult == GrabFrozen) {
439
                interp->result = "grab failed: keyboard or pointer frozen";
440
            } else if (grabResult == GrabInvalidTime) {
441
                interp->result = "grab failed: invalid time";
442
            } else {
443
                char msg[100];
444
 
445
                sprintf(msg, "grab failed for unknown reason (code %d)",
446
                        grabResult);
447
                Tcl_AppendResult(interp, msg, (char *) NULL);
448
            }
449
            return TCL_ERROR;
450
        }
451
        grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
452
                False, GrabModeAsync, GrabModeAsync, CurrentTime);
453
        if (grabResult != 0) {
454
            XUngrabPointer(dispPtr->display, CurrentTime);
455
            goto grabError;
456
        }
457
 
458
        /*
459
         * Eat up any grab-related events generated by the server for the
460
         * grab.  There are several reasons for doing this:
461
         *
462
         * 1. We have to synthesize the events for local grabs anyway, since
463
         *    the server doesn't participate in them.
464
         * 2. The server doesn't always generate the right events for global
465
         *    grabs (e.g. it generates events even if the current window is
466
         *    in the grab tree, which we don't want).
467
         * 3. We want all the grab-related events to be processed immediately
468
         *    (before other events that are already queued); events coming
469
         *    from the server will be in the wrong place, but events we
470
         *    synthesize here will go to the front of the queue.
471
         */
472
 
473
        EatGrabEvents(dispPtr, serial);
474
    }
475
 
476
    /*
477
     * Synthesize leave events to move the pointer from its current window
478
     * up to the lowest ancestor that it has in common with the grab window.
479
     * However, only do this if the pointer is outside the grab window's
480
     * subtree but inside the grab window's application.
481
     */
482
 
483
    if ((dispPtr->serverWinPtr != NULL)
484
            && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) {
485
        for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) {
486
            if (winPtr2 == winPtr) {
487
                break;
488
            }
489
            if (winPtr2 == NULL) {
490
                MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0);
491
                break;
492
            }
493
        }
494
    }
495
    QueueGrabWindowChange(dispPtr, winPtr);
496
    return TCL_OK;
497
}
498
 
499
/*
500
 *----------------------------------------------------------------------
501
 *
502
 * Tk_Ungrab --
503
 *
504
 *      Releases a grab on the mouse pointer and keyboard, if there
505
 *      is one set on the specified window.
506
 *
507
 * Results:
508
 *      None.
509
 *
510
 * Side effects:
511
 *      Pointer and keyboard events will start being delivered to other
512
 *      windows again.
513
 *
514
 *----------------------------------------------------------------------
515
 */
516
 
517
void
518
Tk_Ungrab(tkwin)
519
    Tk_Window tkwin;                    /* Window whose grab should be
520
                                         * released. */
521
{
522
    TkDisplay *dispPtr;
523
    TkWindow *grabWinPtr, *winPtr;
524
    unsigned int serial;
525
 
526
    grabWinPtr = (TkWindow *) tkwin;
527
    dispPtr = grabWinPtr->dispPtr;
528
    if (grabWinPtr != dispPtr->eventualGrabWinPtr) {
529
        return;
530
    }
531
    ReleaseButtonGrab(dispPtr);
532
    QueueGrabWindowChange(dispPtr, (TkWindow *) NULL);
533
    if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) {
534
        dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
535
        serial = NextRequest(dispPtr->display);
536
        XUngrabPointer(dispPtr->display, CurrentTime);
537
        XUngrabKeyboard(dispPtr->display, CurrentTime);
538
        EatGrabEvents(dispPtr, serial);
539
    }
540
 
541
    /*
542
     * Generate events to move the pointer back to the window where it
543
     * really is.  Some notes:
544
     * 1. As with grabs, only do this if the "real" window is not a
545
     *    descendant of the grab window, since in this case the pointer
546
     *    is already where it's supposed to be.
547
     * 2. If the "real" window is in some other application then don't
548
     *    generate any events at all, since everything's already been
549
     *    reported correctly.
550
     * 3. Only generate enter events.  Don't generate leave events,
551
     *    because we never told the lower-level windows that they
552
     *    had the pointer in the first place.
553
     */
554
 
555
    for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) {
556
        if (winPtr == grabWinPtr) {
557
            break;
558
        }
559
        if (winPtr == NULL) {
560
            if ((dispPtr->serverWinPtr == NULL) ||
561
                    (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) {
562
                MovePointer2(grabWinPtr, dispPtr->serverWinPtr,
563
                        NotifyUngrab, 0, 1);
564
            }
565
            break;
566
        }
567
    }
568
}
569
 
570
/*
571
 *----------------------------------------------------------------------
572
 *
573
 * ReleaseButtonGrab --
574
 *
575
 *      This procedure is called to release a simulated button grab, if
576
 *      there is one in effect.  A button grab is present whenever
577
 *      dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL
578
 *      flag is set.
579
 *
580
 * Results:
581
 *      None.
582
 *
583
 * Side effects:
584
 *      DispPtr->buttonWinPtr is reset to NULL, and enter and leave
585
 *      events are generated if necessary to move the pointer from
586
 *      the button grab window to its current window.
587
 *
588
 *----------------------------------------------------------------------
589
 */
590
 
591
static void
592
ReleaseButtonGrab(dispPtr)
593
    register TkDisplay *dispPtr;        /* Display whose button grab is to be
594
                                         * released. */
595
{
596
    unsigned int serial;
597
 
598
    if (dispPtr->buttonWinPtr != NULL) {
599
        if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) {
600
            MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr,
601
                    NotifyUngrab, 1, 1);
602
        }
603
        dispPtr->buttonWinPtr = NULL;
604
    }
605
    if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) {
606
        dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL;
607
        serial = NextRequest(dispPtr->display);
608
        XUngrabPointer(dispPtr->display, CurrentTime);
609
        XUngrabKeyboard(dispPtr->display, CurrentTime);
610
        EatGrabEvents(dispPtr, serial);
611
    }
612
}
613
 
614
/*
615
 *----------------------------------------------------------------------
616
 *
617
 * TkPointerEvent --
618
 *
619
 *      This procedure is called for each pointer-related event, before
620
 *      the event has been processed.  It does various things to make
621
 *      grabs work correctly.
622
 *
623
 * Results:
624
 *      If the return value is 1 it means the event should be processed
625
 *      (event handlers should be invoked).  If the return value is 0
626
 *      it means the event should be ignored in order to make grabs
627
 *      work correctly.  In some cases this procedure modifies the event.
628
 *
629
 * Side effects:
630
 *      Grab state information may be updated.  New events may also be
631
 *      pushed back onto the event queue to replace or augment the
632
 *      one passed in here.
633
 *
634
 *----------------------------------------------------------------------
635
 */
636
 
637
int
638
TkPointerEvent(eventPtr, winPtr)
639
    register XEvent *eventPtr;          /* Pointer to the event. */
640
    TkWindow *winPtr;                   /* Tk's information for window
641
                                         * where event was reported. */
642
{
643
    register TkWindow *winPtr2;
644
    TkDisplay *dispPtr = winPtr->dispPtr;
645
    unsigned int serial;
646
    int outsideGrabTree = 0;
647
    int ancestorOfGrab = 0;
648
    int appGrabbed = 0;                  /* Non-zero means event is being
649
                                         * reported to an application that is
650
                                         * affected by the grab. */
651
 
652
    /*
653
     * Collect information about the grab (if any).
654
     */
655
 
656
    switch (TkGrabState(winPtr)) {
657
        case TK_GRAB_IN_TREE:
658
            appGrabbed = 1;
659
            break;
660
        case TK_GRAB_ANCESTOR:
661
            appGrabbed = 1;
662
            outsideGrabTree = 1;
663
            ancestorOfGrab = 1;
664
            break;
665
        case TK_GRAB_EXCLUDED:
666
            appGrabbed = 1;
667
            outsideGrabTree = 1;
668
            break;
669
    }
670
 
671
    if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
672
        /*
673
         * Keep track of what window the mouse is *really* over.
674
         * Any events that we generate have a special send_event value,
675
         * which is detected below and used to ignore the event for
676
         * purposes of setting serverWinPtr.
677
         */
678
 
679
        if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) {
680
            if ((eventPtr->type == LeaveNotify) &&
681
                    (winPtr->flags & TK_TOP_LEVEL)) {
682
                dispPtr->serverWinPtr = NULL;
683
            } else {
684
                dispPtr->serverWinPtr = winPtr;
685
            }
686
        }
687
 
688
        /*
689
         * When a grab is active, X continues to report enter and leave
690
         * events for windows outside the tree of the grab window:
691
         * 1. Detect these events and ignore them except for
692
         *    windows above the grab window.
693
         * 2. Allow Enter and Leave events to pass through the
694
         *    windows above the grab window, but never let them
695
         *    end up with the pointer *in* one of those windows.
696
         */
697
 
698
        if (dispPtr->grabWinPtr != NULL) {
699
            if (outsideGrabTree && appGrabbed) {
700
                if (!ancestorOfGrab) {
701
                    return 0;
702
                }
703
                switch (eventPtr->xcrossing.detail) {
704
                    case NotifyInferior:
705
                        return 0;
706
                    case NotifyAncestor:
707
                        eventPtr->xcrossing.detail = NotifyVirtual;
708
                        break;
709
                    case NotifyNonlinear:
710
                        eventPtr->xcrossing.detail = NotifyNonlinearVirtual;
711
                        break;
712
                }
713
            }
714
 
715
            /*
716
             * Make buttons have the same grab-like behavior inside a grab
717
             * as they do outside a grab:  do this by ignoring enter and
718
             * leave events except for the window in which the button was
719
             * pressed.
720
             */
721
 
722
            if ((dispPtr->buttonWinPtr != NULL)
723
                    && (winPtr != dispPtr->buttonWinPtr)) {
724
                return 0;
725
            }
726
        }
727
        return 1;
728
    }
729
 
730
    if (!appGrabbed) {
731
        return 1;
732
    }
733
 
734
    if (eventPtr->type == MotionNotify) {
735
        /*
736
         * When grabs are active, X reports motion events relative to the
737
         * window under the pointer.  Instead, it should report the events
738
         * relative to the window the button went down in, if there is a
739
         * button down.  Otherwise, if the pointer window is outside the
740
         * subtree of the grab window, the events should be reported
741
         * relative to the grab window.  Otherwise, the event should be
742
         * reported to the pointer window.
743
         */
744
 
745
        winPtr2 = winPtr;
746
        if (dispPtr->buttonWinPtr != NULL) {
747
            winPtr2 = dispPtr->buttonWinPtr;
748
        } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
749
            winPtr2 = dispPtr->grabWinPtr;
750
        }
751
        if (winPtr2 != winPtr) {
752
            TkChangeEventWindow(eventPtr, winPtr2);
753
            Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
754
            return 0;
755
        }
756
        return 1;
757
    }
758
 
759
    /*
760
     * Process ButtonPress and ButtonRelease events:
761
     * 1. Keep track of whether a button is down and what window it
762
     *    went down in.
763
     * 2. If the first button goes down outside the grab tree, pretend
764
     *    it went down in the grab window.  Note: it's important to
765
     *    redirect events to the grab window like this in order to make
766
     *    things like menus work, where button presses outside the
767
     *    grabbed menu need to be seen.  An application can always
768
     *    ignore the events if they occur outside its window.
769
     * 3. If a button press or release occurs outside the window where
770
     *    the first button was pressed, retarget the event so it's reported
771
     *    to the window where the first button was pressed.
772
     * 4. If the last button is released in a window different than where
773
     *    the first button was pressed, generate Enter/Leave events to
774
     *    move the mouse from the button window to its current window.
775
     * 5. If the grab is set at a time when a button is already down, or
776
     *    if the window where the button was pressed was deleted, then
777
     *    dispPtr->buttonWinPtr will stay NULL.  Just forget about the
778
     *    auto-grab for the button press;  events will go to whatever
779
     *    window contains the pointer.  If this window isn't in the grab
780
     *    tree then redirect events to the grab window.
781
     * 6. When a button is pressed during a local grab, the X server sets
782
     *    a grab of its own, since it doesn't even know about our local
783
     *    grab.  This causes enter and leave events no longer to be
784
     *    generated in the same way as for global grabs.  To eliminate this
785
     *    problem, set a temporary global grab when the first button goes
786
     *    down and release it when the last button comes up.
787
     */
788
 
789
    if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
790
        winPtr2 = dispPtr->buttonWinPtr;
791
        if (winPtr2 == NULL) {
792
            if (outsideGrabTree) {
793
                winPtr2 = dispPtr->grabWinPtr;                  /* Note 5. */
794
            } else {
795
                winPtr2 = winPtr;                               /* Note 5. */
796
            }
797
        }
798
        if (eventPtr->type == ButtonPress) {
799
            if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
800
                if (outsideGrabTree) {
801
                    TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr);
802
                    Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
803
                    return 0;                                    /* Note 2. */
804
                }
805
                if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {      /* Note 6. */
806
                    serial = NextRequest(dispPtr->display);
807
                    if (XGrabPointer(dispPtr->display,
808
                            dispPtr->grabWinPtr->window, True,
809
                            ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
810
                            GrabModeAsync, GrabModeAsync, None, None,
811
                            CurrentTime) == 0) {
812
                        EatGrabEvents(dispPtr, serial);
813
                        if (XGrabKeyboard(dispPtr->display, winPtr->window,
814
                                False, GrabModeAsync, GrabModeAsync,
815
                                CurrentTime) == 0) {
816
                            dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
817
                        } else {
818
                            XUngrabPointer(dispPtr->display, CurrentTime);
819
                        }
820
                    }
821
                }
822
                dispPtr->buttonWinPtr = winPtr;
823
                return 1;
824
            }
825
        } else {
826
            if ((eventPtr->xbutton.state & ALL_BUTTONS)
827
                    == buttonStates[eventPtr->xbutton.button - Button1]) {
828
                ReleaseButtonGrab(dispPtr);                     /* Note 4. */
829
            }
830
        }
831
        if (winPtr2 != winPtr) {
832
            TkChangeEventWindow(eventPtr, winPtr2);
833
            Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
834
            return 0;                                            /* Note 3. */
835
        }
836
    }
837
 
838
    return 1;
839
}
840
 
841
/*
842
 *----------------------------------------------------------------------
843
 *
844
 * TkChangeEventWindow --
845
 *
846
 *      Given an event and a new window to which the event should be
847
 *      retargeted, modify fields of the event so that the event is
848
 *      properly retargeted to the new window.
849
 *
850
 * Results:
851
 *      The following fields of eventPtr are modified:  window,
852
 *      subwindow, x, y, same_screen.
853
 *
854
 * Side effects:
855
 *      None.
856
 *
857
 *----------------------------------------------------------------------
858
 */
859
 
860
void
861
TkChangeEventWindow(eventPtr, winPtr)
862
    register XEvent *eventPtr;  /* Event to retarget.  Must have
863
                                 * type ButtonPress, ButtonRelease, KeyPress,
864
                                 * KeyRelease, MotionNotify, EnterNotify,
865
                                 * or LeaveNotify. */
866
    TkWindow *winPtr;           /* New target window for event. */
867
{
868
    int x, y, sameScreen, bd;
869
    register TkWindow *childPtr;
870
 
871
    eventPtr->xmotion.window = Tk_WindowId(winPtr);
872
    if (eventPtr->xmotion.root ==
873
            RootWindow(winPtr->display, winPtr->screenNum)) {
874
        Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
875
        eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
876
        eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
877
        eventPtr->xmotion.subwindow = None;
878
        for (childPtr = winPtr->childList; childPtr != NULL;
879
                childPtr = childPtr->nextPtr) {
880
            if (childPtr->flags & TK_TOP_LEVEL) {
881
                continue;
882
            }
883
            x = eventPtr->xmotion.x - childPtr->changes.x;
884
            y = eventPtr->xmotion.y - childPtr->changes.y;
885
            bd = childPtr->changes.border_width;
886
            if ((x >= -bd) && (y >= -bd)
887
                    && (x < (childPtr->changes.width + bd))
888
                    && (y < (childPtr->changes.height + bd))) {
889
                eventPtr->xmotion.subwindow = childPtr->window;
890
            }
891
        }
892
        sameScreen = 1;
893
    } else {
894
        eventPtr->xmotion.x = 0;
895
        eventPtr->xmotion.y = 0;
896
        eventPtr->xmotion.subwindow = None;
897
        sameScreen = 0;
898
    }
899
    if (eventPtr->type == MotionNotify) {
900
        eventPtr->xmotion.same_screen = sameScreen;
901
    } else {
902
        eventPtr->xbutton.same_screen = sameScreen;
903
    }
904
}
905
 
906
/*
907
 *----------------------------------------------------------------------
908
 *
909
 * TkInOutEvents --
910
 *
911
 *      This procedure synthesizes EnterNotify and LeaveNotify events
912
 *      to correctly transfer the pointer from one window to another.
913
 *      It can also be used to generate FocusIn and FocusOut events
914
 *      to move the input focus.
915
 *
916
 * Results:
917
 *      None.
918
 *
919
 * Side effects:
920
 *      Synthesized events may be pushed back onto the event queue.
921
 *      The event pointed to by eventPtr is modified.
922
 *
923
 *----------------------------------------------------------------------
924
 */
925
 
926
void
927
TkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType, position)
928
    XEvent *eventPtr;           /* A template X event.  Must have all fields
929
                                 * properly set except for type, window,
930
                                 * subwindow, x, y, detail, and same_screen
931
                                 * (Not all of these fields are valid for
932
                                 * FocusIn/FocusOut events;  x_root and y_root
933
                                 * must be valid for Enter/Leave events, even
934
                                 * though x and y needn't be valid). */
935
    TkWindow *sourcePtr;        /* Window that used to have the pointer or
936
                                 * focus (NULL means it was not in a window
937
                                 * managed by this process). */
938
    TkWindow *destPtr;          /* Window that is to end up with the pointer
939
                                 * or focus (NULL means it's not one managed
940
                                 * by this process). */
941
    int leaveType;              /* Type of events to generate for windows
942
                                 * being left (LeaveNotify or FocusOut).  0
943
                                 * means don't generate leave events. */
944
    int enterType;              /* Type of events to generate for windows
945
                                 * being entered (EnterNotify or FocusIn).  0
946
                                 * means don't generate enter events. */
947
    Tcl_QueuePosition position; /* Position at which events are added to
948
                                 * the system event queue. */
949
{
950
    register TkWindow *winPtr;
951
    int upLevels, downLevels, i, j, focus;
952
 
953
    /*
954
     * There are four possible cases to deal with:
955
     *
956
     * 1. SourcePtr and destPtr are the same.  There's nothing to do in
957
     *    this case.
958
     * 2. SourcePtr is an ancestor of destPtr in the same top-level
959
     *    window.  Must generate events down the window tree from source
960
     *    to dest.
961
     * 3. DestPtr is an ancestor of sourcePtr in the same top-level
962
     *    window.  Must generate events up the window tree from sourcePtr
963
     *    to destPtr.
964
     * 4. All other cases.  Must first generate events up the window tree
965
     *    from sourcePtr to its top-level, then down from destPtr's
966
     *    top-level to destPtr. This form is called "non-linear."
967
     *
968
     * The call to FindCommonAncestor separates these four cases and decides
969
     * how many levels up and down events have to be generated for.
970
     */
971
 
972
    if (sourcePtr == destPtr) {
973
        return;
974
    }
975
    if ((leaveType == FocusOut) || (enterType == FocusIn)) {
976
        focus = 1;
977
    } else {
978
        focus = 0;
979
    }
980
    FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels);
981
 
982
    /*
983
     * Generate enter/leave events and add them to the grab event queue.
984
     */
985
 
986
 
987
#define QUEUE(w, t, d)                                  \
988
    if (w->window != None) {                            \
989
        eventPtr->type = t;                             \
990
        if (focus) {                                    \
991
            eventPtr->xfocus.window = w->window;        \
992
            eventPtr->xfocus.detail = d;                \
993
        } else {                                        \
994
            eventPtr->xcrossing.detail = d;             \
995
            TkChangeEventWindow(eventPtr, w);           \
996
        }                                               \
997
        Tk_QueueWindowEvent(eventPtr, position);        \
998
    }
999
 
1000
    if (downLevels == 0) {
1001
 
1002
        /*
1003
         * SourcePtr is an inferior of destPtr.
1004
         */
1005
 
1006
        if (leaveType != 0) {
1007
            QUEUE(sourcePtr, leaveType, NotifyAncestor);
1008
            for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
1009
                    winPtr = winPtr->parentPtr, i--) {
1010
                QUEUE(winPtr, leaveType, NotifyVirtual);
1011
            }
1012
        }
1013
        if ((enterType != 0) && (destPtr != NULL)) {
1014
            QUEUE(destPtr, enterType, NotifyInferior);
1015
        }
1016
    } else if (upLevels == 0) {
1017
 
1018
        /*
1019
         * DestPtr is an inferior of sourcePtr.
1020
         */
1021
 
1022
        if ((leaveType != 0) && (sourcePtr != NULL)) {
1023
            QUEUE(sourcePtr, leaveType, NotifyInferior);
1024
        }
1025
        if (enterType != 0) {
1026
            for (i = downLevels-1; i > 0; i--) {
1027
                for (winPtr = destPtr->parentPtr, j = 1; j < i;
1028
                        winPtr = winPtr->parentPtr, j++) {
1029
                }
1030
                QUEUE(winPtr, enterType, NotifyVirtual);
1031
            }
1032
            if (destPtr != NULL) {
1033
                QUEUE(destPtr, enterType, NotifyAncestor);
1034
            }
1035
        }
1036
    } else {
1037
 
1038
        /*
1039
         * Non-linear:  neither window is an inferior of the other.
1040
         */
1041
 
1042
        if (leaveType != 0) {
1043
            QUEUE(sourcePtr, leaveType, NotifyNonlinear);
1044
            for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
1045
                    winPtr = winPtr->parentPtr, i--) {
1046
                QUEUE(winPtr, leaveType, NotifyNonlinearVirtual);
1047
            }
1048
        }
1049
        if (enterType != 0) {
1050
            for (i = downLevels-1; i > 0; i--) {
1051
                for (winPtr = destPtr->parentPtr, j = 1; j < i;
1052
                        winPtr = winPtr->parentPtr, j++) {
1053
                }
1054
                QUEUE(winPtr, enterType, NotifyNonlinearVirtual);
1055
            }
1056
            if (destPtr != NULL) {
1057
                QUEUE(destPtr, enterType, NotifyNonlinear);
1058
            }
1059
        }
1060
    }
1061
}
1062
 
1063
/*
1064
 *----------------------------------------------------------------------
1065
 *
1066
 * MovePointer2 --
1067
 *
1068
 *      This procedure synthesizes  EnterNotify and LeaveNotify events
1069
 *      to correctly transfer the pointer from one window to another.
1070
 *      It is different from TkInOutEvents in that no template X event
1071
 *      needs to be supplied;  this procedure generates the template
1072
 *      event and calls TkInOutEvents.
1073
 *
1074
 * Results:
1075
 *      None.
1076
 *
1077
 * Side effects:
1078
 *      Synthesized events may be pushed back onto the event queue.
1079
 *
1080
 *----------------------------------------------------------------------
1081
 */
1082
 
1083
static void
1084
MovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents)
1085
    TkWindow *sourcePtr;        /* Window currently containing pointer (NULL
1086
                                 * means it's not one managed by this
1087
                                 * process). */
1088
    TkWindow *destPtr;          /* Window that is to end up containing the
1089
                                 * pointer (NULL means it's not one managed
1090
                                 * by this process). */
1091
    int mode;                   /* Mode for enter/leave events, such as
1092
                                 * NotifyNormal or NotifyUngrab. */
1093
    int leaveEvents;            /* Non-zero means generate leave events for the
1094
                                 * windows being left.  Zero means don't
1095
                                 * generate leave events. */
1096
    int enterEvents;            /* Non-zero means generate enter events for the
1097
                                 * windows being entered.  Zero means don't
1098
                                 * generate enter events. */
1099
{
1100
    XEvent event;
1101
    Window dummy1, dummy2;
1102
    int dummy3, dummy4;
1103
    TkWindow *winPtr;
1104
 
1105
    winPtr = sourcePtr;
1106
    if ((winPtr == NULL) || (winPtr->window == None)) {
1107
        winPtr = destPtr;
1108
        if ((winPtr == NULL) || (winPtr->window == None)) {
1109
            return;
1110
        }
1111
    }
1112
 
1113
    event.xcrossing.serial = LastKnownRequestProcessed(
1114
        winPtr->display);
1115
    event.xcrossing.send_event = GENERATED_EVENT_MAGIC;
1116
    event.xcrossing.display = winPtr->display;
1117
    event.xcrossing.root = RootWindow(winPtr->display,
1118
            winPtr->screenNum);
1119
    event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
1120
    XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
1121
            &event.xcrossing.x_root, &event.xcrossing.y_root,
1122
            &dummy3, &dummy4, &event.xcrossing.state);
1123
    event.xcrossing.mode = mode;
1124
    event.xcrossing.focus = False;
1125
    TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0,
1126
            (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK);
1127
}
1128
 
1129
/*
1130
 *----------------------------------------------------------------------
1131
 *
1132
 * TkGrabDeadWindow --
1133
 *
1134
 *      This procedure is invoked whenever a window is deleted, so that
1135
 *      grab-related cleanup can be performed.
1136
 *
1137
 * Results:
1138
 *      None.
1139
 *
1140
 * Side effects:
1141
 *      Various cleanups happen, such as generating events to move the
1142
 *      pointer back to its "natural" window as if an ungrab had been
1143
 *      done.  See the code.
1144
 *
1145
 *----------------------------------------------------------------------
1146
 */
1147
 
1148
void
1149
TkGrabDeadWindow(winPtr)
1150
    register TkWindow *winPtr;          /* Window that is in the process
1151
                                         * of being deleted. */
1152
{
1153
    TkDisplay *dispPtr = winPtr->dispPtr;
1154
 
1155
    if (dispPtr->eventualGrabWinPtr == winPtr) {
1156
        /*
1157
         * Grab window was deleted.  Release the grab.
1158
         */
1159
 
1160
        Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
1161
    } else if (dispPtr->buttonWinPtr == winPtr) {
1162
        ReleaseButtonGrab(dispPtr);
1163
    }
1164
    if (dispPtr->serverWinPtr == winPtr) {
1165
        if (winPtr->flags & TK_TOP_LEVEL) {
1166
            dispPtr->serverWinPtr = NULL;
1167
        } else {
1168
            dispPtr->serverWinPtr = winPtr->parentPtr;
1169
        }
1170
    }
1171
    if (dispPtr->grabWinPtr == winPtr) {
1172
        dispPtr->grabWinPtr = NULL;
1173
    }
1174
}
1175
 
1176
/*
1177
 *----------------------------------------------------------------------
1178
 *
1179
 * EatGrabEvents --
1180
 *
1181
 *      This procedure is called to eliminate any Enter, Leave,
1182
 *      FocusIn, or FocusOut events in the event queue for a
1183
 *      display that have mode NotifyGrab or NotifyUngrab and
1184
 *      have a serial number no less than a given value and are not
1185
 *      generated by the grab module.
1186
 *
1187
 * Results:
1188
 *      None.
1189
 *
1190
 * Side effects:
1191
 *      DispPtr's display gets sync-ed, and some of the events get
1192
 *      removed from the Tk event queue.
1193
 *
1194
 *----------------------------------------------------------------------
1195
 */
1196
 
1197
static void
1198
EatGrabEvents(dispPtr, serial)
1199
    TkDisplay *dispPtr;         /* Display from which to consume events. */
1200
    unsigned int serial;        /* Only discard events that have a serial
1201
                                 * number at least this great. */
1202
{
1203
    Tk_RestrictProc *oldProc;
1204
    GrabInfo info;
1205
    ClientData oldArg, dummy;
1206
 
1207
    info.display = dispPtr->display;
1208
    info.serial = serial;
1209
    TkpSync(info.display);
1210
    oldProc = Tk_RestrictEvents(GrabRestrictProc, (ClientData)&info, &oldArg);
1211
    while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
1212
    }
1213
    Tk_RestrictEvents(oldProc, oldArg, &dummy);
1214
}
1215
 
1216
/*
1217
 *----------------------------------------------------------------------
1218
 *
1219
 * GrabRestrictProc --
1220
 *
1221
 *      A Tk_RestrictProc used by EatGrabEvents to eliminate any
1222
 *      Enter, Leave, FocusIn, or FocusOut events in the event queue
1223
 *      for a display that has mode NotifyGrab or NotifyUngrab and
1224
 *      have a serial number no less than a given value.
1225
 *
1226
 * Results:
1227
 *      Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
1228
 *
1229
 * Side effects:
1230
 *      None.
1231
 *
1232
 *----------------------------------------------------------------------
1233
 */
1234
 
1235
static Tk_RestrictAction
1236
GrabRestrictProc(arg, eventPtr)
1237
    ClientData arg;
1238
    XEvent *eventPtr;
1239
{
1240
    GrabInfo *info = (GrabInfo *) arg;
1241
    int mode, diff;
1242
 
1243
    /*
1244
     * The diff caculation is trickier than it may seem.  Don't forget
1245
     * that serial numbers can wrap around, so can't compare the two
1246
     * serial numbers directly.
1247
     */
1248
 
1249
    diff = eventPtr->xany.serial - info->serial;
1250
    if ((eventPtr->type == EnterNotify)
1251
            || (eventPtr->type == LeaveNotify)) {
1252
        mode = eventPtr->xcrossing.mode;
1253
    } else if ((eventPtr->type == FocusIn)
1254
            || (eventPtr->type == FocusOut)) {
1255
        mode = eventPtr->xfocus.mode;
1256
    } else {
1257
        mode = NotifyNormal;
1258
    }
1259
    if ((info->display != eventPtr->xany.display) || (mode == NotifyNormal)
1260
            || (diff < 0)) {
1261
        return TK_DEFER_EVENT;
1262
    } else {
1263
        return TK_DISCARD_EVENT;
1264
    }
1265
}
1266
 
1267
/*
1268
 *----------------------------------------------------------------------
1269
 *
1270
 * QueueGrabWindowChange --
1271
 *
1272
 *      This procedure queues a special event in the Tcl event queue,
1273
 *      which will cause the "grabWinPtr" field for the display to get
1274
 *      modified when the event is processed.  This is needed to make
1275
 *      sure that the grab window changes at the proper time relative
1276
 *      to grab-related enter and leave events that are also in the
1277
 *      queue.  In particular, this approach works even when multiple
1278
 *      grabs and ungrabs happen back-to-back.
1279
 *
1280
 * Results:
1281
 *      None.
1282
 *
1283
 * Side effects:
1284
 *      DispPtr->grabWinPtr will be modified later (by GrabWinEventProc)
1285
 *      when the event is removed from the grab event queue.
1286
 *
1287
 *----------------------------------------------------------------------
1288
 */
1289
 
1290
static void
1291
QueueGrabWindowChange(dispPtr, grabWinPtr)
1292
    TkDisplay *dispPtr;         /* Display on which to change the grab
1293
                                 * window. */
1294
    TkWindow *grabWinPtr;       /* Window that is to become the new grab
1295
                                 * window (may be NULL). */
1296
{
1297
    NewGrabWinEvent *grabEvPtr;
1298
 
1299
    grabEvPtr = (NewGrabWinEvent *) ckalloc(sizeof(NewGrabWinEvent));
1300
    grabEvPtr->header.proc = GrabWinEventProc;
1301
    grabEvPtr->dispPtr = dispPtr;
1302
    if (grabWinPtr == NULL) {
1303
        grabEvPtr->grabWindow = None;
1304
    } else {
1305
        grabEvPtr->grabWindow = grabWinPtr->window;
1306
    }
1307
    Tcl_QueueEvent(&grabEvPtr->header, TCL_QUEUE_MARK);
1308
    dispPtr->eventualGrabWinPtr = grabWinPtr;
1309
}
1310
 
1311
/*
1312
 *----------------------------------------------------------------------
1313
 *
1314
 * GrabWinEventProc --
1315
 *
1316
 *      This procedure is invoked as a handler for Tcl_Events of type
1317
 *      NewGrabWinEvent.  It updates the current grab window field in
1318
 *      a display.
1319
 *
1320
 * Results:
1321
 *      Returns 1 if the event was processed, 0 if it should be deferred
1322
 *      for processing later.
1323
 *
1324
 * Side effects:
1325
 *      The grabWinPtr field is modified in the display associated with
1326
 *      the event.
1327
 *
1328
 *----------------------------------------------------------------------
1329
 */
1330
 
1331
static int
1332
GrabWinEventProc(evPtr, flags)
1333
    Tcl_Event *evPtr;           /* Event of type NewGrabWinEvent. */
1334
    int flags;                  /* Flags argument to Tk_DoOneEvent: indicates
1335
                                 * what kinds of events are being processed
1336
                                 * right now. */
1337
{
1338
    NewGrabWinEvent *grabEvPtr = (NewGrabWinEvent *) evPtr;
1339
 
1340
    grabEvPtr->dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow(
1341
            grabEvPtr->dispPtr->display, grabEvPtr->grabWindow);
1342
    return 1;
1343
}
1344
 
1345
/*
1346
 *----------------------------------------------------------------------
1347
 *
1348
 * FindCommonAncestor --
1349
 *
1350
 *      Given two windows, this procedure finds their least common
1351
 *      ancestor and also computes how many levels up this ancestor
1352
 *      is from each of the original windows.
1353
 *
1354
 * Results:
1355
 *      If the windows are in different applications or top-level
1356
 *      windows, then NULL is returned and *countPtr1 and *countPtr2
1357
 *      are set to the depths of the two windows in their respective
1358
 *      top-level windows (1 means the window is a top-level, 2 means
1359
 *      its parent is a top-level, and so on).  Otherwise, the return
1360
 *      value is a pointer to the common ancestor and the counts are
1361
 *      set to the distance of winPtr1 and winPtr2 from this ancestor
1362
 *      (1 means they're children, 2 means grand-children, etc.).
1363
 *
1364
 * Side effects:
1365
 *      None.
1366
 *
1367
 *----------------------------------------------------------------------
1368
 */
1369
 
1370
static TkWindow *
1371
FindCommonAncestor(winPtr1, winPtr2, countPtr1, countPtr2)
1372
    TkWindow *winPtr1;          /* First window.   May be NULL. */
1373
    TkWindow *winPtr2;          /* Second window.  May be NULL. */
1374
    int *countPtr1;             /* Store nesting level of winPtr1 within
1375
                                 * common ancestor here. */
1376
    int *countPtr2;             /* Store nesting level of winPtr2 within
1377
                                 * common ancestor here. */
1378
{
1379
    register TkWindow *winPtr;
1380
    TkWindow *ancestorPtr;
1381
    int count1, count2, i;
1382
 
1383
    /*
1384
     * Mark winPtr1 and all of its ancestors with a special flag bit.
1385
     */
1386
 
1387
    if (winPtr1 != NULL) {
1388
        for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) {
1389
            winPtr->flags |= TK_GRAB_FLAG;
1390
            if (winPtr->flags & TK_TOP_LEVEL) {
1391
                break;
1392
            }
1393
        }
1394
    }
1395
 
1396
    /*
1397
     * Search upwards from winPtr2 until an ancestor of winPtr1 is
1398
     * found or a top-level window is reached.
1399
     */
1400
 
1401
    winPtr = winPtr2;
1402
    count2 = 0;
1403
    ancestorPtr = NULL;
1404
    if (winPtr2 != NULL) {
1405
        for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) {
1406
            if (winPtr->flags & TK_GRAB_FLAG) {
1407
                ancestorPtr = winPtr;
1408
                break;
1409
            }
1410
            if (winPtr->flags & TK_TOP_LEVEL)  {
1411
                count2++;
1412
                break;
1413
            }
1414
        }
1415
    }
1416
 
1417
    /*
1418
     * Search upwards from winPtr1 again, clearing the flag bits and
1419
     * remembering how many levels up we had to go.
1420
     */
1421
 
1422
    if (winPtr1 == NULL) {
1423
        count1 = 0;
1424
    } else {
1425
        count1 = -1;
1426
        for (i = 0, winPtr = winPtr1; winPtr != NULL;
1427
                i++, winPtr = winPtr->parentPtr) {
1428
            winPtr->flags &= ~TK_GRAB_FLAG;
1429
            if (winPtr == ancestorPtr) {
1430
                count1 = i;
1431
            }
1432
            if (winPtr->flags & TK_TOP_LEVEL) {
1433
                if (count1 == -1) {
1434
                    count1 = i+1;
1435
                }
1436
                break;
1437
            }
1438
        }
1439
    }
1440
 
1441
    *countPtr1 = count1;
1442
    *countPtr2 = count2;
1443
    return ancestorPtr;
1444
}
1445
 
1446
/*
1447
 *----------------------------------------------------------------------
1448
 *
1449
 * TkPositionInTree --
1450
 *
1451
 *      Compute where the given window is relative to a particular
1452
 *      subtree of the window hierarchy.
1453
 *
1454
 * Results:
1455
 *
1456
 *      Returns TK_GRAB_IN_TREE if the window is contained in the
1457
 *      subtree.  Returns TK_GRAB_ANCESTOR if the window is an
1458
 *      ancestor of the subtree, in the same toplevel.  Otherwise
1459
 *      it returns TK_GRAB_EXCLUDED.
1460
 *
1461
 * Side effects:
1462
 *      None.
1463
 *
1464
 *----------------------------------------------------------------------
1465
 */
1466
 
1467
int
1468
TkPositionInTree(winPtr, treePtr)
1469
    TkWindow *winPtr;           /* Window to be checked. */
1470
    TkWindow *treePtr;          /* Root of tree to compare against. */
1471
{
1472
    TkWindow *winPtr2;
1473
 
1474
    for (winPtr2 = winPtr; winPtr2 != treePtr;
1475
           winPtr2 = winPtr2->parentPtr) {
1476
        if (winPtr2 == NULL) {
1477
            for (winPtr2 = treePtr; winPtr2 != NULL;
1478
                    winPtr2 = winPtr2->parentPtr) {
1479
                if (winPtr2 == winPtr) {
1480
                    return TK_GRAB_ANCESTOR;
1481
                }
1482
                if (winPtr2->flags & TK_TOP_LEVEL) {
1483
                    break;
1484
                }
1485
            }
1486
            return TK_GRAB_EXCLUDED;
1487
        }
1488
    }
1489
    return TK_GRAB_IN_TREE;
1490
}
1491
 
1492
/*
1493
 *----------------------------------------------------------------------
1494
 *
1495
 * TkGrabState --
1496
 *
1497
 *      Given a window, this procedure returns a value that indicates
1498
 *      the grab state of the application relative to the window.
1499
 *
1500
 * Results:
1501
 *      The return value is one of three things:
1502
 *          TK_GRAB_NONE -      no grab is in effect.
1503
 *          TK_GRAB_IN_TREE -   there is a grab in effect, and winPtr
1504
 *                              is in the grabbed subtree.
1505
 *          TK_GRAB_ANCESTOR -  there is a grab in effect;  winPtr is
1506
 *                              an ancestor of the grabbed window, in
1507
 *                              the same toplevel.
1508
 *          TK_GRAB_EXCLUDED -  there is a grab in effect; winPtr is
1509
 *                              outside the tree of the grab and is not
1510
 *                              an ancestor of the grabbed window in the
1511
 *                              same toplevel.
1512
 *
1513
 * Side effects:
1514
 *      None.
1515
 *
1516
 *----------------------------------------------------------------------
1517
 */
1518
 
1519
int
1520
TkGrabState(winPtr)
1521
    TkWindow *winPtr;           /* Window for which grab information is
1522
                                 * needed. */
1523
{
1524
    TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
1525
 
1526
    if (grabWinPtr == NULL) {
1527
        return TK_GRAB_NONE;
1528
    }
1529
    if ((winPtr->mainPtr != grabWinPtr->mainPtr)
1530
            && !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) {
1531
        return TK_GRAB_NONE;
1532
    }
1533
 
1534
    return TkPositionInTree(winPtr, grabWinPtr);
1535
}

powered by: WebSVN 2.1.0

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