2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window timers messages
24 * FILE: subsys/win32k/ntuser/timer.c
30 /* INCLUDES ******************************************************************/
32 #include <ddk/ntddk.h>
33 #include <win32k/win32k.h>
34 #include <win32k/ntuser.h>
35 #include <internal/ntoskrnl.h>
36 #include <internal/ps.h>
37 #include <include/msgqueue.h>
39 #include <napi/win32.h>
44 /* GLOBALS *******************************************************************/
46 //windows 2000 has room for 32768 handle-less timers
47 #define NUM_HANDLE_LESS_TIMERS 1024
49 static FAST_MUTEX Mutex;
50 static LIST_ENTRY TimerListHead;
52 static RTL_BITMAP HandleLessTimersBitMap;
53 static PVOID HandleLessTimersBitMapBuffer;
54 static ULONG HintIndex = 0;
55 static HANDLE MsgTimerThreadHandle;
56 static CLIENT_ID MsgTimerThreadId;
59 typedef struct _MSG_TIMER_ENTRY{
61 LARGE_INTEGER Timeout;
65 } MSG_TIMER_ENTRY, *PMSG_TIMER_ENTRY;
68 /* FUNCTIONS *****************************************************************/
71 //return true if the new timer became the first entry
72 //must hold mutex while calling this
75 InsertTimerAscendingOrder(PMSG_TIMER_ENTRY NewTimer)
77 PLIST_ENTRY EnumEntry, InsertAfter;
78 PMSG_TIMER_ENTRY MsgTimer;
82 EnumEntry = TimerListHead.Flink;
83 while (EnumEntry != &TimerListHead)
85 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
86 if (NewTimer->Timeout.QuadPart > MsgTimer->Timeout.QuadPart)
88 InsertAfter = EnumEntry;
90 EnumEntry = EnumEntry->Flink;
95 InsertTailList(InsertAfter, &NewTimer->ListEntry);
99 //insert as first entry
100 InsertHeadList(&TimerListHead, &NewTimer->ListEntry);
105 //must hold mutex while calling this
108 RemoveTimer(HWND hWnd, UINT_PTR IDEvent, HANDLE ThreadID)
110 PMSG_TIMER_ENTRY MsgTimer;
111 PLIST_ENTRY EnumEntry;
113 //remove timer if allready in the queue
114 EnumEntry = TimerListHead.Flink;
115 while (EnumEntry != &TimerListHead)
117 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
118 EnumEntry = EnumEntry->Flink;
120 if (MsgTimer->Msg.hwnd == hWnd &&
121 MsgTimer->Msg.wParam == (WPARAM)IDEvent &&
122 MsgTimer->ThreadID == ThreadID)
124 RemoveEntryList(&MsgTimer->ListEntry);
134 * NOTE: It doesn't kill timers. It just removes them from the list.
138 RemoveTimersThread(HANDLE ThreadID)
140 PMSG_TIMER_ENTRY MsgTimer;
141 PLIST_ENTRY EnumEntry;
143 ExAcquireFastMutex(&Mutex);
145 EnumEntry = TimerListHead.Flink;
146 while (EnumEntry != &TimerListHead)
148 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
149 EnumEntry = EnumEntry->Flink;
151 if (MsgTimer->ThreadID == ThreadID)
153 if (MsgTimer->Msg.hwnd == NULL)
155 RtlClearBits(&HandleLessTimersBitMap, ((UINT_PTR)MsgTimer->Msg.wParam) - 1, 1);
158 RemoveEntryList(&MsgTimer->ListEntry);
159 ExFreePool(MsgTimer);
163 ExReleaseFastMutex(&Mutex);
176 TIMERPROC lpTimerFunc
180 PMSG_TIMER_ENTRY MsgTimer = NULL;
181 PMSG_TIMER_ENTRY NewTimer;
182 LARGE_INTEGER CurrentTime;
185 //FIXME: WINE: window must be owned by the calling thread
187 if(hWnd && !(hWnd = WIN_IsCurrentThread(hWnd))
189 return STATUS_UNSUCCESSFUL;
193 ThreadID = PsGetCurrentThreadId();
194 KeQuerySystemTime(&CurrentTime);
195 ExAcquireFastMutex(&Mutex);
199 /* find a free, handle-less timer id */
200 Index = RtlFindClearBitsAndSet(&HandleLessTimersBitMap, 1, HintIndex);
202 if(Index == (ULONG) -1)
204 /* FIXME: set the last error */
205 ExReleaseFastMutex(&Mutex);
211 ExReleaseFastMutex(&Mutex);
216 /* remove timer if already in the queue */
217 MsgTimer = RemoveTimer(hWnd, nIDEvent, ThreadID);
222 /* modify existing (removed) timer */
225 NewTimer->Period = uElapse;
226 NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
227 NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
231 /* FIXME: use lookaside? */
232 NewTimer = ExAllocatePool(PagedPool, sizeof(MSG_TIMER_ENTRY));
234 NewTimer->Msg.hwnd = hWnd;
235 NewTimer->Msg.message = WM_TIMER;
236 NewTimer->Msg.wParam = (WPARAM)nIDEvent;
237 NewTimer->Msg.lParam = (LPARAM)lpTimerFunc;
238 NewTimer->Period = uElapse;
239 NewTimer->Timeout.QuadPart = CurrentTime.QuadPart + (uElapse * 10000);
240 NewTimer->ThreadID = ThreadID;
243 if(InsertTimerAscendingOrder(NewTimer))
245 /* new timer is first in queue and expires first */
246 KeSetTimer(&Timer, NewTimer->Timeout, NULL);
249 ExReleaseFastMutex(&Mutex);
263 PMSG_TIMER_ENTRY MsgTimer;
265 ExAcquireFastMutex(&Mutex);
267 /* handle-less timer? */
270 if(!RtlAreBitsSet(&HandleLessTimersBitMap, uIDEvent - 1, 1))
272 /* bit was not set */
273 /* FIXME: set the last error */
274 ExReleaseFastMutex(&Mutex);
278 RtlClearBits(&HandleLessTimersBitMap, uIDEvent - 1, 1);
281 MsgTimer = RemoveTimer(hWnd, uIDEvent, PsGetCurrentThreadId());
283 ExReleaseFastMutex(&Mutex);
287 /* didn't find timer */
288 /* FIXME: set the last error */
292 /* FIXME: use lookaside? */
293 ExFreePool(MsgTimer);
300 NtUserSetSystemTimer(
312 static VOID STDCALL_FUNC
318 LARGE_INTEGER CurrentTime;
319 PLIST_ENTRY EnumEntry;
320 PMSG_TIMER_ENTRY MsgTimer;
326 Status = KeWaitForSingleObject( &Timer,
332 if (!NT_SUCCESS(Status))
334 DPRINT1("Error waiting in TimerThreadMain\n");
338 ExAcquireFastMutex(&Mutex);
340 KeQuerySystemTime(&CurrentTime);
342 EnumEntry = TimerListHead.Flink;
343 while (EnumEntry != &TimerListHead)
345 MsgTimer = CONTAINING_RECORD(EnumEntry, MSG_TIMER_ENTRY, ListEntry);
346 EnumEntry = EnumEntry->Flink;
348 if (CurrentTime.QuadPart >= MsgTimer->Timeout.QuadPart)
350 RemoveEntryList(&MsgTimer->ListEntry);
353 * FIXME: 1) Find a faster way of getting the thread message queue? (lookup by id is slow)
356 if (!NT_SUCCESS(PsLookupThreadByThreadId(MsgTimer->ThreadID, &Thread)))
358 ExFreePool(MsgTimer);
362 MsqPostMessage(((PW32THREAD)Thread->Win32Thread)->MessageQueue, MsqCreateMessage(&MsgTimer->Msg));
364 ObDereferenceObject(Thread);
366 //set up next periodic timeout
367 MsgTimer->Timeout.QuadPart += (MsgTimer->Period * 10000);
368 InsertTimerAscendingOrder(MsgTimer);
377 //set up next timeout from first entry (if any)
378 if (!IsListEmpty(&TimerListHead))
380 MsgTimer = CONTAINING_RECORD( TimerListHead.Flink, MSG_TIMER_ENTRY, ListEntry);
381 KeSetTimer(&Timer, MsgTimer->Timeout, NULL);
385 /* Reinitialize the timer, this reset the state of the timer event on which we wait */
386 KeInitializeTimer(&Timer);
389 ExReleaseFastMutex(&Mutex);
403 BitmapBytes = ROUND_UP(NUM_HANDLE_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
405 InitializeListHead(&TimerListHead);
406 KeInitializeTimer(&Timer);
407 ExInitializeFastMutex(&Mutex);
409 HandleLessTimersBitMapBuffer = ExAllocatePool(PagedPool, BitmapBytes);
410 RtlInitializeBitMap(&HandleLessTimersBitMap,
411 HandleLessTimersBitMapBuffer,
414 //yes we need this, since ExAllocatePool isn't supposed to zero out allocated memory
415 RtlClearAllBits(&HandleLessTimersBitMap);
417 Status = PsCreateSystemThread(&MsgTimerThreadHandle,