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