Fixed some bogus infinite expiration time.
[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     if (pTimer)
211     {
212         DWORD now = GetTickCount();
213         retValue = (pTimer->expires <= now) ? 0 : (pTimer->expires - now);
214     }
215     else retValue = -1;
216
217     LeaveCriticalSection( &csTimer );
218     return retValue;
219 }
220
221
222 /***********************************************************************
223  *           TIMER_ExpireTimers
224  *
225  * Mark expired timers and wake the appropriate queues.
226  */
227 void TIMER_ExpireTimers(void)
228 {
229     TIMER *pTimer;
230     DWORD curTime = GetTickCount();
231
232     EnterCriticalSection( &csTimer );
233     
234     pTimer = pNextTimer;
235     
236     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
237         pTimer = pTimer->next;
238     while (pTimer && (pTimer->expires <= curTime))
239     {
240         pTimer->expires = 0;
241         QUEUE_IncTimerCount( pTimer->hq );
242         pTimer = pTimer->next;
243     }
244     
245     LeaveCriticalSection( &csTimer );
246 }
247
248
249 /***********************************************************************
250  *           TIMER_GetTimerMsg
251  *
252  * Build a message for an expired timer.
253  */
254 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
255                           HQUEUE16 hQueue, BOOL remove )
256 {
257     TIMER *pTimer;
258     DWORD curTime = GetTickCount();
259
260     EnterCriticalSection( &csTimer );
261
262     pTimer = pNextTimer;
263     
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;
268
269     if (!pTimer || (pTimer->expires > curTime))
270     {
271         LeaveCriticalSection( &csTimer );
272         return FALSE; /* No timer */
273     }
274     
275     if (remove) TIMER_RestartTimer( pTimer, curTime );  /* Restart it */
276
277     TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n", 
278                    pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
279
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;
285     msg->time    = curTime;
286
287     LeaveCriticalSection( &csTimer );
288     
289     return TRUE;
290 }
291
292
293 /***********************************************************************
294  *           TIMER_SetTimer
295  */
296 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
297                               WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
298 {
299     int i;
300     TIMER * pTimer;
301
302     if (!timeout) return 0;
303
304     EnterCriticalSection( &csTimer );
305     
306       /* Check if there's already a timer with the same hwnd and id */
307
308     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
309         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
310             (pTimer->timeout != 0))
311         {
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 );
322             return id;
323         }
324
325       /* Find a free timer */
326     
327     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
328         if (!pTimer->timeout) break;
329
330     if ( (i >= NB_TIMERS) ||
331          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
332     {
333         LeaveCriticalSection( &csTimer );
334         return 0;
335     }
336     
337     if (!hwnd) id = i + 1;
338     
339       /* Add the timer */
340
341     pTimer->hwnd    = hwnd;
342     pTimer->hq      = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
343                              : GetFastQueue16( );
344     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
345     pTimer->id      = id;
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 );
354     
355     LeaveCriticalSection( &csTimer );
356     
357     if (!id) return TRUE;
358     else return id;
359 }
360
361
362 /***********************************************************************
363  *           TIMER_KillTimer
364  */
365 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
366 {
367     int i;
368     TIMER * pTimer;
369     
370     EnterCriticalSection( &csTimer );
371     
372     /* Find the timer */
373     
374     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
375         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
376             (pTimer->timeout != 0)) break;
377
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)) )
382     {
383         LeaveCriticalSection( &csTimer );
384         return FALSE;
385     }
386
387     /* Delete the timer */
388
389     TIMER_ClearTimer( pTimer );
390     
391     LeaveCriticalSection( &csTimer );
392     
393     return TRUE;
394 }
395
396
397 /***********************************************************************
398  *           SetTimer16   (USER.10)
399  */
400 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
401                           TIMERPROC16 proc )
402 {
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 );
407 }
408
409
410 /***********************************************************************
411  *           SetTimer32   (USER32.511)
412  */
413 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
414                           TIMERPROC proc )
415 {
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 );
420 }
421
422
423 /***********************************************************************
424  *           SetSystemTimer16   (USER.11)
425  */
426 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
427                                 TIMERPROC16 proc )
428 {
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_16, TRUE );
433 }
434
435
436 /***********************************************************************
437  *           SetSystemTimer32   (USER32.509)
438  */
439 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
440                                 TIMERPROC proc )
441 {
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 );
446 }
447
448
449 /***********************************************************************
450  *           KillTimer16   (USER.12)
451  */
452 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
453 {
454     TRACE(timer, "%04x %d\n", hwnd, id );
455     return TIMER_KillTimer( hwnd, id, FALSE );
456 }
457
458
459 /***********************************************************************
460  *           KillTimer32   (USER32.354)
461  */
462 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
463 {
464     TRACE(timer, "%04x %d\n", hwnd, id );
465     return TIMER_KillTimer( hwnd, id, FALSE );
466 }
467
468
469 /***********************************************************************
470  *           KillSystemTimer16   (USER.182)
471  */
472 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
473 {
474     TRACE(timer, "%04x %d\n", hwnd, id );
475     return TIMER_KillTimer( hwnd, id, TRUE );
476 }
477
478
479 /***********************************************************************
480  *           KillSystemTimer32   (USER32.353)
481  */
482 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
483 {
484     TRACE(timer, "%04x %d\n", hwnd, id );
485     return TIMER_KillTimer( hwnd, id, TRUE );
486 }