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
, &Handle
, otTimer
, sizeof(TIMER
));
53 if (FirstpTmr
) InitializeListHead(&FirstpTmr
->ptmrList
);
58 Ret
= UserCreateObject(gHandleTable
, &Handle
, otTimer
, sizeof(TIMER
));
59 if (Ret
) InsertTailList(&FirstpTmr
->ptmrList
, &Ret
->ptmrList
);
67 RemoveTimer(PTIMER pTmr
)
71 RemoveEntryList(&pTmr
->ptmrList
);
72 UserDeleteObject( USER_BODY_TO_HEADER(pTmr
)->hSelf
, otTimer
);
80 FindTimer(PWINDOW_OBJECT Window
,
85 PTIMER pTmr
= FirstpTmr
;
86 KeEnterCriticalRegion();
91 if ( pTmr
->nID
== nID
&&
92 pTmr
->pWnd
== Window
&&
93 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) == (flags
& (TMRF_SYSTEM
|TMRF_RIT
)))
98 pTmr
= (PTIMER
)1; // We are here to remove the timer.
103 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
104 } while (pTmr
!= FirstpTmr
);
105 KeLeaveCriticalRegion();
112 FindSystemTimer(PMSG pMsg
)
114 PTIMER pTmr
= FirstpTmr
;
115 KeEnterCriticalRegion();
120 if ( pMsg
->lParam
== (LPARAM
)pTmr
->pfn
&&
121 (pTmr
->flags
& TMRF_SYSTEM
) )
124 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
125 } while (pTmr
!= FirstpTmr
);
126 KeLeaveCriticalRegion();
133 ValidateTimerCallback(PTHREADINFO pti
,
134 PWINDOW_OBJECT Window
,
138 PTIMER pTmr
= FirstpTmr
;
140 if (!pTmr
) return FALSE
;
142 KeEnterCriticalRegion();
145 if ( (lParam
== (LPARAM
)pTmr
->pfn
) &&
146 (pTmr
->flags
& (TMRF_SYSTEM
|TMRF_RIT
)) &&
147 // (pTmr->head.pti->ppi == pti->ppi) )
148 (pTmr
->pti
->ppi
== pti
->ppi
) )
151 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
152 } while (pTmr
!= FirstpTmr
);
153 KeLeaveCriticalRegion();
155 if (!pTmr
) return FALSE
;
160 // Rename it to IntSetTimer after move.
162 InternalSetTimer( PWINDOW_OBJECT Window
,
169 LARGE_INTEGER DueTime
;
170 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
173 /* Windows NT/2k/XP behaviour */
174 if (Elapse
> MAX_ELAPSE_TIME
)
176 DPRINT("Adjusting uElapse\n");
180 /* Windows XP SP2 and Windows Server 2003 behaviour */
181 if (Elapse
> MAX_ELAPSE_TIME
)
183 DPRINT("Adjusting uElapse\n");
184 Elapse
= MAX_ELAPSE_TIME
;
188 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
191 DPRINT("Adjusting uElapse\n");
195 pTmr
= FindTimer(Window
, IDEvent
, Type
, FALSE
);
198 pTmr
= CreateTimer();
201 if (Window
&& (Type
& TMRF_TIFROMWND
))
202 pTmr
->pti
= Window
->OwnerThread
->Tcb
.Win32Thread
;
206 pTmr
->pti
= ptiRawInput
;
208 pTmr
->pti
= PsGetCurrentThreadWin32Thread();
211 pTmr
->cmsCountdown
= Elapse
;
212 pTmr
->cmsRate
= Elapse
;
213 pTmr
->flags
= Type
|TMRF_INIT
; // Set timer to Init mode.
214 pTmr
->pfn
= TimerFunc
;
217 InsertTailList(&FirstpTmr
->ptmrList
, &pTmr
->ptmrList
);
220 // Start the timer thread!
221 KeSetTimer(MasterTimer
, DueTime
, NULL
);
223 if (!pTmr
->nID
) return 1;
228 // Process win32k system timers.
232 SystemTimerProc(HWND hwnd
,
237 DPRINT( "Timer Running!\n" );
244 // Need to start gdi syncro timers then start timer with Hang App proc
245 // that calles Idle process so the screen savers will know to run......
246 InternalSetTimer(NULL
, 0, 1000, SystemTimerProc
, TMRF_RIT
);
251 SetSystemTimer( PWINDOW_OBJECT Window
,
254 TIMERPROC lpTimerFunc
)
256 if (Window
&& Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
258 SetLastWin32Error(ERROR_ACCESS_DENIED
);
261 return InternalSetTimer( Window
, nIDEvent
, uElapse
, lpTimerFunc
, TMRF_SYSTEM
);
266 PostTimerMessages(PWINDOW_OBJECT Window
)
268 PUSER_MESSAGE_QUEUE ThreadQueue
;
272 PTIMER pTmr
= FirstpTmr
;
274 if (!pTmr
) return FALSE
;
276 if (Window
&& (int)Window
!= 1)
278 if (!Window
->Wnd
) return FALSE
;
281 pti
= PsGetCurrentThreadWin32Thread();
282 ThreadQueue
= pti
->MessageQueue
;
284 KeEnterCriticalRegion();
287 if ( (pTmr
->flags
& TMRF_READY
) &&
288 (pTmr
->pti
== pti
) &&
289 (pTmr
->pWnd
== Window
))
291 Msg
.hwnd
= Window
->hSelf
;
292 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
293 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
294 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
295 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_POSTMESSAGE
);
297 pTmr
->flags
&= ~TMRF_READY
;
298 ThreadQueue
->WakeMask
= ~QS_TIMER
;
302 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
303 } while (pTmr
!= FirstpTmr
);
304 KeLeaveCriticalRegion();
313 LARGE_INTEGER TickCount
, DueTime
;
315 PTIMER pTmr
= FirstpTmr
;
319 UserEnterExclusive();
321 KeQueryTickCount(&TickCount
);
322 Time
= MsqCalculateMessageTime(&TickCount
);
324 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
328 if (pTmr
->flags
& TMRF_WAITING
)
330 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
334 if (pTmr
->flags
& TMRF_INIT
)
335 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
338 if (pTmr
->cmsCountdown
< 0)
340 if (!(pTmr
->flags
& TMRF_READY
))
342 if (pTmr
->flags
& TMRF_ONESHOT
)
343 pTmr
->flags
|= TMRF_WAITING
;
345 if (pTmr
->flags
& TMRF_RIT
)
347 // Hard coded call here, inside raw input thread.
348 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
352 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
353 // Set thread message queue for this timer.
354 if (pTmr
->pti
->MessageQueue
)
356 pTmr
->pti
->MessageQueue
->WakeMask
|= QS_TIMER
;
357 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
361 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
364 pTmr
->cmsCountdown
-= Time
- TimeLast
;
366 pTmr
= (PTIMER
)pTmr
->ptmrList
.Flink
;
367 } while (pTmr
!= FirstpTmr
);
369 // Restart the timer thread!
370 KeSetTimer(MasterTimer
, DueTime
, NULL
);
379 // Old Timer Queueing
383 IntSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
385 PWINDOW_OBJECT Window
;
388 PUSER_MESSAGE_QUEUE MessageQueue
;
390 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
391 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
393 if ((Wnd
== NULL
) && ! SystemTimer
)
395 DPRINT("Window-less timer\n");
396 /* find a free, window-less timer id */
397 IntLockWindowlessTimerBitmap();
398 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
400 if (IDEvent
== (UINT_PTR
) -1)
402 IntUnlockWindowlessTimerBitmap();
403 DPRINT1("Unable to find a free window-less timer id\n");
404 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
408 HintIndex
= ++IDEvent
;
409 IntUnlockWindowlessTimerBitmap();
411 pti
= PsGetCurrentThreadWin32Thread();
412 MessageQueue
= pti
->MessageQueue
;
416 if (!(Window
= UserGetWindowObject(Wnd
)))
418 DPRINT1("Invalid window handle\n");
422 if (Window
->OwnerThread
->ThreadsProcess
!= PsGetCurrentProcess())
424 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
425 SetLastWin32Error(ERROR_ACCESS_DENIED
);
430 MessageQueue
= Window
->MessageQueue
;
435 /* Windows NT/2k/XP behaviour */
436 if (Elapse
> 0x7fffffff)
438 DPRINT("Adjusting uElapse\n");
444 /* Windows XP SP2 and Windows Server 2003 behaviour */
445 if (Elapse
> 0x7fffffff)
447 DPRINT("Adjusting uElapse\n");
453 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
456 DPRINT("Adjusting uElapse\n");
460 if (! MsqSetTimer(MessageQueue
, Wnd
,
461 IDEvent
, Elapse
, TimerFunc
,
462 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
464 DPRINT1("Failed to set timer in message queue\n");
465 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
475 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
478 PWINDOW_OBJECT Window
= NULL
;
480 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
481 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
483 pti
= PsGetCurrentThreadWin32Thread();
486 Window
= UserGetWindowObject(Wnd
);
488 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
489 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
491 // Give it another chance to find the timer.
492 if (Window
&& !( MsqKillTimer(Window
->MessageQueue
, Wnd
,
493 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
495 DPRINT1("Unable to locate timer in message queue for Window.\n");
496 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
502 /* window-less timer? */
503 if ((Wnd
== NULL
) && ! SystemTimer
)
505 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
506 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
508 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
509 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
514 IntLockWindowlessTimerBitmap();
516 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
517 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
519 IntUnlockWindowlessTimerBitmap();
530 ExInitializeFastMutex(&Mutex
);
532 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
533 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
534 if (WindowLessTimersBitMapBuffer
== NULL
)
536 return STATUS_UNSUCCESSFUL
;
539 RtlInitializeBitMap(&WindowLessTimersBitMap
,
540 WindowLessTimersBitMapBuffer
,
543 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
544 RtlClearAllBits(&WindowLessTimersBitMap
);
546 return STATUS_SUCCESS
;
556 TIMERPROC lpTimerFunc
559 DECLARE_RETURN(UINT_PTR
);
561 DPRINT("Enter NtUserSetTimer\n");
562 UserEnterExclusive();
564 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
567 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
581 DECLARE_RETURN(BOOL
);
583 DPRINT("Enter NtUserKillTimer\n");
584 UserEnterExclusive();
586 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
589 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
597 NtUserSetSystemTimer(
601 TIMERPROC lpTimerFunc
604 DECLARE_RETURN(UINT_PTR
);
606 DPRINT("Enter NtUserSetSystemTimer\n");
607 UserEnterExclusive();
609 // This is wrong, lpTimerFunc is NULL!
610 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
613 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);