]>
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 *----------------------------------------------------------------------
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 #define ALL_BUTTONS \
502 (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
503 static unsigned int state
[] = {
504 Button1Mask
, Button2Mask
, Button3Mask
, Button4Mask
, Button5Mask
508 * Don't do any filtering on events generated by the event-sharing code.
511 if (eventPtr
== tkShareEventPtr
) {
516 * If a grab is in effect, see if the event is being reported to
517 * a window in the grab tree. Also see if the event is being reported
518 * to an application that is affected by the grab.
521 if (dispPtr
->grabWinPtr
!= NULL
) {
522 if ((winPtr
->mainPtr
== dispPtr
->grabWinPtr
->mainPtr
)
523 || (dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
526 for (winPtr2
= winPtr
; winPtr2
!= dispPtr
->grabWinPtr
;
527 winPtr2
= winPtr2
->parentPtr
) {
528 if (winPtr2
== NULL
) {
535 originalFlags
= dispPtr
->grabFlags
;
536 dispPtr
->grabFlags
&= ~GRAB_BUTTON_RELEASE
;
537 if ((eventPtr
->type
== EnterNotify
) || (eventPtr
->type
== LeaveNotify
)) {
538 if ((eventPtr
->type
== EnterNotify
)
539 && (eventPtr
->xcrossing
.detail
!= NotifyVirtual
)
540 && (eventPtr
->xcrossing
.detail
!= NotifyNonlinearVirtual
)) {
541 if ((dispPtr
->grabWinPtr
== NULL
)
542 || (dispPtr
->grabWinPtr
->mainPtr
== winPtr
->mainPtr
)) {
543 dispPtr
->ungrabWinPtr
= winPtr
;
545 dispPtr
->serverWinPtr
= winPtr
;
547 dispPtr
->serverWinPtr
= NULL
;
549 if (dispPtr
->grabWinPtr
!= NULL
) {
550 if (eventPtr
->xcrossing
.mode
== NotifyNormal
) {
552 * When a grab is active, X continues to report enter and
553 * leave events for windows outside the tree of the grab
554 * window. Detect these events and ignore them.
557 if (outsideGrabTree
&& appGrabbed
) {
562 * Make buttons have the same grab-like behavior inside a grab
563 * as they do outside a grab: do this by ignoring enter and
564 * leave events except for the window in which the button was
568 if ((dispPtr
->buttonWinPtr
!= NULL
)
569 && (winPtr
!= dispPtr
->buttonWinPtr
)) {
572 } else if (eventPtr
->xcrossing
.mode
== NotifyUngrab
) {
574 * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
577 dispPtr
->grabFlags
= originalFlags
;
578 if (outsideGrabTree
&& appGrabbed
579 && (dispPtr
->grabFlags
& GRAB_BUTTON_RELEASE
)) {
581 * The only way we get here is if a button was pressed,
582 * then moved to a different window and released. Enter
583 * and leave events were deferred while the button was
584 * down, but now we're getting them to move the pointer
585 * back to the right window, and this particular event
586 * is for a window outside the grab tree. Ignore it.
595 * Keep track of the window containing the mouse, in order to
596 * detect various bogus event sequences.
599 dispPtr
->pointerWinPtr
= dispPtr
->serverWinPtr
;
602 if ((dispPtr
->grabWinPtr
== NULL
) || !appGrabbed
) {
606 if (eventPtr
->type
== MotionNotify
) {
608 * When grabs are active, X reports motion events relative to the
609 * window under the pointer. Instead, it should report the events
610 * relative to the window the button went down in, if there is a
611 * button down. Otherwise, if the pointer window is outside the
612 * subtree of the grab window, the events should be reported
613 * relative to the grab window. Otherwise, the event should be
614 * reported to the pointer window.
618 if (dispPtr
->buttonWinPtr
!= NULL
) {
619 winPtr2
= dispPtr
->buttonWinPtr
;
620 } else if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
621 winPtr2
= dispPtr
->grabWinPtr
;
623 if (winPtr2
!= winPtr
) {
626 newEvent
= *eventPtr
;
627 ChangeEventWindow(&newEvent
, winPtr2
);
628 XPutBackEvent(winPtr2
->display
, &newEvent
);
635 * Process ButtonPress and ButtonRelease events:
636 * 1. Keep track of whether a button is down and what window it
638 * 2. If the first button goes down outside the grab tree, pretend
639 * it went down in the grab window. Note: it's important to
640 * redirect events to the grab window like this in order to make
641 * things like menus work, where button presses outside the
642 * grabbed menu need to be seen. An application can always
643 * ignore the events if they occur outside its window.
644 * 3. If a button press or release occurs outside the window where
645 * the first button was pressed, retarget the event so it's reported
646 * to the window where the first button was pressed.
647 * 4. If the last button is released in a window different than where
648 * the first button was pressed, generate Enter/Leave events to
649 * move the mouse from the button window to its current window.
650 * 5. If the grab is set at a time when a button is already down, or
651 * if the window where the button was pressed was deleted, then
652 * dispPtr->buttonWinPtr will stay NULL. Just forget about the
653 * auto-grab for the button press; events will go to whatever
654 * window contains the pointer. If this window isn't in the grab
655 * tree then redirect events to the grab window.
658 if ((eventPtr
->type
== ButtonPress
) || (eventPtr
->type
== ButtonRelease
)) {
659 winPtr2
= dispPtr
->buttonWinPtr
;
660 if (winPtr2
== NULL
) {
661 if (outsideGrabTree
) {
662 winPtr2
= dispPtr
->grabWinPtr
; /* Note 5. */
664 winPtr2
= winPtr
; /* Note 5. */
667 if (eventPtr
->type
== ButtonPress
) {
668 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
) == 0) {
669 if (outsideGrabTree
) {
672 newEvent
= *eventPtr
;
673 ChangeEventWindow(&newEvent
, dispPtr
->grabWinPtr
);
674 XPutBackEvent(dispPtr
->display
, &newEvent
);
675 return 0; /* Note 2. */
677 dispPtr
->buttonWinPtr
= winPtr
;
681 if ((eventPtr
->xbutton
.state
& ALL_BUTTONS
)
682 == state
[eventPtr
->xbutton
.button
- Button1
]) {
683 if ((dispPtr
->buttonWinPtr
!= winPtr
)
684 && (dispPtr
->buttonWinPtr
!= NULL
)) {
685 XEvent newEvent
; /* Note 4. */
688 * If the button release is made with pointer outside
689 * all applications, X reports it relative to the grab
690 * window. Change the current window to NULL to
691 * reflect that the pointer's outside everything. Do
692 * the same if the pointer's in a window that's not
693 * part of the grab tree.
696 if (outsideGrabTree
|| (dispPtr
->serverWinPtr
== NULL
)) {
699 newEvent
= *eventPtr
;
700 newEvent
.xcrossing
.mode
= NotifyUngrab
;
701 newEvent
.xcrossing
.focus
= False
;
702 newEvent
.xcrossing
.state
=
703 eventPtr
->xbutton
.state
& ~ALL_BUTTONS
;
704 MovePointer(&newEvent
, dispPtr
->buttonWinPtr
, winPtr
);
706 dispPtr
->buttonWinPtr
= NULL
;
707 dispPtr
->grabFlags
|= GRAB_BUTTON_RELEASE
;
710 if (winPtr2
!= winPtr
) {
713 newEvent
= *eventPtr
;
714 ChangeEventWindow(&newEvent
, winPtr2
);
715 XPutBackEvent(dispPtr
->display
, &newEvent
);
716 return 0; /* Note 3. */
724 *----------------------------------------------------------------------
726 * ChangeEventWindow --
728 * Given an event and a new window to which the event should be
729 * retargeted, modify fields of the event so that the event is
730 * properly retargeted to the new window.
733 * The following fields of eventPtr are modified: window,
734 * subwindow, x, y, same_screen.
739 *----------------------------------------------------------------------
743 ChangeEventWindow(eventPtr
, winPtr
)
744 register XEvent
*eventPtr
; /* Event to retarget. Must have
745 * type ButtonPress, ButtonRelease, KeyPress,
746 * KeyRelease, MotionNotify, EnterNotify,
748 TkWindow
*winPtr
; /* New target window for event. */
750 int x
, y
, sameScreen
, bd
;
751 register TkWindow
*childPtr
;
753 eventPtr
->xmotion
.window
= Tk_WindowId(winPtr
);
754 if (eventPtr
->xmotion
.root
==
755 RootWindow(winPtr
->display
, winPtr
->screenNum
)) {
756 Tk_GetRootCoords((Tk_Window
) winPtr
, &x
, &y
);
757 eventPtr
->xmotion
.x
= eventPtr
->xmotion
.x_root
- x
;
758 eventPtr
->xmotion
.y
= eventPtr
->xmotion
.y_root
- y
;
759 eventPtr
->xmotion
.subwindow
= None
;
760 for (childPtr
= winPtr
->childList
; childPtr
!= NULL
;
761 childPtr
= childPtr
->nextPtr
) {
762 if (childPtr
->flags
& TK_TOP_LEVEL
) {
765 x
= eventPtr
->xmotion
.x
- childPtr
->changes
.x
;
766 y
= eventPtr
->xmotion
.y
- childPtr
->changes
.y
;
767 bd
= childPtr
->changes
.border_width
;
768 if ((x
>= -bd
) && (y
>= -bd
)
769 && (x
< (childPtr
->changes
.width
+ bd
))
770 && (y
< (childPtr
->changes
.width
+ bd
))) {
771 eventPtr
->xmotion
.subwindow
= childPtr
->window
;
776 eventPtr
->xmotion
.x
= 0;
777 eventPtr
->xmotion
.y
= 0;
778 eventPtr
->xmotion
.subwindow
= None
;
781 if (eventPtr
->type
== MotionNotify
) {
782 eventPtr
->xmotion
.same_screen
= sameScreen
;
784 eventPtr
->xbutton
.same_screen
= sameScreen
;
789 *----------------------------------------------------------------------
793 * This procedure synthesizes EnterNotify and LeaveNotify events
794 * to correctly transfer the pointer from one window to another.
800 * Synthesized events may be pushed back onto the event queue.
801 * The event pointed to by eventPtr is modified.
803 *----------------------------------------------------------------------
807 MovePointer(eventPtr
, sourcePtr
, destPtr
)
808 XEvent
*eventPtr
; /* A template X event. Must have all fields
809 * properly set for EnterNotify and LeaveNotify
810 * events except window, subwindow, x, y,
811 * detail, and same_screen. (x_root and y_root
812 * must be valid, even though x and y needn't
814 TkWindow
*sourcePtr
; /* Window currently containing pointer (NULL
815 * means it's not one managed by this
817 TkWindow
*destPtr
; /* Window that is to end up containing the
818 * pointer (NULL means it's not one managed
819 * by this process). */
822 register TkWindow
*ancestorPtr
; /* Lowest ancestor shared between
823 * sourcePtr and destPtr, or
824 * sourcePtr's top-level window if no
825 * shared ancestor. */
826 register TkWindow
*winPtr
;
827 int upLevels
, downLevels
, i
, j
;
830 * There are four possible cases to deal with:
832 * 1. SourcePtr and destPtr are the same. There's nothing to do in
834 * 2. SourcePtr is an ancestor of destPtr in the same top-level
835 * window. Must generate events down the window tree from source
837 * 3. DestPtr is an ancestor of sourcePtr in the same top-level
838 * window. Must generate events up the window tree from sourcePtr
840 * 4. All other cases. Must first generate events up the window tree
841 * from sourcePtr to its top-level, then down from destPtr's
842 * top-level to destPtr. This form is called "non-linear."
844 * The code below separates these four cases and decides how many levels
845 * up and down events have to be generated for.
848 if (sourcePtr
== destPtr
) {
853 * Mark destPtr and all of its ancestors with a special flag bit.
856 if (destPtr
!= NULL
) {
857 dispPtr
= destPtr
->dispPtr
;
858 for (winPtr
= destPtr
; ; winPtr
= winPtr
->parentPtr
) {
859 winPtr
->flags
|= TK_GRAB_FLAG
;
860 if (winPtr
->flags
& TK_TOP_LEVEL
) {
865 dispPtr
= sourcePtr
->dispPtr
;
869 * Search upwards from sourcePtr until an ancestor of destPtr is
870 * found or a top-level window is reached. Remember if we pass out
871 * of the grab tree along the way, since this means we'll have to
872 * skip some of the events that would otherwise be generated.
875 ancestorPtr
= sourcePtr
;
877 if (sourcePtr
!= NULL
) {
878 for (; ; upLevels
++, ancestorPtr
= ancestorPtr
->parentPtr
) {
879 if (ancestorPtr
->flags
& TK_GRAB_FLAG
) {
882 if (ancestorPtr
->flags
& TK_TOP_LEVEL
) {
890 * Search upwards from destPtr again, clearing the flag bits and
891 * remembering how many levels up we had to go.
894 if (destPtr
== NULL
) {
898 for (i
= 0, winPtr
= destPtr
; ; i
++, winPtr
= winPtr
->parentPtr
) {
899 winPtr
->flags
&= ~TK_GRAB_FLAG
;
900 if (winPtr
== ancestorPtr
) {
903 if (winPtr
->flags
& TK_TOP_LEVEL
) {
904 if (downLevels
== -1) {
913 * Generate enter/leave events and push them back onto the event
914 * queue. This has to be done backwards, since the last event
915 * pushed will be the first one processed.
918 #define PUSH_EVENT(w, t, d) \
919 if (w->window != None) { \
920 eventPtr->type = t; \
921 eventPtr->xcrossing.detail = d; \
922 ChangeEventWindow(eventPtr, w); \
923 XPutBackEvent(w->display, eventPtr); \
926 if (downLevels
== 0) {
929 * SourcePtr is an inferior of destPtr.
932 if (destPtr
!= NULL
) {
933 PUSH_EVENT(destPtr
, EnterNotify
, NotifyInferior
);
935 for (i
= upLevels
-1; i
> 0; i
--) {
936 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
937 winPtr
= winPtr
->parentPtr
, j
++) {
938 if (winPtr
== dispPtr
->grabWinPtr
) {
942 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyVirtual
);
943 nextIteration
: continue;
945 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyAncestor
);
946 } else if (upLevels
== 0) {
949 * DestPtr is an inferior of sourcePtr.
952 if (destPtr
!= NULL
) {
953 PUSH_EVENT(destPtr
, EnterNotify
, NotifyAncestor
);
955 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
956 winPtr
= winPtr
->parentPtr
, i
--) {
957 PUSH_EVENT(winPtr
, EnterNotify
, NotifyVirtual
);
959 if (sourcePtr
!= NULL
) {
960 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyInferior
);
965 * Non-linear: neither window is an inferior of the other.
968 if (destPtr
!= NULL
) {
969 PUSH_EVENT(destPtr
, EnterNotify
, NotifyNonlinear
);
971 if (destPtr
!= dispPtr
->grabWinPtr
) {
972 for (winPtr
= destPtr
->parentPtr
, i
= downLevels
-1; i
> 0;
973 winPtr
= winPtr
->parentPtr
, i
--) {
974 PUSH_EVENT(winPtr
, EnterNotify
, NotifyNonlinearVirtual
);
975 if (winPtr
== dispPtr
->grabWinPtr
) {
980 for (i
= upLevels
-1; i
> 0; i
--) {
981 for (winPtr
= sourcePtr
, j
= 0; j
< i
;
982 winPtr
= winPtr
->parentPtr
, j
++) {
983 if (winPtr
== dispPtr
->grabWinPtr
) {
987 PUSH_EVENT(winPtr
, LeaveNotify
, NotifyNonlinearVirtual
);
988 nextWindow
: continue;
990 PUSH_EVENT(sourcePtr
, LeaveNotify
, NotifyNonlinear
);
995 *----------------------------------------------------------------------
999 * This procedure synthesizes EnterNotify and LeaveNotify events
1000 * to correctly transfer the pointer from one window to another.
1001 * It is different from MovePointer in that no template X event
1002 * needs to be supplied; this procedure generates the template
1003 * event and calls MovePointer.
1009 * Synthesized events may be pushed back onto the event queue.
1011 *----------------------------------------------------------------------
1015 MovePointer2(sourcePtr
, destPtr
, mode
)
1016 TkWindow
*sourcePtr
; /* Window currently containing pointer (NULL
1017 * means it's not one managed by this
1019 TkWindow
*destPtr
; /* Window that is to end up containing the
1020 * pointer (NULL means it's not one managed
1021 * by this process). */
1022 int mode
; /* Mode for enter/leave events, such as
1023 * NotifyNormal or NotifyUngrab. */
1026 Window dummy1
, dummy2
;
1031 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1033 if ((winPtr
== NULL
) || (winPtr
->window
== None
)) {
1038 event
.xcrossing
.serial
= LastKnownRequestProcessed(winPtr
->display
);
1039 event
.xcrossing
.send_event
= False
;
1040 event
.xcrossing
.display
= winPtr
->display
;
1041 event
.xcrossing
.root
= RootWindow(winPtr
->display
, winPtr
->screenNum
);
1042 event
.xcrossing
.time
= TkCurrentTime(winPtr
->dispPtr
);
1043 XQueryPointer(winPtr
->display
, winPtr
->window
, &dummy1
, &dummy2
,
1044 &event
.xcrossing
.x_root
, &event
.xcrossing
.y_root
,
1045 &dummy3
, &dummy4
, &event
.xcrossing
.state
);
1046 event
.xcrossing
.mode
= mode
;
1047 event
.xcrossing
.focus
= False
;
1048 MovePointer(&event
, sourcePtr
, destPtr
);
1052 *----------------------------------------------------------------------
1054 * TkGrabDeadWindow --
1056 * This procedure is invoked whenever a window is deleted, so that
1057 * grab-related cleanup can be performed.
1063 * Various cleanups happen, such as generating events to move the
1064 * pointer back to its "natural" window as if an ungrab had been
1065 * done. See the code.
1067 *----------------------------------------------------------------------
1071 TkGrabDeadWindow(winPtr
)
1072 register TkWindow
*winPtr
; /* Window that is in the process
1073 * of being deleted. */
1075 TkDisplay
*dispPtr
= winPtr
->dispPtr
;
1077 if (dispPtr
->grabWinPtr
== winPtr
) {
1078 dispPtr
->grabWinPtr
= NULL
;
1079 if (!(dispPtr
->grabFlags
& GRAB_GLOBAL
)) {
1081 * Must generate enter/leave events to move back to the window
1082 * that contains the mouse pointer. We needn't filter events
1083 * here like we do in Tk_Ungrab because there are no children
1084 * of the grab window left in existence.
1088 if ((dispPtr
->ungrabWinPtr
!= NULL
)
1089 && (dispPtr
->ungrabWinPtr
->mainPtr
!= winPtr
->mainPtr
)) {
1090 dispPtr
->ungrabWinPtr
= NULL
;
1092 MovePointer2(winPtr
, dispPtr
->ungrabWinPtr
, NotifyUngrab
);
1094 } else if (dispPtr
->buttonWinPtr
== winPtr
) {
1096 * The window in which a button was pressed was deleted. Simulate
1097 * dropping the button auto-grab by generating Enter and Leave
1098 * events to move the pointer back to the window it's really on
1102 dispPtr
->buttonWinPtr
= NULL
;
1103 goto movePointerBack
;
1105 if (dispPtr
->ungrabWinPtr
== winPtr
) {
1106 dispPtr
->ungrabWinPtr
= NULL
;
1108 if (dispPtr
->pointerWinPtr
== winPtr
) {
1109 dispPtr
->pointerWinPtr
= NULL
;
1111 if (dispPtr
->serverWinPtr
== winPtr
) {
1112 dispPtr
->serverWinPtr
= NULL
;