home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tcltk805.zip / tcl805s.zip / tcl8.0.5 / os2 / tclOS2Notify.c < prev    next >
C/C++ Source or Header  |  2001-02-09  |  26KB  |  858 lines

  1. /* 
  2.  * tclOS2Notify.c --
  3.  *
  4.  *    This file contains OS/2-specific procedures for the notifier,
  5.  *    which is the lowest-level part of the Tcl event loop.  This file
  6.  *    works together with ../generic/tclNotify.c.
  7.  *
  8.  * Copyright (c) 1995-1996 Sun Microsystems, Inc.
  9.  * Copyright (c) 1996-2001 Illya Vaes
  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.  */
  15.  
  16. #include "tclOS2Int.h"
  17.  
  18. /*
  19.  * This structure is used to keep track of the notifier info for a
  20.  * a registered file.
  21.  */
  22.  
  23. typedef struct FileHandler {
  24.     int fd;
  25.     int mask;                   /* Mask of desired events: TCL_READABLE,
  26.                                  * etc. */
  27.     int readyMask;              /* Mask of events that have been seen since the
  28.                                  * last time file handlers were invoked for
  29.                                  * this file. */
  30.     Tcl_FileProc *proc;         /* Procedure to call, in the style of
  31.                                  * Tcl_CreateFileHandler. */
  32.     ClientData clientData;      /* Argument to pass to proc. */
  33.     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
  34. } FileHandler;
  35.  
  36. /*
  37.  * The following structure is what is added to the Tcl event queue when
  38.  * file handlers are ready to fire.
  39.  */
  40.  
  41. typedef struct FileHandlerEvent {
  42.     Tcl_Event header;           /* Information that is standard for
  43.                                  * all events. */
  44.     int fd;                     /* File descriptor that is ready.  Used
  45.                                  * to find the FileHandler structure for
  46.                                  * the file (can't point directly to the
  47.                                  * FileHandler structure because it could
  48.                                  * go away while the event is queued). */
  49. } FileHandlerEvent;
  50.  
  51. /*
  52.  * The following static structure contains the state information for the
  53.  * select based implementation of the Tcl notifier.
  54.  */
  55.  
  56. static struct {
  57.     FileHandler *firstFileHandlerPtr;
  58.                                 /* Pointer to head of file handler list. */
  59.     fd_set checkMasks[3];
  60.                                 /* This array is used to build up the masks
  61.                                  * to be used in the next call to select.
  62.                                  * Bits are set in response to calls to
  63.                                  * Tcl_CreateFileHandler. */
  64.     fd_set readyMasks[3];
  65.                                 /* This array reflects the readable/writable
  66.                                  * conditions that were found to exist by the
  67.                                  * last call to select. */
  68.     int numFdBits;              /* Number of valid bits in checkMasks
  69.                                  * (one more than highest fd for which
  70.                                  * Tcl_WatchFile has been called). */
  71.     HWND hwnd;            /* Messaging window. */
  72.     ULONG timeout;        /* Current timeout value */
  73.     int timerActive;        /* 1 if interval timer is running */
  74. } notifier;
  75.  
  76. /*
  77.  * The following static indicates whether this module has been initialized.
  78.  */
  79.  
  80. static int initialized = 0;
  81.  
  82. /*
  83.  * The following static holds the event semaphore handle for PM-less running.
  84.  */
  85.  
  86. static HEV eventSem;
  87.     static HTIMER timerHandle = NULLHANDLE;
  88.  
  89. /*
  90.  * Static routines defined in this file:
  91.  */
  92.  
  93. static void             InitNotifier _ANSI_ARGS_((void));
  94. static void             NotifierExitHandler _ANSI_ARGS_((
  95.                             ClientData clientData));
  96. static MRESULT EXPENTRY NotifierProc(HWND hwnd, ULONG message,
  97.                             MPARAM param1, MPARAM param2);
  98. static int              FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  99.                             int flags));
  100. static void             UpdateTimer(ULONG timeout);
  101.  
  102. /*
  103.  *----------------------------------------------------------------------
  104.  *
  105.  * InitNotifier --
  106.  *
  107.  *      Initializes the notifier.
  108.  *
  109.  * Results:
  110.  *      None.
  111.  *
  112.  * Side effects:
  113.  *      Creates a new notifier.
  114.  *
  115.  *----------------------------------------------------------------------
  116.  */
  117.  
  118. static void
  119. InitNotifier(void)
  120. {
  121. #ifdef VERBOSE
  122.     printf("InitNotifier (Tcl) 1: initialized %d\n", initialized);
  123. #endif
  124.     initialized = 1;
  125.     memset(¬ifier, 0, sizeof(notifier));
  126.     if (usePm) {
  127.         if (!WinRegisterClass(TclOS2GetHAB(), "TclNotifier", NotifierProc, 0L,
  128.              0L)) {
  129.             panic("Unable to register TclNotifier window class");
  130.         }
  131.         notifier.hwnd = WinCreateWindow(HWND_DESKTOP, "TclNotifier", NULL,
  132.                                         0L, 0, 0, 0, 0, HWND_DESKTOP, HWND_TOP,
  133.                                         0, NULL, NULL);
  134. #ifdef VERBOSE
  135.         printf("InitNotifier (Tcl): hwnd %x\n", notifier.hwnd);
  136. #endif
  137.     } else {
  138.         rc = DosCreateEventSem(NULL, &eventSem, DC_SEM_SHARED, FALSE);
  139. #ifdef VERBOSE
  140.         if (rc != NO_ERROR) {
  141.             printf("InitNotifier (Tcl): DosCreateEventSem ERROR %d\n", rc);
  142.         } else {
  143.             printf("InitNotifier (Tcl): DosCreateEventSem OK %x\n", eventSem);
  144.         }
  145. #endif
  146.     }
  147.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  148. }
  149.  
  150. /*
  151.  *----------------------------------------------------------------------
  152.  *
  153.  * NotifierExitHandler --
  154.  *
  155.  *      This function is called to cleanup the notifier state before
  156.  *      Tcl is unloaded.
  157.  *
  158.  * Results:
  159.  *      None.
  160.  *
  161.  * Side effects:
  162.  *      Destroys the notifier.
  163.  *
  164.  *----------------------------------------------------------------------
  165.  */
  166.  
  167. static void
  168. NotifierExitHandler(
  169.     ClientData clientData)      /* Old window proc */
  170. {
  171. #ifdef VERBOSE
  172.     printf("NotifierExitHandler (Tcl): hwnd %x, initialized %d => 0\n",
  173.            notifier.hwnd, initialized);
  174. #endif
  175.     initialized = 0;
  176.     Tcl_DeleteExitHandler(NotifierExitHandler, NULL);
  177.     if (usePm) {
  178.         if (notifier.hwnd != NULLHANDLE) {
  179.             WinStopTimer(TclOS2GetHAB(), notifier.hwnd, TID_USERMAX - 1);
  180.             WinDestroyWindow(notifier.hwnd);
  181.             notifier.hwnd = NULLHANDLE;
  182.         }
  183.     } else {
  184.         DosCloseEventSem(eventSem);
  185.     }
  186. }
  187.  
  188. /*
  189.  *----------------------------------------------------------------------
  190.  *
  191.  * UpdateTimer --
  192.  *
  193.  *      This function starts or stops the notifier interval timer.
  194.  *
  195.  * Results:
  196.  *      None.
  197.  *
  198.  * Side effects:
  199.  *      None.
  200.  *
  201.  *----------------------------------------------------------------------
  202.  */
  203.  
  204. void
  205. UpdateTimer(
  206.     ULONG timeout)                /* ms timeout, 0 means cancel timer */
  207. {
  208.     static ULONG timerId = 0L;
  209.  
  210. #ifdef VERBOSE
  211.     printf("UpdateTimer timeout %dms\n", timeout);
  212.     fflush(stdout);
  213. #endif
  214.     notifier.timeout = timeout;
  215.     if (timeout != 0) {
  216.         notifier.timerActive = 1;
  217.         if (usePm) {
  218.             timerId = WinStartTimer(TclOS2GetHAB(), notifier.hwnd,
  219.                                     TID_USERMAX - 1, timeout);
  220. #ifdef VERBOSE
  221.             if (timerId == 0) {
  222.                 printf("WinStartTimer hwnd %x timeout %d ERROR %x\n",
  223.                        notifier.hwnd, timeout, WinGetLastError(TclOS2GetHAB()));
  224.             } else {
  225.                 printf("WinStartTimer hwnd %x timeout %d OK %x\n",
  226.                        notifier.hwnd, timeout, timerId);
  227.             }
  228. #endif
  229.         } else {
  230.             rc = DosAsyncTimer(timeout, (HSEM) eventSem, &timerHandle);
  231. #ifdef VERBOSE
  232.             if (rc != NO_ERROR) {
  233.                 printf("DosAsyncTimer %dms ERROR %d\n", timeout, rc);
  234.             } else {
  235.                 printf("DosAsyncTimer %dms OK %x\n", timeout, timerHandle);
  236.             }
  237. #endif
  238.         }
  239.     } else {
  240.         notifier.timerActive = 0;
  241.         if (usePm) {
  242.             if (timerId) {
  243.                 rc = WinStopTimer(TclOS2GetHAB(), notifier.hwnd, TID_USERMAX-1);
  244. #ifdef VERBOSE
  245.                 if (rc == FALSE) {
  246.                     printf("WinStopTimer hwnd %x ERROR/NOT EXIST %x\n",
  247.                            notifier.hwnd, WinGetLastError(TclOS2GetHAB()));
  248.                 } else {
  249.                     printf("WinStopTimer hwnd %x OK\n", notifier.hwnd);
  250.                 }
  251. #endif
  252.             }
  253.             timerId = 0;
  254.         } else {
  255.             ULONG postCount;
  256.  
  257. #ifdef VERBOSE
  258.             printf("UpdateTimer: timerHandle %x\n", timerHandle);
  259. #endif
  260.             if (timerHandle != NULLHANDLE) {
  261.                 rc = DosStopTimer(timerHandle);
  262. #ifdef VERBOSE
  263.                 if (rc != NO_ERROR) {
  264.                     printf("DosStopTimer %x ERROR %d\n", timerHandle, rc);
  265.                 } else {
  266.                     printf("DosStopTimer %x OK\n", timerHandle);
  267.                 }
  268. #endif
  269.                 rc = DosResetEventSem(eventSem, &postCount);
  270. #ifdef VERBOSE
  271.                 if (rc != NO_ERROR) {
  272.                     printf("DosResetEventSem %x ERROR %d\n", eventSem, rc);
  273.                 } else {
  274.                     printf("DosResetEventSem %x OK %d\n", eventSem, postCount);
  275.                 }
  276. #endif
  277.                 timerHandle = NULLHANDLE;
  278.             }
  279.         }
  280.     }
  281. }
  282.  
  283. /*
  284.  *----------------------------------------------------------------------
  285.  *
  286.  * Tcl_SetTimer --
  287.  *
  288.  *      This procedure sets the current notifier timer value. The
  289.  *      notifier will ensure that Tcl_ServiceAll() is called after
  290.  *      the specified interval, even if no events have occurred.
  291.  *
  292.  * Results:
  293.  *      None.
  294.  *
  295.  * Side effects:
  296.  *      Replaces any previous timer.
  297.  *
  298.  *----------------------------------------------------------------------
  299.  */
  300.  
  301. void
  302. Tcl_SetTimer(timePtr)
  303.     Tcl_Time *timePtr;          /* Timeout value, may be NULL. */
  304. {
  305.     ULONG timeout;
  306.  
  307. #ifdef VERBOSE
  308.     printf("Tcl_SetTimer, timePtr %x\n", timePtr);
  309. #endif
  310.     if (!initialized) {
  311. #ifdef VERBOSE
  312.         printf("InitNotifier being called by Tcl_SetTimer\n");
  313. #endif
  314.         InitNotifier();
  315.     }
  316.  
  317.     if (!timePtr) {
  318.         timeout = 0;
  319.     } else {
  320.         /*
  321.          * For OS/2 2.1 and earlier, this must be an unsigned int range
  322.          * (0-65535), for higher version an unsigned long (0-4294967295).
  323.          */
  324.         timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
  325. #ifdef VERBOSE
  326.         printf("Tcl_SetTimer timeout %d (%d * 1000 + %d / 1000)\n", timeout,
  327.                timePtr->sec, timePtr->usec);
  328. #endif
  329.         if ( timeout > 65535 && sysInfo[QSV_VERSION_MAJOR - 1] == 20
  330.             && sysInfo[QSV_VERSION_MINOR - 1] < 30) {
  331.             timeout = 65535;
  332. #ifdef VERBOSE
  333.             printf("Tcl_SetTimer major %d minor %d => timeout %d\n",
  334.                    sysInfo[QSV_VERSION_MAJOR - 1],
  335.                    sysInfo[QSV_VERSION_MINOR - 1], timeout);
  336. #endif
  337.         }
  338.     }
  339.     UpdateTimer(timeout);
  340. }
  341.  
  342. /*
  343.  *----------------------------------------------------------------------
  344.  *
  345.  * NotifierProc --
  346.  *
  347.  *      This procedure is invoked by OS/2 to process the timer
  348.  *      message whenever we are using an external dispatch loop.
  349.  *
  350.  * Results:
  351.  *      A standard windows result.
  352.  *
  353.  * Side effects:
  354.  *      Services any pending events.
  355.  *
  356.  *----------------------------------------------------------------------
  357.  */
  358.  
  359. static MRESULT EXPENTRY
  360. NotifierProc(
  361.     HWND hwnd,
  362.     ULONG message,
  363.     MPARAM param1,
  364.     MPARAM param2)
  365. {
  366.  
  367. #ifdef VERBOSE
  368.     printf("NotifierProc hwnd %x, msg %x, p1 %x, p2 %x\n", hwnd, message,
  369.            param1, param2);
  370.     fflush(stdout);
  371. #endif
  372.     if (message != WM_TIMER) {
  373.         return WinDefWindowProc(hwnd, message, param1, param2);
  374.     }
  375.  
  376.     /*
  377.      * Process all of the runnable events.
  378.      */
  379.  
  380.     Tcl_ServiceAll();
  381.     return 0;
  382. }
  383.  
  384. /*
  385.  *----------------------------------------------------------------------
  386.  *
  387.  * Tcl_CreateFileHandler --
  388.  *
  389.  *      This procedure registers a file handler with the Xt notifier.
  390.  *
  391.  * Results:
  392.  *      None.
  393.  *
  394.  * Side effects:
  395.  *      Creates a new file handler structure and registers one or more
  396.  *      input procedures with Xt.
  397.  *
  398.  *----------------------------------------------------------------------
  399.  */
  400.  
  401. void
  402. Tcl_CreateFileHandler(fd, mask, proc, clientData)
  403.     int fd;                     /* Handle of stream to watch. */
  404.     int mask;                   /* OR'ed combination of TCL_READABLE,
  405.                                  * TCL_WRITABLE and TCL_EXCEPTION:
  406.                                  * indicates conditions under which
  407.                                  * proc should be called. */
  408.     Tcl_FileProc *proc;         /* Procedure to call for each
  409.                                  * selected event. */
  410.     ClientData clientData;      /* Arbitrary data to pass to proc. */
  411. {
  412.     FileHandler *filePtr;
  413.  
  414. #ifdef VERBOSE
  415.     printf("Tcl_CreateFileHandler\n");
  416. #endif
  417.     if (!initialized) {
  418. #ifdef VERBOSE
  419.         printf("InitNotifier being called by Tcl_CreateFileHandler\n");
  420. #endif
  421.         InitNotifier();
  422.     }
  423.  
  424.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  425.             filePtr = filePtr->nextPtr) {
  426.         if (filePtr->fd == fd) {
  427.             break;
  428.         }
  429.     }
  430.     if (filePtr == NULL) {
  431.         filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
  432.         filePtr->fd = fd;
  433.         filePtr->readyMask = 0;
  434.         filePtr->nextPtr = notifier.firstFileHandlerPtr;
  435.         notifier.firstFileHandlerPtr = filePtr;
  436.     }
  437.     filePtr->proc = proc;
  438.     filePtr->clientData = clientData;
  439.     filePtr->mask = mask;
  440.  
  441.     /*
  442.      * Update the check masks for this file.
  443.      */
  444.  
  445.     if (mask & TCL_READABLE) {
  446.         FD_SET(fd, ¬ifier.checkMasks[0]);
  447.     } else {
  448.         FD_CLR(fd, ¬ifier.checkMasks[0]);
  449.     }
  450.     if (mask & TCL_WRITABLE) {
  451.         FD_SET(fd, ¬ifier.checkMasks[1]);
  452.     } else {
  453.         FD_CLR(fd, ¬ifier.checkMasks[1]);
  454.     }
  455.     if (mask & TCL_EXCEPTION) {
  456.         FD_SET(fd, ¬ifier.checkMasks[2]);
  457.     } else {
  458.         FD_CLR(fd, ¬ifier.checkMasks[2]);
  459.     }
  460.     if (notifier.numFdBits <= fd) {
  461.         notifier.numFdBits = fd+1;
  462.     }
  463. }
  464.  
  465. /*
  466.  *----------------------------------------------------------------------
  467.  *
  468.  * Tcl_DeleteFileHandler --
  469.  *
  470.  *      Cancel a previously-arranged callback arrangement for
  471.  *      a file.
  472.  *
  473.  * Results:
  474.  *      None.
  475.  *
  476.  * Side effects:
  477.  *      If a callback was previously registered on file, remove it.
  478.  *
  479.  *----------------------------------------------------------------------
  480.  */
  481.  
  482. void
  483. Tcl_DeleteFileHandler(fd)
  484.     int fd;             /* Stream id for which to remove callback procedure. */
  485. {
  486.     FileHandler *filePtr, *prevPtr;
  487.     int index, i;
  488.     unsigned long flags;
  489.  
  490. #ifdef VERBOSE
  491.     printf("Tcl_DeleteFileHandler\n");
  492. #endif
  493.     if (!initialized) {
  494. #ifdef VERBOSE
  495.         printf("InitNotifier being called by Tcl_DeleteFileHandler\n");
  496. #endif
  497.         InitNotifier();
  498.     }
  499.  
  500.     /*
  501.      * Find the entry for the given file (and return if there
  502.      * isn't one).
  503.      */
  504.  
  505.     for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
  506.             prevPtr = filePtr, filePtr = filePtr->nextPtr) {
  507.         if (filePtr == NULL) {
  508.             return;
  509.         }
  510.         if (filePtr->fd == fd) {
  511.             break;
  512.         }
  513.     }
  514.  
  515.     /*
  516.      * Update the check masks for this file.
  517.      */
  518.  
  519.     if (filePtr->mask & TCL_READABLE) {
  520.         FD_CLR(fd, ¬ifier.checkMasks[0]);
  521.     }
  522.     if (filePtr->mask & TCL_WRITABLE) {
  523.         FD_CLR(fd, ¬ifier.checkMasks[1]);
  524.     }
  525.     if (filePtr->mask & TCL_EXCEPTION) {
  526.         FD_CLR(fd, ¬ifier.checkMasks[2]);
  527.     }
  528.  
  529.     /*
  530.      * Find current max fd.
  531.      */
  532.  
  533.     if (fd+1 == notifier.numFdBits) {
  534.         for (index = fd/sizeof(fd_set), notifier.numFdBits = 0;
  535.              index >= 0; index--) {
  536.             flags = FD_ISSET(index, ¬ifier.checkMasks[0])
  537.                    | FD_ISSET(index, ¬ifier.checkMasks[1])
  538.                    | FD_ISSET(index, ¬ifier.checkMasks[2]);
  539.             if (flags) {
  540.                 for (i = FD_SETSIZE; i > 0; i--) {
  541.                     if (flags & (i << (i-1))) {
  542.                         break;
  543.                     }
  544.                 }
  545.                 notifier.numFdBits = index * (sizeof(fd_set)) + i;
  546.                 return;
  547.             }
  548.         }
  549.     }
  550.  
  551.     /*
  552.      * Clean up information in the callback record.
  553.      */
  554.  
  555.     if (prevPtr == NULL) {
  556.         notifier.firstFileHandlerPtr = filePtr->nextPtr;
  557.     } else {
  558.         prevPtr->nextPtr = filePtr->nextPtr;
  559.     }
  560.     ckfree((char *) filePtr);
  561. }
  562.  
  563. /*
  564.  *----------------------------------------------------------------------
  565.  *
  566.  * FileHandlerEventProc --
  567.  *
  568.  *      This procedure is called by Tcl_ServiceEvent when a file event
  569.  *      reaches the front of the event queue.  This procedure is
  570.  *      responsible for actually handling the event by invoking the
  571.  *      callback for the file handler.
  572.  *
  573.  * Results:
  574.  *      Returns 1 if the event was handled, meaning it should be removed
  575.  *      from the queue.  Returns 0 if the event was not handled, meaning
  576.  *      it should stay on the queue.  The only time the event isn't
  577.  *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
  578.  *
  579.  * Side effects:
  580.  *      Whatever the file handler's callback procedure does.
  581.  *
  582.  *----------------------------------------------------------------------
  583.  */
  584.  
  585. static int
  586. FileHandlerEventProc(evPtr, flags)
  587.     Tcl_Event *evPtr;           /* Event to service. */
  588.     int flags;                  /* Flags that indicate what events to
  589.                                  * handle, such as TCL_FILE_EVENTS. */
  590. {
  591.     FileHandler *filePtr;
  592.     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
  593.     int mask;
  594.  
  595. #ifdef VERBOSE
  596.     printf("FileHandlerEventProc\n");
  597. #endif
  598.     if (!(flags & TCL_FILE_EVENTS)) {
  599.         return 0;
  600.     }
  601.  
  602.     /*
  603.      * Search through the file handlers to find the one whose handle matches
  604.      * the event.  We do this rather than keeping a pointer to the file
  605.      * handler directly in the event, so that the handler can be deleted
  606.      * while the event is queued without leaving a dangling pointer.
  607.      */
  608.  
  609.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  610.             filePtr = filePtr->nextPtr) {
  611.         if (filePtr->fd != fileEvPtr->fd) {
  612.             continue;
  613.         }
  614.  
  615.         /*
  616.          * The code is tricky for two reasons:
  617.          * 1. The file handler's desired events could have changed
  618.          *    since the time when the event was queued, so AND the
  619.          *    ready mask with the desired mask.
  620.          * 2. The file could have been closed and re-opened since
  621.          *    the time when the event was queued.  This is why the
  622.          *    ready mask is stored in the file handler rather than
  623.          *    the queued event:  it will be zeroed when a new
  624.          *    file handler is created for the newly opened file.
  625.          */
  626.  
  627.         mask = filePtr->readyMask & filePtr->mask;
  628.         filePtr->readyMask = 0;
  629.         if (mask != 0) {
  630.             (*filePtr->proc)(filePtr->clientData, mask);
  631.         }
  632.         break;
  633.     }
  634.     return 1;
  635. }
  636.  
  637. /*
  638.  *----------------------------------------------------------------------
  639.  *
  640.  * Tcl_WaitForEvent --
  641.  *
  642.  *      This function is called by Tcl_DoOneEvent to wait for new
  643.  *      events on the message queue.  If the block time is 0, then
  644.  *      Tcl_WaitForEvent just polls the event queue without blocking.
  645.  *
  646.  * Results:
  647.  *      Returns -1 if the select would block forever, otherwise
  648.  *      returns 0.
  649.  *
  650.  * Side effects:
  651.  *      Queues file events that are detected by the select.
  652.  *
  653.  *----------------------------------------------------------------------
  654.  */
  655.  
  656. int
  657. Tcl_WaitForEvent(
  658.     Tcl_Time *timePtr)        /* Maximum block time, or NULL. */
  659. {
  660.     QMSG msg;
  661. #ifdef 0
  662.     int foundEvent = 1;
  663. #endif
  664.     FileHandler *filePtr;
  665.     FileHandlerEvent *fileEvPtr;
  666.     struct timeval selectTimeout;
  667.     ULONG timeout;
  668.     int mask, numFound;
  669.  
  670. #ifdef VERBOSE
  671.     printf("Tcl_WaitForEvent\n");
  672. #endif
  673.     if (!initialized) {
  674. #ifdef VERBOSE
  675.         printf("InitNotifier being called by Tcl_WaitForEvent\n");
  676. #endif
  677.         InitNotifier();
  678.     }
  679. #ifdef VERBOSE
  680.     printf("Tcl_WaitForEvent, timePtr %s\n", timePtr == NULL ? "NULL"
  681.                                                              : "non-NULL");
  682.     fflush(stdout);
  683. #endif
  684.  
  685.     /*
  686.      * Only use the interval timer for non-zero timeouts.  This avoids
  687.      * generating useless messages when we really just want to poll.
  688.      */
  689.  
  690.     if (timePtr) {
  691.         timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
  692.         selectTimeout.tv_sec = timePtr->sec;
  693.         selectTimeout.tv_usec = timePtr->usec;
  694.     } else {
  695.         timeout = 0;
  696.         selectTimeout.tv_sec = 0;
  697.         selectTimeout.tv_usec = 0;
  698.     }
  699.     UpdateTimer(timeout);
  700.  
  701.     /*
  702.      * If we're not using PM, then select() will handle the timeout for us.
  703.      * If we're using PM then we have to check the message queue, timing out
  704.      * when nothing is there if Tcl specified so. In that case, after timing
  705.      * out we have to set the timeout for select() to 0 to prevent waiting
  706.      * twice.
  707.      */
  708.     if (usePm) {
  709.         if (!timePtr || (timeout != 0) ||
  710.             WinPeekMsg(TclOS2GetHAB(), &msg, NULLHANDLE, 0, 0, PM_NOREMOVE)) {
  711.             if (!WinGetMsg(TclOS2GetHAB(), &msg, NULLHANDLE, 0, 0)) {
  712.                 /* The application is exiting */
  713. /*
  714.                 if (msg.msg == WM_QUIT) {
  715. #ifdef VERBOSE
  716.                     printf("WinPostQueueMsg WM_QUIT (would be Tcl_Exit(0)\n");
  717.                     fflush(stdout);
  718. #endif
  719.                     WinPostQueueMsg(msg.hwnd, WM_QUIT, 0, 0);
  720.                     return -1;
  721. */
  722.                     Tcl_Exit(0);
  723. /*
  724.                 }
  725. */
  726.             }
  727.             /*
  728.              * Handle timer expiration as a special case so we don't
  729.              * claim to be doing work when we aren't.
  730.              */
  731. #ifdef VERBOSE
  732.             if (msg.msg == WM_TIMER) {
  733.                 printf("Tcl_WaitForEvent, WM_TIMER hwnd %x\n", msg.hwnd);
  734.                 fflush(stdout);
  735.             }
  736. #endif
  737.             if (msg.msg == WM_TIMER && msg.hwnd == notifier.hwnd) {
  738.                 return 0;
  739.             }
  740.  
  741.             WinDispatchMsg(TclOS2GetHAB(), &msg);
  742.             return 1;
  743.         }
  744.         /*
  745.          * We've had any timeout already, so if there's nothing to look for
  746.          * just return TCL_OK
  747.          */
  748.         if (notifier.numFdBits == 0) {
  749. #ifdef VERBOSE
  750.             printf("Tcl_WaitForEvent returning 0 after Win*Msg\n");
  751.             fflush(stdout);
  752. #endif
  753.             return 0;
  754.         }
  755.         /* Set timeout to 0 to prevent waiting twice */
  756.         selectTimeout.tv_sec = 0;
  757.         selectTimeout.tv_usec = 0;
  758.     }
  759.  
  760. /*
  761.     if (notifier.numFdBits == 0) {
  762.         return 0;
  763.     }
  764. */
  765.  
  766. #ifdef VERBOSE
  767.     printf("Tcl_WaitForEvent: calling select with timeout %d:%d\n",
  768.            selectTimeout.tv_sec, selectTimeout.tv_usec);
  769.     fflush(stdout);
  770. #endif
  771.     memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
  772.             3*sizeof(fd_set));
  773.     numFound = select(notifier.numFdBits, ¬ifier.readyMasks[0],
  774.                       ¬ifier.readyMasks[1], ¬ifier.readyMasks[2],
  775.                       &selectTimeout);
  776. #ifdef VERBOSE
  777.     printf("Tcl_WaitForEvent: select found %d with %d\n", numFound,
  778.            notifier.numFdBits);
  779.     fflush(stdout);
  780. #endif
  781.  
  782.     /*
  783.      * Some systems don't clear the masks after an error, so
  784.      * we have to do it here.
  785.      */
  786.  
  787.     if (numFound == -1) {
  788.         memset((VOID *) notifier.readyMasks, 0, 3*sizeof(fd_set));
  789.     }
  790.  
  791.     /*
  792.      * Queue all detected file events before returning.
  793.      */
  794.  
  795.     for (filePtr = notifier.firstFileHandlerPtr;
  796.             (filePtr != NULL) && (numFound > 0);
  797.             filePtr = filePtr->nextPtr) {
  798.         mask = 0;
  799.  
  800.         if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[0])) {
  801.             mask |= TCL_READABLE;
  802.         }
  803.         if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[1])) {
  804.             mask |= TCL_WRITABLE;
  805.         }
  806.         if (FD_ISSET(filePtr->fd, ¬ifier.readyMasks[2])) {
  807.             mask |= TCL_EXCEPTION;
  808.         }
  809.  
  810.         if (!mask) {
  811.             continue;
  812.         } else {
  813.             numFound--;
  814.         }
  815.  
  816.         /*
  817.          * Don't bother to queue an event if the mask was previously
  818.          * non-zero since an event must still be on the queue.
  819.          */
  820.  
  821.         if (filePtr->readyMask == 0) {
  822.             fileEvPtr = (FileHandlerEvent *) ckalloc(
  823.                 sizeof(FileHandlerEvent));
  824.             fileEvPtr->header.proc = FileHandlerEventProc;
  825.             fileEvPtr->fd = filePtr->fd;
  826.             Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
  827.         }
  828.         filePtr->readyMask = mask;
  829.     }
  830.     return 0;
  831. }
  832.  
  833. /*
  834.  *----------------------------------------------------------------------
  835.  *
  836.  * Tcl_Sleep --
  837.  *
  838.  *    Delay execution for the specified number of milliseconds.
  839.  *
  840.  * Results:
  841.  *    None.
  842.  *
  843.  * Side effects:
  844.  *    Time passes.
  845.  *
  846.  *----------------------------------------------------------------------
  847.  */
  848.  
  849. void
  850. Tcl_Sleep(ms)
  851.     int ms;            /* Number of milliseconds to sleep. */
  852. {
  853. #ifdef VERBOSE
  854.     printf("Tcl_Sleep %dms\n", ms);
  855. #endif
  856.     DosSleep(ms);
  857. }
  858.