| 1 | /* | 
|---|
| 2 |  * tclWinNotify.c -- | 
|---|
| 3 |  * | 
|---|
| 4 |  *      This file contains Windows-specific procedures for the notifier, which | 
|---|
| 5 |  *      is the lowest-level part of the Tcl event loop. This file works | 
|---|
| 6 |  *      together with ../generic/tclNotify.c. | 
|---|
| 7 |  * | 
|---|
| 8 |  * Copyright (c) 1995-1997 Sun Microsystems, Inc. | 
|---|
| 9 |  * | 
|---|
| 10 |  * See the file "license.terms" for information on usage and redistribution of | 
|---|
| 11 |  * this file, and for a DISCLAIMER OF ALL WARRANTIES. | 
|---|
| 12 |  * | 
|---|
| 13 |  * RCS: @(#) $Id: tclWinNotify.c,v 1.21 2005/11/04 00:06:50 dkf Exp $ | 
|---|
| 14 |  */ | 
|---|
| 15 |  | 
|---|
| 16 | #include "tclInt.h" | 
|---|
| 17 |  | 
|---|
| 18 | /* | 
|---|
| 19 |  * The follwing static indicates whether this module has been initialized. | 
|---|
| 20 |  */ | 
|---|
| 21 |  | 
|---|
| 22 | #define INTERVAL_TIMER  1       /* Handle of interval timer. */ | 
|---|
| 23 |  | 
|---|
| 24 | #define WM_WAKEUP       WM_USER /* Message that is send by | 
|---|
| 25 |                                  * Tcl_AlertNotifier. */ | 
|---|
| 26 | /* | 
|---|
| 27 |  * The following static structure contains the state information for the | 
|---|
| 28 |  * Windows implementation of the Tcl notifier. One of these structures is | 
|---|
| 29 |  * created for each thread that is using the notifier. | 
|---|
| 30 |  */ | 
|---|
| 31 |  | 
|---|
| 32 | typedef struct ThreadSpecificData { | 
|---|
| 33 |     CRITICAL_SECTION crit;      /* Monitor for this notifier. */ | 
|---|
| 34 |     DWORD thread;               /* Identifier for thread associated with this | 
|---|
| 35 |                                  * notifier. */ | 
|---|
| 36 |     HANDLE event;               /* Event object used to wake up the notifier | 
|---|
| 37 |                                  * thread. */ | 
|---|
| 38 |     int pending;                /* Alert message pending, this field is locked | 
|---|
| 39 |                                  * by the notifierMutex. */ | 
|---|
| 40 |     HWND hwnd;                  /* Messaging window. */ | 
|---|
| 41 |     int timeout;                /* Current timeout value. */ | 
|---|
| 42 |     int timerActive;            /* 1 if interval timer is running. */ | 
|---|
| 43 | } ThreadSpecificData; | 
|---|
| 44 |  | 
|---|
| 45 | static Tcl_ThreadDataKey dataKey; | 
|---|
| 46 |  | 
|---|
| 47 | extern TclStubs tclStubs; | 
|---|
| 48 | extern Tcl_NotifierProcs tclOriginalNotifier; | 
|---|
| 49 |  | 
|---|
| 50 | /* | 
|---|
| 51 |  * The following static indicates the number of threads that have initialized | 
|---|
| 52 |  * notifiers. It controls the lifetime of the TclNotifier window class. | 
|---|
| 53 |  * | 
|---|
| 54 |  * You must hold the notifierMutex lock before accessing this variable. | 
|---|
| 55 |  */ | 
|---|
| 56 |  | 
|---|
| 57 | static int notifierCount = 0; | 
|---|
| 58 | TCL_DECLARE_MUTEX(notifierMutex) | 
|---|
| 59 |  | 
|---|
| 60 | /* | 
|---|
| 61 |  * Static routines defined in this file. | 
|---|
| 62 |  */ | 
|---|
| 63 |  | 
|---|
| 64 | static LRESULT CALLBACK         NotifierProc(HWND hwnd, UINT message, | 
|---|
| 65 |                                     WPARAM wParam, LPARAM lParam); | 
|---|
| 66 |  | 
|---|
| 67 | /* | 
|---|
| 68 |  *---------------------------------------------------------------------- | 
|---|
| 69 |  * | 
|---|
| 70 |  * Tcl_InitNotifier -- | 
|---|
| 71 |  * | 
|---|
| 72 |  *      Initializes the platform specific notifier state. | 
|---|
| 73 |  * | 
|---|
| 74 |  * Results: | 
|---|
| 75 |  *      Returns a handle to the notifier state for this thread.. | 
|---|
| 76 |  * | 
|---|
| 77 |  * Side effects: | 
|---|
| 78 |  *      None. | 
|---|
| 79 |  * | 
|---|
| 80 |  *---------------------------------------------------------------------- | 
|---|
| 81 |  */ | 
|---|
| 82 |  | 
|---|
| 83 | ClientData | 
|---|
| 84 | Tcl_InitNotifier(void) | 
|---|
| 85 | { | 
|---|
| 86 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 87 |     WNDCLASS class; | 
|---|
| 88 |  | 
|---|
| 89 |     /* | 
|---|
| 90 |      * Register Notifier window class if this is the first thread to use this | 
|---|
| 91 |      * module. | 
|---|
| 92 |      */ | 
|---|
| 93 |  | 
|---|
| 94 |     Tcl_MutexLock(¬ifierMutex); | 
|---|
| 95 |     if (notifierCount == 0) { | 
|---|
| 96 |         class.style = 0; | 
|---|
| 97 |         class.cbClsExtra = 0; | 
|---|
| 98 |         class.cbWndExtra = 0; | 
|---|
| 99 |         class.hInstance = TclWinGetTclInstance(); | 
|---|
| 100 |         class.hbrBackground = NULL; | 
|---|
| 101 |         class.lpszMenuName = NULL; | 
|---|
| 102 |         class.lpszClassName = "TclNotifier"; | 
|---|
| 103 |         class.lpfnWndProc = NotifierProc; | 
|---|
| 104 |         class.hIcon = NULL; | 
|---|
| 105 |         class.hCursor = NULL; | 
|---|
| 106 |  | 
|---|
| 107 |         if (!RegisterClassA(&class)) { | 
|---|
| 108 |             Tcl_Panic("Unable to register TclNotifier window class"); | 
|---|
| 109 |         } | 
|---|
| 110 |     } | 
|---|
| 111 |     notifierCount++; | 
|---|
| 112 |     Tcl_MutexUnlock(¬ifierMutex); | 
|---|
| 113 |  | 
|---|
| 114 |     tsdPtr->pending = 0; | 
|---|
| 115 |     tsdPtr->timerActive = 0; | 
|---|
| 116 |  | 
|---|
| 117 |     InitializeCriticalSection(&tsdPtr->crit); | 
|---|
| 118 |  | 
|---|
| 119 |     tsdPtr->hwnd = NULL; | 
|---|
| 120 |     tsdPtr->thread = GetCurrentThreadId(); | 
|---|
| 121 |     tsdPtr->event = CreateEvent(NULL, TRUE /* manual */, | 
|---|
| 122 |             FALSE /* !signaled */, NULL); | 
|---|
| 123 |  | 
|---|
| 124 |     return (ClientData) tsdPtr; | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | /* | 
|---|
| 128 |  *---------------------------------------------------------------------- | 
|---|
| 129 |  * | 
|---|
| 130 |  * Tcl_FinalizeNotifier -- | 
|---|
| 131 |  * | 
|---|
| 132 |  *      This function is called to cleanup the notifier state before a thread | 
|---|
| 133 |  *      is terminated. | 
|---|
| 134 |  * | 
|---|
| 135 |  * Results: | 
|---|
| 136 |  *      None. | 
|---|
| 137 |  * | 
|---|
| 138 |  * Side effects: | 
|---|
| 139 |  *      May dispose of the notifier window and class. | 
|---|
| 140 |  * | 
|---|
| 141 |  *---------------------------------------------------------------------- | 
|---|
| 142 |  */ | 
|---|
| 143 |  | 
|---|
| 144 | void | 
|---|
| 145 | Tcl_FinalizeNotifier( | 
|---|
| 146 |     ClientData clientData)      /* Pointer to notifier data. */ | 
|---|
| 147 | { | 
|---|
| 148 |     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; | 
|---|
| 149 |  | 
|---|
| 150 |     /* | 
|---|
| 151 |      * Only finalize the notifier if a notifier was installed in the current | 
|---|
| 152 |      * thread; there is a route in which this is not guaranteed to be true | 
|---|
| 153 |      * (when tclWin32Dll.c:DllMain() is called with the flag | 
|---|
| 154 |      * DLL_PROCESS_DETACH by the OS, which could be doing so from a thread | 
|---|
| 155 |      * that's never previously been involved with Tcl, e.g. the task manager) | 
|---|
| 156 |      * so this check is important. | 
|---|
| 157 |      * | 
|---|
| 158 |      * Fixes Bug #217982 reported by Hugh Vu and Gene Leache. | 
|---|
| 159 |      */ | 
|---|
| 160 |  | 
|---|
| 161 |     if (tsdPtr == NULL) { | 
|---|
| 162 |         return; | 
|---|
| 163 |     } | 
|---|
| 164 |  | 
|---|
| 165 |     DeleteCriticalSection(&tsdPtr->crit); | 
|---|
| 166 |     CloseHandle(tsdPtr->event); | 
|---|
| 167 |  | 
|---|
| 168 |     /* | 
|---|
| 169 |      * Clean up the timer and messaging window for this thread. | 
|---|
| 170 |      */ | 
|---|
| 171 |  | 
|---|
| 172 |     if (tsdPtr->hwnd) { | 
|---|
| 173 |         KillTimer(tsdPtr->hwnd, INTERVAL_TIMER); | 
|---|
| 174 |         DestroyWindow(tsdPtr->hwnd); | 
|---|
| 175 |     } | 
|---|
| 176 |  | 
|---|
| 177 |     /* | 
|---|
| 178 |      * If this is the last thread to use the notifier, unregister the notifier | 
|---|
| 179 |      * window class. | 
|---|
| 180 |      */ | 
|---|
| 181 |  | 
|---|
| 182 |     Tcl_MutexLock(¬ifierMutex); | 
|---|
| 183 |     notifierCount--; | 
|---|
| 184 |     if (notifierCount == 0) { | 
|---|
| 185 |         UnregisterClassA("TclNotifier", TclWinGetTclInstance()); | 
|---|
| 186 |     } | 
|---|
| 187 |     Tcl_MutexUnlock(¬ifierMutex); | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | /* | 
|---|
| 191 |  *---------------------------------------------------------------------- | 
|---|
| 192 |  * | 
|---|
| 193 |  * Tcl_AlertNotifier -- | 
|---|
| 194 |  * | 
|---|
| 195 |  *      Wake up the specified notifier from any thread. This routine is called | 
|---|
| 196 |  *      by the platform independent notifier code whenever the Tcl_ThreadAlert | 
|---|
| 197 |  *      routine is called. This routine is guaranteed not to be called on a | 
|---|
| 198 |  *      given notifier after Tcl_FinalizeNotifier is called for that notifier. | 
|---|
| 199 |  *      This routine is typically called from a thread other than the | 
|---|
| 200 |  *      notifier's thread. | 
|---|
| 201 |  * | 
|---|
| 202 |  * Results: | 
|---|
| 203 |  *      None. | 
|---|
| 204 |  * | 
|---|
| 205 |  * Side effects: | 
|---|
| 206 |  *      Sends a message to the messaging window for the notifier if there | 
|---|
| 207 |  *      isn't already one pending. | 
|---|
| 208 |  * | 
|---|
| 209 |  *---------------------------------------------------------------------- | 
|---|
| 210 |  */ | 
|---|
| 211 |  | 
|---|
| 212 | void | 
|---|
| 213 | Tcl_AlertNotifier( | 
|---|
| 214 |     ClientData clientData)      /* Pointer to thread data. */ | 
|---|
| 215 | { | 
|---|
| 216 |     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; | 
|---|
| 217 |  | 
|---|
| 218 |     /* | 
|---|
| 219 |      * Note that we do not need to lock around access to the hwnd because the | 
|---|
| 220 |      * race condition has no effect since any race condition implies that the | 
|---|
| 221 |      * notifier thread is already awake. | 
|---|
| 222 |      */ | 
|---|
| 223 |  | 
|---|
| 224 |     if (tsdPtr->hwnd) { | 
|---|
| 225 |         /* | 
|---|
| 226 |          * We do need to lock around access to the pending flag. | 
|---|
| 227 |          */ | 
|---|
| 228 |  | 
|---|
| 229 |         EnterCriticalSection(&tsdPtr->crit); | 
|---|
| 230 |         if (!tsdPtr->pending) { | 
|---|
| 231 |             PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0); | 
|---|
| 232 |         } | 
|---|
| 233 |         tsdPtr->pending = 1; | 
|---|
| 234 |         LeaveCriticalSection(&tsdPtr->crit); | 
|---|
| 235 |     } else { | 
|---|
| 236 |         SetEvent(tsdPtr->event); | 
|---|
| 237 |     } | 
|---|
| 238 | } | 
|---|
| 239 |  | 
|---|
| 240 | /* | 
|---|
| 241 |  *---------------------------------------------------------------------- | 
|---|
| 242 |  * | 
|---|
| 243 |  * Tcl_SetTimer -- | 
|---|
| 244 |  * | 
|---|
| 245 |  *      This procedure sets the current notifier timer value. The notifier | 
|---|
| 246 |  *      will ensure that Tcl_ServiceAll() is called after the specified | 
|---|
| 247 |  *      interval, even if no events have occurred. | 
|---|
| 248 |  * | 
|---|
| 249 |  * Results: | 
|---|
| 250 |  *      None. | 
|---|
| 251 |  * | 
|---|
| 252 |  * Side effects: | 
|---|
| 253 |  *      Replaces any previous timer. | 
|---|
| 254 |  * | 
|---|
| 255 |  *---------------------------------------------------------------------- | 
|---|
| 256 |  */ | 
|---|
| 257 |  | 
|---|
| 258 | void | 
|---|
| 259 | Tcl_SetTimer( | 
|---|
| 260 |     Tcl_Time *timePtr)          /* Maximum block time, or NULL. */ | 
|---|
| 261 | { | 
|---|
| 262 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 263 |     UINT timeout; | 
|---|
| 264 |  | 
|---|
| 265 |     /* | 
|---|
| 266 |      * Allow the notifier to be hooked. This may not make sense on Windows, | 
|---|
| 267 |      * but mirrors the UNIX hook. | 
|---|
| 268 |      */ | 
|---|
| 269 |  | 
|---|
| 270 |     if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { | 
|---|
| 271 |         tclStubs.tcl_SetTimer(timePtr); | 
|---|
| 272 |         return; | 
|---|
| 273 |     } | 
|---|
| 274 |  | 
|---|
| 275 |     /* | 
|---|
| 276 |      * We only need to set up an interval timer if we're being called from an | 
|---|
| 277 |      * external event loop. If we don't have a window handle then we just | 
|---|
| 278 |      * return immediately and let Tcl_WaitForEvent handle timeouts. | 
|---|
| 279 |      */ | 
|---|
| 280 |  | 
|---|
| 281 |     if (!tsdPtr->hwnd) { | 
|---|
| 282 |         return; | 
|---|
| 283 |     } | 
|---|
| 284 |  | 
|---|
| 285 |     if (!timePtr) { | 
|---|
| 286 |         timeout = 0; | 
|---|
| 287 |     } else { | 
|---|
| 288 |         /* | 
|---|
| 289 |          * Make sure we pass a non-zero value into the timeout argument. | 
|---|
| 290 |          * Windows seems to get confused by zero length timers. | 
|---|
| 291 |          */ | 
|---|
| 292 |  | 
|---|
| 293 |         timeout = timePtr->sec * 1000 + timePtr->usec / 1000; | 
|---|
| 294 |         if (timeout == 0) { | 
|---|
| 295 |             timeout = 1; | 
|---|
| 296 |         } | 
|---|
| 297 |     } | 
|---|
| 298 |     tsdPtr->timeout = timeout; | 
|---|
| 299 |     if (timeout != 0) { | 
|---|
| 300 |         tsdPtr->timerActive = 1; | 
|---|
| 301 |         SetTimer(tsdPtr->hwnd, INTERVAL_TIMER, (unsigned long) tsdPtr->timeout, | 
|---|
| 302 |                 NULL); | 
|---|
| 303 |     } else { | 
|---|
| 304 |         tsdPtr->timerActive = 0; | 
|---|
| 305 |         KillTimer(tsdPtr->hwnd, INTERVAL_TIMER); | 
|---|
| 306 |     } | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|
| 309 | /* | 
|---|
| 310 |  *---------------------------------------------------------------------- | 
|---|
| 311 |  * | 
|---|
| 312 |  * Tcl_ServiceModeHook -- | 
|---|
| 313 |  * | 
|---|
| 314 |  *      This function is invoked whenever the service mode changes. | 
|---|
| 315 |  * | 
|---|
| 316 |  * Results: | 
|---|
| 317 |  *      None. | 
|---|
| 318 |  * | 
|---|
| 319 |  * Side effects: | 
|---|
| 320 |  *      If this is the first time the notifier is set into TCL_SERVICE_ALL, | 
|---|
| 321 |  *      then the communication window is created. | 
|---|
| 322 |  * | 
|---|
| 323 |  *---------------------------------------------------------------------- | 
|---|
| 324 |  */ | 
|---|
| 325 |  | 
|---|
| 326 | void | 
|---|
| 327 | Tcl_ServiceModeHook( | 
|---|
| 328 |     int mode)                   /* Either TCL_SERVICE_ALL, or | 
|---|
| 329 |                                  * TCL_SERVICE_NONE. */ | 
|---|
| 330 | { | 
|---|
| 331 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 332 |  | 
|---|
| 333 |     /* | 
|---|
| 334 |      * If this is the first time that the notifier has been used from a modal | 
|---|
| 335 |      * loop, then create a communication window. Note that after this point, | 
|---|
| 336 |      * the application needs to service events in a timely fashion or Windows | 
|---|
| 337 |      * will hang waiting for the window to respond to synchronous system | 
|---|
| 338 |      * messages. At some point, we may want to consider destroying the window | 
|---|
| 339 |      * if we leave the modal loop, but for now we'll leave it around. | 
|---|
| 340 |      */ | 
|---|
| 341 |  | 
|---|
| 342 |     if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) { | 
|---|
| 343 |         tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier", WS_TILED, | 
|---|
| 344 |                 0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL); | 
|---|
| 345 |  | 
|---|
| 346 |         /* | 
|---|
| 347 |          * Send an initial message to the window to ensure that we wake up the | 
|---|
| 348 |          * notifier once we get into the modal loop. This will force the | 
|---|
| 349 |          * notifier to recompute the timeout value and schedule a timer if one | 
|---|
| 350 |          * is needed. | 
|---|
| 351 |          */ | 
|---|
| 352 |  | 
|---|
| 353 |         Tcl_AlertNotifier((ClientData)tsdPtr); | 
|---|
| 354 |     } | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | /* | 
|---|
| 358 |  *---------------------------------------------------------------------- | 
|---|
| 359 |  * | 
|---|
| 360 |  * NotifierProc -- | 
|---|
| 361 |  * | 
|---|
| 362 |  *      This procedure is invoked by Windows to process events on the notifier | 
|---|
| 363 |  *      window. Messages will be sent to this window in response to external | 
|---|
| 364 |  *      timer events or calls to TclpAlertTsdPtr-> | 
|---|
| 365 |  * | 
|---|
| 366 |  * Results: | 
|---|
| 367 |  *      A standard windows result. | 
|---|
| 368 |  * | 
|---|
| 369 |  * Side effects: | 
|---|
| 370 |  *      Services any pending events. | 
|---|
| 371 |  * | 
|---|
| 372 |  *---------------------------------------------------------------------- | 
|---|
| 373 |  */ | 
|---|
| 374 |  | 
|---|
| 375 | static LRESULT CALLBACK | 
|---|
| 376 | NotifierProc( | 
|---|
| 377 |     HWND hwnd,                  /* Passed on... */ | 
|---|
| 378 |     UINT message,               /* What messsage is this? */ | 
|---|
| 379 |     WPARAM wParam,              /* Passed on... */ | 
|---|
| 380 |     LPARAM lParam)              /* Passed on... */ | 
|---|
| 381 | { | 
|---|
| 382 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 383 |  | 
|---|
| 384 |     if (message == WM_WAKEUP) { | 
|---|
| 385 |         EnterCriticalSection(&tsdPtr->crit); | 
|---|
| 386 |         tsdPtr->pending = 0; | 
|---|
| 387 |         LeaveCriticalSection(&tsdPtr->crit); | 
|---|
| 388 |     } else if (message != WM_TIMER) { | 
|---|
| 389 |         return DefWindowProc(hwnd, message, wParam, lParam); | 
|---|
| 390 |     } | 
|---|
| 391 |  | 
|---|
| 392 |     /* | 
|---|
| 393 |      * Process all of the runnable events. | 
|---|
| 394 |      */ | 
|---|
| 395 |  | 
|---|
| 396 |     Tcl_ServiceAll(); | 
|---|
| 397 |     return 0; | 
|---|
| 398 | } | 
|---|
| 399 |  | 
|---|
| 400 | /* | 
|---|
| 401 |  *---------------------------------------------------------------------- | 
|---|
| 402 |  * | 
|---|
| 403 |  * Tcl_WaitForEvent -- | 
|---|
| 404 |  * | 
|---|
| 405 |  *      This function is called by Tcl_DoOneEvent to wait for new events on | 
|---|
| 406 |  *      the message queue. If the block time is 0, then Tcl_WaitForEvent just | 
|---|
| 407 |  *      polls the event queue without blocking. | 
|---|
| 408 |  * | 
|---|
| 409 |  * Results: | 
|---|
| 410 |  *      Returns -1 if a WM_QUIT message is detected, returns 1 if a message | 
|---|
| 411 |  *      was dispatched, otherwise returns 0. | 
|---|
| 412 |  * | 
|---|
| 413 |  * Side effects: | 
|---|
| 414 |  *      Dispatches a message to a window procedure, which could do anything. | 
|---|
| 415 |  * | 
|---|
| 416 |  *---------------------------------------------------------------------- | 
|---|
| 417 |  */ | 
|---|
| 418 |  | 
|---|
| 419 | int | 
|---|
| 420 | Tcl_WaitForEvent( | 
|---|
| 421 |     Tcl_Time *timePtr)          /* Maximum block time, or NULL. */ | 
|---|
| 422 | { | 
|---|
| 423 |     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); | 
|---|
| 424 |     MSG msg; | 
|---|
| 425 |     DWORD timeout, result; | 
|---|
| 426 |     int status; | 
|---|
| 427 |  | 
|---|
| 428 |     /* | 
|---|
| 429 |      * Allow the notifier to be hooked. This may not make sense on windows, | 
|---|
| 430 |      * but mirrors the UNIX hook. | 
|---|
| 431 |      */ | 
|---|
| 432 |  | 
|---|
| 433 |     if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { | 
|---|
| 434 |         return tclStubs.tcl_WaitForEvent(timePtr); | 
|---|
| 435 |     } | 
|---|
| 436 |  | 
|---|
| 437 |     /* | 
|---|
| 438 |      * Compute the timeout in milliseconds. | 
|---|
| 439 |      */ | 
|---|
| 440 |  | 
|---|
| 441 |     if (timePtr) { | 
|---|
| 442 |         /* | 
|---|
| 443 |          * TIP #233 (Virtualized Time). Convert virtual domain delay to | 
|---|
| 444 |          * real-time. | 
|---|
| 445 |          */ | 
|---|
| 446 |  | 
|---|
| 447 |         Tcl_Time myTime; | 
|---|
| 448 |  | 
|---|
| 449 |         myTime.sec  = timePtr->sec; | 
|---|
| 450 |         myTime.usec = timePtr->usec; | 
|---|
| 451 |  | 
|---|
| 452 |         if (myTime.sec != 0 || myTime.usec != 0) { | 
|---|
| 453 |             (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData); | 
|---|
| 454 |         } | 
|---|
| 455 |  | 
|---|
| 456 |         timeout = myTime.sec * 1000 + myTime.usec / 1000; | 
|---|
| 457 |     } else { | 
|---|
| 458 |         timeout = INFINITE; | 
|---|
| 459 |     } | 
|---|
| 460 |  | 
|---|
| 461 |     /* | 
|---|
| 462 |      * Check to see if there are any messages in the queue before waiting | 
|---|
| 463 |      * because MsgWaitForMultipleObjects will not wake up if there are events | 
|---|
| 464 |      * currently sitting in the queue. | 
|---|
| 465 |      */ | 
|---|
| 466 |  | 
|---|
| 467 |     if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { | 
|---|
| 468 |         /* | 
|---|
| 469 |          * Wait for something to happen (a signal from another thread, a | 
|---|
| 470 |          * message, or timeout) or loop servicing asynchronous procedure calls | 
|---|
| 471 |          * queued to this thread. | 
|---|
| 472 |          */ | 
|---|
| 473 |  | 
|---|
| 474 |     again: | 
|---|
| 475 |         result = MsgWaitForMultipleObjectsEx(1, &tsdPtr->event, timeout, | 
|---|
| 476 |                 QS_ALLINPUT, MWMO_ALERTABLE); | 
|---|
| 477 |         if (result == WAIT_IO_COMPLETION) { | 
|---|
| 478 |             goto again; | 
|---|
| 479 |         } else if (result == WAIT_FAILED) { | 
|---|
| 480 |             status = -1; | 
|---|
| 481 |             goto end; | 
|---|
| 482 |         } | 
|---|
| 483 |     } | 
|---|
| 484 |  | 
|---|
| 485 |     /* | 
|---|
| 486 |      * Check to see if there are any messages to process. | 
|---|
| 487 |      */ | 
|---|
| 488 |  | 
|---|
| 489 |     if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { | 
|---|
| 490 |         /* | 
|---|
| 491 |          * Retrieve and dispatch the first message. | 
|---|
| 492 |          */ | 
|---|
| 493 |  | 
|---|
| 494 |         result = GetMessage(&msg, NULL, 0, 0); | 
|---|
| 495 |         if (result == 0) { | 
|---|
| 496 |             /* | 
|---|
| 497 |              * We received a request to exit this thread (WM_QUIT), so | 
|---|
| 498 |              * propagate the quit message and start unwinding. | 
|---|
| 499 |              */ | 
|---|
| 500 |  | 
|---|
| 501 |             PostQuitMessage((int) msg.wParam); | 
|---|
| 502 |             status = -1; | 
|---|
| 503 |         } else if (result == -1) { | 
|---|
| 504 |             /* | 
|---|
| 505 |              * We got an error from the system. I have no idea why this would | 
|---|
| 506 |              * happen, so we'll just unwind. | 
|---|
| 507 |              */ | 
|---|
| 508 |  | 
|---|
| 509 |             status = -1; | 
|---|
| 510 |         } else { | 
|---|
| 511 |             TranslateMessage(&msg); | 
|---|
| 512 |             DispatchMessage(&msg); | 
|---|
| 513 |             status = 1; | 
|---|
| 514 |         } | 
|---|
| 515 |     } else { | 
|---|
| 516 |         status = 0; | 
|---|
| 517 |     } | 
|---|
| 518 |  | 
|---|
| 519 |   end: | 
|---|
| 520 |     ResetEvent(tsdPtr->event); | 
|---|
| 521 |     return status; | 
|---|
| 522 | } | 
|---|
| 523 |  | 
|---|
| 524 | /* | 
|---|
| 525 |  *---------------------------------------------------------------------- | 
|---|
| 526 |  * | 
|---|
| 527 |  * Tcl_Sleep -- | 
|---|
| 528 |  * | 
|---|
| 529 |  *      Delay execution for the specified number of milliseconds. | 
|---|
| 530 |  * | 
|---|
| 531 |  * Results: | 
|---|
| 532 |  *      None. | 
|---|
| 533 |  * | 
|---|
| 534 |  * Side effects: | 
|---|
| 535 |  *      Time passes. | 
|---|
| 536 |  * | 
|---|
| 537 |  *---------------------------------------------------------------------- | 
|---|
| 538 |  */ | 
|---|
| 539 |  | 
|---|
| 540 | void | 
|---|
| 541 | Tcl_Sleep( | 
|---|
| 542 |     int ms)                     /* Number of milliseconds to sleep. */ | 
|---|
| 543 | { | 
|---|
| 544 |     /* | 
|---|
| 545 |      * Simply calling 'Sleep' for the requisite number of milliseconds can | 
|---|
| 546 |      * make the process appear to wake up early because it isn't synchronized | 
|---|
| 547 |      * with the CPU performance counter that is used in tclWinTime.c. This | 
|---|
| 548 |      * behavior is probably benign, but messes up some of the corner cases in | 
|---|
| 549 |      * the test suite. We get around this problem by repeating the 'Sleep' | 
|---|
| 550 |      * call as many times as necessary to make the clock advance by the | 
|---|
| 551 |      * requisite amount. | 
|---|
| 552 |      */ | 
|---|
| 553 |  | 
|---|
| 554 |     Tcl_Time now;               /* Current wall clock time. */ | 
|---|
| 555 |     Tcl_Time desired;           /* Desired wakeup time. */ | 
|---|
| 556 |     Tcl_Time vdelay;            /* Time to sleep, for scaling virtual -> | 
|---|
| 557 |                                  * real. */ | 
|---|
| 558 |     DWORD sleepTime;            /* Time to sleep, real-time */ | 
|---|
| 559 |  | 
|---|
| 560 |     vdelay.sec  = ms / 1000; | 
|---|
| 561 |     vdelay.usec = (ms % 1000) * 1000; | 
|---|
| 562 |  | 
|---|
| 563 |     Tcl_GetTime(&now); | 
|---|
| 564 |     desired.sec  = now.sec  + vdelay.sec; | 
|---|
| 565 |     desired.usec = now.usec + vdelay.usec; | 
|---|
| 566 |     if (desired.usec > 1000000) { | 
|---|
| 567 |         ++desired.sec; | 
|---|
| 568 |         desired.usec -= 1000000; | 
|---|
| 569 |     } | 
|---|
| 570 |  | 
|---|
| 571 |     /* | 
|---|
| 572 |      * TIP #233: Scale delay from virtual to real-time. | 
|---|
| 573 |      */ | 
|---|
| 574 |  | 
|---|
| 575 |     (*tclScaleTimeProcPtr) (&vdelay, tclTimeClientData); | 
|---|
| 576 |     sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000; | 
|---|
| 577 |  | 
|---|
| 578 |     for (;;) { | 
|---|
| 579 |         Sleep(sleepTime); | 
|---|
| 580 |         Tcl_GetTime(&now); | 
|---|
| 581 |         if (now.sec > desired.sec) { | 
|---|
| 582 |             break; | 
|---|
| 583 |         } else if ((now.sec == desired.sec) && (now.usec >= desired.usec)) { | 
|---|
| 584 |             break; | 
|---|
| 585 |         } | 
|---|
| 586 |  | 
|---|
| 587 |         vdelay.sec  = desired.sec  - now.sec; | 
|---|
| 588 |         vdelay.usec = desired.usec - now.usec; | 
|---|
| 589 |  | 
|---|
| 590 |         (*tclScaleTimeProcPtr) (&vdelay, tclTimeClientData); | 
|---|
| 591 |         sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000; | 
|---|
| 592 |     } | 
|---|
| 593 | } | 
|---|
| 594 |  | 
|---|
| 595 | /* | 
|---|
| 596 |  * Local Variables: | 
|---|
| 597 |  * mode: c | 
|---|
| 598 |  * c-basic-offset: 4 | 
|---|
| 599 |  * fill-column: 78 | 
|---|
| 600 |  * End: | 
|---|
| 601 |  */ | 
|---|