2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window timers messages
5 * FILE: subsystems/win32/win32k/ntuser/timer.c
7 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
8 * REVISION HISTORY: 10/04/2003 Implemented System Timers
12 /* INCLUDES ******************************************************************/
19 /* GLOBALS *******************************************************************/
21 static PTIMER FirstpTmr
= NULL
;
22 static LONG TimeLast
= 0;
24 #define MAX_ELAPSE_TIME 0x7FFFFFFF
26 /* Windows 2000 has room for 32768 window-less timers */
27 #define NUM_WINDOW_LESS_TIMERS 1024
29 static FAST_MUTEX Mutex
;
30 static RTL_BITMAP WindowLessTimersBitMap
;
31 static PVOID WindowLessTimersBitMapBuffer
;
32 static ULONG HintIndex
= 0;
35 #define IntLockWindowlessTimerBitmap() \
36 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
38 #define IntUnlockWindowlessTimerBitmap() \
39 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
41 /* FUNCTIONS *****************************************************************/
52 FirstpTmr
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otTimer
, sizeof(TIMER
));
53 if (FirstpTmr
) InitializeListHead(&FirstpTmr
->ptmrList
);
58 Ret
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otTimer
, sizeof(TIMER
));
59 if (Ret
) InsertTailList(&FirstpTmr
->ptmrList
, &Ret
->ptmrList
);
67 RemoveTimer(PTIMER pTmr
)
71 /* Set the flag, it will be removed when ready */
72 pTmr
->flags
|= TMRF_DELETEPENDING
;
80 FindTimer(PWINDOW_OBJECT Window
,
86 PTIMER pTmr
= FirstpTmr
, RetTmr
= NULL
;
87 KeEnterCriticalRegion();
92 if ( pTmr
->nID
== nID
&&
93 pTmr
->pWnd
== Window
&&
94 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
104 pLE
= pTmr
->ptmrList
.Flink
;
105 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
106 } while (pTmr
!= FirstpTmr
);
107 KeLeaveCriticalRegion();
114 FindSystemTimer(PMSG pMsg
)
117 PTIMER pTmr
= FirstpTmr
;
118 KeEnterCriticalRegion();
123 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
124 (pTmr
->flags
& TMRF_SYSTEM
) )
127 pLE
= pTmr
->ptmrList
.Flink
;
128 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
129 } while (pTmr
!= FirstpTmr
);
130 KeLeaveCriticalRegion();
137 ValidateTimerCallback(PTHREADINFO pti
,
138 PWINDOW_OBJECT Window
,
143 PTIMER pTmr
= FirstpTmr
;
145 if (!pTmr
) return FALSE
;
147 KeEnterCriticalRegion();
150 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
151 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
152 (pTmr
->pti
->ppi
== pti
->ppi
) )
155 pLE
= pTmr
->ptmrList
.Flink
;
156 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
157 } while (pTmr
!= FirstpTmr
);
158 KeLeaveCriticalRegion();
160 if (!pTmr
) return FALSE
;
166 IntSetTimer( PWINDOW_OBJECT Window
,
174 LARGE_INTEGER DueTime
;
175 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
178 /* Windows NT/2k/XP behaviour */
179 if (Elapse
> MAX_ELAPSE_TIME
)
181 DPRINT("Adjusting uElapse\n");
185 /* Windows XP SP2 and Windows Server 2003 behaviour */
186 if (Elapse
> MAX_ELAPSE_TIME
)
188 DPRINT("Adjusting uElapse\n");
189 Elapse
= MAX_ELAPSE_TIME
;
193 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
196 DPRINT("Adjusting uElapse\n");
200 if ((Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
202 IntLockWindowlessTimerBitmap();
203 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
205 if (IDEvent
== (UINT_PTR
) -1)
207 IntUnlockWindowlessTimerBitmap();
208 DPRINT1("Unable to find a free window-less timer id\n");
209 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
213 HintIndex
= ++IDEvent
;
214 IntUnlockWindowlessTimerBitmap();
218 pTmr
= FindTimer(Window
, IDEvent
, Type
, FALSE
);
221 pTmr
= CreateTimer();
224 if (Window
&& (Type
& TMRF_TIFROMWND
))
225 pTmr
->pti
= Window
->pti
->pEThread
->Tcb
.Win32Thread
;
229 pTmr
->pti
= ptiRawInput
;
231 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
234 pTmr
->cmsCountdown
= Elapse
;
235 pTmr
->cmsRate
= Elapse
;
236 pTmr
->pfn
= TimerFunc
;
238 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
241 pTmr
->cmsCountdown
= Elapse
;
242 pTmr
->cmsRate
= Elapse
;
243 if (pTmr
->flags
& TMRF_DELETEPENDING
)
245 pTmr
->flags
&= ~TMRF_DELETEPENDING
;
248 ASSERT(MasterTimer
!= NULL
);
249 // Start the timer thread!
250 if (pTmr
== FirstpTmr
)
251 KeSetTimer(MasterTimer
, DueTime
, NULL
);
257 // Process win32k system timers.
261 SystemTimerProc(HWND hwnd
,
266 DPRINT( "Timer Running!\n" );
273 // Need to start gdi syncro timers then start timer with Hang App proc
274 // that calles Idle process so the screen savers will know to run......
275 IntSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
280 SystemTimerSet( PWINDOW_OBJECT Window
,
283 TIMERPROC lpTimerFunc
)
285 if (Window
&& Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
287 SetLastWin32Error(ERROR_ACCESS_DENIED
);
290 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
295 PostTimerMessages(PWINDOW_OBJECT Window
)
298 PUSER_MESSAGE_QUEUE ThreadQueue
;
302 PTIMER pTmr
= FirstpTmr
;
304 if (!pTmr
) return FALSE
;
306 pti
= PsGetCurrentThreadWin32Thread();
307 ThreadQueue
= pti
->MessageQueue
;
309 KeEnterCriticalRegion();
313 if ( (pTmr
->flags
& TMRF_READY
) &&
314 (pTmr
->pti
== pti
) &&
315 ((pTmr
->pWnd
== Window
) || (Window
== NULL
) ) )
317 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->hSelf
: 0;
318 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
319 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
320 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
322 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_TIMER
);
323 pTmr
->flags
&= ~TMRF_READY
;
324 ThreadQueue
->WakeMask
= ~QS_TIMER
;
328 pLE
= pTmr
->ptmrList
.Flink
;
329 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
330 } while (pTmr
!= FirstpTmr
);
332 KeLeaveCriticalRegion();
341 LARGE_INTEGER TickCount
, DueTime
;
344 PTIMER pTmr
= FirstpTmr
;
348 UserEnterExclusive();
350 KeQueryTickCount(&TickCount
);
351 Time
= MsqCalculateMessageTime(&TickCount
);
353 DueTime
.QuadPart
= (LONGLONG
)(-1000000);
357 if (pTmr
->flags
& TMRF_WAITING
)
359 pLE
= pTmr
->ptmrList
.Flink
;
360 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
364 if (pTmr
->flags
& TMRF_INIT
)
366 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
370 if (pTmr
->cmsCountdown
< 0)
373 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
375 if (pTmr
->flags
& TMRF_ONESHOT
)
376 pTmr
->flags
|= TMRF_WAITING
;
378 if (pTmr
->flags
& TMRF_RIT
)
380 // Hard coded call here, inside raw input thread.
381 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
385 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
386 // Set thread message queue for this timer.
387 if (pTmr
->pti
->MessageQueue
)
389 ASSERT(pTmr
->pti
->MessageQueue
->NewMessages
!= NULL
);
390 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
394 if (pTmr
->flags
& TMRF_DELETEPENDING
)
396 DPRINT("Removing Timer %x from List\n", pTmr
);
398 /* FIXME: Fix this!!!! */
402 DPRINT1("Clearing Bits for WindowLess Timer\n");
403 IntLockWindowlessTimerBitmap();
404 RtlSetBits(&WindowLessTimersBitMap, pTmr->nID, 1);
405 IntUnlockWindowlessTimerBitmap();
408 RemoveEntryList(&pTmr
->ptmrList
);
409 UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
412 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
415 pTmr
->cmsCountdown
-= Time
- TimeLast
;
418 pLE
= pTmr
->ptmrList
.Flink
;
419 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
420 } while (pTmr
!= FirstpTmr
);
422 // Restart the timer thread!
423 ASSERT(MasterTimer
!= NULL
);
424 KeSetTimer(MasterTimer
, DueTime
, NULL
);
433 // Old Timer Queueing
437 InternalSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
439 PWINDOW_OBJECT Window
;
442 PUSER_MESSAGE_QUEUE MessageQueue
;
444 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
445 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
447 if ((Wnd
== NULL
) && ! SystemTimer
)
449 DPRINT("Window-less timer\n");
450 /* find a free, window-less timer id */
451 IntLockWindowlessTimerBitmap();
452 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
454 if (IDEvent
== (UINT_PTR
) -1)
456 IntUnlockWindowlessTimerBitmap();
457 DPRINT1("Unable to find a free window-less timer id\n");
458 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
462 HintIndex
= ++IDEvent
;
463 IntUnlockWindowlessTimerBitmap();
465 pti
= PsGetCurrentThreadWin32Thread();
466 MessageQueue
= pti
->MessageQueue
;
470 if (!(Window
= UserGetWindowObject(Wnd
)))
472 DPRINT1("Invalid window handle\n");
476 if (Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
478 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
479 SetLastWin32Error(ERROR_ACCESS_DENIED
);
484 MessageQueue
= Window
->pti
->MessageQueue
;
489 /* Windows NT/2k/XP behaviour */
490 if (Elapse
> 0x7fffffff)
492 DPRINT("Adjusting uElapse\n");
498 /* Windows XP SP2 and Windows Server 2003 behaviour */
499 if (Elapse
> 0x7fffffff)
501 DPRINT("Adjusting uElapse\n");
507 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
510 DPRINT("Adjusting uElapse\n");
514 if (! MsqSetTimer(MessageQueue
, Wnd
,
515 IDEvent
, Elapse
, TimerFunc
,
516 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
518 DPRINT1("Failed to set timer in message queue\n");
519 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
523 if (Ret
== 0) ASSERT(FALSE
);
528 DestroyTimersForThread(PTHREADINFO pti
)
531 PTIMER pTmr
= FirstpTmr
;
532 BOOL TimersRemoved
= FALSE
;
534 if (FirstpTmr
== NULL
)
537 KeEnterCriticalRegion();
541 if ((pTmr
) && (pTmr
->pti
== pti
))
543 pTmr
->flags
&= ~TMRF_READY
;
544 pTmr
->flags
|= TMRF_DELETEPENDING
;
545 TimersRemoved
= TRUE
;
547 pLE
= pTmr
->ptmrList
.Flink
;
548 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
549 } while (pTmr
!= FirstpTmr
);
551 KeLeaveCriticalRegion();
553 return TimersRemoved
;
558 IntKillTimer(PWINDOW_OBJECT Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
561 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
562 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
567 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0, TRUE
);
568 return pTmr
? TRUE
: FALSE
;
578 InternalKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
581 PWINDOW_OBJECT Window
= NULL
;
583 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
584 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
586 pti
= PsGetCurrentThreadWin32Thread();
589 Window
= UserGetWindowObject(Wnd
);
591 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
592 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
594 // Give it another chance to find the timer.
595 if (Window
&& !( MsqKillTimer(Window
->pti
->MessageQueue
, Wnd
,
596 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
598 DPRINT1("Unable to locate timer in message queue for Window.\n");
599 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
605 /* window-less timer? */
606 if ((Wnd
== NULL
) && ! SystemTimer
)
608 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
609 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
611 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
612 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
617 IntLockWindowlessTimerBitmap();
619 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
620 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
622 HintIndex
= IDEvent
- 1;
624 IntUnlockWindowlessTimerBitmap();
635 ExInitializeFastMutex(&Mutex
);
637 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
638 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
639 if (WindowLessTimersBitMapBuffer
== NULL
)
641 return STATUS_UNSUCCESSFUL
;
644 RtlInitializeBitMap(&WindowLessTimersBitMap
,
645 WindowLessTimersBitMapBuffer
,
648 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
649 RtlClearAllBits(&WindowLessTimersBitMap
);
651 return STATUS_SUCCESS
;
661 TIMERPROC lpTimerFunc
664 DECLARE_RETURN(UINT_PTR
);
666 DPRINT("Enter NtUserSetTimer\n");
667 UserEnterExclusive();
669 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, 0));
672 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
686 PWINDOW_OBJECT Window
;
687 DECLARE_RETURN(BOOL
);
689 DPRINT("Enter NtUserKillTimer\n");
690 UserEnterExclusive();
692 Window
= UserGetWindowObject(hWnd
);
694 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
697 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
705 NtUserSetSystemTimer(
709 TIMERPROC lpTimerFunc
712 DECLARE_RETURN(UINT_PTR
);
714 DPRINT("Enter NtUserSetSystemTimer\n");
715 UserEnterExclusive();
717 // This is wrong, lpTimerFunc is NULL!
718 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
));
721 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);