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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (show annotations) (download)
Sat Nov 5 10:54:17 2016 UTC (6 years, 3 months ago) by dashley
File MIME type: text/plain
File size: 49511 byte(s)
License and property (keyword) changes.
1 /* $Header$ */
2
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 /* End of tkgrab.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25