tkGrab.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:48k
源码类别:

通讯编程

开发平台:

Visual C++

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