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