Prevent from crashing/hanging in Windows 95 OSR2.
[wine] / windows / timer.c
1 /*
2  * Timer functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "wine/winuser16.h"
30 #include "winuser.h"
31 #include "winerror.h"
32
33 #include "winproc.h"
34 #include "message.h"
35 #include "win.h"
36 #include "wine/server.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(timer);
40
41
42 typedef struct tagTIMER
43 {
44     HWND           hwnd;
45     DWORD          thread;
46     UINT           msg;  /* WM_TIMER or WM_SYSTIMER */
47     UINT           id;
48     UINT           timeout;
49     WNDPROC        proc;
50 } TIMER;
51
52 #define NB_TIMERS            34
53 #define NB_RESERVED_TIMERS    2  /* for SetSystemTimer */
54
55 #define SYS_TIMER_RATE  55   /* min. timer rate in ms (actually 54.925)*/
56
57 static TIMER TimersArray[NB_TIMERS];
58
59 static CRITICAL_SECTION csTimer;
60 static CRITICAL_SECTION_DEBUG critsect_debug =
61 {
62     0, 0, &csTimer,
63     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
64       0, 0, { 0, (DWORD)(__FILE__ ": csTimer") }
65 };
66 static CRITICAL_SECTION csTimer = { &critsect_debug, -1, 0, 0, 0, 0 };
67
68
69 /***********************************************************************
70  *           TIMER_ClearTimer
71  *
72  * Clear and remove a timer.
73  */
74 static void TIMER_ClearTimer( TIMER * pTimer )
75 {
76     pTimer->hwnd    = 0;
77     pTimer->msg     = 0;
78     pTimer->id      = 0;
79     pTimer->timeout = 0;
80     WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
81 }
82
83
84 /***********************************************************************
85  *           TIMER_RemoveWindowTimers
86  *
87  * Remove all timers for a given window.
88  */
89 void TIMER_RemoveWindowTimers( HWND hwnd )
90 {
91     int i;
92     TIMER *pTimer;
93
94     EnterCriticalSection( &csTimer );
95
96     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
97         if ((pTimer->hwnd == hwnd) && pTimer->timeout)
98             TIMER_ClearTimer( pTimer );
99
100     LeaveCriticalSection( &csTimer );
101 }
102
103
104 /***********************************************************************
105  *           TIMER_RemoveThreadTimers
106  *
107  * Remove all timers for the current thread.
108  */
109 void TIMER_RemoveThreadTimers(void)
110 {
111     int i;
112     TIMER *pTimer;
113
114     EnterCriticalSection( &csTimer );
115
116     for (i = NB_TIMERS, pTimer = TimersArray; i > 0; i--, pTimer++)
117         if ((pTimer->thread == GetCurrentThreadId()) && pTimer->timeout)
118             TIMER_ClearTimer( pTimer );
119
120     LeaveCriticalSection( &csTimer );
121 }
122
123
124 /***********************************************************************
125  *           TIMER_SetTimer
126  */
127 static UINT_PTR TIMER_SetTimer( HWND hwnd, UINT_PTR id, UINT timeout,
128                                 WNDPROC proc, WINDOWPROCTYPE type, BOOL sys )
129 {
130     int i;
131     TIMER * pTimer;
132     WNDPROC winproc = 0;
133
134     if (hwnd && !(hwnd = WIN_IsCurrentThread( hwnd )))
135     {
136         SetLastError( ERROR_INVALID_WINDOW_HANDLE );
137         return 0;
138     }
139
140     if (!timeout)
141       {       /* timeout==0 is a legal argument  UB 990821*/
142        WARN("Timeout== 0 not implemented, using timeout=1\n");
143         timeout=1;
144       }
145
146     EnterCriticalSection( &csTimer );
147
148       /* Check if there's already a timer with the same hwnd and id */
149
150     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
151         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
152             (pTimer->timeout != 0))
153         {
154             TIMER_ClearTimer( pTimer );
155             break;
156         }
157
158     if ( i == NB_TIMERS )
159     {
160           /* Find a free timer */
161
162         for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
163             if (!pTimer->timeout) break;
164
165         if ( (i >= NB_TIMERS) ||
166              (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) )
167         {
168             LeaveCriticalSection( &csTimer );
169             return 0;
170         }
171     }
172
173     if (!hwnd) id = i + 1;
174
175     if (proc) WINPROC_SetProc( &winproc, proc, type, WIN_PROC_TIMER );
176
177     SERVER_START_REQ( set_win_timer )
178     {
179         req->win    = hwnd;
180         req->msg    = sys ? WM_SYSTIMER : WM_TIMER;
181         req->id     = id;
182         req->rate   = max( timeout, SYS_TIMER_RATE );
183         req->lparam = (unsigned int)winproc;
184         wine_server_call( req );
185     }
186     SERVER_END_REQ;
187
188       /* Add the timer */
189
190     pTimer->hwnd    = hwnd;
191     pTimer->thread  = GetCurrentThreadId();
192     pTimer->msg     = sys ? WM_SYSTIMER : WM_TIMER;
193     pTimer->id      = id;
194     pTimer->timeout = timeout;
195     pTimer->proc    = winproc;
196
197     TRACE("Timer added: %p, %p, %04x, %04x, %p\n",
198           pTimer, pTimer->hwnd, pTimer->msg, pTimer->id, pTimer->proc );
199
200     LeaveCriticalSection( &csTimer );
201
202     if (!id) return TRUE;
203     else return id;
204 }
205
206
207 /***********************************************************************
208  *           TIMER_KillTimer
209  */
210 static BOOL TIMER_KillTimer( HWND hwnd, UINT_PTR id, BOOL sys )
211 {
212     int i;
213     TIMER * pTimer;
214
215     SERVER_START_REQ( kill_win_timer )
216     {
217         req->win = hwnd;
218         req->msg = sys ? WM_SYSTIMER : WM_TIMER;
219         req->id  = id;
220         wine_server_call( req );
221     }
222     SERVER_END_REQ;
223
224     EnterCriticalSection( &csTimer );
225
226     /* Find the timer */
227
228     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
229         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) &&
230             (pTimer->timeout != 0)) break;
231
232     if ( (i >= NB_TIMERS) ||
233          (!sys && (i >= NB_TIMERS-NB_RESERVED_TIMERS)) ||
234          (!sys && (pTimer->msg != WM_TIMER)) ||
235          (sys && (pTimer->msg != WM_SYSTIMER)) )
236     {
237         LeaveCriticalSection( &csTimer );
238         return FALSE;
239     }
240
241     /* Delete the timer */
242
243     TIMER_ClearTimer( pTimer );
244
245     LeaveCriticalSection( &csTimer );
246
247     return TRUE;
248 }
249
250
251 /***********************************************************************
252  *              SetTimer (USER.10)
253  */
254 UINT16 WINAPI SetTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
255                           TIMERPROC16 proc )
256 {
257     TRACE("%04x %d %d %08lx\n",
258                    hwnd, id, timeout, (LONG)proc );
259     return TIMER_SetTimer( WIN_Handle32(hwnd), id, timeout, (WNDPROC)proc,
260                            WIN_PROC_16, FALSE );
261 }
262
263
264 /***********************************************************************
265  *              SetTimer (USER32.@)
266  */
267 UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout,
268                           TIMERPROC proc )
269 {
270     TRACE("%p %d %d %p\n", hwnd, id, timeout, proc );
271     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC)proc, WIN_PROC_32A, FALSE );
272 }
273
274
275 /***********************************************************************
276  *           TIMER_IsTimerValid
277  */
278 BOOL TIMER_IsTimerValid( HWND hwnd, UINT_PTR id, WNDPROC proc )
279 {
280     int i;
281     TIMER *pTimer;
282     BOOL ret = FALSE;
283
284     hwnd = WIN_GetFullHandle( hwnd );
285     EnterCriticalSection( &csTimer );
286
287     for (i = 0, pTimer = TimersArray; i < NB_TIMERS; i++, pTimer++)
288         if ((pTimer->hwnd == hwnd) && (pTimer->id == id) && (pTimer->proc == proc))
289         {
290             ret = TRUE;
291             break;
292         }
293
294    LeaveCriticalSection( &csTimer );
295    return ret;
296 }
297
298
299 /***********************************************************************
300  *              SetSystemTimer (USER.11)
301  */
302 UINT16 WINAPI SetSystemTimer16( HWND16 hwnd, UINT16 id, UINT16 timeout,
303                                 TIMERPROC16 proc )
304 {
305     TRACE("%04x %d %d %08lx\n",
306                    hwnd, id, timeout, (LONG)proc );
307     return TIMER_SetTimer( WIN_Handle32(hwnd), id, timeout, (WNDPROC)proc, WIN_PROC_16, TRUE );
308 }
309
310
311 /***********************************************************************
312  *              SetSystemTimer (USER32.@)
313  */
314 UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout,
315                                 TIMERPROC proc )
316 {
317     TRACE("%p %d %d %p\n", hwnd, id, timeout, proc );
318     return TIMER_SetTimer( hwnd, id, timeout, (WNDPROC)proc, WIN_PROC_32A, TRUE );
319 }
320
321
322 /***********************************************************************
323  *              KillTimer (USER32.@)
324  */
325 BOOL WINAPI KillTimer( HWND hwnd, UINT_PTR id )
326 {
327     TRACE("%p %d\n", hwnd, id );
328     return TIMER_KillTimer( hwnd, id, FALSE );
329 }
330
331
332 /***********************************************************************
333  *              KillSystemTimer (USER32.@)
334  */
335 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT_PTR id )
336 {
337     TRACE("%p %d\n", hwnd, id );
338     return TIMER_KillTimer( hwnd, id, TRUE );
339 }