|
|
1.1 root 1: /*
2: * tkGrab.c --
3: *
4: * This file provides procedures that implement grabs for Tk.
5: *
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.
14: */
15:
16: #ifndef lint
17: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkGrab.c,v 1.18 92/08/07 09:55:31 ouster Exp $ SPRITE (Berkeley)";
18: #endif
19:
20: #include "tkconfig.h"
21: #include "tkint.h"
22:
23: /*
24: *-------------------------------------------------------------------
25: * Problems with current grab implementation (8/7/92):
26: *
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: *-------------------------------------------------------------------
40: */
41:
42: /*
43: * Bit definitions for grabFlags field of TkDisplay structures:
44: *
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
52: * mode.
53: */
54:
55: #define GRAB_GLOBAL 1
56: #define GRAB_BUTTON_RELEASE 2
57:
58: /*
59: * Forward declarations for procedures declared later in this file:
60: */
61:
62: static void ChangeEventWindow _ANSI_ARGS_((XEvent *eventPtr,
63: TkWindow *winPtr));
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));
68:
69: /*
70: *----------------------------------------------------------------------
71: *
72: * Tk_GrabCmd --
73: *
74: * This procedure is invoked to process the "grab" Tcl command.
75: * See the user documentation for details on what it does.
76: *
77: * Results:
78: * A standard Tcl result.
79: *
80: * Side effects:
81: * See the user documentation.
82: *
83: *----------------------------------------------------------------------
84: */
85:
86: /* ARGSUSED */
87: int
88: Tk_GrabCmd(clientData, interp, argc, argv)
89: ClientData clientData; /* Main window associated with
90: * interpreter. */
91: Tcl_Interp *interp; /* Current interpreter. */
92: int argc; /* Number of arguments. */
93: char **argv; /* Argument strings. */
94: {
95: TkWindow *winPtr = (TkWindow *) clientData;
96: int length, lockScreen;
97: char *window;
98:
99: if (argc > 3) {
100: badArgs:
101: Tcl_AppendResult(interp, "wrong # args: should be \"",
102: argv[0], " ?-global? ?window?\"", (char *) NULL);
103: return TCL_ERROR;
104: }
105: if (argc == 1) {
106: if ((winPtr->dispPtr->grabWinPtr != NULL)
107: && (winPtr->dispPtr->grabWinPtr->mainPtr
108: == winPtr->mainPtr)) {
109: interp->result = Tk_PathName(winPtr->dispPtr->grabWinPtr);
110: } else {
111: interp->result = "none";
112: }
113: return TCL_OK;
114: }
115: if (argc == 3) {
116: length = strlen(argv[1]);
117: if (strncmp(argv[1], "-off", length) == 0) {
118: lockScreen = -1;
119: } else {
120: if ((strncmp(argv[1], "-global", length) != 0) || (length < 2)) {
121: goto badArgs;
122: }
123: lockScreen = 1;
124: }
125: window = argv[2];
126: } else {
127: lockScreen = 0;
128: window = argv[1];
129: }
130: if ((window[0] == '\0')
131: || (strncmp(window, "none", strlen(window)) == 0)) {
132: Tk_Ungrab((Tk_Window) winPtr);
133: } else {
134: Tk_Window tkwin;
135:
136: tkwin = Tk_NameToWindow(interp, window, (Tk_Window) winPtr);
137: if (tkwin == NULL) {
138: return TCL_ERROR;
139: }
140: if (lockScreen < 0) {
141: Tk_Ungrab(tkwin);
142: } else {
143: return Tk_Grab(interp, tkwin, lockScreen);
144: }
145: }
146: return TCL_OK;
147: }
148:
149: /*
150: *----------------------------------------------------------------------
151: *
152: * Tk_Grab --
153: *
154: * Grabs the pointer and keyboard, so that mouse-related events are
155: * only reported relative to a given window and its descendants.
156: *
157: * Results:
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.
161: *
162: * Side effects:
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
167: * one.
168: *
169: *----------------------------------------------------------------------
170: */
171:
172: int
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. */
182: {
183: int grabResult;
184: TkWindow *winPtr = (TkWindow *) tkwin;
185: TkDisplay *dispPtr = winPtr->dispPtr;
186: int grabRequest, inSequence, ignoring, numEvents, i, diff;
187: XEvent *events, *eventPtr;
188: TkWindow *winPtr2;
189:
190: if (dispPtr->grabWinPtr != NULL) {
191: if ((dispPtr->grabWinPtr == winPtr)
192: && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
193: return TCL_OK;
194: }
195: if (dispPtr->grabWinPtr->mainPtr != winPtr->mainPtr) {
196: alreadyGrabbed:
197: interp->result = "grab failed: another application has grab";
198: return TCL_ERROR;
199: }
200: Tk_Ungrab(tkwin);
201: }
202:
203: if (grabGlobal) {
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) {
210: grabError:
211: if (grabResult == GrabNotViewable) {
212: interp->result = "grab failed: window not viewable";
213: } else if (grabResult == AlreadyGrabbed) {
214: goto 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";
219: } else {
220: char msg[100];
221:
222: sprintf(msg, "grab failed for unknown reason (code %d)",
223: grabResult);
224: Tcl_AppendResult(interp, msg, (char *) NULL);
225: }
226: return TCL_ERROR;
227: }
228: grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
229: False, GrabModeAsync, GrabModeAsync, TkCurrentTime(dispPtr));
230: if (grabResult != 0) {
231: XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
232: goto grabError;
233: }
234: dispPtr->grabFlags |= GRAB_GLOBAL;
235: } else {
236: /*
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).
241: */
242:
243: XUngrabPointer(dispPtr->display, TkCurrentTime(dispPtr));
244: grabRequest = LastKnownRequestProcessed(dispPtr->display);
245: dispPtr->grabFlags &= ~GRAB_GLOBAL;
246:
247: /*
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.
251: */
252:
253: MovePointer2(dispPtr->pointerWinPtr, winPtr, NotifyGrab);
254: }
255: dispPtr->grabWinPtr = winPtr;
256:
257: /*
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.
264: *
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.
268: *
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.
277: */
278:
279: XSync(dispPtr->display, False);
280: numEvents = QLength(dispPtr->display);
281: if (numEvents == 0) {
282: return TCL_OK;
283: }
284: events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
285: for (i = 0; i < numEvents; i++) {
286: XNextEvent(dispPtr->display, &events[i]);
287: }
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)) {
293: continue;
294: }
295:
296: /*
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.
300: */
301:
302: diff = eventPtr->xcrossing.serial;
303: diff -= grabRequest;
304: if (!inSequence && (diff >= 0)) {
305: /*
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.
308: */
309:
310: inSequence = 1;
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) {
315: ignoring = 1;
316: break;
317: }
318: }
319: }
320: }
321: if (ignoring) {
322: eventPtr->type = 0;
323: }
324: if (inSequence && (eventPtr->type == EnterNotify)
325: && (dispPtr->grabWinPtr->window
326: == eventPtr->xcrossing.window)) {
327: eventPtr->type = 0;
328: break;
329: }
330: }
331: for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
332: if (eventPtr->type != 0) {
333: XPutBackEvent(dispPtr->display, eventPtr);
334: }
335: }
336: ckfree((char *) events);
337: return TCL_OK;
338: }
339:
340: /*
341: *----------------------------------------------------------------------
342: *
343: * Tk_Ungrab --
344: *
345: * Releases a grab on the mouse pointer and keyboard.
346: *
347: * Results:
348: * None.
349: *
350: * Side effects:
351: * Pointer and keyboard events will start being delivered to other
352: * windows again.
353: *
354: *----------------------------------------------------------------------
355: */
356:
357: void
358: Tk_Ungrab(tkwin)
359: Tk_Window tkwin; /* Window that identifies display
360: * for grab to be released. */
361: {
362: TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
363: int inSequence, ignoring, ungrabRequest, numEvents, i, j, diff;
364: TkWindow *grabWinPtr, *winPtr;
365: XEvent *events, *eventPtr, *eventPtr2;
366:
367: grabWinPtr = dispPtr->grabWinPtr;
368: if (grabWinPtr == NULL) {
369: return;
370: }
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);
378: } else {
379: ungrabRequest = LastKnownRequestProcessed(dispPtr->display);
380: if ((dispPtr->ungrabWinPtr != NULL)
381: && (dispPtr->ungrabWinPtr->mainPtr != grabWinPtr->mainPtr)) {
382:
383: /*
384: * Don't report entries down into a window of a different
385: * application, since it's already seen those entries earlier.
386: */
387:
388: dispPtr->ungrabWinPtr = NULL;
389: }
390: MovePointer2(grabWinPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
391: }
392:
393: /*
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.
399: */
400:
401: numEvents = QLength(dispPtr->display);
402: if (numEvents == 0) {
403: return;
404: }
405: events = (XEvent *) ckalloc((unsigned) (numEvents * sizeof(XEvent)));
406: for (i = 0; i < numEvents; i++) {
407: XNextEvent(dispPtr->display, &events[i]);
408: }
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)) {
414: continue;
415: }
416: diff = eventPtr->xcrossing.serial;
417: diff -= ungrabRequest;
418: if (!inSequence && (diff >= 0)) {
419:
420: /*
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.
424: */
425:
426: inSequence = 1;
427: for (j = i, eventPtr2 = eventPtr; j >= 0; j--, eventPtr2++) {
428: if (eventPtr2->type == EnterNotify) {
429: if (eventPtr2->xcrossing.mode != NotifyUngrab) {
430: break;
431: }
432: if ((eventPtr2->xcrossing.detail != NotifyAncestor)
433: && (eventPtr2->xcrossing.detail != NotifyInferior)
434: && (eventPtr2->xcrossing.detail
435: != NotifyNonlinear)) {
436: continue;
437: }
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) {
443: ignoring = 1;
444: break;
445: }
446: }
447: }
448: break;
449: } else if ((eventPtr2->type != LeaveNotify)
450: || (eventPtr2->xcrossing.mode != NotifyUngrab)) {
451: break;
452: }
453: }
454: }
455: if (ignoring) {
456: eventPtr->type = 0;
457: }
458: }
459: for (i = numEvents-1, eventPtr = &events[i]; i >= 0; i--, eventPtr--) {
460: if (eventPtr->type != 0) {
461: XPutBackEvent(dispPtr->display, eventPtr);
462: }
463: }
464: ckfree((char *) events);
465: }
466:
467: /*
468: *----------------------------------------------------------------------
469: *
470: * TkPointerEvent --
471: *
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.
475: *
476: * Results:
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.
481: *
482: * Side effects:
483: * Grab state information may be updated.
484: *
485: *----------------------------------------------------------------------
486: */
487:
488: int
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. */
493: {
494: register TkWindow *winPtr2;
495: TkDisplay *dispPtr = winPtr->dispPtr;
496: int outsideGrabTree = 0;
497: int originalFlags;
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
505: };
506:
507: /*
508: * Don't do any filtering on events generated by the event-sharing code.
509: */
510:
511: if (eventPtr == tkShareEventPtr) {
512: return 1;
513: }
514:
515: /*
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.
519: */
520:
521: if (dispPtr->grabWinPtr != NULL) {
522: if ((winPtr->mainPtr == dispPtr->grabWinPtr->mainPtr)
523: || (dispPtr->grabFlags & GRAB_GLOBAL)) {
524: appGrabbed = 1;
525: }
526: for (winPtr2 = winPtr; winPtr2 != dispPtr->grabWinPtr;
527: winPtr2 = winPtr2->parentPtr) {
528: if (winPtr2 == NULL) {
529: outsideGrabTree = 1;
530: break;
531: }
532: }
533: }
534:
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;
544: }
545: dispPtr->serverWinPtr = winPtr;
546: } else {
547: dispPtr->serverWinPtr = NULL;
548: }
549: if (dispPtr->grabWinPtr != NULL) {
550: if (eventPtr->xcrossing.mode == NotifyNormal) {
551: /*
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.
555: */
556:
557: if (outsideGrabTree && appGrabbed) {
558: return 0;
559: }
560:
561: /*
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
565: * pressed.
566: */
567:
568: if ((dispPtr->buttonWinPtr != NULL)
569: && (winPtr != dispPtr->buttonWinPtr)) {
570: return 0;
571: }
572: } else if (eventPtr->xcrossing.mode == NotifyUngrab) {
573: /*
574: * Keep the GRAB_BUTTON_RELEASE flag on if it used to be on.
575: */
576:
577: dispPtr->grabFlags = originalFlags;
578: if (outsideGrabTree && appGrabbed
579: && (dispPtr->grabFlags & GRAB_BUTTON_RELEASE)) {
580: /*
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.
587: */
588:
589: return 0;
590: }
591: }
592: }
593:
594: /*
595: * Keep track of the window containing the mouse, in order to
596: * detect various bogus event sequences.
597: */
598:
599: dispPtr->pointerWinPtr = dispPtr->serverWinPtr;
600: return 1;
601: }
602: if ((dispPtr->grabWinPtr == NULL) || !appGrabbed) {
603: return 1;
604: }
605:
606: if (eventPtr->type == MotionNotify) {
607: /*
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.
615: */
616:
617: winPtr2 = winPtr;
618: if (dispPtr->buttonWinPtr != NULL) {
619: winPtr2 = dispPtr->buttonWinPtr;
620: } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
621: winPtr2 = dispPtr->grabWinPtr;
622: }
623: if (winPtr2 != winPtr) {
624: XEvent newEvent;
625:
626: newEvent = *eventPtr;
627: ChangeEventWindow(&newEvent, winPtr2);
628: XPutBackEvent(winPtr2->display, &newEvent);
629: return 0;
630: }
631: return 1;
632: }
633:
634: /*
635: * Process ButtonPress and ButtonRelease events:
636: * 1. Keep track of whether a button is down and what window it
637: * went down in.
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.
656: */
657:
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. */
663: } else {
664: winPtr2 = winPtr; /* Note 5. */
665: }
666: }
667: if (eventPtr->type == ButtonPress) {
668: if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
669: if (outsideGrabTree) {
670: XEvent newEvent;
671:
672: newEvent = *eventPtr;
673: ChangeEventWindow(&newEvent, dispPtr->grabWinPtr);
674: XPutBackEvent(dispPtr->display, &newEvent);
675: return 0; /* Note 2. */
676: }
677: dispPtr->buttonWinPtr = winPtr;
678: return 1;
679: }
680: } else {
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. */
686:
687: /*
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.
694: */
695:
696: if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
697: winPtr = NULL;
698: }
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);
705: }
706: dispPtr->buttonWinPtr = NULL;
707: dispPtr->grabFlags |= GRAB_BUTTON_RELEASE;
708: }
709: }
710: if (winPtr2 != winPtr) {
711: XEvent newEvent;
712:
713: newEvent = *eventPtr;
714: ChangeEventWindow(&newEvent, winPtr2);
715: XPutBackEvent(dispPtr->display, &newEvent);
716: return 0; /* Note 3. */
717: }
718: }
719:
720: return 1;
721: }
722:
723: /*
724: *----------------------------------------------------------------------
725: *
726: * ChangeEventWindow --
727: *
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.
731: *
732: * Results:
733: * The following fields of eventPtr are modified: window,
734: * subwindow, x, y, same_screen.
735: *
736: * Side effects:
737: * None.
738: *
739: *----------------------------------------------------------------------
740: */
741:
742: static void
743: ChangeEventWindow(eventPtr, winPtr)
744: register XEvent *eventPtr; /* Event to retarget. Must have
745: * type ButtonPress, ButtonRelease, KeyPress,
746: * KeyRelease, MotionNotify, EnterNotify,
747: * or LeaveNotify. */
748: TkWindow *winPtr; /* New target window for event. */
749: {
750: int x, y, sameScreen, bd;
751: register TkWindow *childPtr;
752:
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) {
763: continue;
764: }
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;
772: }
773: }
774: sameScreen = 1;
775: } else {
776: eventPtr->xmotion.x = 0;
777: eventPtr->xmotion.y = 0;
778: eventPtr->xmotion.subwindow = None;
779: sameScreen = 0;
780: }
781: if (eventPtr->type == MotionNotify) {
782: eventPtr->xmotion.same_screen = sameScreen;
783: } else {
784: eventPtr->xbutton.same_screen = sameScreen;
785: }
786: }
787:
788: /*
789: *----------------------------------------------------------------------
790: *
791: * MovePointer --
792: *
793: * This procedure synthesizes EnterNotify and LeaveNotify events
794: * to correctly transfer the pointer from one window to another.
795: *
796: * Results:
797: * None.
798: *
799: * Side effects:
800: * Synthesized events may be pushed back onto the event queue.
801: * The event pointed to by eventPtr is modified.
802: *
803: *----------------------------------------------------------------------
804: */
805:
806: static void
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
813: * be valid). */
814: TkWindow *sourcePtr; /* Window currently containing pointer (NULL
815: * means it's not one managed by this
816: * process). */
817: TkWindow *destPtr; /* Window that is to end up containing the
818: * pointer (NULL means it's not one managed
819: * by this process). */
820: {
821: TkDisplay *dispPtr;
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;
828:
829: /*
830: * There are four possible cases to deal with:
831: *
832: * 1. SourcePtr and destPtr are the same. There's nothing to do in
833: * this case.
834: * 2. SourcePtr is an ancestor of destPtr in the same top-level
835: * window. Must generate events down the window tree from source
836: * to dest.
837: * 3. DestPtr is an ancestor of sourcePtr in the same top-level
838: * window. Must generate events up the window tree from sourcePtr
839: * to destPtr.
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."
843: *
844: * The code below separates these four cases and decides how many levels
845: * up and down events have to be generated for.
846: */
847:
848: if (sourcePtr == destPtr) {
849: return;
850: }
851:
852: /*
853: * Mark destPtr and all of its ancestors with a special flag bit.
854: */
855:
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) {
861: break;
862: }
863: }
864: } else {
865: dispPtr = sourcePtr->dispPtr;
866: }
867:
868: /*
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.
873: */
874:
875: ancestorPtr = sourcePtr;
876: upLevels = 0;
877: if (sourcePtr != NULL) {
878: for (; ; upLevels++, ancestorPtr = ancestorPtr->parentPtr) {
879: if (ancestorPtr->flags & TK_GRAB_FLAG) {
880: break;
881: }
882: if (ancestorPtr->flags & TK_TOP_LEVEL) {
883: upLevels++;
884: break;
885: }
886: }
887: }
888:
889: /*
890: * Search upwards from destPtr again, clearing the flag bits and
891: * remembering how many levels up we had to go.
892: */
893:
894: if (destPtr == NULL) {
895: downLevels = 0;
896: } else {
897: downLevels = -1;
898: for (i = 0, winPtr = destPtr; ; i++, winPtr = winPtr->parentPtr) {
899: winPtr->flags &= ~TK_GRAB_FLAG;
900: if (winPtr == ancestorPtr) {
901: downLevels = i;
902: }
903: if (winPtr->flags & TK_TOP_LEVEL) {
904: if (downLevels == -1) {
905: downLevels = i+1;
906: }
907: break;
908: }
909: }
910: }
911:
912: /*
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.
916: */
917:
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); \
924: }
925:
926: if (downLevels == 0) {
927:
928: /*
929: * SourcePtr is an inferior of destPtr.
930: */
931:
932: if (destPtr != NULL) {
933: PUSH_EVENT(destPtr, EnterNotify, NotifyInferior);
934: }
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) {
939: goto nextIteration;
940: }
941: }
942: PUSH_EVENT(winPtr, LeaveNotify, NotifyVirtual);
943: nextIteration: continue;
944: }
945: PUSH_EVENT(sourcePtr, LeaveNotify, NotifyAncestor);
946: } else if (upLevels == 0) {
947:
948: /*
949: * DestPtr is an inferior of sourcePtr.
950: */
951:
952: if (destPtr != NULL) {
953: PUSH_EVENT(destPtr, EnterNotify, NotifyAncestor);
954: }
955: for (winPtr = destPtr->parentPtr, i = downLevels-1; i > 0;
956: winPtr = winPtr->parentPtr, i--) {
957: PUSH_EVENT(winPtr, EnterNotify, NotifyVirtual);
958: }
959: if (sourcePtr != NULL) {
960: PUSH_EVENT(sourcePtr, LeaveNotify, NotifyInferior);
961: }
962: } else {
963:
964: /*
965: * Non-linear: neither window is an inferior of the other.
966: */
967:
968: if (destPtr != NULL) {
969: PUSH_EVENT(destPtr, EnterNotify, NotifyNonlinear);
970: }
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) {
976: break;
977: }
978: }
979: }
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) {
984: goto nextWindow;
985: }
986: }
987: PUSH_EVENT(winPtr, LeaveNotify, NotifyNonlinearVirtual);
988: nextWindow: continue;
989: }
990: PUSH_EVENT(sourcePtr, LeaveNotify, NotifyNonlinear);
991: }
992: }
993:
994: /*
995: *----------------------------------------------------------------------
996: *
997: * MovePointer2 --
998: *
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.
1004: *
1005: * Results:
1006: * None.
1007: *
1008: * Side effects:
1009: * Synthesized events may be pushed back onto the event queue.
1010: *
1011: *----------------------------------------------------------------------
1012: */
1013:
1014: static void
1015: MovePointer2(sourcePtr, destPtr, mode)
1016: TkWindow *sourcePtr; /* Window currently containing pointer (NULL
1017: * means it's not one managed by this
1018: * process). */
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. */
1024: {
1025: XEvent event;
1026: Window dummy1, dummy2;
1027: int dummy3, dummy4;
1028: TkWindow *winPtr;
1029:
1030: winPtr = sourcePtr;
1031: if ((winPtr == NULL) || (winPtr->window == None)) {
1032: winPtr = destPtr;
1033: if ((winPtr == NULL) || (winPtr->window == None)) {
1034: return;
1035: }
1036: }
1037:
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);
1049: }
1050:
1051: /*
1052: *----------------------------------------------------------------------
1053: *
1054: * TkGrabDeadWindow --
1055: *
1056: * This procedure is invoked whenever a window is deleted, so that
1057: * grab-related cleanup can be performed.
1058: *
1059: * Results:
1060: * None.
1061: *
1062: * Side effects:
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.
1066: *
1067: *----------------------------------------------------------------------
1068: */
1069:
1070: void
1071: TkGrabDeadWindow(winPtr)
1072: register TkWindow *winPtr; /* Window that is in the process
1073: * of being deleted. */
1074: {
1075: TkDisplay *dispPtr = winPtr->dispPtr;
1076:
1077: if (dispPtr->grabWinPtr == winPtr) {
1078: dispPtr->grabWinPtr = NULL;
1079: if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {
1080: /*
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.
1085: */
1086:
1087: movePointerBack:
1088: if ((dispPtr->ungrabWinPtr != NULL)
1089: && (dispPtr->ungrabWinPtr->mainPtr != winPtr->mainPtr)) {
1090: dispPtr->ungrabWinPtr = NULL;
1091: }
1092: MovePointer2(winPtr, dispPtr->ungrabWinPtr, NotifyUngrab);
1093: }
1094: } else if (dispPtr->buttonWinPtr == winPtr) {
1095: /*
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
1099: * top of.
1100: */
1101:
1102: dispPtr->buttonWinPtr = NULL;
1103: goto movePointerBack;
1104: }
1105: if (dispPtr->ungrabWinPtr == winPtr) {
1106: dispPtr->ungrabWinPtr = NULL;
1107: }
1108: if (dispPtr->pointerWinPtr == winPtr) {
1109: dispPtr->pointerWinPtr = NULL;
1110: }
1111: if (dispPtr->serverWinPtr == winPtr) {
1112: dispPtr->serverWinPtr = NULL;
1113: }
1114: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.