2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
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.
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
25 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
26 * REVISION HISTORY: 10/04/2003 Implemented System Timers
30 /* INCLUDES ******************************************************************/
37 /* GLOBALS *******************************************************************/
39 static PTIMER FirstpTmr
= NULL
;
41 /* Windows 2000 has room for 32768 window-less timers */
42 #define NUM_WINDOW_LESS_TIMERS 1024
44 static FAST_MUTEX Mutex
;
45 static RTL_BITMAP WindowLessTimersBitMap
;
46 static PVOID WindowLessTimersBitMapBuffer
;
47 static ULONG HintIndex
= 0;
50 #define IntLockWindowlessTimerBitmap() \
51 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
53 #define IntUnlockWindowlessTimerBitmap() \
54 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
56 /* FUNCTIONS *****************************************************************/
60 IntSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
62 PWINDOW_OBJECT Window
;
64 PUSER_MESSAGE_QUEUE MessageQueue
;
66 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
67 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
69 if ((Wnd
== NULL
) && ! SystemTimer
)
71 DPRINT("Window-less timer\n");
72 /* find a free, window-less timer id */
73 IntLockWindowlessTimerBitmap();
74 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
76 if (IDEvent
== (UINT_PTR
) -1)
78 IntUnlockWindowlessTimerBitmap();
79 DPRINT1("Unable to find a free window-less timer id\n");
80 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
84 HintIndex
= ++IDEvent
;
85 IntUnlockWindowlessTimerBitmap();
87 MessageQueue
= PsGetCurrentThreadWin32Thread()->MessageQueue
;
91 if (!(Window
= UserGetWindowObject(Wnd
)))
93 DPRINT1("Invalid window handle\n");
97 if (Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
99 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
100 SetLastWin32Error(ERROR_ACCESS_DENIED
);
105 MessageQueue
= Window
->MessageQueue
;
110 /* Windows NT/2k/XP behaviour */
111 if (Elapse
> 0x7fffffff)
113 DPRINT("Adjusting uElapse\n");
119 /* Windows XP SP2 and Windows Server 2003 behaviour */
120 if (Elapse
> 0x7fffffff)
122 DPRINT("Adjusting uElapse\n");
128 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
131 DPRINT("Adjusting uElapse\n");
135 if (! MsqSetTimer(MessageQueue
, Wnd
,
136 IDEvent
, Elapse
, TimerFunc
,
137 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
139 DPRINT1("Failed to set timer in message queue\n");
140 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
150 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
152 PWINDOW_OBJECT Window
= NULL
;
154 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
155 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
159 Window
= UserGetWindowObject(Wnd
);
161 if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue
, Wnd
,
162 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
164 // Give it another chance to find the timer.
165 if (Window
&& !( MsqKillTimer(Window
->MessageQueue
, Wnd
,
166 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
168 DPRINT1("Unable to locate timer in message queue for Window.\n");
169 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
175 /* window-less timer? */
176 if ((Wnd
== NULL
) && ! SystemTimer
)
178 if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue
, Wnd
,
179 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
181 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
182 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
187 IntLockWindowlessTimerBitmap();
189 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
190 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
192 IntUnlockWindowlessTimerBitmap();
203 ExInitializeFastMutex(&Mutex
);
205 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
206 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
207 if (WindowLessTimersBitMapBuffer
== NULL
)
209 return STATUS_UNSUCCESSFUL
;
212 RtlInitializeBitMap(&WindowLessTimersBitMap
,
213 WindowLessTimersBitMapBuffer
,
216 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
217 RtlClearAllBits(&WindowLessTimersBitMap
);
221 FirstpTmr
= ExAllocatePoolWithTag(PagedPool
, sizeof(TIMER
), TAG_TIMERBMP
);
222 if (FirstpTmr
) InitializeListHead(&FirstpTmr
->ptmrList
);
225 return STATUS_SUCCESS
;
236 TIMERPROC lpTimerFunc
239 DECLARE_RETURN(UINT_PTR
);
241 DPRINT("Enter NtUserSetTimer\n");
242 UserEnterExclusive();
244 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
247 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
261 DECLARE_RETURN(BOOL
);
263 DPRINT("Enter NtUserKillTimer\n");
264 UserEnterExclusive();
266 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
269 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
277 NtUserSetSystemTimer(
281 TIMERPROC lpTimerFunc
284 DECLARE_RETURN(UINT_PTR
);
286 DPRINT("Enter NtUserSetSystemTimer\n");
287 UserEnterExclusive();
289 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
292 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);