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