Merge from branch ReactX to Trunk,
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / timer.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window timers messages
24 * FILE: subsys/win32k/ntuser/timer.c
25 * PROGRAMER: Gunnar
26 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
27 * REVISION HISTORY: 10/04/2003 Implemented System Timers
28 *
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <w32k.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 /* GLOBALS *******************************************************************/
39
40 /* Windows 2000 has room for 32768 window-less timers */
41 #define NUM_WINDOW_LESS_TIMERS 1024
42
43 static FAST_MUTEX Mutex;
44 static RTL_BITMAP WindowLessTimersBitMap;
45 static PVOID WindowLessTimersBitMapBuffer;
46 static ULONG HintIndex = 0;
47
48
49 #define IntLockWindowlessTimerBitmap() \
50 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
51
52 #define IntUnlockWindowlessTimerBitmap() \
53 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
54
55 /* FUNCTIONS *****************************************************************/
56
57
58 UINT_PTR FASTCALL
59 IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer)
60 {
61 PWINDOW_OBJECT Window;
62 UINT_PTR Ret = 0;
63 PUSER_MESSAGE_QUEUE MessageQueue;
64
65 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
66 Wnd, IDEvent, Elapse, TimerFunc, SystemTimer ? "TRUE" : "FALSE");
67
68 if ((Wnd == NULL) && ! SystemTimer)
69 {
70 DPRINT("Window-less timer\n");
71 /* find a free, window-less timer id */
72 IntLockWindowlessTimerBitmap();
73 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
74
75 if (IDEvent == (UINT_PTR) -1)
76 {
77 IntUnlockWindowlessTimerBitmap();
78 DPRINT1("Unable to find a free window-less timer id\n");
79 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
80 return 0;
81 }
82
83 HintIndex = ++IDEvent;
84 IntUnlockWindowlessTimerBitmap();
85 Ret = IDEvent;
86 MessageQueue = PsGetCurrentThreadWin32Thread()->MessageQueue;
87 }
88 else
89 {
90 if (!(Window = UserGetWindowObject(Wnd)))
91 {
92 DPRINT1("Invalid window handle\n");
93 return 0;
94 }
95
96 if (Window->OwnerThread->ThreadsProcess != PsGetCurrentProcess())
97 {
98 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
99 SetLastWin32Error(ERROR_ACCESS_DENIED);
100 return 0;
101 }
102
103 Ret = IDEvent;
104 MessageQueue = Window->MessageQueue;
105 }
106
107 #if 0
108
109 /* Windows NT/2k/XP behaviour */
110 if (Elapse > 0x7fffffff)
111 {
112 DPRINT("Adjusting uElapse\n");
113 Elapse = 1;
114 }
115
116 #else
117
118 /* Windows XP SP2 and Windows Server 2003 behaviour */
119 if (Elapse > 0x7fffffff)
120 {
121 DPRINT("Adjusting uElapse\n");
122 Elapse = 0x7fffffff;
123 }
124
125 #endif
126
127 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
128 if (Elapse < 10)
129 {
130 DPRINT("Adjusting uElapse\n");
131 Elapse = 10;
132 }
133
134 if (! MsqSetTimer(MessageQueue, Wnd,
135 IDEvent, Elapse, TimerFunc,
136 SystemTimer ? WM_SYSTIMER : WM_TIMER))
137 {
138 DPRINT1("Failed to set timer in message queue\n");
139 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
140 return 0;
141 }
142
143
144 return Ret;
145 }
146
147
148 BOOL FASTCALL
149 IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
150 {
151 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
152 Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
153
154 if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue, Wnd,
155 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
156 {
157 DPRINT1("Unable to locate timer in message queue\n");
158 SetLastWin32Error(ERROR_INVALID_PARAMETER);
159 return FALSE;
160 }
161
162 /* window-less timer? */
163 if ((Wnd == NULL) && ! SystemTimer)
164 {
165 /* Release the id */
166 IntLockWindowlessTimerBitmap();
167
168 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
169 RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
170
171 IntUnlockWindowlessTimerBitmap();
172 }
173
174 return TRUE;
175 }
176
177 NTSTATUS FASTCALL
178 InitTimerImpl(VOID)
179 {
180 ULONG BitmapBytes;
181
182 ExInitializeFastMutex(&Mutex);
183
184 BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
185 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
186 if (WindowLessTimersBitMapBuffer == NULL)
187 {
188 return STATUS_UNSUCCESSFUL;
189 }
190
191 RtlInitializeBitMap(&WindowLessTimersBitMap,
192 WindowLessTimersBitMapBuffer,
193 BitmapBytes * 8);
194
195 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
196 RtlClearAllBits(&WindowLessTimersBitMap);
197
198 return STATUS_SUCCESS;
199 }
200
201
202 UINT_PTR
203 STDCALL
204 NtUserSetTimer
205 (
206 HWND hWnd,
207 UINT_PTR nIDEvent,
208 UINT uElapse,
209 TIMERPROC lpTimerFunc
210 )
211 {
212 DECLARE_RETURN(UINT_PTR);
213
214 DPRINT("Enter NtUserSetTimer\n");
215 UserEnterExclusive();
216
217 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, FALSE));
218
219 CLEANUP:
220 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_);
221 UserLeave();
222 END_CLEANUP;
223 }
224
225
226 BOOL
227 STDCALL
228 NtUserKillTimer
229 (
230 HWND hWnd,
231 UINT_PTR uIDEvent
232 )
233 {
234 DECLARE_RETURN(BOOL);
235
236 DPRINT("Enter NtUserKillTimer\n");
237 UserEnterExclusive();
238
239 RETURN(IntKillTimer(hWnd, uIDEvent, FALSE));
240
241 CLEANUP:
242 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_);
243 UserLeave();
244 END_CLEANUP;
245 }
246
247
248 UINT_PTR
249 STDCALL
250 NtUserSetSystemTimer(
251 HWND hWnd,
252 UINT_PTR nIDEvent,
253 UINT uElapse,
254 TIMERPROC lpTimerFunc
255 )
256 {
257 DECLARE_RETURN(UINT_PTR);
258
259 DPRINT("Enter NtUserSetSystemTimer\n");
260 UserEnterExclusive();
261
262 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, TRUE));
263
264 CLEANUP:
265 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_);
266 UserLeave();
267 END_CLEANUP;
268 }
269
270
271 BOOL
272 STDCALL
273 NtUserKillSystemTimer(
274 HWND hWnd,
275 UINT_PTR uIDEvent
276 )
277 {
278 DECLARE_RETURN(BOOL);
279
280 DPRINT("Enter NtUserKillSystemTimer\n");
281 UserEnterExclusive();
282
283 RETURN(IntKillTimer(hWnd, uIDEvent, TRUE));
284
285 CLEANUP:
286 DPRINT("Leave NtUserKillSystemTimer, ret=%i\n", _ret_);
287 UserLeave();
288 END_CLEANUP;
289 }
290
291 /* EOF */