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 RemoveEntryList(&pTmr
->ptmrList
);
73 UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
81 FindTimer(PWINDOW_OBJECT Window
,
87 PTIMER pTmr
= FirstpTmr
, RetTmr
= NULL
;
88 KeEnterCriticalRegion();
93 if ( pTmr
->nID
== nID
&&
94 pTmr
->pWnd
== Window
&&
95 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
105 pLE
= pTmr
->ptmrList
.Flink
;
106 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
107 } while (pTmr
!= FirstpTmr
);
108 KeLeaveCriticalRegion();
115 FindSystemTimer(PMSG pMsg
)
118 PTIMER pTmr
= FirstpTmr
;
119 KeEnterCriticalRegion();
124 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
125 (pTmr
->flags
& TMRF_SYSTEM
) )
128 pLE
= pTmr
->ptmrList
.Flink
;
129 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
130 } while (pTmr
!= FirstpTmr
);
131 KeLeaveCriticalRegion();
138 ValidateTimerCallback(PTHREADINFO pti
,
139 PWINDOW_OBJECT Window
,
144 PTIMER pTmr
= FirstpTmr
;
146 if (!pTmr
) return FALSE
;
148 KeEnterCriticalRegion();
151 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
152 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
153 (pTmr
->pti
->ppi
== pti
->ppi
) )
156 pLE
= pTmr
->ptmrList
.Flink
;
157 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
158 } while (pTmr
!= FirstpTmr
);
159 KeLeaveCriticalRegion();
161 if (!pTmr
) return FALSE
;
167 IntSetTimer( PWINDOW_OBJECT Window
,
175 LARGE_INTEGER DueTime
;
176 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
179 /* Windows NT/2k/XP behaviour */
180 if (Elapse
> MAX_ELAPSE_TIME
)
182 DPRINT("Adjusting uElapse\n");
186 /* Windows XP SP2 and Windows Server 2003 behaviour */
187 if (Elapse
> MAX_ELAPSE_TIME
)
189 DPRINT("Adjusting uElapse\n");
190 Elapse
= MAX_ELAPSE_TIME
;
194 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
197 DPRINT("Adjusting uElapse\n");
201 if ((Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
203 IntLockWindowlessTimerBitmap();
204 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
206 if (IDEvent
== (UINT_PTR
) -1)
208 IntUnlockWindowlessTimerBitmap();
209 DPRINT1("Unable to find a free window-less timer id\n");
210 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
214 HintIndex
= ++IDEvent
;
215 IntUnlockWindowlessTimerBitmap();
219 if ((Window
) && (IDEvent
== 0))
222 pTmr
= FindTimer(Window
, IDEvent
, Type
, FALSE
);
225 pTmr
= CreateTimer();
228 if (Window
&& (Type
& TMRF_TIFROMWND
))
229 pTmr
->pti
= Window
->pti
->pEThread
->Tcb
.Win32Thread
;
233 pTmr
->pti
= ptiRawInput
;
235 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
239 pTmr
->cmsCountdown
= Elapse
;
240 pTmr
->cmsRate
= Elapse
;
241 pTmr
->pfn
= TimerFunc
;
243 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
247 pTmr
->cmsCountdown
= Elapse
;
248 pTmr
->cmsRate
= Elapse
;
251 ASSERT(MasterTimer
!= NULL
);
252 // Start the timer thread!
253 if (pTmr
== FirstpTmr
)
254 KeSetTimer(MasterTimer
, DueTime
, NULL
);
260 // Process win32k system timers.
264 SystemTimerProc(HWND hwnd
,
269 DPRINT( "Timer Running!\n" );
276 // Need to start gdi syncro timers then start timer with Hang App proc
277 // that calles Idle process so the screen savers will know to run......
278 IntSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
283 SystemTimerSet( PWINDOW_OBJECT Window
,
286 TIMERPROC lpTimerFunc
)
288 if (Window
&& Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
290 SetLastWin32Error(ERROR_ACCESS_DENIED
);
293 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
298 PostTimerMessages(PWINDOW_OBJECT Window
)
301 PUSER_MESSAGE_QUEUE ThreadQueue
;
305 PTIMER pTmr
= FirstpTmr
;
307 if (!pTmr
) return FALSE
;
309 pti
= PsGetCurrentThreadWin32Thread();
310 ThreadQueue
= pti
->MessageQueue
;
312 UserEnterExclusive();
316 if ( (pTmr
->flags
& TMRF_READY
) &&
317 (pTmr
->pti
== pti
) &&
318 ((pTmr
->pWnd
== Window
) || (Window
== NULL
) ) )
320 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->hSelf
: 0;
321 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
322 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
323 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
325 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_TIMER
);
326 pTmr
->flags
&= ~TMRF_READY
;
327 ThreadQueue
->WakeMask
= ~QS_TIMER
;
331 pLE
= pTmr
->ptmrList
.Flink
;
332 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
333 } while (pTmr
!= FirstpTmr
);
344 LARGE_INTEGER TickCount
, DueTime
;
347 PTIMER pTmr
= FirstpTmr
;
352 UserEnterExclusive();
354 KeQueryTickCount(&TickCount
);
355 Time
= MsqCalculateMessageTime(&TickCount
);
357 DueTime
.QuadPart
= (LONGLONG
)(-1000000);
362 if (pTmr
->flags
& TMRF_WAITING
)
364 pLE
= pTmr
->ptmrList
.Flink
;
365 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
369 if (pTmr
->flags
& TMRF_INIT
)
371 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
375 if (pTmr
->cmsCountdown
< 0)
378 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
380 if (pTmr
->flags
& TMRF_ONESHOT
)
381 pTmr
->flags
|= TMRF_WAITING
;
383 if (pTmr
->flags
& TMRF_RIT
)
385 // Hard coded call here, inside raw input thread.
386 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
390 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
391 // Set thread message queue for this timer.
392 if (pTmr
->pti
->MessageQueue
)
394 ASSERT(pTmr
->pti
->MessageQueue
->NewMessages
!= NULL
);
395 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
399 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
402 pTmr
->cmsCountdown
-= Time
- TimeLast
;
405 pLE
= pTmr
->ptmrList
.Flink
;
406 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
407 } while (pTmr
!= FirstpTmr
);
409 // Restart the timer thread!
410 ASSERT(MasterTimer
!= NULL
);
411 KeSetTimer(MasterTimer
, DueTime
, NULL
);
416 DPRINT("TimerCount = %d\n", TimerCount
);
421 // Old Timer Queueing
425 InternalSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
427 PWINDOW_OBJECT Window
;
430 PUSER_MESSAGE_QUEUE MessageQueue
;
432 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
433 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
435 if ((Wnd
== NULL
) && ! SystemTimer
)
437 DPRINT("Window-less timer\n");
438 /* find a free, window-less timer id */
439 IntLockWindowlessTimerBitmap();
440 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
442 if (IDEvent
== (UINT_PTR
) -1)
444 IntUnlockWindowlessTimerBitmap();
445 DPRINT1("Unable to find a free window-less timer id\n");
446 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
450 HintIndex
= ++IDEvent
;
451 IntUnlockWindowlessTimerBitmap();
453 pti
= PsGetCurrentThreadWin32Thread();
454 MessageQueue
= pti
->MessageQueue
;
458 if (!(Window
= UserGetWindowObject(Wnd
)))
460 DPRINT1("Invalid window handle\n");
464 if (Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
466 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
467 SetLastWin32Error(ERROR_ACCESS_DENIED
);
472 MessageQueue
= Window
->pti
->MessageQueue
;
477 /* Windows NT/2k/XP behaviour */
478 if (Elapse
> 0x7fffffff)
480 DPRINT("Adjusting uElapse\n");
486 /* Windows XP SP2 and Windows Server 2003 behaviour */
487 if (Elapse
> 0x7fffffff)
489 DPRINT("Adjusting uElapse\n");
495 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
498 DPRINT("Adjusting uElapse\n");
502 if (! MsqSetTimer(MessageQueue
, Wnd
,
503 IDEvent
, Elapse
, TimerFunc
,
504 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
506 DPRINT1("Failed to set timer in message queue\n");
507 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
511 if (Ret
== 0) ASSERT(FALSE
);
516 DestroyTimersForWindow(PTHREADINFO pti
, PWINDOW_OBJECT Window
)
519 PTIMER pTmr
= FirstpTmr
;
520 BOOL TimersRemoved
= FALSE
;
522 if ((FirstpTmr
== NULL
) || (Window
== NULL
))
525 UserEnterExclusive();
529 if ((pTmr
) && (pTmr
->pti
== pti
) && (pTmr
->pWnd
== Window
))
531 RemoveEntryList(&pTmr
->ptmrList
);
532 UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
533 TimersRemoved
= TRUE
;
535 pLE
= pTmr
->ptmrList
.Flink
;
536 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
537 } while (pTmr
!= FirstpTmr
);
541 return TimersRemoved
;
545 DestroyTimersForThread(PTHREADINFO pti
)
548 PTIMER pTmr
= FirstpTmr
;
549 BOOL TimersRemoved
= FALSE
;
551 if (FirstpTmr
== NULL
)
554 UserEnterExclusive();
558 if ((pTmr
) && (pTmr
->pti
== pti
))
560 RemoveEntryList(&pTmr
->ptmrList
);
561 UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
562 TimersRemoved
= TRUE
;
564 pLE
= pTmr
->ptmrList
.Flink
;
565 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
566 } while (pTmr
!= FirstpTmr
);
570 return TimersRemoved
;
574 IntKillTimer(PWINDOW_OBJECT Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
577 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
578 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
580 if ((Window
) && (IDEvent
== 0))
583 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0, TRUE
);
584 return pTmr
? TRUE
: FALSE
;
593 InternalKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
596 PWINDOW_OBJECT Window
= NULL
;
598 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
599 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
601 pti
= PsGetCurrentThreadWin32Thread();
604 Window
= UserGetWindowObject(Wnd
);
606 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
607 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
609 // Give it another chance to find the timer.
610 if (Window
&& !( MsqKillTimer(Window
->pti
->MessageQueue
, Wnd
,
611 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
613 DPRINT1("Unable to locate timer in message queue for Window.\n");
614 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
620 /* window-less timer? */
621 if ((Wnd
== NULL
) && ! SystemTimer
)
623 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
624 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
626 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
627 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
632 IntLockWindowlessTimerBitmap();
634 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
635 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
637 HintIndex
= IDEvent
- 1;
639 IntUnlockWindowlessTimerBitmap();
650 ExInitializeFastMutex(&Mutex
);
652 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
653 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
654 if (WindowLessTimersBitMapBuffer
== NULL
)
656 return STATUS_UNSUCCESSFUL
;
659 RtlInitializeBitMap(&WindowLessTimersBitMap
,
660 WindowLessTimersBitMapBuffer
,
663 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
664 RtlClearAllBits(&WindowLessTimersBitMap
);
666 return STATUS_SUCCESS
;
676 TIMERPROC lpTimerFunc
679 DECLARE_RETURN(UINT_PTR
);
681 DPRINT("Enter NtUserSetTimer\n");
682 UserEnterExclusive();
684 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, TMRF_TIFROMWND
));
687 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
701 PWINDOW_OBJECT Window
;
702 DECLARE_RETURN(BOOL
);
704 DPRINT("Enter NtUserKillTimer\n");
705 UserEnterExclusive();
707 Window
= UserGetWindowObject(hWnd
);
709 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
712 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
720 NtUserSetSystemTimer(
724 TIMERPROC lpTimerFunc
727 DECLARE_RETURN(UINT_PTR
);
729 DPRINT("Enter NtUserSetSystemTimer\n");
730 UserEnterExclusive();
732 // This is wrong, lpTimerFunc is NULL!
733 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
));
736 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);