Release 960616
[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 "stddebug.h"
10 /* #define DEBUG_TIMER */
11 #include "debug.h"
12
13
14 typedef struct tagTIMER
15 {
16     HWND             hwnd;
17     HQUEUE           hq;
18     WORD             msg;  /* WM_TIMER or WM_SYSTIMER */
19     WORD             id;
20     WORD             timeout;
21     struct tagTIMER *next;
22     DWORD            expires;  /* Next expiration, or 0 if already expired */
23     FARPROC          proc;
24 } TIMER;
25
26 #define NB_TIMERS            34
27 #define NB_RESERVED_TIMERS    2  /* for SetSystemTimer */
28
29 static TIMER TimersArray[NB_TIMERS];
30
31 static TIMER * pNextTimer = NULL;  /* Next timer to expire */
32
33   /* Duration from 'time' until expiration of the timer */
34 #define EXPIRE_TIME(pTimer,time) \
35           (((pTimer)->expires <= (time)) ? 0 : (pTimer)->expires - (time))
36
37
38 /***********************************************************************
39  *           TIMER_InsertTimer
40  *
41  * Insert the timer at its place in the chain.
42  */
43 static void TIMER_InsertTimer( TIMER * pTimer )
44 {
45     if (!pNextTimer || (pTimer->expires < pNextTimer->expires))
46     {
47         pTimer->next = pNextTimer;
48         pNextTimer = pTimer;
49     }
50     else
51     {
52         TIMER * ptr = pNextTimer;       
53         while (ptr->next && (pTimer->expires >= ptr->next->expires))
54             ptr = ptr->next;
55         pTimer->next = ptr->next;
56         ptr->next = pTimer;
57     }
58 }
59
60
61 /***********************************************************************
62  *           TIMER_RemoveTimer
63  *
64  * Remove the timer from the chain.
65  */
66 static void TIMER_RemoveTimer( TIMER * pTimer )
67 {
68     TIMER **ppTimer = &pNextTimer;
69
70     while (*ppTimer && (*ppTimer != pTimer)) ppTimer = &(*ppTimer)->next;
71     if (*ppTimer) *ppTimer = pTimer->next;
72     pTimer->next = NULL;
73     if (!pTimer->expires) QUEUE_DecTimerCount( pTimer->hq );
74 }
75
76
77 /***********************************************************************
78  *           TIMER_ClearTimer
79  *
80  * Clear and remove a timer.
81  */
82 static void TIMER_ClearTimer( TIMER * pTimer )
83 {
84     TIMER_RemoveTimer( pTimer );
85     pTimer->hwnd    = 0;
86     pTimer->msg     = 0;
87     pTimer->id      = 0;
88     pTimer->timeout = 0;
89     pTimer->proc    = 0;
90 }
91
92
93 /***********************************************************************
94  *           TIMER_SwitchQueue
95  */
96 void TIMER_SwitchQueue(HQUEUE old, HQUEUE new)
97 {
98     TIMER * pT = pNextTimer;
99
100     while (pT)
101     {
102         if (pT->hq == old) pT->hq = new;
103         pT = pT->next;
104     }
105 }
106
107
108 /***********************************************************************
109  *           TIMER_RemoveWindowTimers
110  *
111  * Remove all timers for a given window.
112  */
113 void TIMER_RemoveWindowTimers( HWND hwnd )
114 {
115     int i;
116     TIMER *pTimer;
117
118     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
119         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
120             TIMER_ClearTimer( pTimer );
121 }
122
123
124 /***********************************************************************
125  *           TIMER_RemoveQueueTimers
126  *
127  * Remove all timers for a given queue.
128  */
129 void TIMER_RemoveQueueTimers( HQUEUE hqueue )
130 {
131     int i;
132     TIMER *pTimer;
133
134     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
135         if ((pTimer->hq == hqueue) && pTimer->timeout)
136             TIMER_ClearTimer( pTimer );
137 }
138
139
140 /***********************************************************************
141  *           TIMER_RestartTimers
142  *
143  * Restart an expired timer.
144  */
145 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
146 {
147     TIMER_RemoveTimer( pTimer );
148     pTimer->expires = curTime + pTimer->timeout;
149     TIMER_InsertTimer( pTimer );
150 }
151
152                                
153 /***********************************************************************
154  *           TIMER_GetNextExpiration
155  *
156  * Return next timer expiration time, or -1 if none.
157  */
158 LONG TIMER_GetNextExpiration(void)
159 {
160     return pNextTimer ? EXPIRE_TIME( pNextTimer, GetTickCount() ) : -1;
161 }
162
163
164 /***********************************************************************
165  *           TIMER_ExpireTimers
166  *
167  * Mark expired timers and wake the appropriate queues.
168  */
169 void TIMER_ExpireTimers(void)
170 {
171     TIMER *pTimer = pNextTimer;
172     DWORD curTime = GetTickCount();
173
174     while (pTimer && !pTimer->expires)  /* Skip already expired timers */
175         pTimer = pTimer->next;
176     while (pTimer && (pTimer->expires <= curTime))
177     {
178         pTimer->expires = 0;
179         QUEUE_IncTimerCount( pTimer->hq );
180         pTimer = pTimer->next;
181     }
182 }
183
184
185 /***********************************************************************
186  *           TIMER_GetTimerMsg
187  *
188  * Build a message for an expired timer.
189  */
190 BOOL TIMER_GetTimerMsg( MSG16 *msg, HWND hwnd, HQUEUE hQueue, BOOL 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_timer( stddeb, "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    = pTimer->hwnd;
208     msg->message = pTimer->msg;
209     msg->wParam  = 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 WORD TIMER_SetTimer( HWND hwnd, WORD id, WORD timeout,
220                             FARPROC proc, BOOL sys )
221 {
222     int i;
223     TIMER * pTimer;
224
225     if (!timeout) return 0;
226 /*    if (!hwnd && !proc) return 0; */
227
228       /* Check if there's already a timer with the same hwnd and id */
229
230     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
231         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
232             (pTimer->timeout != 0))
233         {
234               /* Got one: set new values and return */
235             TIMER_RemoveTimer( pTimer );
236             pTimer->timeout = timeout;
237             pTimer->proc    = proc;
238             pTimer->expires = GetTickCount() + timeout;
239             TIMER_InsertTimer( pTimer );
240             return id;
241         }
242
243       /* Find a free timer */
244     
245     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
246         if (!pTimer->timeout) break;
247
248     if (i >= NB_TIMERS) return 0;
249     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return 0;
250     if (!hwnd) id = i + 1;
251     
252       /* Add the timer */
253
254     pTimer->hwnd    = hwnd;
255     pTimer->hq      = (hwnd) ? GetTaskQueue( GetWindowTask( hwnd ) )
256                              : GetTaskQueue( 0 );
257     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
258     pTimer->id      = id;
259     pTimer->timeout = timeout;
260     pTimer->expires = GetTickCount() + timeout;
261     pTimer->proc    = proc;
262     dprintf_timer(stddeb, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
263                   pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
264     TIMER_InsertTimer( pTimer );
265     if (!id)
266         return TRUE;
267     else
268         return id;
269 }
270
271
272 /***********************************************************************
273  *           TIMER_KillTimer
274  */
275 static BOOL TIMER_KillTimer( HWND hwnd, WORD id, BOOL sys )
276 {
277     int i;
278     TIMER * pTimer;
279     
280     /* Find the timer */
281     
282     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
283         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
284             (pTimer->timeout != 0)) break;
285     if (i >= NB_TIMERS) return FALSE;
286     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return FALSE;
287     if (!sys && (pTimer->msg != WM_TIMER)) return FALSE;
288     else if (sys && (pTimer->msg != WM_SYSTIMER)) return FALSE;    
289
290     /* Delete the timer */
291
292     TIMER_ClearTimer( pTimer );
293     return TRUE;
294 }
295
296
297 /***********************************************************************
298  *           SetTimer   (USER.10)
299  */
300 WORD SetTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
301 {
302     dprintf_timer(stddeb, "SetTimer: %04x %d %d %08lx\n", hwnd, id, timeout, (LONG)proc );
303     return TIMER_SetTimer( hwnd, id, timeout, proc, FALSE );
304 }
305
306
307 /***********************************************************************
308  *           SetSystemTimer   (USER.11)
309  */
310 WORD SetSystemTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
311 {
312     dprintf_timer(stddeb, "SetSystemTimer: %04x %d %d %08lx\n", 
313                   hwnd, id, timeout, (LONG)proc );
314     return TIMER_SetTimer( hwnd, id, timeout, proc, TRUE );
315 }
316
317
318 /***********************************************************************
319  *           KillTimer   (USER.12)
320  */
321 BOOL KillTimer( HWND hwnd, WORD id )
322 {
323     dprintf_timer(stddeb, "KillTimer: %04x %d\n", hwnd, id );
324     return TIMER_KillTimer( hwnd, id, FALSE );
325 }
326
327
328 /***********************************************************************
329  *           KillSystemTimer   (USER.182)
330  */
331 BOOL KillSystemTimer( HWND hwnd, WORD id )
332 {
333     dprintf_timer(stddeb, "KillSystemTimer: %04x %d\n", hwnd, id );
334     return TIMER_KillTimer( hwnd, id, TRUE );
335 }