home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkPointer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  15.4 KB  |  635 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkPointer.c --
  3.  *
  4.  *    This file contains functions for emulating the X server
  5.  *    pointer and grab state machine.  This file is used by the
  6.  *    Mac and Windows platforms to generate appropriate enter/leave
  7.  *    events, and to update the global grab window information.
  8.  *
  9.  * Copyright (c) 1996 by Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkPointer.c 1.10 97/03/05 14:32:21
  15.  */
  16.  
  17. #include "tkInt.h"
  18.  
  19. #ifdef MAC_TCL
  20. #define Cursor XCursor
  21. #endif
  22.  
  23. /*
  24.  * Mask that selects any of the state bits corresponding to buttons,
  25.  * plus masks that select individual buttons' bits:
  26.  */
  27.  
  28. #define ALL_BUTTONS \
  29.     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
  30. static unsigned int buttonMasks[] = {
  31.     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
  32. };
  33. #define ButtonMask(b) (buttonMasks[(b)-Button1])
  34.  
  35. /*
  36.  * Declarations of static variables used in the pointer module.
  37.  */
  38.  
  39. static TkWindow *cursorWinPtr = NULL;    /* Window that is currently
  40.                      * controlling the global cursor. */
  41. static TkWindow *grabWinPtr = NULL;    /* Window that defines the top of the
  42.                      * grab tree in a global grab. */
  43. static XPoint lastPos = { 0, 0};    /* Last reported mouse position. */
  44. static int lastState = 0;        /* Last known state flags. */
  45. static TkWindow *lastWinPtr = NULL;    /* Last reported mouse window. */
  46. static TkWindow *restrictWinPtr = NULL;    /* Window to which all mouse events
  47.                      * will be reported. */
  48.  
  49. /*
  50.  * Forward declarations of procedures used in this file.
  51.  */
  52.  
  53. static int        GenerateEnterLeave _ANSI_ARGS_((TkWindow *winPtr,
  54.                 int x, int y, int state));
  55. static void        InitializeEvent _ANSI_ARGS_((XEvent* eventPtr,
  56.                 TkWindow *winPtr, int type, int x, int y,
  57.                 int state, int detail));
  58. static void        UpdateCursor _ANSI_ARGS_((TkWindow *winPtr));
  59.  
  60. /*
  61.  *----------------------------------------------------------------------
  62.  *
  63.  * InitializeEvent --
  64.  *
  65.  *    Initializes the common fields for several X events.
  66.  *
  67.  * Results:
  68.  *    None.
  69.  *
  70.  * Side effects:
  71.  *    Fills in the specified event structure.
  72.  *
  73.  *----------------------------------------------------------------------
  74.  */
  75.  
  76. static void
  77. InitializeEvent(eventPtr, winPtr, type, x, y, state, detail)
  78.     XEvent* eventPtr;        /* Event structure to initialize. */
  79.     TkWindow *winPtr;        /* Window to make event relative to. */
  80.     int type;            /* Message type. */
  81.     int x, y;            /* Root coords of event. */
  82.     int state;            /* State flags. */
  83.     int detail;            /* Detail value. */
  84. {
  85.     eventPtr->type = type;
  86.     eventPtr->xany.serial = LastKnownRequestProcessed(winPtr->display);
  87.     eventPtr->xany.send_event = False;
  88.     eventPtr->xany.display = winPtr->display;
  89.  
  90.     eventPtr->xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum);
  91.     eventPtr->xcrossing.time = TkpGetMS();
  92.     eventPtr->xcrossing.x_root = x;
  93.     eventPtr->xcrossing.y_root = y;
  94.  
  95.     switch (type) {
  96.     case EnterNotify:
  97.     case LeaveNotify:
  98.         eventPtr->xcrossing.mode = NotifyNormal;
  99.         eventPtr->xcrossing.state = state;
  100.         eventPtr->xcrossing.detail = detail;
  101.         eventPtr->xcrossing.focus = False;
  102.         break;
  103.     case MotionNotify:
  104.         eventPtr->xmotion.state = state;
  105.         eventPtr->xmotion.is_hint = detail;
  106.         break;
  107.     case ButtonPress:
  108.     case ButtonRelease:
  109.         eventPtr->xbutton.state = state;
  110.         eventPtr->xbutton.button = detail;
  111.         break;
  112.     }
  113.     TkChangeEventWindow(eventPtr, winPtr);
  114. }
  115.  
  116. /*
  117.  *----------------------------------------------------------------------
  118.  *
  119.  * GenerateEnterLeave --
  120.  *
  121.  *    Update the current mouse window and position, and generate
  122.  *    any enter/leave events that are needed.
  123.  *
  124.  * Results:
  125.  *    Returns 1 if enter/leave events were generated.
  126.  *
  127.  * Side effects:
  128.  *    May insert events into the Tk event queue.
  129.  *
  130.  *----------------------------------------------------------------------
  131.  */
  132.  
  133. static int
  134. GenerateEnterLeave(winPtr, x, y, state)
  135.     TkWindow *winPtr;        /* Current Tk window (or NULL). */
  136.     int x,y;            /* Current mouse position in root coords. */
  137.     int state;            /* State flags. */
  138. {
  139.     int crossed = 0;        /* 1 if mouse crossed a window boundary */
  140.  
  141.     if (winPtr != lastWinPtr) {
  142.     if (restrictWinPtr) {
  143.         int newPos, oldPos;
  144.  
  145.         newPos = TkPositionInTree(winPtr, restrictWinPtr);
  146.         oldPos = TkPositionInTree(lastWinPtr, restrictWinPtr);
  147.  
  148.         /*
  149.          * Check if the mouse crossed into or out of the restrict
  150.          * window.  If so, we need to generate an Enter or Leave event.
  151.          */
  152.  
  153.         if ((newPos != oldPos) && ((newPos == TK_GRAB_IN_TREE)
  154.             || (oldPos == TK_GRAB_IN_TREE))) {
  155.         XEvent event;
  156.         int type, detail;
  157.  
  158.         if (newPos == TK_GRAB_IN_TREE) {
  159.             type = EnterNotify;
  160.         } else {
  161.             type = LeaveNotify;
  162.         }
  163.         if ((oldPos == TK_GRAB_ANCESTOR)
  164.             || (newPos == TK_GRAB_ANCESTOR)) {
  165.             detail = NotifyAncestor;
  166.         } else {
  167.             detail = NotifyVirtual;
  168.         }
  169.         InitializeEvent(&event, restrictWinPtr, type, x, y,
  170.             state, detail);
  171.         Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  172.         }
  173.  
  174.     } else {
  175.         TkWindow *targetPtr;
  176.  
  177.         if ((lastWinPtr == NULL)
  178.         || (lastWinPtr->window == None)) {
  179.         targetPtr = winPtr;
  180.         } else {
  181.         targetPtr = lastWinPtr;
  182.         }
  183.  
  184.         if (targetPtr && (targetPtr->window != None)) {
  185.         XEvent event;
  186.  
  187.         /*
  188.          * Generate appropriate Enter/Leave events.
  189.          */
  190.  
  191.         InitializeEvent(&event, targetPtr, LeaveNotify, x, y, state,
  192.             NotifyNormal);
  193.  
  194.         TkInOutEvents(&event, lastWinPtr, winPtr, LeaveNotify,
  195.             EnterNotify, TCL_QUEUE_TAIL);
  196.         crossed = 1;
  197.         }
  198.     }
  199.     lastWinPtr = winPtr;
  200.     }
  201.  
  202.     return crossed;
  203. }
  204.  
  205. /*
  206.  *----------------------------------------------------------------------
  207.  *
  208.  * Tk_UpdatePointer --
  209.  *
  210.  *    This function updates the pointer state machine given an
  211.  *    the current window, position and modifier state.
  212.  *
  213.  * Results:
  214.  *    None.
  215.  *
  216.  * Side effects:
  217.  *    May queue new events and update the grab state.
  218.  *
  219.  *----------------------------------------------------------------------
  220.  */
  221.  
  222. void
  223. Tk_UpdatePointer(tkwin, x, y, state)
  224.     Tk_Window tkwin;        /* Window to which pointer event
  225.                  * is reported. May be NULL. */
  226.     int x, y;            /* Pointer location in root coords. */
  227.     int state;            /* Modifier state mask. */
  228. {
  229.     TkWindow *winPtr = (TkWindow *)tkwin;
  230.     TkWindow *targetWinPtr;
  231.     XPoint pos;
  232.     XEvent event;
  233.     int changes = (state ^ lastState) & ALL_BUTTONS;
  234.     int type, b, mask;
  235.  
  236.     pos.x = x;
  237.     pos.y = y;
  238.  
  239.     /*
  240.      * Use the current keyboard state, but the old mouse button
  241.      * state since we haven't generated the button events yet.
  242.      */
  243.  
  244.     lastState = (state & ~ALL_BUTTONS) | (lastState & ALL_BUTTONS);
  245.  
  246.     /*
  247.      * Generate Enter/Leave events.  If the pointer has crossed window
  248.      * boundaries, update the current mouse position so we don't generate
  249.      * redundant motion events.
  250.      */
  251.  
  252.     if (GenerateEnterLeave(winPtr, x, y, lastState)) {
  253.     lastPos = pos;
  254.     }
  255.  
  256.     /*
  257.      * Generate ButtonPress/ButtonRelease events based on the differences
  258.      * between the current button state and the last known button state.
  259.      */
  260.  
  261.     for (b = Button1; b <= Button3; b++) {
  262.     mask = ButtonMask(b);
  263.     if (changes & mask) {
  264.         if (state & mask) {    
  265.         type = ButtonPress;
  266.  
  267.             /*
  268.          * ButtonPress - Set restrict window if we aren't grabbed, or
  269.          * if this is the first button down.
  270.          */
  271.  
  272.         if (!restrictWinPtr) {
  273.             if (!grabWinPtr) {
  274.  
  275.             /*
  276.              * Mouse is not grabbed, so set a button grab.
  277.              */
  278.  
  279.             restrictWinPtr = winPtr;
  280.             TkpSetCapture(restrictWinPtr);
  281.  
  282.             } else if ((lastState & ALL_BUTTONS) == 0) {
  283.  
  284.             /*
  285.              * Mouse is in a non-button grab, so ensure
  286.              * the button grab is inside the grab tree.
  287.              */
  288.  
  289.             if (TkPositionInTree(winPtr, grabWinPtr)
  290.                 == TK_GRAB_IN_TREE) {
  291.                 restrictWinPtr = winPtr;
  292.             } else {
  293.                 restrictWinPtr = grabWinPtr;
  294.             }
  295.             TkpSetCapture(restrictWinPtr);
  296.             }
  297.         }
  298.  
  299.         } else {
  300.         type = ButtonRelease;
  301.  
  302.             /*
  303.          * ButtonRelease - Release the mouse capture and clear the
  304.          * restrict window when the last button is released and we
  305.          * aren't in a global grab.
  306.          */
  307.  
  308.         if ((lastState & ALL_BUTTONS) == mask) {
  309.             if (!grabWinPtr) {
  310.             TkpSetCapture(NULL);
  311.             }
  312.         }
  313.  
  314.         /*
  315.          * If we are releasing a restrict window, then we need
  316.          * to send the button event followed by mouse motion from
  317.          * the restrict window to the current mouse position.
  318.          */
  319.  
  320.         if (restrictWinPtr) {
  321.             InitializeEvent(&event, restrictWinPtr, type, x, y,
  322.                 lastState, b);
  323.             Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  324.             lastState &= ~mask;
  325.             lastWinPtr = restrictWinPtr;
  326.             restrictWinPtr = NULL;
  327.  
  328.             GenerateEnterLeave(winPtr, x, y, lastState);
  329.             lastPos = pos;
  330.             continue;
  331.         }        
  332.         }
  333.  
  334.         /*
  335.          * If a restrict window is set, make sure the pointer event
  336.          * is reported relative to that window.  Otherwise, if a
  337.          * global grab is in effect then events outside of windows
  338.          * managed by Tk should be reported to the grab window.
  339.          */
  340.  
  341.         if (restrictWinPtr) {
  342.         targetWinPtr = restrictWinPtr;
  343.         } else if (grabWinPtr && !winPtr) {
  344.         targetWinPtr = grabWinPtr;
  345.         } else {
  346.         targetWinPtr = winPtr;
  347.         }
  348.  
  349.         /*
  350.          * If we still have a target window, send the event.
  351.          */
  352.  
  353.         if (winPtr != NULL) {
  354.         InitializeEvent(&event, targetWinPtr, type, x, y,
  355.             lastState, b);
  356.         Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  357.         }
  358.  
  359.         /*
  360.          * Update the state for the next iteration.
  361.          */
  362.  
  363.         lastState = (type == ButtonPress)
  364.         ? (lastState | mask) : (lastState & ~mask);
  365.         lastPos = pos;
  366.     }
  367.     }
  368.  
  369.     /*
  370.      * Make sure the cursor window is up to date.
  371.      */
  372.  
  373.     if (restrictWinPtr) {
  374.     targetWinPtr = restrictWinPtr;
  375.     } else if (grabWinPtr) {
  376.     targetWinPtr = (TkPositionInTree(winPtr, grabWinPtr)
  377.         == TK_GRAB_IN_TREE) ? winPtr : grabWinPtr;
  378.     } else {
  379.     targetWinPtr = winPtr;
  380.     }
  381.     UpdateCursor(targetWinPtr);
  382.  
  383.     /*
  384.      * If no other events caused the position to be updated,
  385.      * generate a motion event.
  386.      */
  387.  
  388.     if (lastPos.x != pos.x || lastPos.y != pos.y) {
  389.     if (restrictWinPtr) {
  390.         targetWinPtr = restrictWinPtr;
  391.     } else if (grabWinPtr && !winPtr) {
  392.         targetWinPtr = grabWinPtr;
  393.     }
  394.  
  395.     if (winPtr != NULL) {
  396.         InitializeEvent(&event, targetWinPtr, MotionNotify, x, y,
  397.             lastState, NotifyNormal);
  398.         Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  399.     }
  400.     lastPos = pos;
  401.     }
  402. }
  403.  
  404. /*
  405.  *----------------------------------------------------------------------
  406.  *
  407.  * XGrabPointer --
  408.  *
  409.  *    Capture the mouse so event are reported outside of toplevels.
  410.  *    Note that this is a very limited implementation that only
  411.  *    supports GrabModeAsync and owner_events True.
  412.  *
  413.  * Results:
  414.  *    Always returns GrabSuccess.
  415.  *
  416.  * Side effects:
  417.  *    Turns on mouse capture, sets the global grab pointer, and
  418.  *    clears any window restrictions.
  419.  *
  420.  *----------------------------------------------------------------------
  421.  */
  422.  
  423. int
  424. XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode,
  425.     keyboard_mode, confine_to, cursor, time)
  426.     Display* display;
  427.     Window grab_window;
  428.     Bool owner_events;
  429.     unsigned int event_mask;
  430.     int pointer_mode;
  431.     int keyboard_mode;
  432.     Window confine_to;
  433.     Cursor cursor;
  434.     Time time;
  435. {
  436.     display->request++;
  437.     grabWinPtr = (TkWindow *) Tk_IdToWindow(display, grab_window);
  438.     restrictWinPtr = NULL;
  439.     TkpSetCapture(grabWinPtr);
  440.     if (TkPositionInTree(lastWinPtr, grabWinPtr) != TK_GRAB_IN_TREE) {
  441.     UpdateCursor(grabWinPtr);
  442.     }
  443.     return GrabSuccess;
  444. }
  445.  
  446. /*
  447.  *----------------------------------------------------------------------
  448.  *
  449.  * XUngrabPointer --
  450.  *
  451.  *    Release the current grab.
  452.  *
  453.  * Results:
  454.  *    None.
  455.  *
  456.  * Side effects:
  457.  *    Releases the mouse capture.
  458.  *
  459.  *----------------------------------------------------------------------
  460.  */
  461.  
  462. void
  463. XUngrabPointer(display, time)
  464.     Display* display;
  465.     Time time;
  466. {
  467.     display->request++;
  468.     grabWinPtr = NULL;
  469.     restrictWinPtr = NULL;
  470.     TkpSetCapture(NULL);
  471.     UpdateCursor(lastWinPtr);
  472. }
  473.  
  474. /*
  475.  *----------------------------------------------------------------------
  476.  *
  477.  * TkPointerDeadWindow --
  478.  *
  479.  *    Clean up pointer module state when a window is destroyed.
  480.  *
  481.  * Results:
  482.  *    None.
  483.  *
  484.  * Side effects:
  485.  *    May release the current capture window.
  486.  *
  487.  *----------------------------------------------------------------------
  488.  */
  489.  
  490. void
  491. TkPointerDeadWindow(winPtr)
  492.     TkWindow *winPtr;
  493. {
  494.     if (winPtr == lastWinPtr) {
  495.     lastWinPtr = NULL;
  496.     }
  497.     if (winPtr == grabWinPtr) {
  498.     grabWinPtr = NULL;
  499.     }
  500.     if (winPtr == restrictWinPtr) {
  501.     restrictWinPtr = NULL;
  502.     }
  503.     if (!(restrictWinPtr || grabWinPtr)) {
  504.     TkpSetCapture(NULL);
  505.     }
  506. }
  507.  
  508. /*
  509.  *----------------------------------------------------------------------
  510.  *
  511.  * UpdateCursor --
  512.  *
  513.  *    Set the windows global cursor to the cursor associated with
  514.  *    the given Tk window.
  515.  *
  516.  * Results:
  517.  *    None.
  518.  *
  519.  * Side effects:
  520.  *    Changes the mouse cursor.
  521.  *
  522.  *----------------------------------------------------------------------
  523.  */
  524.  
  525. static void
  526. UpdateCursor(winPtr)
  527.     TkWindow *winPtr;
  528. {
  529.     Cursor cursor = None;
  530.  
  531.     /*
  532.      * A window inherits its cursor from its parent if it doesn't
  533.      * have one of its own.  Top level windows inherit the default
  534.      * cursor.
  535.      */
  536.  
  537.     cursorWinPtr = winPtr;
  538.     while (winPtr != NULL) {
  539.     if (winPtr->atts.cursor != None) {
  540.         cursor = winPtr->atts.cursor;
  541.         break;
  542.     } else if (winPtr->flags & TK_TOP_LEVEL) {
  543.         break;
  544.     }
  545.     winPtr = winPtr->parentPtr;
  546.     }
  547.     TkpSetCursor((TkpCursor) cursor);
  548. }
  549.  
  550. /*
  551.  *----------------------------------------------------------------------
  552.  *
  553.  * XDefineCursor --
  554.  *
  555.  *    This function is called to update the cursor on a window.
  556.  *    Since the mouse might be in the specified window, we need to
  557.  *    check the specified window against the current mouse position
  558.  *    and grab state.
  559.  *
  560.  * Results:
  561.  *    None.
  562.  *
  563.  * Side effects:
  564.  *    May update the cursor.
  565.  *
  566.  *----------------------------------------------------------------------
  567.  */
  568.  
  569. void
  570. XDefineCursor(display, w, cursor)
  571.     Display* display;
  572.     Window w;
  573.     Cursor cursor;
  574. {
  575.     TkWindow *winPtr = (TkWindow *)Tk_IdToWindow(display, w);
  576.  
  577.     if (cursorWinPtr == winPtr) {
  578.     UpdateCursor(winPtr);
  579.     }
  580.     display->request++;
  581. }
  582.  
  583. /*
  584.  *----------------------------------------------------------------------
  585.  *
  586.  * TkGenerateActivateEvents --
  587.  *
  588.  *    This function is called by the Mac and Windows window manager
  589.  *    routines when a toplevel window is activated or deactivated.
  590.  *    Activate/Deactivate events will be sent to every subwindow of
  591.  *    the toplevel followed by a FocusIn/FocusOut message.
  592.  *
  593.  * Results:
  594.  *    None.
  595.  *
  596.  * Side effects:
  597.  *    Generates X events.
  598.  *
  599.  *----------------------------------------------------------------------
  600.  */
  601.  
  602. void
  603. TkGenerateActivateEvents(winPtr, active)
  604.     TkWindow *winPtr;        /* Toplevel to activate. */
  605.     int active;            /* Non-zero if the window is being
  606.                  * activated, else 0.*/
  607. {
  608.     XEvent event;
  609.     
  610.     /* 
  611.      * Generate Activate and Deactivate events.  This event
  612.      * is sent to every subwindow in a toplevel window.
  613.      */
  614.  
  615.     event.xany.serial = winPtr->display->request++;
  616.     event.xany.send_event = False;
  617.     event.xany.display = winPtr->display;
  618.     event.xany.window = winPtr->window;
  619.  
  620.     event.xany.type = active ? ActivateNotify : DeactivateNotify;
  621.     TkQueueEventForAllChildren(winPtr, &event);
  622.     
  623.     /* 
  624.      * Generate FocusIn and FocusOut events.  This event
  625.      * is only sent to the toplevel window.
  626.      */
  627.  
  628.     event.xany.window = winPtr->window;
  629.     event.xany.type = active ? FocusIn : FocusOut;
  630.     event.xfocus.mode = NotifyNormal;
  631.     event.xfocus.detail = NotifyNonlinear;
  632.  
  633.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  634. }
  635.