[14] | 1 |
|
---|
| 2 | /*
|
---|
| 3 | *@@sourcefile timer.c:
|
---|
[41] | 4 | * XTimers, which can be used to avoid excessive PM
|
---|
| 5 | * timer usage.
|
---|
[14] | 6 | *
|
---|
[44] | 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.
|
---|
[14] | 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 | *
|
---|
[127] | 20 | * XTimers are used excessively by the default widgets
|
---|
| 21 | * of XWorkplace's XCenter.
|
---|
| 22 | *
|
---|
[14] | 23 | * There are a few limitations with the XTimers though:
|
---|
| 24 | *
|
---|
[44] | 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.
|
---|
[14] | 31 | *
|
---|
[44] | 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 | *
|
---|
[14] | 36 | * -- Queue timers (with HWND == NULLHANDLE) are not
|
---|
| 37 | * supported.
|
---|
| 38 | *
|
---|
[44] | 39 | * -- When a window is destroyed, its timers are not
|
---|
| 40 | * automatically cleaned up. tmrTimerTick does
|
---|
[19] | 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.
|
---|
[14] | 45 | *
|
---|
[127] | 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 | *
|
---|
[14] | 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 | /*
|
---|
[297] | 70 | * Copyright (C) 2000-2005 Ulrich Mller.
|
---|
[14] | 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
|
---|
[15] | 92 | #define INCL_DOSMISC
|
---|
[14] | 93 | #define INCL_DOSERRORS
|
---|
[17] | 94 |
|
---|
[41] | 95 | #define INCL_WINWINDOWMGR
|
---|
[17] | 96 | #define INCL_WINMESSAGEMGR
|
---|
[41] | 97 | #define INCL_WINTIMER
|
---|
[14] | 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"
|
---|
[86] | 108 | #include "helpers\math.h"
|
---|
[91] | 109 | #include "helpers\standards.h"
|
---|
[14] | 110 | #include "helpers\threads.h"
|
---|
| 111 | #include "helpers\timer.h"
|
---|
| 112 |
|
---|
[164] | 113 | // #define DEBUG_XTIMERS
|
---|
[163] | 114 |
|
---|
[14] | 115 | /*
|
---|
| 116 | *@@category: Helpers\PM helpers\Timer replacements
|
---|
[21] | 117 | * see timer.c.
|
---|
[14] | 118 | */
|
---|
| 119 |
|
---|
| 120 | /* ******************************************************************
|
---|
| 121 | *
|
---|
| 122 | * Private declarations
|
---|
| 123 | *
|
---|
| 124 | ********************************************************************/
|
---|
| 125 |
|
---|
| 126 | /*
|
---|
| 127 | *@@ XTIMER:
|
---|
| 128 | * one of these represents an XTimer.
|
---|
[44] | 129 | * These are stored in a linked list in
|
---|
| 130 | * an _XTIMERSET.
|
---|
[14] | 131 | */
|
---|
| 132 |
|
---|
| 133 | typedef struct _XTIMER
|
---|
| 134 | {
|
---|
[41] | 135 | USHORT usTimerID; // timer ID, as passed to tmrStartXTimer
|
---|
| 136 | HWND hwndTarget; // target window, as passed to tmrStartXTimer
|
---|
[14] | 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 |
|
---|
[68] | 147 | HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex
|
---|
[14] | 148 |
|
---|
[68] | 149 | /* ******************************************************************
|
---|
| 150 | *
|
---|
| 151 | * Private functions
|
---|
| 152 | *
|
---|
| 153 | ********************************************************************/
|
---|
| 154 |
|
---|
[14] | 155 | /*
|
---|
[68] | 156 | *@@ LockTimers:
|
---|
[14] | 157 | *
|
---|
[68] | 158 | *@@added V0.9.12 (2001-05-12) [umoeller]
|
---|
[14] | 159 | */
|
---|
| 160 |
|
---|
[222] | 161 | STATIC BOOL LockTimers(VOID)
|
---|
[14] | 162 | {
|
---|
[68] | 163 | if (!G_hmtxTimers)
|
---|
[297] | 164 | return !DosCreateMutexSem(NULL,
|
---|
| 165 | &G_hmtxTimers,
|
---|
| 166 | 0,
|
---|
| 167 | TRUE); // request!
|
---|
| 168 |
|
---|
| 169 | return !DosRequestMutexSem(G_hmtxTimers, SEM_INDEFINITE_WAIT);
|
---|
[68] | 170 | }
|
---|
[14] | 171 |
|
---|
[68] | 172 | /*
|
---|
| 173 | *@@ UnlockTimers:
|
---|
| 174 | *
|
---|
| 175 | *@@added V0.9.12 (2001-05-12) [umoeller]
|
---|
| 176 | */
|
---|
[14] | 177 |
|
---|
[222] | 178 | STATIC VOID UnlockTimers(VOID)
|
---|
[68] | 179 | {
|
---|
| 180 | DosReleaseMutexSem(G_hmtxTimers);
|
---|
| 181 | }
|
---|
[14] | 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 | *
|
---|
[68] | 189 | * Internal function. Caller must hold the mutex.
|
---|
[14] | 190 | */
|
---|
| 191 |
|
---|
[222] | 192 | STATIC PXTIMER FindTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
---|
[142] | 193 | HWND hwnd, // in: timer target window
|
---|
| 194 | USHORT usTimerID) // in: timer ID
|
---|
[14] | 195 | {
|
---|
[127] | 196 | PLINKLIST pllXTimers;
|
---|
| 197 | if ( (pSet)
|
---|
| 198 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
---|
| 199 | )
|
---|
[14] | 200 | {
|
---|
[41] | 201 | PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
|
---|
| 202 | while (pNode)
|
---|
[14] | 203 | {
|
---|
[41] | 204 | PXTIMER pTimer = (PXTIMER)pNode->pItemData;
|
---|
| 205 | if ( (pTimer->usTimerID == usTimerID)
|
---|
| 206 | && (pTimer->hwndTarget == hwnd)
|
---|
| 207 | )
|
---|
| 208 | {
|
---|
[297] | 209 | return pTimer;
|
---|
[41] | 210 | }
|
---|
| 211 |
|
---|
| 212 | pNode = pNode->pNext;
|
---|
[14] | 213 | }
|
---|
[41] | 214 | }
|
---|
[14] | 215 |
|
---|
[169] | 216 | return NULL;
|
---|
[14] | 217 | }
|
---|
| 218 |
|
---|
| 219 | /*
|
---|
| 220 | *@@ RemoveTimer:
|
---|
| 221 | * removes the specified XTIMER structure from
|
---|
| 222 | * the global linked list of running timers.
|
---|
| 223 | *
|
---|
[68] | 224 | * Internal function. Caller must hold the mutex.
|
---|
[14] | 225 | */
|
---|
| 226 |
|
---|
[222] | 227 | STATIC VOID RemoveTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
---|
[163] | 228 | PXTIMER pTimer) // in: timer to remove
|
---|
[14] | 229 | {
|
---|
[127] | 230 | PLINKLIST pllXTimers;
|
---|
| 231 | if ( (pSet)
|
---|
| 232 | && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
---|
| 233 | )
|
---|
[41] | 234 | {
|
---|
[163] | 235 | #ifdef DEBUG_XTIMERS
|
---|
| 236 | _Pmpf((__FUNCTION__ ": removing timer %d", pTimer->usTimerID));
|
---|
| 237 | #endif
|
---|
[41] | 238 | lstRemoveItem(pllXTimers,
|
---|
| 239 | pTimer); // auto-free!
|
---|
| 240 | }
|
---|
[14] | 241 | }
|
---|
| 242 |
|
---|
[74] | 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]
|
---|
[86] | 251 | *@@changed V0.9.14 (2001-07-07) [umoeller]: added GCD optimizations
|
---|
[74] | 252 | */
|
---|
| 253 |
|
---|
[222] | 254 | STATIC VOID AdjustPMTimer(PXTIMERSET pSet)
|
---|
[74] | 255 | {
|
---|
| 256 | PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers;
|
---|
[91] | 257 | ULONG cTimers = lstCountItems(pllXTimers);
|
---|
[93] | 258 |
|
---|
| 259 | #ifdef DEBUG_XTIMERS
|
---|
| 260 | _Pmpf((__FUNCTION__ ": entering"));
|
---|
| 261 | #endif
|
---|
| 262 |
|
---|
[91] | 263 | if (!cTimers)
|
---|
[74] | 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:
|
---|
[86] | 281 |
|
---|
[91] | 282 | ULONG ulOldPMTimeout = pSet->ulPMTimeout;
|
---|
[74] | 283 |
|
---|
[91] | 284 | PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
|
---|
| 285 | PXTIMER pTimer1 = (PXTIMER)pNode->pItemData;
|
---|
| 286 |
|
---|
| 287 | if (cTimers == 1)
|
---|
[74] | 288 | {
|
---|
[86] | 289 | // only one timer:
|
---|
| 290 | // that's easy
|
---|
[93] | 291 | #ifdef DEBUG_XTIMERS
|
---|
| 292 | _Pmpf((" got 1 timer"));
|
---|
| 293 | #endif
|
---|
| 294 |
|
---|
[91] | 295 | pSet->ulPMTimeout = pTimer1->ulTimeout;
|
---|
[86] | 296 | }
|
---|
[91] | 297 | else if (cTimers == 2)
|
---|
[86] | 298 | {
|
---|
| 299 | // exactly two timers:
|
---|
| 300 | // find the greatest common denominator
|
---|
[91] | 301 | PXTIMER pTimer2 = (PXTIMER)pNode->pNext->pItemData;
|
---|
[93] | 302 | #ifdef DEBUG_XTIMERS
|
---|
| 303 | _Pmpf((" got 2 timers"));
|
---|
| 304 | #endif
|
---|
[74] | 305 |
|
---|
[86] | 306 | pSet->ulPMTimeout = mathGCD(pTimer1->ulTimeout,
|
---|
| 307 | pTimer2->ulTimeout);
|
---|
| 308 | }
|
---|
| 309 | else
|
---|
| 310 | {
|
---|
[91] | 311 | // more than two timers:
|
---|
[86] | 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 |
|
---|
[93] | 317 | #ifdef DEBUG_XTIMERS
|
---|
| 318 | _Pmpf((" got %d timers", cTimers));
|
---|
| 319 | #endif
|
---|
[86] | 320 |
|
---|
| 321 | // fill an array of integers with the
|
---|
| 322 | // timer frequencies
|
---|
| 323 | while (pNode)
|
---|
| 324 | {
|
---|
[91] | 325 | pTimer1 = (PXTIMER)pNode->pItemData;
|
---|
[86] | 326 |
|
---|
[93] | 327 | #ifdef DEBUG_XTIMERS
|
---|
| 328 | _Pmpf((" timeout %d is %d", i, pTimer1->ulTimeout));
|
---|
| 329 | #endif
|
---|
[86] | 330 |
|
---|
[91] | 331 | paInts[i++] = pTimer1->ulTimeout;
|
---|
[86] | 332 |
|
---|
| 333 | pNode = pNode->pNext;
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | pSet->ulPMTimeout = mathGCDMulti(paInts,
|
---|
| 337 | cTimers);
|
---|
[74] | 338 | }
|
---|
| 339 |
|
---|
[93] | 340 | #ifdef DEBUG_XTIMERS
|
---|
| 341 | _Pmpf(("--> GCD is %d", pSet->ulPMTimeout));
|
---|
| 342 | #endif
|
---|
| 343 |
|
---|
[91] | 344 | if ( (!pSet->idPMTimerRunning) // timer not running?
|
---|
[74] | 345 | || (pSet->ulPMTimeout != ulOldPMTimeout) // timeout changed?
|
---|
| 346 | )
|
---|
[93] | 347 | {
|
---|
[74] | 348 | // start or restart PM timer
|
---|
| 349 | pSet->idPMTimerRunning = WinStartTimer(pSet->hab,
|
---|
| 350 | pSet->hwndOwner,
|
---|
| 351 | pSet->idPMTimer,
|
---|
| 352 | pSet->ulPMTimeout);
|
---|
[93] | 353 | }
|
---|
[74] | 354 | }
|
---|
| 355 | }
|
---|
| 356 |
|
---|
[41] | 357 | /* ******************************************************************
|
---|
| 358 | *
|
---|
| 359 | * Exported functions
|
---|
| 360 | *
|
---|
| 361 | ********************************************************************/
|
---|
| 362 |
|
---|
[14] | 363 | /*
|
---|
[41] | 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 | *
|
---|
[44] | 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 | *
|
---|
[41] | 376 | * Use tmrDestroySet to free all resources again.
|
---|
| 377 | *
|
---|
| 378 | *@@added V0.9.9 (2001-02-28) [umoeller]
|
---|
| 379 | */
|
---|
| 380 |
|
---|
[44] | 381 | PXTIMERSET tmrCreateSet(HWND hwndOwner, // in: owner window
|
---|
[41] | 382 | USHORT usPMTimerID)
|
---|
| 383 | {
|
---|
[91] | 384 | PXTIMERSET pSet = NEW(XTIMERSET);
|
---|
[41] | 385 | if (pSet)
|
---|
| 386 | {
|
---|
| 387 | pSet->hab = WinQueryAnchorBlock(hwndOwner);
|
---|
| 388 | pSet->hwndOwner = hwndOwner;
|
---|
| 389 | pSet->idPMTimer = usPMTimerID;
|
---|
| 390 | pSet->idPMTimerRunning = 0;
|
---|
[44] | 391 | pSet->ulPMTimeout = 0;
|
---|
[41] | 392 |
|
---|
| 393 | pSet->pvllXTimers = (PVOID)lstCreate(TRUE);
|
---|
| 394 | }
|
---|
| 395 |
|
---|
[297] | 396 | return pSet;
|
---|
[41] | 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]
|
---|
[68] | 408 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
---|
[41] | 409 | */
|
---|
| 410 |
|
---|
[44] | 411 | VOID tmrDestroySet(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
|
---|
[41] | 412 | {
|
---|
[74] | 413 | if (LockTimers())
|
---|
[41] | 414 | {
|
---|
[74] | 415 | if (pSet)
|
---|
[41] | 416 | {
|
---|
[127] | 417 | PLINKLIST pllXTimers;
|
---|
| 418 | if (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
|
---|
[68] | 419 | {
|
---|
[74] | 420 | PLISTNODE pTimerNode = lstQueryFirstNode(pllXTimers);
|
---|
| 421 | while (pTimerNode)
|
---|
[68] | 422 | {
|
---|
[74] | 423 | PLISTNODE pNext = pTimerNode->pNext;
|
---|
[68] | 424 | PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
|
---|
| 425 | RemoveTimer(pSet, pTimer);
|
---|
[74] | 426 | pTimerNode = pNext;
|
---|
[68] | 427 | }
|
---|
| 428 |
|
---|
[74] | 429 | lstFree(&pllXTimers);
|
---|
| 430 | pSet->pvllXTimers = NULL;
|
---|
[41] | 431 | }
|
---|
| 432 |
|
---|
[127] | 433 | if (pSet->idPMTimerRunning)
|
---|
| 434 | {
|
---|
| 435 | WinStopTimer(pSet->hab,
|
---|
| 436 | pSet->hwndOwner,
|
---|
| 437 | pSet->idPMTimer);
|
---|
| 438 | pSet->idPMTimerRunning = 0;
|
---|
| 439 | }
|
---|
| 440 |
|
---|
| 441 | free(pSet);
|
---|
[44] | 442 | }
|
---|
| 443 |
|
---|
[74] | 444 | UnlockTimers();
|
---|
[44] | 445 | }
|
---|
[41] | 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]
|
---|
[68] | 460 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
---|
[91] | 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
|
---|
[93] | 463 | *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed "half frequency" regression caused by frequency optimizations
|
---|
[127] | 464 | *@@changed V0.9.16 (2001-12-18) [umoeller]: now using WinDispatchMsg to avoid crashes during win destruction
|
---|
[163] | 465 | *@@changed V0.9.19 (2002-05-04) [umoeller]: added excpt handling to avoid hanging all timers on the mutex
|
---|
[41] | 466 | */
|
---|
| 467 |
|
---|
[44] | 468 | VOID tmrTimerTick(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
|
---|
[41] | 469 | {
|
---|
[297] | 470 | volatile BOOL fLocked = FALSE; // XWP V1.0.4 (2005-10-09) [pr]
|
---|
[163] | 471 |
|
---|
| 472 | TRY_LOUD(excpt1)
|
---|
[41] | 473 | {
|
---|
[163] | 474 | if (fLocked = LockTimers())
|
---|
[68] | 475 | {
|
---|
[163] | 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;
|
---|
[127] | 485 |
|
---|
[163] | 486 | if (!(pTimerNode = lstQueryFirstNode(pllXTimers)))
|
---|
[68] | 487 | {
|
---|
[163] | 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;
|
---|
[127] | 500 | }
|
---|
[163] | 501 | else
|
---|
[74] | 502 | {
|
---|
[163] | 503 | // we have timers:
|
---|
| 504 | BOOL fFoundInvalid = FALSE;
|
---|
[68] | 505 |
|
---|
[163] | 506 | // get current time
|
---|
| 507 | ULONG ulTimeNow = 0;
|
---|
| 508 | DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
|
---|
| 509 | &ulTimeNow, sizeof(ulTimeNow));
|
---|
[41] | 510 |
|
---|
[93] | 511 | #ifdef DEBUG_XTIMERS
|
---|
[163] | 512 | _Pmpf((__FUNCTION__ ": ulTimeNow = %d", ulTimeNow));
|
---|
[93] | 513 | #endif
|
---|
| 514 |
|
---|
[163] | 515 | while (pTimerNode)
|
---|
[74] | 516 | {
|
---|
[163] | 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;
|
---|
[41] | 521 |
|
---|
[163] | 522 | PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
|
---|
| 523 |
|
---|
[93] | 524 | #ifdef DEBUG_XTIMERS
|
---|
[163] | 525 | _Pmpf((" timer %d: ulNextFire = %d",
|
---|
| 526 | lstIndexFromItem(pllXTimers, pTimer),
|
---|
| 527 | pTimer->ulNextFire));
|
---|
[93] | 528 | #endif
|
---|
| 529 |
|
---|
[163] | 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 | )
|
---|
[74] | 541 | {
|
---|
[163] | 542 | // this timer has elapsed:
|
---|
| 543 | // fire!
|
---|
[93] | 544 |
|
---|
[163] | 545 | #ifdef DEBUG_XTIMERS
|
---|
| 546 | _Pmpf((" --> fire!"));
|
---|
| 547 | #endif
|
---|
[93] | 548 |
|
---|
[163] | 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);
|
---|
[91] | 557 |
|
---|
[163] | 558 | // moved this up V0.9.14 (2001-08-01) [umoeller]
|
---|
| 559 | pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout;
|
---|
[91] | 560 |
|
---|
[163] | 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); */
|
---|
[41] | 575 |
|
---|
[163] | 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
|
---|
[91] | 587 |
|
---|
[163] | 588 | // V0.9.14 (2001-08-01) [umoeller]
|
---|
[41] | 589 |
|
---|
[163] | 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
|
---|
[41] | 601 |
|
---|
[163] | 602 | fFoundInvalid = TRUE;
|
---|
| 603 | }
|
---|
[68] | 604 |
|
---|
[163] | 605 | } // end if (pTimer->ulNextFire < ulTimeNow)
|
---|
[41] | 606 |
|
---|
[163] | 607 | // next timer
|
---|
| 608 | pTimerNode = pNext; // V0.9.12 (2001-05-24) [umoeller]
|
---|
| 609 | } // end while (pTimerNode)
|
---|
[41] | 610 |
|
---|
[163] | 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)
|
---|
[74] | 622 | UnlockTimers();
|
---|
[41] | 623 | }
|
---|
| 624 |
|
---|
| 625 | /*
|
---|
| 626 | *@@ tmrStartXTimer:
|
---|
[14] | 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 | *
|
---|
[127] | 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.
|
---|
[14] | 637 | *
|
---|
[264] | 638 | * Returns 0 if an error occurred. It is thus
|
---|
[127] | 639 | * invalid to specify a timer ID of 0.
|
---|
| 640 | *
|
---|
[14] | 641 | * The timer is _not_ stopped automatically
|
---|
[15] | 642 | * when the window is destroyed.
|
---|
[14] | 643 | *
|
---|
[86] | 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 | *
|
---|
[15] | 660 | *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
|
---|
[68] | 661 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
---|
[86] | 662 | *@@changed V0.9.14 (2001-07-12) [umoeller]: now rounding freq's to multiples of 25
|
---|
[14] | 663 | */
|
---|
| 664 |
|
---|
[44] | 665 | USHORT XWPENTRY tmrStartXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
---|
| 666 | HWND hwnd, // in: target window for XTimer
|
---|
[127] | 667 | USHORT usTimerID, // in: timer ID for XTimer's WM_TIMER (must be > 0)
|
---|
[44] | 668 | ULONG ulTimeout) // in: XTimer's timeout
|
---|
[14] | 669 | {
|
---|
| 670 | USHORT usrc = 0;
|
---|
| 671 |
|
---|
[41] | 672 | // _Pmpf((__FUNCTION__ ": entering"));
|
---|
| 673 |
|
---|
[74] | 674 | if (LockTimers())
|
---|
[14] | 675 | {
|
---|
[127] | 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 | )
|
---|
[14] | 683 | {
|
---|
[127] | 684 | PXTIMER pTimer;
|
---|
| 685 | ULONG ulTimeNow;
|
---|
[41] | 686 |
|
---|
[127] | 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;
|
---|
[68] | 695 |
|
---|
[127] | 696 | DosQuerySysInfo(QSV_MS_COUNT,
|
---|
| 697 | QSV_MS_COUNT,
|
---|
| 698 | &ulTimeNow,
|
---|
| 699 | sizeof(ulTimeNow));
|
---|
[86] | 700 |
|
---|
[127] | 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))
|
---|
[14] | 714 | {
|
---|
[127] | 715 | pTimer->usTimerID = usTimerID;
|
---|
| 716 | pTimer->hwndTarget = hwnd;
|
---|
| 717 | pTimer->ulTimeout = ulTimeout;
|
---|
[15] | 718 | pTimer->ulNextFire = ulTimeNow + ulTimeout;
|
---|
[127] | 719 |
|
---|
| 720 | lstAppendItem(pllXTimers,
|
---|
| 721 | pTimer);
|
---|
[91] | 722 | usrc = usTimerID;
|
---|
[14] | 723 | }
|
---|
[127] | 724 | }
|
---|
[14] | 725 |
|
---|
[127] | 726 | if (usrc)
|
---|
| 727 | // timer created or reset:
|
---|
| 728 | AdjustPMTimer(pSet);
|
---|
[68] | 729 |
|
---|
[127] | 730 | } // if ((hwnd) && (ulTimeout))
|
---|
[74] | 731 |
|
---|
| 732 | UnlockTimers();
|
---|
[41] | 733 | }
|
---|
[14] | 734 |
|
---|
[297] | 735 | return usrc;
|
---|
[14] | 736 | }
|
---|
| 737 |
|
---|
| 738 | /*
|
---|
[41] | 739 | *@@ tmrStopXTimer:
|
---|
[14] | 740 | * similar to WinStopTimer, this stops the
|
---|
| 741 | * specified timer (which must have been
|
---|
| 742 | * started with the same hwnd and usTimerID
|
---|
[41] | 743 | * using tmrStartXTimer).
|
---|
[14] | 744 | *
|
---|
| 745 | * Returns TRUE if the timer was stopped.
|
---|
[68] | 746 | *
|
---|
| 747 | *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
|
---|
[14] | 748 | */
|
---|
| 749 |
|
---|
[44] | 750 | BOOL XWPENTRY tmrStopXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
|
---|
[41] | 751 | HWND hwnd,
|
---|
| 752 | USHORT usTimerID)
|
---|
[14] | 753 | {
|
---|
| 754 | BOOL brc = FALSE;
|
---|
[74] | 755 | if (LockTimers())
|
---|
[41] | 756 | {
|
---|
[127] | 757 | PXTIMER pTimer;
|
---|
[163] | 758 | #ifdef DEBUG_XTIMERS
|
---|
| 759 | _Pmpf((__FUNCTION__ ": finding timer %d", usTimerID));
|
---|
| 760 | #endif
|
---|
[127] | 761 | if (pTimer = FindTimer(pSet,
|
---|
| 762 | hwnd,
|
---|
| 763 | usTimerID))
|
---|
| 764 | // FindTimer checks the params
|
---|
[68] | 765 | {
|
---|
[127] | 766 | RemoveTimer(pSet, pTimer);
|
---|
| 767 | // recalculate
|
---|
| 768 | AdjustPMTimer(pSet);
|
---|
| 769 | brc = TRUE;
|
---|
[74] | 770 | }
|
---|
[68] | 771 |
|
---|
[74] | 772 | UnlockTimers();
|
---|
[14] | 773 | }
|
---|
| 774 |
|
---|
[167] | 775 | return brc;
|
---|
[14] | 776 | }
|
---|
| 777 |
|
---|
| 778 |
|
---|