4 * Copyright 1993 Alexandre Julliard
14 DEFAULT_DEBUG_CHANNEL(timer)
17 typedef struct tagTIMER
21 UINT16 msg; /* WM_TIMER or WM_SYSTIMER */
24 struct tagTIMER *next;
25 DWORD expires; /* Next expiration, or 0 if already expired */
30 #define NB_RESERVED_TIMERS 2 /* for SetSystemTimer */
32 static TIMER TimersArray[NB_TIMERS];
34 static TIMER * pNextTimer = NULL; /* Next timer to expire */
36 static CRITICAL_SECTION csTimer;
38 /* Duration from 'time' until expiration of the timer */
39 #define EXPIRE_TIME(pTimer,time) \
40 (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
43 /***********************************************************************
46 * Initialize critical section for the timer.
48 BOOL TIMER_Init( void )
50 InitializeCriticalSection( &csTimer );
51 MakeCriticalSectionGlobal( &csTimer );
57 /***********************************************************************
60 * Insert the timer at its place in the chain.
62 static void TIMER_InsertTimer( TIMER * pTimer )
64 EnterCriticalSection( &csTimer );
66 if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
68 pTimer->next = pNextTimer;
73 TIMER * ptr = pNextTimer;
74 while (ptr->next && (pTimer->expires >= ptr->next->expires))
76 pTimer->next = ptr->next;
80 LeaveCriticalSection( &csTimer );
84 /***********************************************************************
87 * Remove the timer from the chain.
89 static void TIMER_RemoveTimer( TIMER * pTimer )
91 TIMER **ppTimer = &pNextTimer;
93 EnterCriticalSection( &csTimer );
95 while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
96 if (*ppTimer) *ppTimer = pTimer->next;
99 LeaveCriticalSection( &csTimer );
101 if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
105 /***********************************************************************
108 * Clear and remove a timer.
110 static void TIMER_ClearTimer( TIMER * pTimer )
112 TIMER_RemoveTimer( pTimer );
117 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
121 /***********************************************************************
124 void TIMER_SwitchQueue( HQUEUE16 old, HQUEUE16 new )
128 EnterCriticalSection( &csTimer );
133 if (pT->hq == old) pT->hq = new;
137 LeaveCriticalSection( &csTimer );
141 /***********************************************************************
142 * TIMER_RemoveWindowTimers
144 * Remove all timers for a given window.
146 void TIMER_RemoveWindowTimers( HWND hwnd )
151 EnterCriticalSection( &csTimer );
153 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
154 if ((pTimer->hwnd == hwnd) && pTimer->timeout)
155 TIMER_ClearTimer( pTimer );
157 LeaveCriticalSection( &csTimer );
161 /***********************************************************************
162 * TIMER_RemoveQueueTimers
164 * Remove all timers for a given queue.
166 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
171 EnterCriticalSection( &csTimer );
173 for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
174 if ((pTimer->hq == hqueue) && pTimer->timeout)
175 TIMER_ClearTimer( pTimer );
177 LeaveCriticalSection( &csTimer );
181 /***********************************************************************
182 * TIMER_RestartTimers
184 * Restart an expired timer.
186 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
188 TIMER_RemoveTimer( pTimer );
189 pTimer->expires = curTime + pTimer->timeout;
190 TIMER_InsertTimer( pTimer );
193 /***********************************************************************
196 * Mark expired timers and wake the appropriate queues.
198 static void CALLBACK TIMER_CheckTimers( ULONG_PTR forceTimer )
200 static HANDLE ServiceHandle = INVALID_HANDLE_VALUE;
201 static LONG ServiceTimeout = 0;
204 DWORD curTime = GetTickCount();
206 EnterCriticalSection( &csTimer );
208 TRACE(timer, "Called at %ld (%s)\n", curTime, forceTimer? "manual" : "auto" );
212 while (pTimer && !pTimer->expires) /* Skip already expired timers */
213 pTimer = pTimer->next;
214 while (pTimer && (pTimer->expires <= curTime))
217 QUEUE_IncTimerCount( pTimer->hq );
218 pTimer = pTimer->next;
221 /* Install service callback with appropriate timeout, so that
222 we get called again once the next timer has expired */
226 LONG timeout = pTimer->expires - curTime;
228 if ( forceTimer || timeout != ServiceTimeout )
230 if ( ServiceHandle != INVALID_HANDLE_VALUE )
231 SERVICE_Delete( ServiceHandle );
233 ServiceHandle = SERVICE_AddTimer( timeout * 1000L,
234 TIMER_CheckTimers, FALSE );
235 ServiceTimeout = timeout;
237 TRACE(timer, "Installed service callback with timeout %ld\n", timeout );
242 if ( ServiceHandle != INVALID_HANDLE_VALUE )
244 SERVICE_Delete( ServiceHandle );
245 ServiceHandle = INVALID_HANDLE_VALUE;
248 TRACE(timer, "Deleted service callback\n" );
252 LeaveCriticalSection( &csTimer );
256 /***********************************************************************
259 * Build a message for an expired timer.
261 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
262 HQUEUE16 hQueue, BOOL remove )
265 DWORD curTime = GetTickCount();
267 EnterCriticalSection( &csTimer );
271 if (hwnd) /* Find first timer for this window */
272 while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
273 else /* Find first timer for this queue */
274 while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
276 if (!pTimer || (pTimer->expires > curTime))
278 LeaveCriticalSection( &csTimer );
279 return FALSE; /* No timer */
282 TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n",
283 pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
287 TIMER_RestartTimer( pTimer, curTime ); /* Restart it */
288 TIMER_CheckTimers( TRUE );
291 /* Build the message */
292 msg->hwnd = pTimer->hwnd;
293 msg->message = pTimer->msg;
294 msg->wParam = pTimer->id;
295 msg->lParam = (LONG)pTimer->proc;
298 LeaveCriticalSection( &csTimer );
304 /***********************************************************************
307 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
308 WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
313 if (!timeout) return 0;
315 EnterCriticalSection( &csTimer );
317 /* Check if there's already a timer with the same hwnd and id */
319 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
320 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
321 (pTimer->timeout != 0))
323 /* Got one: set new values and return */
324 TIMER_RemoveTimer( pTimer );
325 pTimer->timeout = timeout;
326 WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
327 pTimer->proc = (HWINDOWPROC)0;
328 if (proc) WINPROC_SetProc( &pTimer->proc, proc,
329 type, WIN_PROC_TIMER );
330 pTimer->expires = GetTickCount() + timeout;
331 TIMER_InsertTimer( pTimer );
332 TIMER_CheckTimers( TRUE );
333 LeaveCriticalSection( &csTimer );
337 /* Find a free timer */
339 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
340 if (!pTimer->timeout) break;
342 if ( (i >= NB_TIMERS) ||
343 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
345 LeaveCriticalSection( &csTimer );
349 if (!hwnd) id = i + 1;
354 pTimer->hq = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
356 pTimer->msg = sys ? WM_SYSTIMER : WM_TIMER;
358 pTimer->timeout = timeout;
359 pTimer->expires = GetTickCount() + timeout;
360 pTimer->proc = (HWINDOWPROC)0;
361 if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
362 TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n",
363 pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
364 (DWORD)pTimer->proc );
365 TIMER_InsertTimer( pTimer );
366 TIMER_CheckTimers( TRUE );
368 LeaveCriticalSection( &csTimer );
370 if (!id) return TRUE;
375 /***********************************************************************
378 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
383 EnterCriticalSection( &csTimer );
387 for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
388 if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
389 (pTimer->timeout != 0)) break;
391 if ( (i >= NB_TIMERS) ||
392 (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
393 (!sys && (pTimer->msg != WM_TIMER)) ||
394 (sys && (pTimer->msg != WM_SYSTIMER)) )
396 LeaveCriticalSection( &csTimer );
400 /* Delete the timer */
402 TIMER_ClearTimer( pTimer );
404 LeaveCriticalSection( &csTimer );
410 /***********************************************************************
411 * SetTimer16 (USER.10)
413 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 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_16, FALSE );
423 /***********************************************************************
424 * SetTimer32 (USER32.511)
426 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
429 TRACE(timer, "%04x %d %d %08lx\n",
430 hwnd, id, timeout, (LONG)proc );
431 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
432 WIN_PROC_32A, FALSE );
436 /***********************************************************************
437 * SetSystemTimer16 (USER.11)
439 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
442 TRACE(timer, "%04x %d %d %08lx\n",
443 hwnd, id, timeout, (LONG)proc );
444 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
449 /***********************************************************************
450 * SetSystemTimer32 (USER32.509)
452 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
455 TRACE(timer, "%04x %d %d %08lx\n",
456 hwnd, id, timeout, (LONG)proc );
457 return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
458 WIN_PROC_32A, TRUE );
462 /***********************************************************************
463 * KillTimer16 (USER.12)
465 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
467 TRACE(timer, "%04x %d\n", hwnd, id );
468 return TIMER_KillTimer( hwnd, id, FALSE );
472 /***********************************************************************
473 * KillTimer32 (USER32.354)
475 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
477 TRACE(timer, "%04x %d\n", hwnd, id );
478 return TIMER_KillTimer( hwnd, id, FALSE );
482 /***********************************************************************
483 * KillSystemTimer16 (USER.182)
485 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
487 TRACE(timer, "%04x %d\n", hwnd, id );
488 return TIMER_KillTimer( hwnd, id, TRUE );
492 /***********************************************************************
493 * KillSystemTimer32 (USER32.353)
495 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
497 TRACE(timer, "%04x %d\n", hwnd, id );
498 return TIMER_KillTimer( hwnd, id, TRUE );