source: trunk/src/helpers/timer.c@ 133

Last change on this file since 133 was 127, checked in by umoeller, 24 years ago

Tons of updates for turbo folders and replacement icons.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 25.1 KB
Line 
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 M”ller.
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/*
114 *@@category: Helpers\PM helpers\Timer replacements
115 * see timer.c.
116 */
117
118/* ******************************************************************
119 *
120 * Private declarations
121 *
122 ********************************************************************/
123
124/*
125 *@@ XTIMER:
126 * one of these represents an XTimer.
127 * These are stored in a linked list in
128 * an _XTIMERSET.
129 */
130
131typedef struct _XTIMER
132{
133 USHORT usTimerID; // timer ID, as passed to tmrStartXTimer
134 HWND hwndTarget; // target window, as passed to tmrStartXTimer
135 ULONG ulTimeout; // timer's timeout (in ms)
136 ULONG ulNextFire; // next time scalar (from dtGetULongTime) to fire at
137} XTIMER, *PXTIMER;
138
139/* ******************************************************************
140 *
141 * Global variables
142 *
143 ********************************************************************/
144
145HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex
146
147/* ******************************************************************
148 *
149 * Private functions
150 *
151 ********************************************************************/
152
153/*
154 *@@ LockTimers:
155 *
156 *@@added V0.9.12 (2001-05-12) [umoeller]
157 */
158
159BOOL LockTimers(VOID)
160{
161 if (!G_hmtxTimers)
162 return (!DosCreateMutexSem(NULL,
163 &G_hmtxTimers,
164 0,
165 TRUE)); // request!
166 else
167 return (!WinRequestMutexSem(G_hmtxTimers, SEM_INDEFINITE_WAIT));
168}
169
170/*
171 *@@ UnlockTimers:
172 *
173 *@@added V0.9.12 (2001-05-12) [umoeller]
174 */
175
176VOID UnlockTimers(VOID)
177{
178 DosReleaseMutexSem(G_hmtxTimers);
179}
180
181/*
182 *@@ FindTimer:
183 * returns the XTIMER structure from the global
184 * linked list which matches the given window
185 * _and_ timer ID.
186 *
187 * Internal function. Caller must hold the mutex.
188 */
189
190PXTIMER FindTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
191 HWND hwnd, // in: timer target window
192 USHORT usTimerID) // in: timer ID
193{
194 PLINKLIST pllXTimers;
195 if ( (pSet)
196 && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
197 )
198 {
199 PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
200 while (pNode)
201 {
202 PXTIMER pTimer = (PXTIMER)pNode->pItemData;
203 if ( (pTimer->usTimerID == usTimerID)
204 && (pTimer->hwndTarget == hwnd)
205 )
206 {
207 return (pTimer);
208 }
209
210 pNode = pNode->pNext;
211 }
212 }
213
214 return (NULL);
215}
216
217/*
218 *@@ RemoveTimer:
219 * removes the specified XTIMER structure from
220 * the global linked list of running timers.
221 *
222 * Internal function. Caller must hold the mutex.
223 */
224
225VOID RemoveTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
226 PXTIMER pTimer) // in: timer to remove.
227{
228 PLINKLIST pllXTimers;
229 if ( (pSet)
230 && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
231 )
232 {
233 lstRemoveItem(pllXTimers,
234 pTimer); // auto-free!
235 }
236}
237
238/*
239 *@@ AdjustPMTimer:
240 * goes thru all XTimers in the sets and starts
241 * or stops the PM timer with a decent frequency.
242 *
243 * Internal function. Caller must hold the mutex.
244 *
245 *@@added V0.9.9 (2001-03-07) [umoeller]
246 *@@changed V0.9.14 (2001-07-07) [umoeller]: added GCD optimizations
247 */
248
249VOID AdjustPMTimer(PXTIMERSET pSet)
250{
251 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers;
252 ULONG cTimers = lstCountItems(pllXTimers);
253
254 #ifdef DEBUG_XTIMERS
255 _Pmpf((__FUNCTION__ ": entering"));
256 #endif
257
258 if (!cTimers)
259 {
260 // no XTimers running:
261 if (pSet->idPMTimerRunning)
262 {
263 // but PM timer running:
264 // stop it
265 WinStopTimer(pSet->hab,
266 pSet->hwndOwner,
267 pSet->idPMTimer);
268 pSet->idPMTimerRunning = 0;
269 }
270
271 pSet->ulPMTimeout = 0;
272 }
273 else
274 {
275 // we have timers:
276
277 ULONG ulOldPMTimeout = pSet->ulPMTimeout;
278
279 PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
280 PXTIMER pTimer1 = (PXTIMER)pNode->pItemData;
281
282 if (cTimers == 1)
283 {
284 // only one timer:
285 // that's easy
286 #ifdef DEBUG_XTIMERS
287 _Pmpf((" got 1 timer"));
288 #endif
289
290 pSet->ulPMTimeout = pTimer1->ulTimeout;
291 }
292 else if (cTimers == 2)
293 {
294 // exactly two timers:
295 // find the greatest common denominator
296 PXTIMER pTimer2 = (PXTIMER)pNode->pNext->pItemData;
297 #ifdef DEBUG_XTIMERS
298 _Pmpf((" got 2 timers"));
299 #endif
300
301 pSet->ulPMTimeout = mathGCD(pTimer1->ulTimeout,
302 pTimer2->ulTimeout);
303 }
304 else
305 {
306 // more than two timers:
307 // run through all timers and find the greatest
308 // common denominator of all frequencies...
309 int *paInts = (int*)_alloca(sizeof(int) * cTimers),
310 i = 0;
311
312 #ifdef DEBUG_XTIMERS
313 _Pmpf((" got %d timers", cTimers));
314 #endif
315
316 // fill an array of integers with the
317 // timer frequencies
318 while (pNode)
319 {
320 pTimer1 = (PXTIMER)pNode->pItemData;
321
322 #ifdef DEBUG_XTIMERS
323 _Pmpf((" timeout %d is %d", i, pTimer1->ulTimeout));
324 #endif
325
326 paInts[i++] = pTimer1->ulTimeout;
327
328 pNode = pNode->pNext;
329 }
330
331 pSet->ulPMTimeout = mathGCDMulti(paInts,
332 cTimers);
333 }
334
335 #ifdef DEBUG_XTIMERS
336 _Pmpf(("--> GCD is %d", pSet->ulPMTimeout));
337 #endif
338
339 if ( (!pSet->idPMTimerRunning) // timer not running?
340 || (pSet->ulPMTimeout != ulOldPMTimeout) // timeout changed?
341 )
342 {
343 // start or restart PM timer
344 pSet->idPMTimerRunning = WinStartTimer(pSet->hab,
345 pSet->hwndOwner,
346 pSet->idPMTimer,
347 pSet->ulPMTimeout);
348 }
349 }
350}
351
352/* ******************************************************************
353 *
354 * Exported functions
355 *
356 ********************************************************************/
357
358/*
359 *@@ tmrCreateSet:
360 * creates a "timer set" for use with the XTimer functions.
361 * This is the first step if you want to use the XTimers.
362 *
363 * hwndOwner must specify the "owner window", the target
364 * window for the master PM timer. This window must respond
365 * to a WM_TIMER message with the specified usPMTimerID and
366 * invoke tmrTimerTick then.
367 *
368 * Note that the master timer is not started until an XTimer
369 * is started.
370 *
371 * Use tmrDestroySet to free all resources again.
372 *
373 *@@added V0.9.9 (2001-02-28) [umoeller]
374 */
375
376PXTIMERSET tmrCreateSet(HWND hwndOwner, // in: owner window
377 USHORT usPMTimerID)
378{
379 PXTIMERSET pSet = NEW(XTIMERSET);
380 if (pSet)
381 {
382 pSet->hab = WinQueryAnchorBlock(hwndOwner);
383 pSet->hwndOwner = hwndOwner;
384 pSet->idPMTimer = usPMTimerID;
385 pSet->idPMTimerRunning = 0;
386 pSet->ulPMTimeout = 0;
387
388 pSet->pvllXTimers = (PVOID)lstCreate(TRUE);
389 }
390
391 return (pSet);
392}
393
394/*
395 *@@ tmrDestroySet:
396 * destroys a timer set previously created using
397 * tmrCreateSet.
398 *
399 * Of course, this will stop all XTimers which
400 * might still be running with this set.
401 *
402 *@@added V0.9.9 (2001-02-28) [umoeller]
403 *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
404 */
405
406VOID tmrDestroySet(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
407{
408 if (LockTimers())
409 {
410 if (pSet)
411 {
412 PLINKLIST pllXTimers;
413 if (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
414 {
415 PLISTNODE pTimerNode = lstQueryFirstNode(pllXTimers);
416 while (pTimerNode)
417 {
418 PLISTNODE pNext = pTimerNode->pNext;
419 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
420 RemoveTimer(pSet, pTimer);
421 pTimerNode = pNext;
422 }
423
424 lstFree(&pllXTimers);
425 pSet->pvllXTimers = NULL;
426 }
427
428 if (pSet->idPMTimerRunning)
429 {
430 WinStopTimer(pSet->hab,
431 pSet->hwndOwner,
432 pSet->idPMTimer);
433 pSet->idPMTimerRunning = 0;
434 }
435
436 free(pSet);
437 }
438
439 UnlockTimers();
440 }
441}
442
443/*
444 *@@ tmrTimerTick:
445 * implements a PM timer tick.
446 *
447 * When your window procs receives WM_TIMER for the
448 * one PM timer which is supposed to trigger all the
449 * XTimers, it must call this function. This will
450 * evaluate all XTimers on the list and "fire" them
451 * by calling the window procs directly with the
452 * WM_TIMER message.
453 *
454 *@@added V0.9.9 (2001-02-28) [umoeller]
455 *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
456 *@@changed V0.9.12 (2001-05-24) [umoeller]: fixed crash if this got called during tmrTimerTick
457 *@@changed V0.9.14 (2001-08-01) [umoeller]: fixed mem overwrite which might have caused crashes if this got called during tmrTimerTick
458 *@@changed V0.9.14 (2001-08-03) [umoeller]: fixed "half frequency" regression caused by frequency optimizations
459 *@@changed V0.9.16 (2001-12-18) [umoeller]: now using WinDispatchMsg to avoid crashes during win destruction
460 */
461
462VOID tmrTimerTick(PXTIMERSET pSet) // in: timer set (from tmrCreateSet)
463{
464 if (LockTimers())
465 {
466 PLINKLIST pllXTimers;
467 if ( (pSet)
468 && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
469 )
470 {
471 // go thru all XTimers and see which one
472 // has elapsed; for all of these, post WM_TIMER
473 // to the target window proc
474 PLISTNODE pTimerNode = lstQueryFirstNode(pllXTimers);
475
476 if (!pTimerNode)
477 {
478 // no timers left:
479 if (pSet->idPMTimerRunning)
480 {
481 // but PM timer running:
482 // stop it
483 WinStopTimer(pSet->hab,
484 pSet->hwndOwner,
485 pSet->idPMTimer);
486 pSet->idPMTimerRunning = 0;
487 }
488
489 pSet->ulPMTimeout = 0;
490 }
491 else
492 {
493 // we have timers:
494 BOOL fFoundInvalid = FALSE;
495
496 // get current time
497 ULONG ulTimeNow = 0;
498 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
499 &ulTimeNow, sizeof(ulTimeNow));
500
501 #ifdef DEBUG_XTIMERS
502 _Pmpf((__FUNCTION__ ": ulTimeNow = %d", ulTimeNow));
503 #endif
504
505 while (pTimerNode)
506 {
507 // get next node first because the
508 // list can get modified while processing
509 // V0.9.12 (2001-05-24) [umoeller]
510 PLISTNODE pNext = pTimerNode->pNext;
511
512 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
513
514 #ifdef DEBUG_XTIMERS
515 _Pmpf((" timer %d: ulNextFire = %d",
516 lstIndexFromItem(pllXTimers, pTimer),
517 pTimer->ulNextFire));
518 #endif
519
520 if ( (pTimer)
521 // && (pTimer->ulNextFire < ulTimeNow)
522 // V0.9.14 (2001-08-01) [umoeller]
523 // use <= because otherwise we'll get
524 // only half the frequency...
525 // we get here frequently where the
526 // two values are EXACTLY equal due
527 // to the above optimization (DosQuerySysInfo
528 // called once only for the entire loop)
529 && (pTimer->ulNextFire <= ulTimeNow)
530 )
531 {
532 // this timer has elapsed:
533 // fire!
534
535 #ifdef DEBUG_XTIMERS
536 _Pmpf((" --> fire!"));
537 #endif
538
539 if (WinIsWindow(pSet->hab,
540 pTimer->hwndTarget))
541 {
542 // window still valid:
543 // get the window's window proc
544 QMSG qmsg;
545 /* PFNWP pfnwp = (PFNWP)WinQueryWindowPtr(pTimer->hwndTarget,
546 QWP_PFNWP); */
547
548 // moved this up V0.9.14 (2001-08-01) [umoeller]
549 pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout;
550
551 // call the window proc DIRECTLY
552 // V0.9.16 (2001-12-18) [umoeller]:
553 // now using WinDispatchMsg to avoid crashes
554 // while hwndTarget is being destroyed
555 qmsg.hwnd = pTimer->hwndTarget;
556 qmsg.msg = WM_TIMER;
557 qmsg.mp1 = (MPARAM)pTimer->usTimerID;
558 qmsg.mp2 = (MPARAM)0;
559 qmsg.time = 0;
560 qmsg.ptl.x = 0;
561 qmsg.ptl.y = 0;
562 qmsg.reserved = 0;
563 WinDispatchMsg(pSet->hab,
564 &qmsg);
565
566 /* pfnwp(pTimer->hwndTarget,
567 WM_TIMER,
568 (MPARAM)pTimer->usTimerID,
569 0); */
570 // V0.9.12 (2001-05-24) [umoeller]
571 // if the winproc chooses to start or
572 // stop a timer, pNext still points
573 // to a valid node...
574 // -- if a timer is removed, that's OK
575 // -- if a timer is added, it is added to
576 // the list, so we'll see it in this loop
577
578 // V0.9.14 (2001-08-01) [umoeller]
579
580 // DO NOT REFERENCE pTimer AFTER THIS CODE;
581 // the winproc might have removed the timer,
582 // and since the list is auto-free, pTimer
583 // might have been freed!!
584 }
585 else
586 {
587 // window has been destroyed:
588 lstRemoveNode(pllXTimers,
589 pTimerNode);
590 // pNext is still valid
591
592 fFoundInvalid = TRUE;
593 }
594
595 } // end if (pTimer->ulNextFire < ulTimeNow)
596
597 // next timer
598 pTimerNode = pNext; // V0.9.12 (2001-05-24) [umoeller]
599 } // end while (pTimerNode)
600
601 // destroy invalid timers, if any
602 if (fFoundInvalid)
603 AdjustPMTimer(pSet);
604
605 } // end else if (!pTimerNode)
606 } // end if (pllXTimers)
607
608 UnlockTimers();
609 } // end if (LockTimers())
610}
611
612/*
613 *@@ tmrStartXTimer:
614 * starts an XTimer.
615 *
616 * Any window can request an XTimer using
617 * this function. This operates similar to
618 * WinStartTimer, except that the number of
619 * XTimers is not limited.
620 *
621 * Returns the ID of a new timer or resets an
622 * existing timer (if usTimerID is already used
623 * with hwnd). Use tmrStopXTimer to stop the timer.
624 *
625 * Returns 0 if an error occured. It is thus
626 * invalid to specify a timer ID of 0.
627 *
628 * The timer is _not_ stopped automatically
629 * when the window is destroyed.
630 *
631 * Note: Unless you own the timer set that
632 * your timer runs on, it is strongly recommended
633 * that your timer frequency is set to a multiple
634 * of 125. The PM master timer behind the timer
635 * set will be set to the greatest common divisor
636 * of all frequencies, and if you set one timer
637 * to 2000 and the other one to 2001, you will
638 * cause quite a lot of overhead. This applies
639 * especially to timers started by XCenter widgets.
640 *
641 * For security, all timer frequencies will be
642 * rounded to multiples of 25 anyway. Still,
643 * running two timers at 1000 and 1025 will cause
644 * the master timer to be set to 25, which is
645 * overkill.
646 *
647 *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
648 *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
649 *@@changed V0.9.14 (2001-07-12) [umoeller]: now rounding freq's to multiples of 25
650 */
651
652USHORT XWPENTRY tmrStartXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
653 HWND hwnd, // in: target window for XTimer
654 USHORT usTimerID, // in: timer ID for XTimer's WM_TIMER (must be > 0)
655 ULONG ulTimeout) // in: XTimer's timeout
656{
657 USHORT usrc = 0;
658
659 // _Pmpf((__FUNCTION__ ": entering"));
660
661 if (LockTimers())
662 {
663 PLINKLIST pllXTimers;
664 if ( (pSet)
665 && (pllXTimers = (PLINKLIST)pSet->pvllXTimers)
666 && (hwnd)
667 && (ulTimeout)
668 && (usTimerID) // V0.9.16 (2001-12-18) [umoeller]
669 )
670 {
671 PXTIMER pTimer;
672 ULONG ulTimeNow;
673
674 // fix the timeout... we allow only multiples of
675 // 25, and it must be at least 25 (otherwise our
676 // internal master timer calculations will fail)
677 // V0.9.14 (2001-07-07) [umoeller]
678 if (ulTimeout < 25)
679 ulTimeout = 25;
680 else
681 ulTimeout = (ulTimeout + 10) / 25 * 25;
682
683 DosQuerySysInfo(QSV_MS_COUNT,
684 QSV_MS_COUNT,
685 &ulTimeNow,
686 sizeof(ulTimeNow));
687
688 // check if this timer exists already
689 if (pTimer = FindTimer(pSet,
690 hwnd,
691 usTimerID))
692 {
693 // exists already: reset only
694 pTimer->ulNextFire = ulTimeNow + ulTimeout;
695 usrc = usTimerID;
696 }
697 else
698 {
699 // new timer needed:
700 if (pTimer = NEW(XTIMER))
701 {
702 pTimer->usTimerID = usTimerID;
703 pTimer->hwndTarget = hwnd;
704 pTimer->ulTimeout = ulTimeout;
705 pTimer->ulNextFire = ulTimeNow + ulTimeout;
706
707 lstAppendItem(pllXTimers,
708 pTimer);
709 usrc = usTimerID;
710 }
711 }
712
713 if (usrc)
714 // timer created or reset:
715 AdjustPMTimer(pSet);
716
717 } // if ((hwnd) && (ulTimeout))
718
719 UnlockTimers();
720 }
721
722 return (usrc);
723}
724
725/*
726 *@@ tmrStopXTimer:
727 * similar to WinStopTimer, this stops the
728 * specified timer (which must have been
729 * started with the same hwnd and usTimerID
730 * using tmrStartXTimer).
731 *
732 * Returns TRUE if the timer was stopped.
733 *
734 *@@changed V0.9.12 (2001-05-12) [umoeller]: added mutex protection
735 */
736
737BOOL XWPENTRY tmrStopXTimer(PXTIMERSET pSet, // in: timer set (from tmrCreateSet)
738 HWND hwnd,
739 USHORT usTimerID)
740{
741 BOOL brc = FALSE;
742 if (LockTimers())
743 {
744 PXTIMER pTimer;
745 if (pTimer = FindTimer(pSet,
746 hwnd,
747 usTimerID))
748 // FindTimer checks the params
749 {
750 RemoveTimer(pSet, pTimer);
751 // recalculate
752 AdjustPMTimer(pSet);
753 brc = TRUE;
754 }
755
756 UnlockTimers();
757 }
758
759 return (brc);
760}
761
762
Note: See TracBrowser for help on using the repository browser.