1/* 2 * tkGrab.c -- 3 * 4 * This file provides functions that implement grabs for Tk. 5 * 6 * Copyright (c) 1992-1994 The Regents of the University of California. 7 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16 17#if !(defined(__WIN32__) || defined(MAC_OSX_TK)) 18#include "tkUnixInt.h" 19#endif 20 21/* 22 * The grab state machine has four states: ungrabbed, button pressed, grabbed, 23 * and button pressed while grabbed. In addition, there are three pieces of 24 * grab state information: the current grab window, the current restrict 25 * window, and whether the mouse is captured. 26 * 27 * The current grab window specifies the point in the Tk window heirarchy 28 * above which pointer events will not be reported. Any window within the 29 * subtree below the grab window will continue to receive events as normal. 30 * Events outside of the grab tree will be reported to the grab window. 31 * 32 * If the current restrict window is set, then all pointer events will be 33 * reported only to the restrict window. The restrict window is normally set 34 * during an automatic button grab. 35 * 36 * The mouse capture state specifies whether the window system will report 37 * mouse events outside of any Tk toplevels. This is set during a global grab 38 * or an automatic button grab. 39 * 40 * The transitions between different states is given in the following table: 41 * 42 * Event\State U B G GB 43 * ----------- -- -- -- -- 44 * FirstPress B B GB GB 45 * Press B B G GB 46 * Release U B G GB 47 * LastRelease U U G G 48 * Grab G G G G 49 * Ungrab U B U U 50 * 51 * Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button 52 * 53 * In addition, the following conditions are always true: 54 * 55 * State\Variable Grab Restrict Capture 56 * -------------- ---- -------- ------- 57 * Ungrabbed 0 0 0 58 * Button 0 1 1 59 * Grabbed 1 0 b/g 60 * Grab and Button 1 1 1 61 * 62 * Note: 0 means variable is set to NULL, 1 means variable is set to some 63 * window, b/g means the variable is set to a window if a button is currently 64 * down or a global grab is in effect. 65 * 66 * The final complication to all of this is enter and leave events. In order 67 * to correctly handle all of the various cases, Tk cannot rely on X 68 * enter/leave events in all situations. The following describes the correct 69 * sequence of enter and leave events that should be observed by Tk scripts: 70 * 71 * Event(state) Enter/Leave From -> To 72 * ------------ ---------------------- 73 * LastRelease(B | GB): restrict window -> anc(grab window, event window) 74 * Grab(U | B): event window -> anc(grab window, event window) 75 * Grab(G): anc(old grab window, event window) -> 76 * anc(new grab window, event window) 77 * Grab(GB): restrict window -> anc(new grab window, event window) 78 * Ungrab(G): anc(grab window, event window) -> event window 79 * Ungrab(GB): restrict window -> event window 80 * 81 * Note: anc(x,y) returns the least ancestor of y that is in the tree of x, 82 * terminating at toplevels. 83 */ 84 85/* 86 * The following structure is used to pass information to GrabRestrictProc 87 * from EatGrabEvents. 88 */ 89 90typedef struct { 91 Display *display; /* Display from which to discard events. */ 92 unsigned int serial; /* Serial number with which to compare. */ 93} GrabInfo; 94 95/* 96 * Bit definitions for grabFlags field of TkDisplay structures: 97 * 98 * GRAB_GLOBAL 1 means this is a global grab (we grabbed via 99 * the server so all applications are locked out). 100 * 0 means this is a local grab that affects only 101 * this application. 102 * GRAB_TEMP_GLOBAL 1 means we've temporarily grabbed via the 103 * server because a button is down and we want to 104 * make sure that we get the button-up event. The 105 * grab will be released when the last mouse 106 * button goes up. 107 */ 108 109#define GRAB_GLOBAL 1 110#define GRAB_TEMP_GLOBAL 4 111 112/* 113 * The following structure is a Tcl_Event that triggers a change in the 114 * grabWinPtr field of a display. This event guarantees that the change occurs 115 * in the proper order relative to enter and leave events. 116 */ 117 118typedef struct NewGrabWinEvent { 119 Tcl_Event header; /* Standard information for all Tcl events. */ 120 TkDisplay *dispPtr; /* Display whose grab window is to change. */ 121 Window grabWindow; /* New grab window for display. This is 122 * recorded instead of a (TkWindow *) because 123 * it will allow us to detect cases where the 124 * window is destroyed before this event is 125 * processed. */ 126} NewGrabWinEvent; 127 128/* 129 * The following magic value is stored in the "send_event" field of 130 * EnterNotify and LeaveNotify events that are generated in this file. This 131 * allows us to separate "real" events coming from the server from those that 132 * we generated. 133 */ 134 135#define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac) 136 137/* 138 * Mask that selects any of the state bits corresponding to buttons, plus 139 * masks that select individual buttons' bits: 140 */ 141 142#define ALL_BUTTONS \ 143 (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask) 144static unsigned int buttonStates[] = { 145 Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask 146}; 147 148/* 149 * Forward declarations for functions declared later in this file: 150 */ 151 152static void EatGrabEvents(TkDisplay *dispPtr, unsigned int serial); 153static TkWindow * FindCommonAncestor(TkWindow *winPtr1, 154 TkWindow *winPtr2, int *countPtr1, int *countPtr2); 155static Tk_RestrictAction GrabRestrictProc(ClientData arg, XEvent *eventPtr); 156static int GrabWinEventProc(Tcl_Event *evPtr, int flags); 157static void MovePointer2(TkWindow *sourcePtr, TkWindow *destPtr, 158 int mode, int leaveEvents, int EnterEvents); 159static void QueueGrabWindowChange(TkDisplay *dispPtr, 160 TkWindow *grabWinPtr); 161static void ReleaseButtonGrab(TkDisplay *dispPtr); 162 163/* 164 *---------------------------------------------------------------------- 165 * 166 * Tk_GrabObjCmd -- 167 * 168 * This function is invoked to process the "grab" Tcl command. See the 169 * user documentation for details on what it does. 170 * 171 * Results: 172 * A standard Tcl result. 173 * 174 * Side effects: 175 * See the user documentation. 176 * 177 *---------------------------------------------------------------------- 178 */ 179 180 /* ARGSUSED */ 181int 182Tk_GrabObjCmd( 183 ClientData clientData, /* Main window associated with interpreter. */ 184 Tcl_Interp *interp, /* Current interpreter. */ 185 int objc, /* Number of arguments. */ 186 Tcl_Obj *CONST objv[]) /* Argument objects. */ 187{ 188 int globalGrab; 189 Tk_Window tkwin; 190 TkDisplay *dispPtr; 191 char *arg; 192 int index; 193 int len; 194 static CONST char *optionStrings[] = { 195 "current", "release", "set", "status", NULL 196 }; 197 static CONST char *flagStrings[] = { 198 "-global", NULL 199 }; 200 enum options { 201 GRABCMD_CURRENT, GRABCMD_RELEASE, GRABCMD_SET, GRABCMD_STATUS 202 }; 203 204 if (objc < 2) { 205 /* 206 * Can't use Tcl_WrongNumArgs here because we want the message to 207 * read: 208 * wrong # args: should be "cmd ?-global window" or "cmd option 209 * ?arg arg ...?" 210 * We can fake it with Tcl_WrongNumArgs if we assume the command name 211 * is "grab", but if it has been aliased, the message will be 212 * incorrect. 213 */ 214 Tcl_ResetResult(interp); 215 Tcl_AppendResult(interp, "wrong # args: should be \"", 216 Tcl_GetString(objv[0]), " ?-global? window\" or \"", 217 Tcl_GetString(objv[0]), " option ?arg arg ...?\"", NULL); 218 return TCL_ERROR; 219 } 220 221 /* 222 * First check for a window name or "-global" as the first argument. 223 */ 224 225 arg = Tcl_GetStringFromObj(objv[1], &len); 226 if (arg[0] == '.') { 227 /* [grab window] */ 228 if (objc != 2) { 229 Tcl_WrongNumArgs(interp, 1, objv, "?-global? window"); 230 return TCL_ERROR; 231 } 232 tkwin = Tk_NameToWindow(interp, arg, (Tk_Window) clientData); 233 if (tkwin == NULL) { 234 return TCL_ERROR; 235 } 236 return Tk_Grab(interp, tkwin, 0); 237 } else if (arg[0] == '-' && len > 1) { 238 if (Tcl_GetIndexFromObj(interp, objv[1], flagStrings, "option", 0, 239 &index) != TCL_OK) { 240 return TCL_ERROR; 241 } 242 243 /* [grab -global window] */ 244 if (objc != 3) { 245 Tcl_WrongNumArgs(interp, 1, objv, "?-global? window"); 246 return TCL_ERROR; 247 } 248 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 249 (Tk_Window) clientData); 250 if (tkwin == NULL) { 251 return TCL_ERROR; 252 } 253 return Tk_Grab(interp, tkwin, 1); 254 } 255 256 /* 257 * First argument is not a window name and not "-global", find out which 258 * option it is. 259 */ 260 261 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, 262 &index) != TCL_OK) { 263 return TCL_ERROR; 264 } 265 266 switch ((enum options) index) { 267 case GRABCMD_CURRENT: 268 /* [grab current ?window?] */ 269 if (objc > 3) { 270 Tcl_WrongNumArgs(interp, 1, objv, "current ?window?"); 271 return TCL_ERROR; 272 } 273 if (objc == 3) { 274 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 275 (Tk_Window) clientData); 276 if (tkwin == NULL) { 277 return TCL_ERROR; 278 } 279 dispPtr = ((TkWindow *) tkwin)->dispPtr; 280 if (dispPtr->eventualGrabWinPtr != NULL) { 281 Tcl_SetResult(interp, dispPtr->eventualGrabWinPtr->pathName, 282 TCL_STATIC); 283 } 284 } else { 285 for (dispPtr = TkGetDisplayList(); dispPtr != NULL; 286 dispPtr = dispPtr->nextPtr) { 287 if (dispPtr->eventualGrabWinPtr != NULL) { 288 Tcl_AppendElement(interp, 289 dispPtr->eventualGrabWinPtr->pathName); 290 } 291 } 292 } 293 return TCL_OK; 294 295 case GRABCMD_RELEASE: 296 /* [grab release window] */ 297 if (objc != 3) { 298 Tcl_WrongNumArgs(interp, 1, objv, "release window"); 299 return TCL_ERROR; 300 } 301 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 302 (Tk_Window) clientData); 303 if (tkwin == NULL) { 304 Tcl_ResetResult(interp); 305 } else { 306 Tk_Ungrab(tkwin); 307 } 308 break; 309 310 case GRABCMD_SET: 311 /* [grab set ?-global? window] */ 312 if ((objc != 3) && (objc != 4)) { 313 Tcl_WrongNumArgs(interp, 1, objv, "set ?-global? window"); 314 return TCL_ERROR; 315 } 316 if (objc == 3) { 317 globalGrab = 0; 318 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 319 (Tk_Window) clientData); 320 } else { 321 globalGrab = 1; 322 323 /* 324 * We could just test the argument by hand instead of using 325 * Tcl_GetIndexFromObj; the benefit of using the function is that 326 * it sets up the error message for us, so we are certain to be 327 * consistant with the rest of Tcl. 328 */ 329 330 if (Tcl_GetIndexFromObj(interp, objv[2], flagStrings, "option", 331 0, &index) != TCL_OK) { 332 return TCL_ERROR; 333 } 334 tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[3]), 335 (Tk_Window) clientData); 336 } 337 if (tkwin == NULL) { 338 return TCL_ERROR; 339 } 340 return Tk_Grab(interp, tkwin, globalGrab); 341 342 case GRABCMD_STATUS: { 343 /* [grab status window] */ 344 TkWindow *winPtr; 345 346 if (objc != 3) { 347 Tcl_WrongNumArgs(interp, 1, objv, "status window"); 348 return TCL_ERROR; 349 } 350 winPtr = (TkWindow *) Tk_NameToWindow(interp, Tcl_GetString(objv[2]), 351 (Tk_Window) clientData); 352 if (winPtr == NULL) { 353 return TCL_ERROR; 354 } 355 dispPtr = winPtr->dispPtr; 356 if (dispPtr->eventualGrabWinPtr != winPtr) { 357 Tcl_SetResult(interp, "none", TCL_STATIC); 358 } else if (dispPtr->grabFlags & GRAB_GLOBAL) { 359 Tcl_SetResult(interp, "global", TCL_STATIC); 360 } else { 361 Tcl_SetResult(interp, "local", TCL_STATIC); 362 } 363 break; 364 } 365 } 366 367 return TCL_OK; 368} 369 370/* 371 *---------------------------------------------------------------------- 372 * 373 * Tk_Grab -- 374 * 375 * Grabs the pointer and keyboard, so that mouse-related events are only 376 * reported relative to a given window and its descendants. 377 * 378 * Results: 379 * A standard Tcl result is returned. TCL_OK is the normal return value; 380 * if the grab could not be set then TCL_ERROR is returned and the 381 * interp's result will hold an error message. 382 * 383 * Side effects: 384 * Once this call completes successfully, no window outside the tree 385 * rooted at tkwin will receive pointer- or keyboard-related events until 386 * the next call to Tk_Ungrab. If a previous grab was in effect within 387 * this application, then it is replaced with a new one. 388 * 389 *---------------------------------------------------------------------- 390 */ 391 392int 393Tk_Grab( 394 Tcl_Interp *interp, /* Used for error reporting. */ 395 Tk_Window tkwin, /* Window on whose behalf the pointer is to be 396 * grabbed. */ 397 int grabGlobal) /* Non-zero means issue a grab to the server 398 * so that no other application gets mouse or 399 * keyboard events. Zero means the grab only 400 * applies within this application. */ 401{ 402 int grabResult, numTries; 403 TkWindow *winPtr = (TkWindow *) tkwin; 404 TkDisplay *dispPtr = winPtr->dispPtr; 405 TkWindow *winPtr2; 406 unsigned int serial; 407 408 ReleaseButtonGrab(dispPtr); 409 if (dispPtr->eventualGrabWinPtr != NULL) { 410 if ((dispPtr->eventualGrabWinPtr == winPtr) 411 && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) { 412 return TCL_OK; 413 } 414 if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) { 415 alreadyGrabbed: 416 Tcl_SetResult(interp, "grab failed: another application has grab", 417 TCL_STATIC); 418 return TCL_ERROR; 419 } 420 Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr); 421 } 422 423 Tk_MakeWindowExist(tkwin); 424#ifndef MAC_OSX_TK 425 if (!grabGlobal) 426#else 427 if (0) 428#endif 429 { 430 Window dummy1, dummy2; 431 int dummy3, dummy4, dummy5, dummy6; 432 unsigned int state; 433 434 /* 435 * Local grab. However, if any mouse buttons are down, turn it into a 436 * global grab temporarily, until the last button goes up. This does 437 * two things: (a) it makes sure that we see the button-up event; and 438 * (b) it allows us to track mouse motion among all of the windows of 439 * this application. 440 */ 441 442 dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); 443 XQueryPointer(dispPtr->display, winPtr->window, &dummy1, 444 &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state); 445 if ((state & ALL_BUTTONS) != 0) { 446 dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; 447 goto setGlobalGrab; 448 } 449 } else { 450 dispPtr->grabFlags |= GRAB_GLOBAL; 451 setGlobalGrab: 452 453 /* 454 * Tricky point: must ungrab before grabbing. This is needed in case 455 * there is a button auto-grab already in effect. If there is, and the 456 * mouse has moved to a different window, X won't generate enter and 457 * leave events to move the mouse if we grab without ungrabbing. 458 */ 459 460 XUngrabPointer(dispPtr->display, CurrentTime); 461 serial = NextRequest(dispPtr->display); 462 463 /* 464 * Another tricky point: there are races with some window managers 465 * that can cause grabs to fail because the window manager hasn't 466 * released its grab quickly enough. To work around this problem, 467 * retry a few times after AlreadyGrabbed errors to give the grab 468 * release enough time to register with the server. 469 */ 470 471 grabResult = 0; /* Needed only to prevent gcc compiler 472 * warnings. */ 473 for (numTries = 0; numTries < 10; numTries++) { 474 grabResult = XGrabPointer(dispPtr->display, winPtr->window, 475 True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask 476 |PointerMotionMask, GrabModeAsync, GrabModeAsync, None, 477 None, CurrentTime); 478 if (grabResult != AlreadyGrabbed) { 479 break; 480 } 481 Tcl_Sleep(100); 482 } 483 if (grabResult != 0) { 484 grabError: 485 if (grabResult == GrabNotViewable) { 486 Tcl_SetResult(interp, "grab failed: window not viewable", 487 TCL_STATIC); 488 } else if (grabResult == AlreadyGrabbed) { 489 goto alreadyGrabbed; 490 } else if (grabResult == GrabFrozen) { 491 Tcl_SetResult(interp, 492 "grab failed: keyboard or pointer frozen", TCL_STATIC); 493 } else if (grabResult == GrabInvalidTime) { 494 Tcl_SetResult(interp, "grab failed: invalid time", 495 TCL_STATIC); 496 } else { 497 char msg[64 + TCL_INTEGER_SPACE]; 498 499 sprintf(msg, "grab failed for unknown reason (code %d)", 500 grabResult); 501 Tcl_AppendResult(interp, msg, NULL); 502 } 503 return TCL_ERROR; 504 } 505 grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin), 506 False, GrabModeAsync, GrabModeAsync, CurrentTime); 507 if (grabResult != 0) { 508 XUngrabPointer(dispPtr->display, CurrentTime); 509 goto grabError; 510 } 511 512 /* 513 * Eat up any grab-related events generated by the server for the 514 * grab. There are several reasons for doing this: 515 * 516 * 1. We have to synthesize the events for local grabs anyway, since 517 * the server doesn't participate in them. 518 * 2. The server doesn't always generate the right events for global 519 * grabs (e.g. it generates events even if the current window is in 520 * the grab tree, which we don't want). 521 * 3. We want all the grab-related events to be processed immediately 522 * (before other events that are already queued); events coming 523 * from the server will be in the wrong place, but events we 524 * synthesize here will go to the front of the queue. 525 */ 526 527 EatGrabEvents(dispPtr, serial); 528 } 529 530 /* 531 * Synthesize leave events to move the pointer from its current window up 532 * to the lowest ancestor that it has in common with the grab window. 533 * However, only do this if the pointer is outside the grab window's 534 * subtree but inside the grab window's application. 535 */ 536 537 if ((dispPtr->serverWinPtr != NULL) 538 && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) { 539 for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) { 540 if (winPtr2 == winPtr) { 541 break; 542 } 543 if (winPtr2 == NULL) { 544 MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0); 545 break; 546 } 547 } 548 } 549 QueueGrabWindowChange(dispPtr, winPtr); 550 return TCL_OK; 551} 552 553/* 554 *---------------------------------------------------------------------- 555 * 556 * Tk_Ungrab -- 557 * 558 * Releases a grab on the mouse pointer and keyboard, if there is one set 559 * on the specified window. 560 * 561 * Results: 562 * None. 563 * 564 * Side effects: 565 * Pointer and keyboard events will start being delivered to other 566 * windows again. 567 * 568 *---------------------------------------------------------------------- 569 */ 570 571void 572Tk_Ungrab( 573 Tk_Window tkwin) /* Window whose grab should be released. */ 574{ 575 TkDisplay *dispPtr; 576 TkWindow *grabWinPtr, *winPtr; 577 unsigned int serial; 578 579 grabWinPtr = (TkWindow *) tkwin; 580 dispPtr = grabWinPtr->dispPtr; 581 if (grabWinPtr != dispPtr->eventualGrabWinPtr) { 582 return; 583 } 584 ReleaseButtonGrab(dispPtr); 585 QueueGrabWindowChange(dispPtr, NULL); 586 if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) { 587 dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); 588 serial = NextRequest(dispPtr->display); 589 XUngrabPointer(dispPtr->display, CurrentTime); 590 XUngrabKeyboard(dispPtr->display, CurrentTime); 591 EatGrabEvents(dispPtr, serial); 592 } 593 594 /* 595 * Generate events to move the pointer back to the window where it really 596 * is. Some notes: 597 * 1. As with grabs, only do this if the "real" window is not a descendant 598 * of the grab window, since in this case the pointer is already where 599 * it's supposed to be. 600 * 2. If the "real" window is in some other application then don't 601 * generate any events at all, since everything's already been reported 602 * correctly. 603 * 3. Only generate enter events. Don't generate leave events, because we 604 * never told the lower-level windows that they had the pointer in the 605 * first place. 606 */ 607 608 for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) { 609 if (winPtr == grabWinPtr) { 610 break; 611 } 612 if (winPtr == NULL) { 613 if ((dispPtr->serverWinPtr == NULL) || 614 (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) { 615 MovePointer2(grabWinPtr, dispPtr->serverWinPtr, 616 NotifyUngrab, 0, 1); 617 } 618 break; 619 } 620 } 621} 622 623/* 624 *---------------------------------------------------------------------- 625 * 626 * ReleaseButtonGrab -- 627 * 628 * This function is called to release a simulated button grab, if there 629 * is one in effect. A button grab is present whenever 630 * dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL flag is 631 * set. 632 * 633 * Results: 634 * None. 635 * 636 * Side effects: 637 * DispPtr->buttonWinPtr is reset to NULL, and enter and leave events are 638 * generated if necessary to move the pointer from the button grab window 639 * to its current window. 640 * 641 *---------------------------------------------------------------------- 642 */ 643 644static void 645ReleaseButtonGrab( 646 register TkDisplay *dispPtr)/* Display whose button grab is to be 647 * released. */ 648{ 649 unsigned int serial; 650 651 if (dispPtr->buttonWinPtr != NULL) { 652 if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) { 653 MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr, 654 NotifyUngrab, 1, 1); 655 } 656 dispPtr->buttonWinPtr = NULL; 657 } 658 if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) { 659 dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL; 660 serial = NextRequest(dispPtr->display); 661 XUngrabPointer(dispPtr->display, CurrentTime); 662 XUngrabKeyboard(dispPtr->display, CurrentTime); 663 EatGrabEvents(dispPtr, serial); 664 } 665} 666 667/* 668 *---------------------------------------------------------------------- 669 * 670 * TkPointerEvent -- 671 * 672 * This function is called for each pointer-related event, before the 673 * event has been processed. It does various things to make grabs work 674 * correctly. 675 * 676 * Results: 677 * If the return value is 1 it means the event should be processed (event 678 * handlers should be invoked). If the return value is 0 it means the 679 * event should be ignored in order to make grabs work correctly. In some 680 * cases this function modifies the event. 681 * 682 * Side effects: 683 * Grab state information may be updated. New events may also be pushed 684 * back onto the event queue to replace or augment the one passed in 685 * here. 686 * 687 *---------------------------------------------------------------------- 688 */ 689 690int 691TkPointerEvent( 692 register XEvent *eventPtr, /* Pointer to the event. */ 693 TkWindow *winPtr) /* Tk's information for window where event was 694 * reported. */ 695{ 696 register TkWindow *winPtr2; 697 TkDisplay *dispPtr = winPtr->dispPtr; 698 unsigned int serial; 699 int outsideGrabTree = 0; 700 int ancestorOfGrab = 0; 701 int appGrabbed = 0; /* Non-zero means event is being reported to 702 * an application that is affected by the 703 * grab. */ 704 705 /* 706 * Collect information about the grab (if any). 707 */ 708 709 switch (TkGrabState(winPtr)) { 710 case TK_GRAB_IN_TREE: 711 appGrabbed = 1; 712 break; 713 case TK_GRAB_ANCESTOR: 714 appGrabbed = 1; 715 outsideGrabTree = 1; 716 ancestorOfGrab = 1; 717 break; 718 case TK_GRAB_EXCLUDED: 719 appGrabbed = 1; 720 outsideGrabTree = 1; 721 break; 722 } 723 724 if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { 725 /* 726 * Keep track of what window the mouse is *really* over. Any events 727 * that we generate have a special send_event value, which is detected 728 * below and used to ignore the event for purposes of setting 729 * serverWinPtr. 730 */ 731 732 if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) { 733 if ((eventPtr->type == LeaveNotify) && 734 (winPtr->flags & TK_TOP_HIERARCHY)) { 735 dispPtr->serverWinPtr = NULL; 736 } else { 737 dispPtr->serverWinPtr = winPtr; 738 } 739 } 740 741 /* 742 * When a grab is active, X continues to report enter and leave events 743 * for windows outside the tree of the grab window: 744 * 1. Detect these events and ignore them except for windows above the 745 * grab window. 746 * 2. Allow Enter and Leave events to pass through the windows above 747 * the grab window, but never let them end up with the pointer *in* 748 * one of those windows. 749 */ 750 751 if (dispPtr->grabWinPtr != NULL) { 752 if (outsideGrabTree && appGrabbed) { 753 if (!ancestorOfGrab) { 754 return 0; 755 } 756 switch (eventPtr->xcrossing.detail) { 757 case NotifyInferior: 758 return 0; 759 case NotifyAncestor: 760 eventPtr->xcrossing.detail = NotifyVirtual; 761 break; 762 case NotifyNonlinear: 763 eventPtr->xcrossing.detail = NotifyNonlinearVirtual; 764 break; 765 } 766 } 767 768 /* 769 * Make buttons have the same grab-like behavior inside a grab as 770 * they do outside a grab: do this by ignoring enter and leave 771 * events except for the window in which the button was pressed. 772 */ 773 774 if ((dispPtr->buttonWinPtr != NULL) 775 && (winPtr != dispPtr->buttonWinPtr)) { 776 return 0; 777 } 778 } 779 return 1; 780 } 781 782 if (!appGrabbed) { 783 return 1; 784 } 785 786 if (eventPtr->type == MotionNotify) { 787 /* 788 * When grabs are active, X reports motion events relative to the 789 * window under the pointer. Instead, it should report the events 790 * relative to the window the button went down in, if there is a 791 * button down. Otherwise, if the pointer window is outside the 792 * subtree of the grab window, the events should be reported relative 793 * to the grab window. Otherwise, the event should be reported to the 794 * pointer window. 795 */ 796 797 winPtr2 = winPtr; 798 if (dispPtr->buttonWinPtr != NULL) { 799 winPtr2 = dispPtr->buttonWinPtr; 800 } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) { 801 winPtr2 = dispPtr->grabWinPtr; 802 } 803 if (winPtr2 != winPtr) { 804 TkChangeEventWindow(eventPtr, winPtr2); 805 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); 806 return 0; 807 } 808 return 1; 809 } 810 811 /* 812 * Process ButtonPress and ButtonRelease events: 813 * 1. Keep track of whether a button is down and what window it went down 814 * in. 815 * 2. If the first button goes down outside the grab tree, pretend it went 816 * down in the grab window. Note: it's important to redirect events to 817 * the grab window like this in order to make things like menus work, 818 * where button presses outside the grabbed menu need to be seen. An 819 * application can always ignore the events if they occur outside its 820 * window. 821 * 3. If a button press or release occurs outside the window where the 822 * first button was pressed, retarget the event so it's reported to the 823 * window where the first button was pressed. 824 * 4. If the last button is released in a window different than where the 825 * first button was pressed, generate Enter/Leave events to move the 826 * mouse from the button window to its current window. 827 * 5. If the grab is set at a time when a button is already down, or if 828 * the window where the button was pressed was deleted, then 829 * dispPtr->buttonWinPtr will stay NULL. Just forget about the 830 * auto-grab for the button press; events will go to whatever window 831 * contains the pointer. If this window isn't in the grab tree then 832 * redirect events to the grab window. 833 * 6. When a button is pressed during a local grab, the X server sets a 834 * grab of its own, since it doesn't even know about our local grab. 835 * This causes enter and leave events no longer to be generated in the 836 * same way as for global grabs. To eliminate this problem, set a 837 * temporary global grab when the first button goes down and release it 838 * when the last button comes up. 839 */ 840 841 if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) { 842 winPtr2 = dispPtr->buttonWinPtr; 843 if (winPtr2 == NULL) { 844 if (outsideGrabTree) { 845 winPtr2 = dispPtr->grabWinPtr; /* Note 5. */ 846 } else { 847 winPtr2 = winPtr; /* Note 5. */ 848 } 849 } 850 if (eventPtr->type == ButtonPress) { 851 if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) { 852 if (outsideGrabTree) { 853 TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr); 854 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); 855 return 0; /* Note 2. */ 856 } 857 if (!(dispPtr->grabFlags & GRAB_GLOBAL)) { /* Note 6. */ 858 serial = NextRequest(dispPtr->display); 859 if (XGrabPointer(dispPtr->display, 860 dispPtr->grabWinPtr->window, True, 861 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask, 862 GrabModeAsync, GrabModeAsync, None, None, 863 CurrentTime) == 0) { 864 EatGrabEvents(dispPtr, serial); 865 if (XGrabKeyboard(dispPtr->display, winPtr->window, 866 False, GrabModeAsync, GrabModeAsync, 867 CurrentTime) == 0) { 868 dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; 869 } else { 870 XUngrabPointer(dispPtr->display, CurrentTime); 871 } 872 } 873 } 874 dispPtr->buttonWinPtr = winPtr; 875 return 1; 876 } 877 } else { 878 if ((eventPtr->xbutton.state & ALL_BUTTONS) 879 == buttonStates[eventPtr->xbutton.button - Button1]) { 880 ReleaseButtonGrab(dispPtr); /* Note 4. */ 881 } 882 } 883 if (winPtr2 != winPtr) { 884 TkChangeEventWindow(eventPtr, winPtr2); 885 Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); 886 return 0; /* Note 3. */ 887 } 888 } 889 890 return 1; 891} 892 893/* 894 *---------------------------------------------------------------------- 895 * 896 * TkChangeEventWindow -- 897 * 898 * Given an event and a new window to which the event should be 899 * retargeted, modify fields of the event so that the event is properly 900 * retargeted to the new window. 901 * 902 * Results: 903 * The following fields of eventPtr are modified: window, subwindow, x, 904 * y, same_screen. 905 * 906 * Side effects: 907 * None. 908 * 909 *---------------------------------------------------------------------- 910 */ 911 912void 913TkChangeEventWindow( 914 register XEvent *eventPtr, /* Event to retarget. Must have type 915 * ButtonPress, ButtonRelease, KeyPress, 916 * KeyRelease, MotionNotify, EnterNotify, or 917 * LeaveNotify. */ 918 TkWindow *winPtr) /* New target window for event. */ 919{ 920 int x, y, sameScreen, bd; 921 register TkWindow *childPtr; 922 923 eventPtr->xmotion.window = Tk_WindowId(winPtr); 924 if (eventPtr->xmotion.root == 925 RootWindow(winPtr->display, winPtr->screenNum)) { 926 Tk_GetRootCoords((Tk_Window) winPtr, &x, &y); 927 eventPtr->xmotion.x = eventPtr->xmotion.x_root - x; 928 eventPtr->xmotion.y = eventPtr->xmotion.y_root - y; 929 eventPtr->xmotion.subwindow = None; 930 for (childPtr = winPtr->childList; childPtr != NULL; 931 childPtr = childPtr->nextPtr) { 932 if (childPtr->flags & TK_TOP_HIERARCHY) { 933 continue; 934 } 935 x = eventPtr->xmotion.x - childPtr->changes.x; 936 y = eventPtr->xmotion.y - childPtr->changes.y; 937 bd = childPtr->changes.border_width; 938 if ((x >= -bd) && (y >= -bd) 939 && (x < (childPtr->changes.width + bd)) 940 && (y < (childPtr->changes.height + bd))) { 941 eventPtr->xmotion.subwindow = childPtr->window; 942 } 943 } 944 sameScreen = 1; 945 } else { 946 eventPtr->xmotion.x = 0; 947 eventPtr->xmotion.y = 0; 948 eventPtr->xmotion.subwindow = None; 949 sameScreen = 0; 950 } 951 if (eventPtr->type == MotionNotify) { 952 eventPtr->xmotion.same_screen = sameScreen; 953 } else { 954 eventPtr->xbutton.same_screen = sameScreen; 955 } 956} 957 958/* 959 *---------------------------------------------------------------------- 960 * 961 * TkInOutEvents -- 962 * 963 * This function synthesizes EnterNotify and LeaveNotify events to 964 * correctly transfer the pointer from one window to another. It can also 965 * be used to generate FocusIn and FocusOut events to move the input 966 * focus. 967 * 968 * Results: 969 * None. 970 * 971 * Side effects: 972 * Synthesized events may be pushed back onto the event queue. The event 973 * pointed to by eventPtr is modified. 974 * 975 *---------------------------------------------------------------------- 976 */ 977 978void 979TkInOutEvents( 980 XEvent *eventPtr, /* A template X event. Must have all fields 981 * properly set except for type, window, 982 * subwindow, x, y, detail, and same_screen. 983 * (Not all of these fields are valid for 984 * FocusIn/FocusOut events; x_root and y_root 985 * must be valid for Enter/Leave events, even 986 * though x and y needn't be valid). */ 987 TkWindow *sourcePtr, /* Window that used to have the pointer or 988 * focus (NULL means it was not in a window 989 * managed by this process). */ 990 TkWindow *destPtr, /* Window that is to end up with the pointer 991 * or focus (NULL means it's not one managed 992 * by this process). */ 993 int leaveType, /* Type of events to generate for windows 994 * being left (LeaveNotify or FocusOut). 0 995 * means don't generate leave events. */ 996 int enterType, /* Type of events to generate for windows 997 * being entered (EnterNotify or FocusIn). 0 998 * means don't generate enter events. */ 999 Tcl_QueuePosition position) /* Position at which events are added to the 1000 * system event queue. */ 1001{ 1002 register TkWindow *winPtr; 1003 int upLevels, downLevels, i, j, focus; 1004 1005 /* 1006 * There are four possible cases to deal with: 1007 * 1008 * 1. SourcePtr and destPtr are the same. There's nothing to do in this 1009 * case. 1010 * 2. SourcePtr is an ancestor of destPtr in the same top-level window. 1011 * Must generate events down the window tree from source to dest. 1012 * 3. DestPtr is an ancestor of sourcePtr in the same top-level window. 1013 * Must generate events up the window tree from sourcePtr to destPtr. 1014 * 4. All other cases. Must first generate events up the window tree from 1015 * sourcePtr to its top-level, then down from destPtr's top-level to 1016 * destPtr. This form is called "non-linear." 1017 * 1018 * The call to FindCommonAncestor separates these four cases and decides 1019 * how many levels up and down events have to be generated for. 1020 */ 1021 1022 if (sourcePtr == destPtr) { 1023 return; 1024 } 1025 if ((leaveType == FocusOut) || (enterType == FocusIn)) { 1026 focus = 1; 1027 } else { 1028 focus = 0; 1029 } 1030 FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels); 1031 1032 /* 1033 * Generate enter/leave events and add them to the grab event queue. 1034 */ 1035 1036#define QUEUE(w, t, d) \ 1037 if (w->window != None) { \ 1038 eventPtr->type = t; \ 1039 if (focus) { \ 1040 eventPtr->xfocus.window = w->window; \ 1041 eventPtr->xfocus.detail = d; \ 1042 } else { \ 1043 eventPtr->xcrossing.detail = d; \ 1044 TkChangeEventWindow(eventPtr, w); \ 1045 } \ 1046 Tk_QueueWindowEvent(eventPtr, position); \ 1047 } 1048 1049 if (downLevels == 0) { 1050 /* 1051 * SourcePtr is an inferior of destPtr. 1052 */ 1053 1054 if (leaveType != 0) { 1055 QUEUE(sourcePtr, leaveType, NotifyAncestor); 1056 for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; 1057 winPtr = winPtr->parentPtr, i--) { 1058 QUEUE(winPtr, leaveType, NotifyVirtual); 1059 } 1060 } 1061 if ((enterType != 0) && (destPtr != NULL)) { 1062 QUEUE(destPtr, enterType, NotifyInferior); 1063 } 1064 } else if (upLevels == 0) { 1065 /* 1066 * DestPtr is an inferior of sourcePtr. 1067 */ 1068 1069 if ((leaveType != 0) && (sourcePtr != NULL)) { 1070 QUEUE(sourcePtr, leaveType, NotifyInferior); 1071 } 1072 if (enterType != 0) { 1073 for (i = downLevels-1; i > 0; i--) { 1074 for (winPtr = destPtr->parentPtr, j = 1; j < i; 1075 winPtr = winPtr->parentPtr, j++) { 1076 /* empty */ 1077 } 1078 QUEUE(winPtr, enterType, NotifyVirtual); 1079 } 1080 if (destPtr != NULL) { 1081 QUEUE(destPtr, enterType, NotifyAncestor); 1082 } 1083 } 1084 } else { 1085 /* 1086 * Non-linear: neither window is an inferior of the other. 1087 */ 1088 1089 if (leaveType != 0) { 1090 QUEUE(sourcePtr, leaveType, NotifyNonlinear); 1091 for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; 1092 winPtr = winPtr->parentPtr, i--) { 1093 QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); 1094 } 1095 } 1096 if (enterType != 0) { 1097 for (i = downLevels-1; i > 0; i--) { 1098 for (winPtr = destPtr->parentPtr, j = 1; j < i; 1099 winPtr = winPtr->parentPtr, j++) { 1100 } 1101 QUEUE(winPtr, enterType, NotifyNonlinearVirtual); 1102 } 1103 if (destPtr != NULL) { 1104 QUEUE(destPtr, enterType, NotifyNonlinear); 1105 } 1106 } 1107 } 1108} 1109 1110/* 1111 *---------------------------------------------------------------------- 1112 * 1113 * MovePointer2 -- 1114 * 1115 * This function synthesizes EnterNotify and LeaveNotify events to 1116 * correctly transfer the pointer from one window to another. It is 1117 * different from TkInOutEvents in that no template X event needs to be 1118 * supplied; this function generates the template event and calls 1119 * TkInOutEvents. 1120 * 1121 * Results: 1122 * None. 1123 * 1124 * Side effects: 1125 * Synthesized events may be pushed back onto the event queue. 1126 * 1127 *---------------------------------------------------------------------- 1128 */ 1129 1130static void 1131MovePointer2( 1132 TkWindow *sourcePtr, /* Window currently containing pointer (NULL 1133 * means it's not one managed by this 1134 * process). */ 1135 TkWindow *destPtr, /* Window that is to end up containing the 1136 * pointer (NULL means it's not one managed by 1137 * this process). */ 1138 int mode, /* Mode for enter/leave events, such as 1139 * NotifyNormal or NotifyUngrab. */ 1140 int leaveEvents, /* Non-zero means generate leave events for 1141 * the windows being left. Zero means don't 1142 * generate leave events. */ 1143 int enterEvents) /* Non-zero means generate enter events for 1144 * the windows being entered. Zero means don't 1145 * generate enter events. */ 1146{ 1147 XEvent event; 1148 Window dummy1, dummy2; 1149 int dummy3, dummy4; 1150 TkWindow *winPtr; 1151 1152 winPtr = sourcePtr; 1153 if ((winPtr == NULL) || (winPtr->window == None)) { 1154 winPtr = destPtr; 1155 if ((winPtr == NULL) || (winPtr->window == None)) { 1156 return; 1157 } 1158 } 1159 1160 event.xcrossing.serial = LastKnownRequestProcessed(winPtr->display); 1161 event.xcrossing.send_event = GENERATED_EVENT_MAGIC; 1162 event.xcrossing.display = winPtr->display; 1163 event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum); 1164 event.xcrossing.time = TkCurrentTime(winPtr->dispPtr); 1165 XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2, 1166 &event.xcrossing.x_root, &event.xcrossing.y_root, 1167 &dummy3, &dummy4, &event.xcrossing.state); 1168 event.xcrossing.mode = mode; 1169 event.xcrossing.focus = False; 1170 TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0, 1171 (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK); 1172} 1173 1174/* 1175 *---------------------------------------------------------------------- 1176 * 1177 * TkGrabDeadWindow -- 1178 * 1179 * This function is invoked whenever a window is deleted, so that 1180 * grab-related cleanup can be performed. 1181 * 1182 * Results: 1183 * None. 1184 * 1185 * Side effects: 1186 * Various cleanups happen, such as generating events to move the pointer 1187 * back to its "natural" window as if an ungrab had been done. See the 1188 * code. 1189 * 1190 *---------------------------------------------------------------------- 1191 */ 1192 1193void 1194TkGrabDeadWindow( 1195 register TkWindow *winPtr) /* Window that is in the process of being 1196 * deleted. */ 1197{ 1198 TkDisplay *dispPtr = winPtr->dispPtr; 1199 1200 if (dispPtr->eventualGrabWinPtr == winPtr) { 1201 /* 1202 * Grab window was deleted. Release the grab. 1203 */ 1204 1205 Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr); 1206 } else if (dispPtr->buttonWinPtr == winPtr) { 1207 ReleaseButtonGrab(dispPtr); 1208 } 1209 if (dispPtr->serverWinPtr == winPtr) { 1210 if (winPtr->flags & TK_TOP_HIERARCHY) { 1211 dispPtr->serverWinPtr = NULL; 1212 } else { 1213 dispPtr->serverWinPtr = winPtr->parentPtr; 1214 } 1215 } 1216 if (dispPtr->grabWinPtr == winPtr) { 1217 dispPtr->grabWinPtr = NULL; 1218 } 1219} 1220 1221/* 1222 *---------------------------------------------------------------------- 1223 * 1224 * EatGrabEvents -- 1225 * 1226 * This function is called to eliminate any Enter, Leave, FocusIn, or 1227 * FocusOut events in the event queue for a display that have mode 1228 * NotifyGrab or NotifyUngrab and have a serial number no less than a 1229 * given value and are not generated by the grab module. 1230 * 1231 * Results: 1232 * None. 1233 * 1234 * Side effects: 1235 * DispPtr's display gets sync-ed, and some of the events get removed 1236 * from the Tk event queue. 1237 * 1238 *---------------------------------------------------------------------- 1239 */ 1240 1241static void 1242EatGrabEvents( 1243 TkDisplay *dispPtr, /* Display from which to consume events. */ 1244 unsigned int serial) /* Only discard events that have a serial 1245 * number at least this great. */ 1246{ 1247 Tk_RestrictProc *oldProc; 1248 GrabInfo info; 1249 ClientData oldArg, dummy; 1250 1251 info.display = dispPtr->display; 1252 info.serial = serial; 1253 TkpSync(info.display); 1254 oldProc = Tk_RestrictEvents(GrabRestrictProc, (ClientData)&info, &oldArg); 1255 while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) { 1256 } 1257 Tk_RestrictEvents(oldProc, oldArg, &dummy); 1258} 1259 1260/* 1261 *---------------------------------------------------------------------- 1262 * 1263 * GrabRestrictProc -- 1264 * 1265 * A Tk_RestrictProc used by EatGrabEvents to eliminate any Enter, Leave, 1266 * FocusIn, or FocusOut events in the event queue for a display that has 1267 * mode NotifyGrab or NotifyUngrab and have a serial number no less than 1268 * a given value. 1269 * 1270 * Results: 1271 * Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT. 1272 * 1273 * Side effects: 1274 * None. 1275 * 1276 *---------------------------------------------------------------------- 1277 */ 1278 1279static Tk_RestrictAction 1280GrabRestrictProc( 1281 ClientData arg, 1282 XEvent *eventPtr) 1283{ 1284 GrabInfo *info = (GrabInfo *) arg; 1285 int mode, diff; 1286 1287 /* 1288 * The diff caculation is trickier than it may seem. Don't forget that 1289 * serial numbers can wrap around, so can't compare the two serial numbers 1290 * directly. 1291 */ 1292 1293 diff = eventPtr->xany.serial - info->serial; 1294 if ((eventPtr->type == EnterNotify) 1295 || (eventPtr->type == LeaveNotify)) { 1296 mode = eventPtr->xcrossing.mode; 1297 } else if ((eventPtr->type == FocusIn) 1298 || (eventPtr->type == FocusOut)) { 1299 mode = eventPtr->xfocus.mode; 1300 } else { 1301 mode = NotifyNormal; 1302 } 1303 if ((info->display != eventPtr->xany.display) || (mode == NotifyNormal) 1304 || (diff < 0)) { 1305 return TK_DEFER_EVENT; 1306 } else { 1307 return TK_DISCARD_EVENT; 1308 } 1309} 1310 1311/* 1312 *---------------------------------------------------------------------- 1313 * 1314 * QueueGrabWindowChange -- 1315 * 1316 * This function queues a special event in the Tcl event queue, which 1317 * will cause the "grabWinPtr" field for the display to get modified when 1318 * the event is processed. This is needed to make sure that the grab 1319 * window changes at the proper time relative to grab-related enter and 1320 * leave events that are also in the queue. In particular, this approach 1321 * works even when multiple grabs and ungrabs happen back-to-back. 1322 * 1323 * Results: 1324 * None. 1325 * 1326 * Side effects: 1327 * DispPtr->grabWinPtr will be modified later (by GrabWinEventProc) when 1328 * the event is removed from the grab event queue. 1329 * 1330 *---------------------------------------------------------------------- 1331 */ 1332 1333static void 1334QueueGrabWindowChange( 1335 TkDisplay *dispPtr, /* Display on which to change the grab 1336 * window. */ 1337 TkWindow *grabWinPtr) /* Window that is to become the new grab 1338 * window (may be NULL). */ 1339{ 1340 NewGrabWinEvent *grabEvPtr; 1341 1342 grabEvPtr = (NewGrabWinEvent *) ckalloc(sizeof(NewGrabWinEvent)); 1343 grabEvPtr->header.proc = GrabWinEventProc; 1344 grabEvPtr->dispPtr = dispPtr; 1345 if (grabWinPtr == NULL) { 1346 grabEvPtr->grabWindow = None; 1347 } else { 1348 grabEvPtr->grabWindow = grabWinPtr->window; 1349 } 1350 Tcl_QueueEvent(&grabEvPtr->header, TCL_QUEUE_MARK); 1351 dispPtr->eventualGrabWinPtr = grabWinPtr; 1352} 1353 1354/* 1355 *---------------------------------------------------------------------- 1356 * 1357 * GrabWinEventProc -- 1358 * 1359 * This function is invoked as a handler for Tcl_Events of type 1360 * NewGrabWinEvent. It updates the current grab window field in a 1361 * display. 1362 * 1363 * Results: 1364 * Returns 1 if the event was processed, 0 if it should be deferred for 1365 * processing later. 1366 * 1367 * Side effects: 1368 * The grabWinPtr field is modified in the display associated with the 1369 * event. 1370 * 1371 *---------------------------------------------------------------------- 1372 */ 1373 1374static int 1375GrabWinEventProc( 1376 Tcl_Event *evPtr, /* Event of type NewGrabWinEvent. */ 1377 int flags) /* Flags argument to Tk_DoOneEvent: indicates 1378 * what kinds of events are being processed 1379 * right now. */ 1380{ 1381 NewGrabWinEvent *grabEvPtr = (NewGrabWinEvent *) evPtr; 1382 1383 grabEvPtr->dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow( 1384 grabEvPtr->dispPtr->display, grabEvPtr->grabWindow); 1385 return 1; 1386} 1387 1388/* 1389 *---------------------------------------------------------------------- 1390 * 1391 * FindCommonAncestor -- 1392 * 1393 * Given two windows, this function finds their least common ancestor and 1394 * also computes how many levels up this ancestor is from each of the 1395 * original windows. 1396 * 1397 * Results: 1398 * If the windows are in different applications or top-level windows, 1399 * then NULL is returned and *countPtr1 and *countPtr2 are set to the 1400 * depths of the two windows in their respective top-level windows (1 1401 * means the window is a top-level, 2 means its parent is a top-level, 1402 * and so on). Otherwise, the return value is a pointer to the common 1403 * ancestor and the counts are set to the distance of winPtr1 and winPtr2 1404 * from this ancestor (1 means they're children, 2 means grand-children, 1405 * etc.). 1406 * 1407 * Side effects: 1408 * None. 1409 * 1410 *---------------------------------------------------------------------- 1411 */ 1412 1413static TkWindow * 1414FindCommonAncestor( 1415 TkWindow *winPtr1, /* First window. May be NULL. */ 1416 TkWindow *winPtr2, /* Second window. May be NULL. */ 1417 int *countPtr1, /* Store nesting level of winPtr1 within 1418 * common ancestor here. */ 1419 int *countPtr2) /* Store nesting level of winPtr2 within 1420 * common ancestor here. */ 1421{ 1422 register TkWindow *winPtr; 1423 TkWindow *ancestorPtr; 1424 int count1, count2, i; 1425 1426 /* 1427 * Mark winPtr1 and all of its ancestors with a special flag bit. 1428 */ 1429 1430 if (winPtr1 != NULL) { 1431 for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) { 1432 winPtr->flags |= TK_GRAB_FLAG; 1433 if (winPtr->flags & TK_TOP_HIERARCHY) { 1434 break; 1435 } 1436 } 1437 } 1438 1439 /* 1440 * Search upwards from winPtr2 until an ancestor of winPtr1 is found or a 1441 * top-level window is reached. 1442 */ 1443 1444 winPtr = winPtr2; 1445 count2 = 0; 1446 ancestorPtr = NULL; 1447 if (winPtr2 != NULL) { 1448 for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) { 1449 if (winPtr->flags & TK_GRAB_FLAG) { 1450 ancestorPtr = winPtr; 1451 break; 1452 } 1453 if (winPtr->flags & TK_TOP_HIERARCHY) { 1454 count2++; 1455 break; 1456 } 1457 } 1458 } 1459 1460 /* 1461 * Search upwards from winPtr1 again, clearing the flag bits and 1462 * remembering how many levels up we had to go. 1463 */ 1464 1465 if (winPtr1 == NULL) { 1466 count1 = 0; 1467 } else { 1468 count1 = -1; 1469 for (i = 0, winPtr = winPtr1; winPtr != NULL; 1470 i++, winPtr = winPtr->parentPtr) { 1471 winPtr->flags &= ~TK_GRAB_FLAG; 1472 if (winPtr == ancestorPtr) { 1473 count1 = i; 1474 } 1475 if (winPtr->flags & TK_TOP_HIERARCHY) { 1476 if (count1 == -1) { 1477 count1 = i+1; 1478 } 1479 break; 1480 } 1481 } 1482 } 1483 1484 *countPtr1 = count1; 1485 *countPtr2 = count2; 1486 return ancestorPtr; 1487} 1488 1489/* 1490 *---------------------------------------------------------------------- 1491 * 1492 * TkPositionInTree -- 1493 * 1494 * Compute where the given window is relative to a particular subtree of 1495 * the window hierarchy. 1496 * 1497 * Results: 1498 * Returns TK_GRAB_IN_TREE if the window is contained in the subtree. 1499 * Returns TK_GRAB_ANCESTOR if the window is an ancestor of the subtree, 1500 * in the same toplevel. Otherwise it returns TK_GRAB_EXCLUDED. 1501 * 1502 * Side effects: 1503 * None. 1504 * 1505 *---------------------------------------------------------------------- 1506 */ 1507 1508int 1509TkPositionInTree( 1510 TkWindow *winPtr, /* Window to be checked. */ 1511 TkWindow *treePtr) /* Root of tree to compare against. */ 1512{ 1513 TkWindow *winPtr2; 1514 1515 for (winPtr2 = winPtr; winPtr2 != treePtr; 1516 winPtr2 = winPtr2->parentPtr) { 1517 if (winPtr2 == NULL) { 1518 for (winPtr2 = treePtr; winPtr2 != NULL; 1519 winPtr2 = winPtr2->parentPtr) { 1520 if (winPtr2 == winPtr) { 1521 return TK_GRAB_ANCESTOR; 1522 } 1523 if (winPtr2->flags & TK_TOP_HIERARCHY) { 1524 break; 1525 } 1526 } 1527 return TK_GRAB_EXCLUDED; 1528 } 1529 } 1530 return TK_GRAB_IN_TREE; 1531} 1532 1533/* 1534 *---------------------------------------------------------------------- 1535 * 1536 * TkGrabState -- 1537 * 1538 * Given a window, this function returns a value that indicates the grab 1539 * state of the application relative to the window. 1540 * 1541 * Results: 1542 * The return value is one of three things: 1543 * TK_GRAB_NONE - no grab is in effect. 1544 * TK_GRAB_IN_TREE - there is a grab in effect, and winPtr is in 1545 * the grabbed subtree. 1546 * TK_GRAB_ANCESTOR - there is a grab in effect; winPtr is an 1547 * ancestor of the grabbed window, in the same 1548 * toplevel. 1549 * TK_GRAB_EXCLUDED - there is a grab in effect; winPtr is outside 1550 * the tree of the grab and is not an ancestor of 1551 * the grabbed window in the same toplevel. 1552 * 1553 * Side effects: 1554 * None. 1555 * 1556 *---------------------------------------------------------------------- 1557 */ 1558 1559int 1560TkGrabState( 1561 TkWindow *winPtr) /* Window for which grab information is 1562 * needed. */ 1563{ 1564 TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr; 1565 1566 if (grabWinPtr == NULL) { 1567 return TK_GRAB_NONE; 1568 } 1569 if ((winPtr->mainPtr != grabWinPtr->mainPtr) 1570 && !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) { 1571 return TK_GRAB_NONE; 1572 } 1573 1574 return TkPositionInTree(winPtr, grabWinPtr); 1575} 1576 1577/* 1578 * Local Variables: 1579 * mode: c 1580 * c-basic-offset: 4 1581 * fill-column: 78 1582 * End: 1583 */ 1584