xref: /reactos/win32ss/user/ntuser/timer.c (revision cf955094)
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