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