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
;
65 PUSER_MESSAGE_QUEUE MessageQueue
;
67 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
68 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
70 if ((Wnd
== NULL
) && ! SystemTimer
)
72 DPRINT("Window-less timer\n");
73 /* find a free, window-less timer id */
74 IntLockWindowlessTimerBitmap();
75 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
77 if (IDEvent
== (UINT_PTR
) -1)
79 IntUnlockWindowlessTimerBitmap();
80 DPRINT1("Unable to find a free window-less timer id\n");
81 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
85 HintIndex
= ++IDEvent
;
86 IntUnlockWindowlessTimerBitmap();
88 pti
= PsGetCurrentThreadWin32Thread();
89 MessageQueue
= pti
->MessageQueue
;
93 if (!(Window
= UserGetWindowObject(Wnd
)))
95 DPRINT1("Invalid window handle\n");
99 if (Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
101 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
102 SetLastWin32Error(ERROR_ACCESS_DENIED
);
107 MessageQueue
= Window
->MessageQueue
;
112 /* Windows NT/2k/XP behaviour */
113 if (Elapse
> 0x7fffffff)
115 DPRINT("Adjusting uElapse\n");
121 /* Windows XP SP2 and Windows Server 2003 behaviour */
122 if (Elapse
> 0x7fffffff)
124 DPRINT("Adjusting uElapse\n");
130 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
133 DPRINT("Adjusting uElapse\n");
137 if (! MsqSetTimer(MessageQueue
, Wnd
,
138 IDEvent
, Elapse
, TimerFunc
,
139 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
141 DPRINT1("Failed to set timer in message queue\n");
142 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
152 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
155 PWINDOW_OBJECT Window
= NULL
;
157 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
158 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
160 pti
= PsGetCurrentThreadWin32Thread();
163 Window
= UserGetWindowObject(Wnd
);
165 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
166 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
168 // Give it another chance to find the timer.
169 if (Window
&& !( MsqKillTimer(Window
->MessageQueue
, Wnd
,
170 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
172 DPRINT1("Unable to locate timer in message queue for Window.\n");
173 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
179 /* window-less timer? */
180 if ((Wnd
== NULL
) && ! SystemTimer
)
182 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
183 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
185 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
186 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
191 IntLockWindowlessTimerBitmap();
193 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
194 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
196 IntUnlockWindowlessTimerBitmap();
207 ExInitializeFastMutex(&Mutex
);
209 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
210 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
211 if (WindowLessTimersBitMapBuffer
== NULL
)
213 return STATUS_UNSUCCESSFUL
;
216 RtlInitializeBitMap(&WindowLessTimersBitMap
,
217 WindowLessTimersBitMapBuffer
,
220 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
221 RtlClearAllBits(&WindowLessTimersBitMap
);
225 FirstpTmr
= ExAllocatePoolWithTag(PagedPool
, sizeof(TIMER
), TAG_TIMERBMP
);
226 if (FirstpTmr
) InitializeListHead(&FirstpTmr
->ptmrList
);
229 return STATUS_SUCCESS
;
240 TIMERPROC lpTimerFunc
243 DECLARE_RETURN(UINT_PTR
);
245 DPRINT("Enter NtUserSetTimer\n");
246 UserEnterExclusive();
248 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
251 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
265 DECLARE_RETURN(BOOL
);
267 DPRINT("Enter NtUserKillTimer\n");
268 UserEnterExclusive();
270 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
273 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
281 NtUserSetSystemTimer(
285 TIMERPROC lpTimerFunc
288 DECLARE_RETURN(UINT_PTR
);
290 DPRINT("Enter NtUserSetSystemTimer\n");
291 UserEnterExclusive();
293 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
296 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);