home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tcl / os2 / tclNotify.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  17KB  |  579 lines

  1. /* 
  2.  * tclNotify.c --
  3.  *
  4.  *    This file provides the parts of the Tcl event notifier that are
  5.  *    the same on all platforms, plus a few other parts that are used
  6.  *    on more than one platform but not all.
  7.  *
  8.  *    The notifier is the lowest-level part of the event system.  It
  9.  *    manages an event queue that holds Tcl_Event structures and a list
  10.  *    of event sources that can add events to the queue.  It also
  11.  *    contains the procedure Tcl_DoOneEvent that invokes the event
  12.  *    sources and blocks to wait for new events, but Tcl_DoOneEvent
  13.  *    is in the platform-specific part of the notifier (in files like
  14.  *    tclUnixNotify.c).
  15.  *
  16.  * Copyright (c) 1995 Sun Microsystems, Inc.
  17.  *
  18.  * See the file "license.terms" for information on usage and redistribution
  19.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  20.  *
  21.  * SCCS: @(#) tclNotify.c 1.6 96/02/29 09:20:10
  22.  */
  23.  
  24. #include "tclInt.h"
  25. #include "tclPort.h"
  26.  
  27. /*
  28.  * The following variable records the address of the first event
  29.  * source in the list of all event sources for the application.
  30.  * This variable is accessed by the notifier to traverse the list
  31.  * and invoke each event source.
  32.  */
  33.  
  34. TclEventSource *tclFirstEventSourcePtr = NULL;
  35.  
  36. /*
  37.  * The following variables indicate how long to block in the event
  38.  * notifier the next time it blocks (default:  block forever).
  39.  */
  40.  
  41. static int blockTimeSet = 0;    /* 0 means there is no maximum block
  42.                  * time:  block forever. */
  43. static Tcl_Time blockTime;    /* If blockTimeSet is 1, gives the
  44.                  * maximum elapsed time for the next block. */
  45.  
  46. /*
  47.  * The following variables keep track of the event queue.  In addition
  48.  * to the first (next to be serviced) and last events in the queue,
  49.  * we keep track of a "marker" event.  This provides a simple priority
  50.  * mechanism whereby events can be inserted at the front of the queue
  51.  * but behind all other high-priority events already in the queue (this
  52.  * is used for things like a sequence of Enter and Leave events generated
  53.  * during a grab in Tk).
  54.  */
  55.  
  56. static Tcl_Event *firstEventPtr = NULL;
  57.                 /* First pending event, or NULL if none. */
  58. static Tcl_Event *lastEventPtr = NULL;
  59.                 /* Last pending event, or NULL if none. */
  60. static Tcl_Event *markerEventPtr = NULL;
  61.                 /* Last high-priority event in queue, or
  62.                  * NULL if none. */
  63.  
  64. /*
  65.  * Prototypes for procedures used only in this file:
  66.  */
  67.  
  68. static int        ServiceEvent _ANSI_ARGS_((int flags));
  69.  
  70. /*
  71.  *----------------------------------------------------------------------
  72.  *
  73.  * Tcl_CreateEventSource --
  74.  *
  75.  *    This procedure is invoked to create a new source of events.
  76.  *    The source is identified by a procedure that gets invoked
  77.  *    during Tcl_DoOneEvent to check for events on that source
  78.  *    and queue them.
  79.  *
  80.  *
  81.  * Results:
  82.  *    None.
  83.  *
  84.  * Side effects:
  85.  *    SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
  86.  *    runs out of things to do.  SetupProc will be invoked before
  87.  *    Tcl_DoOneEvent calls select or whatever else it uses to wait
  88.  *    for events.  SetupProc typically calls functions like Tcl_WatchFile
  89.  *    or Tcl_SetMaxBlockTime to indicate what to wait for.
  90.  *
  91.  *    CheckProc is called after select or whatever operation was actually
  92.  *    used to wait.  It figures out whether anything interesting actually
  93.  *    happened (e.g. by calling Tcl_FileReady), and then calls
  94.  *    Tcl_QueueEvent to queue any events that are ready.
  95.  *
  96.  *    Each of these procedures is passed two arguments, e.g.
  97.  *        (*checkProc)(ClientData clientData, int flags));
  98.  *    ClientData is the same as the clientData argument here, and flags
  99.  *    is a combination of things like TCL_FILE_EVENTS that indicates
  100.  *    what events are of interest:  setupProc and checkProc use flags
  101.  *    to figure out whether their events are relevant or not.
  102.  *
  103.  *----------------------------------------------------------------------
  104.  */
  105.  
  106. void
  107. Tcl_CreateEventSource(setupProc, checkProc, clientData)
  108.     Tcl_EventSetupProc *setupProc;    /* Procedure to invoke to figure out
  109.                      * what to wait for. */
  110.     Tcl_EventCheckProc *checkProc;    /* Procedure to call after waiting
  111.                      * to see what happened. */
  112.     ClientData clientData;        /* One-word argument to pass to
  113.                      * setupProc and checkProc. */
  114. {
  115.     TclEventSource *sourcePtr;
  116.  
  117.     sourcePtr = (TclEventSource *) ckalloc(sizeof(TclEventSource));
  118.     sourcePtr->setupProc = setupProc;
  119.     sourcePtr->checkProc = checkProc;
  120.     sourcePtr->clientData = clientData;
  121.     sourcePtr->nextPtr = tclFirstEventSourcePtr;
  122.     tclFirstEventSourcePtr = sourcePtr;
  123. }
  124.  
  125. /*
  126.  *----------------------------------------------------------------------
  127.  *
  128.  * Tcl_DeleteEventSource --
  129.  *
  130.  *    This procedure is invoked to delete the source of events
  131.  *    given by proc and clientData.
  132.  *
  133.  * Results:
  134.  *    None.
  135.  *
  136.  * Side effects:
  137.  *    The given event source is cancelled, so its procedure will
  138.  *    never again be called.  If no such source exists, nothing
  139.  *    happens.
  140.  *
  141.  *----------------------------------------------------------------------
  142.  */
  143.  
  144. void
  145. Tcl_DeleteEventSource(setupProc, checkProc, clientData)
  146.     Tcl_EventSetupProc *setupProc;    /* Procedure to invoke to figure out
  147.                      * what to wait for. */
  148.     Tcl_EventCheckProc *checkProc;    /* Procedure to call after waiting
  149.                      * to see what happened. */
  150.     ClientData clientData;        /* One-word argument to pass to
  151.                      * setupProc and checkProc. */
  152. {
  153.     TclEventSource *sourcePtr, *prevPtr;
  154.  
  155.     for (sourcePtr = tclFirstEventSourcePtr, prevPtr = NULL;
  156.         sourcePtr != NULL;
  157.         prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
  158.     if ((sourcePtr->setupProc != setupProc)
  159.         || (sourcePtr->checkProc != checkProc)
  160.         || (sourcePtr->clientData != clientData)) {
  161.         continue;
  162.     }
  163.     if (prevPtr == NULL) {
  164.         tclFirstEventSourcePtr = sourcePtr->nextPtr;
  165.     } else {
  166.         prevPtr->nextPtr = sourcePtr->nextPtr;
  167.     }
  168.     ckfree((char *) sourcePtr);
  169.     return;
  170.     }
  171. }
  172.  
  173. /*
  174.  *----------------------------------------------------------------------
  175.  *
  176.  * Tcl_QueueEvent --
  177.  *
  178.  *    Insert an event into the Tk event queue at one of three
  179.  *    positions: the head, the tail, or before a floating marker.
  180.  *    Events inserted before the marker will be processed in
  181.  *    first-in-first-out order, but before any events inserted at
  182.  *    the tail of the queue.  Events inserted at the head of the
  183.  *    queue will be processed in last-in-first-out order.
  184.  *
  185.  * Results:
  186.  *    None.
  187.  *
  188.  * Side effects:
  189.  *    None.
  190.  *
  191.  *----------------------------------------------------------------------
  192.  */
  193.  
  194. void
  195. Tcl_QueueEvent(evPtr, position)
  196.     Tcl_Event* evPtr;        /* Event to add to queue.  The storage
  197.                  * space must have been allocated the caller
  198.                  * with malloc (ckalloc), and it becomes
  199.                  * the property of the event queue.  It
  200.                  * will be freed after the event has been
  201.                  * handled. */
  202.     Tcl_QueuePosition position;    /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
  203.                  * TCL_QUEUE_MARK. */
  204. {
  205.     if (position == TCL_QUEUE_TAIL) {
  206.     /*
  207.      * Append the event on the end of the queue.
  208.      */
  209.  
  210.     evPtr->nextPtr = NULL;
  211.     if (firstEventPtr == NULL) {
  212.         firstEventPtr = evPtr;
  213.     } else {
  214.         lastEventPtr->nextPtr = evPtr;
  215.     }
  216.     lastEventPtr = evPtr;
  217.     } else if (position == TCL_QUEUE_HEAD) {
  218.     /*
  219.      * Push the event on the head of the queue.
  220.      */
  221.  
  222.     evPtr->nextPtr = firstEventPtr;
  223.     if (firstEventPtr == NULL) {
  224.         lastEventPtr = evPtr;
  225.     }        
  226.     firstEventPtr = evPtr;
  227.     } else if (position == TCL_QUEUE_MARK) {
  228.     /*
  229.      * Insert the event after the current marker event and advance
  230.      * the marker to the new event.
  231.      */
  232.  
  233.     if (markerEventPtr == NULL) {
  234.         evPtr->nextPtr = firstEventPtr;
  235.         firstEventPtr = evPtr;
  236.     } else {
  237.         evPtr->nextPtr = markerEventPtr->nextPtr;
  238.         markerEventPtr->nextPtr = evPtr;
  239.     }
  240.     markerEventPtr = evPtr;
  241.     if (evPtr->nextPtr == NULL) {
  242.         lastEventPtr = evPtr;
  243.     }
  244.     }
  245. }
  246.  
  247. /*
  248.  *----------------------------------------------------------------------
  249.  *
  250.  * Tcl_DeleteEvents --
  251.  *
  252.  *    Calls a procedure for each event in the queue and deletes those
  253.  *    for which the procedure returns 1. Events for which the
  254.  *    procedure returns 0 are left in the queue.
  255.  *
  256.  * Results:
  257.  *    None.
  258.  *
  259.  * Side effects:
  260.  *    Potentially removes one or more events from the event queue.
  261.  *
  262.  *----------------------------------------------------------------------
  263.  */
  264.  
  265. void
  266. Tcl_DeleteEvents(proc, clientData)
  267.     Tcl_EventDeleteProc *proc;        /* The procedure to call. */
  268.     ClientData clientData;            /* type-specific data. */
  269. {
  270.     Tcl_Event *evPtr, *prevPtr, *hold;
  271.  
  272.     for (prevPtr = (Tcl_Event *) NULL, evPtr = firstEventPtr;
  273.              evPtr != (Tcl_Event *) NULL;
  274.              ) {
  275.         if ((*proc) (evPtr, clientData) == 1) {
  276.             if (firstEventPtr == evPtr) {
  277.                 firstEventPtr = evPtr->nextPtr;
  278.                 if (evPtr->nextPtr == (Tcl_Event *) NULL) {
  279.                     lastEventPtr = (Tcl_Event *) NULL;
  280.                 }
  281.             } else {
  282.                 prevPtr->nextPtr = evPtr->nextPtr;
  283.             }
  284.             hold = evPtr;
  285.             evPtr = evPtr->nextPtr;
  286.             ckfree((char *) hold);
  287.         } else {
  288.             prevPtr = evPtr;
  289.             evPtr = evPtr->nextPtr;
  290.         }
  291.     }
  292. }
  293.  
  294. /*
  295.  *----------------------------------------------------------------------
  296.  *
  297.  * ServiceEvent --
  298.  *
  299.  *    Process one event from the event queue.  This routine is called
  300.  *    by the notifier whenever it wants Tk to process an event.  
  301.  *
  302.  * Results:
  303.  *    The return value is 1 if the procedure actually found an event
  304.  *    to process.  If no processing occurred, then 0 is returned.
  305.  *
  306.  * Side effects:
  307.  *    Invokes all of the event handlers for the highest priority
  308.  *    event in the event queue.  May collapse some events into a
  309.  *    single event or discard stale events.
  310.  *
  311.  *----------------------------------------------------------------------
  312.  */
  313.  
  314. static int
  315. ServiceEvent(flags)
  316.     int flags;            /* Indicates what events should be processed.
  317.                  * May be any combination of TCL_WINDOW_EVENTS
  318.                  * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
  319.                  * flags defined elsewhere.  Events not
  320.                  * matching this will be skipped for processing
  321.                  * later. */
  322. {
  323.     Tcl_Event *evPtr, *prevPtr;
  324.     Tcl_EventProc *proc;
  325.  
  326.     /*
  327.      * No event flags is equivalent to TCL_ALL_EVENTS.
  328.      */
  329.     
  330.     if ((flags & TCL_ALL_EVENTS) == 0) {
  331.     flags |= TCL_ALL_EVENTS;
  332.     }
  333.  
  334.     /*
  335.      * Loop through all the events in the queue until we find one
  336.      * that can actually be handled.
  337.      */
  338.  
  339.     for (evPtr = firstEventPtr; evPtr != NULL; evPtr = evPtr->nextPtr) {
  340.     /*
  341.      * Call the handler for the event.  If it actually handles the
  342.      * event then free the storage for the event.  There are two
  343.      * tricky things here, but stemming from the fact that the event
  344.      * code may be re-entered while servicing the event:
  345.      *
  346.      * 1. Set the "proc" field to NULL.  This is a signal to ourselves
  347.      *    that we shouldn't reexecute the handler if the event loop
  348.      *    is re-entered.
  349.      * 2. When freeing the event, must search the queue again from the
  350.      *    front to find it.  This is because the event queue could
  351.      *    change almost arbitrarily while handling the event, so we
  352.      *    can't depend on pointers found now still being valid when
  353.      *    the handler returns.
  354.      */
  355.  
  356.     proc = evPtr->proc;
  357.     evPtr->proc = NULL;
  358.     if ((proc != NULL) && (*proc)(evPtr, flags)) {
  359.         if (firstEventPtr == evPtr) {
  360.         firstEventPtr = evPtr->nextPtr;
  361.         if (evPtr->nextPtr == NULL) {
  362.             lastEventPtr = NULL;
  363.         }
  364.         } else {
  365.         for (prevPtr = firstEventPtr; prevPtr->nextPtr != evPtr;
  366.             prevPtr = prevPtr->nextPtr) {
  367.             /* Empty loop body. */
  368.         }
  369.         prevPtr->nextPtr = evPtr->nextPtr;
  370.         if (evPtr->nextPtr == NULL) {
  371.             lastEventPtr = prevPtr;
  372.         }
  373.         }
  374.         if (markerEventPtr == evPtr) {
  375.         markerEventPtr = NULL;
  376.         }
  377.         ckfree((char *) evPtr);
  378.         return 1;
  379.     } else {
  380.         /*
  381.          * The event wasn't actually handled, so we have to restore
  382.          * the proc field to allow the event to be attempted again.
  383.          */
  384.  
  385.         evPtr->proc = proc;
  386.     }
  387.  
  388.     /*
  389.      * The handler for this event asked to defer it.  Just go on to
  390.      * the next event.
  391.      */
  392.  
  393.     continue;
  394.     }
  395.     return 0;
  396. }
  397.  
  398. /*
  399.  *----------------------------------------------------------------------
  400.  *
  401.  * Tcl_SetMaxBlockTime --
  402.  *
  403.  *    This procedure is invoked by event sources to tell the notifier
  404.  *    how long it may block the next time it blocks.  The timePtr
  405.  *    argument gives a maximum time;  the actual time may be less if
  406.  *    some other event source requested a smaller time.
  407.  *
  408.  * Results:
  409.  *    None.
  410.  *
  411.  * Side effects:
  412.  *    May reduce the length of the next sleep in the notifier.
  413.  *
  414.  *----------------------------------------------------------------------
  415.  */
  416.  
  417. void
  418. Tcl_SetMaxBlockTime(timePtr)
  419.     Tcl_Time *timePtr;        /* Specifies a maximum elapsed time for
  420.                  * the next blocking operation in the
  421.                  * event notifier. */
  422. {
  423.     if (!blockTimeSet || (timePtr->sec < blockTime.sec)
  424.         || ((timePtr->sec == blockTime.sec)
  425.         && (timePtr->usec < blockTime.usec))) {
  426.     blockTime = *timePtr;
  427.     blockTimeSet = 1;
  428.     }
  429. }
  430.  
  431. /*
  432.  *----------------------------------------------------------------------
  433.  *
  434.  * Tcl_DoOneEvent --
  435.  *
  436.  *    Process a single event of some sort.  If there's no work to
  437.  *    do, wait for an event to occur, then process it.
  438.  *
  439.  * Results:
  440.  *    The return value is 1 if the procedure actually found an event
  441.  *    to process.  If no processing occurred, then 0 is returned (this
  442.  *    can happen if the TCL_DONT_WAIT flag is set or if there are no
  443.  *    event handlers to wait for in the set specified by flags).
  444.  *
  445.  * Side effects:
  446.  *    May delay execution of process while waiting for an event,
  447.  *    unless TCL_DONT_WAIT is set in the flags argument.  Event
  448.  *    sources are invoked to check for and queue events.  Event
  449.  *    handlers may produce arbitrary side effects.
  450.  *
  451.  *----------------------------------------------------------------------
  452.  */
  453.  
  454. int
  455. Tcl_DoOneEvent(flags)
  456.     int flags;            /* Miscellaneous flag values:  may be any
  457.                  * combination of TCL_DONT_WAIT,
  458.                  * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
  459.                  * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
  460.                  * others defined by event sources. */
  461. {
  462.     TclEventSource *sourcePtr;
  463.     Tcl_Time *timePtr;
  464.  
  465.     /*
  466.      * No event flags is equivalent to TCL_ALL_EVENTS.
  467.      */
  468.     
  469.     if ((flags & TCL_ALL_EVENTS) == 0) {
  470.     flags |= TCL_ALL_EVENTS;
  471.     }
  472.  
  473.     /*
  474.      * The core of this procedure is an infinite loop, even though
  475.      * we only service one event.  The reason for this is that we
  476.      * might think we have an event ready (e.g. the connection to
  477.      * the server becomes readable), but then we might discover that
  478.      * there's nothing interesting on that connection, so no event
  479.      * was serviced.  Or, the select operation could return prematurely
  480.      * due to a signal.  The easiest thing in both these cases is
  481.      * just to loop back and try again.
  482.      */
  483.  
  484.     while (1) {
  485.  
  486.     /*
  487.      * The first thing we do is to service any asynchronous event
  488.      * handlers.
  489.      */
  490.     
  491.     if (Tcl_AsyncReady()) {
  492.         (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
  493.         return 1;
  494.     }
  495.  
  496.     /*
  497.      * If idle events are the only things to service, skip the
  498.      * main part of the loop and go directly to handle idle
  499.      * events (i.e. don't wait even if TCL_DONT_WAIT isn't set.
  500.      */
  501.  
  502.     if (flags == TCL_IDLE_EVENTS) {
  503.         flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
  504.         goto idleEvents;
  505.     }
  506.  
  507.     /*
  508.      * Ask Tk to service a queued event, if there are any.
  509.      */
  510.  
  511.     if (ServiceEvent(flags)) {
  512.         return 1;
  513.     }
  514.  
  515.     /*
  516.      * There are no events already queued.  Invoke all of the
  517.      * event sources to give them a chance to setup for the wait.
  518.      */
  519.  
  520.     blockTimeSet = 0;
  521.     for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
  522.         sourcePtr = sourcePtr->nextPtr) {
  523.         (*sourcePtr->setupProc)(sourcePtr->clientData, flags);
  524.     }
  525.     if ((flags & TCL_DONT_WAIT) ||
  526.         ((flags & TCL_IDLE_EVENTS) && TclIdlePending())) {
  527.         /*
  528.          * Don't block:  there are idle events waiting, or we don't
  529.          * care about idle events anyway, or the caller asked us not
  530.          * to block.
  531.          */
  532.  
  533.         blockTime.sec = 0;
  534.         blockTime.usec = 0;
  535.         timePtr = &blockTime;
  536.     } else if (blockTimeSet) {
  537.         timePtr = &blockTime;
  538.     } else {
  539.         timePtr = NULL;
  540.     }
  541.  
  542.     /*
  543.      * Wait until an event occurs or the timer expires.
  544.      */
  545.  
  546.     if (Tcl_WaitForEvent(timePtr) == TCL_ERROR) {
  547.         return 0;
  548.     }
  549.  
  550.     /*
  551.      * Give each of the event sources a chance to queue events,
  552.      * then call ServiceEvent and give it another chance to
  553.      * service events.
  554.      */
  555.  
  556.     for (sourcePtr = tclFirstEventSourcePtr; sourcePtr != NULL;
  557.         sourcePtr = sourcePtr->nextPtr) {
  558.         (*sourcePtr->checkProc)(sourcePtr->clientData, flags);
  559.     }
  560.     if (ServiceEvent(flags)) {
  561.         return 1;
  562.     }
  563.  
  564.     /*
  565.      * We've tried everything at this point, but nobody had anything
  566.      * to do.  Check for idle events.  If none, either quit or go back
  567.      * to the top and try again.
  568.      */
  569.  
  570.     idleEvents:
  571.     if ((flags & TCL_IDLE_EVENTS) && TclServiceIdle()) {
  572.         return 1;
  573.     }
  574.     if (flags & TCL_DONT_WAIT) {
  575.         return 0;
  576.     }
  577.     }
  578. }
  579.