home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkGrab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  45.0 KB  |  1,467 lines

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