Release 960528
[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;
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 }
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     QUEUE_DecTimerCount( pTimer->hq );
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_GetNextExp
155  *
156  * Return next timer expiration time, or -1 if none.
157  */
158 LONG TIMER_GetNextExp(void)
159 {
160     return pNextTimer ? EXPIRE_TIME( pNextTimer, GetTickCount() ) : -1;
161 }
162
163
164 /***********************************************************************
165  *           TIMER_CheckTimer
166  *
167  * Check whether a timer has expired, and create a message if necessary.
168  * Otherwise, return time until next timer expiration in 'next'.
169  * If 'hwnd' is not NULL, only consider timers for this window.
170  * If 'remove' is TRUE, remove all expired timers up to the returned one.
171  */
172 BOOL TIMER_CheckTimer( LONG *next, MSG *msg, HWND hwnd, BOOL remove )
173 {
174     TIMER * pTimer = pNextTimer;
175     DWORD curTime = GetTickCount();
176
177     if (hwnd)  /* Find first timer for this window */
178         while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
179
180     if (!pTimer) *next = -1;
181     else *next = EXPIRE_TIME( pTimer, curTime );
182     if (*next != 0) return FALSE;  /* No timer expired */
183
184     if (remove) /* Restart all timers before pTimer, and then pTimer itself */
185     {
186         while (pNextTimer != pTimer) TIMER_RestartTimer( pNextTimer, curTime );
187         TIMER_RestartTimer( pTimer, curTime );
188     }
189
190     dprintf_timer(stddeb, "Timer expired: %p, %04x, %04x, %04x, %08lx\n", 
191                   pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
192       /* Build the message */
193     msg->hwnd    = pTimer->hwnd;
194     msg->message = pTimer->msg;
195     msg->wParam  = pTimer->id;
196     msg->lParam  = (LONG)pTimer->proc;
197     msg->time    = curTime;
198     return TRUE;
199 }
200
201
202 /***********************************************************************
203  *           TIMER_SetTimer
204  */
205 static WORD TIMER_SetTimer( HWND hwnd, WORD id, WORD timeout,
206                             FARPROC proc, BOOL sys )
207 {
208     int i;
209     TIMER * pTimer;
210
211     if (!timeout) return 0;
212 /*    if (!hwnd && !proc) return 0; */
213
214       /* Check if there's already a timer with the same hwnd and id */
215
216     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
217         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
218             (pTimer->timeout != 0))
219         {
220               /* Got one: set new values and return */
221             pTimer->timeout = timeout;
222             pTimer->expires = GetTickCount() + timeout;
223             pTimer->proc    = proc;
224             TIMER_RemoveTimer( pTimer );
225             TIMER_InsertTimer( pTimer );
226             return id;
227         }
228
229       /* Find a free timer */
230     
231     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
232         if (!pTimer->timeout) break;
233
234     if (i >= NB_TIMERS) return 0;
235     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return 0;
236     if (!hwnd) id = i + 1;
237     
238       /* Add the timer */
239
240     pTimer->hwnd    = hwnd;
241     pTimer->hq      = (hwnd) ? GetTaskQueue( GetWindowTask( hwnd ) )
242                              : GetTaskQueue( 0 );
243     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
244     pTimer->id      = id;
245     pTimer->timeout = timeout;
246     pTimer->expires = GetTickCount() + timeout;
247     pTimer->proc    = proc;
248     dprintf_timer(stddeb, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
249                   pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
250     TIMER_InsertTimer( pTimer );
251     QUEUE_IncTimerCount( pTimer->hq );
252     if (!id)
253         return TRUE;
254     else
255         return id;
256 }
257
258
259 /***********************************************************************
260  *           TIMER_KillTimer
261  */
262 static BOOL TIMER_KillTimer( HWND hwnd, WORD id, BOOL sys )
263 {
264     int i;
265     TIMER * pTimer;
266     
267     /* Find the timer */
268     
269     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
270         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
271             (pTimer->timeout != 0)) break;
272     if (i >= NB_TIMERS) return FALSE;
273     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return FALSE;
274     if (!sys && (pTimer->msg != WM_TIMER)) return FALSE;
275     else if (sys && (pTimer->msg != WM_SYSTIMER)) return FALSE;    
276
277     /* Delete the timer */
278
279     TIMER_ClearTimer( pTimer );
280     return TRUE;
281 }
282
283
284 /***********************************************************************
285  *           SetTimer   (USER.10)
286  */
287 WORD SetTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
288 {
289     dprintf_timer(stddeb, "SetTimer: %04x %d %d %08lx\n", hwnd, id, timeout, (LONG)proc );
290     return TIMER_SetTimer( hwnd, id, timeout, proc, FALSE );
291 }
292
293
294 /***********************************************************************
295  *           SetSystemTimer   (USER.11)
296  */
297 WORD SetSystemTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
298 {
299     dprintf_timer(stddeb, "SetSystemTimer: %04x %d %d %08lx\n", 
300                   hwnd, id, timeout, (LONG)proc );
301     return TIMER_SetTimer( hwnd, id, timeout, proc, TRUE );
302 }
303
304
305 /***********************************************************************
306  *           KillTimer   (USER.12)
307  */
308 BOOL KillTimer( HWND hwnd, WORD id )
309 {
310     dprintf_timer(stddeb, "KillTimer: %04x %d\n", hwnd, id );
311     return TIMER_KillTimer( hwnd, id, FALSE );
312 }
313
314
315 /***********************************************************************
316  *           KillSystemTimer   (USER.182)
317  */
318 BOOL KillSystemTimer( HWND hwnd, WORD id )
319 {
320     dprintf_timer(stddeb, "KillSystemTimer: %04x %d\n", hwnd, id );
321     return TIMER_KillTimer( hwnd, id, TRUE );
322 }