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