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 RemoveEntryList(&pTmr
->ptmrList
);
72 UserDeleteObject( UserHMGetHandle(pTmr
), otTimer
);
80 FindTimer(PWINDOW_OBJECT Window
,
86 PTIMER pTmr
= FirstpTmr
;
87 KeEnterCriticalRegion();
92 if ( pTmr
->nID
== nID
&&
93 pTmr
->pWnd
== Window
&&
94 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
99 pTmr
= (PTIMER
)1; // We are here to remove the timer.
104 pLE
= pTmr
->ptmrList
.Flink
;
105 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
106 } while (pTmr
!= FirstpTmr
);
107 KeLeaveCriticalRegion();
114 FindSystemTimer(PMSG pMsg
)
117 PTIMER pTmr
= FirstpTmr
;
118 KeEnterCriticalRegion();
123 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
124 (pTmr
->flags
& TMRF_SYSTEM
) )
127 pLE
= pTmr
->ptmrList
.Flink
;
128 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
129 } while (pTmr
!= FirstpTmr
);
130 KeLeaveCriticalRegion();
137 ValidateTimerCallback(PTHREADINFO pti
,
138 PWINDOW_OBJECT Window
,
143 PTIMER pTmr
= FirstpTmr
;
145 if (!pTmr
) return FALSE
;
147 KeEnterCriticalRegion();
150 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
151 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
152 (pTmr
->pti
->ppi
== pti
->ppi
) )
155 pLE
= pTmr
->ptmrList
.Flink
;
156 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
157 } while (pTmr
!= FirstpTmr
);
158 KeLeaveCriticalRegion();
160 if (!pTmr
) return FALSE
;
165 // Rename it to IntSetTimer after move.
167 InternalSetTimer( PWINDOW_OBJECT Window
,
174 LARGE_INTEGER DueTime
;
175 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
178 /* Windows NT/2k/XP behaviour */
179 if (Elapse
> MAX_ELAPSE_TIME
)
181 DPRINT("Adjusting uElapse\n");
185 /* Windows XP SP2 and Windows Server 2003 behaviour */
186 if (Elapse
> MAX_ELAPSE_TIME
)
188 DPRINT("Adjusting uElapse\n");
189 Elapse
= MAX_ELAPSE_TIME
;
193 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
196 DPRINT("Adjusting uElapse\n");
200 pTmr
= FindTimer(Window
, IDEvent
, Type
, FALSE
);
203 pTmr
= CreateTimer();
206 if (Window
&& (Type
& TMRF_TIFROMWND
))
207 pTmr
->pti
= Window
->pti
->pEThread
->Tcb
.Win32Thread
;
211 pTmr
->pti
= ptiRawInput
;
213 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
216 pTmr
->cmsCountdown
= Elapse
;
217 pTmr
->cmsRate
= Elapse
;
218 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
219 pTmr
->pfn
= TimerFunc
;
222 InsertTailList(&FirstpTmr
->ptmrList
, &pTmr
->ptmrList
);
225 // Start the timer thread!
226 KeSetTimer(MasterTimer
, DueTime
, NULL
);
228 if (!pTmr
->nID
) return 1;
233 // Process win32k system timers.
237 SystemTimerProc(HWND hwnd
,
242 DPRINT( "Timer Running!\n" );
249 // Need to start gdi syncro timers then start timer with Hang App proc
250 // that calles Idle process so the screen savers will know to run......
251 InternalSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
256 SystemTimerSet( PWINDOW_OBJECT Window
,
259 TIMERPROC lpTimerFunc
)
261 if (Window
&& Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
263 SetLastWin32Error(ERROR_ACCESS_DENIED
);
266 return InternalSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
271 PostTimerMessages(PWINDOW_OBJECT Window
)
274 PUSER_MESSAGE_QUEUE ThreadQueue
;
278 PTIMER pTmr
= FirstpTmr
;
280 if (!pTmr
) return FALSE
;
282 if (Window
&& ((ULONG_PTR
)Window
!= 1))
284 if (!Window
->Wnd
) return FALSE
;
287 pti
= PsGetCurrentThreadWin32Thread();
288 ThreadQueue
= pti
->MessageQueue
;
290 KeEnterCriticalRegion();
293 if ( (pTmr
->flags
& TMRF_READY
) &&
294 (pTmr
->pti
== pti
) &&
295 (pTmr
->pWnd
== Window
))
297 ASSERT((ULONG_PTR
)Window
!= 1);
298 Msg
.hwnd
= Window
->hSelf
;
299 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
300 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
301 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
302 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_POSTMESSAGE
);
304 pTmr
->flags
&= ~TMRF_READY
;
305 ThreadQueue
->WakeMask
= ~QS_TIMER
;
309 pLE
= pTmr
->ptmrList
.Flink
;
310 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
311 } while (pTmr
!= FirstpTmr
);
312 KeLeaveCriticalRegion();
321 LARGE_INTEGER TickCount
, DueTime
;
324 PTIMER pTmr
= FirstpTmr
;
328 UserEnterExclusive();
330 KeQueryTickCount(&TickCount
);
331 Time
= MsqCalculateMessageTime(&TickCount
);
333 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
337 if (pTmr
->flags
& TMRF_WAITING
)
339 pLE
= pTmr
->ptmrList
.Flink
;
340 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
344 if (pTmr
->flags
& TMRF_INIT
)
345 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
348 if (pTmr
->cmsCountdown
< 0)
350 if (!(pTmr
->flags
& TMRF_READY
))
352 if (pTmr
->flags
& TMRF_ONESHOT
)
353 pTmr
->flags
|= TMRF_WAITING
;
355 if (pTmr
->flags
& TMRF_RIT
)
357 // Hard coded call here, inside raw input thread.
358 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
362 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
363 // Set thread message queue for this timer.
364 if (pTmr
->pti
->MessageQueue
)
366 pTmr
->pti
->MessageQueue
->WakeMask
|= QS_TIMER
;
367 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
371 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
374 pTmr
->cmsCountdown
-= Time
- TimeLast
;
376 pLE
= pTmr
->ptmrList
.Flink
;
377 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
378 } while (pTmr
!= FirstpTmr
);
380 // Restart the timer thread!
381 KeSetTimer(MasterTimer
, DueTime
, NULL
);
390 // Old Timer Queueing
394 IntSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
396 PWINDOW_OBJECT Window
;
399 PUSER_MESSAGE_QUEUE MessageQueue
;
401 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
402 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
404 if ((Wnd
== NULL
) && ! SystemTimer
)
406 DPRINT("Window-less timer\n");
407 /* find a free, window-less timer id */
408 IntLockWindowlessTimerBitmap();
409 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
411 if (IDEvent
== (UINT_PTR
) -1)
413 IntUnlockWindowlessTimerBitmap();
414 DPRINT1("Unable to find a free window-less timer id\n");
415 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
419 HintIndex
= ++IDEvent
;
420 IntUnlockWindowlessTimerBitmap();
422 pti
= PsGetCurrentThreadWin32Thread();
423 MessageQueue
= pti
->MessageQueue
;
427 if (!(Window
= UserGetWindowObject(Wnd
)))
429 DPRINT1("Invalid window handle\n");
433 if (Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
435 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
436 SetLastWin32Error(ERROR_ACCESS_DENIED
);
441 MessageQueue
= Window
->pti
->MessageQueue
;
446 /* Windows NT/2k/XP behaviour */
447 if (Elapse
> 0x7fffffff)
449 DPRINT("Adjusting uElapse\n");
455 /* Windows XP SP2 and Windows Server 2003 behaviour */
456 if (Elapse
> 0x7fffffff)
458 DPRINT("Adjusting uElapse\n");
464 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
467 DPRINT("Adjusting uElapse\n");
471 if (! MsqSetTimer(MessageQueue
, Wnd
,
472 IDEvent
, Elapse
, TimerFunc
,
473 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
475 DPRINT1("Failed to set timer in message queue\n");
476 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
486 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
489 PWINDOW_OBJECT Window
= NULL
;
491 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
492 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
494 pti
= PsGetCurrentThreadWin32Thread();
497 Window
= UserGetWindowObject(Wnd
);
499 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
500 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
502 // Give it another chance to find the timer.
503 if (Window
&& !( MsqKillTimer(Window
->pti
->MessageQueue
, Wnd
,
504 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
506 DPRINT1("Unable to locate timer in message queue for Window.\n");
507 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
513 /* window-less timer? */
514 if ((Wnd
== NULL
) && ! SystemTimer
)
516 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
517 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
519 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
520 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
525 IntLockWindowlessTimerBitmap();
527 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
528 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
530 HintIndex
= IDEvent
- 1;
532 IntUnlockWindowlessTimerBitmap();
543 ExInitializeFastMutex(&Mutex
);
545 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
546 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
547 if (WindowLessTimersBitMapBuffer
== NULL
)
549 return STATUS_UNSUCCESSFUL
;
552 RtlInitializeBitMap(&WindowLessTimersBitMap
,
553 WindowLessTimersBitMapBuffer
,
556 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
557 RtlClearAllBits(&WindowLessTimersBitMap
);
559 return STATUS_SUCCESS
;
569 TIMERPROC lpTimerFunc
572 DECLARE_RETURN(UINT_PTR
);
574 DPRINT("Enter NtUserSetTimer\n");
575 UserEnterExclusive();
577 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
580 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
594 DECLARE_RETURN(BOOL
);
596 DPRINT("Enter NtUserKillTimer\n");
597 UserEnterExclusive();
599 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
602 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
610 NtUserSetSystemTimer(
614 TIMERPROC lpTimerFunc
617 DECLARE_RETURN(UINT_PTR
);
619 DPRINT("Enter NtUserSetSystemTimer\n");
620 UserEnterExclusive();
622 // This is wrong, lpTimerFunc is NULL!
623 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
626 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);