]>
cvs.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 *----------------------------------------------------------------------
89 ClientData clientData
, /* Main window associated with
91 Tcl_Interp
*interp
, /* Current interpreter. */
92 int argc
, /* Number of arguments. */
93 char **argv
/* Argument strings. */
96 TkWindow
*winPtr
= (TkWindow
*) clientData
;
97 int length
, lockScreen
;
102 Tcl_AppendResult(interp
, "wrong # args: should be \"",
103 argv
[0], " ?-global? ?window?\"", (char *) NULL
);
107 if ((winPtr
->dispPtr
->grabWinPtr
!= NULL
)
108 && (winPtr
->dispPtr
->grabWinPtr
->mainPtr
109 == winPtr
->mainPtr
)) {
110 interp
->result
= Tk_PathName(winPtr
->dispPtr
->grabWinPtr
);
112 interp
->result
= "none";
117 length
= strlen(argv
[1]);
118 if (strncmp(argv
[1], "-off", length
) == 0) {
121 if ((strncmp(argv
[1], "-global", length
) != 0) || (length
< 2)) {
131 if ((window
[0] == '\0')
132 || (strncmp(window
, "none", strlen(window
)) == 0)) {
133 Tk_Ungrab((Tk_Window
) winPtr
);
137 tkwin
= Tk_NameToWindow(interp
, window
, (Tk_Window
) winPtr
);
141 if (lockScreen
< 0) {
144 return Tk_Grab(interp
, tkwin
, lockScreen
);
151 *----------------------------------------------------------------------
155 * Grabs the pointer and keyboard, so that mouse-related events are
156 * only reported relative to a given window and its descendants.
159 * A standard Tcl result is returned. TCL_OK is the normal return
160 * value; if the grab could not be set then TCL_ERROR is returned
161 * and interp->result will hold an error message.
164 * Once this call completes successfully, no window outside the
165 * tree rooted at tkwin will receive pointer- or keyboard-related
166 * events until the next call to Tk_Ungrab. If a previous grab was
167 * in effect within this application, then it is replaced with a new
170 *----------------------------------------------------------------------
175 Tcl_Interp
*interp
, /* Used for error reporting. */
176 Tk_Window tkwin
, /* Window on whose behalf the pointer
177 * is to be grabbed. */
178 int grabGlobal
/* Non-zero means issue a grab to the
179 * server so that no other application
180 * gets mouse or keyboard events.
181 * Zero means the grab only applies
182 * within this application. */
186 TkWindow
*winPtr
= (TkWindow
*) tkwin
;
187 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
188 int grabRequest
, inSequence
, ignoring
, numEvents
, i
, diff
;
189 XEvent
*events
, *eventPtr
;
192 if (dispPtr
->grabWinPtr
!= NULL
) {
193 if ((dispPtr
->grabWinPtr
== winPtr
)
194 && (grabGlobal
== ((dispPtr
->grabFlags
& GRAB_GLOBAL
) != 0))) {
197 if (dispPtr
->grabWinPtr
->mainPtr
!= winPtr
->mainPtr
) {
199 interp
->result
= "grab failed: another application has grab";
206 grabRequest
= NextRequest(dispPtr
->display
);
207 grabResult
= XGrabPointer(dispPtr
->display
, Tk_WindowId(tkwin
),
208 True
, ButtonPressMask
|ButtonReleaseMask
|ButtonMotionMask
|PointerMotionMask
,
209 GrabModeAsync
, GrabModeAsync
, None
, None
,
210 TkCurrentTime(dispPtr
));
211 if (grabResult
!= 0) {
213 if (grabResult
== GrabNotViewable
) {
214 interp
->result
= "grab failed: window not viewable";
215 } else if (grabResult
== AlreadyGrabbed
) {
217 } else if (grabResult
== GrabFrozen
) {
218 interp
->result
= "grab failed: keyboard or pointer frozen";
219 } else if (grabResult
== GrabInvalidTime
) {
220 interp
->result
= "grab failed: invalid time";
224 sprintf(msg
, "grab failed for unknown reason (code %d)",
226 Tcl_AppendResult(interp
, msg
, (char *) NULL
);
230 grabResult
= XGrabKeyboard(dispPtr
->display
, Tk_WindowId(tkwin
),
231 False
, GrabModeAsync
, GrabModeAsync
, TkCurrentTime(dispPtr
));
232 if (grabResult
!= 0) {
233 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
236 dispPtr
->grabFlags
|= GRAB_GLOBAL
;
239 * The call to XUngrabPointer below is needed to release any
240 * existing auto-grab due to a button press. This is needed
241 * so that local grabs behave the same as global grabs (the
242 * button grab is released by the X server in a global grab).
245 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
246 grabRequest
= LastKnownRequestProcessed(dispPtr
->display
);
247 dispPtr
->grabFlags
&= ~GRAB_GLOBAL
;
250 * Since we're not telling the server about the grab, we have
251 * to generate Leave and Enter events to move the pointer from
252 * its current window to the grab window.
255 MovePointer2(dispPtr
->pointerWinPtr
, winPtr
, NotifyGrab
);
257 dispPtr
->grabWinPtr
= winPtr
;
260 * When a grab occurs, X generates Enter and Leave events to move
261 * the pointer from its current window to the grab window, even if
262 * the current window is in the grab tree. We don't want these
263 * events getting through to the application if the current window
264 * is in the grab tree. In order to eliminate the bogus events,
265 * process all pending events and filter out the bogus ones.
267 * Also, filter out the final enter event into the grab window in
268 * any case: this event shouldn't be delivered until the mouse really
269 * moves into that window.
271 * The code below reads in all the pending events, filters out the bad
272 * ones, and then pushes back all the events that weren't filtered.
273 * Another alternative would be to simply process the events
274 * immediately rather than pushing them back again. However, this
275 * tends to interfere with scripts since it causes pending events
276 * to be processed during the "grab" command. The "grab" command
277 * might have been invoked in the middle of some computation where
278 * it's a bad idea to process new events.
281 XSync(dispPtr
->display
, False
);
282 numEvents
= QLength(dispPtr
->display
);
283 if (numEvents
== 0) {
286 events
= (XEvent
*) ckalloc((unsigned) (numEvents
* sizeof(XEvent
)));
287 for (i
= 0; i
< numEvents
; i
++) {
288 XNextEvent(dispPtr
->display
, &events
[i
]);
290 inSequence
= ignoring
= 0;
291 for (i
= numEvents
-1, eventPtr
= events
; i
>= 0; i
--, eventPtr
++) {
292 if (((eventPtr
->type
!= EnterNotify
)
293 && (eventPtr
->type
!= LeaveNotify
))
294 || (eventPtr
->xcrossing
.mode
!= NotifyGrab
)) {
299 * The diff caculcation below is trickier than you might think,
300 * due to the fact that the event serial number is unsigned and
301 * serial numbers can wrap around.
304 diff
= eventPtr
->xcrossing
.serial
;
306 if (!inSequence
&& (diff
>= 0)) {
308 * This is the first event of the grab sequence. See if its
309 * window is in the grab tree and ignore the sequence if it is.
313 if (XFindContext(dispPtr
->display
, eventPtr
->xcrossing
.window
,
314 tkWindowContext
, (void *) &winPtr2
) == 0) {
315 for ( ; winPtr2
!= NULL
; winPtr2
= winPtr2
->parentPtr
) {
316 if (winPtr2
== dispPtr
->grabWinPtr
) {
326 if (inSequence
&& (eventPtr
->type
== EnterNotify
)
327 && (dispPtr
->grabWinPtr
->window
328 == eventPtr
->xcrossing
.window
)) {
333 for (i
= numEvents
-1, eventPtr
= &events
[i
]; i
>= 0; i
--, eventPtr
--) {
334 if (eventPtr
->type
!= 0) {
335 XPutBackEvent(dispPtr
->display
, eventPtr
);
338 ckfree((char *) events
);
343 *----------------------------------------------------------------------
347 * Releases a grab on the mouse pointer and keyboard.
353 * Pointer and keyboard events will start being delivered to other
356 *----------------------------------------------------------------------
361 Tk_Window tkwin
/* Window that identifies display
362 * for grab to be released. */
365 TkDisplay
*dispPtr
= ((TkWindow
*) tkwin
)->dispPtr
;
366 int inSequence
, ignoring
, ungrabRequest
, numEvents
, i
, j
, diff
;
367 TkWindow
*grabWinPtr
, *winPtr
;
368 XEvent
*events
, *eventPtr
, *eventPtr2
;
370 grabWinPtr
= dispPtr
->grabWinPtr
;
371 if (grabWinPtr
== NULL
) {
374 dispPtr
->grabWinPtr
= NULL
;
375 dispPtr
->buttonWinPtr
= NULL
;
376 if (dispPtr
->grabFlags
& GRAB_GLOBAL
) {
377 ungrabRequest
= NextRequest(dispPtr
->display
);
378 XUngrabPointer(dispPtr
->display
, TkCurrentTime(dispPtr
));
379 XUngrabKeyboard(dispPtr
->display
, TkCurrentTime(dispPtr
));
380 XSync(dispPtr
->display
, False
);
382 ungrabRequest
= LastKnownRequestProcessed(dispPtr
->display
);
383 if ((dispPtr
->ungrabWinPtr
!= NULL
)
384 && (dispPtr
->ungrabWinPtr
->mainPtr
!= grabWinPtr
->mainPtr
)) {
387 * Don't report entries down into a window of a different
388 * application, since it's already seen those entries earlier.
391 dispPtr
->ungrabWinPtr
= NULL
;
393 MovePointer2(grabWinPtr
, dispPtr
->ungrabWinPtr
, NotifyUngrab
);
397 * We have to filter all the pending events in a fashion similar to
398 * Tk_Grab. As with grabs, the X server generates an Enter-Leave event
399 * sequence to move the pointer from the grab window back to its
400 * current window. We need to ignore this sequence if the pointer
401 * is being moved to a window that's already in the grab tree.
404 numEvents
= QLength(dispPtr
->display
);
405 if (numEvents
== 0) {
408 events
= (XEvent
*) ckalloc((unsigned) (numEvents
* sizeof(XEvent
)));
409 for (i
= 0; i
< numEvents
; i
++) {
410 XNextEvent(dispPtr
->display
, &events
[i
]);
412 inSequence
= ignoring
= 0;
413 for (i
= numEvents
-1, eventPtr
= events
; i
>= 0; i
--, eventPtr
++) {
414 if (((eventPtr
->type
!= EnterNotify
)
415 && (eventPtr
->type
!= LeaveNotify
))
416 || (eventPtr
->xcrossing
.mode
!= NotifyUngrab
)) {
419 diff
= eventPtr
->xcrossing
.serial
;
420 diff
-= ungrabRequest
;
421 if (!inSequence
&& (diff
>= 0)) {
424 * This is the first event of the ungrab sequence. Scan forward
425 * looking for the final Enter event in the sequence. Then see
426 * if that event's window is in the grab tree.
430 for (j
= i
, eventPtr2
= eventPtr
; j
>= 0; j
--, eventPtr2
++) {
431 if (eventPtr2
->type
== EnterNotify
) {
432 if (eventPtr2
->xcrossing
.mode
!= NotifyUngrab
) {
435 if ((eventPtr2
->xcrossing
.detail
!= NotifyAncestor
)
436 && (eventPtr2
->xcrossing
.detail
!= NotifyInferior
)
437 && (eventPtr2
->xcrossing
.detail
438 != NotifyNonlinear
)) {
441 if (XFindContext(dispPtr
->display
,
442 eventPtr2
->xcrossing
.window
,
443 tkWindowContext
, (void *) &winPtr
) == 0) {
444 for ( ; winPtr
!= NULL
; winPtr
= winPtr
->parentPtr
) {
445 if (winPtr
== grabWinPtr
) {
452 } else if ((eventPtr2
->type
!= LeaveNotify
)
453 || (eventPtr2
->xcrossing
.mode
!= NotifyUngrab
)) {
462 for (i
= numEvents
-1, eventPtr
= &events
[i
]; i
>= 0; i
--, eventPtr
--) {
463 if (eventPtr
->type
!= 0) {
464 XPutBackEvent(dispPtr
->display
, eventPtr
);
467 ckfree((char *) events
);
471 *----------------------------------------------------------------------
475 * This procedure is called for each pointer-related event, before
476 * the event has been processed. It does various things to make
477 * grabs work correctly.
480 * If the return value is 1 it means the event should be processed
481 * (event handlers should be invoked). If the return value is 0
482 * it means the event should be ignored in order to make grabs
483 * work correctly. Note: the event may be modified by this procedure.
486 * Grab state information may be updated.
488 *----------------------------------------------------------------------
493 register XEvent
*eventPtr
, /* Pointer to the event. */
494 TkWindow
*winPtr
/* Tk's information for window
495 * where event was reported. */
498 register TkWindow
*winPtr2
;
499 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
500 int outsideGrabTree
= 0;
502 int appGrabbed
= 0; /* Non-zero means event is being
503 * reported to an application that is
504 * affected by the grab. */
505 static unsigned int state
[] = {
506 Button1Mask
, Button2Mask
, Button3Mask
, Button4Mask
, Button5Mask
510 * Don't do any filtering on events generated by the event-sharing code.
513 if (eventPtr
== tkShareEventPtr
) {
518 * If a grab is in effect, see if the event is being reported to
519 * a window in the grab tree. Also see if the event is being reported
520 * to an application that is affected by the grab.
523 if (dispPtr
->grabWinPtr
!= NULL
) {
524 if ((winPtr
->mainPtr
== dispPtr
->grabWinPtr
->mainPtr
)
525 || (dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
528 for (winPtr2
= winPtr
; winPtr2
!= dispPtr
->grabWinPtr
;
529 winPtr2
= winPtr2
->parentPtr
) {
530 if (winPtr2
== NULL
) {
537 originalFlags
= dispPtr
->grabFlags
;
538 dispPtr
->grabFlags
&= ~GRAB_BUTTON_RELEASE
;
539 if ((eventPtr
->type
== EnterNotify
) || (eventPtr
->type
== LeaveNotify
)) {
540 if ((eventPtr
->type
== EnterNotify
)
541 && (eventPtr
->xcrossing
.detail
!= NotifyVirtual
)
542 && (eventPtr
->xcrossing
.detail
!= NotifyNonlinearVirtual
)) {
543 if ((dispPtr
->grabWinPtr
== NULL
)
544 || (dispPtr
->grabWinPtr
->mainPtr
== winPtr
->mainPtr
)) {
545 dispPtr
->ungrabWinPtr
= winPtr
;
547 dispPtr
->serverWinPtr
= winPtr
;
549 dispPtr
->serverWinPtr
= NULL
;
551 if (dispPtr
->grabWinPtr
!= NULL
) {
552 if (eventPtr
->xcrossing
.mode
== NotifyNormal
) {
554 * When a grab is active, X continues to report enter and
555 * leave events for windows outside the tree of the grab
556 * window. Detect these events and ignore them.
559 if (outsideGrabTree
&& appGrabbed
) {
564 * Make buttons have the same grab-like behavior inside a grab
565 * as they do outside a grab: do this by ignoring enter and
566 * leave events except for the window in which the button was
570 if ((dispPtr
->buttonWinPtr
!= NULL
)
571 && (winPtr
!= dispPtr
->buttonWinPtr
)) {
574 } else if (eventPtr
->xcrossing
.mode
== NotifyUngrab
) {
576 * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
579 dispPtr
->grabFlags
= originalFlags
;
580 if (outsideGrabTree
&& appGrabbed
581 && (dispPtr
->grabFlags
& GRAB_BUTTON_RELEASE
)) {
583 * The only way we get here is if a button was pressed,
584 * then moved to a different window and released. Enter
585 * and leave events were deferred while the button was
586 * down, but now we're getting them to move the pointer
587 * back to the right window, and this particular event
588 * is for a window outside the grab tree. Ignore it.
597 * Keep track of the window containing the mouse, in order to
598 * detect various bogus event sequences.
601 dispPtr
->pointerWinPtr
= dispPtr
->serverWinPtr
;
604 if ((dispPtr
->grabWinPtr
== NULL
) || !appGrabbed
) {
608 if (eventPtr
->type
== MotionNotify
) {
610 * When grabs are active, X reports motion events relative to the
611 * window under the pointer. Instead, it should report the events
612 * relative to the window the button went down in, if there is a
613 * button down. Otherwise, if the pointer window is outside the
614 * subtree of the grab window, the events should be reported
615 * relative to the grab window. Otherwise, the event should be
616 * reported to the pointer window.
620 if (dispPtr
->buttonWinPtr
!= NULL
) {
621 winPtr2
= dispPtr
->buttonWinPtr
;
622 } else if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
623 winPtr2
= dispPtr
->grabWinPtr
;
625 if (winPtr2
!= winPtr
) {
628 newEvent
= *eventPtr
;
629 ChangeEventWindow(&newEvent
, winPtr2
);
630 XPutBackEvent(winPtr2
->display
, &newEvent
);
637 * Process ButtonPress and ButtonRelease events:
638 * 1. Keep track of whether a button is down and what window it
640 * 2. If the first button goes down outside the grab tree, pretend
641 * it went down in the grab window. Note: it's important to
642 * redirect events to the grab window like this in order to make
643 * things like menus work, where button presses outside the
644 * grabbed menu need to be seen. An application can always
645 * ignore the events if they occur outside its window.
646 * 3. If a button press or release occurs outside the window where
647 * the first button was pressed, retarget the event so it's reported
648 * to the window where the first button was pressed.
649 * 4. If the last button is released in a window different than where
650 * the first button was pressed, generate Enter/Leave events to
651 * move the mouse from the button window to its current window.
652 * 5. If the grab is set at a time when a button is already down, or
653 * if the window where the button was pressed was deleted, then
654 * dispPtr->buttonWinPtr will stay NULL. Just forget about the
655 * auto-grab for the button press; events will go to whatever
656 * window contains the pointer. If this window isn't in the grab
657 * tree then redirect events to the grab window.
660 if ((eventPtr
->type
== ButtonPress
) || (eventPtr
->type
== ButtonRelease
)) {
661 winPtr2
= dispPtr
->buttonWinPtr
;
662 if (winPtr2
== NULL
) {
663 if (outsideGrabTree
) {
664 winPtr2
= dispPtr
->grabWinPtr
; /* Note 5. */
666 winPtr2
= winPtr
; /* Note 5. */
669 if (eventPtr
->type
== ButtonPress
) {
670 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
) == 0) {
671 if (outsideGrabTree
) {
674 newEvent
= *eventPtr
;
675 ChangeEventWindow(&newEvent
, dispPtr
->grabWinPtr
);
676 XPutBackEvent(dispPtr
->display
, &newEvent
);
677 return 0; /* Note 2. */
679 dispPtr
->buttonWinPtr
= winPtr
;
683 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
)
684 == state
[eventPtr
->xbutton
.button
- Button1
]) {
685 if ((dispPtr
->buttonWinPtr
!= winPtr
)
686 && (dispPtr
->buttonWinPtr
!= NULL
)) {
687 XEvent newEvent
; /* Note 4. */
690 * If the button release is made with pointer outside
691 * all applications, X reports it relative to the grab
692 * window. Change the current window to NULL to
693 * reflect that the pointer's outside everything. Do
694 * the same if the pointer's in a window that's not
695 * part of the grab tree.
698 if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
701 newEvent
= *eventPtr
;
702 newEvent
.xcrossing
.mode
= NotifyUngrab
;
703 newEvent
.xcrossing
.focus
= False
;
704 newEvent
.xcrossing
.state
=
705 eventPtr
->xbutton
.state
& ~ALL_BUTTONS
;
706 MovePointer(&newEvent
, dispPtr
->buttonWinPtr
, winPtr
);
708 dispPtr
->buttonWinPtr
= NULL
;
709 dispPtr
->grabFlags
|= GRAB_BUTTON_RELEASE
;
712 if (winPtr2
!= winPtr
) {
715 newEvent
= *eventPtr
;
716 ChangeEventWindow(&newEvent
, winPtr2
);
717 XPutBackEvent(dispPtr
->display
, &newEvent
);
718 return 0; /* Note 3. */
726 *----------------------------------------------------------------------
728 * ChangeEventWindow --
730 * Given an event and a new window to which the event should be
731 * retargeted, modify fields of the event so that the event is
732 * properly retargeted to the new window.
735 * The following fields of eventPtr are modified: window,
736 * subwindow, x, y, same_screen.
741 *----------------------------------------------------------------------
746 register XEvent
*eventPtr
, /* Event to retarget. Must have
747 * type ButtonPress, ButtonRelease, KeyPress,
748 * KeyRelease, MotionNotify, EnterNotify,
750 TkWindow
*winPtr
/* New target window for event. */
753 int x
, y
, sameScreen
, bd
;
754 register TkWindow
*childPtr
;
756 eventPtr
->xmotion
.window
= Tk_WindowId(winPtr
);
757 if (eventPtr
->xmotion
.root
==
758 RootWindow(winPtr
->display
, winPtr
->screenNum
)) {
759 Tk_GetRootCoords((Tk_Window
) winPtr
, &x
, &y
);
760 eventPtr
->xmotion
.x
= eventPtr
->xmotion
.x_root
- x
;
761 eventPtr
->xmotion
.y
= eventPtr
->xmotion
.y_root
- y
;
762 eventPtr
->xmotion
.subwindow
= None
;
763 for (childPtr
= winPtr
->childList
; childPtr
!= NULL
;
764 childPtr
= childPtr
->nextPtr
) {
765 if (childPtr
->flags
& TK_TOP_LEVEL
) {
768 x
= eventPtr
->xmotion
.x
- childPtr
->changes
.x
;
769 y
= eventPtr
->xmotion
.y
- childPtr
->changes
.y
;
770 bd
= childPtr
->changes
.border_width
;
771 if ((x
>= -bd
) && (y
>= -bd
)
772 && (x
< (childPtr
->changes
.width
+ bd
))
773 && (y
< (childPtr
->changes
.width
+ bd
))) {
774 eventPtr
->xmotion
.subwindow
= childPtr
->window
;
779 eventPtr
->xmotion
.x
= 0;
780 eventPtr
->xmotion
.y
= 0;
781 eventPtr
->xmotion
.subwindow
= None
;
784 if (eventPtr
->type
== MotionNotify
) {
785 eventPtr
->xmotion
.same_screen
= sameScreen
;
787 eventPtr
->xbutton
.same_screen
= sameScreen
;
792 *----------------------------------------------------------------------
796 * This procedure synthesizes EnterNotify and LeaveNotify events
797 * to correctly transfer the pointer from one window to another.
803 * Synthesized events may be pushed back onto the event queue.
804 * The event pointed to by eventPtr is modified.
806 *----------------------------------------------------------------------
811 XEvent
*eventPtr
, /* A template X event. Must have all fields
812 * properly set for EnterNotify and LeaveNotify
813 * events except window, subwindow, x, y,
814 * detail, and same_screen. (x_root and y_root
815 * must be valid, even though x and y needn't
817 TkWindow
*sourcePtr
, /* Window currently containing pointer (NULL
818 * means it's not one managed by this
820 TkWindow
*destPtr
/* Window that is to end up containing the
821 * pointer (NULL means it's not one managed
822 * by this process). */
826 register TkWindow
*ancestorPtr
; /* Lowest ancestor shared between
827 * sourcePtr and destPtr, or
828 * sourcePtr's top-level window if no
829 * shared ancestor. */
830 register TkWindow
*winPtr
;
831 int upLevels
, downLevels
, i
, j
;
834 * There are four possible cases to deal with:
836 * 1. SourcePtr and destPtr are the same. There's nothing to do in
838 * 2. SourcePtr is an ancestor of destPtr in the same top-level
839 * window. Must generate events down the window tree from source
841 * 3. DestPtr is an ancestor of sourcePtr in the same top-level
842 * window. Must generate events up the window tree from sourcePtr
844 * 4. All other cases. Must first generate events up the window tree
845 * from sourcePtr to its top-level, then down from destPtr's
846 * top-level to destPtr. This form is called "non-linear."
848 * The code below separates these four cases and decides how many levels
849 * up and down events have to be generated for.
852 if (sourcePtr
== destPtr
) {
857 * Mark destPtr and all of its ancestors with a special flag bit.
860 if (destPtr
!= NULL
) {
861 dispPtr
= destPtr
->dispPtr
;
862 for (winPtr
= destPtr
; ; winPtr
= winPtr
->parentPtr
) {
863 winPtr
->flags
|= TK_GRAB_FLAG
;
864 if (winPtr
->flags
& TK_TOP_LEVEL
) {
869 dispPtr
= sourcePtr
->dispPtr
;
873 * Search upwards from sourcePtr until an ancestor of destPtr is
874 * found or a top-level window is reached. Remember if we pass out
875 * of the grab tree along the way, since this means we'll have to
876 * skip some of the events that would otherwise be generated.
879 ancestorPtr
= sourcePtr
;
881 if (sourcePtr
!= NULL
) {
882 for (; ; upLevels
++, ancestorPtr
= ancestorPtr
->parentPtr
) {
883 if (ancestorPtr
->flags
& TK_GRAB_FLAG
) {
886 if (ancestorPtr
->flags
& TK_TOP_LEVEL
) {
894 * Search upwards from destPtr again, clearing the flag bits and
895 * remembering how many levels up we had to go.
898 if (destPtr
== NULL
) {
902 for (i
= 0, winPtr
= destPtr
; ; i
++, winPtr
= winPtr
->parentPtr
) {
903 winPtr
->flags
&= ~TK_GRAB_FLAG
;
904 if (winPtr
== ancestorPtr
) {
907 if (winPtr
->flags
& TK_TOP_LEVEL
) {
908 if (downLevels
== -1) {
917 * Generate enter/leave events and push them back onto the event
918 * queue. This has to be done backwards, since the last event
919 * pushed will be the first one processed.
922 #define PUSH_EVENT(w, t, d) \
923 if (w->window != None) { \
924 eventPtr->type = t; \
925 eventPtr->xcrossing.detail = d; \
926 ChangeEventWindow(eventPtr, w); \
927 XPutBackEvent(w->display, eventPtr); \
930 if (downLevels
== 0) {
933 * SourcePtr is an inferior of destPtr.
936 if (destPtr
!= NULL
) {
937 PUSH_EVENT(destPtr
, EnterNotify
, NotifyInferior
);
939 for (i
= upLevels
-1; i
> 0; i
--) {
940 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
941 winPtr
= winPtr
->parentPtr
, j
++) {
942 if (winPtr
== dispPtr
->grabWinPtr
) {
946 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyVirtual
);
947 nextIteration
: continue;
949 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyAncestor
);
950 } else if (upLevels
== 0) {
953 * DestPtr is an inferior of sourcePtr.
956 if (destPtr
!= NULL
) {
957 PUSH_EVENT(destPtr
, EnterNotify
, NotifyAncestor
);
959 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
960 winPtr
= winPtr
->parentPtr
, i
--) {
961 PUSH_EVENT(winPtr
, EnterNotify
, NotifyVirtual
);
963 if (sourcePtr
!= NULL
) {
964 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyInferior
);
969 * Non-linear: neither window is an inferior of the other.
972 if (destPtr
!= NULL
) {
973 PUSH_EVENT(destPtr
, EnterNotify
, NotifyNonlinear
);
975 if (destPtr
!= dispPtr
->grabWinPtr
) {
976 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
977 winPtr
= winPtr
->parentPtr
, i
--) {
978 PUSH_EVENT(winPtr
, EnterNotify
, NotifyNonlinearVirtual
);
979 if (winPtr
== dispPtr
->grabWinPtr
) {
984 for (i
= upLevels
-1; i
> 0; i
--) {
985 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
986 winPtr
= winPtr
->parentPtr
, j
++) {
987 if (winPtr
== dispPtr
->grabWinPtr
) {
991 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyNonlinearVirtual
);
992 nextWindow
: continue;
994 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyNonlinear
);
999 *----------------------------------------------------------------------
1003 * This procedure synthesizes EnterNotify and LeaveNotify events
1004 * to correctly transfer the pointer from one window to another.
1005 * It is different from MovePointer in that no template X event
1006 * needs to be supplied; this procedure generates the template
1007 * event and calls MovePointer.
1013 * Synthesized events may be pushed back onto the event queue.
1015 *----------------------------------------------------------------------
1020 TkWindow
*sourcePtr
, /* Window currently containing pointer (NULL
1021 * means it's not one managed by this
1023 TkWindow
*destPtr
, /* Window that is to end up containing the
1024 * pointer (NULL means it's not one managed
1025 * by this process). */
1026 int mode
/* Mode for enter/leave events, such as
1027 * NotifyNormal or NotifyUngrab. */
1031 Window dummy1
, dummy2
;
1036 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1038 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1043 event
.xcrossing
.serial
= LastKnownRequestProcessed(winPtr
->display
);
1044 event
.xcrossing
.send_event
= False
;
1045 event
.xcrossing
.display
= winPtr
->display
;
1046 event
.xcrossing
.root
= RootWindow(winPtr
->display
, winPtr
->screenNum
);
1047 event
.xcrossing
.time
= TkCurrentTime(winPtr
->dispPtr
);
1048 XQueryPointer(winPtr
->display
, winPtr
->window
, &dummy1
, &dummy2
,
1049 &event
.xcrossing
.x_root
, &event
.xcrossing
.y_root
,
1050 &dummy3
, &dummy4
, &event
.xcrossing
.state
);
1051 event
.xcrossing
.mode
= mode
;
1052 event
.xcrossing
.focus
= False
;
1053 MovePointer(&event
, sourcePtr
, destPtr
);
1057 *----------------------------------------------------------------------
1059 * TkGrabDeadWindow --
1061 * This procedure is invoked whenever a window is deleted, so that
1062 * grab-related cleanup can be performed.
1068 * Various cleanups happen, such as generating events to move the
1069 * pointer back to its "natural" window as if an ungrab had been
1070 * done. See the code.
1072 *----------------------------------------------------------------------
1077 register TkWindow
*winPtr
/* Window that is in the process
1078 * of being deleted. */
1081 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
1083 if (dispPtr
->grabWinPtr
== winPtr
) {
1084 dispPtr
->grabWinPtr
= NULL
;
1085 if (!(dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
1087 * Must generate enter/leave events to move back to the window
1088 * that contains the mouse pointer. We needn't filter events
1089 * here like we do in Tk_Ungrab because there are no children
1090 * of the grab window left in existence.
1094 if ((dispPtr
->ungrabWinPtr
!= NULL
)
1095 && (dispPtr
->ungrabWinPtr
->mainPtr
!= winPtr
->mainPtr
)) {
1096 dispPtr
->ungrabWinPtr
= NULL
;
1098 MovePointer2(winPtr
, dispPtr
->ungrabWinPtr
, NotifyUngrab
);
1100 } else if (dispPtr
->buttonWinPtr
== winPtr
) {
1102 * The window in which a button was pressed was deleted. Simulate
1103 * dropping the button auto-grab by generating Enter and Leave
1104 * events to move the pointer back to the window it's really on
1108 dispPtr
->buttonWinPtr
= NULL
;
1109 goto movePointerBack
;
1111 if (dispPtr
->ungrabWinPtr
== winPtr
) {
1112 dispPtr
->ungrabWinPtr
= NULL
;
1114 if (dispPtr
->pointerWinPtr
== winPtr
) {
1115 dispPtr
->pointerWinPtr
= NULL
;
1117 if (dispPtr
->serverWinPtr
== winPtr
) {
1118 dispPtr
->serverWinPtr
= NULL
;