Allow timeout of 0. Cap timer rate to SYS_TIMER_RATE.
[wine] / windows / timer.c
1 /*
2  * Timer functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  */
6
7 #include "wine/winuser16.h"
8 #include "winuser.h"
9 #include "queue.h"
10 #include "task.h"
11 #include "winproc.h"
12 #include "services.h"
13 #include "message.h"
14 #include "debugtools.h"
15
16 DEFAULT_DEBUG_CHANNEL(timer)
17
18
19 typedef struct tagTIMER
20 {
21     HWND           hwnd;
22     HQUEUE16       hq;
23     UINT16         msg;  /* WM_TIMER or WM_SYSTIMER */
24     UINT           id;
25     UINT           timeout;
26     HANDLE         hService;
27     BOOL           expired;
28     HWINDOWPROC    proc;
29 } TIMER;
30
31 #define NB_TIMERS            34
32 #define NB_RESERVED_TIMERS    2  /* for SetSystemTimer */
33
34 #define SYS_TIMER_RATE  54925
35
36 static TIMER TimersArray[NB_TIMERS];
37
38 static CRITICAL_SECTION csTimer;
39
40
41 /***********************************************************************
42  *           TIMER_Init
43  *
44  * Initialize critical section for the timer.
45  */
46 BOOL TIMER_Init( void )
47 {
48     InitializeCriticalSection( &csTimer );
49     MakeCriticalSectionGlobal( &csTimer );
50
51     return TRUE;
52 }
53
54
55 /***********************************************************************
56  *           TIMER_ClearTimer
57  *
58  * Clear and remove a timer.
59  */
60 static void TIMER_ClearTimer( TIMER * pTimer )
61 {
62     if ( pTimer->hService != INVALID_HANDLE_VALUE ) 
63     {
64         SERVICE_Delete( pTimer->hService );
65         pTimer->hService = INVALID_HANDLE_VALUE;
66     }
67
68     if ( pTimer->expired ) 
69     {
70         QUEUE_DecTimerCount( pTimer->hq );
71         pTimer->expired = FALSE;
72     }
73
74     pTimer->hwnd    = 0;
75     pTimer->msg     = 0;
76     pTimer->id      = 0;
77     pTimer->timeout = 0;
78     WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
79 }
80
81
82 /***********************************************************************
83  *           TIMER_RemoveWindowTimers
84  *
85  * Remove all timers for a given window.
86  */
87 void TIMER_RemoveWindowTimers( HWND hwnd )
88 {
89     int i;
90     TIMER *pTimer;
91
92     EnterCriticalSection( &csTimer );
93     
94     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
95         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
96             TIMER_ClearTimer( pTimer );
97     
98     LeaveCriticalSection( &csTimer );
99 }
100
101
102 /***********************************************************************
103  *           TIMER_RemoveQueueTimers
104  *
105  * Remove all timers for a given queue.
106  */
107 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
108 {
109     int i;
110     TIMER *pTimer;
111
112     EnterCriticalSection( &csTimer );
113     
114     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
115         if ((pTimer->hq == hqueue) && pTimer->timeout)
116             TIMER_ClearTimer( pTimer );
117     
118     LeaveCriticalSection( &csTimer );
119 }
120
121
122 /***********************************************************************
123  *           TIMER_CheckTimer
124  */
125 static void CALLBACK TIMER_CheckTimer( ULONG_PTR timer_ptr )
126 {
127     TIMER *pTimer = (TIMER *)timer_ptr;
128     HQUEUE16 wakeQueue = 0;
129
130     EnterCriticalSection( &csTimer );
131
132     /* Paranoid check to prevent a race condition ... */
133     if ( !pTimer->timeout )
134     {
135         LeaveCriticalSection( &csTimer );
136         return;
137     }
138
139     if ( !pTimer->expired )
140     {
141         TRACE("Timer expired: %04x, %04x, %04x, %08lx\n", 
142                      pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
143
144         pTimer->expired = TRUE;
145         wakeQueue = pTimer->hq;
146     }
147
148     LeaveCriticalSection( &csTimer );
149
150     /* Note: This has to be done outside the csTimer critical section,
151              otherwise we'll get deadlocks. */
152
153     if ( wakeQueue )
154         QUEUE_IncTimerCount( wakeQueue );
155 }
156
157
158 /***********************************************************************
159  *           TIMER_GetTimerMsg
160  *
161  * Build a message for an expired timer.
162  */
163 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
164                           HQUEUE16 hQueue, BOOL remove )
165 {
166     TIMER *pTimer;
167     int i;
168
169     EnterCriticalSection( &csTimer );
170
171     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
172         if (    pTimer->timeout != 0 && pTimer->expired
173              && (hwnd? (pTimer->hwnd == hwnd) : (pTimer->hq == hQueue)) )
174             break;
175
176     if ( i == NB_TIMERS )
177     {
178         LeaveCriticalSection( &csTimer );
179         return FALSE; /* No timer */
180     }
181     
182     TRACE("Timer got message: %04x, %04x, %04x, %08lx\n", 
183                    pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
184
185     if (remove) 
186         pTimer->expired = FALSE;
187
188       /* Build the message */
189     msg->hwnd    = pTimer->hwnd;
190     msg->message = pTimer->msg;
191     msg->wParam  = pTimer->id;
192     msg->lParam  = (LONG)pTimer->proc;
193     msg->time    = GetTickCount();
194
195     LeaveCriticalSection( &csTimer );
196     
197     return TRUE;
198 }
199
200
201 /***********************************************************************
202  *           TIMER_SetTimer
203  */
204 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
205                               WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
206 {
207     int i;
208     TIMER * pTimer;
209
210     if (!timeout)
211       {       /* timeout==0 is a legal argument  UB 990821*/
212        WARN("Timeout== 0 not implemented, using timeout=1\n");
213         timeout=1; 
214       }
215     EnterCriticalSection( &csTimer );
216     
217       /* Check if there's already a timer with the same hwnd and id */
218
219     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
220         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
221             (pTimer->timeout != 0))
222         {
223             TIMER_ClearTimer( pTimer );
224             break;
225         }
226
227     if ( i == NB_TIMERS )
228     {
229           /* Find a free timer */
230     
231         for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
232             if (!pTimer->timeout) break;
233
234         if ( (i >= NB_TIMERS) ||
235              (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
236         {
237             LeaveCriticalSection( &csTimer );
238             return 0;
239         }
240     }
241
242     if (!hwnd) id = i + 1;
243     
244       /* Add the timer */
245
246     pTimer->hwnd    = hwnd;
247     pTimer->hq      = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
248                              : GetFastQueue16( );
249     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
250     pTimer->id      = id;
251     pTimer->timeout = timeout;
252     pTimer->proc    = (HWINDOWPROC)0;
253     if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
254
255     pTimer->expired  = FALSE;
256     pTimer->hService = SERVICE_AddTimer( MAX( timeout * 1000L, SYS_TIMER_RATE ),
257                                        TIMER_CheckTimer, (ULONG_PTR)pTimer );
258     
259     TRACE("Timer added: %p, %04x, %04x, %04x, %08lx\n", 
260                    pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
261                    (DWORD)pTimer->proc );
262
263     LeaveCriticalSection( &csTimer );
264     
265     if (!id) return TRUE;
266     else return id;
267 }
268
269
270 /***********************************************************************
271  *           TIMER_KillTimer
272  */
273 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
274 {
275     int i;
276     TIMER * pTimer;
277     
278     EnterCriticalSection( &csTimer );
279     
280     /* Find the timer */
281     
282     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
283         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
284             (pTimer->timeout != 0)) break;
285
286     if ( (i >= NB_TIMERS) ||
287          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
288          (!sys && (pTimer->msg != WM_TIMER)) ||
289          (sys && (pTimer->msg != WM_SYSTIMER)) )
290     {
291         LeaveCriticalSection( &csTimer );
292         return FALSE;
293     }
294
295     /* Delete the timer */
296
297     TIMER_ClearTimer( pTimer );
298     
299     LeaveCriticalSection( &csTimer );
300     
301     return TRUE;
302 }
303
304
305 /***********************************************************************
306  *           SetTimer16   (USER.10)
307  */
308 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
309                           TIMERPROC16 proc )
310 {
311     TRACE("%04x %d %d %08lx\n",
312                    hwnd, id, timeout, (LONG)proc );
313     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
314                            WIN_PROC_16, FALSE );
315 }
316
317
318 /***********************************************************************
319  *           SetTimer32   (USER32.511)
320  */
321 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
322                           TIMERPROC proc )
323 {
324     TRACE("%04x %d %d %08lx\n",
325                    hwnd, id, timeout, (LONG)proc );
326     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
327                            WIN_PROC_32A, FALSE );
328 }
329
330
331 /***********************************************************************
332  *           SetSystemTimer16   (USER.11)
333  */
334 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
335                                 TIMERPROC16 proc )
336 {
337     TRACE("%04x %d %d %08lx\n", 
338                    hwnd, id, timeout, (LONG)proc );
339     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
340                            WIN_PROC_16, TRUE );
341 }
342
343
344 /***********************************************************************
345  *           SetSystemTimer32   (USER32.509)
346  */
347 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
348                                 TIMERPROC proc )
349 {
350     TRACE("%04x %d %d %08lx\n", 
351                    hwnd, id, timeout, (LONG)proc );
352     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
353                            WIN_PROC_32A, TRUE );
354 }
355
356
357 /***********************************************************************
358  *           KillTimer16   (USER.12)
359  */
360 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
361 {
362     TRACE("%04x %d\n", hwnd, id );
363     return TIMER_KillTimer( hwnd, id, FALSE );
364 }
365
366
367 /***********************************************************************
368  *           KillTimer32   (USER32.354)
369  */
370 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
371 {
372     TRACE("%04x %d\n", hwnd, id );
373     return TIMER_KillTimer( hwnd, id, FALSE );
374 }
375
376
377 /***********************************************************************
378  *           KillSystemTimer16   (USER.182)
379  */
380 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
381 {
382     TRACE("%04x %d\n", hwnd, id );
383     return TIMER_KillTimer( hwnd, id, TRUE );
384 }
385
386
387 /***********************************************************************
388  *           KillSystemTimer32   (USER32.353)
389  */
390 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
391 {
392     TRACE("%04x %d\n", hwnd, id );
393     return TIMER_KillTimer( hwnd, id, TRUE );
394 }