sync to trunk head (37853) (except rbuild changes)
[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 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Window timers messages
23 * FILE: subsystems/win32/win32k/ntuser/timer.c
24 * PROGRAMER: Gunnar
25 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
26 * REVISION HISTORY: 10/04/2003 Implemented System Timers
27 *
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <w32k.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 /* GLOBALS *******************************************************************/
38
39 static PTIMER FirstpTmr = NULL;
40
41 /* Windows 2000 has room for 32768 window-less timers */
42 #define NUM_WINDOW_LESS_TIMERS 1024
43
44 static FAST_MUTEX Mutex;
45 static RTL_BITMAP WindowLessTimersBitMap;
46 static PVOID WindowLessTimersBitMapBuffer;
47 static ULONG HintIndex = 0;
48
49
50 #define IntLockWindowlessTimerBitmap() \
51 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
52
53 #define IntUnlockWindowlessTimerBitmap() \
54 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
55
56 /* FUNCTIONS *****************************************************************/
57
58
59 UINT_PTR FASTCALL
60 IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer)
61 {
62 PWINDOW_OBJECT Window;
63 UINT_PTR Ret = 0;
64 PTHREADINFO pti;
65 PUSER_MESSAGE_QUEUE MessageQueue;
66
67 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
68 Wnd, IDEvent, Elapse, TimerFunc, SystemTimer ? "TRUE" : "FALSE");
69
70 if ((Wnd == NULL) && ! SystemTimer)
71 {
72 DPRINT("Window-less timer\n");
73 /* find a free, window-less timer id */
74 IntLockWindowlessTimerBitmap();
75 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
76
77 if (IDEvent == (UINT_PTR) -1)
78 {
79 IntUnlockWindowlessTimerBitmap();
80 DPRINT1("Unable to find a free window-less timer id\n");
81 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
82 return 0;
83 }
84
85 HintIndex = ++IDEvent;
86 IntUnlockWindowlessTimerBitmap();
87 Ret = IDEvent;
88 pti = PsGetCurrentThreadWin32Thread();
89 MessageQueue = pti->MessageQueue;
90 }
91 else
92 {
93 if (!(Window = UserGetWindowObject(Wnd)))
94 {
95 DPRINT1("Invalid window handle\n");
96 return 0;
97 }
98
99 if (Window->OwnerThread->ThreadsProcess != PsGetCurrentProcess())
100 {
101 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
102 SetLastWin32Error(ERROR_ACCESS_DENIED);
103 return 0;
104 }
105
106 Ret = IDEvent;
107 MessageQueue = Window->MessageQueue;
108 }
109
110 #if 0
111
112 /* Windows NT/2k/XP behaviour */
113 if (Elapse > 0x7fffffff)
114 {
115 DPRINT("Adjusting uElapse\n");
116 Elapse = 1;
117 }
118
119 #else
120
121 /* Windows XP SP2 and Windows Server 2003 behaviour */
122 if (Elapse > 0x7fffffff)
123 {
124 DPRINT("Adjusting uElapse\n");
125 Elapse = 0x7fffffff;
126 }
127
128 #endif
129
130 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
131 if (Elapse < 10)
132 {
133 DPRINT("Adjusting uElapse\n");
134 Elapse = 10;
135 }
136
137 if (! MsqSetTimer(MessageQueue, Wnd,
138 IDEvent, Elapse, TimerFunc,
139 SystemTimer ? WM_SYSTIMER : WM_TIMER))
140 {
141 DPRINT1("Failed to set timer in message queue\n");
142 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
143 return 0;
144 }
145
146
147 return Ret;
148 }
149
150
151 BOOL FASTCALL
152 IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
153 {
154 PTHREADINFO pti;
155 PWINDOW_OBJECT Window = NULL;
156
157 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
158 Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
159
160 pti = PsGetCurrentThreadWin32Thread();
161 if (Wnd)
162 {
163 Window = UserGetWindowObject(Wnd);
164
165 if (! MsqKillTimer(pti->MessageQueue, Wnd,
166 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
167 {
168 // Give it another chance to find the timer.
169 if (Window && !( MsqKillTimer(Window->MessageQueue, Wnd,
170 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER)))
171 {
172 DPRINT1("Unable to locate timer in message queue for Window.\n");
173 SetLastWin32Error(ERROR_INVALID_PARAMETER);
174 return FALSE;
175 }
176 }
177 }
178
179 /* window-less timer? */
180 if ((Wnd == NULL) && ! SystemTimer)
181 {
182 if (! MsqKillTimer(pti->MessageQueue, Wnd,
183 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
184 {
185 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
186 SetLastWin32Error(ERROR_INVALID_PARAMETER);
187 return FALSE;
188 }
189
190 /* Release the id */
191 IntLockWindowlessTimerBitmap();
192
193 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
194 RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
195
196 IntUnlockWindowlessTimerBitmap();
197 }
198
199 return TRUE;
200 }
201
202 NTSTATUS FASTCALL
203 InitTimerImpl(VOID)
204 {
205 ULONG BitmapBytes;
206
207 ExInitializeFastMutex(&Mutex);
208
209 BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
210 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
211 if (WindowLessTimersBitMapBuffer == NULL)
212 {
213 return STATUS_UNSUCCESSFUL;
214 }
215
216 RtlInitializeBitMap(&WindowLessTimersBitMap,
217 WindowLessTimersBitMapBuffer,
218 BitmapBytes * 8);
219
220 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
221 RtlClearAllBits(&WindowLessTimersBitMap);
222
223 if (!FirstpTmr)
224 {
225 FirstpTmr = ExAllocatePoolWithTag(PagedPool, sizeof(TIMER), TAG_TIMERBMP);
226 if (FirstpTmr) InitializeListHead(&FirstpTmr->ptmrList);
227 }
228
229 return STATUS_SUCCESS;
230 }
231
232
233 UINT_PTR
234 APIENTRY
235 NtUserSetTimer
236 (
237 HWND hWnd,
238 UINT_PTR nIDEvent,
239 UINT uElapse,
240 TIMERPROC lpTimerFunc
241 )
242 {
243 DECLARE_RETURN(UINT_PTR);
244
245 DPRINT("Enter NtUserSetTimer\n");
246 UserEnterExclusive();
247
248 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, FALSE));
249
250 CLEANUP:
251 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_);
252 UserLeave();
253 END_CLEANUP;
254 }
255
256
257 BOOL
258 APIENTRY
259 NtUserKillTimer
260 (
261 HWND hWnd,
262 UINT_PTR uIDEvent
263 )
264 {
265 DECLARE_RETURN(BOOL);
266
267 DPRINT("Enter NtUserKillTimer\n");
268 UserEnterExclusive();
269
270 RETURN(IntKillTimer(hWnd, uIDEvent, FALSE));
271
272 CLEANUP:
273 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_);
274 UserLeave();
275 END_CLEANUP;
276 }
277
278
279 UINT_PTR
280 APIENTRY
281 NtUserSetSystemTimer(
282 HWND hWnd,
283 UINT_PTR nIDEvent,
284 UINT uElapse,
285 TIMERPROC lpTimerFunc
286 )
287 {
288 DECLARE_RETURN(UINT_PTR);
289
290 DPRINT("Enter NtUserSetSystemTimer\n");
291 UserEnterExclusive();
292
293 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, TRUE));
294
295 CLEANUP:
296 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_);
297 UserLeave();
298 END_CLEANUP;
299 }
300
301
302 /* EOF */