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)
9 * REVISION HISTORY: 10/04/2003 Implemented System Timers
13 /* INCLUDES ******************************************************************/
20 /* GLOBALS *******************************************************************/
22 static PTIMER FirstpTmr
= NULL
;
23 static LONG TimeLast
= 0;
25 #define MAX_ELAPSE_TIME 0x7FFFFFFF
27 /* Windows 2000 has room for 32768 window-less timers */
28 #define NUM_WINDOW_LESS_TIMERS 32768
30 static FAST_MUTEX Mutex
;
31 static RTL_BITMAP WindowLessTimersBitMap
;
32 static PVOID WindowLessTimersBitMapBuffer
;
33 static ULONG HintIndex
= 0;
37 #define IntLockWindowlessTimerBitmap() \
38 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
40 #define IntUnlockWindowlessTimerBitmap() \
41 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
43 #define TimerEnterExclusive() \
45 KeEnterCriticalRegion(); \
46 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
49 #define TimerLeave() \
51 ExReleaseResourceLite(&TimerLock); \
52 KeLeaveCriticalRegion(); \
56 /* FUNCTIONS *****************************************************************/
67 ExInitializeResourceLite(&TimerLock
);
68 FirstpTmr
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otTimer
, sizeof(TIMER
));
71 FirstpTmr
->head
.h
= Handle
;
72 InitializeListHead(&FirstpTmr
->ptmrList
);
78 Ret
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otTimer
, sizeof(TIMER
));
82 InsertTailList(&FirstpTmr
->ptmrList
, &Ret
->ptmrList
);
91 RemoveTimer(PTIMER pTmr
)
96 /* Set the flag, it will be removed when ready */
97 RemoveEntryList(&pTmr
->ptmrList
);
98 if ((pTmr
->pWnd
== NULL
) && (!(pTmr
->flags
& TMRF_SYSTEM
)))
100 DPRINT("Clearing Bit %d)\n", pTmr
->nID
);
101 IntLockWindowlessTimerBitmap();
102 RtlClearBit(&WindowLessTimersBitMap
, pTmr
->nID
);
103 IntUnlockWindowlessTimerBitmap();
105 UserDereferenceObject(pTmr
);
106 Ret
= UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
108 if (!Ret
) DPRINT1("Warning: Unable to delete timer\n");
115 FindTimer(PWINDOW_OBJECT Window
,
120 PTIMER pTmr
= FirstpTmr
, RetTmr
= NULL
;
121 TimerEnterExclusive();
126 if ( pTmr
->nID
== nID
&&
127 pTmr
->pWnd
== Window
&&
128 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
134 pLE
= pTmr
->ptmrList
.Flink
;
135 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
136 } while (pTmr
!= FirstpTmr
);
144 FindSystemTimer(PMSG pMsg
)
147 PTIMER pTmr
= FirstpTmr
;
148 TimerEnterExclusive();
153 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
154 (pTmr
->flags
& TMRF_SYSTEM
) )
157 pLE
= pTmr
->ptmrList
.Flink
;
158 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
159 } while (pTmr
!= FirstpTmr
);
167 ValidateTimerCallback(PTHREADINFO pti
,
168 PWINDOW_OBJECT Window
,
173 PTIMER pTmr
= FirstpTmr
;
175 if (!pTmr
) return FALSE
;
177 TimerEnterExclusive();
180 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
181 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
182 (pTmr
->pti
->ppi
== pti
->ppi
) )
185 pLE
= pTmr
->ptmrList
.Flink
;
186 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
187 } while (pTmr
!= FirstpTmr
);
190 if (!pTmr
) return FALSE
;
196 IntSetTimer( PWINDOW_OBJECT Window
,
204 LARGE_INTEGER DueTime
;
205 DueTime
.QuadPart
= (LONGLONG
)(-5000000);
208 /* Windows NT/2k/XP behaviour */
209 if (Elapse
> MAX_ELAPSE_TIME
)
211 DPRINT("Adjusting uElapse\n");
215 /* Windows XP SP2 and Windows Server 2003 behaviour */
216 if (Elapse
> MAX_ELAPSE_TIME
)
218 DPRINT("Adjusting uElapse\n");
219 Elapse
= MAX_ELAPSE_TIME
;
223 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
226 DPRINT("Adjusting uElapse\n");
230 if ((Window
) && (IDEvent
== 0))
233 pTmr
= FindTimer(Window
, IDEvent
, Type
);
235 if ((!pTmr
) && (Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
237 IntLockWindowlessTimerBitmap();
239 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
241 if (IDEvent
== (UINT_PTR
) -1)
243 IntUnlockWindowlessTimerBitmap();
244 DPRINT1("Unable to find a free window-less timer id\n");
245 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
250 //HintIndex = IDEvent + 1;
251 IntUnlockWindowlessTimerBitmap();
256 pTmr
= CreateTimer();
259 if (Window
&& (Type
& TMRF_TIFROMWND
))
260 pTmr
->pti
= Window
->pti
->pEThread
->Tcb
.Win32Thread
;
264 pTmr
->pti
= ptiRawInput
;
266 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
270 pTmr
->cmsCountdown
= Elapse
;
271 pTmr
->cmsRate
= Elapse
;
272 pTmr
->pfn
= TimerFunc
;
274 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
278 pTmr
->cmsCountdown
= Elapse
;
279 pTmr
->cmsRate
= Elapse
;
282 ASSERT(MasterTimer
!= NULL
);
283 // Start the timer thread!
284 if (pTmr
== FirstpTmr
)
285 KeSetTimer(MasterTimer
, DueTime
, NULL
);
291 // Process win32k system timers.
295 SystemTimerProc(HWND hwnd
,
300 DPRINT( "Timer Running!\n" );
307 // Need to start gdi syncro timers then start timer with Hang App proc
308 // that calles Idle process so the screen savers will know to run......
309 IntSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
314 SystemTimerSet( PWINDOW_OBJECT Window
,
317 TIMERPROC lpTimerFunc
)
319 if (Window
&& Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
321 SetLastWin32Error(ERROR_ACCESS_DENIED
);
324 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
329 PostTimerMessages(PWINDOW_OBJECT Window
)
332 PUSER_MESSAGE_QUEUE ThreadQueue
;
336 PTIMER pTmr
= FirstpTmr
;
338 if (!pTmr
) return FALSE
;
340 pti
= PsGetCurrentThreadWin32Thread();
341 ThreadQueue
= pti
->MessageQueue
;
343 TimerEnterExclusive();
347 if ( (pTmr
->flags
& TMRF_READY
) &&
348 (pTmr
->pti
== pti
) &&
349 ((pTmr
->pWnd
== Window
) || (Window
== NULL
)) )
351 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->hSelf
: 0;
352 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
353 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
354 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
356 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_TIMER
);
357 pTmr
->flags
&= ~TMRF_READY
;
358 ThreadQueue
->WakeMask
= ~QS_TIMER
;
363 pLE
= pTmr
->ptmrList
.Flink
;
364 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
365 } while (pTmr
!= FirstpTmr
);
376 LARGE_INTEGER TickCount
, DueTime
;
379 PTIMER pTmr
= FirstpTmr
;
384 TimerEnterExclusive();
386 KeQueryTickCount(&TickCount
);
387 Time
= MsqCalculateMessageTime(&TickCount
);
389 DueTime
.QuadPart
= (LONGLONG
)(-500000);
394 if (pTmr
->flags
& TMRF_WAITING
)
396 pLE
= pTmr
->ptmrList
.Flink
;
397 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
401 if (pTmr
->flags
& TMRF_INIT
)
403 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
407 if (pTmr
->cmsCountdown
< 0)
410 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
412 if (pTmr
->flags
& TMRF_ONESHOT
)
413 pTmr
->flags
|= TMRF_WAITING
;
415 if (pTmr
->flags
& TMRF_RIT
)
417 // Hard coded call here, inside raw input thread.
418 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
422 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
423 // Set thread message queue for this timer.
424 if (pTmr
->pti
->MessageQueue
)
426 ASSERT(pTmr
->pti
->MessageQueue
->NewMessages
!= NULL
);
427 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
431 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
434 pTmr
->cmsCountdown
-= Time
- TimeLast
;
437 pLE
= pTmr
->ptmrList
.Flink
;
438 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
439 } while (pTmr
!= FirstpTmr
);
441 // Restart the timer thread!
442 ASSERT(MasterTimer
!= NULL
);
443 KeSetTimer(MasterTimer
, DueTime
, NULL
);
448 DPRINT("TimerCount = %d\n", TimerCount
);
452 DestroyTimersForWindow(PTHREADINFO pti
, PWINDOW_OBJECT Window
)
455 PTIMER pTmr
= FirstpTmr
;
456 BOOL TimersRemoved
= FALSE
;
458 if ((FirstpTmr
== NULL
) || (Window
== NULL
))
461 TimerEnterExclusive();
465 if ((pTmr
) && (pTmr
->pti
== pti
) && (pTmr
->pWnd
== Window
))
467 TimersRemoved
= RemoveTimer(pTmr
);
469 pLE
= pTmr
->ptmrList
.Flink
;
470 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
471 } while (pTmr
!= FirstpTmr
);
475 return TimersRemoved
;
479 DestroyTimersForThread(PTHREADINFO pti
)
482 PTIMER pTmr
= FirstpTmr
;
483 BOOL TimersRemoved
= FALSE
;
485 if (FirstpTmr
== NULL
)
488 TimerEnterExclusive();
492 if ((pTmr
) && (pTmr
->pti
== pti
))
494 TimersRemoved
= RemoveTimer(pTmr
);
496 pLE
= pTmr
->ptmrList
.Flink
;
497 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
498 } while (pTmr
!= FirstpTmr
);
502 return TimersRemoved
;
506 IntKillTimer(PWINDOW_OBJECT Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
509 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
510 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
512 if ((Window
) && (IDEvent
== 0))
515 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0);
519 TimerEnterExclusive();
524 return pTmr
? TRUE
: FALSE
;
532 ExInitializeFastMutex(&Mutex
);
534 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
535 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
536 if (WindowLessTimersBitMapBuffer
== NULL
)
538 return STATUS_UNSUCCESSFUL
;
541 RtlInitializeBitMap(&WindowLessTimersBitMap
,
542 WindowLessTimersBitMapBuffer
,
545 /* yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
546 RtlClearAllBits(&WindowLessTimersBitMap
);
548 return STATUS_SUCCESS
;
558 TIMERPROC lpTimerFunc
561 PWINDOW_OBJECT Window
;
562 DECLARE_RETURN(UINT_PTR
);
564 DPRINT("Enter NtUserSetTimer\n");
565 UserEnterExclusive();
566 Window
= UserGetWindowObject(hWnd
);
569 RETURN(IntSetTimer(Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_TIFROMWND
));
572 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
586 PWINDOW_OBJECT Window
;
587 DECLARE_RETURN(BOOL
);
589 DPRINT("Enter NtUserKillTimer\n");
590 UserEnterExclusive();
591 Window
= UserGetWindowObject(hWnd
);
594 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
597 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
604 NtUserSetSystemTimer(
608 TIMERPROC lpTimerFunc
611 DECLARE_RETURN(UINT_PTR
);
613 DPRINT("Enter NtUserSetSystemTimer\n");
615 // This is wrong, lpTimerFunc is NULL!
616 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
));
619 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);