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