New debug scheme with explicit debug channels declaration.
[wine] / windows / timer.c
1 /*
2  * Timer functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  */
6
7 #include "winuser.h"
8 #include "queue.h"
9 #include "task.h"
10 #include "winproc.h"
11 #include "debug.h"
12
13 DEFAULT_DEBUG_CHANNEL(timer)
14
15
16 typedef struct tagTIMER
17 {
18     HWND           hwnd;
19     HQUEUE16         hq;
20     UINT16           msg;  /* WM_TIMER or WM_SYSTIMER */
21     UINT           id;
22     UINT           timeout;
23     struct tagTIMER *next;
24     DWORD            expires;  /* Next expiration, or 0 if already expired */
25     HWINDOWPROC      proc;
26 } TIMER;
27
28 #define NB_TIMERS            34
29 #define NB_RESERVED_TIMERS    2  /* for SetSystemTimer */
30
31 static TIMER TimersArray[NB_TIMERS];
32
33 static TIMER * pNextTimer = NULL;  /* Next timer to expire */
34
35 static CRITICAL_SECTION csTimer;
36
37   /* Duration from 'time' until expiration of the timer */
38 #define EXPIRE_TIME(pTimer,time) \
39           (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
40
41
42 /***********************************************************************
43  *           TIMER_Init
44  *
45  * Initialize critical section for the timer.
46  */
47 BOOL TIMER_Init( void )
48 {
49     InitializeCriticalSection( &csTimer );
50     MakeCriticalSectionGlobal( &csTimer );
51
52     return TRUE;
53 }
54
55
56 /***********************************************************************
57  *           TIMER_InsertTimer
58  *
59  * Insert the timer at its place in the chain.
60  */
61 static void TIMER_InsertTimer( TIMER * pTimer )
62 {
63     EnterCriticalSection( &csTimer );
64     
65     if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
66     {
67         pTimer->next = pNextTimer;
68         pNextTimer = pTimer;
69     }
70     else
71     {
72         TIMER * ptr = pNextTimer;       
73         while (ptr->next && (pTimer->expires >= ptr->next->expires))
74             ptr = ptr->next;
75         pTimer->next = ptr->next;
76         ptr->next = pTimer;
77     }
78     
79     LeaveCriticalSection( &csTimer );
80 }
81
82
83 /***********************************************************************
84  *           TIMER_RemoveTimer
85  *
86  * Remove the timer from the chain.
87  */
88 static void TIMER_RemoveTimer( TIMER * pTimer )
89 {
90     TIMER **ppTimer = &pNextTimer;
91
92     EnterCriticalSection( &csTimer );
93     
94     while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
95     if (*ppTimer) *ppTimer = pTimer->next;
96     pTimer->next = NULL;
97     
98     LeaveCriticalSection( &csTimer );
99     
100     if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
101 }
102
103
104 /***********************************************************************
105  *           TIMER_ClearTimer
106  *
107  * Clear and remove a timer.
108  */
109 static void TIMER_ClearTimer( TIMER * pTimer )
110 {
111     TIMER_RemoveTimer( pTimer );
112     pTimer->hwnd    = 0;
113     pTimer->msg     = 0;
114     pTimer->id      = 0;
115     pTimer->timeout = 0;
116     WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
117 }
118
119
120 /***********************************************************************
121  *           TIMER_SwitchQueue
122  */
123 void TIMER_SwitchQueue( HQUEUE16 old, HQUEUE16 new )
124 {
125     TIMER * pT;
126
127     EnterCriticalSection( &csTimer );
128
129     pT = pNextTimer;
130     while (pT)
131     {
132         if (pT->hq == old) pT->hq = new;
133         pT = pT->next;
134     }
135     
136     LeaveCriticalSection( &csTimer );
137 }
138
139
140 /***********************************************************************
141  *           TIMER_RemoveWindowTimers
142  *
143  * Remove all timers for a given window.
144  */
145 void TIMER_RemoveWindowTimers( HWND hwnd )
146 {
147     int i;
148     TIMER *pTimer;
149
150     EnterCriticalSection( &csTimer );
151     
152     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
153         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
154             TIMER_ClearTimer( pTimer );
155     
156     LeaveCriticalSection( &csTimer );
157 }
158
159
160 /***********************************************************************
161  *           TIMER_RemoveQueueTimers
162  *
163  * Remove all timers for a given queue.
164  */
165 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
166 {
167     int i;
168     TIMER *pTimer;
169
170     EnterCriticalSection( &csTimer );
171     
172     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
173         if ((pTimer->hq == hqueue) && pTimer->timeout)
174             TIMER_ClearTimer( pTimer );
175     
176     LeaveCriticalSection( &csTimer );
177 }
178
179
180 /***********************************************************************
181  *           TIMER_RestartTimers
182  *
183  * Restart an expired timer.
184  */
185 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
186 {
187     TIMER_RemoveTimer( pTimer );
188     pTimer->expires = curTime + pTimer->timeout;
189     TIMER_InsertTimer( pTimer );
190 }
191
192                                
193 /***********************************************************************
194  *           TIMER_GetNextExpiration
195  *
196  * Return next timer expiration time, or -1 if none.
197  */
198 LONG TIMER_GetNextExpiration(void)
199 {
200     TIMER *pTimer;
201     LONG retValue;
202     
203     EnterCriticalSection( &csTimer );
204
205     pTimer = pNextTimer;
206
207     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
208         pTimer = pTimer->next;
209
210     retValue = pTimer ? EXPIRE_TIME( pTimer, GetTickCount() ) : -1;
211     LeaveCriticalSection( &csTimer );
212
213     return retValue;
214 }
215
216
217 /***********************************************************************
218  *           TIMER_ExpireTimers
219  *
220  * Mark expired timers and wake the appropriate queues.
221  */
222 void TIMER_ExpireTimers(void)
223 {
224     TIMER *pTimer;
225     DWORD curTime = GetTickCount();
226
227     EnterCriticalSection( &csTimer );
228     
229     pTimer = pNextTimer;
230     
231     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
232         pTimer = pTimer->next;
233     while (pTimer && (pTimer->expires <= curTime))
234     {
235         pTimer->expires = 0;
236         QUEUE_IncTimerCount( pTimer->hq );
237         pTimer = pTimer->next;
238     }
239     
240     LeaveCriticalSection( &csTimer );
241 }
242
243
244 /***********************************************************************
245  *           TIMER_GetTimerMsg
246  *
247  * Build a message for an expired timer.
248  */
249 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
250                           HQUEUE16 hQueue, BOOL remove )
251 {
252     TIMER *pTimer;
253     DWORD curTime = GetTickCount();
254
255     EnterCriticalSection( &csTimer );
256
257     pTimer = pNextTimer;
258     
259     if (hwnd)  /* Find first timer for this window */
260         while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
261     else   /* Find first timer for this queue */
262         while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
263
264     if (!pTimer || (pTimer->expires > curTime))
265     {
266         LeaveCriticalSection( &csTimer );
267         return FALSE; /* No timer */
268     }
269     
270     if (remove) TIMER_RestartTimer( pTimer, curTime );  /* Restart it */
271
272     TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n", 
273                    pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
274
275       /* Build the message */
276     msg->hwnd    = pTimer->hwnd;
277     msg->message = pTimer->msg;
278     msg->wParam  = pTimer->id;
279     msg->lParam  = (LONG)pTimer->proc;
280     msg->time    = curTime;
281
282     LeaveCriticalSection( &csTimer );
283     
284     return TRUE;
285 }
286
287
288 /***********************************************************************
289  *           TIMER_SetTimer
290  */
291 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
292                               WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
293 {
294     int i;
295     TIMER * pTimer;
296
297     if (!timeout) return 0;
298
299     EnterCriticalSection( &csTimer );
300     
301       /* Check if there's already a timer with the same hwnd and id */
302
303     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
304         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
305             (pTimer->timeout != 0))
306         {
307               /* Got one: set new values and return */
308             TIMER_RemoveTimer( pTimer );
309             pTimer->timeout = timeout;
310             WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
311             pTimer->proc = (HWINDOWPROC)0;
312             if (proc) WINPROC_SetProc( &pTimer->proc, proc,
313                                        type, WIN_PROC_TIMER );
314             pTimer->expires = GetTickCount() + timeout;
315             TIMER_InsertTimer( pTimer );
316             LeaveCriticalSection( &csTimer );
317             return id;
318         }
319
320       /* Find a free timer */
321     
322     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
323         if (!pTimer->timeout) break;
324
325     if ( (i >= NB_TIMERS) ||
326          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
327     {
328         LeaveCriticalSection( &csTimer );
329         return 0;
330     }
331     
332     if (!hwnd) id = i + 1;
333     
334       /* Add the timer */
335
336     pTimer->hwnd    = hwnd;
337     pTimer->hq      = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
338                              : GetFastQueue16( );
339     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
340     pTimer->id      = id;
341     pTimer->timeout = timeout;
342     pTimer->expires = GetTickCount() + timeout;
343     pTimer->proc    = (HWINDOWPROC)0;
344     if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
345     TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
346                    pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
347                    (DWORD)pTimer->proc );
348     TIMER_InsertTimer( pTimer );
349     
350     LeaveCriticalSection( &csTimer );
351     
352     if (!id) return TRUE;
353     else return id;
354 }
355
356
357 /***********************************************************************
358  *           TIMER_KillTimer
359  */
360 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
361 {
362     int i;
363     TIMER * pTimer;
364     
365     EnterCriticalSection( &csTimer );
366     
367     /* Find the timer */
368     
369     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
370         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
371             (pTimer->timeout != 0)) break;
372
373     if ( (i >= NB_TIMERS) ||
374          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
375          (!sys && (pTimer->msg != WM_TIMER)) ||
376          (sys && (pTimer->msg != WM_SYSTIMER)) )
377     {
378         LeaveCriticalSection( &csTimer );
379         return FALSE;
380     }
381
382     /* Delete the timer */
383
384     TIMER_ClearTimer( pTimer );
385     
386     LeaveCriticalSection( &csTimer );
387     
388     return TRUE;
389 }
390
391
392 /***********************************************************************
393  *           SetTimer16   (USER.10)
394  */
395 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
396                           TIMERPROC16 proc )
397 {
398     TRACE(timer, "%04x %d %d %08lx\n",
399                    hwnd, id, timeout, (LONG)proc );
400     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
401                            WIN_PROC_16, FALSE );
402 }
403
404
405 /***********************************************************************
406  *           SetTimer32   (USER32.511)
407  */
408 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
409                           TIMERPROC proc )
410 {
411     TRACE(timer, "%04x %d %d %08lx\n",
412                    hwnd, id, timeout, (LONG)proc );
413     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
414                            WIN_PROC_32A, FALSE );
415 }
416
417
418 /***********************************************************************
419  *           SetSystemTimer16   (USER.11)
420  */
421 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
422                                 TIMERPROC16 proc )
423 {
424     TRACE(timer, "%04x %d %d %08lx\n", 
425                    hwnd, id, timeout, (LONG)proc );
426     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
427                            WIN_PROC_16, TRUE );
428 }
429
430
431 /***********************************************************************
432  *           SetSystemTimer32   (USER32.509)
433  */
434 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
435                                 TIMERPROC proc )
436 {
437     TRACE(timer, "%04x %d %d %08lx\n", 
438                    hwnd, id, timeout, (LONG)proc );
439     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
440                            WIN_PROC_32A, TRUE );
441 }
442
443
444 /***********************************************************************
445  *           KillTimer16   (USER.12)
446  */
447 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
448 {
449     TRACE(timer, "%04x %d\n", hwnd, id );
450     return TIMER_KillTimer( hwnd, id, FALSE );
451 }
452
453
454 /***********************************************************************
455  *           KillTimer32   (USER32.354)
456  */
457 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
458 {
459     TRACE(timer, "%04x %d\n", hwnd, id );
460     return TIMER_KillTimer( hwnd, id, FALSE );
461 }
462
463
464 /***********************************************************************
465  *           KillSystemTimer16   (USER.182)
466  */
467 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
468 {
469     TRACE(timer, "%04x %d\n", hwnd, id );
470     return TIMER_KillTimer( hwnd, id, TRUE );
471 }
472
473
474 /***********************************************************************
475  *           KillSystemTimer32   (USER32.353)
476  */
477 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
478 {
479     TRACE(timer, "%04x %d\n", hwnd, id );
480     return TIMER_KillTimer( hwnd, id, TRUE );
481 }