4 * Copyright 1993 Alexandre Julliard
13 DEFAULT_DEBUG_CHANNEL(timer)
16 typedef struct tagTIMER
20 UINT16 msg; /* WM_TIMER or WM_SYSTIMER */
23 struct tagTIMER *next;
24 DWORD expires; /* Next expiration, or 0 if already expired */
29 #define NB_RESERVED_TIMERS 2 /* for SetSystemTimer */
31 static TIMER TimersArray[NB_TIMERS];
33 static TIMER * pNextTimer = NULL; /* Next timer to expire */
35 static CRITICAL_SECTION csTimer;
37 /* Duration from 'time' until expiration of the timer */
38 #define EXPIRE_TIME(pTimer,time) \
39 (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
42 /***********************************************************************
45 * Initialize critical section for the timer.
47 BOOL TIMER_Init( void )
49 InitializeCriticalSection( &csTimer );
50 MakeCriticalSectionGlobal( &csTimer );
56 /***********************************************************************
59 * Insert the timer at its place in the chain.
61 static void TIMER_InsertTimer( TIMER * pTimer )
63 EnterCriticalSection( &csTimer );
65 if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
67 pTimer->next = pNextTimer;
72 TIMER * ptr = pNextTimer;
73 while (ptr->next && (pTimer->expires >= ptr->next->expires))
75 pTimer->next = ptr->next;
79 LeaveCriticalSection( &csTimer );
83 /***********************************************************************
86 * Remove the timer from the chain.
88 static void TIMER_RemoveTimer( TIMER * pTimer )
90 TIMER **ppTimer = &pNextTimer;
92 EnterCriticalSection( &csTimer );
94 while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
95 if (*ppTimer) *ppTimer = pTimer->next;
98 LeaveCriticalSection( &csTimer );
100 if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
104 /***********************************************************************
107 * Clear and remove a timer.
109 static void TIMER_ClearTimer( TIMER * pTimer )
111 TIMER_RemoveTimer( pTimer );
116 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
120 /***********************************************************************
123 void TIMER_SwitchQueue( HQUEUE16 old, HQUEUE16 new )
127 EnterCriticalSection( &csTimer );
132 if (pT->hq == old) pT->hq = new;
136 LeaveCriticalSection( &csTimer );
140 /***********************************************************************
141 * TIMER_RemoveWindowTimers
143 * Remove all timers for a given window.
145 void TIMER_RemoveWindowTimers( HWND hwnd )
150 EnterCriticalSection( &csTimer );
152 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
153 if ((pTimer->hwnd == hwnd) && pTimer->timeout)
154 TIMER_ClearTimer( pTimer );
156 LeaveCriticalSection( &csTimer );
160 /***********************************************************************
161 * TIMER_RemoveQueueTimers
163 * Remove all timers for a given queue.
165 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
170 EnterCriticalSection( &csTimer );
172 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
173 if ((pTimer->hq == hqueue) && pTimer->timeout)
174 TIMER_ClearTimer( pTimer );
176 LeaveCriticalSection( &csTimer );
180 /***********************************************************************
181 * TIMER_RestartTimers
183 * Restart an expired timer.
185 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
187 TIMER_RemoveTimer( pTimer );
188 pTimer->expires = curTime + pTimer->timeout;
189 TIMER_InsertTimer( pTimer );
193 /***********************************************************************
194 * TIMER_GetNextExpiration
196 * Return next timer expiration time, or -1 if none.
198 LONG TIMER_GetNextExpiration(void)
203 EnterCriticalSection( &csTimer );
207 while (pTimer && !pTimer->expires) /* Skip already expired timers */
208 pTimer = pTimer->next;
212 DWORD now = GetTickCount();
213 retValue = (pTimer->expires <= now) ? 0 : (pTimer->expires - now);
217 LeaveCriticalSection( &csTimer );
222 /***********************************************************************
225 * Mark expired timers and wake the appropriate queues.
227 void TIMER_ExpireTimers(void)
230 DWORD curTime = GetTickCount();
232 EnterCriticalSection( &csTimer );
236 while (pTimer && !pTimer->expires) /* Skip already expired timers */
237 pTimer = pTimer->next;
238 while (pTimer && (pTimer->expires <= curTime))
241 QUEUE_IncTimerCount( pTimer->hq );
242 pTimer = pTimer->next;
245 LeaveCriticalSection( &csTimer );
249 /***********************************************************************
252 * Build a message for an expired timer.
254 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
255 HQUEUE16 hQueue, BOOL remove )
258 DWORD curTime = GetTickCount();
260 EnterCriticalSection( &csTimer );
264 if (hwnd) /* Find first timer for this window */
265 while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
266 else /* Find first timer for this queue */
267 while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
269 if (!pTimer || (pTimer->expires > curTime))
271 LeaveCriticalSection( &csTimer );
272 return FALSE; /* No timer */
275 if (remove) TIMER_RestartTimer( pTimer, curTime ); /* Restart it */
277 TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n",
278 pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
280 /* Build the message */
281 msg->hwnd = pTimer->hwnd;
282 msg->message = pTimer->msg;
283 msg->wParam = pTimer->id;
284 msg->lParam = (LONG)pTimer->proc;
287 LeaveCriticalSection( &csTimer );
293 /***********************************************************************
296 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
297 WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
302 if (!timeout) return 0;
304 EnterCriticalSection( &csTimer );
306 /* Check if there's already a timer with the same hwnd and id */
308 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
309 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
310 (pTimer->timeout != 0))
312 /* Got one: set new values and return */
313 TIMER_RemoveTimer( pTimer );
314 pTimer->timeout = timeout;
315 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
316 pTimer->proc = (HWINDOWPROC)0;
317 if (proc) WINPROC_SetProc( &pTimer->proc, proc,
318 type, WIN_PROC_TIMER );
319 pTimer->expires = GetTickCount() + timeout;
320 TIMER_InsertTimer( pTimer );
321 LeaveCriticalSection( &csTimer );
325 /* Find a free timer */
327 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
328 if (!pTimer->timeout) break;
330 if ( (i >= NB_TIMERS) ||
331 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
333 LeaveCriticalSection( &csTimer );
337 if (!hwnd) id = i + 1;
342 pTimer->hq = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
344 pTimer->msg = sys ? WM_SYSTIMER : WM_TIMER;
346 pTimer->timeout = timeout;
347 pTimer->expires = GetTickCount() + timeout;
348 pTimer->proc = (HWINDOWPROC)0;
349 if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
350 TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n",
351 pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
352 (DWORD)pTimer->proc );
353 TIMER_InsertTimer( pTimer );
355 LeaveCriticalSection( &csTimer );
357 if (!id) return TRUE;
362 /***********************************************************************
365 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
370 EnterCriticalSection( &csTimer );
374 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
375 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
376 (pTimer->timeout != 0)) break;
378 if ( (i >= NB_TIMERS) ||
379 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
380 (!sys && (pTimer->msg != WM_TIMER)) ||
381 (sys && (pTimer->msg != WM_SYSTIMER)) )
383 LeaveCriticalSection( &csTimer );
387 /* Delete the timer */
389 TIMER_ClearTimer( pTimer );
391 LeaveCriticalSection( &csTimer );
397 /***********************************************************************
398 * SetTimer16 (USER.10)
400 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
403 TRACE(timer, "%04x %d %d %08lx\n",
404 hwnd, id, timeout, (LONG)proc );
405 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
406 WIN_PROC_16, FALSE );
410 /***********************************************************************
411 * SetTimer32 (USER32.511)
413 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
416 TRACE(timer, "%04x %d %d %08lx\n",
417 hwnd, id, timeout, (LONG)proc );
418 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
419 WIN_PROC_32A, FALSE );
423 /***********************************************************************
424 * SetSystemTimer16 (USER.11)
426 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
429 TRACE(timer, "%04x %d %d %08lx\n",
430 hwnd, id, timeout, (LONG)proc );
431 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
436 /***********************************************************************
437 * SetSystemTimer32 (USER32.509)
439 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
442 TRACE(timer, "%04x %d %d %08lx\n",
443 hwnd, id, timeout, (LONG)proc );
444 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
445 WIN_PROC_32A, TRUE );
449 /***********************************************************************
450 * KillTimer16 (USER.12)
452 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
454 TRACE(timer, "%04x %d\n", hwnd, id );
455 return TIMER_KillTimer( hwnd, id, FALSE );
459 /***********************************************************************
460 * KillTimer32 (USER32.354)
462 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
464 TRACE(timer, "%04x %d\n", hwnd, id );
465 return TIMER_KillTimer( hwnd, id, FALSE );
469 /***********************************************************************
470 * KillSystemTimer16 (USER.182)
472 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
474 TRACE(timer, "%04x %d\n", hwnd, id );
475 return TIMER_KillTimer( hwnd, id, TRUE );
479 /***********************************************************************
480 * KillSystemTimer32 (USER32.353)
482 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
484 TRACE(timer, "%04x %d\n", hwnd, id );
485 return TIMER_KillTimer( hwnd, id, TRUE );