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 403 pti = PsGetCurrentThreadWin32Thread(); 404 405 TimerEnterExclusive(); 406 pLE = TimersListHead.Flink; 407 while(pLE != &TimersListHead) 408 { 409 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList); 410 if ( (pTmr->flags & TMRF_READY) && 411 (pTmr->pti == pti) && 412 ((pTmr->pWnd == Window) || (Window == NULL)) ) 413 { 414 Msg.hwnd = (pTmr->pWnd) ? pTmr->pWnd->head.h : 0; 415 Msg.message = (pTmr->flags & TMRF_SYSTEM) ? WM_SYSTIMER : WM_TIMER; 416 Msg.wParam = (WPARAM) pTmr->nID; 417 Msg.lParam = (LPARAM) pTmr->pfn; 418 Msg.time = EngGetTickCount32(); 419 // Fix all wine win:test_GetMessagePos WM_TIMER tests. See CORE-10867. 420 Msg.pt = gpsi->ptCursor; 421 422 MsqPostMessage(pti, &Msg, FALSE, (QS_POSTMESSAGE|QS_ALLPOSTMESSAGE), 0, 0); 423 pTmr->flags &= ~TMRF_READY; 424 ClearMsgBitsMask(pti, QS_TIMER); 425 Hit = TRUE; 426 // Now move this entry to the end of the list so it will not be 427 // called again in the next msg loop. 428 if (pLE != &TimersListHead) 429 { 430 RemoveEntryList(&pTmr->ptmrList); 431 InsertTailList(&TimersListHead, &pTmr->ptmrList); 432 } 433 break; 434 } 435 436 pLE = pLE->Flink; 437 } 438 439 TimerLeave(); 440 441 return Hit; 442 } 443 444 VOID 445 FASTCALL 446 ProcessTimers(VOID) 447 { 448 LARGE_INTEGER DueTime; 449 LONG Time; 450 PLIST_ENTRY pLE; 451 PTIMER pTmr; 452 LONG TimerCount = 0; 453 454 TimerEnterExclusive(); 455 pLE = TimersListHead.Flink; 456 Time = EngGetTickCount32(); 457 458 DueTime.QuadPart = (LONGLONG)(-97656); // 1024hz .9765625 ms set to 10.0 ms 459 460 while(pLE != &TimersListHead) 461 { 462 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList); 463 TimerCount++; 464 if (pTmr->flags & TMRF_WAITING) 465 { 466 pLE = pTmr->ptmrList.Flink; 467 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList); 468 continue; 469 } 470 471 if (pTmr->flags & TMRF_INIT) 472 { 473 pTmr->flags &= ~TMRF_INIT; // Skip this run. 474 } 475 else 476 { 477 if (pTmr->cmsCountdown < 0) 478 { 479 ASSERT(pTmr->pti); 480 if ((!(pTmr->flags & TMRF_READY)) && (!(pTmr->pti->TIF_flags & TIF_INCLEANUP))) 481 { 482 if (pTmr->flags & TMRF_ONESHOT) 483 pTmr->flags |= TMRF_WAITING; 484 485 if (pTmr->flags & TMRF_RIT) 486 { 487 // Hard coded call here, inside raw input thread. 488 pTmr->pfn(NULL, WM_SYSTIMER, pTmr->nID, (LPARAM)pTmr); 489 } 490 else 491 { 492 pTmr->flags |= TMRF_READY; // Set timer ready to be ran. 493 // Set thread message queue for this timer. 494 if (pTmr->pti) 495 { // Wakeup thread 496 pTmr->pti->cTimersReady++; 497 ASSERT(pTmr->pti->pEventQueueServer != NULL); 498 MsqWakeQueue(pTmr->pti, QS_TIMER, TRUE); 499 } 500 } 501 } 502 pTmr->cmsCountdown = pTmr->cmsRate; 503 } 504 else 505 pTmr->cmsCountdown -= Time - TimeLast; 506 } 507 508 pLE = pLE->Flink; 509 } 510 511 // Restart the timer thread! 512 ASSERT(MasterTimer != NULL); 513 KeSetTimer(MasterTimer, DueTime, NULL); 514 515 TimeLast = Time; 516 517 TimerLeave(); 518 TRACE("TimerCount = %d\n", TimerCount); 519 } 520 521 BOOL FASTCALL 522 DestroyTimersForWindow(PTHREADINFO pti, PWND Window) 523 { 524 PLIST_ENTRY pLE; 525 PTIMER pTmr; 526 BOOL TimersRemoved = FALSE; 527 528 if (Window == NULL) 529 return FALSE; 530 531 TimerEnterExclusive(); 532 pLE = TimersListHead.Flink; 533 while(pLE != &TimersListHead) 534 { 535 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList); 536 pLE = pLE->Flink; /* get next timer list entry before current timer is removed */ 537 if ((pTmr) && (pTmr->pti == pti) && (pTmr->pWnd == Window)) 538 { 539 TimersRemoved = RemoveTimer(pTmr); 540 } 541 } 542 543 TimerLeave(); 544 545 return TimersRemoved; 546 } 547 548 BOOL FASTCALL 549 DestroyTimersForThread(PTHREADINFO pti) 550 { 551 PLIST_ENTRY pLE = TimersListHead.Flink; 552 PTIMER pTmr; 553 BOOL TimersRemoved = FALSE; 554 555 TimerEnterExclusive(); 556 557 while(pLE != &TimersListHead) 558 { 559 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList); 560 pLE = pLE->Flink; /* get next timer list entry before current timer is removed */ 561 if ((pTmr) && (pTmr->pti == pti)) 562 { 563 TimersRemoved = RemoveTimer(pTmr); 564 } 565 } 566 567 TimerLeave(); 568 569 return TimersRemoved; 570 } 571 572 BOOL FASTCALL 573 IntKillTimer(PWND Window, UINT_PTR IDEvent, BOOL SystemTimer) 574 { 575 PTIMER pTmr = NULL; 576 TRACE("IntKillTimer Window %p id %uI systemtimer %s\n", 577 Window, IDEvent, SystemTimer ? "TRUE" : "FALSE"); 578 579 TimerEnterExclusive(); 580 pTmr = FindTimer(Window, IDEvent, SystemTimer ? TMRF_SYSTEM : 0); 581 582 if (pTmr) 583 { 584 RemoveTimer(pTmr); 585 } 586 TimerLeave(); 587 588 return pTmr ? TRUE : FALSE; 589 } 590 591 CODE_SEG("INIT") 592 NTSTATUS 593 NTAPI 594 InitTimerImpl(VOID) 595 { 596 ULONG BitmapBytes; 597 598 /* Allocate FAST_MUTEX from non paged pool */ 599 Mutex = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC); 600 if (!Mutex) 601 { 602 return STATUS_INSUFFICIENT_RESOURCES; 603 } 604 605 ExInitializeFastMutex(Mutex); 606 607 BitmapBytes = ALIGN_UP_BY(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8; 608 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(NonPagedPool, BitmapBytes, TAG_TIMERBMP); 609 if (WindowLessTimersBitMapBuffer == NULL) 610 { 611 return STATUS_UNSUCCESSFUL; 612 } 613 614 RtlInitializeBitMap(&WindowLessTimersBitMap, 615 WindowLessTimersBitMapBuffer, 616 BitmapBytes * 8); 617 618 /* Yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */ 619 RtlClearAllBits(&WindowLessTimersBitMap); 620 621 ExInitializeResourceLite(&TimerLock); 622 InitializeListHead(&TimersListHead); 623 624 return STATUS_SUCCESS; 625 } 626 627 UINT_PTR 628 APIENTRY 629 NtUserSetTimer 630 ( 631 HWND hWnd, 632 UINT_PTR nIDEvent, 633 UINT uElapse, 634 TIMERPROC lpTimerFunc 635 ) 636 { 637 PWND Window = NULL; 638 UINT_PTR ret; 639 640 TRACE("Enter NtUserSetTimer\n"); 641 UserEnterExclusive(); 642 if (hWnd) Window = UserGetWindowObject(hWnd); 643 644 ret = IntSetTimer(Window, nIDEvent, uElapse, lpTimerFunc, TMRF_TIFROMWND); 645 646 UserLeave(); 647 TRACE("Leave NtUserSetTimer, ret=%u\n", ret); 648 649 return ret; 650 } 651 652 653 BOOL 654 APIENTRY 655 NtUserKillTimer 656 ( 657 HWND hWnd, 658 UINT_PTR uIDEvent 659 ) 660 { 661 PWND Window = NULL; 662 BOOL ret; 663 664 TRACE("Enter NtUserKillTimer\n"); 665 UserEnterExclusive(); 666 if (hWnd) Window = UserGetWindowObject(hWnd); 667 668 ret = IntKillTimer(Window, uIDEvent, FALSE); 669 670 UserLeave(); 671 672 TRACE("Leave NtUserKillTimer, ret=%i\n", ret); 673 return ret; 674 } 675 676 677 UINT_PTR 678 APIENTRY 679 NtUserSetSystemTimer( 680 HWND hWnd, 681 UINT_PTR nIDEvent, 682 UINT uElapse, 683 TIMERPROC lpTimerFunc 684 ) 685 { 686 UINT_PTR ret; 687 688 UserEnterExclusive(); 689 TRACE("Enter NtUserSetSystemTimer\n"); 690 691 ret = IntSetTimer(UserGetWindowObject(hWnd), nIDEvent, uElapse, NULL, TMRF_SYSTEM); 692 693 UserLeave(); 694 695 TRACE("Leave NtUserSetSystemTimer, ret=%u\n", ret); 696 return ret; 697 } 698 699 BOOL 700 APIENTRY 701 NtUserValidateTimerCallback( 702 LPARAM lParam) 703 { 704 BOOL Ret = FALSE; 705 706 UserEnterShared(); 707 708 Ret = ValidateTimerCallback(PsGetCurrentThreadWin32Thread(), lParam); 709 710 UserLeave(); 711 return Ret; 712 } 713 714 /* EOF */ 715