[WIN32SS][NTUSER] Fix PostTimeMessage message time
[reactos.git] / win32ss / user / ntuser / timer.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window timers messages
5 * FILE: win32ss/user/ntuser/timer.c
6 * PROGRAMER: Gunnar
7 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
8 * Michael Martin (michael.martin@reactos.org)
9 */
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserTimer);
13
14 /* GLOBALS *******************************************************************/
15
16 static LIST_ENTRY TimersListHead;
17 static LONG TimeLast = 0;
18
19 /* Windows 2000 has room for 32768 window-less timers */
20 #define NUM_WINDOW_LESS_TIMERS 32768
21
22 static PFAST_MUTEX Mutex;
23 static RTL_BITMAP WindowLessTimersBitMap;
24 static PVOID WindowLessTimersBitMapBuffer;
25 static ULONG HintIndex = 1;
26
27 ERESOURCE TimerLock;
28
29 #define IntLockWindowlessTimerBitmap() \
30 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(Mutex)
31
32 #define IntUnlockWindowlessTimerBitmap() \
33 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(Mutex)
34
35 #define TimerEnterExclusive() \
36 { \
37 KeEnterCriticalRegion(); \
38 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
39 }
40
41 #define TimerLeave() \
42 { \
43 ExReleaseResourceLite(&TimerLock); \
44 KeLeaveCriticalRegion(); \
45 }
46
47
48 /* FUNCTIONS *****************************************************************/
49 static
50 PTIMER
51 FASTCALL
52 CreateTimer(VOID)
53 {
54 HANDLE Handle;
55 PTIMER Ret = NULL;
56
57 Ret = UserCreateObject(gHandleTable, NULL, NULL, &Handle, TYPE_TIMER, sizeof(TIMER));
58 if (Ret)
59 {
60 Ret->head.h = Handle;
61 InsertTailList(&TimersListHead, &Ret->ptmrList);
62 }
63
64 return Ret;
65 }
66
67 static
68 BOOL
69 FASTCALL
70 RemoveTimer(PTIMER pTmr)
71 {
72 BOOL Ret = FALSE;
73 if (pTmr)
74 {
75 /* Set the flag, it will be removed when ready */
76 RemoveEntryList(&pTmr->ptmrList);
77 if ((pTmr->pWnd == NULL) && (!(pTmr->flags & TMRF_SYSTEM))) // System timers are reusable.
78 {
79 UINT_PTR IDEvent;
80
81 IDEvent = NUM_WINDOW_LESS_TIMERS - pTmr->nID;
82 IntLockWindowlessTimerBitmap();
83 RtlClearBit(&WindowLessTimersBitMap, IDEvent);
84 IntUnlockWindowlessTimerBitmap();
85 }
86 UserDereferenceObject(pTmr);
87 Ret = UserDeleteObject( UserHMGetHandle(pTmr), TYPE_TIMER);
88 }
89 if (!Ret) ERR("Warning: Unable to delete timer\n");
90
91 return Ret;
92 }
93
94 PTIMER
95 FASTCALL
96 FindTimer(PWND Window,
97 UINT_PTR nID,
98 UINT flags)
99 {
100 PLIST_ENTRY pLE;
101 PTIMER pTmr, RetTmr = NULL;
102
103 TimerEnterExclusive();
104 pLE = TimersListHead.Flink;
105 while (pLE != &TimersListHead)
106 {
107 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
108
109 if ( pTmr->nID == nID &&
110 pTmr->pWnd == Window &&
111 (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) == (flags & (TMRF_SYSTEM|TMRF_RIT)))
112 {
113 RetTmr = pTmr;
114 break;
115 }
116
117 pLE = pLE->Flink;
118 }
119 TimerLeave();
120
121 return RetTmr;
122 }
123
124 PTIMER
125 FASTCALL
126 FindSystemTimer(PMSG pMsg)
127 {
128 PLIST_ENTRY pLE;
129 PTIMER pTmr = NULL;
130
131 TimerEnterExclusive();
132 pLE = TimersListHead.Flink;
133 while (pLE != &TimersListHead)
134 {
135 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
136
137 if ( pMsg->lParam == (LPARAM)pTmr->pfn &&
138 (pTmr->flags & TMRF_SYSTEM) )
139 break;
140
141 pLE = pLE->Flink;
142 }
143 TimerLeave();
144
145 return pTmr;
146 }
147
148 BOOL
149 FASTCALL
150 ValidateTimerCallback(PTHREADINFO pti,
151 LPARAM lParam)
152 {
153 PLIST_ENTRY pLE;
154 BOOL Ret = FALSE;
155 PTIMER pTmr;
156
157 TimerEnterExclusive();
158 pLE = TimersListHead.Flink;
159 while (pLE != &TimersListHead)
160 {
161 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
162 if ( (lParam == (LPARAM)pTmr->pfn) &&
163 !(pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) &&
164 (pTmr->pti->ppi == pti->ppi) )
165 {
166 Ret = TRUE;
167 break;
168 }
169 pLE = pLE->Flink;
170 }
171 TimerLeave();
172
173 return Ret;
174 }
175
176 UINT_PTR FASTCALL
177 IntSetTimer( PWND Window,
178 UINT_PTR IDEvent,
179 UINT Elapse,
180 TIMERPROC TimerFunc,
181 INT Type)
182 {
183 PTIMER pTmr;
184 UINT Ret = IDEvent;
185 LARGE_INTEGER DueTime;
186 DueTime.QuadPart = (LONGLONG)(-97656); // 1024hz .9765625 ms set to 10.0 ms
187
188 #if 0
189 /* Windows NT/2k/XP behaviour */
190 if (Elapse > USER_TIMER_MAXIMUM)
191 {
192 TRACE("Adjusting uElapse\n");
193 Elapse = 1;
194 }
195 #else
196 /* Windows XP SP2 and Windows Server 2003 behaviour */
197 if (Elapse > USER_TIMER_MAXIMUM)
198 {
199 TRACE("Adjusting uElapse\n");
200 Elapse = USER_TIMER_MAXIMUM;
201 }
202 #endif
203
204 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
205 if (Elapse < USER_TIMER_MINIMUM)
206 {
207 TRACE("Adjusting uElapse\n");
208 Elapse = USER_TIMER_MINIMUM; // 1024hz .9765625 ms, set to 10.0 ms (+/-)1 ms
209 }
210
211 /* Passing an IDEvent of 0 and the SetTimer returns 1.
212 It will create the timer with an ID of 0 */
213 if ((Window) && (IDEvent == 0))
214 Ret = 1;
215
216 pTmr = FindTimer(Window, IDEvent, Type);
217
218 if ((!pTmr) && (Window == NULL) && (!(Type & TMRF_SYSTEM)))
219 {
220 IntLockWindowlessTimerBitmap();
221
222 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
223
224 if (IDEvent == (UINT_PTR) -1)
225 {
226 IntUnlockWindowlessTimerBitmap();
227 ERR("Unable to find a free window-less timer id\n");
228 EngSetLastError(ERROR_NO_SYSTEM_RESOURCES);
229 ASSERT(FALSE);
230 return 0;
231 }
232
233 IDEvent = NUM_WINDOW_LESS_TIMERS - IDEvent;
234 Ret = IDEvent;
235
236 IntUnlockWindowlessTimerBitmap();
237 }
238
239 if (!pTmr)
240 {
241 pTmr = CreateTimer();
242 if (!pTmr) return 0;
243
244 if (Window && (Type & TMRF_TIFROMWND))
245 pTmr->pti = Window->head.pti->pEThread->Tcb.Win32Thread;
246 else
247 {
248 if (Type & TMRF_RIT)
249 pTmr->pti = ptiRawInput;
250 else
251 pTmr->pti = PsGetCurrentThreadWin32Thread();
252 }
253
254 pTmr->pWnd = Window;
255 pTmr->cmsCountdown = Elapse;
256 pTmr->cmsRate = Elapse;
257 pTmr->pfn = TimerFunc;
258 pTmr->nID = IDEvent;
259 pTmr->flags = Type|TMRF_INIT;
260 }
261 else
262 {
263 pTmr->cmsCountdown = Elapse;
264 pTmr->cmsRate = Elapse;
265 }
266
267 ASSERT(MasterTimer != NULL);
268 // Start the timer thread!
269 if (TimersListHead.Flink == TimersListHead.Blink) // There is only one timer
270 KeSetTimer(MasterTimer, DueTime, NULL);
271
272 return Ret;
273 }
274
275 //
276 // Process win32k system timers.
277 //
278 VOID
279 CALLBACK
280 SystemTimerProc(HWND hwnd,
281 UINT uMsg,
282 UINT_PTR idEvent,
283 DWORD dwTime)
284 {
285 PDESKTOP pDesk;
286 PWND pWnd = NULL;
287
288 if (hwnd)
289 {
290 pWnd = UserGetWindowObject(hwnd);
291 if (!pWnd)
292 {
293 ERR("System Timer Proc has invalid window handle! %p Id: %u\n", hwnd, idEvent);
294 return;
295 }
296 }
297 else
298 {
299 TRACE( "Windowless Timer Running!\n" );
300 return;
301 }
302
303 switch (idEvent)
304 {
305 /*
306 Used in NtUserTrackMouseEvent.
307 */
308 case ID_EVENT_SYSTIMER_MOUSEHOVER:
309 {
310 POINT Point;
311 UINT Msg;
312 WPARAM wParam;
313
314 pDesk = pWnd->head.rpdesk;
315 if ( pDesk->dwDTFlags & DF_TME_HOVER &&
316 pWnd == pDesk->spwndTrack )
317 {
318 Point = gpsi->ptCursor;
319 if ( RECTL_bPointInRect(&pDesk->rcMouseHover, Point.x, Point.y) )
320 {
321 if (pDesk->htEx == HTCLIENT) // In a client area.
322 {
323 wParam = MsqGetDownKeyState(pWnd->head.pti->MessageQueue);
324 Msg = WM_MOUSEHOVER;
325
326 if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
327 {
328 Point.x = pWnd->rcClient.right - Point.x - 1;
329 }
330 else
331 Point.x -= pWnd->rcClient.left;
332 Point.y -= pWnd->rcClient.top;
333 }
334 else
335 {
336 wParam = pDesk->htEx; // Need to support all HTXYZ hits.
337 Msg = WM_NCMOUSEHOVER;
338 }
339 TRACE("Generating WM_NCMOUSEHOVER\n");
340 UserPostMessage(hwnd, Msg, wParam, MAKELPARAM(Point.x, Point.y));
341 pDesk->dwDTFlags &= ~DF_TME_HOVER;
342 break; // Kill this timer.
343 }
344 }
345 }
346 return; // Not this window so just return.
347
348 case ID_EVENT_SYSTIMER_FLASHWIN:
349 {
350 FLASHWINFO fwi =
351 {sizeof(FLASHWINFO),
352 UserHMGetHandle(pWnd),
353 FLASHW_SYSTIMER,0,0};
354
355 IntFlashWindowEx(pWnd, &fwi);
356 }
357 return;
358
359 default:
360 ERR("System Timer Proc invalid id %u!\n", idEvent);
361 break;
362 }
363 IntKillTimer(pWnd, idEvent, TRUE);
364 }
365
366 VOID
367 FASTCALL
368 StartTheTimers(VOID)
369 {
370 // Need to start gdi syncro timers then start timer with Hang App proc
371 // that calles Idle process so the screen savers will know to run......
372 IntSetTimer(NULL, 0, 1000, HungAppSysTimerProc, TMRF_RIT);
373 // Test Timers
374 // IntSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
375 }
376
377 UINT_PTR
378 FASTCALL
379 SystemTimerSet( PWND Window,
380 UINT_PTR nIDEvent,
381 UINT uElapse,
382 TIMERPROC lpTimerFunc)
383 {
384 if (Window && Window->head.pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
385 {
386 EngSetLastError(ERROR_ACCESS_DENIED);
387 TRACE("SysemTimerSet: Access Denied!\n");
388 return 0;
389 }
390 return IntSetTimer( Window, nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM);
391 }
392
393 BOOL
394 FASTCALL
395 PostTimerMessages(PWND Window)
396 {
397 PLIST_ENTRY pLE;
398 MSG Msg;
399 PTHREADINFO pti;
400 BOOL Hit = FALSE;
401 PTIMER pTmr;
402 LARGE_INTEGER TickCount;
403
404 pti = PsGetCurrentThreadWin32Thread();
405
406 TimerEnterExclusive();
407 pLE = TimersListHead.Flink;
408 while(pLE != &TimersListHead)
409 {
410 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
411 if ( (pTmr->flags & TMRF_READY) &&
412 (pTmr->pti == pti) &&
413 ((pTmr->pWnd == Window) || (Window == NULL)) )
414 {
415 KeQueryTickCount(&TickCount);
416
417 Msg.hwnd = (pTmr->pWnd) ? pTmr->pWnd->head.h : 0;
418 Msg.message = (pTmr->flags & TMRF_SYSTEM) ? WM_SYSTIMER : WM_TIMER;
419 Msg.wParam = (WPARAM) pTmr->nID;
420 Msg.lParam = (LPARAM) pTmr->pfn;
421 Msg.time = MsqCalculateMessageTime(&TickCount);
422 // Fix all wine win:test_GetMessagePos WM_TIMER tests. See CORE-10867.
423 Msg.pt = gpsi->ptCursor;
424
425 MsqPostMessage(pti, &Msg, FALSE, (QS_POSTMESSAGE|QS_ALLPOSTMESSAGE), 0, 0);
426 pTmr->flags &= ~TMRF_READY;
427 ClearMsgBitsMask(pti, QS_TIMER);
428 Hit = TRUE;
429 // Now move this entry to the end of the list so it will not be
430 // called again in the next msg loop.
431 if (pLE != &TimersListHead)
432 {
433 RemoveEntryList(&pTmr->ptmrList);
434 InsertTailList(&TimersListHead, &pTmr->ptmrList);
435 }
436 break;
437 }
438
439 pLE = pLE->Flink;
440 }
441
442 TimerLeave();
443
444 return Hit;
445 }
446
447 VOID
448 FASTCALL
449 ProcessTimers(VOID)
450 {
451 LARGE_INTEGER TickCount, DueTime;
452 LONG Time;
453 PLIST_ENTRY pLE;
454 PTIMER pTmr;
455 LONG TimerCount = 0;
456
457 TimerEnterExclusive();
458 pLE = TimersListHead.Flink;
459 KeQueryTickCount(&TickCount);
460 Time = MsqCalculateMessageTime(&TickCount);
461
462 DueTime.QuadPart = (LONGLONG)(-97656); // 1024hz .9765625 ms set to 10.0 ms
463
464 while(pLE != &TimersListHead)
465 {
466 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
467 TimerCount++;
468 if (pTmr->flags & TMRF_WAITING)
469 {
470 pLE = pTmr->ptmrList.Flink;
471 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
472 continue;
473 }
474
475 if (pTmr->flags & TMRF_INIT)
476 {
477 pTmr->flags &= ~TMRF_INIT; // Skip this run.
478 }
479 else
480 {
481 if (pTmr->cmsCountdown < 0)
482 {
483 ASSERT(pTmr->pti);
484 if ((!(pTmr->flags & TMRF_READY)) && (!(pTmr->pti->TIF_flags & TIF_INCLEANUP)))
485 {
486 if (pTmr->flags & TMRF_ONESHOT)
487 pTmr->flags |= TMRF_WAITING;
488
489 if (pTmr->flags & TMRF_RIT)
490 {
491 // Hard coded call here, inside raw input thread.
492 pTmr->pfn(NULL, WM_SYSTIMER, pTmr->nID, (LPARAM)pTmr);
493 }
494 else
495 {
496 pTmr->flags |= TMRF_READY; // Set timer ready to be ran.
497 // Set thread message queue for this timer.
498 if (pTmr->pti)
499 { // Wakeup thread
500 pTmr->pti->cTimersReady++;
501 ASSERT(pTmr->pti->pEventQueueServer != NULL);
502 MsqWakeQueue(pTmr->pti, QS_TIMER, TRUE);
503 }
504 }
505 }
506 pTmr->cmsCountdown = pTmr->cmsRate;
507 }
508 else
509 pTmr->cmsCountdown -= Time - TimeLast;
510 }
511
512 pLE = pLE->Flink;
513 }
514
515 // Restart the timer thread!
516 ASSERT(MasterTimer != NULL);
517 KeSetTimer(MasterTimer, DueTime, NULL);
518
519 TimeLast = Time;
520
521 TimerLeave();
522 TRACE("TimerCount = %d\n", TimerCount);
523 }
524
525 BOOL FASTCALL
526 DestroyTimersForWindow(PTHREADINFO pti, PWND Window)
527 {
528 PLIST_ENTRY pLE;
529 PTIMER pTmr;
530 BOOL TimersRemoved = FALSE;
531
532 if (Window == NULL)
533 return FALSE;
534
535 TimerEnterExclusive();
536 pLE = TimersListHead.Flink;
537 while(pLE != &TimersListHead)
538 {
539 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
540 pLE = pLE->Flink; /* get next timer list entry before current timer is removed */
541 if ((pTmr) && (pTmr->pti == pti) && (pTmr->pWnd == Window))
542 {
543 TimersRemoved = RemoveTimer(pTmr);
544 }
545 }
546
547 TimerLeave();
548
549 return TimersRemoved;
550 }
551
552 BOOL FASTCALL
553 DestroyTimersForThread(PTHREADINFO pti)
554 {
555 PLIST_ENTRY pLE = TimersListHead.Flink;
556 PTIMER pTmr;
557 BOOL TimersRemoved = FALSE;
558
559 TimerEnterExclusive();
560
561 while(pLE != &TimersListHead)
562 {
563 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
564 pLE = pLE->Flink; /* get next timer list entry before current timer is removed */
565 if ((pTmr) && (pTmr->pti == pti))
566 {
567 TimersRemoved = RemoveTimer(pTmr);
568 }
569 }
570
571 TimerLeave();
572
573 return TimersRemoved;
574 }
575
576 BOOL FASTCALL
577 IntKillTimer(PWND Window, UINT_PTR IDEvent, BOOL SystemTimer)
578 {
579 PTIMER pTmr = NULL;
580 TRACE("IntKillTimer Window %p id %uI systemtimer %s\n",
581 Window, IDEvent, SystemTimer ? "TRUE" : "FALSE");
582
583 TimerEnterExclusive();
584 pTmr = FindTimer(Window, IDEvent, SystemTimer ? TMRF_SYSTEM : 0);
585
586 if (pTmr)
587 {
588 RemoveTimer(pTmr);
589 }
590 TimerLeave();
591
592 return pTmr ? TRUE : FALSE;
593 }
594
595 INIT_FUNCTION
596 NTSTATUS
597 NTAPI
598 InitTimerImpl(VOID)
599 {
600 ULONG BitmapBytes;
601
602 /* Allocate FAST_MUTEX from non paged pool */
603 Mutex = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
604 if (!Mutex)
605 {
606 return STATUS_INSUFFICIENT_RESOURCES;
607 }
608
609 ExInitializeFastMutex(Mutex);
610
611 BitmapBytes = ALIGN_UP_BY(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
612 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapBytes, TAG_TIMERBMP);
613 if (WindowLessTimersBitMapBuffer == NULL)
614 {
615 return STATUS_UNSUCCESSFUL;
616 }
617
618 RtlInitializeBitMap(&WindowLessTimersBitMap,
619 WindowLessTimersBitMapBuffer,
620 BitmapBytes * 8);
621
622 /* Yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
623 RtlClearAllBits(&WindowLessTimersBitMap);
624
625 ExInitializeResourceLite(&TimerLock);
626 InitializeListHead(&TimersListHead);
627
628 return STATUS_SUCCESS;
629 }
630
631 UINT_PTR
632 APIENTRY
633 NtUserSetTimer
634 (
635 HWND hWnd,
636 UINT_PTR nIDEvent,
637 UINT uElapse,
638 TIMERPROC lpTimerFunc
639 )
640 {
641 PWND Window = NULL;
642 DECLARE_RETURN(UINT_PTR);
643
644 TRACE("Enter NtUserSetTimer\n");
645 UserEnterExclusive();
646 if (hWnd) Window = UserGetWindowObject(hWnd);
647 UserLeave();
648
649 RETURN(IntSetTimer(Window, nIDEvent, uElapse, lpTimerFunc, TMRF_TIFROMWND));
650
651 CLEANUP:
652 TRACE("Leave NtUserSetTimer, ret=%u\n", _ret_);
653
654 END_CLEANUP;
655 }
656
657
658 BOOL
659 APIENTRY
660 NtUserKillTimer
661 (
662 HWND hWnd,
663 UINT_PTR uIDEvent
664 )
665 {
666 PWND Window = NULL;
667 DECLARE_RETURN(BOOL);
668
669 TRACE("Enter NtUserKillTimer\n");
670 UserEnterExclusive();
671 if (hWnd) Window = UserGetWindowObject(hWnd);
672 UserLeave();
673
674 RETURN(IntKillTimer(Window, uIDEvent, FALSE));
675
676 CLEANUP:
677 TRACE("Leave NtUserKillTimer, ret=%i\n", _ret_);
678 END_CLEANUP;
679 }
680
681
682 UINT_PTR
683 APIENTRY
684 NtUserSetSystemTimer(
685 HWND hWnd,
686 UINT_PTR nIDEvent,
687 UINT uElapse,
688 TIMERPROC lpTimerFunc
689 )
690 {
691 DECLARE_RETURN(UINT_PTR);
692
693 TRACE("Enter NtUserSetSystemTimer\n");
694
695 RETURN(IntSetTimer(UserGetWindowObject(hWnd), nIDEvent, uElapse, NULL, TMRF_SYSTEM));
696
697 CLEANUP:
698 TRACE("Leave NtUserSetSystemTimer, ret=%u\n", _ret_);
699 END_CLEANUP;
700 }
701
702 BOOL
703 APIENTRY
704 NtUserValidateTimerCallback(
705 LPARAM lParam)
706 {
707 BOOL Ret = FALSE;
708
709 UserEnterShared();
710
711 Ret = ValidateTimerCallback(PsGetCurrentThreadWin32Thread(), lParam);
712
713 UserLeave();
714 return Ret;
715 }
716
717 /* EOF */