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
;
40 static LONG TimeLast
= 0;
42 #define MAX_ELAPSE_TIME 0x7FFFFFFF
44 /* Windows 2000 has room for 32768 window-less timers */
45 #define NUM_WINDOW_LESS_TIMERS 1024
47 static FAST_MUTEX Mutex
;
48 static RTL_BITMAP WindowLessTimersBitMap
;
49 static PVOID WindowLessTimersBitMapBuffer
;
50 static ULONG HintIndex
= 0;
53 #define IntLockWindowlessTimerBitmap() \
54 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
56 #define IntUnlockWindowlessTimerBitmap() \
57 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
59 /* FUNCTIONS *****************************************************************/
70 FirstpTmr
= UserCreateObject(gHandleTable
, &Handle
, otTimer
, sizeof(TIMER
));
71 if (FirstpTmr
) InitializeListHead(&FirstpTmr
->ptmrList
);
76 Ret
= UserCreateObject(gHandleTable
, &Handle
, otTimer
, sizeof(TIMER
));
77 if (Ret
) InsertTailList(&FirstpTmr
->ptmrList
, &Ret
->ptmrList
);
85 RemoveTimer(PTIMER pTmr
)
89 RemoveEntryList(&pTmr
->ptmrList
);
90 UserDeleteObject( USER_BODY_TO_HEADER(pTmr
)->hSelf
, otTimer
);
98 FindTimer(PWINDOW_OBJECT Window
,
103 PTIMER pTmr
= FirstpTmr
;
104 KeEnterCriticalRegion();
109 if ( pTmr
->nID
== nID
&&
110 pTmr
->pWnd
== Window
&&
111 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
116 pTmr
= (PTIMER
)1; // We are here to remove the timer.
121 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
122 } while (pTmr
!= FirstpTmr
);
123 KeLeaveCriticalRegion();
130 FindSystemTimer(PMSG pMsg
)
132 PTIMER pTmr
= FirstpTmr
;
133 KeEnterCriticalRegion();
138 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
139 (pTmr
->flags
& TMRF_SYSTEM
) )
142 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
143 } while (pTmr
!= FirstpTmr
);
144 KeLeaveCriticalRegion();
151 ValidateTimerCallback(PTHREADINFO pti
,
152 PWINDOW_OBJECT Window
,
156 PTIMER pTmr
= FirstpTmr
;
158 if (!pTmr
) return FALSE
;
160 KeEnterCriticalRegion();
163 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
164 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
165 (pTmr
->pti
->ThreadInfo
->ppi
== pti
->ThreadInfo
->ppi
) )
168 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
169 } while (pTmr
!= FirstpTmr
);
170 KeLeaveCriticalRegion();
172 if (!pTmr
) return FALSE
;
177 // Rename it to IntSetTimer after move.
179 InternalSetTimer( PWINDOW_OBJECT Window
,
186 LARGE_INTEGER DueTime
;
187 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
190 /* Windows NT/2k/XP behaviour */
191 if (Elapse
> MAX_ELAPSE_TIME
)
193 DPRINT("Adjusting uElapse\n");
197 /* Windows XP SP2 and Windows Server 2003 behaviour */
198 if (Elapse
> MAX_ELAPSE_TIME
)
200 DPRINT("Adjusting uElapse\n");
201 Elapse
= MAX_ELAPSE_TIME
;
205 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
208 DPRINT("Adjusting uElapse\n");
212 pTmr
= FindTimer(Window
, IDEvent
, Type
, FALSE
);
215 pTmr
= CreateTimer();
218 if (Window
&& (Type
& TMRF_TIFROMWND
))
219 pTmr
->pti
= Window
->OwnerThread
->Tcb
.Win32Thread
;
223 pTmr
->pti
= ptiRawInput
;
225 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
228 pTmr
->cmsCountdown
= Elapse
;
229 pTmr
->cmsRate
= Elapse
;
230 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
231 pTmr
->pfn
= TimerFunc
;
234 InsertTailList(&FirstpTmr
->ptmrList
, &pTmr
->ptmrList
);
237 // Start the timer thread!
238 KeSetTimer(MasterTimer
, DueTime
, NULL
);
240 if (!pTmr
->nID
) return 1;
245 // Process win32k system timers.
249 SystemTimerProc(HWND hwnd
,
254 DPRINT( "Timer Running!\n" );
261 // Need to start gdi syncro timers then start timer with Hang App proc
262 // that calles Idle process so the screen savers will know to run......
263 InternalSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
268 SetSystemTimer( PWINDOW_OBJECT Window
,
271 TIMERPROC lpTimerFunc
)
273 if (Window
&& Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
275 SetLastWin32Error(ERROR_ACCESS_DENIED
);
278 return InternalSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
283 PostTimerMessages(HWND hWnd
)
285 PUSER_MESSAGE_QUEUE ThreadQueue
;
288 PWINDOW_OBJECT pWnd
= NULL
;
290 PTIMER pTmr
= FirstpTmr
;
292 if (!pTmr
) return FALSE
;
296 pWnd
= UserGetWindowObject(hWnd
);
297 if (!pWnd
|| !pWnd
->Wnd
) return FALSE
;
300 pti
= PsGetCurrentThreadWin32Thread();
301 ThreadQueue
= pti
->MessageQueue
;
303 KeEnterCriticalRegion();
306 if ( (pTmr
->flags
& TMRF_READY
) &&
307 (pTmr
->pti
== pti
) &&
308 (pTmr
->pWnd
== pWnd
))
311 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
312 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
313 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
314 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_POSTMESSAGE
);
316 pTmr
->flags
&= ~TMRF_READY
;
317 ThreadQueue
->WakeMask
= ~QS_TIMER
;
321 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
322 } while (pTmr
!= FirstpTmr
);
323 KeLeaveCriticalRegion();
332 LARGE_INTEGER TickCount
, DueTime
;
334 PTIMER pTmr
= FirstpTmr
;
338 UserEnterExclusive();
340 KeQueryTickCount(&TickCount
);
341 Time
= MsqCalculateMessageTime(&TickCount
);
343 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
347 if (pTmr
->flags
& TMRF_WAITING
)
349 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
353 if (pTmr
->flags
& TMRF_INIT
)
354 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
357 if (pTmr
->cmsCountdown
< 0)
359 if (!(pTmr
->flags
& TMRF_READY
))
361 if (pTmr
->flags
& TMRF_ONESHOT
)
362 pTmr
->flags
|= TMRF_WAITING
;
364 if (pTmr
->flags
& TMRF_RIT
)
366 // Hard coded call here, inside raw input thread.
367 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
371 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
372 // Set thread message queue for this timer.
373 if (pTmr
->pti
->MessageQueue
)
375 pTmr
->pti
->MessageQueue
->WakeMask
|= QS_TIMER
;
376 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
380 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
383 pTmr
->cmsCountdown
-= Time
- TimeLast
;
385 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
386 } while (pTmr
!= FirstpTmr
);
388 // Restart the timer thread!
389 KeSetTimer(MasterTimer
, DueTime
, NULL
);
398 // Old Timer Queueing
402 IntSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
404 PWINDOW_OBJECT Window
;
407 PUSER_MESSAGE_QUEUE MessageQueue
;
409 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
410 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
412 if ((Wnd
== NULL
) && ! SystemTimer
)
414 DPRINT("Window-less timer\n");
415 /* find a free, window-less timer id */
416 IntLockWindowlessTimerBitmap();
417 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
419 if (IDEvent
== (UINT_PTR
) -1)
421 IntUnlockWindowlessTimerBitmap();
422 DPRINT1("Unable to find a free window-less timer id\n");
423 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
427 HintIndex
= ++IDEvent
;
428 IntUnlockWindowlessTimerBitmap();
430 pti
= PsGetCurrentThreadWin32Thread();
431 MessageQueue
= pti
->MessageQueue
;
435 if (!(Window
= UserGetWindowObject(Wnd
)))
437 DPRINT1("Invalid window handle\n");
441 if (Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
443 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
444 SetLastWin32Error(ERROR_ACCESS_DENIED
);
449 MessageQueue
= Window
->MessageQueue
;
454 /* Windows NT/2k/XP behaviour */
455 if (Elapse
> 0x7fffffff)
457 DPRINT("Adjusting uElapse\n");
463 /* Windows XP SP2 and Windows Server 2003 behaviour */
464 if (Elapse
> 0x7fffffff)
466 DPRINT("Adjusting uElapse\n");
472 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
475 DPRINT("Adjusting uElapse\n");
479 if (! MsqSetTimer(MessageQueue
, Wnd
,
480 IDEvent
, Elapse
, TimerFunc
,
481 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
483 DPRINT1("Failed to set timer in message queue\n");
484 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
494 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
497 PWINDOW_OBJECT Window
= NULL
;
499 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
500 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
502 pti
= PsGetCurrentThreadWin32Thread();
505 Window
= UserGetWindowObject(Wnd
);
507 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
508 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
510 // Give it another chance to find the timer.
511 if (Window
&& !( MsqKillTimer(Window
->MessageQueue
, Wnd
,
512 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
514 DPRINT1("Unable to locate timer in message queue for Window.\n");
515 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
521 /* window-less timer? */
522 if ((Wnd
== NULL
) && ! SystemTimer
)
524 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
525 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
527 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
528 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
533 IntLockWindowlessTimerBitmap();
535 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
536 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
538 IntUnlockWindowlessTimerBitmap();
549 ExInitializeFastMutex(&Mutex
);
551 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
552 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
553 if (WindowLessTimersBitMapBuffer
== NULL
)
555 return STATUS_UNSUCCESSFUL
;
558 RtlInitializeBitMap(&WindowLessTimersBitMap
,
559 WindowLessTimersBitMapBuffer
,
562 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
563 RtlClearAllBits(&WindowLessTimersBitMap
);
565 return STATUS_SUCCESS
;
575 TIMERPROC lpTimerFunc
578 DECLARE_RETURN(UINT_PTR
);
580 DPRINT("Enter NtUserSetTimer\n");
581 UserEnterExclusive();
583 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
586 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
600 DECLARE_RETURN(BOOL
);
602 DPRINT("Enter NtUserKillTimer\n");
603 UserEnterExclusive();
605 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
608 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
616 NtUserSetSystemTimer(
620 TIMERPROC lpTimerFunc
623 DECLARE_RETURN(UINT_PTR
);
625 DPRINT("Enter NtUserSetSystemTimer\n");
626 UserEnterExclusive();
628 // This is wrong, lpTimerFunc is NULL!
629 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
632 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);