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 SetSystemTimer( 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
&& (int)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 Msg
.hwnd
= Window
->hSelf
;
298 Msg
.message
= (pTmr
->flags
& TMRF_SYSTEM
) ? WM_SYSTIMER
: WM_TIMER
;
299 Msg
.wParam
= (WPARAM
) pTmr
->nID
;
300 Msg
.lParam
= (LPARAM
) pTmr
->pfn
;
301 MsqPostMessage(ThreadQueue
, &Msg
, FALSE
, QS_POSTMESSAGE
);
303 pTmr
->flags
&= ~TMRF_READY
;
304 ThreadQueue
->WakeMask
= ~QS_TIMER
;
308 pLE
= pTmr
->ptmrList
.Flink
;
309 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
310 } while (pTmr
!= FirstpTmr
);
311 KeLeaveCriticalRegion();
320 LARGE_INTEGER TickCount
, DueTime
;
323 PTIMER pTmr
= FirstpTmr
;
327 UserEnterExclusive();
329 KeQueryTickCount(&TickCount
);
330 Time
= MsqCalculateMessageTime(&TickCount
);
332 DueTime
.QuadPart
= (LONGLONG
)(-10000000);
336 if (pTmr
->flags
& TMRF_WAITING
)
338 pLE
= pTmr
->ptmrList
.Flink
;
339 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
343 if (pTmr
->flags
& TMRF_INIT
)
344 pTmr
->flags
&= ~TMRF_INIT
; // Skip this run.
347 if (pTmr
->cmsCountdown
< 0)
349 if (!(pTmr
->flags
& TMRF_READY
))
351 if (pTmr
->flags
& TMRF_ONESHOT
)
352 pTmr
->flags
|= TMRF_WAITING
;
354 if (pTmr
->flags
& TMRF_RIT
)
356 // Hard coded call here, inside raw input thread.
357 pTmr
->pfn(NULL
, WM_SYSTIMER
, pTmr
->nID
, (LPARAM
)pTmr
);
361 pTmr
->flags
|= TMRF_READY
; // Set timer ready to be ran.
362 // Set thread message queue for this timer.
363 if (pTmr
->pti
->MessageQueue
)
365 pTmr
->pti
->MessageQueue
->WakeMask
|= QS_TIMER
;
366 KeSetEvent(pTmr
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
370 pTmr
->cmsCountdown
= pTmr
->cmsRate
;
373 pTmr
->cmsCountdown
-= Time
- TimeLast
;
375 pLE
= pTmr
->ptmrList
.Flink
;
376 pTmr
= CONTAINING_RECORD(pLE
, TIMER
, ptmrList
);
377 } while (pTmr
!= FirstpTmr
);
379 // Restart the timer thread!
380 KeSetTimer(MasterTimer
, DueTime
, NULL
);
389 // Old Timer Queueing
393 IntSetTimer(HWND Wnd
, UINT_PTR IDEvent
, UINT Elapse
, TIMERPROC TimerFunc
, BOOL SystemTimer
)
395 PWINDOW_OBJECT Window
;
398 PUSER_MESSAGE_QUEUE MessageQueue
;
400 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
401 Wnd
, IDEvent
, Elapse
, TimerFunc
, SystemTimer
? "TRUE" : "FALSE");
403 if ((Wnd
== NULL
) && ! SystemTimer
)
405 DPRINT("Window-less timer\n");
406 /* find a free, window-less timer id */
407 IntLockWindowlessTimerBitmap();
408 IDEvent
= RtlFindClearBitsAndSet(&WindowLessTimersBitMap
, 1, HintIndex
);
410 if (IDEvent
== (UINT_PTR
) -1)
412 IntUnlockWindowlessTimerBitmap();
413 DPRINT1("Unable to find a free window-less timer id\n");
414 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
418 HintIndex
= ++IDEvent
;
419 IntUnlockWindowlessTimerBitmap();
421 pti
= PsGetCurrentThreadWin32Thread();
422 MessageQueue
= pti
->MessageQueue
;
426 if (!(Window
= UserGetWindowObject(Wnd
)))
428 DPRINT1("Invalid window handle\n");
432 if (Window
->pti
->pEThread
->ThreadsProcess
!= PsGetCurrentProcess())
434 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
435 SetLastWin32Error(ERROR_ACCESS_DENIED
);
440 MessageQueue
= Window
->pti
->MessageQueue
;
445 /* Windows NT/2k/XP behaviour */
446 if (Elapse
> 0x7fffffff)
448 DPRINT("Adjusting uElapse\n");
454 /* Windows XP SP2 and Windows Server 2003 behaviour */
455 if (Elapse
> 0x7fffffff)
457 DPRINT("Adjusting uElapse\n");
463 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
466 DPRINT("Adjusting uElapse\n");
470 if (! MsqSetTimer(MessageQueue
, Wnd
,
471 IDEvent
, Elapse
, TimerFunc
,
472 SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
474 DPRINT1("Failed to set timer in message queue\n");
475 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES
);
485 IntKillTimer(HWND Wnd
, UINT_PTR IDEvent
, BOOL SystemTimer
)
488 PWINDOW_OBJECT Window
= NULL
;
490 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
491 Wnd
, IDEvent
, SystemTimer
? "TRUE" : "FALSE");
493 pti
= PsGetCurrentThreadWin32Thread();
496 Window
= UserGetWindowObject(Wnd
);
498 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
499 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
501 // Give it another chance to find the timer.
502 if (Window
&& !( MsqKillTimer(Window
->pti
->MessageQueue
, Wnd
,
503 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
)))
505 DPRINT1("Unable to locate timer in message queue for Window.\n");
506 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
512 /* window-less timer? */
513 if ((Wnd
== NULL
) && ! SystemTimer
)
515 if (! MsqKillTimer(pti
->MessageQueue
, Wnd
,
516 IDEvent
, SystemTimer
? WM_SYSTIMER
: WM_TIMER
))
518 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
519 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
524 IntLockWindowlessTimerBitmap();
526 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap
, IDEvent
- 1, 1));
527 RtlClearBits(&WindowLessTimersBitMap
, IDEvent
- 1, 1);
529 IntUnlockWindowlessTimerBitmap();
540 ExInitializeFastMutex(&Mutex
);
542 BitmapBytes
= ROUND_UP(NUM_WINDOW_LESS_TIMERS
, sizeof(ULONG
) * 8) / 8;
543 WindowLessTimersBitMapBuffer
= ExAllocatePoolWithTag(PagedPool
, BitmapBytes
, TAG_TIMERBMP
);
544 if (WindowLessTimersBitMapBuffer
== NULL
)
546 return STATUS_UNSUCCESSFUL
;
549 RtlInitializeBitMap(&WindowLessTimersBitMap
,
550 WindowLessTimersBitMapBuffer
,
553 /* yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory */
554 RtlClearAllBits(&WindowLessTimersBitMap
);
556 return STATUS_SUCCESS
;
566 TIMERPROC lpTimerFunc
569 DECLARE_RETURN(UINT_PTR
);
571 DPRINT("Enter NtUserSetTimer\n");
572 UserEnterExclusive();
574 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, FALSE
));
577 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_
);
591 DECLARE_RETURN(BOOL
);
593 DPRINT("Enter NtUserKillTimer\n");
594 UserEnterExclusive();
596 RETURN(IntKillTimer(hWnd
, uIDEvent
, FALSE
));
599 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_
);
607 NtUserSetSystemTimer(
611 TIMERPROC lpTimerFunc
614 DECLARE_RETURN(UINT_PTR
);
616 DPRINT("Enter NtUserSetSystemTimer\n");
617 UserEnterExclusive();
619 // This is wrong, lpTimerFunc is NULL!
620 RETURN(IntSetTimer(hWnd
, nIDEvent
, uElapse
, lpTimerFunc
, TRUE
));
623 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_
);