[win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / timer.c
1 /*
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
6 * PROGRAMER: Gunnar
7 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
8 * Michael Martin (michael.martin@reactos.org)
9 * REVISION HISTORY: 10/04/2003 Implemented System Timers
10 *
11 */
12
13 /* INCLUDES ******************************************************************/
14
15 #include <win32k.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 /* GLOBALS *******************************************************************/
21
22 static PTIMER FirstpTmr = NULL;
23 static LONG TimeLast = 0;
24
25 #define MAX_ELAPSE_TIME 0x7FFFFFFF
26
27 /* Windows 2000 has room for 32768 window-less timers */
28 #define NUM_WINDOW_LESS_TIMERS 32768
29
30 static FAST_MUTEX Mutex;
31 static RTL_BITMAP WindowLessTimersBitMap;
32 static PVOID WindowLessTimersBitMapBuffer;
33 static ULONG HintIndex = 0;
34
35 ERESOURCE TimerLock;
36
37 #define IntLockWindowlessTimerBitmap() \
38 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
39
40 #define IntUnlockWindowlessTimerBitmap() \
41 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
42
43 #define TimerEnterExclusive() \
44 { \
45 KeEnterCriticalRegion(); \
46 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
47 }
48
49 #define TimerLeave() \
50 { \
51 ExReleaseResourceLite(&TimerLock); \
52 KeLeaveCriticalRegion(); \
53 }
54
55
56 /* FUNCTIONS *****************************************************************/
57 static
58 PTIMER
59 FASTCALL
60 CreateTimer(VOID)
61 {
62 HANDLE Handle;
63 PTIMER Ret = NULL;
64
65 if (!FirstpTmr)
66 {
67 ExInitializeResourceLite(&TimerLock);
68 FirstpTmr = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
69 if (FirstpTmr)
70 {
71 FirstpTmr->head.h = Handle;
72 InitializeListHead(&FirstpTmr->ptmrList);
73 }
74 Ret = FirstpTmr;
75 }
76 else
77 {
78 Ret = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
79 if (Ret)
80 {
81 Ret->head.h = Handle;
82 InsertTailList(&FirstpTmr->ptmrList, &Ret->ptmrList);
83 }
84 }
85 return Ret;
86 }
87
88 static
89 BOOL
90 FASTCALL
91 RemoveTimer(PTIMER pTmr)
92 {
93 BOOL Ret = FALSE;
94 if (pTmr)
95 {
96 /* Set the flag, it will be removed when ready */
97 RemoveEntryList(&pTmr->ptmrList);
98 if ((pTmr->pWnd == NULL) && (!(pTmr->flags & TMRF_SYSTEM)))
99 {
100 DPRINT("Clearing Bit %d)\n", pTmr->nID);
101 IntLockWindowlessTimerBitmap();
102 RtlClearBit(&WindowLessTimersBitMap, pTmr->nID);
103 IntUnlockWindowlessTimerBitmap();
104 }
105 UserDereferenceObject(pTmr);
106 Ret = UserDeleteObject( UserHMGetHandle(pTmr), otTimer);
107 }
108 if (!Ret) DPRINT1("Warning: Unable to delete timer\n");
109
110 return Ret;
111 }
112
113 PTIMER
114 FASTCALL
115 FindTimer(PWINDOW_OBJECT Window,
116 UINT_PTR nID,
117 UINT flags)
118 {
119 PLIST_ENTRY pLE;
120 PTIMER pTmr = FirstpTmr, RetTmr = NULL;
121 TimerEnterExclusive();
122 do
123 {
124 if (!pTmr) break;
125
126 if ( pTmr->nID == nID &&
127 pTmr->pWnd == Window &&
128 (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) == (flags & (TMRF_SYSTEM|TMRF_RIT)))
129 {
130 RetTmr = pTmr;
131 break;
132 }
133
134 pLE = pTmr->ptmrList.Flink;
135 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
136 } while (pTmr != FirstpTmr);
137 TimerLeave();
138
139 return RetTmr;
140 }
141
142 PTIMER
143 FASTCALL
144 FindSystemTimer(PMSG pMsg)
145 {
146 PLIST_ENTRY pLE;
147 PTIMER pTmr = FirstpTmr;
148 TimerEnterExclusive();
149 do
150 {
151 if (!pTmr) break;
152
153 if ( pMsg->lParam == (LPARAM)pTmr->pfn &&
154 (pTmr->flags & TMRF_SYSTEM) )
155 break;
156
157 pLE = pTmr->ptmrList.Flink;
158 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
159 } while (pTmr != FirstpTmr);
160 TimerLeave();
161
162 return pTmr;
163 }
164
165 BOOL
166 FASTCALL
167 ValidateTimerCallback(PTHREADINFO pti,
168 PWINDOW_OBJECT Window,
169 WPARAM wParam,
170 LPARAM lParam)
171 {
172 PLIST_ENTRY pLE;
173 PTIMER pTmr = FirstpTmr;
174
175 if (!pTmr) return FALSE;
176
177 TimerEnterExclusive();
178 do
179 {
180 if ( (lParam == (LPARAM)pTmr->pfn) &&
181 (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) &&
182 (pTmr->pti->ppi == pti->ppi) )
183 break;
184
185 pLE = pTmr->ptmrList.Flink;
186 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
187 } while (pTmr != FirstpTmr);
188 TimerLeave();
189
190 if (!pTmr) return FALSE;
191
192 return TRUE;
193 }
194
195 UINT_PTR FASTCALL
196 IntSetTimer( PWINDOW_OBJECT Window,
197 UINT_PTR IDEvent,
198 UINT Elapse,
199 TIMERPROC TimerFunc,
200 INT Type)
201 {
202 PTIMER pTmr;
203 UINT Ret= IDEvent;
204 LARGE_INTEGER DueTime;
205 DueTime.QuadPart = (LONGLONG)(-5000000);
206
207 #if 0
208 /* Windows NT/2k/XP behaviour */
209 if (Elapse > MAX_ELAPSE_TIME)
210 {
211 DPRINT("Adjusting uElapse\n");
212 Elapse = 1;
213 }
214 #else
215 /* Windows XP SP2 and Windows Server 2003 behaviour */
216 if (Elapse > MAX_ELAPSE_TIME)
217 {
218 DPRINT("Adjusting uElapse\n");
219 Elapse = MAX_ELAPSE_TIME;
220 }
221 #endif
222
223 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
224 if (Elapse < 10)
225 {
226 DPRINT("Adjusting uElapse\n");
227 Elapse = 10;
228 }
229
230 if ((Window) && (IDEvent == 0))
231 IDEvent = 1;
232
233 pTmr = FindTimer(Window, IDEvent, Type);
234
235 if ((!pTmr) && (Window == NULL) && (!(Type & TMRF_SYSTEM)))
236 {
237 IntLockWindowlessTimerBitmap();
238
239 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
240
241 if (IDEvent == (UINT_PTR) -1)
242 {
243 IntUnlockWindowlessTimerBitmap();
244 DPRINT1("Unable to find a free window-less timer id\n");
245 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
246 return 0;
247 }
248
249 Ret = IDEvent;
250 //HintIndex = IDEvent + 1;
251 IntUnlockWindowlessTimerBitmap();
252 }
253
254 if (!pTmr)
255 {
256 pTmr = CreateTimer();
257 if (!pTmr) return 0;
258
259 if (Window && (Type & TMRF_TIFROMWND))
260 pTmr->pti = Window->pti->pEThread->Tcb.Win32Thread;
261 else
262 {
263 if (Type & TMRF_RIT)
264 pTmr->pti = ptiRawInput;
265 else
266 pTmr->pti = PsGetCurrentThreadWin32Thread();
267 }
268
269 pTmr->pWnd = Window;
270 pTmr->cmsCountdown = Elapse;
271 pTmr->cmsRate = Elapse;
272 pTmr->pfn = TimerFunc;
273 pTmr->nID = IDEvent;
274 pTmr->flags = Type|TMRF_INIT; // Set timer to Init mode.
275 }
276 else
277 {
278 pTmr->cmsCountdown = Elapse;
279 pTmr->cmsRate = Elapse;
280 }
281
282 ASSERT(MasterTimer != NULL);
283 // Start the timer thread!
284 if (pTmr == FirstpTmr)
285 KeSetTimer(MasterTimer, DueTime, NULL);
286
287 return Ret;
288 }
289
290 //
291 // Process win32k system timers.
292 //
293 VOID
294 CALLBACK
295 SystemTimerProc(HWND hwnd,
296 UINT uMsg,
297 UINT_PTR idEvent,
298 DWORD dwTime)
299 {
300 DPRINT( "Timer Running!\n" );
301 }
302
303 VOID
304 FASTCALL
305 StartTheTimers(VOID)
306 {
307 // Need to start gdi syncro timers then start timer with Hang App proc
308 // that calles Idle process so the screen savers will know to run......
309 IntSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
310 }
311
312 UINT_PTR
313 FASTCALL
314 SystemTimerSet( PWINDOW_OBJECT Window,
315 UINT_PTR nIDEvent,
316 UINT uElapse,
317 TIMERPROC lpTimerFunc)
318 {
319 if (Window && Window->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
320 {
321 SetLastWin32Error(ERROR_ACCESS_DENIED);
322 return 0;
323 }
324 return IntSetTimer( Window, nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM);
325 }
326
327 BOOL
328 FASTCALL
329 PostTimerMessages(PWINDOW_OBJECT Window)
330 {
331 PLIST_ENTRY pLE;
332 PUSER_MESSAGE_QUEUE ThreadQueue;
333 MSG Msg;
334 PTHREADINFO pti;
335 BOOL Hit = FALSE;
336 PTIMER pTmr = FirstpTmr;
337
338 if (!pTmr) return FALSE;
339
340 pti = PsGetCurrentThreadWin32Thread();
341 ThreadQueue = pti->MessageQueue;
342
343 TimerEnterExclusive();
344
345 do
346 {
347 if ( (pTmr->flags & TMRF_READY) &&
348 (pTmr->pti == pti) &&
349 ((pTmr->pWnd == Window) || (Window == NULL)) )
350 {
351 Msg.hwnd = (pTmr->pWnd) ? pTmr->pWnd->hSelf : 0;
352 Msg.message = (pTmr->flags & TMRF_SYSTEM) ? WM_SYSTIMER : WM_TIMER;
353 Msg.wParam = (WPARAM) pTmr->nID;
354 Msg.lParam = (LPARAM) pTmr->pfn;
355
356 MsqPostMessage(ThreadQueue, &Msg, FALSE, QS_TIMER);
357 pTmr->flags &= ~TMRF_READY;
358 ThreadQueue->WakeMask = ~QS_TIMER;
359 Hit = TRUE;
360 break;
361 }
362
363 pLE = pTmr->ptmrList.Flink;
364 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
365 } while (pTmr != FirstpTmr);
366
367 TimerLeave();
368
369 return Hit;
370 }
371
372 VOID
373 FASTCALL
374 ProcessTimers(VOID)
375 {
376 LARGE_INTEGER TickCount, DueTime;
377 LONG Time;
378 PLIST_ENTRY pLE;
379 PTIMER pTmr = FirstpTmr;
380 LONG TimerCount = 0;
381
382 if (!pTmr) return;
383
384 TimerEnterExclusive();
385
386 KeQueryTickCount(&TickCount);
387 Time = MsqCalculateMessageTime(&TickCount);
388
389 DueTime.QuadPart = (LONGLONG)(-500000);
390
391 do
392 {
393 TimerCount++;
394 if (pTmr->flags & TMRF_WAITING)
395 {
396 pLE = pTmr->ptmrList.Flink;
397 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
398 continue;
399 }
400
401 if (pTmr->flags & TMRF_INIT)
402 {
403 pTmr->flags &= ~TMRF_INIT; // Skip this run.
404 }
405 else
406 {
407 if (pTmr->cmsCountdown < 0)
408 {
409 ASSERT(pTmr->pti);
410 if ((!(pTmr->flags & TMRF_READY)) && (!(pTmr->pti->TIF_flags & TIF_INCLEANUP)))
411 {
412 if (pTmr->flags & TMRF_ONESHOT)
413 pTmr->flags |= TMRF_WAITING;
414
415 if (pTmr->flags & TMRF_RIT)
416 {
417 // Hard coded call here, inside raw input thread.
418 pTmr->pfn(NULL, WM_SYSTIMER, pTmr->nID, (LPARAM)pTmr);
419 }
420 else
421 {
422 pTmr->flags |= TMRF_READY; // Set timer ready to be ran.
423 // Set thread message queue for this timer.
424 if (pTmr->pti->MessageQueue)
425 { // Wakeup thread
426 ASSERT(pTmr->pti->MessageQueue->NewMessages != NULL);
427 KeSetEvent(pTmr->pti->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
428 }
429 }
430 }
431 pTmr->cmsCountdown = pTmr->cmsRate;
432 }
433 else
434 pTmr->cmsCountdown -= Time - TimeLast;
435 }
436
437 pLE = pTmr->ptmrList.Flink;
438 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
439 } while (pTmr != FirstpTmr);
440
441 // Restart the timer thread!
442 ASSERT(MasterTimer != NULL);
443 KeSetTimer(MasterTimer, DueTime, NULL);
444
445 TimeLast = Time;
446
447 TimerLeave();
448 DPRINT("TimerCount = %d\n", TimerCount);
449 }
450
451 BOOL FASTCALL
452 DestroyTimersForWindow(PTHREADINFO pti, PWINDOW_OBJECT Window)
453 {
454 PLIST_ENTRY pLE;
455 PTIMER pTmr = FirstpTmr;
456 BOOL TimersRemoved = FALSE;
457
458 if ((FirstpTmr == NULL) || (Window == NULL))
459 return FALSE;
460
461 TimerEnterExclusive();
462
463 do
464 {
465 if ((pTmr) && (pTmr->pti == pti) && (pTmr->pWnd == Window))
466 {
467 TimersRemoved = RemoveTimer(pTmr);
468 }
469 pLE = pTmr->ptmrList.Flink;
470 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
471 } while (pTmr != FirstpTmr);
472
473 TimerLeave();
474
475 return TimersRemoved;
476 }
477
478 BOOL FASTCALL
479 DestroyTimersForThread(PTHREADINFO pti)
480 {
481 PLIST_ENTRY pLE;
482 PTIMER pTmr = FirstpTmr;
483 BOOL TimersRemoved = FALSE;
484
485 if (FirstpTmr == NULL)
486 return FALSE;
487
488 TimerEnterExclusive();
489
490 do
491 {
492 if ((pTmr) && (pTmr->pti == pti))
493 {
494 TimersRemoved = RemoveTimer(pTmr);
495 }
496 pLE = pTmr->ptmrList.Flink;
497 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
498 } while (pTmr != FirstpTmr);
499
500 TimerLeave();
501
502 return TimersRemoved;
503 }
504
505 BOOL FASTCALL
506 IntKillTimer(PWINDOW_OBJECT Window, UINT_PTR IDEvent, BOOL SystemTimer)
507 {
508 PTIMER pTmr = NULL;
509 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
510 Window, IDEvent, SystemTimer ? "TRUE" : "FALSE");
511
512 if ((Window) && (IDEvent == 0))
513 IDEvent = 1;
514
515 pTmr = FindTimer(Window, IDEvent, SystemTimer ? TMRF_SYSTEM : 0);
516
517 if (pTmr)
518 {
519 TimerEnterExclusive();
520 RemoveTimer(pTmr);
521 TimerLeave();
522 }
523
524 return pTmr ? TRUE : FALSE;
525 }
526
527 NTSTATUS FASTCALL
528 InitTimerImpl(VOID)
529 {
530 ULONG BitmapBytes;
531
532 ExInitializeFastMutex(&Mutex);
533
534 BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
535 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
536 if (WindowLessTimersBitMapBuffer == NULL)
537 {
538 return STATUS_UNSUCCESSFUL;
539 }
540
541 RtlInitializeBitMap(&WindowLessTimersBitMap,
542 WindowLessTimersBitMapBuffer,
543 BitmapBytes * 8);
544
545 /* yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
546 RtlClearAllBits(&WindowLessTimersBitMap);
547
548 return STATUS_SUCCESS;
549 }
550
551 UINT_PTR
552 APIENTRY
553 NtUserSetTimer
554 (
555 HWND hWnd,
556 UINT_PTR nIDEvent,
557 UINT uElapse,
558 TIMERPROC lpTimerFunc
559 )
560 {
561 PWINDOW_OBJECT Window;
562 DECLARE_RETURN(UINT_PTR);
563
564 DPRINT("Enter NtUserSetTimer\n");
565 UserEnterExclusive();
566 Window = UserGetWindowObject(hWnd);
567 UserLeave();
568
569 RETURN(IntSetTimer(Window, nIDEvent, uElapse, lpTimerFunc, TMRF_TIFROMWND));
570
571 CLEANUP:
572 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_);
573
574 END_CLEANUP;
575 }
576
577
578 BOOL
579 APIENTRY
580 NtUserKillTimer
581 (
582 HWND hWnd,
583 UINT_PTR uIDEvent
584 )
585 {
586 PWINDOW_OBJECT Window;
587 DECLARE_RETURN(BOOL);
588
589 DPRINT("Enter NtUserKillTimer\n");
590 UserEnterExclusive();
591 Window = UserGetWindowObject(hWnd);
592 UserLeave();
593
594 RETURN(IntKillTimer(Window, uIDEvent, FALSE));
595
596 CLEANUP:
597 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_);
598 END_CLEANUP;
599 }
600
601
602 UINT_PTR
603 APIENTRY
604 NtUserSetSystemTimer(
605 HWND hWnd,
606 UINT_PTR nIDEvent,
607 UINT uElapse,
608 TIMERPROC lpTimerFunc
609 )
610 {
611 DECLARE_RETURN(UINT_PTR);
612
613 DPRINT("Enter NtUserSetSystemTimer\n");
614
615 // This is wrong, lpTimerFunc is NULL!
616 RETURN(IntSetTimer(UserGetWindowObject(hWnd), nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM));
617
618 CLEANUP:
619 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_);
620 END_CLEANUP;
621 }
622
623
624 /* EOF */