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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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