home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkGrab.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  47KB  |  1,518 lines

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