- Merge aicom-network-fixes up to r36740
[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 PUSER_MESSAGE_QUEUE MessageQueue;
65
66 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
67 Wnd, IDEvent, Elapse, TimerFunc, SystemTimer ? "TRUE" : "FALSE");
68
69 if ((Wnd == NULL) && ! SystemTimer)
70 {
71 DPRINT("Window-less timer\n");
72 /* find a free, window-less timer id */
73 IntLockWindowlessTimerBitmap();
74 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
75
76 if (IDEvent == (UINT_PTR) -1)
77 {
78 IntUnlockWindowlessTimerBitmap();
79 DPRINT1("Unable to find a free window-less timer id\n");
80 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
81 return 0;
82 }
83
84 HintIndex = ++IDEvent;
85 IntUnlockWindowlessTimerBitmap();
86 Ret = IDEvent;
87 MessageQueue = PsGetCurrentThreadWin32Thread()->MessageQueue;
88 }
89 else
90 {
91 if (!(Window = UserGetWindowObject(Wnd)))
92 {
93 DPRINT1("Invalid window handle\n");
94 return 0;
95 }
96
97 if (Window->OwnerThread->ThreadsProcess != PsGetCurrentProcess())
98 {
99 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
100 SetLastWin32Error(ERROR_ACCESS_DENIED);
101 return 0;
102 }
103
104 Ret = IDEvent;
105 MessageQueue = Window->MessageQueue;
106 }
107
108 #if 0
109
110 /* Windows NT/2k/XP behaviour */
111 if (Elapse > 0x7fffffff)
112 {
113 DPRINT("Adjusting uElapse\n");
114 Elapse = 1;
115 }
116
117 #else
118
119 /* Windows XP SP2 and Windows Server 2003 behaviour */
120 if (Elapse > 0x7fffffff)
121 {
122 DPRINT("Adjusting uElapse\n");
123 Elapse = 0x7fffffff;
124 }
125
126 #endif
127
128 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
129 if (Elapse < 10)
130 {
131 DPRINT("Adjusting uElapse\n");
132 Elapse = 10;
133 }
134
135 if (! MsqSetTimer(MessageQueue, Wnd,
136 IDEvent, Elapse, TimerFunc,
137 SystemTimer ? WM_SYSTIMER : WM_TIMER))
138 {
139 DPRINT1("Failed to set timer in message queue\n");
140 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
141 return 0;
142 }
143
144
145 return Ret;
146 }
147
148
149 BOOL FASTCALL
150 IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
151 {
152 PWINDOW_OBJECT Window = NULL;
153
154 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
155 Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
156
157 if (Wnd)
158 {
159 Window = UserGetWindowObject(Wnd);
160
161 if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue, Wnd,
162 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
163 {
164 // Give it another chance to find the timer.
165 if (Window && !( MsqKillTimer(Window->MessageQueue, Wnd,
166 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER)))
167 {
168 DPRINT1("Unable to locate timer in message queue for Window.\n");
169 SetLastWin32Error(ERROR_INVALID_PARAMETER);
170 return FALSE;
171 }
172 }
173 }
174
175 /* window-less timer? */
176 if ((Wnd == NULL) && ! SystemTimer)
177 {
178 if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue, Wnd,
179 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
180 {
181 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
182 SetLastWin32Error(ERROR_INVALID_PARAMETER);
183 return FALSE;
184 }
185
186 /* Release the id */
187 IntLockWindowlessTimerBitmap();
188
189 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
190 RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
191
192 IntUnlockWindowlessTimerBitmap();
193 }
194
195 return TRUE;
196 }
197
198 NTSTATUS FASTCALL
199 InitTimerImpl(VOID)
200 {
201 ULONG BitmapBytes;
202
203 ExInitializeFastMutex(&Mutex);
204
205 BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
206 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
207 if (WindowLessTimersBitMapBuffer == NULL)
208 {
209 return STATUS_UNSUCCESSFUL;
210 }
211
212 RtlInitializeBitMap(&WindowLessTimersBitMap,
213 WindowLessTimersBitMapBuffer,
214 BitmapBytes * 8);
215
216 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
217 RtlClearAllBits(&WindowLessTimersBitMap);
218
219 if (!FirstpTmr)
220 {
221 FirstpTmr = ExAllocatePoolWithTag(PagedPool, sizeof(TIMER), TAG_TIMERBMP);
222 if (FirstpTmr) InitializeListHead(&FirstpTmr->ptmrList);
223 }
224
225 return STATUS_SUCCESS;
226 }
227
228
229 UINT_PTR
230 STDCALL
231 NtUserSetTimer
232 (
233 HWND hWnd,
234 UINT_PTR nIDEvent,
235 UINT uElapse,
236 TIMERPROC lpTimerFunc
237 )
238 {
239 DECLARE_RETURN(UINT_PTR);
240
241 DPRINT("Enter NtUserSetTimer\n");
242 UserEnterExclusive();
243
244 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, FALSE));
245
246 CLEANUP:
247 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_);
248 UserLeave();
249 END_CLEANUP;
250 }
251
252
253 BOOL
254 STDCALL
255 NtUserKillTimer
256 (
257 HWND hWnd,
258 UINT_PTR uIDEvent
259 )
260 {
261 DECLARE_RETURN(BOOL);
262
263 DPRINT("Enter NtUserKillTimer\n");
264 UserEnterExclusive();
265
266 RETURN(IntKillTimer(hWnd, uIDEvent, FALSE));
267
268 CLEANUP:
269 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_);
270 UserLeave();
271 END_CLEANUP;
272 }
273
274
275 UINT_PTR
276 STDCALL
277 NtUserSetSystemTimer(
278 HWND hWnd,
279 UINT_PTR nIDEvent,
280 UINT uElapse,
281 TIMERPROC lpTimerFunc
282 )
283 {
284 DECLARE_RETURN(UINT_PTR);
285
286 DPRINT("Enter NtUserSetSystemTimer\n");
287 UserEnterExclusive();
288
289 RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, TRUE));
290
291 CLEANUP:
292 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_);
293 UserLeave();
294 END_CLEANUP;
295 }
296
297
298 /* EOF */