implements system timers (similar to standard timers)

svn path=/trunk/; revision=6240
This commit is contained in:
Thomas Bluemel 2003-10-04 22:36:37 +00:00
parent 996d541acf
commit 93e9630e67
9 changed files with 465 additions and 121 deletions

View file

@ -428,6 +428,7 @@ NtUserInternalGetWindowText 3
NtUserInvalidateRect 3
NtUserInvalidateRgn 3
NtUserIsClipboardFormatAvailable 1
NtUserKillSystemTimer 2
NtUserKillTimer 2
NtUserLoadKeyboardLayoutEx 6
NtUserLockWindowStation 1

View file

@ -4713,10 +4713,10 @@ SetWindowStationUser(
UINT
STDCALL
SetSystemTimer(
HWND hwnd,
UINT id,
UINT timeout,
TIMERPROC proc
HWND hWnd,
UINT_PTR IDEvent,
UINT Period,
TIMERPROC TimerFunc
);
WINBOOL
@ -4777,8 +4777,8 @@ RegisterLogonProcess(
WINBOOL
STDCALL
KillSystemTimer(
HWND hwnd,
UINT id
HWND hWnd,
UINT_PTR IDEvent
);
DWORD

View file

@ -1105,6 +1105,7 @@ extern "C" {
#define WM_TCARD (82)
#define WM_TIMECHANGE (30)
#define WM_TIMER (275)
#define WM_SYSTIMER (280)
#define WM_UNDO (772)
#define WM_USER (1024)
#define WM_USERCHANGED (84)

View file

@ -959,6 +959,14 @@ STDCALL
NtUserIsClipboardFormatAvailable(
DWORD Unknown0);
BOOL
STDCALL
NtUserKillSystemTimer
(
HWND hWnd,
UINT_PTR uIDEvent
);
BOOL
STDCALL
NtUserKillTimer
@ -1485,14 +1493,6 @@ NtUserSetSystemMenu(
HWND hWnd,
HMENU hMenu);
DWORD
STDCALL
NtUserSetSystemTimer(
DWORD Unknown0,
DWORD Unknown1,
DWORD Unknown2,
DWORD Unknown3);
BOOL
STDCALL
NtUserSetThreadDesktop(
@ -1504,6 +1504,16 @@ NtUserSetThreadState(
DWORD Unknown0,
DWORD Unknown1);
UINT_PTR
STDCALL
NtUserSetSystemTimer
(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
UINT_PTR
STDCALL
NtUserSetTimer

View file

@ -1,4 +1,4 @@
/* $Id: listbox.c,v 1.11 2003/09/27 00:29:52 weiden Exp $
/* $Id: listbox.c,v 1.12 2003/10/04 22:36:36 weiden Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS User32
@ -74,12 +74,6 @@ BOOL is_old_app(HWND hwnd)
#define WM_LBTRACKPOINT 0x0131
#define WS_EX_DRAGDETECT 0x00000002L
#define WM_BEGINDRAG 0x022C
//#define WM_MOUSEWHEEL 0x020A
//#define WHEEL_DELTA 120
#define WM_SYSTIMER WM_TIMER
#define SetSystemTimer SetTimer
#define KillSystemTimer KillTimer
/* End of hack section -------------------------------- */

View file

@ -1,4 +1,4 @@
/* $Id: stubs.c,v 1.43 2003/09/12 17:51:48 vizzini Exp $
/* $Id: stubs.c,v 1.44 2003/10/04 22:36:36 weiden Exp $
*
* COPYRIGHT: See COPYING WINBOOLthe top level directory
* PROJECT: ReactOS user32.dll
@ -869,22 +869,6 @@ SetWindowStationUser ( DWORD x1, DWORD x2 )
return FALSE;
}
/*
* @unimplemented
*/
UINT
STDCALL
SetSystemTimer(
HWND hwnd,
UINT id,
UINT timeout,
TIMERPROC proc
)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
@ -978,17 +962,6 @@ RegisterLogonProcess ( HANDLE hprocess, BOOL x )
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
KillSystemTimer ( HWND hwnd, UINT id )
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: timer.c,v 1.8 2003/07/10 21:04:31 chorns Exp $
/* $Id: timer.c,v 1.9 2003/10/04 22:36:36 weiden Exp $
*
* PROJECT: ReactOS user32.dll
* FILE: lib/user32/misc/dde.c
@ -35,6 +35,20 @@
/* FUNCTIONS *****************************************************************/
/*
* @implemented
*/
WINBOOL
STDCALL
KillSystemTimer(
HWND hWnd,
UINT_PTR IDEvent)
{
return NtUserKillSystemTimer(hWnd, IDEvent);
}
/*
* @implemented
*/
@ -48,6 +62,21 @@ KillTimer(
}
/*
* @implemented
*/
UINT_PTR
STDCALL
SetSystemTimer(
HWND hWnd,
UINT_PTR IDEvent,
UINT Period,
TIMERPROC TimerFunc)
{
return NtUserSetSystemTimer(hWnd, IDEvent, Period, TimerFunc);
}
/*
* @implemented
*/

View file

@ -16,7 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: timer.c,v 1.10 2003/08/13 20:24:05 chorns Exp $
/* $Id: timer.c,v 1.11 2003/10/04 22:36:37 weiden Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
@ -35,6 +35,8 @@
#include <internal/ntoskrnl.h>
#include <internal/ps.h>
#include <include/msgqueue.h>
#include <include/window.h>
#include <include/error.h>
#include <messages.h>
#include <napi/win32.h>
@ -48,6 +50,7 @@
static FAST_MUTEX Mutex;
static LIST_ENTRY TimerListHead;
static LIST_ENTRY SysTimerListHead;
static KTIMER Timer;
static RTL_BITMAP HandleLessTimersBitMap;
static PVOID HandleLessTimersBitMapBuffer;
@ -72,13 +75,15 @@ typedef struct _MSG_TIMER_ENTRY{
//must hold mutex while calling this
BOOL
FASTCALL
InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer, BOOL SysTimer)
{
PLIST_ENTRY EnumEntry, InsertAfter;
PMSG_TIMER_ENTRY MsgTimer;
InsertAfter = NULL;
if(!SysTimer)
{
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
@ -95,9 +100,31 @@ InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
InsertTailList(InsertAfter, &NewTimer->ListEntry);
return FALSE;
}
//insert as first entry
InsertHeadList(&TimerListHead, &NewTimer->ListEntry);
}
else
{
EnumEntry = SysTimerListHead.Flink;
while (EnumEntry != &SysTimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
if (NewTimer->Timeout.QuadPart > MsgTimer->Timeout.QuadPart)
{
InsertAfter = EnumEntry;
}
EnumEntry = EnumEntry->Flink;
}
if (InsertAfter)
{
InsertTailList(InsertAfter, &NewTimer->ListEntry);
return FALSE;
}
//insert as first entry
InsertHeadList(&SysTimerListHead, &NewTimer->ListEntry);
}
return TRUE;
}
@ -105,11 +132,13 @@ InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
//must hold mutex while calling this
PMSG_TIMER_ENTRY
FASTCALL
RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID, BOOL SysTimer)
{
PMSG_TIMER_ENTRY MsgTimer;
PLIST_ENTRY EnumEntry;
if(!SysTimer)
{
//remove timer if allready in the queue
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
@ -125,6 +154,25 @@ RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
return MsgTimer;
}
}
}
else
{
//remove timer if allready in the queue
EnumEntry = SysTimerListHead.Flink;
while (EnumEntry != &SysTimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (MsgTimer->Msg.hwnd == hWnd &&
MsgTimer->Msg.wParam == (WPARAM)IDEvent &&
MsgTimer->ThreadID == ThreadID)
{
RemoveEntryList(&MsgTimer->ListEntry);
return MsgTimer;
}
}
}
return NULL;
}
@ -142,6 +190,19 @@ RemoveTimersThread(HANDLE ThreadID)
ExAcquireFastMutex(&Mutex);
EnumEntry = SysTimerListHead.Flink;
while (EnumEntry != &SysTimerListHead)
{
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (MsgTimer->ThreadID == ThreadID)
{
RemoveEntryList(&MsgTimer->ListEntry);
ExFreePool(MsgTimer);
}
}
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
@ -177,19 +238,13 @@ NtUserSetTimer
)
{
ULONG Index;
PMSG_TIMER_ENTRY MsgTimer = NULL;
PMSG_TIMER_ENTRY MsgTimer2, MsgTimer = NULL;
PMSG_TIMER_ENTRY NewTimer;
PLIST_ENTRY EnumEntry;
LARGE_INTEGER CurrentTime;
PWINDOW_OBJECT WindowObject;
HANDLE ThreadID;
//FIXME: WINE: window must be owned by the calling thread
#if 0
if(hWnd && !(hWnd = WIN_IsCurrentThread(hWnd))
{
return STATUS_UNSUCCESSFUL;
}
#endif
ThreadID = PsGetCurrentThreadId();
KeQuerySystemTime(&CurrentTime);
ExAcquireFastMutex(&Mutex);
@ -201,7 +256,6 @@ NtUserSetTimer
if(Index == (ULONG) -1)
{
/* FIXME: set the last error */
ExReleaseFastMutex(&Mutex);
return 0;
}
@ -213,10 +267,45 @@ NtUserSetTimer
}
else
{
/* remove timer if already in the queue */
MsgTimer = RemoveTimer(hWnd, nIDEvent, ThreadID);
WindowObject = IntGetWindowObject(hWnd);
if(!WindowObject)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntReleaseWindowObject(WindowObject);
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return 0;
}
IntReleaseWindowObject(WindowObject);
/* remove timer if already in the queue */
MsgTimer = RemoveTimer(hWnd, nIDEvent, ThreadID, FALSE);
}
#if 1
/* Win NT/2k/XP */
if(uElapse > 0x7fffffff)
uElapse = 1;
#else
/* Win Server 2003 */
if(uElapse > 0x7fffffff)
uElapse = 0x7fffffff;
#endif
/* Win 2k/XP */
if(uElapse < 10)
uElapse = 10;
if(MsgTimer)
{
/* modify existing (removed) timer */
@ -230,6 +319,12 @@ NtUserSetTimer
{
/* FIXME: use lookaside? */
NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
if(!NewTimer)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
NewTimer->Msg.hwnd = hWnd;
NewTimer->Msg.message = WM_TIMER;
@ -240,11 +335,24 @@ NtUserSetTimer
NewTimer->ThreadID = ThreadID;
}
if(InsertTimerAscendingOrder(NewTimer))
if(InsertTimerAscendingOrder(NewTimer, FALSE))
{
EnumEntry = SysTimerListHead.Flink;
if(EnumEntry != &SysTimerListHead)
{
MsgTimer2 = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
if (NewTimer->Timeout.QuadPart <= MsgTimer2->Timeout.QuadPart)
{
/* new timer is first in queue and expires first */
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
}
}
else
{
/* new timer is first in queue and expires first */
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
}
}
ExReleaseFastMutex(&Mutex);
@ -261,6 +369,7 @@ NtUserKillTimer
)
{
PMSG_TIMER_ENTRY MsgTimer;
PWINDOW_OBJECT WindowObject;
ExAcquireFastMutex(&Mutex);
@ -277,8 +386,26 @@ NtUserKillTimer
RtlClearBits(&HandleLessTimersBitMap, uIDEvent - 1, 1);
}
else
{
WindowObject = IntGetWindowObject(hWnd);
if(!WindowObject)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return FALSE;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntReleaseWindowObject(WindowObject);
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return FALSE;
}
IntReleaseWindowObject(WindowObject);
}
MsgTimer = RemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId());
MsgTimer = RemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId(), FALSE);
ExReleaseFastMutex(&Mutex);
@ -295,18 +422,172 @@ NtUserKillTimer
return TRUE;
}
DWORD
UINT_PTR
STDCALL
NtUserSetSystemTimer(
DWORD Unknown0,
DWORD Unknown1,
DWORD Unknown2,
DWORD Unknown3)
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
)
{
UNIMPLEMENTED
/* As opposed to SetTimer() this one seems not to allow window-less timers! */
PMSG_TIMER_ENTRY MsgTimer2, MsgTimer = NULL;
PMSG_TIMER_ENTRY NewTimer;
PLIST_ENTRY EnumEntry;
LARGE_INTEGER CurrentTime;
PWINDOW_OBJECT WindowObject;
HANDLE ThreadID;
ThreadID = PsGetCurrentThreadId();
KeQuerySystemTime(&CurrentTime);
ExAcquireFastMutex(&Mutex);
if(hWnd == NULL)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
else
{
WindowObject = IntGetWindowObject(hWnd);
if(!WindowObject)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return 0;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntReleaseWindowObject(WindowObject);
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return 0;
}
IntReleaseWindowObject(WindowObject);
/* remove timer if already in the queue */
MsgTimer = RemoveTimer(hWnd, nIDEvent, ThreadID, TRUE);
}
#if 1
/* Win NT/2k/XP */
if(uElapse > 0x7fffffff)
uElapse = 1;
#else
/* Win Server 2003 */
if(uElapse > 0x7fffffff)
uElapse = 0x7fffffff;
#endif
/* Win 2k/XP */
if(uElapse < 10)
uElapse = 10;
if(MsgTimer)
{
/* modify existing (removed) timer */
NewTimer = MsgTimer;
NewTimer->Period = uElapse;
NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
}
else
{
/* FIXME: use lookaside? */
NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
if(!NewTimer)
{
ExReleaseFastMutex(&Mutex);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
NewTimer->Msg.hwnd = hWnd;
NewTimer->Msg.message = WM_SYSTIMER;
NewTimer->Msg.wParam = (WPARAM)nIDEvent;
NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
NewTimer->Period = uElapse;
NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
NewTimer->ThreadID = ThreadID;
}
if(InsertTimerAscendingOrder(NewTimer, TRUE))
{
EnumEntry = TimerListHead.Flink;
if(EnumEntry != &TimerListHead)
{
MsgTimer2 = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
if (NewTimer->Timeout.QuadPart <= MsgTimer2->Timeout.QuadPart)
{
/* new timer is first in queue and expires first */
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
}
}
else
{
/* new timer is first in queue and expires first */
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
}
}
ExReleaseFastMutex(&Mutex);
return 1;
}
BOOL
STDCALL
NtUserKillSystemTimer(
HWND hWnd,
UINT_PTR uIDEvent
)
{
/* As opposed to KillTimer() this one seems not to allow window-less timers! */
PMSG_TIMER_ENTRY MsgTimer;
PWINDOW_OBJECT WindowObject;
WindowObject = IntGetWindowObject(hWnd);
/* handle-less timer? Not allowed for SystemTimers! */
if(!WindowObject)
{
SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
return FALSE;
}
if(WindowObject->OwnerThread != PsGetCurrentThread())
{
IntReleaseWindowObject(WindowObject);
SetLastWin32Error(ERROR_ACCESS_DENIED);
return FALSE;
}
IntReleaseWindowObject(WindowObject);
ExAcquireFastMutex(&Mutex);
MsgTimer = RemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId(), TRUE);
ExReleaseFastMutex(&Mutex);
if(MsgTimer == NULL)
{
/* didn't find timer */
/* FIXME: set the last error */
return FALSE;
}
/* FIXME: use lookaside? */
ExFreePool(MsgTimer);
return TRUE;
}
static VOID STDCALL_FUNC
@ -317,7 +598,7 @@ TimerThreadMain(
NTSTATUS Status;
LARGE_INTEGER CurrentTime;
PLIST_ENTRY EnumEntry;
PMSG_TIMER_ENTRY MsgTimer;
PMSG_TIMER_ENTRY MsgTimer, MsgTimer2;
PETHREAD Thread;
for (;;)
@ -339,6 +620,41 @@ TimerThreadMain(
KeQuerySystemTime(&CurrentTime);
EnumEntry = SysTimerListHead.Flink;
while (EnumEntry != &SysTimerListHead)
{
MsgTimer2 = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
EnumEntry = EnumEntry->Flink;
if (CurrentTime.QuadPart >= MsgTimer2->Timeout.QuadPart)
{
RemoveEntryList(&MsgTimer2->ListEntry);
/*
* FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
*/
if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer2->ThreadID, &Thread)))
{
ExFreePool(MsgTimer2);
continue;
}
MsqPostMessage(((PW32THREAD)Thread->Win32Thread)->MessageQueue, MsqCreateMessage(&MsgTimer2->Msg));
ObDereferenceObject(Thread);
//set up next periodic timeout
MsgTimer2->Timeout.QuadPart += (MsgTimer2->Period * 10000);
InsertTimerAscendingOrder(MsgTimer2, TRUE);
}
else
{
break;
}
}
EnumEntry = TimerListHead.Flink;
while (EnumEntry != &TimerListHead)
{
@ -365,7 +681,7 @@ TimerThreadMain(
//set up next periodic timeout
MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
InsertTimerAscendingOrder(MsgTimer);
InsertTimerAscendingOrder(MsgTimer, FALSE);
}
else
@ -378,13 +694,32 @@ TimerThreadMain(
if (!IsListEmpty(&TimerListHead))
{
MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
if(!IsListEmpty(&SysTimerListHead))
{
MsgTimer2 = CONTAINING_RECORD( SysTimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
if(MsgTimer->Timeout.QuadPart >= MsgTimer2->Timeout.QuadPart)
KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
else
KeSetTimer(&Timer, MsgTimer2->Timeout, NULL);
}
else
{
KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
}
}
else
{
if(!IsListEmpty(&SysTimerListHead))
{
MsgTimer2 = CONTAINING_RECORD( SysTimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
KeSetTimer(&Timer, MsgTimer2->Timeout, NULL);
}
else
{
/* Reinitialize the timer, this reset the state of the timer event on which we wait */
KeInitializeTimer(&Timer);
}
}
ExReleaseFastMutex(&Mutex);
@ -403,6 +738,7 @@ InitTimerImpl(VOID)
BitmapBytes = ROUND_UP(NUM_HANDLE_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
InitializeListHead(&TimerListHead);
InitializeListHead(&SysTimerListHead);
KeInitializeTimer(&Timer);
ExInitializeFastMutex(&Mutex);

View file

@ -340,7 +340,7 @@ NtUserGetClipCursor(
{
DPRINT("Validation of window station handle (0x%X) failed\n",
PROCESS_WINDOW_STATION());
SetLastWin32Error(Status);
SetLastNtError(Status);
return FALSE;
}