]>
git.zerfleddert.de Git - micropolis/blob - src/tk/tkgrab.c
4 * This file provides procedures that implement grabs for Tk.
6 * Copyright 1992 Regents of the University of California.
7 * Permission to use, copy, modify, and distribute this
8 * software and its documentation for any purpose and without
9 * fee is hereby granted, provided that the above copyright
10 * notice appear in all copies. The University of California
11 * makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without
13 * express or implied warranty.
17 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkGrab.c,v 1.18 92/08/07 09:55:31 ouster Exp $ SPRITE (Berkeley)";
24 *-------------------------------------------------------------------
25 * Problems with current grab implementation (8/7/92):
27 * 1. In a local grab the synthesized events are always placed at the
28 * front of the event queue. If there are several grabs and ungrabs
29 * in a row, the groups of events for the different grabs/ungrabs
30 * end up in backwards order.
31 * 2. The variables serverWinPtr and pointerWinPtr are hardly used at
32 * all and should probably be eliminated.
33 * 3. The fact that grabWinPtr is set at the time a grab is set or
34 * released, rather than when its events are processed, means that
35 * it can get out of sync with the event queue if there's a rapid
36 * sequence of grabs or ungrabs. The only solution I can think of
37 * is to keep a parallel queue to the event queue to update grabWinPtr
38 * (or, synthesize an event to change the pointer?).
39 *-------------------------------------------------------------------
43 * Bit definitions for grabFlags field of TkDisplay structures:
45 * GRAB_GLOBAL 1 means this is a global grab (we grabbed via
46 * the server so all applications are locked out.
47 * 0 means this is a local grab that affects
48 * only this application.
49 * GRAB_BUTTON_RELEASE 1 means that a button-release event just
50 * occurred and we're in the middle of a sequence
51 * of Enter and Leave events with NotifyUngrab
56 #define GRAB_BUTTON_RELEASE 2
59 * Forward declarations for procedures declared later in this file:
62 static void ChangeEventWindow
_ANSI_ARGS_((XEvent
*eventPtr
,
64 static void MovePointer
_ANSI_ARGS_((XEvent
*eventPtr
,
65 TkWindow
*sourcePtr
, TkWindow
*destPtr
));
66 static void MovePointer2
_ANSI_ARGS_((TkWindow
*sourcePtr
,
67 TkWindow
*destPtr
, int mode
));
70 *----------------------------------------------------------------------
74 * This procedure is invoked to process the "grab" Tcl command.
75 * See the user documentation for details on what it does.
78 * A standard Tcl result.
81 * See the user documentation.
83 *----------------------------------------------------------------------
88 Tk_GrabCmd(clientData
, interp
, argc
, argv
)
89 ClientData clientData
; /* Main window associated with
91 Tcl_Interp
*interp
; /* Current interpreter. */
92 int argc
; /* Number of arguments. */
93 char **argv
; /* Argument strings. */
95 TkWindow
*winPtr
= (TkWindow
*) clientData
;
96 int length
, lockScreen
;
101 Tcl_AppendResult(interp
, "wrong # args: should be \"",
102 argv
[0], " ?-global? ?window?\"", (char *) NULL
);
106 if ((winPtr
->dispPtr
->grabWinPtr
!= NULL
)
107 && (winPtr
->dispPtr
->grabWinPtr
->mainPtr
108 == winPtr
->mainPtr
)) {
109 interp
->result
= Tk_PathName(winPtr
->dispPtr
->grabWinPtr
);
111 interp
->result
= "none";
116 length
= strlen(argv
[1]);
117 if (strncmp(argv
[1], "-off", length
) == 0) {
120 if ((strncmp(argv
[1], "-global", length
) != 0) || (length
< 2)) {
130 if ((window
[0] == '\0')
131 || (strncmp(window
, "none", strlen(window
)) == 0)) {
132 Tk_Ungrab((Tk_Window
) winPtr
);
136 tkwin
= Tk_NameToWindow(interp
, window
, (Tk_Window
) winPtr
);
140 if (lockScreen
< 0) {
143 return Tk_Grab(interp
, tkwin
, lockScreen
);
150 *----------------------------------------------------------------------
154 * Grabs the pointer and keyboard, so that mouse-related events are
155 * only reported relative to a given window and its descendants.
158 * A standard Tcl result is returned. TCL_OK is the normal return
159 * value; if the grab could not be set then TCL_ERROR is returned
160 * and interp->result will hold an error message.
163 * Once this call completes successfully, no window outside the
164 * tree rooted at tkwin will receive pointer- or keyboard-related
165 * events until the next call to Tk_Ungrab. If a previous grab was
166 * in effect within this application, then it is replaced with a new
169 *----------------------------------------------------------------------
173 Tk_Grab(interp
, tkwin
, grabGlobal
)
174 Tcl_Interp
*interp
; /* Used for error reporting. */
175 Tk_Window tkwin
; /* Window on whose behalf the pointer
176 * is to be grabbed. */
177 int grabGlobal
; /* Non-zero means issue a grab to the
178 * server so that no other application
179 * gets mouse or keyboard events.
180 * Zero means the grab only applies
181 * within this application. */
184 TkWindow
*winPtr
= (TkWindow
*) tkwin
;
185 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
186 int grabRequest
, inSequence
, ignoring
, numEvents
, i
, diff
;
187 XEvent
*events
, *eventPtr
;
190 if (dispPtr
->grabWinPtr
!= NULL
) {
191 if ((dispPtr
->grabWinPtr
== winPtr
)
192 && (grabGlobal
== ((dispPtr
->grabFlags
& GRAB_GLOBAL
) != 0))) {
195 if (dispPtr
->grabWinPtr
->mainPtr
!= winPtr
->mainPtr
) {
197 interp
->result
= "grab failed: another application has grab";
204 grabRequest
= NextRequest(dispPtr
->display
);
205 grabResult
= XGrabPointer(dispPtr
->display
, Tk_WindowId(tkwin
),
206 True
, ButtonPressMask
|ButtonReleaseMask
|ButtonMotionMask
|PointerMotionMask
,
207 GrabModeAsync
, GrabModeAsync
, None
, None
,
208 TkCurrentTime(dispPtr
));
209 if (grabResult
!= 0) {
211 if (grabResult
== GrabNotViewable
) {
212 interp
->result
= "grab failed: window not viewable";
213 } else if (grabResult
== AlreadyGrabbed
) {
215 } else if (grabResult
== GrabFrozen
) {
216 interp
->result
= "grab failed: keyboard or pointer frozen";
217 } else if (grabResult
== GrabInvalidTime
) {
218 interp
->result
= "grab failed: invalid time";
222 sprintf(msg
, "grab failed for unknown reason (code %d)",
224 Tcl_AppendResult(interp
, msg
, (char *) NULL
);
228 grabResult
= XGrabKeyboard(dispPtr
->display
, Tk_WindowId(tkwin
),
229 False
, GrabModeAsync
, GrabModeAsync
, TkCurrentTime(dispPtr
));
230 if (grabResult
!= 0) {
231 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
234 dispPtr
->grabFlags
|= GRAB_GLOBAL
;
237 * The call to XUngrabPointer below is needed to release any
238 * existing auto-grab due to a button press. This is needed
239 * so that local grabs behave the same as global grabs (the
240 * button grab is released by the X server in a global grab).
243 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
244 grabRequest
= LastKnownRequestProcessed(dispPtr
->display
);
245 dispPtr
->grabFlags
&= ~GRAB_GLOBAL
;
248 * Since we're not telling the server about the grab, we have
249 * to generate Leave and Enter events to move the pointer from
250 * its current window to the grab window.
253 MovePointer2(dispPtr
->pointerWinPtr
, winPtr
, NotifyGrab
);
255 dispPtr
->grabWinPtr
= winPtr
;
258 * When a grab occurs, X generates Enter and Leave events to move
259 * the pointer from its current window to the grab window, even if
260 * the current window is in the grab tree. We don't want these
261 * events getting through to the application if the current window
262 * is in the grab tree. In order to eliminate the bogus events,
263 * process all pending events and filter out the bogus ones.
265 * Also, filter out the final enter event into the grab window in
266 * any case: this event shouldn't be delivered until the mouse really
267 * moves into that window.
269 * The code below reads in all the pending events, filters out the bad
270 * ones, and then pushes back all the events that weren't filtered.
271 * Another alternative would be to simply process the events
272 * immediately rather than pushing them back again. However, this
273 * tends to interfere with scripts since it causes pending events
274 * to be processed during the "grab" command. The "grab" command
275 * might have been invoked in the middle of some computation where
276 * it's a bad idea to process new events.
279 XSync(dispPtr
->display
, False
);
280 numEvents
= QLength(dispPtr
->display
);
281 if (numEvents
== 0) {
284 events
= (XEvent
*) ckalloc((unsigned) (numEvents
* sizeof(XEvent
)));
285 for (i
= 0; i
< numEvents
; i
++) {
286 XNextEvent(dispPtr
->display
, &events
[i
]);
288 inSequence
= ignoring
= 0;
289 for (i
= numEvents
-1, eventPtr
= events
; i
>= 0; i
--, eventPtr
++) {
290 if (((eventPtr
->type
!= EnterNotify
)
291 && (eventPtr
->type
!= LeaveNotify
))
292 || (eventPtr
->xcrossing
.mode
!= NotifyGrab
)) {
297 * The diff caculcation below is trickier than you might think,
298 * due to the fact that the event serial number is unsigned and
299 * serial numbers can wrap around.
302 diff
= eventPtr
->xcrossing
.serial
;
304 if (!inSequence
&& (diff
>= 0)) {
306 * This is the first event of the grab sequence. See if its
307 * window is in the grab tree and ignore the sequence if it is.
311 if (XFindContext(dispPtr
->display
, eventPtr
->xcrossing
.window
,
312 tkWindowContext
, (void *) &winPtr2
) == 0) {
313 for ( ; winPtr2
!= NULL
; winPtr2
= winPtr2
->parentPtr
) {
314 if (winPtr2
== dispPtr
->grabWinPtr
) {
324 if (inSequence
&& (eventPtr
->type
== EnterNotify
)
325 && (dispPtr
->grabWinPtr
->window
326 == eventPtr
->xcrossing
.window
)) {
331 for (i
= numEvents
-1, eventPtr
= &events
[i
]; i
>= 0; i
--, eventPtr
--) {
332 if (eventPtr
->type
!= 0) {
333 XPutBackEvent(dispPtr
->display
, eventPtr
);
336 ckfree((char *) events
);
341 *----------------------------------------------------------------------
345 * Releases a grab on the mouse pointer and keyboard.
351 * Pointer and keyboard events will start being delivered to other
354 *----------------------------------------------------------------------
359 Tk_Window tkwin
; /* Window that identifies display
360 * for grab to be released. */
362 TkDisplay
*dispPtr
= ((TkWindow
*) tkwin
)->dispPtr
;
363 int inSequence
, ignoring
, ungrabRequest
, numEvents
, i
, j
, diff
;
364 TkWindow
*grabWinPtr
, *winPtr
;
365 XEvent
*events
, *eventPtr
, *eventPtr2
;
367 grabWinPtr
= dispPtr
->grabWinPtr
;
368 if (grabWinPtr
== NULL
) {
371 dispPtr
->grabWinPtr
= NULL
;
372 dispPtr
->buttonWinPtr
= NULL
;
373 if (dispPtr
->grabFlags
& GRAB_GLOBAL
) {
374 ungrabRequest
= NextRequest(dispPtr
->display
);
375 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
376 XUngrabKeyboard(dispPtr
->display
, TkCurrentTime(dispPtr
));
377 XSync(dispPtr
->display
, False
);
379 ungrabRequest
= LastKnownRequestProcessed(dispPtr
->display
);
380 if ((dispPtr
->ungrabWinPtr
!= NULL
)
381 && (dispPtr
->ungrabWinPtr
->mainPtr
!= grabWinPtr
->mainPtr
)) {
384 * Don't report entries down into a window of a different
385 * application, since it's already seen those entries earlier.
388 dispPtr
->ungrabWinPtr
= NULL
;
390 MovePointer2(grabWinPtr
, dispPtr
->ungrabWinPtr
, NotifyUngrab
);
394 * We have to filter all the pending events in a fashion similar to
395 * Tk_Grab. As with grabs, the X server generates an Enter-Leave event
396 * sequence to move the pointer from the grab window back to its
397 * current window. We need to ignore this sequence if the pointer
398 * is being moved to a window that's already in the grab tree.
401 numEvents
= QLength(dispPtr
->display
);
402 if (numEvents
== 0) {
405 events
= (XEvent
*) ckalloc((unsigned) (numEvents
* sizeof(XEvent
)));
406 for (i
= 0; i
< numEvents
; i
++) {
407 XNextEvent(dispPtr
->display
, &events
[i
]);
409 inSequence
= ignoring
= 0;
410 for (i
= numEvents
-1, eventPtr
= events
; i
>= 0; i
--, eventPtr
++) {
411 if (((eventPtr
->type
!= EnterNotify
)
412 && (eventPtr
->type
!= LeaveNotify
))
413 || (eventPtr
->xcrossing
.mode
!= NotifyUngrab
)) {
416 diff
= eventPtr
->xcrossing
.serial
;
417 diff
-= ungrabRequest
;
418 if (!inSequence
&& (diff
>= 0)) {
421 * This is the first event of the ungrab sequence. Scan forward
422 * looking for the final Enter event in the sequence. Then see
423 * if that event's window is in the grab tree.
427 for (j
= i
, eventPtr2
= eventPtr
; j
>= 0; j
--, eventPtr2
++) {
428 if (eventPtr2
->type
== EnterNotify
) {
429 if (eventPtr2
->xcrossing
.mode
!= NotifyUngrab
) {
432 if ((eventPtr2
->xcrossing
.detail
!= NotifyAncestor
)
433 && (eventPtr2
->xcrossing
.detail
!= NotifyInferior
)
434 && (eventPtr2
->xcrossing
.detail
435 != NotifyNonlinear
)) {
438 if (XFindContext(dispPtr
->display
,
439 eventPtr2
->xcrossing
.window
,
440 tkWindowContext
, (void *) &winPtr
) == 0) {
441 for ( ; winPtr
!= NULL
; winPtr
= winPtr
->parentPtr
) {
442 if (winPtr
== grabWinPtr
) {
449 } else if ((eventPtr2
->type
!= LeaveNotify
)
450 || (eventPtr2
->xcrossing
.mode
!= NotifyUngrab
)) {
459 for (i
= numEvents
-1, eventPtr
= &events
[i
]; i
>= 0; i
--, eventPtr
--) {
460 if (eventPtr
->type
!= 0) {
461 XPutBackEvent(dispPtr
->display
, eventPtr
);
464 ckfree((char *) events
);
468 *----------------------------------------------------------------------
472 * This procedure is called for each pointer-related event, before
473 * the event has been processed. It does various things to make
474 * grabs work correctly.
477 * If the return value is 1 it means the event should be processed
478 * (event handlers should be invoked). If the return value is 0
479 * it means the event should be ignored in order to make grabs
480 * work correctly. Note: the event may be modified by this procedure.
483 * Grab state information may be updated.
485 *----------------------------------------------------------------------
489 TkPointerEvent(eventPtr
, winPtr
)
490 register XEvent
*eventPtr
; /* Pointer to the event. */
491 TkWindow
*winPtr
; /* Tk's information for window
492 * where event was reported. */
494 register TkWindow
*winPtr2
;
495 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
496 int outsideGrabTree
= 0;
498 int appGrabbed
= 0; /* Non-zero means event is being
499 * reported to an application that is
500 * affected by the grab. */
501 static unsigned int state
[] = {
502 Button1Mask
, Button2Mask
, Button3Mask
, Button4Mask
, Button5Mask
506 * Don't do any filtering on events generated by the event-sharing code.
509 if (eventPtr
== tkShareEventPtr
) {
514 * If a grab is in effect, see if the event is being reported to
515 * a window in the grab tree. Also see if the event is being reported
516 * to an application that is affected by the grab.
519 if (dispPtr
->grabWinPtr
!= NULL
) {
520 if ((winPtr
->mainPtr
== dispPtr
->grabWinPtr
->mainPtr
)
521 || (dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
524 for (winPtr2
= winPtr
; winPtr2
!= dispPtr
->grabWinPtr
;
525 winPtr2
= winPtr2
->parentPtr
) {
526 if (winPtr2
== NULL
) {
533 originalFlags
= dispPtr
->grabFlags
;
534 dispPtr
->grabFlags
&= ~GRAB_BUTTON_RELEASE
;
535 if ((eventPtr
->type
== EnterNotify
) || (eventPtr
->type
== LeaveNotify
)) {
536 if ((eventPtr
->type
== EnterNotify
)
537 && (eventPtr
->xcrossing
.detail
!= NotifyVirtual
)
538 && (eventPtr
->xcrossing
.detail
!= NotifyNonlinearVirtual
)) {
539 if ((dispPtr
->grabWinPtr
== NULL
)
540 || (dispPtr
->grabWinPtr
->mainPtr
== winPtr
->mainPtr
)) {
541 dispPtr
->ungrabWinPtr
= winPtr
;
543 dispPtr
->serverWinPtr
= winPtr
;
545 dispPtr
->serverWinPtr
= NULL
;
547 if (dispPtr
->grabWinPtr
!= NULL
) {
548 if (eventPtr
->xcrossing
.mode
== NotifyNormal
) {
550 * When a grab is active, X continues to report enter and
551 * leave events for windows outside the tree of the grab
552 * window. Detect these events and ignore them.
555 if (outsideGrabTree
&& appGrabbed
) {
560 * Make buttons have the same grab-like behavior inside a grab
561 * as they do outside a grab: do this by ignoring enter and
562 * leave events except for the window in which the button was
566 if ((dispPtr
->buttonWinPtr
!= NULL
)
567 && (winPtr
!= dispPtr
->buttonWinPtr
)) {
570 } else if (eventPtr
->xcrossing
.mode
== NotifyUngrab
) {
572 * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
575 dispPtr
->grabFlags
= originalFlags
;
576 if (outsideGrabTree
&& appGrabbed
577 && (dispPtr
->grabFlags
& GRAB_BUTTON_RELEASE
)) {
579 * The only way we get here is if a button was pressed,
580 * then moved to a different window and released. Enter
581 * and leave events were deferred while the button was
582 * down, but now we're getting them to move the pointer
583 * back to the right window, and this particular event
584 * is for a window outside the grab tree. Ignore it.
593 * Keep track of the window containing the mouse, in order to
594 * detect various bogus event sequences.
597 dispPtr
->pointerWinPtr
= dispPtr
->serverWinPtr
;
600 if ((dispPtr
->grabWinPtr
== NULL
) || !appGrabbed
) {
604 if (eventPtr
->type
== MotionNotify
) {
606 * When grabs are active, X reports motion events relative to the
607 * window under the pointer. Instead, it should report the events
608 * relative to the window the button went down in, if there is a
609 * button down. Otherwise, if the pointer window is outside the
610 * subtree of the grab window, the events should be reported
611 * relative to the grab window. Otherwise, the event should be
612 * reported to the pointer window.
616 if (dispPtr
->buttonWinPtr
!= NULL
) {
617 winPtr2
= dispPtr
->buttonWinPtr
;
618 } else if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
619 winPtr2
= dispPtr
->grabWinPtr
;
621 if (winPtr2
!= winPtr
) {
624 newEvent
= *eventPtr
;
625 ChangeEventWindow(&newEvent
, winPtr2
);
626 XPutBackEvent(winPtr2
->display
, &newEvent
);
633 * Process ButtonPress and ButtonRelease events:
634 * 1. Keep track of whether a button is down and what window it
636 * 2. If the first button goes down outside the grab tree, pretend
637 * it went down in the grab window. Note: it's important to
638 * redirect events to the grab window like this in order to make
639 * things like menus work, where button presses outside the
640 * grabbed menu need to be seen. An application can always
641 * ignore the events if they occur outside its window.
642 * 3. If a button press or release occurs outside the window where
643 * the first button was pressed, retarget the event so it's reported
644 * to the window where the first button was pressed.
645 * 4. If the last button is released in a window different than where
646 * the first button was pressed, generate Enter/Leave events to
647 * move the mouse from the button window to its current window.
648 * 5. If the grab is set at a time when a button is already down, or
649 * if the window where the button was pressed was deleted, then
650 * dispPtr->buttonWinPtr will stay NULL. Just forget about the
651 * auto-grab for the button press; events will go to whatever
652 * window contains the pointer. If this window isn't in the grab
653 * tree then redirect events to the grab window.
656 if ((eventPtr
->type
== ButtonPress
) || (eventPtr
->type
== ButtonRelease
)) {
657 winPtr2
= dispPtr
->buttonWinPtr
;
658 if (winPtr2
== NULL
) {
659 if (outsideGrabTree
) {
660 winPtr2
= dispPtr
->grabWinPtr
; /* Note 5. */
662 winPtr2
= winPtr
; /* Note 5. */
665 if (eventPtr
->type
== ButtonPress
) {
666 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
) == 0) {
667 if (outsideGrabTree
) {
670 newEvent
= *eventPtr
;
671 ChangeEventWindow(&newEvent
, dispPtr
->grabWinPtr
);
672 XPutBackEvent(dispPtr
->display
, &newEvent
);
673 return 0; /* Note 2. */
675 dispPtr
->buttonWinPtr
= winPtr
;
679 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
)
680 == state
[eventPtr
->xbutton
.button
- Button1
]) {
681 if ((dispPtr
->buttonWinPtr
!= winPtr
)
682 && (dispPtr
->buttonWinPtr
!= NULL
)) {
683 XEvent newEvent
; /* Note 4. */
686 * If the button release is made with pointer outside
687 * all applications, X reports it relative to the grab
688 * window. Change the current window to NULL to
689 * reflect that the pointer's outside everything. Do
690 * the same if the pointer's in a window that's not
691 * part of the grab tree.
694 if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
697 newEvent
= *eventPtr
;
698 newEvent
.xcrossing
.mode
= NotifyUngrab
;
699 newEvent
.xcrossing
.focus
= False
;
700 newEvent
.xcrossing
.state
=
701 eventPtr
->xbutton
.state
& ~ALL_BUTTONS
;
702 MovePointer(&newEvent
, dispPtr
->buttonWinPtr
, winPtr
);
704 dispPtr
->buttonWinPtr
= NULL
;
705 dispPtr
->grabFlags
|= GRAB_BUTTON_RELEASE
;
708 if (winPtr2
!= winPtr
) {
711 newEvent
= *eventPtr
;
712 ChangeEventWindow(&newEvent
, winPtr2
);
713 XPutBackEvent(dispPtr
->display
, &newEvent
);
714 return 0; /* Note 3. */
722 *----------------------------------------------------------------------
724 * ChangeEventWindow --
726 * Given an event and a new window to which the event should be
727 * retargeted, modify fields of the event so that the event is
728 * properly retargeted to the new window.
731 * The following fields of eventPtr are modified: window,
732 * subwindow, x, y, same_screen.
737 *----------------------------------------------------------------------
741 ChangeEventWindow(eventPtr
, winPtr
)
742 register XEvent
*eventPtr
; /* Event to retarget. Must have
743 * type ButtonPress, ButtonRelease, KeyPress,
744 * KeyRelease, MotionNotify, EnterNotify,
746 TkWindow
*winPtr
; /* New target window for event. */
748 int x
, y
, sameScreen
, bd
;
749 register TkWindow
*childPtr
;
751 eventPtr
->xmotion
.window
= Tk_WindowId(winPtr
);
752 if (eventPtr
->xmotion
.root
==
753 RootWindow(winPtr
->display
, winPtr
->screenNum
)) {
754 Tk_GetRootCoords((Tk_Window
) winPtr
, &x
, &y
);
755 eventPtr
->xmotion
.x
= eventPtr
->xmotion
.x_root
- x
;
756 eventPtr
->xmotion
.y
= eventPtr
->xmotion
.y_root
- y
;
757 eventPtr
->xmotion
.subwindow
= None
;
758 for (childPtr
= winPtr
->childList
; childPtr
!= NULL
;
759 childPtr
= childPtr
->nextPtr
) {
760 if (childPtr
->flags
& TK_TOP_LEVEL
) {
763 x
= eventPtr
->xmotion
.x
- childPtr
->changes
.x
;
764 y
= eventPtr
->xmotion
.y
- childPtr
->changes
.y
;
765 bd
= childPtr
->changes
.border_width
;
766 if ((x
>= -bd
) && (y
>= -bd
)
767 && (x
< (childPtr
->changes
.width
+ bd
))
768 && (y
< (childPtr
->changes
.width
+ bd
))) {
769 eventPtr
->xmotion
.subwindow
= childPtr
->window
;
774 eventPtr
->xmotion
.x
= 0;
775 eventPtr
->xmotion
.y
= 0;
776 eventPtr
->xmotion
.subwindow
= None
;
779 if (eventPtr
->type
== MotionNotify
) {
780 eventPtr
->xmotion
.same_screen
= sameScreen
;
782 eventPtr
->xbutton
.same_screen
= sameScreen
;
787 *----------------------------------------------------------------------
791 * This procedure synthesizes EnterNotify and LeaveNotify events
792 * to correctly transfer the pointer from one window to another.
798 * Synthesized events may be pushed back onto the event queue.
799 * The event pointed to by eventPtr is modified.
801 *----------------------------------------------------------------------
805 MovePointer(eventPtr
, sourcePtr
, destPtr
)
806 XEvent
*eventPtr
; /* A template X event. Must have all fields
807 * properly set for EnterNotify and LeaveNotify
808 * events except window, subwindow, x, y,
809 * detail, and same_screen. (x_root and y_root
810 * must be valid, even though x and y needn't
812 TkWindow
*sourcePtr
; /* Window currently containing pointer (NULL
813 * means it's not one managed by this
815 TkWindow
*destPtr
; /* Window that is to end up containing the
816 * pointer (NULL means it's not one managed
817 * by this process). */
820 register TkWindow
*ancestorPtr
; /* Lowest ancestor shared between
821 * sourcePtr and destPtr, or
822 * sourcePtr's top-level window if no
823 * shared ancestor. */
824 register TkWindow
*winPtr
;
825 int upLevels
, downLevels
, i
, j
;
828 * There are four possible cases to deal with:
830 * 1. SourcePtr and destPtr are the same. There's nothing to do in
832 * 2. SourcePtr is an ancestor of destPtr in the same top-level
833 * window. Must generate events down the window tree from source
835 * 3. DestPtr is an ancestor of sourcePtr in the same top-level
836 * window. Must generate events up the window tree from sourcePtr
838 * 4. All other cases. Must first generate events up the window tree
839 * from sourcePtr to its top-level, then down from destPtr's
840 * top-level to destPtr. This form is called "non-linear."
842 * The code below separates these four cases and decides how many levels
843 * up and down events have to be generated for.
846 if (sourcePtr
== destPtr
) {
851 * Mark destPtr and all of its ancestors with a special flag bit.
854 if (destPtr
!= NULL
) {
855 dispPtr
= destPtr
->dispPtr
;
856 for (winPtr
= destPtr
; ; winPtr
= winPtr
->parentPtr
) {
857 winPtr
->flags
|= TK_GRAB_FLAG
;
858 if (winPtr
->flags
& TK_TOP_LEVEL
) {
863 dispPtr
= sourcePtr
->dispPtr
;
867 * Search upwards from sourcePtr until an ancestor of destPtr is
868 * found or a top-level window is reached. Remember if we pass out
869 * of the grab tree along the way, since this means we'll have to
870 * skip some of the events that would otherwise be generated.
873 ancestorPtr
= sourcePtr
;
875 if (sourcePtr
!= NULL
) {
876 for (; ; upLevels
++, ancestorPtr
= ancestorPtr
->parentPtr
) {
877 if (ancestorPtr
->flags
& TK_GRAB_FLAG
) {
880 if (ancestorPtr
->flags
& TK_TOP_LEVEL
) {
888 * Search upwards from destPtr again, clearing the flag bits and
889 * remembering how many levels up we had to go.
892 if (destPtr
== NULL
) {
896 for (i
= 0, winPtr
= destPtr
; ; i
++, winPtr
= winPtr
->parentPtr
) {
897 winPtr
->flags
&= ~TK_GRAB_FLAG
;
898 if (winPtr
== ancestorPtr
) {
901 if (winPtr
->flags
& TK_TOP_LEVEL
) {
902 if (downLevels
== -1) {
911 * Generate enter/leave events and push them back onto the event
912 * queue. This has to be done backwards, since the last event
913 * pushed will be the first one processed.
916 #define PUSH_EVENT(w, t, d) \
917 if (w->window != None) { \
918 eventPtr->type = t; \
919 eventPtr->xcrossing.detail = d; \
920 ChangeEventWindow(eventPtr, w); \
921 XPutBackEvent(w->display, eventPtr); \
924 if (downLevels
== 0) {
927 * SourcePtr is an inferior of destPtr.
930 if (destPtr
!= NULL
) {
931 PUSH_EVENT(destPtr
, EnterNotify
, NotifyInferior
);
933 for (i
= upLevels
-1; i
> 0; i
--) {
934 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
935 winPtr
= winPtr
->parentPtr
, j
++) {
936 if (winPtr
== dispPtr
->grabWinPtr
) {
940 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyVirtual
);
941 nextIteration
: continue;
943 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyAncestor
);
944 } else if (upLevels
== 0) {
947 * DestPtr is an inferior of sourcePtr.
950 if (destPtr
!= NULL
) {
951 PUSH_EVENT(destPtr
, EnterNotify
, NotifyAncestor
);
953 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
954 winPtr
= winPtr
->parentPtr
, i
--) {
955 PUSH_EVENT(winPtr
, EnterNotify
, NotifyVirtual
);
957 if (sourcePtr
!= NULL
) {
958 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyInferior
);
963 * Non-linear: neither window is an inferior of the other.
966 if (destPtr
!= NULL
) {
967 PUSH_EVENT(destPtr
, EnterNotify
, NotifyNonlinear
);
969 if (destPtr
!= dispPtr
->grabWinPtr
) {
970 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
971 winPtr
= winPtr
->parentPtr
, i
--) {
972 PUSH_EVENT(winPtr
, EnterNotify
, NotifyNonlinearVirtual
);
973 if (winPtr
== dispPtr
->grabWinPtr
) {
978 for (i
= upLevels
-1; i
> 0; i
--) {
979 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
980 winPtr
= winPtr
->parentPtr
, j
++) {
981 if (winPtr
== dispPtr
->grabWinPtr
) {
985 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyNonlinearVirtual
);
986 nextWindow
: continue;
988 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyNonlinear
);
993 *----------------------------------------------------------------------
997 * This procedure synthesizes EnterNotify and LeaveNotify events
998 * to correctly transfer the pointer from one window to another.
999 * It is different from MovePointer in that no template X event
1000 * needs to be supplied; this procedure generates the template
1001 * event and calls MovePointer.
1007 * Synthesized events may be pushed back onto the event queue.
1009 *----------------------------------------------------------------------
1013 MovePointer2(sourcePtr
, destPtr
, mode
)
1014 TkWindow
*sourcePtr
; /* Window currently containing pointer (NULL
1015 * means it's not one managed by this
1017 TkWindow
*destPtr
; /* Window that is to end up containing the
1018 * pointer (NULL means it's not one managed
1019 * by this process). */
1020 int mode
; /* Mode for enter/leave events, such as
1021 * NotifyNormal or NotifyUngrab. */
1024 Window dummy1
, dummy2
;
1029 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1031 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1036 event
.xcrossing
.serial
= LastKnownRequestProcessed(winPtr
->display
);
1037 event
.xcrossing
.send_event
= False
;
1038 event
.xcrossing
.display
= winPtr
->display
;
1039 event
.xcrossing
.root
= RootWindow(winPtr
->display
, winPtr
->screenNum
);
1040 event
.xcrossing
.time
= TkCurrentTime(winPtr
->dispPtr
);
1041 XQueryPointer(winPtr
->display
, winPtr
->window
, &dummy1
, &dummy2
,
1042 &event
.xcrossing
.x_root
, &event
.xcrossing
.y_root
,
1043 &dummy3
, &dummy4
, &event
.xcrossing
.state
);
1044 event
.xcrossing
.mode
= mode
;
1045 event
.xcrossing
.focus
= False
;
1046 MovePointer(&event
, sourcePtr
, destPtr
);
1050 *----------------------------------------------------------------------
1052 * TkGrabDeadWindow --
1054 * This procedure is invoked whenever a window is deleted, so that
1055 * grab-related cleanup can be performed.
1061 * Various cleanups happen, such as generating events to move the
1062 * pointer back to its "natural" window as if an ungrab had been
1063 * done. See the code.
1065 *----------------------------------------------------------------------
1069 TkGrabDeadWindow(winPtr
)
1070 register TkWindow
*winPtr
; /* Window that is in the process
1071 * of being deleted. */
1073 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
1075 if (dispPtr
->grabWinPtr
== winPtr
) {
1076 dispPtr
->grabWinPtr
= NULL
;
1077 if (!(dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
1079 * Must generate enter/leave events to move back to the window
1080 * that contains the mouse pointer. We needn't filter events
1081 * here like we do in Tk_Ungrab because there are no children
1082 * of the grab window left in existence.
1086 if ((dispPtr
->ungrabWinPtr
!= NULL
)
1087 && (dispPtr
->ungrabWinPtr
->mainPtr
!= winPtr
->mainPtr
)) {
1088 dispPtr
->ungrabWinPtr
= NULL
;
1090 MovePointer2(winPtr
, dispPtr
->ungrabWinPtr
, NotifyUngrab
);
1092 } else if (dispPtr
->buttonWinPtr
== winPtr
) {
1094 * The window in which a button was pressed was deleted. Simulate
1095 * dropping the button auto-grab by generating Enter and Leave
1096 * events to move the pointer back to the window it's really on
1100 dispPtr
->buttonWinPtr
= NULL
;
1101 goto movePointerBack
;
1103 if (dispPtr
->ungrabWinPtr
== winPtr
) {
1104 dispPtr
->ungrabWinPtr
= NULL
;
1106 if (dispPtr
->pointerWinPtr
== winPtr
) {
1107 dispPtr
->pointerWinPtr
= NULL
;
1109 if (dispPtr
->serverWinPtr
== winPtr
) {
1110 dispPtr
->serverWinPtr
= NULL
;