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 #define MAX_ELAPSE_TIME 0x7FFFFFFF
21 /* Windows 2000 has room for 32768 window-less timers */
22 #define NUM_WINDOW_LESS_TIMERS 32768
24 static PFAST_MUTEX Mutex
;
25 static RTL_BITMAP WindowLessTimersBitMap
;
26 static PVOID WindowLessTimersBitMapBuffer
;
27 static ULONG HintIndex
= 1;
31 #define IntLockWindowlessTimerBitmap() \
32 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex)
34 #define IntUnlockWindowlessTimerBitmap() \
35 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex)
37 #define TimerEnterExclusive() \
39 KeEnterCriticalRegion(); \
40 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
43 #define TimerLeave() \
45 ExReleaseResourceLite(&TimerLock); \
46 KeLeaveCriticalRegion(); \
50 /* FUNCTIONS *****************************************************************/
59 Ret
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otTimer
, sizeof(TIMER
));
63 InsertTailList(&TimersListHead
, &Ret
->ptmrList
);
72 RemoveTimer(PTIMER pTmr
)
77 /* Set the flag, it will be removed when ready */
78 RemoveEntryList(&pTmr
->ptmrList
);
79 if ((pTmr
->pWnd
== NULL
) && (!(pTmr
->flags
& TMRF_SYSTEM
))) // System timers are reusable.
83 IDEvent
= NUM_WINDOW_LESS_TIMERS
- pTmr
->nID
;
84 IntLockWindowlessTimerBitmap();
85 RtlClearBit(&WindowLessTimersBitMap
, IDEvent
);
86 IntUnlockWindowlessTimerBitmap();
88 UserDereferenceObject(pTmr
);
89 Ret
= UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
91 if (!Ret
) ERR("Warning: Unable to delete timer\n");
98 FindTimer(PWND Window
,
103 PTIMER pTmr
, RetTmr
= NULL
;
105 TimerEnterExclusive();
106 pLE
= TimersListHead
.Flink
;
107 while (pLE
!= &TimersListHead
)
109 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
111 if ( pTmr
->nID
== nID
&&
112 pTmr
->pWnd
== Window
&&
113 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
128 FindSystemTimer(PMSG pMsg
)
133 TimerEnterExclusive();
134 pLE
= TimersListHead
.Flink
;
135 while (pLE
!= &TimersListHead
)
137 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
139 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
140 (pTmr
->flags
& TMRF_SYSTEM
) )
152 ValidateTimerCallback(PTHREADINFO pti
,
159 TimerEnterExclusive();
160 pLE
= TimersListHead
.Flink
;
161 while (pLE
!= &TimersListHead
)
163 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
164 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
165 !(pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
166 (pTmr
->pti
->ppi
== pti
->ppi
) )
179 IntSetTimer( PWND Window
,
187 LARGE_INTEGER DueTime
;
188 DueTime
.QuadPart
= (LONGLONG
)(-97656); // 1024hz .9765625 ms set to 10.0 ms
191 /* Windows NT/2k/XP behaviour */
192 if (Elapse
> MAX_ELAPSE_TIME
)
194 TRACE("Adjusting uElapse\n");
198 /* Windows XP SP2 and Windows Server 2003 behaviour */
199 if (Elapse
> MAX_ELAPSE_TIME
)
201 TRACE("Adjusting uElapse\n");
202 Elapse
= MAX_ELAPSE_TIME
;
206 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
209 TRACE("Adjusting uElapse\n");
210 Elapse
= 10; // 1024hz .9765625 ms, set to 10.0 ms (+/-)1 ms
213 /* Passing an IDEvent of 0 and the SetTimer returns 1.
214 It will create the timer with an ID of 0 */
215 if ((Window
) && (IDEvent
== 0))
218 pTmr
= FindTimer(Window
, IDEvent
, Type
);
220 if ((!pTmr
) && (Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
222 IntLockWindowlessTimerBitmap();
224 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
226 if (IDEvent
== (UINT_PTR
) -1)
228 IntUnlockWindowlessTimerBitmap();
229 ERR("Unable to find a free window-less timer id\n");
230 EngSetLastError(ERROR_NO_SYSTEM_RESOURCES
);
235 IDEvent
= NUM_WINDOW_LESS_TIMERS
- IDEvent
;
238 IntUnlockWindowlessTimerBitmap();
243 pTmr
= CreateTimer();
246 if (Window
&& (Type
& TMRF_TIFROMWND
))
247 pTmr
->pti
= Window
->head
.pti
->pEThread
->Tcb
.Win32Thread
;
251 pTmr
->pti
= ptiRawInput
;
253 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
257 pTmr
->cmsCountdown
= Elapse
;
258 pTmr
->cmsRate
= Elapse
;
259 pTmr
->pfn
= TimerFunc
;
261 pTmr
->flags
= Type
|TMRF_INIT
;
265 pTmr
->cmsCountdown
= Elapse
;
266 pTmr
->cmsRate
= Elapse
;
269 ASSERT(MasterTimer
!= NULL
);
270 // Start the timer thread!
271 if (TimersListHead
.Flink
== TimersListHead
.Blink
) // There is only one timer
272 KeSetTimer(MasterTimer
, DueTime
, NULL
);
278 // Process win32k system timers.
282 SystemTimerProc(HWND hwnd
,
292 pWnd
= UserGetWindowObject(hwnd
);
295 ERR( "System Timer Proc has invalid window handle! 0x%x Id: %d\n", hwnd
, idEvent
);
301 TRACE( "Windowless Timer Running!\n" );
308 Used in NtUserTrackMouseEvent.
310 case ID_EVENT_SYSTIMER_MOUSEHOVER
:
316 pDesk
= pWnd
->head
.rpdesk
;
317 if ( pDesk
->dwDTFlags
& DF_TME_HOVER
&&
318 pWnd
== pDesk
->spwndTrack
)
320 Point
= pWnd
->head
.pti
->MessageQueue
->MouseMoveMsg
.pt
;
321 if ( RECTL_bPointInRect(&pDesk
->rcMouseHover
, Point
.x
, Point
.y
) )
323 if (pDesk
->htEx
== HTCLIENT
) // In a client area.
325 wParam
= UserGetMouseButtonsState();
328 if (pWnd
->ExStyle
& WS_EX_LAYOUTRTL
)
330 Point
.x
= pWnd
->rcClient
.right
- Point
.x
- 1;
333 Point
.x
-= pWnd
->rcClient
.left
;
334 Point
.y
-= pWnd
->rcClient
.top
;
338 wParam
= pDesk
->htEx
; // Need to support all HTXYZ hits.
339 Msg
= WM_NCMOUSEHOVER
;
341 TRACE("Generating WM_NCMOUSEHOVER\n");
342 UserPostMessage(hwnd
, Msg
, wParam
, MAKELPARAM(Point
.x
, Point
.y
));
343 pDesk
->dwDTFlags
&= ~DF_TME_HOVER
;
344 break; // Kill this timer.
348 return; // Not this window so just return.
351 ERR( "System Timer Proc invalid id %d!\n", idEvent
);
354 IntKillTimer(pWnd
, idEvent
, TRUE
);
361 // Need to start gdi syncro timers then start timer with Hang App proc
362 // that calles Idle process so the screen savers will know to run......
363 IntSetTimer(NULL
, 0, 1000, HungAppSysTimerProc
, TMRF_RIT
);
365 // IntSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
370 SystemTimerSet( PWND Window
,
373 TIMERPROC lpTimerFunc
)
375 if (Window
&& Window
->head
.pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
377 EngSetLastError(ERROR_ACCESS_DENIED
);
378 TRACE("SysemTimerSet: Access Denied!\n");
381 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
386 PostTimerMessages(PWND Window
)
389 PUSER_MESSAGE_QUEUE ThreadQueue
;
395 pti
= PsGetCurrentThreadWin32Thread();
396 ThreadQueue
= pti
->MessageQueue
;
398 TimerEnterExclusive();
399 pLE
= TimersListHead
.Flink
;
400 while(pLE
!= &TimersListHead
)
402 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
403 if ( (pTmr
->flags
& TMRF_READY
) &&
404 (pTmr
->pti
== pti
) &&
405 ((pTmr
->pWnd
== Window
) || (Window
== NULL
)) )
407 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->head
.h
: 0;
408 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
409 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
410 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
412 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_TIMER
);
413 pTmr
->flags
&= ~TMRF_READY
;
416 // Now move this entry to the end of the list so it will not be
417 // called again in the next msg loop.
418 if (pLE
!= &TimersListHead
)
420 RemoveEntryList(&pTmr
->ptmrList
);
421 InsertTailList(&TimersListHead
, &pTmr
->ptmrList
);
438 LARGE_INTEGER TickCount
, DueTime
;
444 TimerEnterExclusive();
445 pLE
= TimersListHead
.Flink
;
446 KeQueryTickCount(&TickCount
);
447 Time
= MsqCalculateMessageTime(&TickCount
);
449 DueTime
.QuadPart
= (LONGLONG
)(-97656); // 1024hz .9765625 ms set to 10.0 ms
451 while(pLE
!= &TimersListHead
)
453 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
455 if (pTmr
->flags
& TMRF_WAITING
)
457 pLE
= pTmr
->ptmrList
.Flink
;
458 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
462 if (pTmr
->flags
& TMRF_INIT
)
464 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
468 if (pTmr
->cmsCountdown
< 0)
471 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
473 if (pTmr
->flags
& TMRF_ONESHOT
)
474 pTmr
->flags
|= TMRF_WAITING
;
476 if (pTmr
->flags
& TMRF_RIT
)
478 // Hard coded call here, inside raw input thread.
479 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
483 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
484 // Set thread message queue for this timer.
485 if (pTmr
->pti
->MessageQueue
)
487 ASSERT(pTmr
->pti
->MessageQueue
->NewMessages
!= NULL
);
488 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
492 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
495 pTmr
->cmsCountdown
-= Time
- TimeLast
;
501 // Restart the timer thread!
502 ASSERT(MasterTimer
!= NULL
);
503 KeSetTimer(MasterTimer
, DueTime
, NULL
);
508 TRACE("TimerCount = %d\n", TimerCount
);
512 DestroyTimersForWindow(PTHREADINFO pti
, PWND Window
)
516 BOOL TimersRemoved
= FALSE
;
518 if ((Window
== NULL
))
521 TimerEnterExclusive();
522 pLE
= TimersListHead
.Flink
;
523 while(pLE
!= &TimersListHead
)
525 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
526 pLE
= pLE
->Flink
; /* get next timer list entry before current timer is removed */
527 if ((pTmr
) && (pTmr
->pti
== pti
) && (pTmr
->pWnd
== Window
))
529 TimersRemoved
= RemoveTimer(pTmr
);
535 return TimersRemoved
;
539 DestroyTimersForThread(PTHREADINFO pti
)
541 PLIST_ENTRY pLE
= TimersListHead
.Flink
;
543 BOOL TimersRemoved
= FALSE
;
545 TimerEnterExclusive();
547 while(pLE
!= &TimersListHead
)
549 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
550 pLE
= pLE
->Flink
; /* get next timer list entry before current timer is removed */
551 if ((pTmr
) && (pTmr
->pti
== pti
))
553 TimersRemoved
= RemoveTimer(pTmr
);
559 return TimersRemoved
;
563 IntKillTimer(PWND Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
566 TRACE("IntKillTimer Window %x id %p systemtimer %s\n",
567 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
569 TimerEnterExclusive();
570 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0);
578 return pTmr
? TRUE
: FALSE
;
588 /* Allocate FAST_MUTEX from non paged pool */
589 Mutex
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FAST_MUTEX
), TAG_INTERNAL_SYNC
);
590 ExInitializeFastMutex(Mutex
);
592 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
593 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(NonPagedPool
, BitmapBytes
, TAG_TIMERBMP
);
594 if (WindowLessTimersBitMapBuffer
== NULL
)
596 return STATUS_UNSUCCESSFUL
;
599 RtlInitializeBitMap(&WindowLessTimersBitMap
,
600 WindowLessTimersBitMapBuffer
,
603 /* Yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
604 RtlClearAllBits(&WindowLessTimersBitMap
);
606 ExInitializeResourceLite(&TimerLock
);
607 InitializeListHead(&TimersListHead
);
609 return STATUS_SUCCESS
;
619 TIMERPROC lpTimerFunc
623 DECLARE_RETURN(UINT_PTR
);
625 TRACE("Enter NtUserSetTimer\n");
626 UserEnterExclusive();
627 Window
= UserGetWindowObject(hWnd
);
630 RETURN(IntSetTimer(Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_TIFROMWND
));
633 TRACE("Leave NtUserSetTimer, ret=%i\n", _ret_
);
648 DECLARE_RETURN(BOOL
);
650 TRACE("Enter NtUserKillTimer\n");
651 UserEnterExclusive();
652 Window
= UserGetWindowObject(hWnd
);
655 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
658 TRACE("Leave NtUserKillTimer, ret=%i\n", _ret_
);
665 NtUserSetSystemTimer(
669 TIMERPROC lpTimerFunc
672 DECLARE_RETURN(UINT_PTR
);
674 TRACE("Enter NtUserSetSystemTimer\n");
676 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, NULL
, TMRF_SYSTEM
));
679 TRACE("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);
685 NtUserValidateTimerCallback(
694 Ret
= ValidateTimerCallback(PsGetCurrentThreadWin32Thread(), lParam
);