Fixed an error and an off-by-one bug in DSA_SetItem(). This
[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     struct tagTIMER *next;
25     DWORD            expires;  /* Next expiration, or 0 if already 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 TIMER * pNextTimer = NULL;  /* Next timer to expire */
35
36 static CRITICAL_SECTION csTimer;
37
38   /* Duration from 'time' until expiration of the timer */
39 #define EXPIRE_TIME(pTimer,time) \
40           (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
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_InsertTimer
59  *
60  * Insert the timer at its place in the chain.
61  */
62 static void TIMER_InsertTimer( TIMER * pTimer )
63 {
64     EnterCriticalSection( &csTimer );
65     
66     if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
67     {
68         pTimer->next = pNextTimer;
69         pNextTimer = pTimer;
70     }
71     else
72     {
73         TIMER * ptr = pNextTimer;       
74         while (ptr->next && (pTimer->expires >= ptr->next->expires))
75             ptr = ptr->next;
76         pTimer->next = ptr->next;
77         ptr->next = pTimer;
78     }
79     
80     LeaveCriticalSection( &csTimer );
81 }
82
83
84 /***********************************************************************
85  *           TIMER_RemoveTimer
86  *
87  * Remove the timer from the chain.
88  */
89 static void TIMER_RemoveTimer( TIMER * pTimer )
90 {
91     TIMER **ppTimer = &pNextTimer;
92
93     EnterCriticalSection( &csTimer );
94     
95     while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
96     if (*ppTimer) *ppTimer = pTimer->next;
97     pTimer->next = NULL;
98     
99     LeaveCriticalSection( &csTimer );
100     
101     if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
102 }
103
104
105 /***********************************************************************
106  *           TIMER_ClearTimer
107  *
108  * Clear and remove a timer.
109  */
110 static void TIMER_ClearTimer( TIMER * pTimer )
111 {
112     TIMER_RemoveTimer( pTimer );
113     pTimer->hwnd    = 0;
114     pTimer->msg     = 0;
115     pTimer->id      = 0;
116     pTimer->timeout = 0;
117     WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
118 }
119
120
121 /***********************************************************************
122  *           TIMER_RemoveWindowTimers
123  *
124  * Remove all timers for a given window.
125  */
126 void TIMER_RemoveWindowTimers( HWND hwnd )
127 {
128     int i;
129     TIMER *pTimer;
130
131     EnterCriticalSection( &csTimer );
132     
133     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
134         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
135             TIMER_ClearTimer( pTimer );
136     
137     LeaveCriticalSection( &csTimer );
138 }
139
140
141 /***********************************************************************
142  *           TIMER_RemoveQueueTimers
143  *
144  * Remove all timers for a given queue.
145  */
146 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
147 {
148     int i;
149     TIMER *pTimer;
150
151     EnterCriticalSection( &csTimer );
152     
153     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
154         if ((pTimer->hq == hqueue) && pTimer->timeout)
155             TIMER_ClearTimer( pTimer );
156     
157     LeaveCriticalSection( &csTimer );
158 }
159
160
161 /***********************************************************************
162  *           TIMER_RestartTimers
163  *
164  * Restart an expired timer.
165  */
166 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
167 {
168     TIMER_RemoveTimer( pTimer );
169     pTimer->expires = curTime + pTimer->timeout;
170     TIMER_InsertTimer( pTimer );
171 }
172
173 /***********************************************************************
174  *           TIMER_CheckTimers
175  *
176  * Mark expired timers and wake the appropriate queues.
177  */
178 static void CALLBACK TIMER_CheckTimers( ULONG_PTR forceTimer )
179 {
180     static HANDLE ServiceHandle  = INVALID_HANDLE_VALUE;
181     static LONG   ServiceTimeout = 0;
182
183     TIMER *pTimer;
184     DWORD curTime = GetTickCount();
185
186     EnterCriticalSection( &csTimer );
187
188     TRACE(timer, "Called at %ld (%s)\n", curTime, forceTimer? "manual" : "auto" );
189     
190     pTimer = pNextTimer;
191     
192     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
193         pTimer = pTimer->next;
194     while (pTimer && (pTimer->expires <= curTime))
195     {
196         pTimer->expires = 0;
197         QUEUE_IncTimerCount( pTimer->hq );
198         pTimer = pTimer->next;
199     }
200
201     /* Install service callback with appropriate timeout, so that
202        we get called again once the next timer has expired */
203
204     if (pTimer)
205     {
206         LONG timeout = pTimer->expires - curTime;
207
208         if ( forceTimer || timeout != ServiceTimeout )
209         {
210             if ( ServiceHandle != INVALID_HANDLE_VALUE ) 
211               SERVICE_Delete( ServiceHandle );
212
213             ServiceHandle = SERVICE_AddTimer( timeout * 1000L, 
214                                               TIMER_CheckTimers, FALSE );
215             ServiceTimeout = timeout;
216
217             TRACE(timer, "Installed service callback with timeout %ld\n", timeout );
218         }
219     }
220     else
221     {
222         if ( ServiceHandle != INVALID_HANDLE_VALUE )
223         {
224             SERVICE_Delete( ServiceHandle );
225             ServiceHandle = INVALID_HANDLE_VALUE;
226             ServiceTimeout = 0;
227
228             TRACE(timer, "Deleted service callback\n" );
229         }
230     }
231     
232     LeaveCriticalSection( &csTimer );
233 }
234
235
236 /***********************************************************************
237  *           TIMER_GetTimerMsg
238  *
239  * Build a message for an expired timer.
240  */
241 BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
242                           HQUEUE16 hQueue, BOOL remove )
243 {
244     TIMER *pTimer;
245     DWORD curTime = GetTickCount();
246
247     EnterCriticalSection( &csTimer );
248
249     pTimer = pNextTimer;
250     
251     if (hwnd)  /* Find first timer for this window */
252         while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
253     else   /* Find first timer for this queue */
254         while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
255
256     if (!pTimer || (pTimer->expires > curTime))
257     {
258         LeaveCriticalSection( &csTimer );
259         return FALSE; /* No timer */
260     }
261     
262     TRACE(timer, "Timer expired: %04x, %04x, %04x, %08lx\n", 
263                    pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
264
265     if (remove) 
266     {
267         TIMER_RestartTimer( pTimer, curTime );  /* Restart it */
268         TIMER_CheckTimers( TRUE );
269     }
270
271       /* Build the message */
272     msg->hwnd    = pTimer->hwnd;
273     msg->message = pTimer->msg;
274     msg->wParam  = pTimer->id;
275     msg->lParam  = (LONG)pTimer->proc;
276     msg->time    = curTime;
277
278     LeaveCriticalSection( &csTimer );
279     
280     return TRUE;
281 }
282
283
284 /***********************************************************************
285  *           TIMER_SetTimer
286  */
287 static UINT TIMER_SetTimer( HWND hwnd, UINT id, UINT timeout,
288                               WNDPROC16 proc, WINDOWPROCTYPE type, BOOL sys )
289 {
290     int i;
291     TIMER * pTimer;
292
293     if (!timeout) return 0;
294
295     EnterCriticalSection( &csTimer );
296     
297       /* Check if there's already a timer with the same hwnd and id */
298
299     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
300         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
301             (pTimer->timeout != 0))
302         {
303               /* Got one: set new values and return */
304             TIMER_RemoveTimer( pTimer );
305             pTimer->timeout = timeout;
306             WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
307             pTimer->proc = (HWINDOWPROC)0;
308             if (proc) WINPROC_SetProc( &pTimer->proc, proc,
309                                        type, WIN_PROC_TIMER );
310             pTimer->expires = GetTickCount() + timeout;
311             TIMER_InsertTimer( pTimer );
312             TIMER_CheckTimers( TRUE );
313             LeaveCriticalSection( &csTimer );
314             return id;
315         }
316
317       /* Find a free timer */
318     
319     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
320         if (!pTimer->timeout) break;
321
322     if ( (i >= NB_TIMERS) ||
323          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
324     {
325         LeaveCriticalSection( &csTimer );
326         return 0;
327     }
328     
329     if (!hwnd) id = i + 1;
330     
331       /* Add the timer */
332
333     pTimer->hwnd    = hwnd;
334     pTimer->hq      = (hwnd) ? GetThreadQueue16( GetWindowThreadProcessId( hwnd, NULL ) )
335                              : GetFastQueue16( );
336     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
337     pTimer->id      = id;
338     pTimer->timeout = timeout;
339     pTimer->expires = GetTickCount() + timeout;
340     pTimer->proc    = (HWINDOWPROC)0;
341     if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
342     TRACE(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
343                    pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
344                    (DWORD)pTimer->proc );
345     TIMER_InsertTimer( pTimer );
346     TIMER_CheckTimers( TRUE );
347     
348     LeaveCriticalSection( &csTimer );
349     
350     if (!id) return TRUE;
351     else return id;
352 }
353
354
355 /***********************************************************************
356  *           TIMER_KillTimer
357  */
358 static BOOL TIMER_KillTimer( HWND hwnd, UINT id, BOOL sys )
359 {
360     int i;
361     TIMER * pTimer;
362     
363     EnterCriticalSection( &csTimer );
364     
365     /* Find the timer */
366     
367     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
368         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
369             (pTimer->timeout != 0)) break;
370
371     if ( (i >= NB_TIMERS) ||
372          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
373          (!sys && (pTimer->msg != WM_TIMER)) ||
374          (sys && (pTimer->msg != WM_SYSTIMER)) )
375     {
376         LeaveCriticalSection( &csTimer );
377         return FALSE;
378     }
379
380     /* Delete the timer */
381
382     TIMER_ClearTimer( pTimer );
383     
384     LeaveCriticalSection( &csTimer );
385     
386     return TRUE;
387 }
388
389
390 /***********************************************************************
391  *           SetTimer16   (USER.10)
392  */
393 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
394                           TIMERPROC16 proc )
395 {
396     TRACE(timer, "%04x %d %d %08lx\n",
397                    hwnd, id, timeout, (LONG)proc );
398     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
399                            WIN_PROC_16, FALSE );
400 }
401
402
403 /***********************************************************************
404  *           SetTimer32   (USER32.511)
405  */
406 UINT WINAPI SetTimer( HWND hwnd, UINT id, UINT timeout,
407                           TIMERPROC proc )
408 {
409     TRACE(timer, "%04x %d %d %08lx\n",
410                    hwnd, id, timeout, (LONG)proc );
411     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
412                            WIN_PROC_32A, FALSE );
413 }
414
415
416 /***********************************************************************
417  *           SetSystemTimer16   (USER.11)
418  */
419 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
420                                 TIMERPROC16 proc )
421 {
422     TRACE(timer, "%04x %d %d %08lx\n", 
423                    hwnd, id, timeout, (LONG)proc );
424     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
425                            WIN_PROC_16, TRUE );
426 }
427
428
429 /***********************************************************************
430  *           SetSystemTimer32   (USER32.509)
431  */
432 UINT WINAPI SetSystemTimer( HWND hwnd, UINT id, UINT timeout,
433                                 TIMERPROC proc )
434 {
435     TRACE(timer, "%04x %d %d %08lx\n", 
436                    hwnd, id, timeout, (LONG)proc );
437     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
438                            WIN_PROC_32A, TRUE );
439 }
440
441
442 /***********************************************************************
443  *           KillTimer16   (USER.12)
444  */
445 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
446 {
447     TRACE(timer, "%04x %d\n", hwnd, id );
448     return TIMER_KillTimer( hwnd, id, FALSE );
449 }
450
451
452 /***********************************************************************
453  *           KillTimer32   (USER32.354)
454  */
455 BOOL WINAPI KillTimer( HWND hwnd, UINT id )
456 {
457     TRACE(timer, "%04x %d\n", hwnd, id );
458     return TIMER_KillTimer( hwnd, id, FALSE );
459 }
460
461
462 /***********************************************************************
463  *           KillSystemTimer16   (USER.182)
464  */
465 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
466 {
467     TRACE(timer, "%04x %d\n", hwnd, id );
468     return TIMER_KillTimer( hwnd, id, TRUE );
469 }
470
471
472 /***********************************************************************
473  *           KillSystemTimer32   (USER32.353)
474  */
475 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT id )
476 {
477     TRACE(timer, "%04x %d\n", hwnd, id );
478     return TIMER_KillTimer( hwnd, id, TRUE );
479 }