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