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