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