home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / py2s152.zip / Modules / tclNotify.c < prev    next >
C/C++ Source or Header  |  1999-06-27  |  27KB  |  965 lines

  1. /*
  2.  * This is a modified version of tclNotify.c from Sun's Tcl 8.0
  3.  * distribution.  The purpose of the modification is to provide an
  4.  * interface to the internals of the notifier that make it possible to
  5.  * write safe multi-threaded Python programs that use Tkinter.
  6.  *
  7.  * Original comments follow.  The file license.terms from the Tcl 8.0
  8.  * distribution is contained in this directory, as required.
  9.  */
  10.  
  11. /* 
  12.  * tclNotify.c --
  13.  *
  14.  *    This file implements the generic portion of the Tcl notifier.
  15.  *    The notifier is lowest-level part of the event system.  It
  16.  *    manages an event queue that holds Tcl_Event structures.  The
  17.  *    platform specific portion of the notifier is defined in the
  18.  *    tcl*Notify.c files in each platform directory.
  19.  *
  20.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  21.  *
  22.  * See the file "license.terms" for information on usage and redistribution
  23.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  24.  *
  25.  * SCCS: @(#) tclNotify.c 1.15 97/06/18 17:14:04
  26.  */
  27.  
  28. #include "tclInt.h"
  29. #include "tclPort.h"
  30.  
  31. /*
  32.  * The following static indicates whether this module has been initialized.
  33.  */
  34.  
  35. static int initialized = 0;
  36.  
  37. /*
  38.  * For each event source (created with Tcl_CreateEventSource) there
  39.  * is a structure of the following type:
  40.  */
  41.  
  42. typedef struct EventSource {
  43.     Tcl_EventSetupProc *setupProc;
  44.     Tcl_EventCheckProc *checkProc;
  45.     ClientData clientData;
  46.     struct EventSource *nextPtr;
  47. } EventSource;
  48.  
  49. /*
  50.  * The following structure keeps track of the state of the notifier.
  51.  * The first three elements keep track of the event queue.  In addition to
  52.  * the first (next to be serviced) and last events in the queue, we keep
  53.  * track of a "marker" event.  This provides a simple priority mechanism
  54.  * whereby events can be inserted at the front of the queue but behind all
  55.  * other high-priority events already in the queue (this is used for things
  56.  * like a sequence of Enter and Leave events generated during a grab in
  57.  * Tk).
  58.  */
  59.  
  60. static struct {
  61.     Tcl_Event *firstEventPtr;    /* First pending event, or NULL if none. */
  62.     Tcl_Event *lastEventPtr;    /* Last pending event, or NULL if none. */
  63.     Tcl_Event *markerEventPtr;    /* Last high-priority event in queue, or
  64.                  * NULL if none. */
  65.     int serviceMode;        /* One of TCL_SERVICE_NONE or
  66.                  * TCL_SERVICE_ALL. */
  67.     int blockTimeSet;        /* 0 means there is no maximum block
  68.                  * time:  block forever. */
  69.     Tcl_Time blockTime;        /* If blockTimeSet is 1, gives the
  70.                  * maximum elapsed time for the next block. */
  71.     int inTraversal;        /* 1 if Tcl_SetMaxBlockTime is being
  72.                  * called during an event source traversal. */
  73.     EventSource *firstEventSourcePtr;
  74.                 /* Pointer to first event source in
  75.                  * global list of event sources. */
  76. } notifier;
  77.  
  78. /*
  79.  * Declarations for functions used in this file.
  80.  */
  81.  
  82. static void    InitNotifier _ANSI_ARGS_((void));
  83. static void    NotifierExitHandler _ANSI_ARGS_((ClientData clientData));
  84.  
  85.  
  86. /*
  87.  *----------------------------------------------------------------------
  88.  *
  89.  * InitNotifier --
  90.  *
  91.  *    This routine is called to initialize the notifier module.
  92.  *
  93.  * Results:
  94.  *    None.
  95.  *
  96.  * Side effects:
  97.  *    Creates an exit handler and initializes static data.
  98.  *
  99.  *----------------------------------------------------------------------
  100.  */
  101.  
  102. static void
  103. InitNotifier()
  104. {
  105.     initialized = 1;
  106.     memset(¬ifier, 0, sizeof(notifier));
  107.     notifier.serviceMode = TCL_SERVICE_NONE;
  108.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  109. }
  110.  
  111. /*
  112.  *----------------------------------------------------------------------
  113.  *
  114.  * NotifierExitHandler --
  115.  *
  116.  *    This routine is called during Tcl finalization.
  117.  *
  118.  * Results:
  119.  *    None.
  120.  *
  121.  * Side effects:
  122.  *    Clears the notifier intialization flag.
  123.  *
  124.  *----------------------------------------------------------------------
  125.  */
  126.  
  127. static void
  128. NotifierExitHandler(clientData)
  129.     ClientData clientData;  /* Not used. */
  130. {
  131.     initialized = 0;
  132. }
  133.  
  134. /*
  135.  *----------------------------------------------------------------------
  136.  *
  137.  * Tcl_CreateEventSource --
  138.  *
  139.  *    This procedure is invoked to create a new source of events.
  140.  *    The source is identified by a procedure that gets invoked
  141.  *    during Tcl_DoOneEvent to check for events on that source
  142.  *    and queue them.
  143.  *
  144.  *
  145.  * Results:
  146.  *    None.
  147.  *
  148.  * Side effects:
  149.  *    SetupProc and checkProc will be invoked each time that Tcl_DoOneEvent
  150.  *    runs out of things to do.  SetupProc will be invoked before
  151.  *    Tcl_DoOneEvent calls select or whatever else it uses to wait
  152.  *    for events.  SetupProc typically calls functions like Tcl_WatchFile
  153.  *    or Tcl_SetMaxBlockTime to indicate what to wait for.
  154.  *
  155.  *    CheckProc is called after select or whatever operation was actually
  156.  *    used to wait.  It figures out whether anything interesting actually
  157.  *    happened (e.g. by calling Tcl_FileReady), and then calls
  158.  *    Tcl_QueueEvent to queue any events that are ready.
  159.  *
  160.  *    Each of these procedures is passed two arguments, e.g.
  161.  *        (*checkProc)(ClientData clientData, int flags));
  162.  *    ClientData is the same as the clientData argument here, and flags
  163.  *    is a combination of things like TCL_FILE_EVENTS that indicates
  164.  *    what events are of interest:  setupProc and checkProc use flags
  165.  *    to figure out whether their events are relevant or not.
  166.  *
  167.  *----------------------------------------------------------------------
  168.  */
  169.  
  170. void
  171. Tcl_CreateEventSource(setupProc, checkProc, clientData)
  172.     Tcl_EventSetupProc *setupProc;    /* Procedure to invoke to figure out
  173.                      * what to wait for. */
  174.     Tcl_EventCheckProc *checkProc;    /* Procedure to call after waiting
  175.                      * to see what happened. */
  176.     ClientData clientData;        /* One-word argument to pass to
  177.                      * setupProc and checkProc. */
  178. {
  179.     EventSource *sourcePtr;
  180.  
  181.     if (!initialized) {
  182.     InitNotifier();
  183.     }
  184.  
  185.     sourcePtr = (EventSource *) ckalloc(sizeof(EventSource));
  186.     sourcePtr->setupProc = setupProc;
  187.     sourcePtr->checkProc = checkProc;
  188.     sourcePtr->clientData = clientData;
  189.     sourcePtr->nextPtr = notifier.firstEventSourcePtr;
  190.     notifier.firstEventSourcePtr = sourcePtr;
  191. }
  192.  
  193. /*
  194.  *----------------------------------------------------------------------
  195.  *
  196.  * Tcl_DeleteEventSource --
  197.  *
  198.  *    This procedure is invoked to delete the source of events
  199.  *    given by proc and clientData.
  200.  *
  201.  * Results:
  202.  *    None.
  203.  *
  204.  * Side effects:
  205.  *    The given event source is cancelled, so its procedure will
  206.  *    never again be called.  If no such source exists, nothing
  207.  *    happens.
  208.  *
  209.  *----------------------------------------------------------------------
  210.  */
  211.  
  212. void
  213. Tcl_DeleteEventSource(setupProc, checkProc, clientData)
  214.     Tcl_EventSetupProc *setupProc;    /* Procedure to invoke to figure out
  215.                      * what to wait for. */
  216.     Tcl_EventCheckProc *checkProc;    /* Procedure to call after waiting
  217.                      * to see what happened. */
  218.     ClientData clientData;        /* One-word argument to pass to
  219.                      * setupProc and checkProc. */
  220. {
  221.     EventSource *sourcePtr, *prevPtr;
  222.  
  223.     for (sourcePtr = notifier.firstEventSourcePtr, prevPtr = NULL;
  224.         sourcePtr != NULL;
  225.         prevPtr = sourcePtr, sourcePtr = sourcePtr->nextPtr) {
  226.     if ((sourcePtr->setupProc != setupProc)
  227.         || (sourcePtr->checkProc != checkProc)
  228.         || (sourcePtr->clientData != clientData)) {
  229.         continue;
  230.     }
  231.     if (prevPtr == NULL) {
  232.         notifier.firstEventSourcePtr = sourcePtr->nextPtr;
  233.     } else {
  234.         prevPtr->nextPtr = sourcePtr->nextPtr;
  235.     }
  236.     ckfree((char *) sourcePtr);
  237.     return;
  238.     }
  239. }
  240.  
  241. /*
  242.  *----------------------------------------------------------------------
  243.  *
  244.  * Tcl_QueueEvent --
  245.  *
  246.  *    Insert an event into the Tk event queue at one of three
  247.  *    positions: the head, the tail, or before a floating marker.
  248.  *    Events inserted before the marker will be processed in
  249.  *    first-in-first-out order, but before any events inserted at
  250.  *    the tail of the queue.  Events inserted at the head of the
  251.  *    queue will be processed in last-in-first-out order.
  252.  *
  253.  * Results:
  254.  *    None.
  255.  *
  256.  * Side effects:
  257.  *    None.
  258.  *
  259.  *----------------------------------------------------------------------
  260.  */
  261.  
  262. void
  263. Tcl_QueueEvent(evPtr, position)
  264.     Tcl_Event* evPtr;        /* Event to add to queue.  The storage
  265.                  * space must have been allocated the caller
  266.                  * with malloc (ckalloc), and it becomes
  267.                  * the property of the event queue.  It
  268.                  * will be freed after the event has been
  269.                  * handled. */
  270.     Tcl_QueuePosition position;    /* One of TCL_QUEUE_TAIL, TCL_QUEUE_HEAD,
  271.                  * TCL_QUEUE_MARK. */
  272. {
  273.     if (!initialized) {
  274.     InitNotifier();
  275.     }
  276.  
  277.     if (position == TCL_QUEUE_TAIL) {
  278.     /*
  279.      * Append the event on the end of the queue.
  280.      */
  281.  
  282.     evPtr->nextPtr = NULL;
  283.     if (notifier.firstEventPtr == NULL) {
  284.         notifier.firstEventPtr = evPtr;
  285.     } else {
  286.         notifier.lastEventPtr->nextPtr = evPtr;
  287.     }
  288.     notifier.lastEventPtr = evPtr;
  289.     } else if (position == TCL_QUEUE_HEAD) {
  290.     /*
  291.      * Push the event on the head of the queue.
  292.      */
  293.  
  294.     evPtr->nextPtr = notifier.firstEventPtr;
  295.     if (notifier.firstEventPtr == NULL) {
  296.         notifier.lastEventPtr = evPtr;
  297.     }        
  298.     notifier.firstEventPtr = evPtr;
  299.     } else if (position == TCL_QUEUE_MARK) {
  300.     /*
  301.      * Insert the event after the current marker event and advance
  302.      * the marker to the new event.
  303.      */
  304.  
  305.     if (notifier.markerEventPtr == NULL) {
  306.         evPtr->nextPtr = notifier.firstEventPtr;
  307.         notifier.firstEventPtr = evPtr;
  308.     } else {
  309.         evPtr->nextPtr = notifier.markerEventPtr->nextPtr;
  310.         notifier.markerEventPtr->nextPtr = evPtr;
  311.     }
  312.     notifier.markerEventPtr = evPtr;
  313.     if (evPtr->nextPtr == NULL) {
  314.         notifier.lastEventPtr = evPtr;
  315.     }
  316.     }
  317. }
  318.  
  319. /*
  320.  *----------------------------------------------------------------------
  321.  *
  322.  * Tcl_DeleteEvents --
  323.  *
  324.  *    Calls a procedure for each event in the queue and deletes those
  325.  *    for which the procedure returns 1. Events for which the
  326.  *    procedure returns 0 are left in the queue.
  327.  *
  328.  * Results:
  329.  *    None.
  330.  *
  331.  * Side effects:
  332.  *    Potentially removes one or more events from the event queue.
  333.  *
  334.  *----------------------------------------------------------------------
  335.  */
  336.  
  337. void
  338. Tcl_DeleteEvents(proc, clientData)
  339.     Tcl_EventDeleteProc *proc;        /* The procedure to call. */
  340.     ClientData clientData;            /* type-specific data. */
  341. {
  342.     Tcl_Event *evPtr, *prevPtr, *hold;
  343.  
  344.     if (!initialized) {
  345.     InitNotifier();
  346.     }
  347.  
  348.     for (prevPtr = (Tcl_Event *) NULL, evPtr = notifier.firstEventPtr;
  349.              evPtr != (Tcl_Event *) NULL;
  350.              ) {
  351.         if ((*proc) (evPtr, clientData) == 1) {
  352.             if (notifier.firstEventPtr == evPtr) {
  353.                 notifier.firstEventPtr = evPtr->nextPtr;
  354.                 if (evPtr->nextPtr == (Tcl_Event *) NULL) {
  355.                     notifier.lastEventPtr = (Tcl_Event *) NULL;
  356.                 }
  357.             } else {
  358.                 prevPtr->nextPtr = evPtr->nextPtr;
  359.             }
  360.             hold = evPtr;
  361.             evPtr = evPtr->nextPtr;
  362.             ckfree((char *) hold);
  363.         } else {
  364.             prevPtr = evPtr;
  365.             evPtr = evPtr->nextPtr;
  366.         }
  367.     }
  368. }
  369.  
  370. /*
  371.  *----------------------------------------------------------------------
  372.  *
  373.  * Tcl_ServiceEvent --
  374.  *
  375.  *    Process one event from the event queue, or invoke an
  376.  *    asynchronous event handler.
  377.  *
  378.  * Results:
  379.  *    The return value is 1 if the procedure actually found an event
  380.  *    to process.  If no processing occurred, then 0 is returned.
  381.  *
  382.  * Side effects:
  383.  *    Invokes all of the event handlers for the highest priority
  384.  *    event in the event queue.  May collapse some events into a
  385.  *    single event or discard stale events.
  386.  *
  387.  *----------------------------------------------------------------------
  388.  */
  389.  
  390. int
  391. Tcl_ServiceEvent(flags)
  392.     int flags;            /* Indicates what events should be processed.
  393.                  * May be any combination of TCL_WINDOW_EVENTS
  394.                  * TCL_FILE_EVENTS, TCL_TIMER_EVENTS, or other
  395.                  * flags defined elsewhere.  Events not
  396.                  * matching this will be skipped for processing
  397.                  * later. */
  398. {
  399.     Tcl_Event *evPtr, *prevPtr;
  400.     Tcl_EventProc *proc;
  401.  
  402.     if (!initialized) {
  403.     InitNotifier();
  404.     }
  405.  
  406.     /*
  407.      * Asynchronous event handlers are considered to be the highest
  408.      * priority events, and so must be invoked before we process events
  409.      * on the event queue.
  410.      */
  411.     
  412.     if (Tcl_AsyncReady()) {
  413.     (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
  414.     return 1;
  415.     }
  416.  
  417.     /*
  418.      * No event flags is equivalent to TCL_ALL_EVENTS.
  419.      */
  420.     
  421.     if ((flags & TCL_ALL_EVENTS) == 0) {
  422.     flags |= TCL_ALL_EVENTS;
  423.     }
  424.  
  425.     /*
  426.      * Loop through all the events in the queue until we find one
  427.      * that can actually be handled.
  428.      */
  429.  
  430.     for (evPtr = notifier.firstEventPtr; evPtr != NULL;
  431.      evPtr = evPtr->nextPtr) {
  432.     /*
  433.      * Call the handler for the event.  If it actually handles the
  434.      * event then free the storage for the event.  There are two
  435.      * tricky things here, but stemming from the fact that the event
  436.      * code may be re-entered while servicing the event:
  437.      *
  438.      * 1. Set the "proc" field to NULL.  This is a signal to ourselves
  439.      *    that we shouldn't reexecute the handler if the event loop
  440.      *    is re-entered.
  441.      * 2. When freeing the event, must search the queue again from the
  442.      *    front to find it.  This is because the event queue could
  443.      *    change almost arbitrarily while handling the event, so we
  444.      *    can't depend on pointers found now still being valid when
  445.      *    the handler returns.
  446.      */
  447.  
  448.     proc = evPtr->proc;
  449.     evPtr->proc = NULL;
  450.     if ((proc != NULL) && (*proc)(evPtr, flags)) {
  451.         if (notifier.firstEventPtr == evPtr) {
  452.         notifier.firstEventPtr = evPtr->nextPtr;
  453.         if (evPtr->nextPtr == NULL) {
  454.             notifier.lastEventPtr = NULL;
  455.         }
  456.         if (notifier.markerEventPtr == evPtr) {
  457.             notifier.markerEventPtr = NULL;
  458.         }
  459.         } else {
  460.         for (prevPtr = notifier.firstEventPtr;
  461.              prevPtr->nextPtr != evPtr; prevPtr = prevPtr->nextPtr) {
  462.             /* Empty loop body. */
  463.         }
  464.         prevPtr->nextPtr = evPtr->nextPtr;
  465.         if (evPtr->nextPtr == NULL) {
  466.             notifier.lastEventPtr = prevPtr;
  467.         }
  468.         if (notifier.markerEventPtr == evPtr) {
  469.             notifier.markerEventPtr = prevPtr;
  470.         }
  471.         }
  472.         ckfree((char *) evPtr);
  473.         return 1;
  474.     } else {
  475.         /*
  476.          * The event wasn't actually handled, so we have to restore
  477.          * the proc field to allow the event to be attempted again.
  478.          */
  479.  
  480.         evPtr->proc = proc;
  481.     }
  482.  
  483.     /*
  484.      * The handler for this event asked to defer it.  Just go on to
  485.      * the next event.
  486.      */
  487.  
  488.     continue;
  489.     }
  490.     return 0;
  491. }
  492.  
  493. /*
  494.  *----------------------------------------------------------------------
  495.  *
  496.  * Tcl_GetServiceMode --
  497.  *
  498.  *    This routine returns the current service mode of the notifier.
  499.  *
  500.  * Results:
  501.  *    Returns either TCL_SERVICE_ALL or TCL_SERVICE_NONE.
  502.  *
  503.  * Side effects:
  504.  *    None.
  505.  *
  506.  *----------------------------------------------------------------------
  507.  */
  508.  
  509. int
  510. Tcl_GetServiceMode()
  511. {
  512.     if (!initialized) {
  513.     InitNotifier();
  514.     }
  515.  
  516.     return notifier.serviceMode;
  517. }
  518.  
  519. /*
  520.  *----------------------------------------------------------------------
  521.  *
  522.  * Tcl_SetServiceMode --
  523.  *
  524.  *    This routine sets the current service mode of the notifier.
  525.  *
  526.  * Results:
  527.  *    Returns the previous service mode.
  528.  *
  529.  * Side effects:
  530.  *    None.
  531.  *
  532.  *----------------------------------------------------------------------
  533.  */
  534.  
  535. int
  536. Tcl_SetServiceMode(mode)
  537.     int mode;            /* New service mode: TCL_SERVICE_ALL or
  538.                  * TCL_SERVICE_NONE */
  539. {
  540.     int oldMode;
  541.  
  542.     if (!initialized) {
  543.     InitNotifier();
  544.     }
  545.  
  546.     oldMode = notifier.serviceMode;
  547.     notifier.serviceMode = mode;
  548.     return oldMode;
  549. }
  550.  
  551. /*
  552.  *----------------------------------------------------------------------
  553.  *
  554.  * Tcl_SetMaxBlockTime --
  555.  *
  556.  *    This procedure is invoked by event sources to tell the notifier
  557.  *    how long it may block the next time it blocks.  The timePtr
  558.  *    argument gives a maximum time;  the actual time may be less if
  559.  *    some other event source requested a smaller time.
  560.  *
  561.  * Results:
  562.  *    None.
  563.  *
  564.  * Side effects:
  565.  *    May reduce the length of the next sleep in the notifier.
  566.  *
  567.  *----------------------------------------------------------------------
  568.  */
  569.  
  570. void
  571. Tcl_SetMaxBlockTime(timePtr)
  572.     Tcl_Time *timePtr;        /* Specifies a maximum elapsed time for
  573.                  * the next blocking operation in the
  574.                  * event notifier. */
  575. {
  576.     if (!initialized) {
  577.     InitNotifier();
  578.     }
  579.  
  580.     if (!notifier.blockTimeSet || (timePtr->sec < notifier.blockTime.sec)
  581.         || ((timePtr->sec == notifier.blockTime.sec)
  582.         && (timePtr->usec < notifier.blockTime.usec))) {
  583.     notifier.blockTime = *timePtr;
  584.     notifier.blockTimeSet = 1;
  585.     }
  586.  
  587.     /*
  588.      * If we are called outside an event source traversal, set the
  589.      * timeout immediately.
  590.      */
  591.  
  592.     if (!notifier.inTraversal) {
  593.     if (notifier.blockTimeSet) {
  594.         Tcl_SetTimer(¬ifier.blockTime);
  595.     } else {
  596.         Tcl_SetTimer(NULL);
  597.     }
  598.     }
  599. }
  600.  
  601. /*
  602.  *----------------------------------------------------------------------
  603.  *
  604.  * Tcl_DoOneEvent --
  605.  *
  606.  *    Process a single event of some sort.  If there's no work to
  607.  *    do, wait for an event to occur, then process it.
  608.  *
  609.  * Results:
  610.  *    The return value is 1 if the procedure actually found an event
  611.  *    to process.  If no processing occurred, then 0 is returned (this
  612.  *    can happen if the TCL_DONT_WAIT flag is set or if there are no
  613.  *    event handlers to wait for in the set specified by flags).
  614.  *
  615.  * Side effects:
  616.  *    May delay execution of process while waiting for an event,
  617.  *    unless TCL_DONT_WAIT is set in the flags argument.  Event
  618.  *    sources are invoked to check for and queue events.  Event
  619.  *    handlers may produce arbitrary side effects.
  620.  *
  621.  *----------------------------------------------------------------------
  622.  */
  623.  
  624. int
  625. Tcl_DoOneEvent(flags)
  626.     int flags;            /* Miscellaneous flag values:  may be any
  627.                  * combination of TCL_DONT_WAIT,
  628.                  * TCL_WINDOW_EVENTS, TCL_FILE_EVENTS,
  629.                  * TCL_TIMER_EVENTS, TCL_IDLE_EVENTS, or
  630.                  * others defined by event sources. */
  631. {
  632.     int result = 0, oldMode;
  633.     EventSource *sourcePtr;
  634.     Tcl_Time *timePtr;
  635.  
  636.     if (!initialized) {
  637.     InitNotifier();
  638.     }
  639.  
  640.     /*
  641.      * The first thing we do is to service any asynchronous event
  642.      * handlers.
  643.      */
  644.     
  645.     if (Tcl_AsyncReady()) {
  646.     (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
  647.     return 1;
  648.     }
  649.  
  650.     /*
  651.      * No event flags is equivalent to TCL_ALL_EVENTS.
  652.      */
  653.     
  654.     if ((flags & TCL_ALL_EVENTS) == 0) {
  655.     flags |= TCL_ALL_EVENTS;
  656.     }
  657.  
  658.     /*
  659.      * Set the service mode to none so notifier event routines won't
  660.      * try to service events recursively.
  661.      */
  662.  
  663.     oldMode = notifier.serviceMode;
  664.     notifier.serviceMode = TCL_SERVICE_NONE;
  665.  
  666.     /*
  667.      * The core of this procedure is an infinite loop, even though
  668.      * we only service one event.  The reason for this is that we
  669.      * may be processing events that don't do anything inside of Tcl.
  670.      */
  671.  
  672.     while (1) {
  673.  
  674.     /*
  675.      * If idle events are the only things to service, skip the
  676.      * main part of the loop and go directly to handle idle
  677.      * events (i.e. don't wait even if TCL_DONT_WAIT isn't set).
  678.      */
  679.  
  680.     if ((flags & TCL_ALL_EVENTS) == TCL_IDLE_EVENTS) {
  681.         flags = TCL_IDLE_EVENTS|TCL_DONT_WAIT;
  682.         goto idleEvents;
  683.     }
  684.  
  685.     /*
  686.      * Ask Tcl to service a queued event, if there are any.
  687.      */
  688.  
  689.     if (Tcl_ServiceEvent(flags)) {
  690.         result = 1;        
  691.         break;
  692.     }
  693.  
  694.     /*
  695.      * If TCL_DONT_WAIT is set, be sure to poll rather than
  696.      * blocking, otherwise reset the block time to infinity.
  697.      */
  698.  
  699.     if (flags & TCL_DONT_WAIT) {
  700.         notifier.blockTime.sec = 0;
  701.         notifier.blockTime.usec = 0;
  702.         notifier.blockTimeSet = 1;
  703.     } else {
  704.         notifier.blockTimeSet = 0;
  705.     }
  706.  
  707.     /*
  708.      * Set up all the event sources for new events.  This will
  709.      * cause the block time to be updated if necessary.
  710.      */
  711.  
  712.     notifier.inTraversal = 1;
  713.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  714.          sourcePtr = sourcePtr->nextPtr) {
  715.         if (sourcePtr->setupProc) {
  716.         (sourcePtr->setupProc)(sourcePtr->clientData, flags);
  717.         }
  718.     }
  719.     notifier.inTraversal = 0;
  720.  
  721.     if ((flags & TCL_DONT_WAIT) || notifier.blockTimeSet) {
  722.         timePtr = ¬ifier.blockTime;
  723.     } else {
  724.         timePtr = NULL;
  725.     }
  726.  
  727.     /*
  728.      * Wait for a new event or a timeout.  If Tcl_WaitForEvent
  729.      * returns -1, we should abort Tcl_DoOneEvent.
  730.      */
  731.  
  732.     result = Tcl_WaitForEvent(timePtr);
  733.     if (result < 0) {
  734.         result = 0;
  735.         break;
  736.     }
  737.  
  738.     /*
  739.      * Check all the event sources for new events.
  740.      */
  741.  
  742.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  743.          sourcePtr = sourcePtr->nextPtr) {
  744.         if (sourcePtr->checkProc) {
  745.         (sourcePtr->checkProc)(sourcePtr->clientData, flags);
  746.         }
  747.     }
  748.  
  749.     /*
  750.      * Check for events queued by the notifier or event sources.
  751.      */
  752.  
  753.     if (Tcl_ServiceEvent(flags)) {
  754.         result = 1;
  755.         break;
  756.     }
  757.  
  758.     /*
  759.      * We've tried everything at this point, but nobody we know
  760.      * about had anything to do.  Check for idle events.  If none,
  761.      * either quit or go back to the top and try again.
  762.      */
  763.  
  764.     idleEvents:
  765.     if (flags & TCL_IDLE_EVENTS) {
  766.         if (TclServiceIdle()) {
  767.         result = 1;
  768.         break;
  769.         }
  770.     }
  771.     if (flags & TCL_DONT_WAIT) {
  772.         break;
  773.     }
  774.     }
  775.  
  776.     notifier.serviceMode = oldMode;
  777.     return result;
  778. }
  779.  
  780. /*
  781.  *----------------------------------------------------------------------
  782.  *
  783.  * Tcl_ServiceAll --
  784.  *
  785.  *    This routine checks all of the event sources, processes
  786.  *    events that are on the Tcl event queue, and then calls the
  787.  *    any idle handlers.  Platform specific notifier callbacks that
  788.  *    generate events should call this routine before returning to
  789.  *    the system in order to ensure that Tcl gets a chance to
  790.  *    process the new events.
  791.  *
  792.  * Results:
  793.  *    Returns 1 if an event or idle handler was invoked, else 0.
  794.  *
  795.  * Side effects:
  796.  *    Anything that an event or idle handler may do.
  797.  *
  798.  *----------------------------------------------------------------------
  799.  */
  800.  
  801. int
  802. Tcl_ServiceAll()
  803. {
  804.     int result = 0;
  805.     EventSource *sourcePtr;
  806.  
  807.     if (!initialized) {
  808.     InitNotifier();
  809.     }
  810.  
  811.     if (notifier.serviceMode == TCL_SERVICE_NONE) {
  812.     return result;
  813.     }
  814.  
  815.     /*
  816.      * We need to turn off event servicing like we to in Tcl_DoOneEvent,
  817.      * to avoid recursive calls.
  818.      */
  819.     
  820.     notifier.serviceMode = TCL_SERVICE_NONE;
  821.  
  822.     /*
  823.      * Check async handlers first.
  824.      */
  825.  
  826.     if (Tcl_AsyncReady()) {
  827.     (void) Tcl_AsyncInvoke((Tcl_Interp *) NULL, 0);
  828.     }
  829.  
  830.     /*
  831.      * Make a single pass through all event sources, queued events,
  832.      * and idle handlers.  Note that we wait to update the notifier
  833.      * timer until the end so we can avoid multiple changes.
  834.      */
  835.  
  836.     notifier.inTraversal = 1;
  837.     notifier.blockTimeSet = 0;
  838.  
  839.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  840.      sourcePtr = sourcePtr->nextPtr) {
  841.     if (sourcePtr->setupProc) {
  842.         (sourcePtr->setupProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
  843.     }
  844.     }
  845.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  846.      sourcePtr = sourcePtr->nextPtr) {
  847.     if (sourcePtr->checkProc) {
  848.         (sourcePtr->checkProc)(sourcePtr->clientData, TCL_ALL_EVENTS);
  849.     }
  850.     }
  851.  
  852.     while (Tcl_ServiceEvent(0)) {
  853.     result = 1;
  854.     }
  855.     if (TclServiceIdle()) {
  856.     result = 1;
  857.     }
  858.  
  859.     if (!notifier.blockTimeSet) {
  860.     Tcl_SetTimer(NULL);
  861.     } else {
  862.     Tcl_SetTimer(¬ifier.blockTime);
  863.     }
  864.     notifier.inTraversal = 0;
  865.     notifier.serviceMode = TCL_SERVICE_ALL;
  866.     return result;
  867. }
  868.  
  869. /*
  870.  *----------------------------------------------------------------------
  871.  *
  872.  * PyTcl_WaitUntilEvent --
  873.  *
  874.  *    New function to wait until a Tcl event is ready without
  875.  *    actually handling the event.  This is different than
  876.  *    TclWaitForEvent(): that function doesn't call the event
  877.  *    check routines, which is necessary for our purpose.
  878.  *    We also can't use Tcl_DoOneEvent(TCL_DONT_WAIT), since that
  879.  *    does too much: it handles the event.  We want the *handling*
  880.  *    of the event to be done with the Python lock held, but the
  881.  *    *waiting* with the lock released.
  882.  *
  883.  *    Since the event administration is not exported, our only
  884.  *    choice is to use a modified copy of the file tclNotify.c,
  885.  *    containing this additional function that makes the desired
  886.  *    functionality available.  It is mostly a stripped down version
  887.  *    of the code in Tcl_DoOneEvent().
  888.  *
  889.  *    This requires that you link with a static version of the Tcl
  890.  *    library.  On Windows/Mac, a custom compilation of Tcl may be
  891.  *    required (I haven't tried this yet).
  892.  *
  893.  *----------------------------------------------------------------------
  894.  */
  895.  
  896. int
  897. PyTcl_WaitUntilEvent()
  898. {
  899.     int flags = TCL_ALL_EVENTS;
  900.     int result = 0, oldMode;
  901.     EventSource *sourcePtr;
  902.     Tcl_Time *timePtr;
  903.  
  904.     if (!initialized) {
  905.     InitNotifier();
  906.     }
  907.  
  908.     /*
  909.      * The first thing we do is to service any asynchronous event
  910.      * handlers.
  911.      */
  912.     
  913.     if (Tcl_AsyncReady())
  914.     return 1;
  915.  
  916.     /*
  917.      * Set the service mode to none so notifier event routines won't
  918.      * try to service events recursively.
  919.      */
  920.  
  921.     oldMode = notifier.serviceMode;
  922.     notifier.serviceMode = TCL_SERVICE_NONE;
  923.  
  924.     notifier.blockTimeSet = 0;
  925.  
  926.     /*
  927.      * Set up all the event sources for new events.  This will
  928.      * cause the block time to be updated if necessary.
  929.      */
  930.  
  931.     notifier.inTraversal = 1;
  932.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  933.      sourcePtr = sourcePtr->nextPtr) {
  934.     if (sourcePtr->setupProc) {
  935.         (sourcePtr->setupProc)(sourcePtr->clientData, flags);
  936.     }
  937.     }
  938.     notifier.inTraversal = 0;
  939.  
  940.     timePtr = NULL;
  941.  
  942.     /*
  943.      * Wait for a new event or a timeout.  If Tcl_WaitForEvent
  944.      * returns -1, we should abort Tcl_DoOneEvent.
  945.      */
  946.  
  947.     result = Tcl_WaitForEvent(timePtr);
  948.     if (result < 0)
  949.     return 0;
  950.  
  951.     /*
  952.      * Check all the event sources for new events.
  953.      */
  954.  
  955.     for (sourcePtr = notifier.firstEventSourcePtr; sourcePtr != NULL;
  956.      sourcePtr = sourcePtr->nextPtr) {
  957.     if (sourcePtr->checkProc) {
  958.         (sourcePtr->checkProc)(sourcePtr->clientData, flags);
  959.     }
  960.     }
  961.  
  962.     notifier.serviceMode = oldMode;
  963.     return result;
  964. }
  965.