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 */ 718