/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrab.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkgrab.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25