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
= 1;
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
)))
102 IDEvent
= NUM_WINDOW_LESS_TIMERS
- pTmr
->nID
;
103 IntLockWindowlessTimerBitmap();
104 RtlClearBit(&WindowLessTimersBitMap
, IDEvent
);
105 IntUnlockWindowlessTimerBitmap();
107 UserDereferenceObject(pTmr
);
108 Ret
= UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
110 if (!Ret
) DPRINT1("Warning: Unable to delete timer\n");
117 FindTimer(PWINDOW_OBJECT Window
,
122 PTIMER pTmr
= FirstpTmr
, RetTmr
= NULL
;
123 TimerEnterExclusive();
128 if ( pTmr
->nID
== nID
&&
129 pTmr
->pWnd
== Window
&&
130 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
136 pLE
= pTmr
->ptmrList
.Flink
;
137 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
138 } while (pTmr
!= FirstpTmr
);
146 FindSystemTimer(PMSG pMsg
)
149 PTIMER pTmr
= FirstpTmr
;
150 TimerEnterExclusive();
155 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
156 (pTmr
->flags
& TMRF_SYSTEM
) )
159 pLE
= pTmr
->ptmrList
.Flink
;
160 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
161 } while (pTmr
!= FirstpTmr
);
169 ValidateTimerCallback(PTHREADINFO pti
,
170 PWINDOW_OBJECT Window
,
175 PTIMER pTmr
= FirstpTmr
;
177 if (!pTmr
) return FALSE
;
179 TimerEnterExclusive();
182 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
183 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
184 (pTmr
->pti
->ppi
== pti
->ppi
) )
187 pLE
= pTmr
->ptmrList
.Flink
;
188 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
189 } while (pTmr
!= FirstpTmr
);
192 if (!pTmr
) return FALSE
;
198 IntSetTimer( PWINDOW_OBJECT Window
,
206 LARGE_INTEGER DueTime
;
207 DueTime
.QuadPart
= (LONGLONG
)(-5000000);
210 /* Windows NT/2k/XP behaviour */
211 if (Elapse
> MAX_ELAPSE_TIME
)
213 DPRINT("Adjusting uElapse\n");
217 /* Windows XP SP2 and Windows Server 2003 behaviour */
218 if (Elapse
> MAX_ELAPSE_TIME
)
220 DPRINT("Adjusting uElapse\n");
221 Elapse
= MAX_ELAPSE_TIME
;
225 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
228 DPRINT("Adjusting uElapse\n");
232 /* Passing an IDEvent of 0 and the SetTimer returns 1.
233 It will create the timer with an ID of 0 */
234 if ((Window
) && (IDEvent
== 0))
237 pTmr
= FindTimer(Window
, IDEvent
, Type
);
239 if ((!pTmr
) && (Window
== NULL
) && (!(Type
& TMRF_SYSTEM
)))
241 IntLockWindowlessTimerBitmap();
243 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
245 if (IDEvent
== (UINT_PTR
) -1)
247 IntUnlockWindowlessTimerBitmap();
248 DPRINT1("Unable to find a free window-less timer id\n");
249 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
254 IDEvent
= NUM_WINDOW_LESS_TIMERS
- IDEvent
;
257 IntUnlockWindowlessTimerBitmap();
262 pTmr
= CreateTimer();
265 if (Window
&& (Type
& TMRF_TIFROMWND
))
266 pTmr
->pti
= Window
->pti
->pEThread
->Tcb
.Win32Thread
;
270 pTmr
->pti
= ptiRawInput
;
272 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
276 pTmr
->cmsCountdown
= Elapse
;
277 pTmr
->cmsRate
= Elapse
;
278 pTmr
->pfn
= TimerFunc
;
280 pTmr
->flags
= Type
|TMRF_INIT
;
284 pTmr
->cmsCountdown
= Elapse
;
285 pTmr
->cmsRate
= Elapse
;
288 ASSERT(MasterTimer
!= NULL
);
289 // Start the timer thread!
290 if (pTmr
== FirstpTmr
)
291 KeSetTimer(MasterTimer
, DueTime
, NULL
);
297 // Process win32k system timers.
301 SystemTimerProc(HWND hwnd
,
306 DPRINT( "Timer Running!\n" );
313 // Need to start gdi syncro timers then start timer with Hang App proc
314 // that calles Idle process so the screen savers will know to run......
315 IntSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
320 SystemTimerSet( PWINDOW_OBJECT Window
,
323 TIMERPROC lpTimerFunc
)
325 if (Window
&& Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
327 SetLastWin32Error(ERROR_ACCESS_DENIED
);
328 DPRINT("SysemTimerSet: Access Denied!\n");
331 return IntSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
336 PostTimerMessages(PWINDOW_OBJECT Window
)
339 PUSER_MESSAGE_QUEUE ThreadQueue
;
343 PTIMER pTmr
= FirstpTmr
;
345 if (!pTmr
) return FALSE
;
347 pti
= PsGetCurrentThreadWin32Thread();
348 ThreadQueue
= pti
->MessageQueue
;
350 TimerEnterExclusive();
354 if ( (pTmr
->flags
& TMRF_READY
) &&
355 (pTmr
->pti
== pti
) &&
356 ((pTmr
->pWnd
== Window
) || (Window
== NULL
)) )
358 Msg
.hwnd
= (pTmr
->pWnd
) ? pTmr
->pWnd
->hSelf
: 0;
359 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
360 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
361 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
363 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_TIMER
);
364 pTmr
->flags
&= ~TMRF_READY
;
365 ThreadQueue
->WakeMask
= ~QS_TIMER
;
370 pLE
= pTmr
->ptmrList
.Flink
;
371 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
372 } while (pTmr
!= FirstpTmr
);
383 LARGE_INTEGER TickCount
, DueTime
;
386 PTIMER pTmr
= FirstpTmr
;
391 TimerEnterExclusive();
393 KeQueryTickCount(&TickCount
);
394 Time
= MsqCalculateMessageTime(&TickCount
);
396 DueTime
.QuadPart
= (LONGLONG
)(-500000);
401 if (pTmr
->flags
& TMRF_WAITING
)
403 pLE
= pTmr
->ptmrList
.Flink
;
404 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
408 if (pTmr
->flags
& TMRF_INIT
)
410 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
414 if (pTmr
->cmsCountdown
< 0)
417 if ((!(pTmr
->flags
& TMRF_READY
)) && (!(pTmr
->pti
->TIF_flags
& TIF_INCLEANUP
)))
419 if (pTmr
->flags
& TMRF_ONESHOT
)
420 pTmr
->flags
|= TMRF_WAITING
;
422 if (pTmr
->flags
& TMRF_RIT
)
424 // Hard coded call here, inside raw input thread.
425 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
429 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
430 // Set thread message queue for this timer.
431 if (pTmr
->pti
->MessageQueue
)
433 ASSERT(pTmr
->pti
->MessageQueue
->NewMessages
!= NULL
);
434 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
438 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
441 pTmr
->cmsCountdown
-= Time
- TimeLast
;
444 pLE
= pTmr
->ptmrList
.Flink
;
445 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
446 } while (pTmr
!= FirstpTmr
);
448 // Restart the timer thread!
449 ASSERT(MasterTimer
!= NULL
);
450 KeSetTimer(MasterTimer
, DueTime
, NULL
);
455 DPRINT("TimerCount = %d\n", TimerCount
);
459 DestroyTimersForWindow(PTHREADINFO pti
, PWINDOW_OBJECT Window
)
462 PTIMER pTmr
= FirstpTmr
;
463 BOOL TimersRemoved
= FALSE
;
465 if ((FirstpTmr
== NULL
) || (Window
== NULL
))
468 TimerEnterExclusive();
472 if ((pTmr
) && (pTmr
->pti
== pti
) && (pTmr
->pWnd
== Window
))
474 TimersRemoved
= RemoveTimer(pTmr
);
476 pLE
= pTmr
->ptmrList
.Flink
;
477 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
478 } while (pTmr
!= FirstpTmr
);
482 return TimersRemoved
;
486 DestroyTimersForThread(PTHREADINFO pti
)
489 PTIMER pTmr
= FirstpTmr
;
490 BOOL TimersRemoved
= FALSE
;
492 if (FirstpTmr
== NULL
)
495 TimerEnterExclusive();
499 if ((pTmr
) && (pTmr
->pti
== pti
))
501 TimersRemoved
= RemoveTimer(pTmr
);
503 pLE
= pTmr
->ptmrList
.Flink
;
504 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
505 } while (pTmr
!= FirstpTmr
);
509 return TimersRemoved
;
513 IntKillTimer(PWINDOW_OBJECT Window
, UINT_PTR IDEvent
, BOOL SystemTimer
)
516 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
517 Window
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
519 pTmr
= FindTimer(Window
, IDEvent
, SystemTimer
? TMRF_SYSTEM
: 0);
523 TimerEnterExclusive();
528 return pTmr
? TRUE
: FALSE
;
536 ExInitializeFastMutex(&Mutex
);
538 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
539 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(NonPagedPool
, BitmapBytes
, TAG_TIMERBMP
);
540 if (WindowLessTimersBitMapBuffer
== NULL
)
542 return STATUS_UNSUCCESSFUL
;
545 RtlInitializeBitMap(&WindowLessTimersBitMap
,
546 WindowLessTimersBitMapBuffer
,
549 /* yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
550 RtlClearAllBits(&WindowLessTimersBitMap
);
552 return STATUS_SUCCESS
;
562 TIMERPROC lpTimerFunc
565 PWINDOW_OBJECT Window
;
566 DECLARE_RETURN(UINT_PTR
);
568 DPRINT("Enter NtUserSetTimer\n");
569 UserEnterExclusive();
570 Window
= UserGetWindowObject(hWnd
);
573 RETURN(IntSetTimer(Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_TIFROMWND
));
576 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
590 PWINDOW_OBJECT Window
;
591 DECLARE_RETURN(BOOL
);
593 DPRINT("Enter NtUserKillTimer\n");
594 UserEnterExclusive();
595 Window
= UserGetWindowObject(hWnd
);
598 RETURN(IntKillTimer(Window
, uIDEvent
, FALSE
));
601 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
608 NtUserSetSystemTimer(
612 TIMERPROC lpTimerFunc
615 DECLARE_RETURN(UINT_PTR
);
617 DPRINT("Enter NtUserSetSystemTimer\n");
619 // This is wrong, lpTimerFunc is NULL!
620 RETURN(IntSetTimer(UserGetWindowObject(hWnd
), nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
));
623 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);