| 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-2005 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 |  | 
|---|
| 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 | volatile BOOL fLocked = FALSE; // XWP V1.0.4 (2005-10-09) [pr] | 
|---|
| 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 occurred. 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 |  | 
|---|