/*
- * ReactOS W32 Subsystem
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id$
- *
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Window timers messages
- * FILE: subsys/win32k/ntuser/timer.c
+ * FILE: subsystems/win32/win32k/ntuser/timer.c
* PROGRAMER: Gunnar
* Thomas Weidenmueller (w3seek@users.sourceforge.net)
* REVISION HISTORY: 10/04/2003 Implemented System Timers
/* INCLUDES ******************************************************************/
-#include <w32k.h>
+#include <win32k.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
+static PTIMER FirstpTmr = NULL;
+static LONG TimeLast = 0;
+
+#define MAX_ELAPSE_TIME 0x7FFFFFFF
+
/* Windows 2000 has room for 32768 window-less timers */
#define NUM_WINDOW_LESS_TIMERS 1024
ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
/* FUNCTIONS *****************************************************************/
+static
+PTIMER
+FASTCALL
+CreateTimer(VOID)
+{
+ HANDLE Handle;
+ PTIMER Ret = NULL;
+
+ if (!FirstpTmr)
+ {
+ FirstpTmr = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
+ if (FirstpTmr) InitializeListHead(&FirstpTmr->ptmrList);
+ Ret = FirstpTmr;
+ }
+ else
+ {
+ Ret = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
+ if (Ret) InsertTailList(&FirstpTmr->ptmrList, &Ret->ptmrList);
+ }
+ return Ret;
+}
+
+static
+BOOL
+FASTCALL
+RemoveTimer(PTIMER pTmr)
+{
+ if (pTmr)
+ {
+ RemoveEntryList(&pTmr->ptmrList);
+ UserDeleteObject( UserHMGetHandle(pTmr), otTimer);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+PTIMER
+FASTCALL
+FindTimer(PWINDOW_OBJECT Window,
+ UINT_PTR nID,
+ UINT flags,
+ BOOL Distroy)
+{
+ PLIST_ENTRY pLE;
+ PTIMER pTmr = FirstpTmr;
+ KeEnterCriticalRegion();
+ do
+ {
+ if (!pTmr) break;
+
+ if ( pTmr->nID == nID &&
+ pTmr->pWnd == Window &&
+ (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) == (flags & (TMRF_SYSTEM|TMRF_RIT)))
+ {
+ if (Distroy)
+ {
+ RemoveTimer(pTmr);
+ pTmr = (PTIMER)1; // We are here to remove the timer.
+ }
+ break;
+ }
+
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ } while (pTmr != FirstpTmr);
+ KeLeaveCriticalRegion();
+
+ return pTmr;
+}
+
+PTIMER
+FASTCALL
+FindSystemTimer(PMSG pMsg)
+{
+ PLIST_ENTRY pLE;
+ PTIMER pTmr = FirstpTmr;
+ KeEnterCriticalRegion();
+ do
+ {
+ if (!pTmr) break;
+
+ if ( pMsg->lParam == (LPARAM)pTmr->pfn &&
+ (pTmr->flags & TMRF_SYSTEM) )
+ break;
+
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ } while (pTmr != FirstpTmr);
+ KeLeaveCriticalRegion();
+
+ return pTmr;
+}
+BOOL
+FASTCALL
+ValidateTimerCallback(PTHREADINFO pti,
+ PWINDOW_OBJECT Window,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ PLIST_ENTRY pLE;
+ PTIMER pTmr = FirstpTmr;
+
+ if (!pTmr) return FALSE;
+
+ KeEnterCriticalRegion();
+ do
+ {
+ if ( (lParam == (LPARAM)pTmr->pfn) &&
+ (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) &&
+ (pTmr->pti->ppi == pti->ppi) )
+ break;
+
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ } while (pTmr != FirstpTmr);
+ KeLeaveCriticalRegion();
+ if (!pTmr) return FALSE;
+
+ return TRUE;
+}
+
+// Rename it to IntSetTimer after move.
+UINT_PTR FASTCALL
+InternalSetTimer( PWINDOW_OBJECT Window,
+ UINT_PTR IDEvent,
+ UINT Elapse,
+ TIMERPROC TimerFunc,
+ INT Type)
+{
+ PTIMER pTmr;
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = (LONGLONG)(-10000000);
+
+#if 0
+ /* Windows NT/2k/XP behaviour */
+ if (Elapse > MAX_ELAPSE_TIME)
+ {
+ DPRINT("Adjusting uElapse\n");
+ Elapse = 1;
+ }
+#else
+ /* Windows XP SP2 and Windows Server 2003 behaviour */
+ if (Elapse > MAX_ELAPSE_TIME)
+ {
+ DPRINT("Adjusting uElapse\n");
+ Elapse = MAX_ELAPSE_TIME;
+ }
+#endif
+
+ /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
+ if (Elapse < 10)
+ {
+ DPRINT("Adjusting uElapse\n");
+ Elapse = 10;
+ }
+
+ pTmr = FindTimer(Window, IDEvent, Type, FALSE);
+ if (!pTmr)
+ {
+ pTmr = CreateTimer();
+ if (!pTmr) return 0;
+
+ if (Window && (Type & TMRF_TIFROMWND))
+ pTmr->pti = Window->pti->pEThread->Tcb.Win32Thread;
+ else
+ {
+ if (Type & TMRF_RIT)
+ pTmr->pti = ptiRawInput;
+ else
+ pTmr->pti = PsGetCurrentThreadWin32Thread();
+ }
+ pTmr->pWnd = Window;
+ pTmr->cmsCountdown = Elapse;
+ pTmr->cmsRate = Elapse;
+ pTmr->flags = Type|TMRF_INIT; // Set timer to Init mode.
+ pTmr->pfn = TimerFunc;
+ pTmr->nID = IDEvent;
+
+ InsertTailList(&FirstpTmr->ptmrList, &pTmr->ptmrList);
+ }
+
+ // Start the timer thread!
+ KeSetTimer(MasterTimer, DueTime, NULL);
+
+ if (!pTmr->nID) return 1;
+ return pTmr->nID;
+}
+
+//
+// Process win32k system timers.
+//
+VOID
+CALLBACK
+SystemTimerProc(HWND hwnd,
+ UINT uMsg,
+ UINT_PTR idEvent,
+ DWORD dwTime)
+{
+ DPRINT( "Timer Running!\n" );
+}
+
+VOID
+FASTCALL
+StartTheTimers(VOID)
+{
+ // Need to start gdi syncro timers then start timer with Hang App proc
+ // that calles Idle process so the screen savers will know to run......
+ InternalSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
+}
+
+UINT_PTR
+FASTCALL
+SystemTimerSet( PWINDOW_OBJECT Window,
+ UINT_PTR nIDEvent,
+ UINT uElapse,
+ TIMERPROC lpTimerFunc)
+{
+ if (Window && Window->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
+ {
+ SetLastWin32Error(ERROR_ACCESS_DENIED);
+ return 0;
+ }
+ return InternalSetTimer( Window, nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM);
+}
+
+BOOL
+FASTCALL
+PostTimerMessages(PWINDOW_OBJECT Window)
+{
+ PLIST_ENTRY pLE;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+ MSG Msg;
+ PTHREADINFO pti;
+ BOOL Hit = FALSE;
+ PTIMER pTmr = FirstpTmr;
+
+ if (!pTmr) return FALSE;
+
+ ASSERT(Window);
+
+ if (Window && ((ULONG_PTR)Window != 1))
+ {
+ if (!Window->Wnd) return FALSE;
+ }
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+
+ KeEnterCriticalRegion();
+ do
+ {
+ if ( (pTmr->flags & TMRF_READY) &&
+ (pTmr->pti == pti) &&
+ (pTmr->pWnd == Window))
+ {
+ ASSERT((ULONG_PTR)Window != 1);
+ Msg.hwnd = Window->hSelf;
+ Msg.message = (pTmr->flags & TMRF_SYSTEM) ? WM_SYSTIMER : WM_TIMER;
+ Msg.wParam = (WPARAM) pTmr->nID;
+ Msg.lParam = (LPARAM) pTmr->pfn;
+ MsqPostMessage(ThreadQueue, &Msg, FALSE, QS_POSTMESSAGE);
+
+ pTmr->flags &= ~TMRF_READY;
+ ThreadQueue->WakeMask = ~QS_TIMER;
+ Hit = TRUE;
+ }
+
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ } while (pTmr != FirstpTmr);
+ KeLeaveCriticalRegion();
+
+ return Hit;
+}
+
+VOID
+FASTCALL
+ProcessTimers(VOID)
+{
+ LARGE_INTEGER TickCount, DueTime;
+ LONG Time;
+ PLIST_ENTRY pLE;
+ PTIMER pTmr = FirstpTmr;
+
+ if (!pTmr) return;
+
+ UserEnterExclusive();
+
+ KeQueryTickCount(&TickCount);
+ Time = MsqCalculateMessageTime(&TickCount);
+
+ DueTime.QuadPart = (LONGLONG)(-10000000);
+
+ do
+ {
+ if (pTmr->flags & TMRF_WAITING)
+ {
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ continue;
+ }
+
+ if (pTmr->flags & TMRF_INIT)
+ pTmr->flags &= ~TMRF_INIT; // Skip this run.
+ else
+ {
+ if (pTmr->cmsCountdown < 0)
+ {
+ if (!(pTmr->flags & TMRF_READY))
+ {
+ if (pTmr->flags & TMRF_ONESHOT)
+ pTmr->flags |= TMRF_WAITING;
+
+ if (pTmr->flags & TMRF_RIT)
+ {
+ // Hard coded call here, inside raw input thread.
+ pTmr->pfn(NULL, WM_SYSTIMER, pTmr->nID, (LPARAM)pTmr);
+ }
+ else
+ {
+ pTmr->flags |= TMRF_READY; // Set timer ready to be ran.
+ // Set thread message queue for this timer.
+ if (pTmr->pti->MessageQueue)
+ { // Wakeup thread
+ pTmr->pti->MessageQueue->WakeMask |= QS_TIMER;
+ KeSetEvent(pTmr->pti->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
+ }
+ }
+ }
+ pTmr->cmsCountdown = pTmr->cmsRate;
+ }
+ else
+ pTmr->cmsCountdown -= Time - TimeLast;
+ }
+ pLE = pTmr->ptmrList.Flink;
+ pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
+ } while (pTmr != FirstpTmr);
+
+ // Restart the timer thread!
+ KeSetTimer(MasterTimer, DueTime, NULL);
+
+ TimeLast = Time;
+
+ UserLeave();
+}
+
+//
+//
+// Old Timer Queueing
+//
+//
UINT_PTR FASTCALL
IntSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer)
{
PWINDOW_OBJECT Window;
UINT_PTR Ret = 0;
+ PTHREADINFO pti;
PUSER_MESSAGE_QUEUE MessageQueue;
DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
HintIndex = ++IDEvent;
IntUnlockWindowlessTimerBitmap();
Ret = IDEvent;
- MessageQueue = PsGetCurrentThreadWin32Thread()->MessageQueue;
+ pti = PsGetCurrentThreadWin32Thread();
+ MessageQueue = pti->MessageQueue;
}
else
{
return 0;
}
- if (Window->OwnerThread->ThreadsProcess != PsGetCurrentProcess())
+ if (Window->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
{
DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
SetLastWin32Error(ERROR_ACCESS_DENIED);
}
Ret = IDEvent;
- MessageQueue = Window->MessageQueue;
+ MessageQueue = Window->pti->MessageQueue;
}
#if 0
BOOL FASTCALL
IntKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
{
+ PTHREADINFO pti;
+ PWINDOW_OBJECT Window = NULL;
+
DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
- if (! MsqKillTimer(PsGetCurrentThreadWin32Thread()->MessageQueue, Wnd,
- IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
+ pti = PsGetCurrentThreadWin32Thread();
+ if (Wnd)
{
- DPRINT1("Unable to locate timer in message queue\n");
- SetLastWin32Error(ERROR_INVALID_PARAMETER);
- return FALSE;
+ Window = UserGetWindowObject(Wnd);
+
+ if (! MsqKillTimer(pti->MessageQueue, Wnd,
+ IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
+ {
+ // Give it another chance to find the timer.
+ if (Window && !( MsqKillTimer(Window->pti->MessageQueue, Wnd,
+ IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER)))
+ {
+ DPRINT1("Unable to locate timer in message queue for Window.\n");
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ }
}
/* window-less timer? */
if ((Wnd == NULL) && ! SystemTimer)
{
+ if (! MsqKillTimer(pti->MessageQueue, Wnd,
+ IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
+ {
+ DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
+ SetLastWin32Error(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
/* Release the id */
IntLockWindowlessTimerBitmap();
ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
+ HintIndex = IDEvent - 1;
+
IntUnlockWindowlessTimerBitmap();
}
return STATUS_SUCCESS;
}
-
UINT_PTR
-STDCALL
+APIENTRY
NtUserSetTimer
(
HWND hWnd,
BOOL
-STDCALL
+APIENTRY
NtUserKillTimer
(
HWND hWnd,
UINT_PTR
-STDCALL
+APIENTRY
NtUserSetSystemTimer(
HWND hWnd,
UINT_PTR nIDEvent,
DPRINT("Enter NtUserSetSystemTimer\n");
UserEnterExclusive();
+ // This is wrong, lpTimerFunc is NULL!
RETURN(IntSetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc, TRUE));
CLEANUP:
}
-BOOL
-STDCALL
-NtUserKillSystemTimer(
- HWND hWnd,
- UINT_PTR uIDEvent
-)
-{
- DECLARE_RETURN(BOOL);
-
- DPRINT("Enter NtUserKillSystemTimer\n");
- UserEnterExclusive();
-
- RETURN(IntKillTimer(hWnd, uIDEvent, TRUE));
-
-CLEANUP:
- DPRINT("Leave NtUserKillSystemTimer, ret=%i\n", _ret_);
- UserLeave();
- END_CLEANUP;
-}
-
/* EOF */