mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 17:44:45 +00:00
timer messages impl.
svn path=/trunk/; revision=4479
This commit is contained in:
parent
a812f4e0b2
commit
9db4bafa99
2 changed files with 362 additions and 37 deletions
|
@ -1120,17 +1120,6 @@ NtUserIsClipboardFormatAvailable(
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserKillTimer(
|
||||
DWORD Unknown0,
|
||||
DWORD Unknown1)
|
||||
{
|
||||
UNIMPLEMENTED
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserLoadKeyboardLayoutEx(
|
||||
|
@ -1559,19 +1548,6 @@ NtUserSetSystemMenu(
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserSetSystemTimer(
|
||||
DWORD Unknown0,
|
||||
DWORD Unknown1,
|
||||
DWORD Unknown2,
|
||||
DWORD Unknown3)
|
||||
{
|
||||
UNIMPLEMENTED
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserSetThreadState(
|
||||
|
@ -1583,19 +1559,6 @@ NtUserSetThreadState(
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserSetTimer(
|
||||
DWORD Unknown0,
|
||||
DWORD Unknown1,
|
||||
DWORD Unknown2,
|
||||
DWORD Unknown3)
|
||||
{
|
||||
UNIMPLEMENTED
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserShowCaret(
|
||||
|
|
362
reactos/subsys/win32k/ntuser/timer.c
Normal file
362
reactos/subsys/win32k/ntuser/timer.c
Normal file
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS kernel
|
||||
* PURPOSE: Window timers messages
|
||||
* FILE: subsys/win32k/ntuser/timer.c
|
||||
* PROGRAMER: Gunnar
|
||||
* REVISION HISTORY:
|
||||
*
|
||||
*/
|
||||
|
||||
/* INCLUDES ******************************************************************/
|
||||
|
||||
#include <ddk/ntddk.h>
|
||||
#include <win32k/win32k.h>
|
||||
#include <win32k/ntuser.h>
|
||||
#include <internal/ntoskrnl.h>
|
||||
#include <internal/ps.h>
|
||||
#include <include/msgqueue.h>
|
||||
#include <messages.h>
|
||||
#include <napi/win32.h>
|
||||
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* GLOBALS *******************************************************************/
|
||||
|
||||
|
||||
static FAST_MUTEX Mutex;
|
||||
static LIST_ENTRY TimerListHead;
|
||||
static KTIMER Timer;
|
||||
static RTL_BITMAP HandleLessTimersBitMap;
|
||||
static PVOID HandleLessTimersBitMapBuffer;
|
||||
static ULONG HintIndex = 0;
|
||||
static HANDLE MsgTimerThreadHandle;
|
||||
static CLIENT_ID MsgTimerThreadId;
|
||||
|
||||
|
||||
typedef struct _MSG_TIMER_ENTRY{
|
||||
LIST_ENTRY ListEntry;
|
||||
LARGE_INTEGER Timeout;
|
||||
HANDLE ThreadID;
|
||||
UINT Period;
|
||||
MSG Msg;
|
||||
} MSG_TIMER_ENTRY, *PMSG_TIMER_ENTRY;
|
||||
|
||||
|
||||
/* FUNCTIONS *****************************************************************/
|
||||
|
||||
|
||||
//return true if the new timer became the first entry
|
||||
BOOL
|
||||
FASTCALL
|
||||
InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
|
||||
{
|
||||
PLIST_ENTRY EnumEntry, InsertAfter;
|
||||
PMSG_TIMER_ENTRY MsgTimer;
|
||||
|
||||
InsertAfter = NULL;
|
||||
|
||||
EnumEntry = TimerListHead.Flink;
|
||||
while (EnumEntry != &TimerListHead)
|
||||
{
|
||||
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(&TimerListHead, &NewTimer->ListEntry);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
PMSG_TIMER_ENTRY
|
||||
FASTCALL
|
||||
RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
|
||||
{
|
||||
PMSG_TIMER_ENTRY MsgTimer;
|
||||
PLIST_ENTRY EnumEntry;
|
||||
|
||||
//remove timer if allready in the queue
|
||||
EnumEntry = TimerListHead.Flink;
|
||||
while (EnumEntry != &TimerListHead)
|
||||
{
|
||||
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
|
||||
if (MsgTimer->Msg.hwnd == hWnd &&
|
||||
MsgTimer->Msg.wParam == (WPARAM)IDEvent &&
|
||||
MsgTimer->ThreadID == ThreadID)
|
||||
{
|
||||
RemoveEntryList(EnumEntry);
|
||||
return MsgTimer;
|
||||
}
|
||||
EnumEntry = EnumEntry->Flink;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
NTSTATUS
|
||||
STDCALL
|
||||
NtUserSetTimer(
|
||||
HWND hWnd,
|
||||
UINT_PTR * IDEvent,
|
||||
UINT Period,
|
||||
TIMERPROC TimerFunc
|
||||
)
|
||||
{
|
||||
ULONG Index;
|
||||
PMSG_TIMER_ENTRY MsgTimer = NULL;
|
||||
PMSG_TIMER_ENTRY NewTimer;
|
||||
LARGE_INTEGER CurrentTime;
|
||||
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);
|
||||
|
||||
if (hWnd == NULL)
|
||||
{
|
||||
//find a free, handle-less timer id
|
||||
Index = RtlFindClearBitsAndSet(&HandleLessTimersBitMap, 1, HintIndex);
|
||||
if (Index == -1)
|
||||
{
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
*IDEvent = HintIndex = Index + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//remove timer if allready in the queue
|
||||
MsgTimer = RemoveTimer(hWnd, *IDEvent, ThreadID);
|
||||
}
|
||||
|
||||
if (MsgTimer)
|
||||
{
|
||||
//modify existing (removed) timer
|
||||
NewTimer = MsgTimer;
|
||||
|
||||
NewTimer->Period = Period;
|
||||
NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
|
||||
NewTimer->Msg.lParam = (LPARAM)TimerFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
//FIXME: use lookaside?
|
||||
NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
|
||||
|
||||
NewTimer->Msg.hwnd = hWnd;
|
||||
NewTimer->Msg.message = WM_TIMER;
|
||||
NewTimer->Msg.wParam = (WPARAM)*IDEvent;
|
||||
NewTimer->Msg.lParam = (LPARAM)TimerFunc;
|
||||
NewTimer->Period = Period;
|
||||
NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (Period * 10000);
|
||||
NewTimer->ThreadID = ThreadID;
|
||||
}
|
||||
|
||||
if (InsertTimerAscendingOrder(NewTimer))
|
||||
{
|
||||
//new timer is first in queue and expires first
|
||||
KeSetTimer(&Timer, NewTimer->Timeout, NULL);
|
||||
}
|
||||
|
||||
ExReleaseFastMutex(&Mutex);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS
|
||||
STDCALL
|
||||
NtUserKillTimer(
|
||||
HWND hWnd,
|
||||
UINT_PTR IDEvent)
|
||||
{
|
||||
PMSG_TIMER_ENTRY MsgTimer;
|
||||
|
||||
ExAcquireFastMutex(&Mutex);
|
||||
|
||||
//handle-less timer?
|
||||
if (hWnd == NULL)
|
||||
{
|
||||
if (!RtlAreBitsSet(&HandleLessTimersBitMap, IDEvent - 1, 1))
|
||||
{
|
||||
//bit was not set
|
||||
ExReleaseFastMutex(&Mutex);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
RtlClearBits(&HandleLessTimersBitMap, IDEvent - 1, 1);
|
||||
}
|
||||
|
||||
MsgTimer = RemoveTimer(hWnd, IDEvent, PsGetCurrentThreadId());
|
||||
|
||||
ExReleaseFastMutex(&Mutex);
|
||||
|
||||
if (MsgTimer == NULL)
|
||||
{
|
||||
//didn't find timer
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
//FIXME: use lookaside?
|
||||
ExFreePool(MsgTimer);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DWORD
|
||||
STDCALL
|
||||
NtUserSetSystemTimer(
|
||||
DWORD Unknown0,
|
||||
DWORD Unknown1,
|
||||
DWORD Unknown2,
|
||||
DWORD Unknown3)
|
||||
{
|
||||
UNIMPLEMENTED
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS STDCALL
|
||||
TimerThreadMain()
|
||||
{
|
||||
NTSTATUS Status;
|
||||
LARGE_INTEGER CurrentTime;
|
||||
PLIST_ENTRY EnumEntry;
|
||||
PMSG_TIMER_ENTRY MsgTimer;
|
||||
PETHREAD Thread;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
||||
Status = KeWaitForSingleObject( &Timer,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL
|
||||
);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Error waiting in TimerThreadMain\n");
|
||||
KeBugCheck(0);
|
||||
}
|
||||
|
||||
ExAcquireFastMutex(&Mutex);
|
||||
|
||||
KeQuerySystemTime(&CurrentTime);
|
||||
|
||||
EnumEntry = TimerListHead.Flink;
|
||||
while (EnumEntry != &TimerListHead)
|
||||
{
|
||||
MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
|
||||
EnumEntry = EnumEntry->Flink;
|
||||
|
||||
if (CurrentTime.QuadPart >= MsgTimer->Timeout.QuadPart)
|
||||
{
|
||||
RemoveEntryList(&MsgTimer->ListEntry);
|
||||
|
||||
/*
|
||||
* FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
|
||||
* 2) Kill all timers for thread when the thread exits?
|
||||
*/
|
||||
|
||||
if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer->ThreadID, &Thread)))
|
||||
{
|
||||
//FIXME: remove all other timers for this thread also?
|
||||
ExFreePool(MsgTimer);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: small window here, where the thread can exit between the thread lookup
|
||||
* and the message posting (missing ref count?)
|
||||
*/
|
||||
MsqPostMessage(((PW32THREAD)Thread->Win32Thread)->MessageQueue, MsqCreateMessage(&MsgTimer->Msg));
|
||||
|
||||
//set up next periodic timeout
|
||||
MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
|
||||
InsertTimerAscendingOrder(MsgTimer);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//set up next timeout from first entry (if any)
|
||||
if (!IsListEmpty(&TimerListHead))
|
||||
{
|
||||
MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
|
||||
KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
|
||||
}
|
||||
|
||||
ExReleaseFastMutex(&Mutex);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
NTSTATUS
|
||||
InitTimerImpl()
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
||||
InitializeListHead(&TimerListHead);
|
||||
KeInitializeTimer(&Timer);
|
||||
ExInitializeFastMutex(&Mutex);
|
||||
|
||||
//windows 2000 has room for 32768 handle-less timers
|
||||
HandleLessTimersBitMapBuffer = ExAllocatePool(PagedPool, PAGE_SIZE);
|
||||
RtlInitializeBitMap(&HandleLessTimersBitMap,
|
||||
HandleLessTimersBitMapBuffer,
|
||||
PAGE_SIZE * sizeof(ULONG));
|
||||
|
||||
//yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory
|
||||
RtlClearAllBits(&HandleLessTimersBitMap);
|
||||
|
||||
Status = PsCreateSystemThread(&MsgTimerThreadHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
NULL,
|
||||
NULL,
|
||||
&MsgTimerThreadId,
|
||||
TimerThreadMain,
|
||||
NULL);
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in a new issue