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 / unix / tkUnixEvent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  11.9 KB  |  500 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkUnixEvent.c --
  3.  *
  4.  *    This file implements an event source for X displays for the
  5.  *    UNIX version of Tk.
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkUnixEvent.c 1.16 97/06/06 11:23:15
  13.  */
  14.  
  15. #include "tkInt.h"
  16. #include "tkUnixInt.h"
  17. #include <signal.h>
  18.  
  19. /*
  20.  * The following static indicates whether this module has been initialized.
  21.  */
  22.  
  23. static int initialized = 0;
  24.  
  25. /*
  26.  * Prototypes for procedures that are referenced only in this file:
  27.  */
  28.  
  29. static void        DisplayCheckProc _ANSI_ARGS_((ClientData clientData,
  30.                 int flags));
  31. static void        DisplayExitHandler _ANSI_ARGS_((
  32.                 ClientData clientData));
  33. static void        DisplayFileProc _ANSI_ARGS_((ClientData clientData,
  34.                 int flags));
  35. static void        DisplaySetupProc _ANSI_ARGS_((ClientData clientData,
  36.                 int flags));
  37.  
  38. /*
  39.  *----------------------------------------------------------------------
  40.  *
  41.  * TkCreateXEventSource --
  42.  *
  43.  *    This procedure is called during Tk initialization to create
  44.  *    the event source for X Window events.
  45.  *
  46.  * Results:
  47.  *    None.
  48.  *
  49.  * Side effects:
  50.  *    A new event source is created.
  51.  *
  52.  *----------------------------------------------------------------------
  53.  */
  54.  
  55. void
  56. TkCreateXEventSource()
  57. {
  58.     if (!initialized) {
  59.     initialized = 1;
  60.     Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
  61.     Tcl_CreateExitHandler(DisplayExitHandler, NULL);
  62.     }
  63. }
  64.  
  65. /*
  66.  *----------------------------------------------------------------------
  67.  *
  68.  * DisplayExitHandler --
  69.  *
  70.  *    This function is called during finalization to clean up the
  71.  *    display module.
  72.  *
  73.  * Results:
  74.  *    None.
  75.  *
  76.  * Side effects:
  77.  *    None.
  78.  *
  79.  *----------------------------------------------------------------------
  80.  */
  81.  
  82. static void
  83. DisplayExitHandler(clientData)
  84.     ClientData clientData;    /* Not used. */
  85. {
  86.     Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
  87.     initialized = 0;
  88. }
  89.  
  90. /*
  91.  *----------------------------------------------------------------------
  92.  *
  93.  * TkpOpenDisplay --
  94.  *
  95.  *    Allocates a new TkDisplay, opens the X display, and establishes
  96.  *    the file handler for the connection.
  97.  *
  98.  * Results:
  99.  *    A pointer to a Tk display structure.
  100.  *
  101.  * Side effects:
  102.  *    Opens a display.
  103.  *
  104.  *----------------------------------------------------------------------
  105.  */
  106.  
  107. TkDisplay *
  108. TkpOpenDisplay(display_name)
  109.     char *display_name;
  110. {
  111.     TkDisplay *dispPtr;
  112.     Display *display = XOpenDisplay(display_name);
  113.  
  114.     display = XOpenDisplay(display_name);
  115.  
  116.     if (display == NULL) {
  117.     return NULL;
  118.     }
  119.     dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay));
  120.     dispPtr->display = display;
  121.     Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE,
  122.         DisplayFileProc, (ClientData) dispPtr);
  123.     return dispPtr;
  124. }
  125.  
  126. /*
  127.  *----------------------------------------------------------------------
  128.  *
  129.  * TkpCloseDisplay --
  130.  *
  131.  *    Cancels notifier callbacks and closes a display.  
  132.  *
  133.  * Results:
  134.  *    None.
  135.  *
  136.  * Side effects:
  137.  *    Deallocates the displayPtr.
  138.  *
  139.  *----------------------------------------------------------------------
  140.  */
  141.  
  142. void
  143. TkpCloseDisplay(displayPtr)
  144.     TkDisplay *displayPtr;
  145. {
  146.     TkDisplay *dispPtr = (TkDisplay *) displayPtr;
  147.  
  148.     if (dispPtr->display != 0) {
  149.         Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
  150.     
  151.         (void) XCloseDisplay(dispPtr->display);
  152.     }
  153.     
  154.     ckfree((char *) dispPtr);
  155. }
  156.  
  157. /*
  158.  *----------------------------------------------------------------------
  159.  *
  160.  * DisplaySetupProc --
  161.  *
  162.  *    This procedure implements the setup part of the UNIX X display
  163.  *    event source.  It is invoked by Tcl_DoOneEvent before entering
  164.  *    the notifier to check for events on all displays.
  165.  *
  166.  * Results:
  167.  *    None.
  168.  *
  169.  * Side effects:
  170.  *    If data is queued on a display inside Xlib, then the maximum
  171.  *    block time will be set to 0 to ensure that the notifier returns
  172.  *    control to Tcl even if there is no more data on the X connection.
  173.  *
  174.  *----------------------------------------------------------------------
  175.  */
  176.  
  177. static void
  178. DisplaySetupProc(clientData, flags)
  179.     ClientData clientData;    /* Not used. */
  180.     int flags;
  181. {
  182.     TkDisplay *dispPtr;
  183.     static Tcl_Time blockTime = { 0, 0 };
  184.  
  185.     if (!(flags & TCL_WINDOW_EVENTS)) {
  186.     return;
  187.     }
  188.  
  189.     for (dispPtr = tkDisplayList; dispPtr != NULL;
  190.      dispPtr = dispPtr->nextPtr) {
  191.  
  192.     /*
  193.      * Flush the display. If data is pending on the X queue, set
  194.      * the block time to zero.  This ensures that we won't block
  195.      * in the notifier if there is data in the X queue, but not on
  196.      * the server socket.
  197.      */
  198.  
  199.     XFlush(dispPtr->display);
  200.     if (XQLength(dispPtr->display) > 0) {
  201.         Tcl_SetMaxBlockTime(&blockTime);
  202.     }
  203.     }
  204. }
  205.  
  206. /*
  207.  *----------------------------------------------------------------------
  208.  *
  209.  * DisplayCheckProc --
  210.  *
  211.  *    This procedure checks for events sitting in the X event
  212.  *    queue.
  213.  *
  214.  * Results:
  215.  *    None.
  216.  *
  217.  * Side effects:
  218.  *    Moves queued events onto the Tcl event queue.
  219.  *
  220.  *----------------------------------------------------------------------
  221.  */
  222.  
  223. static void
  224. DisplayCheckProc(clientData, flags)
  225.     ClientData clientData;    /* Not used. */
  226.     int flags;
  227. {
  228.     TkDisplay *dispPtr;
  229.     XEvent event;
  230.     int numFound;
  231.  
  232.     if (!(flags & TCL_WINDOW_EVENTS)) {
  233.     return;
  234.     }
  235.  
  236.     for (dispPtr = tkDisplayList; dispPtr != NULL;
  237.      dispPtr = dispPtr->nextPtr) {
  238.     XFlush(dispPtr->display);
  239.     numFound = XQLength(dispPtr->display);
  240.  
  241.     /*
  242.      * Transfer events from the X event queue to the Tk event queue.
  243.      */
  244.  
  245.     while (numFound > 0) {
  246.         XNextEvent(dispPtr->display, &event);
  247.         Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  248.         numFound--;
  249.     }
  250.     }
  251. }
  252.  
  253. /*
  254.  *----------------------------------------------------------------------
  255.  *
  256.  * DisplayFileProc --
  257.  *
  258.  *    This procedure implements the file handler for the X connection.
  259.  *
  260.  * Results:
  261.  *    None.
  262.  *
  263.  * Side effects:
  264.  *    Makes entries on the Tcl event queue for all the events available
  265.  *    from all the displays.
  266.  *
  267.  *----------------------------------------------------------------------
  268.  */
  269.  
  270. static void
  271. DisplayFileProc(clientData, flags)
  272.     ClientData clientData;        /* The display pointer. */
  273.     int flags;                /* Should be TCL_READABLE. */
  274. {
  275.     TkDisplay *dispPtr = (TkDisplay *) clientData;
  276.     Display *display = dispPtr->display;
  277.     XEvent event;
  278.     int numFound;
  279.  
  280.     XFlush(display);
  281.     numFound = XEventsQueued(display, QueuedAfterReading);
  282.     if (numFound == 0) {
  283.     
  284.     /*
  285.      * Things are very tricky if there aren't any events readable
  286.      * at this point (after all, there was supposedly data
  287.      * available on the connection).  A couple of things could
  288.      * have occurred:
  289.      * 
  290.      * One possibility is that there were only error events in the
  291.      * input from the server.  If this happens, we should return
  292.      * (we don't want to go to sleep in XNextEvent below, since
  293.      * this would block out other sources of input to the
  294.      * process).
  295.      *
  296.      * Another possibility is that our connection to the server
  297.      * has been closed.  This will not necessarily be detected in
  298.      * XEventsQueued (!!), so if we just return then there will be
  299.      * an infinite loop.  To detect such an error, generate a NoOp
  300.      * protocol request to exercise the connection to the server,
  301.      * then return.  However, must disable SIGPIPE while sending
  302.      * the request, or else the process will die from the signal
  303.      * and won't invoke the X error function to print a nice (?!)
  304.      * message.
  305.      */
  306.     
  307.     void (*oldHandler)();
  308.     
  309.     oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
  310.     XNoOp(display);
  311.     XFlush(display);
  312.     (void) signal(SIGPIPE, oldHandler);
  313.     }
  314.     
  315.     /*
  316.      * Transfer events from the X event queue to the Tk event queue.
  317.      */
  318.  
  319.     while (numFound > 0) {
  320.     XNextEvent(display, &event);
  321.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  322.     numFound--;
  323.     }
  324. }
  325.  
  326. /*
  327.  *----------------------------------------------------------------------
  328.  *
  329.  * TkUnixDoOneXEvent --
  330.  *
  331.  *    This routine waits for an X event to be processed or for
  332.  *    a timeout to occur.  The timeout is specified as an absolute
  333.  *    time.  This routine is called when Tk needs to wait for a
  334.  *    particular X event without letting arbitrary events be
  335.  *    processed.  The caller will typically call Tk_RestrictEvents
  336.  *    to set up an event filter before calling this routine.  This
  337.  *    routine will service at most one event per invocation.
  338.  *
  339.  * Results:
  340.  *    Returns 0 if the timeout has expired, otherwise returns 1.
  341.  *
  342.  * Side effects:
  343.  *    Can invoke arbitrary Tcl scripts.
  344.  *
  345.  *----------------------------------------------------------------------
  346.  */
  347.  
  348. int
  349. TkUnixDoOneXEvent(timePtr)
  350.     Tcl_Time *timePtr;        /* Specifies the absolute time when the
  351.                  * call should time out. */
  352. {
  353.     TkDisplay *dispPtr;
  354.     static fd_mask readMask[MASK_SIZE];
  355.     struct timeval blockTime, *timeoutPtr;
  356.     Tcl_Time now;
  357.     int fd, index, bit, numFound, numFdBits = 0;
  358.  
  359.     /*
  360.      * Look for queued events first. 
  361.      */
  362.  
  363.     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
  364.     return 1;
  365.     }
  366.  
  367.     /*
  368.      * Compute the next block time and check to see if we have timed out.
  369.      * Note that HP-UX defines tv_sec to be unsigned so we have to be
  370.      * careful in our arithmetic.
  371.      */
  372.  
  373.     if (timePtr) {
  374.     TclpGetTime(&now);
  375.     blockTime.tv_sec = timePtr->sec;
  376.     blockTime.tv_usec = timePtr->usec - now.usec;
  377.     if (blockTime.tv_usec < 0) {
  378.         now.sec += 1;
  379.         blockTime.tv_usec += 1000000;
  380.     }
  381.     if (blockTime.tv_sec < now.sec) {
  382.         blockTime.tv_sec = 0;
  383.         blockTime.tv_usec = 0;
  384.     } else {
  385.         blockTime.tv_sec -= now.sec;
  386.     }
  387.     timeoutPtr = &blockTime;
  388.     } else {
  389.     timeoutPtr = NULL;
  390.     }
  391.  
  392.     /*
  393.      * Set up the select mask for all of the displays.  If a display has
  394.      * data pending, then we want to poll instead of blocking.
  395.      */
  396.  
  397.     memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
  398.     for (dispPtr = tkDisplayList; dispPtr != NULL;
  399.      dispPtr = dispPtr->nextPtr) {
  400.     XFlush(dispPtr->display);
  401.     if (XQLength(dispPtr->display) > 0) {
  402.         blockTime.tv_sec = 0;
  403.         blockTime.tv_usec = 0;
  404.     }
  405.     fd = ConnectionNumber(dispPtr->display);
  406.     index = fd/(NBBY*sizeof(fd_mask));
  407.     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
  408.     readMask[index] |= bit;
  409.     if (numFdBits <= fd) {
  410.         numFdBits = fd+1;
  411.     }
  412.     }
  413.  
  414.     numFound = select(numFdBits, (SELECT_MASK *) &readMask[0], NULL, NULL,
  415.         timeoutPtr);
  416.     if (numFound <= 0) {
  417.     /*
  418.      * Some systems don't clear the masks after an error, so
  419.      * we have to do it here.
  420.      */
  421.  
  422.     memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
  423.     }
  424.  
  425.     /*
  426.      * Process any new events on the display connections.
  427.      */
  428.  
  429.     for (dispPtr = tkDisplayList; dispPtr != NULL;
  430.      dispPtr = dispPtr->nextPtr) {
  431.     fd = ConnectionNumber(dispPtr->display);
  432.     index = fd/(NBBY*sizeof(fd_mask));
  433.     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
  434.     if ((readMask[index] & bit) || (XQLength(dispPtr->display) > 0)) {
  435.         DisplayFileProc((ClientData)dispPtr, TCL_READABLE);
  436.     }
  437.     }
  438.     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
  439.     return 1;
  440.     }
  441.  
  442.     /*
  443.      * Check to see if we timed out.
  444.      */
  445.  
  446.     if (timePtr) {
  447.     TclpGetTime(&now);
  448.     if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec)
  449.         && (now.usec > timePtr->usec))) {
  450.         return 0;
  451.     }
  452.     }
  453.  
  454.     /*
  455.      * We had an event but we did not generate a Tcl event from it. Behave
  456.      * as though we dealt with it. (JYL&SS)
  457.      */
  458.  
  459.     return 1;
  460. }
  461.  
  462. /*
  463.  *----------------------------------------------------------------------
  464.  *
  465.  * TkpSync --
  466.  *
  467.  *    This routine ensures that all pending X requests have been
  468.  *    seen by the server, and that any pending X events have been
  469.  *    moved onto the Tk event queue.
  470.  *
  471.  * Results:
  472.  *    None.
  473.  *
  474.  * Side effects:
  475.  *    Places new events on the Tk event queue.
  476.  *
  477.  *----------------------------------------------------------------------
  478.  */
  479.  
  480. void
  481. TkpSync(display)
  482.     Display *display;        /* Display to sync. */
  483. {
  484.     int numFound = 0;
  485.     XEvent event;
  486.  
  487.     XSync(display, False);
  488.  
  489.     /*
  490.      * Transfer events from the X event queue to the Tk event queue.
  491.      */
  492.  
  493.     numFound = XQLength(display);
  494.     while (numFound > 0) {
  495.     XNextEvent(display, &event);
  496.     Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
  497.     numFound--;
  498.     }
  499. }
  500.