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