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 * Michael Martin (michael.martin@reactos.org)
12 DBG_DEFAULT_CHANNEL(UserTimer
);
14 /* GLOBALS *******************************************************************/
16 static LIST_ENTRY TimersListHead
;
17 static LONG TimeLast
= 0;
19 /* Windows 2000 has room for 32768 window-less timers */
20 #define NUM_WINDOW_LESS_TIMERS 32768
22 static PFAST_MUTEX Mutex
;
23 static RTL_BITMAP WindowLessTimersBitMap
;
24 static PVOID WindowLessTimersBitMapBuffer
;
25 static ULONG HintIndex
= 1;
29 #define IntLockWindowlessTimerBitmap() \
30 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex)
32 #define IntUnlockWindowlessTimerBitmap() \
33 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex)
35 #define TimerEnterExclusive() \
37 KeEnterCriticalRegion(); \
38 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
41 #define TimerLeave() \
43 ExReleaseResourceLite(&TimerLock); \
44 KeLeaveCriticalRegion(); \
48 /* FUNCTIONS *****************************************************************/
57 Ret
= UserCreateObject(gHandleTable
, NULL
, NULL
, &Handle
, TYPE_TIMER
, sizeof(TIMER
));
61 InsertTailList(&TimersListHead
, &Ret
->ptmrList
);
70 RemoveTimer(PTIMER pTmr
)
75 /* Set the flag, it will be removed when ready */
76 RemoveEntryList(&pTmr
->ptmrList
);
77 if ((pTmr
->pWnd
== NULL
) && (!(pTmr
->flags
& TMRF_SYSTEM
))) // System timers are reusable.
81 IDEvent
= NUM_WINDOW_LESS_TIMERS
- pTmr
->nID
;
82 IntLockWindowlessTimerBitmap();
83 RtlClearBit(&WindowLessTimersBitMap
, IDEvent
);
84 IntUnlockWindowlessTimerBitmap();
86 UserDereferenceObject(pTmr
);
87 Ret
= UserDeleteObject( UserHMGetHandle(pTmr
), TYPE_TIMER
);
89 if (!Ret
) ERR("Warning: Unable to delete timer\n");
96 FindTimer(PWND Window
,
101 PTIMER pTmr
, RetTmr
= NULL
;
103 TimerEnterExclusive();
104 pLE
= TimersListHead
.Flink
;
105 while (pLE
!= &TimersListHead
)
107 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
109 if ( pTmr
->nID
== nID
&&
110 pTmr
->pWnd
== Window
&&
111 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
126 FindSystemTimer(PMSG pMsg
)
131 TimerEnterExclusive();
132 pLE
= TimersListHead
.Flink
;
133 while (pLE
!= &TimersListHead
)
135 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
137 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
138 (pTmr
->flags
& TMRF_SYSTEM
) )
150 ValidateTimerCallback(PTHREADINFO pti
,
157 TimerEnterExclusive();
158 pLE
= TimersListHead
.Flink
;
159 while (pLE
!= &TimersListHead
)
161 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
162 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
163 !(pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
164 (pTmr
->pti
->ppi
== pti
->ppi
) )
177 IntSetTimer( PWND Window
,
185 LARGE_INTEGER DueTime
;
186 DueTime
.QuadPart
= (LONGLONG
)(-97656); // 1024hz .9765625 ms set to 10.0 ms
189 /* Windows NT/2k/XP behaviour */
190 if (Elapse
> USER_TIMER_MAXIMUM
)
192 TRACE("Adjusting uElapse\n");
196 /* Windows XP SP2 and Windows Server 2003 behaviour */
197 if (Elapse
> USER_TIMER_MAXIMUM
)
199 TRACE("Adjusting uElapse\n");
200 Elapse
= USER_TIMER_MAXIMUM
;
204 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
205 if (Elapse
< USER_TIMER_MINIMUM
)
207 TRACE("Adjusting uElapse\n");
208 Elapse
= USER_TIMER_MINIMUM
; // 1024hz .9765625 ms, set to 10.0 ms (+/-)1 ms
211 /* Passing an IDEvent of 0 and the SetTimer returns 1.
212 It will create the timer with an ID of 0 */
213 if ((Window
) && (IDEvent
== 0))
216 pTmr
= FindTimer(Window
, IDEvent
, Type
);
218 if ((!pTmr
) && (Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
220 IntLockWindowlessTimerBitmap();
222 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
224 if (IDEvent
== (UINT_PTR
) -1)
226 IntUnlockWindowlessTimerBitmap();
227 ERR("Unable to find a free window-less timer id\n");
228 EngSetLastError(ERROR_NO_SYSTEM_RESOURCES
);
233 IDEvent
= NUM_WINDOW_LESS_TIMERS
- IDEvent
;
236 IntUnlockWindowlessTimerBitmap();
241 pTmr
= CreateTimer();
244 if (Window
&& (Type
& TMRF_TIFROMWND
))
245 pTmr
->pti
= Window
->head
.pti
->pEThread
->Tcb
.Win32Thread
;
249 pTmr
->pti
= ptiRawInput
;
251 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
255 pTmr
->cmsCountdown
= Elapse
;
256 pTmr
->cmsRate
= Elapse
;
257 pTmr
->pfn
= TimerFunc
;
259 pTmr
->flags
= Type
|TMRF_INIT
;
263 pTmr
->cmsCountdown
= Elapse
;
264 pTmr
->cmsRate
= Elapse
;
267 ASSERT(MasterTimer
!= NULL
);
268 // Start the timer thread!
269 if (TimersListHead
.Flink
== TimersListHead
.Blink
) // There is only one timer
270 KeSetTimer(MasterTimer
, DueTime
, NULL
);
276 // Process win32k system timers.
280 SystemTimerProc(HWND hwnd
,
290 pWnd
= UserGetWindowObject(hwnd
);
293 ERR("System Timer Proc has invalid window handle! %p Id: %u\n", hwnd
, idEvent
);
299 TRACE( "Windowless Timer Running!\n" );
306 Used in NtUserTrackMouseEvent.
308 case ID_EVENT_SYSTIMER_MOUSEHOVER
:
314 pDesk
= pWnd
->head
.rpdesk
;
315 if ( pDesk
->dwDTFlags
& DF_TME_HOVER
&&
316 pWnd
== pDesk
->spwndTrack
)
318 Point
= pWnd
->head
.pti
->MessageQueue
->MouseMoveMsg
.pt
;
319 if ( RECTL_bPointInRect(&pDesk
->rcMouseHover
, Point
.x
, Point
.y
) )
321 if (pDesk
->htEx
== HTCLIENT
) // In a client area.
323 wParam
= UserGetMouseButtonsState();
326 if (pWnd
->ExStyle
& WS_EX_LAYOUTRTL
)
328 Point
.x
= pWnd
->rcClient
.right
- Point
.x
- 1;
331 Point
.x
-= pWnd
->rcClient
.left
;
332 Point
.y
-= pWnd
->rcClient
.top
;
336 wParam
= pDesk
->htEx
; // Need to support all HTXYZ hits.
337 Msg
= WM_NCMOUSEHOVER
;
339 TRACE("Generating WM_NCMOUSEHOVER\n");
340 UserPostMessage(hwnd
, Msg
, wParam
, MAKELPARAM(Point
.x
, Point
.y
));
341 pDesk
->dwDTFlags
&= ~DF_TME_HOVER
;
342 break; // Kill this timer.
346 return; // Not this window so just return.
349 ERR("System Timer Proc invalid id %u!\n", idEvent
);
352 IntKillTimer(pWnd
, idEvent
, TRUE
);
359 // Need to start gdi syncro timers then start timer with Hang App proc
360 // that calles Idle process so the screen savers will know to run......
361 IntSetTimer(NULL
, 0, 1000, HungAppSysTimerProc
, TMRF_RIT
);
363 // IntSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
368 SystemTimerSet( PWND Window
,
371 TIMERPROC lpTimerFunc
)
373 if (Window
&& Window
->head
.pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
375 EngSetLastError(ERROR_ACCESS_DENIED
);
376 TRACE("SysemTimerSet: Access Denied!\n");
379 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
384 PostTimerMessages(PWND Window
)
392 pti
= PsGetCurrentThreadWin32Thread();
394 TimerEnterExclusive();
395 pLE
= TimersListHead
.Flink
;
396 while(pLE
!= &TimersListHead
)
398 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
399 if ( (pTmr
->flags
& TMRF_READY
) &&
400 (pTmr
->pti
== pti
) &&
401 ((pTmr
->pWnd
== Window
) || (Window
== NULL
)) )
403 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->head
.h
: 0;
404 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
405 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
406 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
408 MsqPostMessage(pti
, &Msg
, FALSE
, (QS_POSTMESSAGE
|QS_ALLPOSTMESSAGE
), 0);
409 pTmr
->flags
&= ~TMRF_READY
;
410 ClearMsgBitsMask(pti
, QS_TIMER
);
412 // Now move this entry to the end of the list so it will not be
413 // called again in the next msg loop.
414 if (pLE
!= &TimersListHead
)
416 RemoveEntryList(&pTmr
->ptmrList
);
417 InsertTailList(&TimersListHead
, &pTmr
->ptmrList
);
434 LARGE_INTEGER TickCount
, DueTime
;
440 TimerEnterExclusive();
441 pLE
= TimersListHead
.Flink
;
442 KeQueryTickCount(&TickCount
);
443 Time
= MsqCalculateMessageTime(&TickCount
);
445 DueTime
.QuadPart
= (LONGLONG
)(-97656); // 1024hz .9765625 ms set to 10.0 ms
447 while(pLE
!= &TimersListHead
)
449 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
451 if (pTmr
->flags
& TMRF_WAITING
)
453 pLE
= pTmr
->ptmrList
.Flink
;
454 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
458 if (pTmr
->flags
& TMRF_INIT
)
460 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
464 if (pTmr
->cmsCountdown
< 0)
467 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
469 if (pTmr
->flags
& TMRF_ONESHOT
)
470 pTmr
->flags
|= TMRF_WAITING
;
472 if (pTmr
->flags
& TMRF_RIT
)
474 // Hard coded call here, inside raw input thread.
475 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
479 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
480 // Set thread message queue for this timer.
483 pTmr
->pti
->cTimersReady
++;
484 ASSERT(pTmr
->pti
->pEventQueueServer
!= NULL
);
485 MsqWakeQueue(pTmr
->pti
, QS_TIMER
, TRUE
);
489 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
492 pTmr
->cmsCountdown
-= Time
- TimeLast
;
498 // Restart the timer thread!
499 ASSERT(MasterTimer
!= NULL
);
500 KeSetTimer(MasterTimer
, DueTime
, NULL
);
505 TRACE("TimerCount = %d\n", TimerCount
);
509 DestroyTimersForWindow(PTHREADINFO pti
, PWND Window
)
513 BOOL TimersRemoved
= FALSE
;
515 if ((Window
== NULL
))
518 TimerEnterExclusive();
519 pLE
= TimersListHead
.Flink
;
520 while(pLE
!= &TimersListHead
)
522 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
523 pLE
= pLE
->Flink
; /* get next timer list entry before current timer is removed */
524 if ((pTmr
) && (pTmr
->pti
== pti
) && (pTmr
->pWnd
== Window
))
526 TimersRemoved
= RemoveTimer(pTmr
);
532 return TimersRemoved
;
536 DestroyTimersForThread(PTHREADINFO pti
)
538 PLIST_ENTRY pLE
= TimersListHead
.Flink
;
540 BOOL TimersRemoved
= FALSE
;
542 TimerEnterExclusive();
544 while(pLE
!= &TimersListHead
)
546 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
547 pLE
= pLE
->Flink
; /* get next timer list entry before current timer is removed */
548 if ((pTmr
) && (pTmr
->pti
== pti
))
550 TimersRemoved
= RemoveTimer(pTmr
);
556 return TimersRemoved
;
560 IntKillTimer(PWND Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
563 TRACE("IntKillTimer Window %p id %p systemtimer %s\n",
564 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
566 TimerEnterExclusive();
567 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0);
575 return pTmr
? TRUE
: FALSE
;
585 /* Allocate FAST_MUTEX from non paged pool */
586 Mutex
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FAST_MUTEX
), TAG_INTERNAL_SYNC
);
589 return STATUS_INSUFFICIENT_RESOURCES
;
592 ExInitializeFastMutex(Mutex
);
594 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
595 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(NonPagedPool
, BitmapBytes
, TAG_TIMERBMP
);
596 if (WindowLessTimersBitMapBuffer
== NULL
)
598 return STATUS_UNSUCCESSFUL
;
601 RtlInitializeBitMap(&WindowLessTimersBitMap
,
602 WindowLessTimersBitMapBuffer
,
605 /* Yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
606 RtlClearAllBits(&WindowLessTimersBitMap
);
608 ExInitializeResourceLite(&TimerLock
);
609 InitializeListHead(&TimersListHead
);
611 return STATUS_SUCCESS
;
621 TIMERPROC lpTimerFunc
625 DECLARE_RETURN(UINT_PTR
);
627 TRACE("Enter NtUserSetTimer\n");
628 UserEnterExclusive();
629 if (hWnd
) Window
= UserGetWindowObject(hWnd
);
632 RETURN(IntSetTimer(Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_TIFROMWND
));
635 TRACE("Leave NtUserSetTimer, ret=%u\n", _ret_
);
650 DECLARE_RETURN(BOOL
);
652 TRACE("Enter NtUserKillTimer\n");
653 UserEnterExclusive();
654 if (hWnd
) Window
= UserGetWindowObject(hWnd
);
657 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
660 TRACE("Leave NtUserKillTimer, ret=%i\n", _ret_
);
667 NtUserSetSystemTimer(
671 TIMERPROC lpTimerFunc
674 DECLARE_RETURN(UINT_PTR
);
676 TRACE("Enter NtUserSetSystemTimer\n");
678 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, NULL
, TMRF_SYSTEM
));
681 TRACE("Leave NtUserSetSystemTimer, ret=%u\n", _ret_
);
687 NtUserValidateTimerCallback(
696 Ret
= ValidateTimerCallback(PsGetCurrentThreadWin32Thread(), lParam
);