Release 980301
[wine] / windows / timer.c
1 /*
2  * Timer functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  */
6
7 #include "windows.h"
8 #include "queue.h"
9 #include "winproc.h"
10 #include "debug.h"
11
12
13 typedef struct tagTIMER
14 {
15     HWND32           hwnd;
16     HQUEUE16         hq;
17     UINT16           msg;  /* WM_TIMER or WM_SYSTIMER */
18     UINT32           id;
19     UINT32           timeout;
20     struct tagTIMER *next;
21     DWORD            expires;  /* Next expiration, or 0 if already expired */
22     HWINDOWPROC      proc;
23 } TIMER;
24
25 #define NB_TIMERS            34
26 #define NB_RESERVED_TIMERS    2  /* for SetSystemTimer */
27
28 static TIMER TimersArray[NB_TIMERS];
29
30 static TIMER * pNextTimer = NULL;  /* Next timer to expire */
31
32   /* Duration from 'time' until expiration of the timer */
33 #define EXPIRE_TIME(pTimer,time) \
34           (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
35
36
37 /***********************************************************************
38  *           TIMER_InsertTimer
39  *
40  * Insert the timer at its place in the chain.
41  */
42 static void TIMER_InsertTimer( TIMER * pTimer )
43 {
44     if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
45     {
46         pTimer->next = pNextTimer;
47         pNextTimer = pTimer;
48     }
49     else
50     {
51         TIMER * ptr = pNextTimer;       
52         while (ptr->next && (pTimer->expires >= ptr->next->expires))
53             ptr = ptr->next;
54         pTimer->next = ptr->next;
55         ptr->next = pTimer;
56     }
57 }
58
59
60 /***********************************************************************
61  *           TIMER_RemoveTimer
62  *
63  * Remove the timer from the chain.
64  */
65 static void TIMER_RemoveTimer( TIMER * pTimer )
66 {
67     TIMER **ppTimer = &pNextTimer;
68
69     while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
70     if (*ppTimer) *ppTimer = pTimer->next;
71     pTimer->next = NULL;
72     if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
73 }
74
75
76 /***********************************************************************
77  *           TIMER_ClearTimer
78  *
79  * Clear and remove a timer.
80  */
81 static void TIMER_ClearTimer( TIMER * pTimer )
82 {
83     TIMER_RemoveTimer( pTimer );
84     pTimer->hwnd    = 0;
85     pTimer->msg     = 0;
86     pTimer->id      = 0;
87     pTimer->timeout = 0;
88     WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
89 }
90
91
92 /***********************************************************************
93  *           TIMER_SwitchQueue
94  */
95 void TIMER_SwitchQueue( HQUEUE16 old, HQUEUE16 new )
96 {
97     TIMER * pT = pNextTimer;
98
99     while (pT)
100     {
101         if (pT->hq == old) pT->hq = new;
102         pT = pT->next;
103     }
104 }
105
106
107 /***********************************************************************
108  *           TIMER_RemoveWindowTimers
109  *
110  * Remove all timers for a given window.
111  */
112 void TIMER_RemoveWindowTimers( HWND32 hwnd )
113 {
114     int i;
115     TIMER *pTimer;
116
117     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
118         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
119             TIMER_ClearTimer( pTimer );
120 }
121
122
123 /***********************************************************************
124  *           TIMER_RemoveQueueTimers
125  *
126  * Remove all timers for a given queue.
127  */
128 void TIMER_RemoveQueueTimers( HQUEUE16 hqueue )
129 {
130     int i;
131     TIMER *pTimer;
132
133     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
134         if ((pTimer->hq == hqueue) && pTimer->timeout)
135             TIMER_ClearTimer( pTimer );
136 }
137
138
139 /***********************************************************************
140  *           TIMER_RestartTimers
141  *
142  * Restart an expired timer.
143  */
144 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
145 {
146     TIMER_RemoveTimer( pTimer );
147     pTimer->expires = curTime + pTimer->timeout;
148     TIMER_InsertTimer( pTimer );
149 }
150
151                                
152 /***********************************************************************
153  *           TIMER_GetNextExpiration
154  *
155  * Return next timer expiration time, or -1 if none.
156  */
157 LONG TIMER_GetNextExpiration(void)
158 {
159     return pNextTimer ? EXPIRE_TIME( pNextTimer, GetTickCount() ) : -1;
160 }
161
162
163 /***********************************************************************
164  *           TIMER_ExpireTimers
165  *
166  * Mark expired timers and wake the appropriate queues.
167  */
168 void TIMER_ExpireTimers(void)
169 {
170     TIMER *pTimer = pNextTimer;
171     DWORD curTime = GetTickCount();
172
173     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
174         pTimer = pTimer->next;
175     while (pTimer && (pTimer->expires <= curTime))
176     {
177         pTimer->expires = 0;
178         QUEUE_IncTimerCount( pTimer->hq );
179         pTimer = pTimer->next;
180     }
181 }
182
183
184 /***********************************************************************
185  *           TIMER_GetTimerMsg
186  *
187  * Build a message for an expired timer.
188  */
189 BOOL32 TIMER_GetTimerMsg( MSG16 *msg, HWND32 hwnd,
190                           HQUEUE16 hQueue, BOOL32 remove )
191 {
192     TIMER *pTimer = pNextTimer;
193     DWORD curTime = GetTickCount();
194
195     if (hwnd)  /* Find first timer for this window */
196         while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
197     else   /* Find first timer for this queue */
198         while (pTimer && (pTimer->hq != hQueue)) pTimer = pTimer->next;
199
200     if (!pTimer || (pTimer->expires > curTime)) return FALSE; /* No timer */
201     if (remove) TIMER_RestartTimer( pTimer, curTime );  /* Restart it */
202
203     dprintf_info(timer, "Timer expired: %04x, %04x, %04x, %08lx\n", 
204                    pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
205
206       /* Build the message */
207     msg->hwnd    = (HWND16)pTimer->hwnd;
208     msg->message = pTimer->msg;
209     msg->wParam  = (UINT16)pTimer->id;
210     msg->lParam  = (LONG)pTimer->proc;
211     msg->time    = curTime;
212     return TRUE;
213 }
214
215
216 /***********************************************************************
217  *           TIMER_SetTimer
218  */
219 static UINT32 TIMER_SetTimer( HWND32 hwnd, UINT32 id, UINT32 timeout,
220                               WNDPROC16 proc, WINDOWPROCTYPE type, BOOL32 sys )
221 {
222     int i;
223     TIMER * pTimer;
224
225     if (!timeout) return 0;
226
227       /* Check if there's already a timer with the same hwnd and id */
228
229     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
230         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
231             (pTimer->timeout != 0))
232         {
233               /* Got one: set new values and return */
234             TIMER_RemoveTimer( pTimer );
235             pTimer->timeout = timeout;
236             WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
237             pTimer->proc = (HWINDOWPROC)0;
238             if (proc) WINPROC_SetProc( &pTimer->proc, proc,
239                                        type, WIN_PROC_TIMER );
240             pTimer->expires = GetTickCount() + timeout;
241             TIMER_InsertTimer( pTimer );
242             return id;
243         }
244
245       /* Find a free timer */
246     
247     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
248         if (!pTimer->timeout) break;
249
250     if (i >= NB_TIMERS) return 0;
251     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return 0;
252     if (!hwnd) id = i + 1;
253     
254       /* Add the timer */
255
256     pTimer->hwnd    = hwnd;
257     pTimer->hq      = (hwnd) ? GetTaskQueue( GetWindowTask16( hwnd ) )
258                              : GetTaskQueue( 0 );
259     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
260     pTimer->id      = id;
261     pTimer->timeout = timeout;
262     pTimer->expires = GetTickCount() + timeout;
263     pTimer->proc    = (HWINDOWPROC)0;
264     if (proc) WINPROC_SetProc( &pTimer->proc, proc, type, WIN_PROC_TIMER );
265     dprintf_info(timer, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
266                    pTimer, pTimer->hwnd, pTimer->msg, pTimer->id,
267                    (DWORD)pTimer->proc );
268     TIMER_InsertTimer( pTimer );
269     if (!id) return TRUE;
270     else return id;
271 }
272
273
274 /***********************************************************************
275  *           TIMER_KillTimer
276  */
277 static BOOL32 TIMER_KillTimer( HWND32 hwnd, UINT32 id, BOOL32 sys )
278 {
279     int i;
280     TIMER * pTimer;
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     if (i >= NB_TIMERS) return FALSE;
288     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return FALSE;
289     if (!sys && (pTimer->msg != WM_TIMER)) return FALSE;
290     else if (sys && (pTimer->msg != WM_SYSTIMER)) return FALSE;    
291
292     /* Delete the timer */
293
294     TIMER_ClearTimer( pTimer );
295     return TRUE;
296 }
297
298
299 /***********************************************************************
300  *           SetTimer16   (USER.10)
301  */
302 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
303                           TIMERPROC16 proc )
304 {
305     dprintf_info(timer, "SetTimer16: %04x %d %d %08lx\n",
306                    hwnd, id, timeout, (LONG)proc );
307     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
308                            WIN_PROC_16, FALSE );
309 }
310
311
312 /***********************************************************************
313  *           SetTimer32   (USER32.510)
314  */
315 UINT32 WINAPI SetTimer32( HWND32 hwnd, UINT32 id, UINT32 timeout,
316                           TIMERPROC32 proc )
317 {
318     dprintf_info(timer, "SetTimer32: %04x %d %d %08lx\n",
319                    hwnd, id, timeout, (LONG)proc );
320     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
321                            WIN_PROC_32A, FALSE );
322 }
323
324
325 /***********************************************************************
326  *           SetSystemTimer16   (USER.11)
327  */
328 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
329                                 TIMERPROC16 proc )
330 {
331     dprintf_info(timer, "SetSystemTimer16: %04x %d %d %08lx\n", 
332                    hwnd, id, timeout, (LONG)proc );
333     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
334                            WIN_PROC_16, TRUE );
335 }
336
337
338 /***********************************************************************
339  *           SetSystemTimer32   (USER32.508)
340  */
341 UINT32 WINAPI SetSystemTimer32( HWND32 hwnd, UINT32 id, UINT32 timeout,
342                                 TIMERPROC32 proc )
343 {
344     dprintf_info(timer, "SetSystemTimer32: %04x %d %d %08lx\n", 
345                    hwnd, id, timeout, (LONG)proc );
346     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC16)proc,
347                            WIN_PROC_32A, TRUE );
348 }
349
350
351 /***********************************************************************
352  *           KillTimer16   (USER.12)
353  */
354 BOOL16 WINAPI KillTimer16( HWND16 hwnd, UINT16 id )
355 {
356     dprintf_info(timer, "KillTimer16: %04x %d\n", hwnd, id );
357     return TIMER_KillTimer( hwnd, id, FALSE );
358 }
359
360
361 /***********************************************************************
362  *           KillTimer32   (USER32.353)
363  */
364 BOOL32 WINAPI KillTimer32( HWND32 hwnd, UINT32 id )
365 {
366     dprintf_info(timer, "KillTimer32: %04x %d\n", hwnd, id );
367     return TIMER_KillTimer( hwnd, id, FALSE );
368 }
369
370
371 /***********************************************************************
372  *           KillSystemTimer16   (USER.182)
373  */
374 BOOL16 WINAPI KillSystemTimer16( HWND16 hwnd, UINT16 id )
375 {
376     dprintf_info(timer, "KillSystemTimer16: %04x %d\n", hwnd, id );
377     return TIMER_KillTimer( hwnd, id, TRUE );
378 }
379
380
381 /***********************************************************************
382  *           KillSystemTimer32   (USER32.352)
383  */
384 BOOL32 WINAPI KillSystemTimer32( HWND32 hwnd, UINT32 id )
385 {
386     dprintf_info(timer, "KillSystemTimer32: %04x %d\n", hwnd, id );
387     return TIMER_KillTimer( hwnd, id, TRUE );
388 }