|
|
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.