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