Release 960331
[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     if (pTimer == pNextTimer) pNextTimer = pTimer->next;
69     else
70     {
71         TIMER * ptr = pNextTimer;
72         while (ptr && (ptr->next != pTimer)) ptr = ptr->next;
73         if (ptr) ptr->next = pTimer->next;
74     }
75     pTimer->next = NULL;
76 }
77
78 /***********************************************************************
79  *           TIMER_SwitchQueue
80  */
81 void TIMER_SwitchQueue(HQUEUE old, HQUEUE new)
82 {
83  TIMER*         pT = pNextTimer;
84
85  while(pT)
86   {
87    if( pT->hq == old ) pT->hq = new;
88    pT = pT->next;
89   }
90
91 }
92
93 /***********************************************************************
94  *           TIMER_NukeTimers
95  *
96  * Trash all timers that are bound to the hwnd or hq
97  */
98 void TIMER_NukeTimers(HWND hwnd, HQUEUE hq)
99 {
100  HQUEUE         hQToUpdate = ( hwnd ) ? GetTaskQueue( GetWindowTask( hwnd ) )
101                                       : hq;
102  TIMER*         pT = pNextTimer;
103  TIMER*         pTnext;
104
105  if( !pT ) return;
106
107  while( (hwnd && pT->hwnd == hwnd) ||
108         (hq && pT->hq == hq) )
109       {
110          QUEUE_DecTimerCount( hQToUpdate );
111          if( !(pT = pNextTimer = pNextTimer->next) )
112              return;
113       }
114
115  /* pT points to the "good" timer */
116
117  while( (pTnext = pT->next) )
118     {
119       while( (hwnd && pTnext->hwnd == hwnd) ||
120              (hq && pTnext->hq == hq) )
121            {
122               QUEUE_DecTimerCount( hQToUpdate );
123               if( !(pT->next = pTnext->next) )
124                   return;
125            }
126
127       pT = pT->next;
128     }
129 }
130
131 /***********************************************************************
132  *           TIMER_RestartTimers
133  *
134  * Restart an expired timer.
135  */
136 static void TIMER_RestartTimer( TIMER * pTimer, DWORD curTime )
137 {
138     TIMER_RemoveTimer( pTimer );
139     pTimer->expires = curTime + pTimer->timeout;
140     TIMER_InsertTimer( pTimer );
141 }
142
143                                
144 /***********************************************************************
145  *           TIMER_CheckTimer
146  *
147  * Check whether a timer has expired, and create a message if necessary.
148  * Otherwise, return time until next timer expiration in 'next'.
149  * If 'hwnd' is not NULL, only consider timers for this window.
150  * If 'remove' is TRUE, remove all expired timers up to the returned one.
151  */
152 BOOL TIMER_CheckTimer( LONG *next, MSG *msg, HWND hwnd, BOOL remove )
153 {
154     TIMER * pTimer = pNextTimer;
155     DWORD curTime = GetTickCount();
156
157     if (hwnd)  /* Find first timer for this window */
158         while (pTimer && (pTimer->hwnd != hwnd)) pTimer = pTimer->next;
159
160     if (!pTimer) *next = -1;
161     else *next = EXPIRE_TIME( pTimer, curTime );
162     if (*next != 0) return FALSE;  /* No timer expired */
163
164     if (remove) /* Restart all timers before pTimer, and then pTimer itself */
165     {
166         while (pNextTimer != pTimer) TIMER_RestartTimer( pNextTimer, curTime );
167         TIMER_RestartTimer( pTimer, curTime );
168     }
169
170     dprintf_timer(stddeb, "Timer expired: %p, %04x, %04x, %04x, %08lx\n", 
171                   pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
172       /* Build the message */
173     msg->hwnd    = pTimer->hwnd;
174     msg->message = pTimer->msg;
175     msg->wParam  = pTimer->id;
176     msg->lParam  = (LONG)pTimer->proc;
177     msg->time    = curTime;
178     return TRUE;
179 }
180
181
182 /***********************************************************************
183  *           TIMER_SetTimer
184  */
185 static WORD TIMER_SetTimer( HWND hwnd, WORD id, WORD timeout,
186                             FARPROC proc, BOOL sys )
187 {
188     int i;
189     TIMER * pTimer;
190
191     if (!timeout) return 0;
192 /*    if (!hwnd && !proc) return 0; */
193
194       /* Check if there's already a timer with the same hwnd and id */
195
196     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
197         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
198             (pTimer->timeout != 0))
199         {
200               /* Got one: set new values and return */
201             pTimer->timeout = timeout;
202             pTimer->expires = GetTickCount() + timeout;
203             pTimer->proc    = proc;
204             TIMER_RemoveTimer( pTimer );
205             TIMER_InsertTimer( pTimer );
206             return id;
207         }
208
209       /* Find a free timer */
210     
211     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
212         if (!pTimer->timeout) break;
213
214     if (i >= NB_TIMERS) return 0;
215     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return 0;
216     if (!hwnd) id = i + 1;
217     
218       /* Add the timer */
219
220     pTimer->hwnd    = hwnd;
221     pTimer->hq      = (hwnd) ? GetTaskQueue( GetWindowTask( hwnd ) )
222                              : GetTaskQueue( 0 );
223     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
224     pTimer->id      = id;
225     pTimer->timeout = timeout;
226     pTimer->expires = GetTickCount() + timeout;
227     pTimer->proc    = proc;
228     dprintf_timer(stddeb, "Timer added: %p, %04x, %04x, %04x, %08lx\n", 
229                   pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, (DWORD)pTimer->proc);
230     TIMER_InsertTimer( pTimer );
231     QUEUE_IncTimerCount( pTimer->hq );
232     if (!id)
233         return TRUE;
234     else
235         return id;
236 }
237
238
239 /***********************************************************************
240  *           TIMER_KillTimer
241  */
242 static BOOL TIMER_KillTimer( HWND hwnd, WORD id, BOOL sys )
243 {
244     int i;
245     TIMER * pTimer;
246     HQUEUE  hq;
247     
248       /* Find the timer */
249     
250     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
251         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
252             (pTimer->timeout != 0)) break;
253     if (i >= NB_TIMERS) return FALSE;
254     if (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) return FALSE;
255     if (!sys && (pTimer->msg != WM_TIMER)) return FALSE;
256     else if (sys && (pTimer->msg != WM_SYSTIMER)) return FALSE;    
257
258       /* Delete the timer */
259
260     hq = pTimer->hq;
261
262     pTimer->hwnd    = 0;
263     pTimer->msg     = 0;
264     pTimer->id      = 0;
265     pTimer->timeout = 0;
266     pTimer->proc    = 0;
267     TIMER_RemoveTimer( pTimer );
268     QUEUE_DecTimerCount( hq );
269     return TRUE;
270 }
271
272
273 /***********************************************************************
274  *           SetTimer   (USER.10)
275  */
276 WORD SetTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
277 {
278     dprintf_timer(stddeb, "SetTimer: %04x %d %d %08lx\n", hwnd, id, timeout, (LONG)proc );
279     return TIMER_SetTimer( hwnd, id, timeout, proc, FALSE );
280 }
281
282
283 /***********************************************************************
284  *           SetSystemTimer   (USER.11)
285  */
286 WORD SetSystemTimer( HWND hwnd, WORD id, WORD timeout, FARPROC proc )
287 {
288     dprintf_timer(stddeb, "SetSystemTimer: %04x %d %d %08lx\n", 
289                   hwnd, id, timeout, (LONG)proc );
290     return TIMER_SetTimer( hwnd, id, timeout, proc, TRUE );
291 }
292
293
294 /***********************************************************************
295  *           KillTimer   (USER.12)
296  */
297 BOOL KillTimer( HWND hwnd, WORD id )
298 {
299     dprintf_timer(stddeb, "KillTimer: %04x %d\n", hwnd, id );
300     return TIMER_KillTimer( hwnd, id, FALSE );
301 }
302
303
304 /***********************************************************************
305  *           KillSystemTimer   (USER.182)
306  */
307 BOOL KillSystemTimer( HWND hwnd, WORD id )
308 {
309     dprintf_timer(stddeb, "KillSystemTimer: %04x %d\n", hwnd, id );
310     return TIMER_KillTimer( hwnd, id, TRUE );
311 }