| 1 |
|
|---|
| 2 | /*
|
|---|
| 3 | *@@sourcefile timer.c:
|
|---|
| 4 | * XTimers, which can be used to avoid excessive PM
|
|---|
| 5 | * timer usage.
|
|---|
| 6 | *
|
|---|
| 7 | * The functions here allow you to share a single PM
|
|---|
| 8 | * timer for many windows on the same thread. Basically,
|
|---|
| 9 | * these start a "master PM timer", to whose WM_TIMER
|
|---|
| 10 | * message your window procedure must respond by calling
|
|---|
| 11 | * tmrTimerTick, which will "distribute" the timer tick
|
|---|
| 12 | * to those XTimers which have elapsed.
|
|---|
| 13 | *
|
|---|
| 14 | * The main advantage of the XTimers is that these
|
|---|
| 15 | * are not a limited resource (as opposed to PM timers).
|
|---|
| 16 | * I don't know how many PM timers exist on the system,
|
|---|
| 17 | * but PMREF says that the no. of remaining timers can
|
|---|
| 18 | * be queried with WinQuerySysValue(SV_CTIMERS).
|
|---|
| 19 | *
|
|---|
| 20 | * XTimers are used excessively by the default widgets
|
|---|
| 21 | * of XWorkplace's XCenter.
|
|---|
| 22 | *
|
|---|
| 23 | * There are a few limitations with the XTimers though:
|
|---|
| 24 | *
|
|---|
| 25 | * -- tmrTimerTick (which you must call when the
|
|---|
| 26 | * "master" WM_TIMER comes in) does not post WM_TIMER
|
|---|
| 27 | * messages to the windows specified in the subtimers,
|
|---|
| 28 | * but calls the window procedure of the target window
|
|---|
| 29 | * directly. This makes sure that timers work even if
|
|---|
| 30 | * some thread is hogging the SIQ.
|
|---|
| 31 | *
|
|---|
| 32 | * This however requires that all XTimers must run on
|
|---|
| 33 | * the same thread as the owner window of the master
|
|---|
| 34 | * timer which was specified with tmrCreateSet.
|
|---|
| 35 | *
|
|---|
| 36 | * -- Queue timers (with HWND == NULLHANDLE) are not
|
|---|
| 37 | * supported.
|
|---|
| 38 | *
|
|---|
| 39 | * -- When a window is destroyed, its timers are not
|
|---|
| 40 | * automatically cleaned up. tmrTimerTick does
|
|---|
| 41 | * detect invalid windows and removes them from the
|
|---|
| 42 | * timers list before posting, but to be on the safe
|
|---|
| 43 | * side, always call tmrStopAllTimers when WM_DESTROY
|
|---|
| 44 | * comes into a window which has used timers.
|
|---|
| 45 | *
|
|---|
| 46 | * So to use XTimers, do the following:
|
|---|
| 47 | *
|
|---|
| 48 | * 1. Create a timer set with tmrCreateSet. Specify
|
|---|
| 49 | * an owner window and the timer ID of the master
|
|---|
| 50 | * PM timer.
|
|---|
| 51 | *
|
|---|
| 52 | * 2. You can then start and stop XTimers for windows
|
|---|
| 53 | * on the same thread by calling tmrStartXTimer and
|
|---|
| 54 | * tmrStopXTimer, respectively.
|
|---|
| 55 | *
|
|---|
| 56 | * 3. In the window proc of the owner window, respond
|
|---|
| 57 | * to WM_TIMER for the master PM timer by calling
|
|---|
| 58 | * tmrTimerTick. This will call the window procs
|
|---|
| 59 | * of those windows with WM_TIMER messages directly
|
|---|
| 60 | * whose XTimers have elapsed.
|
|---|
| 61 | *
|
|---|
| 62 | * Function prefixes:
|
|---|
| 63 | * -- tmr* timer functions
|
|---|
| 64 | *
|
|---|
| 65 | *@@header "helpers\timer.h"
|
|---|
| 66 | *@@added V0.9.7 (2000-12-04) [umoeller]
|
|---|
| 67 | */
|
|---|
| 68 |
|
|---|
| 69 | /*
|
|---|
| 70 | * Copyright (C) 2000-2001 Ulrich Mller.
|
|---|
| 71 | * This file is part of the "XWorkplace helpers" source package.
|
|---|
| 72 | * This is free software; you can redistribute it and/or modify
|
|---|
| 73 | * it under the terms of the GNU General Public License as published
|
|---|
| 74 | * by the Free Software Foundation, in version 2 as it comes in the
|
|---|
| 75 | * "COPYING" file of the XWorkplace main distribution.
|
|---|
| 76 | * This program is distributed in the hope that it will be useful,
|
|---|
| 77 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 78 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 79 | * GNU General Public License for more details.
|
|---|
| 80 | */
|
|---|
| 81 |
|
|---|
| 82 | // OS2 includes
|
|---|
| 83 |
|
|---|
| 84 | #define OS2EMX_PLAIN_CHAR
|
|---|
| 85 | // this is needed for "os2emx.h"; if this is defined,
|
|---|
| 86 | // emx will define PSZ as _signed_ char, otherwise
|
|---|
| 87 | // as unsigned char
|
|---|
| 88 |
|
|---|
| 89 | #define INCL_DOSEXCEPTIONS
|
|---|
| 90 | #define INCL_DOSPROCESS
|
|---|
| 91 | #define INCL_DOSSEMAPHORES
|
|---|
| 92 | #define INCL_DOSMISC
|
|---|
| 93 | #define INCL_DOSERRORS
|
|---|
| 94 |
|
|---|
| 95 | #define INCL_WINWINDOWMGR
|
|---|
| 96 | #define INCL_WINMESSAGEMGR
|
|---|
| 97 | #define INCL_WINTIMER
|
|---|
| 98 | #include <os2.h>
|
|---|
| 99 |
|
|---|
| 100 | #include <stdio.h>
|
|---|
| 101 | #include <setjmp.h>
|
|---|
| 102 |
|
|---|
| 103 | #include "setup.h" // code generation and debugging options
|
|---|
| 104 |
|
|---|
| 105 | #include "helpers\datetime.h"
|
|---|
| 106 | #include "helpers\except.h"
|
|---|
| 107 | #include "helpers\linklist.h"
|
|---|
| 108 | #include "helpers\math.h"
|
|---|
| 109 | #include "helpers\standards.h"
|
|---|
| 110 | #include "helpers\threads.h"
|
|---|
| 111 | #include "helpers\timer.h"
|
|---|
| 112 |
|
|---|
| 113 | // #define DEBUG_XTIMERS
|
|---|
| 114 |
|
|---|
| 115 | /*
|
|---|
| 116 | *@@category: Helpers\PM helpers\Timer replacements
|
|---|
| 117 | * see timer.c.
|
|---|
| 118 | */
|
|---|
| 119 |
|
|---|
| 120 | /* ******************************************************************
|
|---|
| 121 | *
|
|---|
| 122 | * Private declarations
|
|---|
| 123 | *
|
|---|
| 124 | ********************************************************************/
|
|---|
| 125 |
|
|---|
| 126 | /*
|
|---|
| 127 | *@@ XTIMER:
|
|---|
| 128 | * one of these represents an XTimer.
|
|---|
| 129 | * These are stored in a linked list in
|
|---|
| 130 | * an _XTIMERSET.
|
|---|
| 131 | */
|
|---|
| 132 |
|
|---|
| 133 | typedef struct _XTIMER
|
|---|
| 134 | {
|
|---|
| 135 | USHORT usTimerID; // timer ID, as passed to tmrStartXTimer
|
|---|
| 136 | HWND hwndTarget; // target window, as passed to tmrStartXTimer
|
|---|
| 137 | ULONG ulTimeout; // timer's timeout (in ms)
|
|---|
| 138 | ULONG ulNextFire; // next time scalar (from dtGetULongTime) to fire at
|
|---|
| 139 | } XTIMER, *PXTIMER;
|
|---|
| 140 |
|
|---|
| 141 | /* ******************************************************************
|
|---|
| 142 | *
|
|---|
| 143 | * Global variables
|
|---|
| 144 | *
|
|---|
| 145 | ********************************************************************/
|
|---|
| 146 |
|
|---|
| 147 | HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex
|
|---|
| 148 |
|
|---|
| 149 | /* ******************************************************************
|
|---|
| 150 | *
|
|---|
| 151 | * Private functions
|
|---|
| 152 | *
|
|---|
| 153 | ********************************************************************/
|
|---|
| 154 |
|
|---|
| 155 | /*
|
|---|
| 156 | *@@ LockTimers:
|
|---|
| 157 | *
|
|---|
| 158 | *@@added V0.9.12 (2001-05-12) [umoeller]
|
|---|
| 159 | */
|
|---|
| 160 |
|
|---|
| 161 | STATIC BOOL LockTimers(VOID)
|
|---|
| 162 | {
|
|---|
| 163 | if (!G_hmtxTimers)
|
|---|
| 164 | return (!DosCreateMutexSem(NULL,
|
|---|
| 165 | &G_hmtxTimers,
|
|---|
| 166 | 0,
|
|---|
| 167 | TRUE)); // request!
|
|---|
| 168 | else
|
|---|
| 169 | return !DosRequestMutexSem(G_hmtxTimers, SEM_INDEFINITE_WAIT);
|
|---|
| 170 | }
|
|---|
| 171 |
|
|---|
| 172 | /*
|
|---|
| 173 | *@@ UnlockTimers:
|
|---|
| 174 | *
|
|---|
| 175 | *@@added V0.9.12 (2001-05-12) [umoeller]
|
|---|
| 176 | */
|
|---|
| 177 |
|
|---|
| 178 | STATIC VOID UnlockTimers(VOID)
|
|---|
| 179 | {
|
|---|
| 180 | DosReleaseMutexSem(G_hmtxTimers);
|
|---|
| 181 | }
|
|---|
| 182 |
|
|---|
| 183 | /*
|
|---|
| 184 | *@@ FindTimer:
|
|---|
| 185 | * returns the XTIMER structure from the global
|
|---|
| 186 | * linked list which matches the given window
|
|---|
| 187 | * _and_ timer ID.
|
|---|
| 188 | *
|
|---|
| 189 | * Internal function. Caller must hold the mutex.
|
|---|
| 190 | */
|
|---|
| 191 |
|
|---|
| 192 | STATIC PXTIMER FindTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
|---|
| 193 | HWND hwnd, // in: timer target window
|
|---|
| 194 | USHORT usTimerID) // in: timer ID
|
|---|
| 195 | {
|
|---|
| 196 | PLINKLIST pllXTimers;
|
|---|
| 197 | if ( (pSet)
|
|---|
| 198 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
|---|
| 199 | )
|
|---|
| 200 | {
|
|---|
| 201 | PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
|
|---|
| 202 | while (pNode)
|
|---|
| 203 | {
|
|---|
| 204 | PXTIMER pTimer = (PXTIMER)pNode->pItemData;
|
|---|
| 205 | if ( (pTimer->usTimerID == usTimerID)
|
|---|
| 206 | && (pTimer->hwndTarget == hwnd)
|
|---|
| 207 | )
|
|---|
| 208 | {
|
|---|
| 209 | return (pTimer);
|
|---|
| 210 | }
|
|---|
| 211 |
|
|---|
| 212 | pNode = pNode->pNext;
|
|---|
| 213 | }
|
|---|
| 214 | }
|
|---|
| 215 |
|
|---|
| 216 | return NULL;
|
|---|
| 217 | }
|
|---|
| 218 |
|
|---|
| 219 | /*
|
|---|
| 220 | *@@ RemoveTimer:
|
|---|
| 221 | * removes the specified XTIMER structure from
|
|---|
| 222 | * the global linked list of running timers.
|
|---|
| 223 | *
|
|---|
| 224 | * Internal function. Caller must hold the mutex.
|
|---|
| 225 | */
|
|---|
| 226 |
|
|---|
| 227 | STATIC VOID RemoveTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
|---|
| 228 | PXTIMER pTimer) // in: timer to remove
|
|---|
| 229 | {
|
|---|
| 230 | PLINKLIST pllXTimers;
|
|---|
| 231 | if ( (pSet)
|
|---|
| 232 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
|---|
| 233 | )
|
|---|
| 234 | {
|
|---|
| 235 | #ifdef DEBUG_XTIMERS
|
|---|
| 236 | _Pmpf((__FUNCTION__ ": removing timer %d", pTimer->usTimerID));
|
|---|
| 237 | #endif
|
|---|
| 238 | lstRemoveItem(pllXTimers,
|
|---|
| 239 | pTimer); // auto-free!
|
|---|
| 240 | }
|
|---|
| 241 | }
|
|---|
| 242 |
|
|---|
| 243 | /*
|
|---|
| 244 | *@@ AdjustPMTimer:
|
|---|
| 245 | * goes thru all XTimers in the sets and starts
|
|---|
| 246 | * or stops the PM timer with a decent frequency.
|
|---|
| 247 | *
|
|---|
| 248 | * Internal function. Caller must hold the mutex.
|
|---|
| 249 | *
|
|---|
| 250 | *@@added V0.9.9 (2001-03-07) [umoeller]
|
|---|
| 251 | *@@changed V0.9.14 (2001-07-07) [umoeller]: added GCD optimizations
|
|---|
| 252 | */
|
|---|
| 253 |
|
|---|
| 254 | STATIC VOID AdjustPMTimer(PXTIMERSET pSet)
|
|---|
| 255 | {
|
|---|
| 256 | PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers;
|
|---|
| 257 | ULONG cTimers = lstCountItems(pllXTimers);
|
|---|
| 258 |
|
|---|
| 259 | #ifdef DEBUG_XTIMERS
|
|---|
| 260 | _Pmpf((__FUNCTION__ ": entering"));
|
|---|
| 261 | #endif
|
|---|
| 262 |
|
|---|
| 263 | if (!cTimers)
|
|---|
| 264 | {
|
|---|
| 265 | // no XTimers running:
|
|---|
| 266 | if (pSet->idPMTimerRunning)
|
|---|
| 267 | {
|
|---|
| 268 | // but PM timer running:
|
|---|
| 269 | // stop it
|
|---|
| 270 | WinStopTimer(pSet->hab,
|
|---|
| 271 | pSet->hwndOwner,
|
|---|
| 272 | pSet->idPMTimer);
|
|---|
| 273 | pSet->idPMTimerRunning = 0;
|
|---|
| 274 | }
|
|---|
| 275 |
|
|---|
| 276 | pSet->ulPMTimeout = 0;
|
|---|
| 277 | }
|
|---|
| 278 | else
|
|---|
| 279 | {
|
|---|
| 280 | // we have timers:
|
|---|
| 281 |
|
|---|
| 282 | ULONG ulOldPMTimeout = pSet->ulPMTimeout;
|
|---|
| 283 |
|
|---|
| 284 | PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
|
|---|
| 285 | PXTIMER pTimer1 = (PXTIMER)pNode->pItemData;
|
|---|
| 286 |
|
|---|
| 287 | if (cTimers == 1)
|
|---|
| 288 | {
|
|---|
| 289 | // only one timer:
|
|---|
| 290 | // that's easy
|
|---|
| 291 | #ifdef DEBUG_XTIMERS
|
|---|
| 292 | _Pmpf((" got 1 timer"));
|
|---|
| 293 | #endif
|
|---|
| 294 |
|
|---|
| 295 | pSet->ulPMTimeout = pTimer1->ulTimeout;
|
|---|
| 296 | }
|
|---|
| 297 | else if (cTimers == 2)
|
|---|
| 298 | {
|
|---|
| 299 | // exactly two timers:
|
|---|
| 300 | // find the greatest common denominator
|
|---|
| 301 | PXTIMER pTimer2 = (PXTIMER)pNode->pNext->pItemData;
|
|---|
| 302 | #ifdef DEBUG_XTIMERS
|
|---|
| 303 | _Pmpf((" got 2 timers"));
|
|---|
| 304 | #endif
|
|---|
| 305 |
|
|---|
| 306 | pSet->ulPMTimeout = mathGCD(pTimer1->ulTimeout,
|
|---|
| 307 | pTimer2->ulTimeout);
|
|---|
| 308 | }
|
|---|
| 309 | else
|
|---|
| 310 | {
|
|---|
| 311 | // more than two timers:
|
|---|
| 312 | // run through all timers and find the greatest
|
|---|
| 313 | // common denominator of all frequencies...
|
|---|
| 314 | int *paInts = (int*)_alloca(sizeof(int) * cTimers),
|
|---|
| 315 | i = 0;
|
|---|
| 316 |
|
|---|
| 317 | #ifdef DEBUG_XTIMERS
|
|---|
| 318 | _Pmpf((" got %d timers", cTimers));
|
|---|
| 319 | #endif
|
|---|
| 320 |
|
|---|
| 321 | // fill an array of integers with the
|
|---|
| 322 | // timer frequencies
|
|---|
| 323 | while (pNode)
|
|---|
| 324 | {
|
|---|
| 325 | pTimer1 = (PXTIMER)pNode->pItemData;
|
|---|
| 326 |
|
|---|
| 327 | #ifdef DEBUG_XTIMERS
|
|---|
| 328 | _Pmpf((" timeout %d is %d", i, pTimer1->ulTimeout));
|
|---|
| 329 | #endif
|
|---|
| 330 |
|
|---|
| 331 | paInts[i++] = pTimer1->ulTimeout;
|
|---|
| 332 |
|
|---|
| 333 | pNode = pNode->pNext;
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | pSet->ulPMTimeout = mathGCDMulti(paInts,
|
|---|
| 337 | cTimers);
|
|---|
| 338 | }
|
|---|
| 339 |
|
|---|
| 340 | #ifdef DEBUG_XTIMERS
|
|---|
| 341 | _Pmpf(("--> GCD is %d", pSet->ulPMTimeout));
|
|---|
| 342 | #endif
|
|---|
| 343 |
|
|---|
| 344 | if ( (!pSet->idPMTimerRunning) // timer not running?
|
|---|
| 345 | || (pSet->ulPMTimeout != ulOldPMTimeout) // timeout changed?
|
|---|
| 346 | )
|
|---|
| 347 | {
|
|---|
| 348 | // start or restart PM timer
|
|---|
| 349 | pSet->idPMTimerRunning = WinStartTimer(pSet->hab,
|
|---|
| 350 | pSet->hwndOwner,
|
|---|
| 351 | pSet->idPMTimer,
|
|---|
| 352 | pSet->ulPMTimeout);
|
|---|
| 353 | }
|
|---|
| 354 | }
|
|---|
| 355 | }
|
|---|
| 356 |
|
|---|
| 357 | /* ******************************************************************
|
|---|
| 358 | *
|
|---|
| 359 | * Exported functions
|
|---|
| 360 | *
|
|---|
| 361 | ********************************************************************/
|
|---|
| 362 |
|
|---|
| 363 | /*
|
|---|
| 364 | *@@ tmrCreateSet:
|
|---|
| 365 | * creates a "timer set" for use with the XTimer functions.
|
|---|
| 366 | * This is the first step if you want to use the XTimers.
|
|---|
| 367 | *
|
|---|
| 368 | * hwndOwner must specify the "owner window", the target
|
|---|
| 369 | * window for the master PM timer. This window must respond
|
|---|
| 370 | * to a WM_TIMER message with the specified usPMTimerID and
|
|---|
| 371 | * invoke tmrTimerTick then.
|
|---|
| 372 | *
|
|---|
| 373 | * Note that the master timer is not started until an XTimer
|
|---|
| 374 | * is started.
|
|---|
| 375 | *
|
|---|
| 376 | * Use tmrDestroySet to free all resources again.
|
|---|
| 377 | *
|
|---|
| 378 | *@@added V0.9.9 (2001-02-28) [umoeller]
|
|---|
| 379 | */
|
|---|
| 380 |
|
|---|
| 381 | PXTIMERSET tmrCreateSet(HWND hwndOwner, // in: owner window
|
|---|
| 382 | USHORT usPMTimerID)
|
|---|
| 383 | {
|
|---|
| 384 | PXTIMERSET pSet = NEW(XTIMERSET);
|
|---|
| 385 | if (pSet)
|
|---|
| 386 | {
|
|---|
| 387 | pSet->hab = WinQueryAnchorBlock(hwndOwner);
|
|---|
| 388 | pSet->hwndOwner = hwndOwner;
|
|---|
| 389 | pSet->idPMTimer = usPMTimerID;
|
|---|
| 390 | pSet->idPMTimerRunning = 0;
|
|---|
| 391 | pSet->ulPMTimeout = 0;
|
|---|
| 392 |
|
|---|
| 393 | pSet->pvllXTimers = (PVOID)lstCreate(TRUE);
|
|---|
| 394 | }
|
|---|
| 395 |
|
|---|
| 396 | return (pSet);
|
|---|
| 397 | }
|
|---|
| 398 |
|
|---|
| 399 | /*
|
|---|
| 400 | *@@ tmrDestroySet:
|
|---|
| 401 | * destroys a timer set previously created using
|
|---|
| 402 | * tmrCreateSet.
|
|---|
| 403 | *
|
|---|
| 404 | * Of course, this will stop all XTimers which
|
|---|
| 405 | * might still be running with this set.
|
|---|
| 406 | *
|
|---|
| 407 | *@@added V0.9.9 (2001-02-28) [umoeller]
|
|---|
| 408 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
|---|
| 409 | */
|
|---|
| 410 |
|
|---|
| 411 | VOID tmrDestroySet(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
|
|---|
| 412 | {
|
|---|
| 413 | if (LockTimers())
|
|---|
| 414 | {
|
|---|
| 415 | if (pSet)
|
|---|
| 416 | {
|
|---|
| 417 | PLINKLIST pllXTimers;
|
|---|
| 418 | if (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
|---|
| 419 | {
|
|---|
| 420 | PLISTNODE pTimerNode = lstQueryFirstNode(pllXTimers);
|
|---|
| 421 | while (pTimerNode)
|
|---|
| 422 | {
|
|---|
| 423 | PLISTNODE pNext = pTimerNode->pNext;
|
|---|
| 424 | PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
|
|---|
| 425 | RemoveTimer(pSet, pTimer);
|
|---|
| 426 | pTimerNode = pNext;
|
|---|
| 427 | }
|
|---|
| 428 |
|
|---|
| 429 | lstFree(&pllXTimers);
|
|---|
| 430 | pSet->pvllXTimers = NULL;
|
|---|
| 431 | }
|
|---|
| 432 |
|
|---|
| 433 | if (pSet->idPMTimerRunning)
|
|---|
| 434 | {
|
|---|
| 435 | WinStopTimer(pSet->hab,
|
|---|
| 436 | pSet->hwndOwner,
|
|---|
| 437 | pSet->idPMTimer);
|
|---|
| 438 | pSet->idPMTimerRunning = 0;
|
|---|
| 439 | }
|
|---|
| 440 |
|
|---|
| 441 | free(pSet);
|
|---|
| 442 | }
|
|---|
| 443 |
|
|---|
| 444 | UnlockTimers();
|
|---|
| 445 | }
|
|---|
| 446 | }
|
|---|
| 447 |
|
|---|
| 448 | /*
|
|---|
| 449 | *@@ tmrTimerTick:
|
|---|
| 450 | * implements a PM timer tick.
|
|---|
| 451 | *
|
|---|
| 452 | * When your window procs receives WM_TIMER for the
|
|---|
| 453 | * one PM timer which is supposed to trigger all the
|
|---|
| 454 | * XTimers, it must call this function. This will
|
|---|
| 455 | * evaluate all XTimers on the list and "fire" them
|
|---|
| 456 | * by calling the window procs directly with the
|
|---|
| 457 | * WM_TIMER message.
|
|---|
| 458 | *
|
|---|
| 459 | *@@added V0.9.9 (2001-02-28) [umoeller]
|
|---|
| 460 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
|---|
| 461 | *@@changed V0.9.12 (2001-05-24) [umoeller]: fixed crash if this got called during tmrTimerTick
|
|---|
| 462 | *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed mem overwrite which might have caused crashes if this got called during tmrTimerTick
|
|---|
| 463 | *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed "half frequency" regression caused by frequency optimizations
|
|---|
| 464 | *@@changed V0.9.16 (2001-12-18) [umoeller]: now using WinDispatchMsg to avoid crashes during win destruction
|
|---|
| 465 | *@@changed V0.9.19 (2002-05-04) [umoeller]: added excpt handling to avoid hanging all timers on the mutex
|
|---|
| 466 | */
|
|---|
| 467 |
|
|---|
| 468 | VOID tmrTimerTick(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
|
|---|
| 469 | {
|
|---|
| 470 | BOOL fLocked = FALSE;
|
|---|
| 471 |
|
|---|
| 472 | TRY_LOUD(excpt1)
|
|---|
| 473 | {
|
|---|
| 474 | if (fLocked = LockTimers())
|
|---|
| 475 | {
|
|---|
| 476 | PLINKLIST pllXTimers;
|
|---|
| 477 | if ( (pSet)
|
|---|
| 478 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
|---|
| 479 | )
|
|---|
| 480 | {
|
|---|
| 481 | // go thru all XTimers and see which one
|
|---|
| 482 | // has elapsed; for all of these, post WM_TIMER
|
|---|
| 483 | // to the target window proc
|
|---|
| 484 | PLISTNODE pTimerNode;
|
|---|
| 485 |
|
|---|
| 486 | if (!(pTimerNode = lstQueryFirstNode(pllXTimers)))
|
|---|
| 487 | {
|
|---|
| 488 | // no timers left:
|
|---|
| 489 | if (pSet->idPMTimerRunning)
|
|---|
| 490 | {
|
|---|
| 491 | // but PM timer running:
|
|---|
| 492 | // stop it
|
|---|
| 493 | WinStopTimer(pSet->hab,
|
|---|
| 494 | pSet->hwndOwner,
|
|---|
| 495 | pSet->idPMTimer);
|
|---|
| 496 | pSet->idPMTimerRunning = 0;
|
|---|
| 497 | }
|
|---|
| 498 |
|
|---|
| 499 | pSet->ulPMTimeout = 0;
|
|---|
| 500 | }
|
|---|
| 501 | else
|
|---|
| 502 | {
|
|---|
| 503 | // we have timers:
|
|---|
| 504 | BOOL fFoundInvalid = FALSE;
|
|---|
| 505 |
|
|---|
| 506 | // get current time
|
|---|
| 507 | ULONG ulTimeNow = 0;
|
|---|
| 508 | DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
|
|---|
| 509 | &ulTimeNow, sizeof(ulTimeNow));
|
|---|
| 510 |
|
|---|
| 511 | #ifdef DEBUG_XTIMERS
|
|---|
| 512 | _Pmpf((__FUNCTION__ ": ulTimeNow = %d", ulTimeNow));
|
|---|
| 513 | #endif
|
|---|
| 514 |
|
|---|
| 515 | while (pTimerNode)
|
|---|
| 516 | {
|
|---|
| 517 | // get next node first because the
|
|---|
| 518 | // list can get modified while processing
|
|---|
| 519 | // V0.9.12 (2001-05-24) [umoeller]
|
|---|
| 520 | PLISTNODE pNext = pTimerNode->pNext;
|
|---|
| 521 |
|
|---|
| 522 | PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
|
|---|
| 523 |
|
|---|
| 524 | #ifdef DEBUG_XTIMERS
|
|---|
| 525 | _Pmpf((" timer %d: ulNextFire = %d",
|
|---|
| 526 | lstIndexFromItem(pllXTimers, pTimer),
|
|---|
| 527 | pTimer->ulNextFire));
|
|---|
| 528 | #endif
|
|---|
| 529 |
|
|---|
| 530 | if ( (pTimer)
|
|---|
| 531 | // && (pTimer->ulNextFire < ulTimeNow)
|
|---|
| 532 | // V0.9.14 (2001-08-01) [umoeller]
|
|---|
| 533 | // use <= because otherwise we'll get
|
|---|
| 534 | // only half the frequency...
|
|---|
| 535 | // we get here frequently where the
|
|---|
| 536 | // two values are EXACTLY equal due
|
|---|
| 537 | // to the above optimization (DosQuerySysInfo
|
|---|
| 538 | // called once only for the entire loop)
|
|---|
| 539 | && (pTimer->ulNextFire <= ulTimeNow)
|
|---|
| 540 | )
|
|---|
| 541 | {
|
|---|
| 542 | // this timer has elapsed:
|
|---|
| 543 | // fire!
|
|---|
| 544 |
|
|---|
| 545 | #ifdef DEBUG_XTIMERS
|
|---|
| 546 | _Pmpf((" --> fire!"));
|
|---|
| 547 | #endif
|
|---|
| 548 |
|
|---|
| 549 | if (WinIsWindow(pSet->hab,
|
|---|
| 550 | pTimer->hwndTarget))
|
|---|
| 551 | {
|
|---|
| 552 | // window still valid:
|
|---|
| 553 | // get the window's window proc
|
|---|
| 554 | QMSG qmsg;
|
|---|
| 555 | PFNWP pfnwp = (PFNWP)WinQueryWindowPtr(pTimer->hwndTarget,
|
|---|
| 556 | QWP_PFNWP);
|
|---|
| 557 |
|
|---|
| 558 | // moved this up V0.9.14 (2001-08-01) [umoeller]
|
|---|
| 559 | pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout;
|
|---|
| 560 |
|
|---|
| 561 | // call the window proc DIRECTLY
|
|---|
| 562 | // V0.9.16 (2001-12-18) [umoeller]:
|
|---|
| 563 | // now using WinDispatchMsg to avoid crashes
|
|---|
| 564 | // while hwndTarget is being destroyed
|
|---|
| 565 | /* qmsg.hwnd = pTimer->hwndTarget;
|
|---|
| 566 | qmsg.msg = WM_TIMER;
|
|---|
| 567 | qmsg.mp1 = (MPARAM)pTimer->usTimerID;
|
|---|
| 568 | qmsg.mp2 = (MPARAM)0;
|
|---|
| 569 | qmsg.time = 0;
|
|---|
| 570 | qmsg.ptl.x = 0;
|
|---|
| 571 | qmsg.ptl.y = 0;
|
|---|
| 572 | qmsg.reserved = 0;
|
|---|
| 573 | WinDispatchMsg(pSet->hab,
|
|---|
| 574 | &qmsg); */
|
|---|
| 575 |
|
|---|
| 576 | pfnwp(pTimer->hwndTarget,
|
|---|
| 577 | WM_TIMER,
|
|---|
| 578 | (MPARAM)pTimer->usTimerID,
|
|---|
| 579 | 0);
|
|---|
| 580 | // V0.9.12 (2001-05-24) [umoeller]
|
|---|
| 581 | // if the winproc chooses to start or
|
|---|
| 582 | // stop a timer, pNext still points
|
|---|
| 583 | // to a valid node...
|
|---|
| 584 | // -- if a timer is removed, that's OK
|
|---|
| 585 | // -- if a timer is added, it is added to
|
|---|
| 586 | // the list, so we'll see it in this loop
|
|---|
| 587 |
|
|---|
| 588 | // V0.9.14 (2001-08-01) [umoeller]
|
|---|
| 589 |
|
|---|
| 590 | // DO NOT REFERENCE pTimer AFTER THIS CODE;
|
|---|
| 591 | // the winproc might have removed the timer,
|
|---|
| 592 | // and since the list is auto-free, pTimer
|
|---|
| 593 | // might have been freed!!
|
|---|
| 594 | }
|
|---|
| 595 | else
|
|---|
| 596 | {
|
|---|
| 597 | // window has been destroyed:
|
|---|
| 598 | lstRemoveNode(pllXTimers,
|
|---|
| 599 | pTimerNode);
|
|---|
| 600 | // pNext is still valid
|
|---|
| 601 |
|
|---|
| 602 | fFoundInvalid = TRUE;
|
|---|
| 603 | }
|
|---|
| 604 |
|
|---|
| 605 | } // end if (pTimer->ulNextFire < ulTimeNow)
|
|---|
| 606 |
|
|---|
| 607 | // next timer
|
|---|
| 608 | pTimerNode = pNext; // V0.9.12 (2001-05-24) [umoeller]
|
|---|
| 609 | } // end while (pTimerNode)
|
|---|
| 610 |
|
|---|
| 611 | // destroy invalid timers, if any
|
|---|
| 612 | if (fFoundInvalid)
|
|---|
| 613 | AdjustPMTimer(pSet);
|
|---|
| 614 |
|
|---|
| 615 | } // end else if (!pTimerNode)
|
|---|
| 616 | } // end if (pllXTimers)
|
|---|
| 617 | } // end if (LockTimers())
|
|---|
| 618 | }
|
|---|
| 619 | CATCH(excpt1) {} END_CATCH();
|
|---|
| 620 |
|
|---|
| 621 | if (fLocked)
|
|---|
| 622 | UnlockTimers();
|
|---|
| 623 | }
|
|---|
| 624 |
|
|---|
| 625 | /*
|
|---|
| 626 | *@@ tmrStartXTimer:
|
|---|
| 627 | * starts an XTimer.
|
|---|
| 628 | *
|
|---|
| 629 | * Any window can request an XTimer using
|
|---|
| 630 | * this function. This operates similar to
|
|---|
| 631 | * WinStartTimer, except that the number of
|
|---|
| 632 | * XTimers is not limited.
|
|---|
| 633 | *
|
|---|
| 634 | * Returns the ID of a new timer or resets an
|
|---|
| 635 | * existing timer (if usTimerID is already used
|
|---|
| 636 | * with hwnd). Use tmrStopXTimer to stop the timer.
|
|---|
| 637 | *
|
|---|
| 638 | * Returns 0 if an error occured. It is thus
|
|---|
| 639 | * invalid to specify a timer ID of 0.
|
|---|
| 640 | *
|
|---|
| 641 | * The timer is _not_ stopped automatically
|
|---|
| 642 | * when the window is destroyed.
|
|---|
| 643 | *
|
|---|
| 644 | * Note: Unless you own the timer set that
|
|---|
| 645 | * your timer runs on, it is strongly recommended
|
|---|
| 646 | * that your timer frequency is set to a multiple
|
|---|
| 647 | * of 125. The PM master timer behind the timer
|
|---|
| 648 | * set will be set to the greatest common divisor
|
|---|
| 649 | * of all frequencies, and if you set one timer
|
|---|
| 650 | * to 2000 and the other one to 2001, you will
|
|---|
| 651 | * cause quite a lot of overhead. This applies
|
|---|
| 652 | * especially to timers started by XCenter widgets.
|
|---|
| 653 | *
|
|---|
| 654 | * For security, all timer frequencies will be
|
|---|
| 655 | * rounded to multiples of 25 anyway. Still,
|
|---|
| 656 | * running two timers at 1000 and 1025 will cause
|
|---|
| 657 | * the master timer to be set to 25, which is
|
|---|
| 658 | * overkill.
|
|---|
| 659 | *
|
|---|
| 660 | *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
|
|---|
| 661 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
|---|
| 662 | *@@changed V0.9.14 (2001-07-12) [umoeller]: now rounding freq's to multiples of 25
|
|---|
| 663 | */
|
|---|
| 664 |
|
|---|
| 665 | USHORT XWPENTRY tmrStartXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
|---|
| 666 | HWND hwnd, // in: target window for XTimer
|
|---|
| 667 | USHORT usTimerID, // in: timer ID for XTimer's WM_TIMER (must be > 0)
|
|---|
| 668 | ULONG ulTimeout) // in: XTimer's timeout
|
|---|
| 669 | {
|
|---|
| 670 | USHORT usrc = 0;
|
|---|
| 671 |
|
|---|
| 672 | // _Pmpf((__FUNCTION__ ": entering"));
|
|---|
| 673 |
|
|---|
| 674 | if (LockTimers())
|
|---|
| 675 | {
|
|---|
| 676 | PLINKLIST pllXTimers;
|
|---|
| 677 | if ( (pSet)
|
|---|
| 678 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
|---|
| 679 | && (hwnd)
|
|---|
| 680 | && (ulTimeout)
|
|---|
| 681 | && (usTimerID) // V0.9.16 (2001-12-18) [umoeller]
|
|---|
| 682 | )
|
|---|
| 683 | {
|
|---|
| 684 | PXTIMER pTimer;
|
|---|
| 685 | ULONG ulTimeNow;
|
|---|
| 686 |
|
|---|
| 687 | // fix the timeout... we allow only multiples of
|
|---|
| 688 | // 25, and it must be at least 25 (otherwise our
|
|---|
| 689 | // internal master timer calculations will fail)
|
|---|
| 690 | // V0.9.14 (2001-07-07) [umoeller]
|
|---|
| 691 | if (ulTimeout < 25)
|
|---|
| 692 | ulTimeout = 25;
|
|---|
| 693 | else
|
|---|
| 694 | ulTimeout = (ulTimeout + 10) / 25 * 25;
|
|---|
| 695 |
|
|---|
| 696 | DosQuerySysInfo(QSV_MS_COUNT,
|
|---|
| 697 | QSV_MS_COUNT,
|
|---|
| 698 | &ulTimeNow,
|
|---|
| 699 | sizeof(ulTimeNow));
|
|---|
| 700 |
|
|---|
| 701 | // check if this timer exists already
|
|---|
| 702 | if (pTimer = FindTimer(pSet,
|
|---|
| 703 | hwnd,
|
|---|
| 704 | usTimerID))
|
|---|
| 705 | {
|
|---|
| 706 | // exists already: reset only
|
|---|
| 707 | pTimer->ulNextFire = ulTimeNow + ulTimeout;
|
|---|
| 708 | usrc = usTimerID;
|
|---|
| 709 | }
|
|---|
| 710 | else
|
|---|
| 711 | {
|
|---|
| 712 | // new timer needed:
|
|---|
| 713 | if (pTimer = NEW(XTIMER))
|
|---|
| 714 | {
|
|---|
| 715 | pTimer->usTimerID = usTimerID;
|
|---|
| 716 | pTimer->hwndTarget = hwnd;
|
|---|
| 717 | pTimer->ulTimeout = ulTimeout;
|
|---|
| 718 | pTimer->ulNextFire = ulTimeNow + ulTimeout;
|
|---|
| 719 |
|
|---|
| 720 | lstAppendItem(pllXTimers,
|
|---|
| 721 | pTimer);
|
|---|
| 722 | usrc = usTimerID;
|
|---|
| 723 | }
|
|---|
| 724 | }
|
|---|
| 725 |
|
|---|
| 726 | if (usrc)
|
|---|
| 727 | // timer created or reset:
|
|---|
| 728 | AdjustPMTimer(pSet);
|
|---|
| 729 |
|
|---|
| 730 | } // if ((hwnd) && (ulTimeout))
|
|---|
| 731 |
|
|---|
| 732 | UnlockTimers();
|
|---|
| 733 | }
|
|---|
| 734 |
|
|---|
| 735 | return (usrc);
|
|---|
| 736 | }
|
|---|
| 737 |
|
|---|
| 738 | /*
|
|---|
| 739 | *@@ tmrStopXTimer:
|
|---|
| 740 | * similar to WinStopTimer, this stops the
|
|---|
| 741 | * specified timer (which must have been
|
|---|
| 742 | * started with the same hwnd and usTimerID
|
|---|
| 743 | * using tmrStartXTimer).
|
|---|
| 744 | *
|
|---|
| 745 | * Returns TRUE if the timer was stopped.
|
|---|
| 746 | *
|
|---|
| 747 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
|---|
| 748 | */
|
|---|
| 749 |
|
|---|
| 750 | BOOL XWPENTRY tmrStopXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
|---|
| 751 | HWND hwnd,
|
|---|
| 752 | USHORT usTimerID)
|
|---|
| 753 | {
|
|---|
| 754 | BOOL brc = FALSE;
|
|---|
| 755 | if (LockTimers())
|
|---|
| 756 | {
|
|---|
| 757 | PXTIMER pTimer;
|
|---|
| 758 | #ifdef DEBUG_XTIMERS
|
|---|
| 759 | _Pmpf((__FUNCTION__ ": finding timer %d", usTimerID));
|
|---|
| 760 | #endif
|
|---|
| 761 | if (pTimer = FindTimer(pSet,
|
|---|
| 762 | hwnd,
|
|---|
| 763 | usTimerID))
|
|---|
| 764 | // FindTimer checks the params
|
|---|
| 765 | {
|
|---|
| 766 | RemoveTimer(pSet, pTimer);
|
|---|
| 767 | // recalculate
|
|---|
| 768 | AdjustPMTimer(pSet);
|
|---|
| 769 | brc = TRUE;
|
|---|
| 770 | }
|
|---|
| 771 |
|
|---|
| 772 | UnlockTimers();
|
|---|
| 773 | }
|
|---|
| 774 |
|
|---|
| 775 | return brc;
|
|---|
| 776 | }
|
|---|
| 777 |
|
|---|
| 778 |
|
|---|