1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Win32k subsystem 4 * PURPOSE: Window painting function 5 * FILE: win32ss/user/ntuser/painting.c 6 * PROGRAMER: Filip Navara (xnavara@volny.cz) 7 */ 8 9 #include <win32k.h> 10 DBG_DEFAULT_CHANNEL(UserPainting); 11 12 BOOL UserExtTextOutW(HDC hdc, INT x, INT y, UINT flags, PRECTL lprc, 13 LPCWSTR lpString, UINT count); 14 15 /* PRIVATE FUNCTIONS **********************************************************/ 16 17 /** 18 * @name IntIntersectWithParents 19 * 20 * Intersect window rectangle with all parent client rectangles. 21 * 22 * @param Child 23 * Pointer to child window to start intersecting from. 24 * @param WindowRect 25 * Pointer to rectangle that we want to intersect in screen 26 * coordinates on input and intersected rectangle on output (if TRUE 27 * is returned). 28 * 29 * @return 30 * If any parent is minimized or invisible or the resulting rectangle 31 * is empty then FALSE is returned. Otherwise TRUE is returned. 32 */ 33 34 BOOL FASTCALL 35 IntIntersectWithParents(PWND Child, RECTL *WindowRect) 36 { 37 PWND ParentWnd; 38 39 if (Child->ExStyle & WS_EX_REDIRECTED) 40 return TRUE; 41 42 ParentWnd = Child->spwndParent; 43 while (ParentWnd != NULL) 44 { 45 if (!(ParentWnd->style & WS_VISIBLE) || 46 (ParentWnd->style & WS_MINIMIZE) || 47 !RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient) ) 48 { 49 return FALSE; 50 } 51 52 if (ParentWnd->ExStyle & WS_EX_REDIRECTED) 53 return TRUE; 54 55 ParentWnd = ParentWnd->spwndParent; 56 } 57 58 return TRUE; 59 } 60 61 BOOL FASTCALL 62 IntValidateParents(PWND Child, BOOL Recurse) 63 { 64 RECTL ParentRect, Rect; 65 BOOL Start, Ret = TRUE; 66 PWND ParentWnd = Child; 67 PREGION Rgn = NULL; 68 69 if (ParentWnd->style & WS_CHILD) 70 { 71 do 72 ParentWnd = ParentWnd->spwndParent; 73 while (ParentWnd->style & WS_CHILD); 74 } 75 76 // No pending nonclient paints. 77 if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE; 78 79 Start = TRUE; 80 ParentWnd = Child->spwndParent; 81 while (ParentWnd) 82 { 83 if (ParentWnd->style & WS_CLIPCHILDREN) 84 break; 85 86 if (ParentWnd->hrgnUpdate != 0) 87 { 88 if (Recurse) 89 { 90 Ret = FALSE; 91 break; 92 } 93 // Start with child clipping. 94 if (Start) 95 { 96 Start = FALSE; 97 98 Rect = Child->rcWindow; 99 100 if (!IntIntersectWithParents(Child, &Rect)) break; 101 102 Rgn = IntSysCreateRectpRgnIndirect(&Rect); 103 104 if (Child->hrgnClip) 105 { 106 PREGION RgnClip = REGION_LockRgn(Child->hrgnClip); 107 IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND); 108 REGION_UnlockRgn(RgnClip); 109 } 110 } 111 112 ParentRect = ParentWnd->rcWindow; 113 114 if (!IntIntersectWithParents(ParentWnd, &ParentRect)) break; 115 116 IntInvalidateWindows( ParentWnd, 117 Rgn, 118 RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOUPDATEDIRTY); 119 } 120 ParentWnd = ParentWnd->spwndParent; 121 } 122 123 if (Rgn) REGION_Delete(Rgn); 124 125 return Ret; 126 } 127 128 /* 129 Synchronize painting to the top-level windows of other threads. 130 */ 131 VOID FASTCALL 132 IntSendSyncPaint(PWND Wnd, ULONG Flags) 133 { 134 PTHREADINFO ptiCur, ptiWnd; 135 PUSER_SENT_MESSAGE Message; 136 PLIST_ENTRY Entry; 137 BOOL bSend = TRUE; 138 139 ptiWnd = Wnd->head.pti; 140 ptiCur = PsGetCurrentThreadWin32Thread(); 141 /* 142 Not the current thread, Wnd is in send Nonclient paint also in send erase background and it is visiable. 143 */ 144 if ( Wnd->head.pti != ptiCur && 145 Wnd->state & WNDS_SENDNCPAINT && 146 Wnd->state & WNDS_SENDERASEBACKGROUND && 147 Wnd->style & WS_VISIBLE) 148 { 149 // For testing, if you see this, break out the Champagne and have a party! 150 TRACE("SendSyncPaint Wnd in State!\n"); 151 if (!IsListEmpty(&ptiWnd->SentMessagesListHead)) 152 { 153 // Scan sent queue messages to see if we received sync paint messages. 154 Entry = ptiWnd->SentMessagesListHead.Flink; 155 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry); 156 do 157 { 158 TRACE("LOOP it\n"); 159 if (Message->Msg.message == WM_SYNCPAINT && 160 Message->Msg.hwnd == UserHMGetHandle(Wnd)) 161 { // Already received so exit out. 162 ERR("SendSyncPaint Found one in the Sent Msg Queue!\n"); 163 bSend = FALSE; 164 break; 165 } 166 Entry = Message->ListEntry.Flink; 167 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry); 168 } 169 while (Entry != &ptiWnd->SentMessagesListHead); 170 } 171 if (bSend) 172 { 173 TRACE("Sending WM_SYNCPAINT\n"); 174 // This message has no parameters. But it does! Pass Flags along. 175 co_IntSendMessageNoWait(UserHMGetHandle(Wnd), WM_SYNCPAINT, Flags, 0); 176 Wnd->state |= WNDS_SYNCPAINTPENDING; 177 } 178 } 179 180 // Send to all the children if this is the desktop window. 181 if (UserIsDesktopWindow(Wnd)) 182 { 183 if ( Flags & RDW_ALLCHILDREN || 184 ( !(Flags & RDW_NOCHILDREN) && Wnd->style & WS_CLIPCHILDREN)) 185 { 186 PWND spwndChild = Wnd->spwndChild; 187 while(spwndChild) 188 { 189 if ( spwndChild->style & WS_CHILD && 190 spwndChild->head.pti != ptiCur) 191 { 192 spwndChild = spwndChild->spwndNext; 193 continue; 194 } 195 IntSendSyncPaint( spwndChild, Flags ); 196 spwndChild = spwndChild->spwndNext; 197 } 198 } 199 } 200 } 201 202 /* 203 * @name IntCalcWindowRgn 204 * 205 * Get a window or client region. 206 */ 207 208 HRGN FASTCALL 209 IntCalcWindowRgn(PWND Wnd, BOOL Client) 210 { 211 HRGN hRgnWindow; 212 213 if (Client) 214 { 215 hRgnWindow = NtGdiCreateRectRgn( 216 Wnd->rcClient.left, 217 Wnd->rcClient.top, 218 Wnd->rcClient.right, 219 Wnd->rcClient.bottom); 220 } 221 else 222 { 223 hRgnWindow = NtGdiCreateRectRgn( 224 Wnd->rcWindow.left, 225 Wnd->rcWindow.top, 226 Wnd->rcWindow.right, 227 Wnd->rcWindow.bottom); 228 } 229 230 if (Wnd->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE)) 231 { 232 NtGdiOffsetRgn(hRgnWindow, 233 -Wnd->rcWindow.left, 234 -Wnd->rcWindow.top); 235 NtGdiCombineRgn(hRgnWindow, hRgnWindow, Wnd->hrgnClip, RGN_AND); 236 NtGdiOffsetRgn(hRgnWindow, 237 Wnd->rcWindow.left, 238 Wnd->rcWindow.top); 239 } 240 241 return hRgnWindow; 242 } 243 244 /* 245 * @name IntGetNCUpdateRgn 246 * 247 * Get non-client update region of a window and optionally validate it. 248 * 249 * @param Window 250 * Pointer to window to get the NC update region from. 251 * @param Validate 252 * Set to TRUE to force validating the NC update region. 253 * 254 * @return 255 * Handle to NC update region. The caller is responsible for deleting 256 * it. 257 */ 258 259 HRGN FASTCALL 260 IntGetNCUpdateRgn(PWND Window, BOOL Validate) 261 { 262 HRGN hRgnNonClient; 263 HRGN hRgnWindow; 264 UINT RgnType, NcType; 265 RECT update; 266 267 if (Window->hrgnUpdate != NULL && 268 Window->hrgnUpdate != HRGN_WINDOW) 269 { 270 hRgnNonClient = IntCalcWindowRgn(Window, FALSE); 271 272 /* 273 * If region creation fails it's safe to fallback to whole 274 * window region. 275 */ 276 if (hRgnNonClient == NULL) 277 { 278 return HRGN_WINDOW; 279 } 280 281 hRgnWindow = IntCalcWindowRgn(Window, TRUE); 282 if (hRgnWindow == NULL) 283 { 284 GreDeleteObject(hRgnNonClient); 285 return HRGN_WINDOW; 286 } 287 288 NcType = IntGdiGetRgnBox(hRgnNonClient, &update); 289 290 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient, hRgnWindow, RGN_DIFF); 291 292 if (RgnType == ERROR) 293 { 294 GreDeleteObject(hRgnWindow); 295 GreDeleteObject(hRgnNonClient); 296 return HRGN_WINDOW; 297 } 298 else if (RgnType == NULLREGION) 299 { 300 GreDeleteObject(hRgnWindow); 301 GreDeleteObject(hRgnNonClient); 302 Window->state &= ~WNDS_UPDATEDIRTY; 303 return NULL; 304 } 305 306 /* 307 * Remove the nonclient region from the standard update region if 308 * we were asked for it. 309 */ 310 311 if (Validate) 312 { 313 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate, hRgnWindow, RGN_AND) == NULLREGION) 314 { 315 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED); 316 GreDeleteObject(Window->hrgnUpdate); 317 Window->state &= ~WNDS_UPDATEDIRTY; 318 Window->hrgnUpdate = NULL; 319 if (!(Window->state & WNDS_INTERNALPAINT)) 320 MsqDecPaintCountQueue(Window->head.pti); 321 } 322 } 323 324 /* check if update rgn contains complete nonclient area */ 325 if (NcType == SIMPLEREGION) 326 { 327 RECT window; 328 IntGetWindowRect( Window, &window ); 329 330 if (IntEqualRect( &window, &update )) 331 { 332 GreDeleteObject(hRgnNonClient); 333 hRgnNonClient = HRGN_WINDOW; 334 } 335 } 336 337 GreDeleteObject(hRgnWindow); 338 339 return hRgnNonClient; 340 } 341 else 342 { 343 return Window->hrgnUpdate; 344 } 345 } 346 347 VOID FASTCALL 348 IntSendNCPaint(PWND pWnd, HRGN hRgn) 349 { 350 pWnd->state &= ~WNDS_SENDNCPAINT; 351 352 if ( pWnd == GetW32ThreadInfo()->MessageQueue->spwndActive && 353 !(pWnd->state & WNDS_ACTIVEFRAME)) 354 { 355 pWnd->state |= WNDS_ACTIVEFRAME; 356 pWnd->state &= ~WNDS_NONCPAINT; 357 hRgn = HRGN_WINDOW; 358 } 359 360 if (pWnd->state2 & WNDS2_FORCEFULLNCPAINTCLIPRGN) 361 { 362 pWnd->state2 &= ~WNDS2_FORCEFULLNCPAINTCLIPRGN; 363 hRgn = HRGN_WINDOW; 364 } 365 366 if (hRgn) co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCPAINT, (WPARAM)hRgn, 0); 367 } 368 369 VOID FASTCALL 370 IntSendChildNCPaint(PWND pWnd) 371 { 372 pWnd = pWnd->spwndChild; 373 while (pWnd) 374 { 375 if ((pWnd->hrgnUpdate == NULL) && (pWnd->state & WNDS_SENDNCPAINT)) 376 { 377 PWND Next; 378 USER_REFERENCE_ENTRY Ref; 379 380 /* Reference, IntSendNCPaint leaves win32k */ 381 UserRefObjectCo(pWnd, &Ref); 382 IntSendNCPaint(pWnd, HRGN_WINDOW); 383 384 /* Make sure to grab next one before dereferencing/freeing */ 385 Next = pWnd->spwndNext; 386 UserDerefObjectCo(pWnd); 387 pWnd = Next; 388 } 389 else 390 { 391 pWnd = pWnd->spwndNext; 392 } 393 } 394 } 395 396 /* 397 * IntPaintWindows 398 * 399 * Internal function used by IntRedrawWindow. 400 */ 401 402 VOID FASTCALL 403 co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse) 404 { 405 HDC hDC; 406 HWND hWnd = UserHMGetHandle(Wnd); 407 HRGN TempRegion = NULL; 408 409 Wnd->state &= ~WNDS_PAINTNOTPROCESSED; 410 411 if (Wnd->state & WNDS_SENDNCPAINT || 412 Wnd->state & WNDS_SENDERASEBACKGROUND) 413 { 414 if (!(Wnd->style & WS_VISIBLE)) 415 { 416 Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 417 return; 418 } 419 else 420 { 421 if (Wnd->hrgnUpdate == NULL) 422 { 423 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 424 } 425 426 if (Wnd->head.pti == PsGetCurrentThreadWin32Thread()) 427 { 428 if (Wnd->state & WNDS_SENDNCPAINT) 429 { 430 TempRegion = IntGetNCUpdateRgn(Wnd, TRUE); 431 432 IntSendNCPaint(Wnd, TempRegion); 433 434 if (TempRegion > HRGN_WINDOW && GreIsHandleValid(TempRegion)) 435 { 436 /* NOTE: The region can already be deleted! */ 437 GreDeleteObject(TempRegion); 438 } 439 } 440 441 if (Wnd->state & WNDS_SENDERASEBACKGROUND) 442 { 443 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 444 if (Wnd->hrgnUpdate) 445 { 446 hDC = UserGetDCEx( Wnd, 447 Wnd->hrgnUpdate, 448 DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN); 449 450 if (Wnd->head.pti->ppi != pti->ppi) 451 { 452 ERR("Sending DC to another Process!!!\n"); 453 } 454 455 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 456 // Kill the loop, so Clear before we send. 457 if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0)) 458 { 459 Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 460 } 461 UserReleaseDC(Wnd, hDC, FALSE); 462 } 463 } 464 } 465 466 } 467 } 468 469 /* 470 * Check that the window is still valid at this point 471 */ 472 if (!IntIsWindow(hWnd)) 473 { 474 return; 475 } 476 477 /* 478 * Paint child windows. 479 */ 480 481 if (!(Flags & RDW_NOCHILDREN) && 482 !(Wnd->style & WS_MINIMIZE) && 483 ( Flags & RDW_ALLCHILDREN || 484 (Flags & RDW_CLIPCHILDREN && Wnd->style & WS_CLIPCHILDREN) ) ) 485 { 486 HWND *List, *phWnd; 487 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 488 489 if ((List = IntWinListChildren(Wnd))) 490 { 491 for (phWnd = List; *phWnd; ++phWnd) 492 { 493 if ((Wnd = UserGetWindowObject(*phWnd)) == NULL) 494 continue; 495 496 if (Wnd->head.pti != pti && Wnd->style & WS_CHILD) 497 continue; 498 499 if (Wnd->style & WS_VISIBLE) 500 { 501 USER_REFERENCE_ENTRY Ref; 502 UserRefObjectCo(Wnd, &Ref); 503 co_IntPaintWindows(Wnd, Flags, TRUE); 504 UserDerefObjectCo(Wnd); 505 } 506 } 507 ExFreePoolWithTag(List, USERTAG_WINDOWLIST); 508 } 509 } 510 } 511 512 /* 513 * IntUpdateWindows 514 * 515 * Internal function used by IntRedrawWindow, simplecall. 516 */ 517 518 VOID FASTCALL 519 co_IntUpdateWindows(PWND Wnd, ULONG Flags, BOOL Recurse) 520 { 521 HWND hWnd = UserHMGetHandle(Wnd); 522 523 if ( Wnd->hrgnUpdate != NULL || Wnd->state & WNDS_INTERNALPAINT ) 524 { 525 if (Wnd->hrgnUpdate) 526 { 527 if (!IntValidateParents(Wnd, Recurse)) 528 { 529 return; 530 } 531 } 532 533 if (Wnd->state & WNDS_INTERNALPAINT) 534 { 535 Wnd->state &= ~WNDS_INTERNALPAINT; 536 537 if (Wnd->hrgnUpdate == NULL) 538 MsqDecPaintCountQueue(Wnd->head.pti); 539 } 540 541 Wnd->state |= WNDS_PAINTNOTPROCESSED; 542 Wnd->state &= ~WNDS_UPDATEDIRTY; 543 544 Wnd->state2 |= WNDS2_WMPAINTSENT; 545 co_IntSendMessage(hWnd, WM_PAINT, 0, 0); 546 547 if (Wnd->state & WNDS_PAINTNOTPROCESSED) 548 { 549 USER_REFERENCE_ENTRY Ref; 550 UserRefObjectCo(Wnd, &Ref); 551 co_IntPaintWindows(Wnd, RDW_NOCHILDREN, FALSE); 552 UserDerefObjectCo(Wnd); 553 } 554 } 555 556 // Force flags as a toggle. Fixes msg:test_paint_messages:WmChildPaintNc. 557 Flags = (Flags & RDW_NOCHILDREN) ? RDW_NOCHILDREN : RDW_ALLCHILDREN; // All children is the default. 558 559 /* 560 * Update child windows. 561 */ 562 563 if (!(Flags & RDW_NOCHILDREN) && 564 (Flags & RDW_ALLCHILDREN) && 565 !UserIsDesktopWindow(Wnd)) 566 { 567 PWND Child; 568 569 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext) 570 { 571 /* transparent window, check for non-transparent sibling to paint first, then skip it */ 572 if ( Child->ExStyle & WS_EX_TRANSPARENT && 573 ( Child->hrgnUpdate != NULL || Child->state & WNDS_INTERNALPAINT ) ) 574 { 575 PWND Next = Child->spwndNext; 576 while (Next) 577 { 578 if ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT ) break; 579 580 Next = Next->spwndNext; 581 } 582 583 if (Next) continue; 584 } 585 586 if (Child->style & WS_VISIBLE) 587 { 588 USER_REFERENCE_ENTRY Ref; 589 UserRefObjectCo(Child, &Ref); 590 co_IntUpdateWindows(Child, Flags, TRUE); 591 UserDerefObjectCo(Child); 592 } 593 } 594 } 595 } 596 597 VOID FASTCALL 598 UserUpdateWindows(PWND pWnd, ULONG Flags) 599 { 600 // If transparent and any sibling windows below needs to be painted, leave. 601 if (pWnd->ExStyle & WS_EX_TRANSPARENT) 602 { 603 PWND Next = pWnd->spwndNext; 604 605 while(Next) 606 { 607 if ( Next->head.pti == pWnd->head.pti && 608 ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT) ) 609 { 610 return; 611 } 612 613 Next = Next->spwndNext; 614 } 615 } 616 co_IntUpdateWindows(pWnd, Flags, FALSE); 617 } 618 619 VOID FASTCALL 620 UserSyncAndPaintWindows(PWND pWnd, ULONG Flags) 621 { 622 PWND Parent = pWnd; 623 // Find parent, if it needs to be painted, leave. 624 while(TRUE) 625 { 626 if ((Parent = Parent->spwndParent) == NULL) break; 627 if ( Parent->style & WS_CLIPCHILDREN ) break; 628 if ( Parent->hrgnUpdate != NULL || Parent->state & WNDS_INTERNALPAINT ) return; 629 } 630 631 IntSendSyncPaint(pWnd, Flags); 632 co_IntPaintWindows(pWnd, Flags, FALSE); 633 } 634 635 /* 636 * IntInvalidateWindows 637 * 638 * Internal function used by IntRedrawWindow, UserRedrawDesktop, 639 * co_WinPosSetWindowPos, co_UserRedrawWindow. 640 */ 641 VOID FASTCALL 642 IntInvalidateWindows(PWND Wnd, PREGION Rgn, ULONG Flags) 643 { 644 INT RgnType = NULLREGION; 645 BOOL HadPaintMessage; 646 647 TRACE("IntInvalidateWindows start Rgn %p\n",Rgn); 648 649 if ( Rgn > PRGN_WINDOW ) 650 { 651 /* 652 * If the nonclient is not to be redrawn, clip the region to the client 653 * rect 654 */ 655 if ((Flags & RDW_INVALIDATE) != 0 && (Flags & RDW_FRAME) == 0) 656 { 657 PREGION RgnClient; 658 659 RgnClient = IntSysCreateRectpRgnIndirect(&Wnd->rcClient); 660 if (RgnClient) 661 { 662 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClient, RGN_AND); 663 REGION_Delete(RgnClient); 664 } 665 } 666 667 /* 668 * Clip the given region with window rectangle (or region) 669 */ 670 671 if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE)) 672 { 673 PREGION RgnWindow = IntSysCreateRectpRgnIndirect(&Wnd->rcWindow); 674 if (RgnWindow) 675 { 676 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnWindow, RGN_AND); 677 REGION_Delete(RgnWindow); 678 } 679 } 680 else 681 { 682 PREGION RgnClip = REGION_LockRgn(Wnd->hrgnClip); 683 if (RgnClip) 684 { 685 REGION_bOffsetRgn(Rgn, 686 -Wnd->rcWindow.left, 687 -Wnd->rcWindow.top); 688 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND); 689 REGION_bOffsetRgn(Rgn, 690 Wnd->rcWindow.left, 691 Wnd->rcWindow.top); 692 REGION_UnlockRgn(RgnClip); 693 } 694 } 695 } 696 else 697 { 698 RgnType = NULLREGION; 699 } 700 701 /* Nothing to paint, just return */ 702 if ((RgnType == NULLREGION && (Flags & RDW_INVALIDATE)) || RgnType == ERROR) 703 { 704 return; 705 } 706 707 /* 708 * Save current state of pending updates 709 */ 710 711 HadPaintMessage = IntIsWindowDirty(Wnd); 712 713 /* 714 * Update the region and flags 715 */ 716 717 // The following flags are used to invalidate the window. 718 if (Flags & (RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_ERASE|RDW_FRAME)) 719 { 720 if (Flags & RDW_INTERNALPAINT) 721 { 722 Wnd->state |= WNDS_INTERNALPAINT; 723 } 724 725 if (Flags & RDW_INVALIDATE ) 726 { 727 PREGION RgnUpdate; 728 729 Wnd->state &= ~WNDS_NONCPAINT; 730 731 /* If not the same thread set it dirty. */ 732 if (Wnd->head.pti != PsGetCurrentThreadWin32Thread()) 733 { 734 Wnd->state |= WNDS_UPDATEDIRTY; 735 if (Wnd->state2 & WNDS2_WMPAINTSENT) 736 Wnd->state2 |= WNDS2_ENDPAINTINVALIDATE; 737 } 738 739 if (Flags & RDW_FRAME) 740 Wnd->state |= WNDS_SENDNCPAINT; 741 742 if (Flags & RDW_ERASE) 743 Wnd->state |= WNDS_SENDERASEBACKGROUND; 744 745 if (RgnType != NULLREGION && Rgn > PRGN_WINDOW) 746 { 747 if (Wnd->hrgnUpdate == NULL) 748 { 749 Wnd->hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0); 750 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC); 751 } 752 753 if (Wnd->hrgnUpdate != HRGN_WINDOW) 754 { 755 RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate); 756 if (RgnUpdate) 757 { 758 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_OR); 759 REGION_UnlockRgn(RgnUpdate); 760 if (RgnType == NULLREGION) 761 { 762 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED); 763 GreDeleteObject(Wnd->hrgnUpdate); 764 Wnd->hrgnUpdate = NULL; 765 } 766 } 767 } 768 } 769 770 Flags |= RDW_ERASE|RDW_FRAME; // For children. 771 772 } 773 774 if (!HadPaintMessage && IntIsWindowDirty(Wnd)) 775 { 776 MsqIncPaintCountQueue(Wnd->head.pti); 777 } 778 779 } // The following flags are used to validate the window. 780 else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME)) 781 { 782 if (Wnd->state & WNDS_UPDATEDIRTY && !(Flags & RDW_NOUPDATEDIRTY)) 783 return; 784 785 if (Flags & RDW_NOINTERNALPAINT) 786 { 787 Wnd->state &= ~WNDS_INTERNALPAINT; 788 } 789 790 if (Flags & RDW_VALIDATE) 791 { 792 if (Flags & RDW_NOFRAME) 793 Wnd->state &= ~WNDS_SENDNCPAINT; 794 795 if (Flags & RDW_NOERASE) 796 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 797 798 if (Wnd->hrgnUpdate > HRGN_WINDOW && RgnType != NULLREGION && Rgn > PRGN_WINDOW) 799 { 800 PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate); 801 802 if (RgnUpdate) 803 { 804 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_DIFF); 805 REGION_UnlockRgn(RgnUpdate); 806 807 if (RgnType == NULLREGION) 808 { 809 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED); 810 GreDeleteObject(Wnd->hrgnUpdate); 811 Wnd->hrgnUpdate = NULL; 812 } 813 } 814 } 815 // If update is null, do not erase. 816 if (Wnd->hrgnUpdate == NULL) 817 { 818 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 819 } 820 } 821 822 if (HadPaintMessage && !IntIsWindowDirty(Wnd)) 823 { 824 MsqDecPaintCountQueue(Wnd->head.pti); 825 } 826 } 827 828 /* 829 * Process children if needed 830 */ 831 832 if (!(Flags & RDW_NOCHILDREN) && 833 !(Wnd->style & WS_MINIMIZE) && 834 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN))) 835 { 836 PWND Child; 837 838 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext) 839 { 840 if (Child->style & WS_VISIBLE) 841 { 842 /* 843 * Recursive call to update children hrgnUpdate 844 */ 845 PREGION RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0); 846 if (RgnTemp) 847 { 848 if (Rgn > PRGN_WINDOW) IntGdiCombineRgn(RgnTemp, Rgn, 0, RGN_COPY); 849 IntInvalidateWindows(Child, ((Rgn > PRGN_WINDOW)?RgnTemp:Rgn), Flags); 850 REGION_Delete(RgnTemp); 851 } 852 } 853 } 854 } 855 TRACE("IntInvalidateWindows exit\n"); 856 } 857 858 /* 859 * IntIsWindowDrawable 860 * 861 * Remarks 862 * Window is drawable when it is visible and all parents are not 863 * minimized. 864 */ 865 866 BOOL FASTCALL 867 IntIsWindowDrawable(PWND Wnd) 868 { 869 PWND WndObject; 870 871 for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent) 872 { 873 if ( WndObject->state2 & WNDS2_INDESTROY || 874 WndObject->state & WNDS_DESTROYED || 875 !WndObject || 876 !(WndObject->style & WS_VISIBLE) || 877 ((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd))) 878 { 879 return FALSE; 880 } 881 } 882 883 return TRUE; 884 } 885 886 /* 887 * IntRedrawWindow 888 * 889 * Internal version of NtUserRedrawWindow that takes WND as 890 * first parameter. 891 */ 892 893 BOOL FASTCALL 894 co_UserRedrawWindow( 895 PWND Window, 896 const RECTL* UpdateRect, 897 PREGION UpdateRgn, 898 ULONG Flags) 899 { 900 PREGION TmpRgn = NULL; 901 TRACE("co_UserRedrawWindow start Rgn %p\n",UpdateRgn); 902 903 /* 904 * Step 1. 905 * Validation of passed parameters. 906 */ 907 908 if (!IntIsWindowDrawable(Window)) 909 { 910 return TRUE; // Just do nothing!!! 911 } 912 913 if (Window == NULL) 914 { 915 Window = UserGetDesktopWindow(); 916 } 917 918 /* 919 * Step 2. 920 * Transform the parameters UpdateRgn and UpdateRect into 921 * a region hRgn specified in screen coordinates. 922 */ 923 924 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY! 925 { 926 /* We can't hold lock on GDI objects while doing roundtrips to user mode, 927 * so use a copy instead */ 928 if (UpdateRgn) 929 { 930 TmpRgn = IntSysCreateRectpRgn(0, 0, 0, 0); 931 932 if (UpdateRgn > PRGN_WINDOW) 933 { 934 IntGdiCombineRgn(TmpRgn, UpdateRgn, NULL, RGN_COPY); 935 } 936 937 if (Window != UserGetDesktopWindow()) 938 { 939 REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top); 940 } 941 } 942 else 943 { 944 if (UpdateRect != NULL) 945 { 946 if (Window == UserGetDesktopWindow()) 947 { 948 TmpRgn = IntSysCreateRectpRgnIndirect(UpdateRect); 949 } 950 else 951 { 952 TmpRgn = IntSysCreateRectpRgn(Window->rcClient.left + UpdateRect->left, 953 Window->rcClient.top + UpdateRect->top, 954 Window->rcClient.left + UpdateRect->right, 955 Window->rcClient.top + UpdateRect->bottom); 956 } 957 } 958 else 959 { 960 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) || 961 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME)) 962 { 963 if (!RECTL_bIsEmptyRect(&Window->rcWindow)) 964 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow); 965 } 966 else 967 { 968 if (!RECTL_bIsEmptyRect(&Window->rcClient)) 969 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcClient); 970 } 971 } 972 } 973 } 974 975 /* Fixes test RDW_INTERNALPAINT behavior */ 976 if (TmpRgn == NULL) 977 { 978 TmpRgn = PRGN_WINDOW; // Need a region so the bits can be set!!! 979 } 980 981 /* 982 * Step 3. 983 * Adjust the window update region depending on hRgn and flags. 984 */ 985 986 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) && 987 TmpRgn != NULL) 988 { 989 IntInvalidateWindows(Window, TmpRgn, Flags); 990 } 991 992 /* 993 * Step 4. 994 * Repaint and erase windows if needed. 995 */ 996 997 if (Flags & RDW_UPDATENOW) 998 { 999 UserUpdateWindows(Window, Flags); 1000 } 1001 else if (Flags & RDW_ERASENOW) 1002 { 1003 if ((Flags & (RDW_NOCHILDREN|RDW_ALLCHILDREN)) == 0) 1004 Flags |= RDW_CLIPCHILDREN; 1005 1006 UserSyncAndPaintWindows(Window, Flags); 1007 } 1008 1009 /* 1010 * Step 5. 1011 * Cleanup ;-) 1012 */ 1013 1014 if (TmpRgn > PRGN_WINDOW) 1015 { 1016 REGION_Delete(TmpRgn); 1017 } 1018 TRACE("co_UserRedrawWindow exit\n"); 1019 1020 return TRUE; 1021 } 1022 1023 VOID FASTCALL 1024 PaintSuspendedWindow(PWND pwnd, HRGN hrgnOrig) 1025 { 1026 if (pwnd->hrgnUpdate) 1027 { 1028 HDC hDC; 1029 INT Flags = DC_NC|DC_NOSENDMSG; 1030 HRGN hrgnTemp; 1031 RECT Rect; 1032 INT type; 1033 PREGION prgn; 1034 1035 if (pwnd->hrgnUpdate > HRGN_WINDOW) 1036 { 1037 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0); 1038 type = NtGdiCombineRgn( hrgnTemp, pwnd->hrgnUpdate, 0, RGN_COPY); 1039 if (type == ERROR) 1040 { 1041 GreDeleteObject(hrgnTemp); 1042 hrgnTemp = HRGN_WINDOW; 1043 } 1044 } 1045 else 1046 { 1047 hrgnTemp = GreCreateRectRgnIndirect(&pwnd->rcWindow); 1048 } 1049 1050 if ( hrgnOrig && 1051 hrgnTemp > HRGN_WINDOW && 1052 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnOrig, RGN_AND) == NULLREGION) 1053 { 1054 GreDeleteObject(hrgnTemp); 1055 return; 1056 } 1057 1058 hDC = UserGetDCEx(pwnd, hrgnTemp, DCX_WINDOW|DCX_INTERSECTRGN|DCX_USESTYLE|DCX_KEEPCLIPRGN); 1059 1060 Rect = pwnd->rcWindow; 1061 RECTL_vOffsetRect(&Rect, -pwnd->rcWindow.left, -pwnd->rcWindow.top); 1062 1063 // Clear out client area! 1064 FillRect(hDC, &Rect, IntGetSysColorBrush(COLOR_WINDOW)); 1065 1066 NC_DoNCPaint(pwnd, hDC, Flags); // Redraw without MENUs. 1067 1068 UserReleaseDC(pwnd, hDC, FALSE); 1069 1070 prgn = REGION_LockRgn(hrgnTemp); 1071 IntInvalidateWindows(pwnd, prgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN); 1072 REGION_UnlockRgn(prgn); 1073 1074 // Set updates for this window. 1075 pwnd->state |= WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_UPDATEDIRTY; 1076 1077 // DCX_KEEPCLIPRGN is set. Check it anyway. 1078 if (hrgnTemp > HRGN_WINDOW && GreIsHandleValid(hrgnTemp)) GreDeleteObject(hrgnTemp); 1079 } 1080 } 1081 1082 VOID FASTCALL 1083 UpdateTheadChildren(PWND pWnd, HRGN hRgn) 1084 { 1085 PaintSuspendedWindow( pWnd, hRgn ); 1086 1087 if (!(pWnd->style & WS_CLIPCHILDREN)) 1088 return; 1089 1090 pWnd = pWnd->spwndChild; // invalidate children if any. 1091 while (pWnd) 1092 { 1093 UpdateTheadChildren( pWnd, hRgn ); 1094 pWnd = pWnd->spwndNext; 1095 } 1096 } 1097 1098 VOID FASTCALL 1099 UpdateThreadWindows(PWND pWnd, PTHREADINFO pti, HRGN hRgn) 1100 { 1101 PWND pwndTemp; 1102 1103 for ( pwndTemp = pWnd; 1104 pwndTemp; 1105 pwndTemp = pwndTemp->spwndNext ) 1106 { 1107 if (pwndTemp->head.pti == pti) 1108 { 1109 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN); 1110 } 1111 else 1112 { 1113 if (IsThreadSuspended(pwndTemp->head.pti) || MsqIsHung(pwndTemp->head.pti, MSQ_HUNG)) 1114 { 1115 UpdateTheadChildren(pwndTemp, hRgn); 1116 } 1117 else 1118 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN); 1119 } 1120 } 1121 } 1122 1123 BOOL FASTCALL 1124 IntIsWindowDirty(PWND Wnd) 1125 { 1126 return ( Wnd->style & WS_VISIBLE && 1127 ( Wnd->hrgnUpdate != NULL || 1128 Wnd->state & WNDS_INTERNALPAINT ) ); 1129 } 1130 1131 /* 1132 Conditions to paint any window: 1133 1134 1. Update region is not null. 1135 2. Internal paint flag is set. 1136 3. Paint count is not zero. 1137 1138 */ 1139 PWND FASTCALL 1140 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread) 1141 { 1142 PWND hChild; 1143 PWND TempWindow; 1144 1145 for (; Window != NULL; Window = Window->spwndNext) 1146 { 1147 if (IntWndBelongsToThread(Window, Thread)) 1148 { 1149 if (IntIsWindowDirty(Window)) 1150 { 1151 /* Make sure all non-transparent siblings are already drawn. */ 1152 if (Window->ExStyle & WS_EX_TRANSPARENT) 1153 { 1154 for (TempWindow = Window->spwndNext; TempWindow != NULL; 1155 TempWindow = TempWindow->spwndNext) 1156 { 1157 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) && 1158 IntWndBelongsToThread(TempWindow, Thread) && 1159 IntIsWindowDirty(TempWindow)) 1160 { 1161 return TempWindow; 1162 } 1163 } 1164 } 1165 return Window; 1166 } 1167 } 1168 /* find a child of the specified window that needs repainting */ 1169 if (Window->spwndChild) 1170 { 1171 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread); 1172 if (hChild != NULL) 1173 return hChild; 1174 } 1175 } 1176 return Window; 1177 } 1178 1179 // 1180 // Internal painting of windows. 1181 // 1182 VOID FASTCALL 1183 IntPaintWindow( PWND Window ) 1184 { 1185 // Handle normal painting. 1186 co_IntPaintWindows( Window, RDW_NOCHILDREN, FALSE ); 1187 } 1188 1189 BOOL FASTCALL 1190 IntGetPaintMessage( 1191 PWND Window, 1192 UINT MsgFilterMin, 1193 UINT MsgFilterMax, 1194 PTHREADINFO Thread, 1195 MSG *Message, 1196 BOOL Remove) 1197 { 1198 PWND PaintWnd, StartWnd; 1199 1200 if ((MsgFilterMin != 0 || MsgFilterMax != 0) && 1201 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT)) 1202 return FALSE; 1203 1204 if (Thread->TIF_flags & TIF_SYSTEMTHREAD ) 1205 { 1206 ERR("WM_PAINT is in a System Thread!\n"); 1207 } 1208 1209 StartWnd = UserGetDesktopWindow(); 1210 PaintWnd = IntFindWindowToRepaint(StartWnd, Thread); 1211 1212 Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL; 1213 1214 if (Message->hwnd == NULL && Thread->cPaintsReady) 1215 { 1216 // Find note in window.c:"PAINTING BUG". 1217 ERR("WARNING SOMETHING HAS GONE WRONG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady); 1218 /* Hack to stop spamming the debug log ! */ 1219 Thread->cPaintsReady = 0; 1220 return FALSE; 1221 } 1222 1223 if (Message->hwnd == NULL) 1224 return FALSE; 1225 1226 if (!(Window == NULL || 1227 PaintWnd == Window || 1228 IntIsChildWindow(Window, PaintWnd))) /* check that it is a child of the specified parent */ 1229 return FALSE; 1230 1231 if (PaintWnd->state & WNDS_INTERNALPAINT) 1232 { 1233 PaintWnd->state &= ~WNDS_INTERNALPAINT; 1234 if (!PaintWnd->hrgnUpdate) 1235 MsqDecPaintCountQueue(Thread); 1236 } 1237 PaintWnd->state2 &= ~WNDS2_STARTPAINT; 1238 PaintWnd->state &= ~WNDS_UPDATEDIRTY; 1239 1240 Window = PaintWnd; 1241 while (Window && !UserIsDesktopWindow(Window)) 1242 { 1243 // Role back and check for clip children, do not set if any. 1244 if (Window->spwndParent && !(Window->spwndParent->style & WS_CLIPCHILDREN)) 1245 { 1246 PaintWnd->state2 |= WNDS2_WMPAINTSENT; 1247 } 1248 Window = Window->spwndParent; 1249 } 1250 1251 Message->wParam = Message->lParam = 0; 1252 Message->message = WM_PAINT; 1253 return TRUE; 1254 } 1255 1256 BOOL 1257 FASTCALL 1258 IntPrintWindow( 1259 PWND pwnd, 1260 HDC hdcBlt, 1261 UINT nFlags) 1262 { 1263 HDC hdcSrc; 1264 INT cx, cy, xSrc, ySrc; 1265 1266 if ( nFlags & PW_CLIENTONLY) 1267 { 1268 cx = pwnd->rcClient.right - pwnd->rcClient.left; 1269 cy = pwnd->rcClient.bottom - pwnd->rcClient.top; 1270 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left; 1271 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top; 1272 } 1273 else 1274 { 1275 cx = pwnd->rcWindow.right - pwnd->rcWindow.left; 1276 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top; 1277 xSrc = 0; 1278 ySrc = 0; 1279 } 1280 1281 // TODO: Setup Redirection for Print. 1282 return FALSE; 1283 1284 /* Update the window just incase. */ 1285 co_IntUpdateWindows( pwnd, RDW_ALLCHILDREN, FALSE); 1286 1287 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW); 1288 /* Print window to printer context. */ 1289 NtGdiBitBlt( hdcBlt, 1290 0, 1291 0, 1292 cx, 1293 cy, 1294 hdcSrc, 1295 xSrc, 1296 ySrc, 1297 SRCCOPY, 1298 0, 1299 0); 1300 1301 UserReleaseDC( pwnd, hdcSrc, FALSE); 1302 1303 // TODO: Release Redirection from Print. 1304 1305 return TRUE; 1306 } 1307 1308 BOOL 1309 FASTCALL 1310 IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi) 1311 { 1312 DWORD_PTR FlashState; 1313 UINT uCount = pfwi->uCount; 1314 BOOL Activate = FALSE, Ret = FALSE; 1315 1316 ASSERT(pfwi); 1317 1318 FlashState = (DWORD_PTR)UserGetProp(pWnd, AtomFlashWndState, TRUE); 1319 1320 if (FlashState == FLASHW_FINISHED) 1321 { 1322 // Cycle has finished, kill timer and set this to Stop. 1323 FlashState |= FLASHW_KILLSYSTIMER; 1324 pfwi->dwFlags = FLASHW_STOP; 1325 } 1326 else 1327 { 1328 if (FlashState) 1329 { 1330 if (pfwi->dwFlags == FLASHW_SYSTIMER) 1331 { 1332 // Called from system timer, restore flags, counts and state. 1333 pfwi->dwFlags = LOWORD(FlashState); 1334 uCount = HIWORD(FlashState); 1335 FlashState = MAKELONG(LOWORD(FlashState),0); 1336 } 1337 else 1338 { 1339 // Clean out the trash! Fix SeaMonkey crash after restart. 1340 FlashState = 0; 1341 } 1342 } 1343 1344 if (FlashState == 0) 1345 { // First time in cycle, setup flash state. 1346 if ( pWnd->state & WNDS_ACTIVEFRAME || 1347 (pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME))) 1348 { 1349 FlashState = FLASHW_STARTED|FLASHW_ACTIVE; 1350 } 1351 } 1352 1353 // Set previous window state. 1354 Ret = !!(FlashState & FLASHW_ACTIVE); 1355 1356 if ( (pfwi->dwFlags & FLASHW_TIMERNOFG) == FLASHW_TIMERNOFG && 1357 gpqForeground == pWnd->head.pti->MessageQueue ) 1358 { 1359 // Flashing until foreground, set this to Stop. 1360 pfwi->dwFlags = FLASHW_STOP; 1361 } 1362 } 1363 1364 // Toggle activate flag. 1365 if ( pfwi->dwFlags == FLASHW_STOP ) 1366 { 1367 if (gpqForeground && gpqForeground->spwndActive == pWnd) 1368 Activate = TRUE; 1369 else 1370 Activate = FALSE; 1371 } 1372 else 1373 { 1374 Activate = (FlashState & FLASHW_ACTIVE) == 0; 1375 } 1376 1377 if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION ) 1378 { 1379 co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0); 1380 } 1381 1382 // FIXME: Check for a Stop Sign here. 1383 if ( pfwi->dwFlags & FLASHW_TRAY ) 1384 { 1385 // Need some shell work here too. 1386 TRACE("FIXME: Flash window no Tray support!\n"); 1387 } 1388 1389 if ( pfwi->dwFlags == FLASHW_STOP ) 1390 { 1391 if (FlashState & FLASHW_KILLSYSTIMER) 1392 { 1393 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE); 1394 } 1395 1396 UserRemoveProp(pWnd, AtomFlashWndState, TRUE); 1397 } 1398 else 1399 { // Have a count and started, set timer. 1400 if ( uCount ) 1401 { 1402 FlashState |= FLASHW_COUNT; 1403 1404 if (!(Activate ^ !!(FlashState & FLASHW_STARTED))) 1405 uCount--; 1406 1407 if (!(FlashState & FLASHW_KILLSYSTIMER)) 1408 pfwi->dwFlags |= FLASHW_TIMER; 1409 } 1410 1411 if (pfwi->dwFlags & FLASHW_TIMER) 1412 { 1413 FlashState |= FLASHW_KILLSYSTIMER; 1414 1415 IntSetTimer( pWnd, 1416 ID_EVENT_SYSTIMER_FLASHWIN, 1417 pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink, 1418 SystemTimerProc, 1419 TMRF_SYSTEM ); 1420 } 1421 1422 if (FlashState & FLASHW_COUNT && uCount == 0) 1423 { 1424 // Keep spinning? Nothing else to do. 1425 FlashState = FLASHW_FINISHED; 1426 } 1427 else 1428 { 1429 // Save state and flags so this can be restored next time through. 1430 FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE; 1431 FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER); 1432 } 1433 FlashState = MAKELONG(LOWORD(FlashState),uCount); 1434 UserSetProp(pWnd, AtomFlashWndState, (HANDLE)FlashState, TRUE); 1435 } 1436 return Ret; 1437 } 1438 1439 // Win: xxxBeginPaint 1440 HDC FASTCALL 1441 IntBeginPaint(PWND Window, PPAINTSTRUCT Ps) 1442 { 1443 RECT Rect; 1444 INT type; 1445 BOOL Erase = FALSE; 1446 1447 co_UserHideCaret(Window); 1448 1449 Window->state2 |= WNDS2_STARTPAINT; 1450 Window->state &= ~WNDS_PAINTNOTPROCESSED; 1451 1452 if (Window->state & WNDS_SENDNCPAINT) 1453 { 1454 HRGN hRgn; 1455 // Application can keep update dirty. 1456 do 1457 { 1458 Window->state &= ~WNDS_UPDATEDIRTY; 1459 hRgn = IntGetNCUpdateRgn(Window, FALSE); 1460 IntSendNCPaint(Window, hRgn); 1461 if (hRgn > HRGN_WINDOW && GreIsHandleValid(hRgn)) 1462 { 1463 /* NOTE: The region can already be deleted! */ 1464 GreDeleteObject(hRgn); 1465 } 1466 } 1467 while(Window->state & WNDS_UPDATEDIRTY); 1468 } 1469 else 1470 { 1471 Window->state &= ~WNDS_UPDATEDIRTY; 1472 } 1473 1474 RtlZeroMemory(Ps, sizeof(PAINTSTRUCT)); 1475 1476 if (Window->state2 & WNDS2_ENDPAINTINVALIDATE) 1477 { 1478 ERR("BP: Another thread invalidated this window\n"); 1479 } 1480 1481 Ps->hdc = UserGetDCEx( Window, 1482 Window->hrgnUpdate, 1483 DCX_INTERSECTRGN | DCX_USESTYLE); 1484 if (!Ps->hdc) 1485 { 1486 return NULL; 1487 } 1488 1489 // If set, always clear flags out due to the conditions later on for sending the message. 1490 if (Window->state & WNDS_SENDERASEBACKGROUND) 1491 { 1492 Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 1493 Erase = TRUE; 1494 } 1495 1496 if (Window->hrgnUpdate != NULL) 1497 { 1498 MsqDecPaintCountQueue(Window->head.pti); 1499 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED); 1500 /* The region is part of the dc now and belongs to the process! */ 1501 Window->hrgnUpdate = NULL; 1502 } 1503 else 1504 { 1505 if (Window->state & WNDS_INTERNALPAINT) 1506 MsqDecPaintCountQueue(Window->head.pti); 1507 } 1508 1509 type = GdiGetClipBox(Ps->hdc, &Ps->rcPaint); 1510 1511 IntGetClientRect(Window, &Rect); 1512 1513 Window->state &= ~WNDS_INTERNALPAINT; 1514 1515 if ( Erase && // Set to erase, 1516 type != NULLREGION && // don't erase if the clip box is empty, 1517 (!(Window->pcls->style & CS_PARENTDC) || // not parent dc or 1518 RECTL_bIntersectRect( &Rect, &Rect, &Ps->rcPaint) ) ) // intersecting. 1519 { 1520 Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0); 1521 if ( Ps->fErase ) 1522 { 1523 Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND); 1524 } 1525 } 1526 else 1527 { 1528 Ps->fErase = FALSE; 1529 } 1530 1531 IntSendChildNCPaint(Window); 1532 1533 return Ps->hdc; 1534 } 1535 1536 // Win: xxxEndPaint 1537 BOOL FASTCALL 1538 IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps) 1539 { 1540 HDC hdc = NULL; 1541 1542 hdc = Ps->hdc; 1543 1544 UserReleaseDC(Wnd, hdc, TRUE); 1545 1546 if (Wnd->state2 & WNDS2_ENDPAINTINVALIDATE) 1547 { 1548 ERR("EP: Another thread invalidated this window\n"); 1549 Wnd->state2 &= ~WNDS2_ENDPAINTINVALIDATE; 1550 } 1551 1552 Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT); 1553 1554 co_UserShowCaret(Wnd); 1555 1556 return TRUE; 1557 } 1558 1559 // Win: xxxFillWindow 1560 BOOL FASTCALL 1561 IntFillWindow(PWND pWndParent, 1562 PWND pWnd, 1563 HDC hDC, 1564 HBRUSH hBrush) 1565 { 1566 RECT Rect, Rect1; 1567 INT type; 1568 1569 if (!pWndParent) 1570 pWndParent = pWnd; 1571 1572 type = GdiGetClipBox(hDC, &Rect); 1573 1574 IntGetClientRect(pWnd, &Rect1); 1575 1576 if ( type != NULLREGION && // Clip box is not empty, 1577 (!(pWnd->pcls->style & CS_PARENTDC) || // not parent dc or 1578 RECTL_bIntersectRect( &Rect, &Rect, &Rect1) ) ) // intersecting. 1579 { 1580 POINT ppt; 1581 INT x = 0, y = 0; 1582 1583 if (!UserIsDesktopWindow(pWndParent)) 1584 { 1585 x = pWndParent->rcClient.left - pWnd->rcClient.left; 1586 y = pWndParent->rcClient.top - pWnd->rcClient.top; 1587 } 1588 1589 GreSetBrushOrg(hDC, x, y, &ppt); 1590 1591 if ( hBrush < (HBRUSH)CTLCOLOR_MAX ) 1592 hBrush = GetControlColor( pWndParent, pWnd, hDC, HandleToUlong(hBrush) + WM_CTLCOLORMSGBOX); 1593 1594 FillRect(hDC, &Rect, hBrush); 1595 1596 GreSetBrushOrg(hDC, ppt.x, ppt.y, NULL); 1597 1598 return TRUE; 1599 } 1600 else 1601 return FALSE; 1602 } 1603 1604 /* PUBLIC FUNCTIONS ***********************************************************/ 1605 1606 /* 1607 * NtUserBeginPaint 1608 * 1609 * Status 1610 * @implemented 1611 */ 1612 1613 HDC APIENTRY 1614 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs) 1615 { 1616 PWND Window; 1617 PAINTSTRUCT Ps; 1618 NTSTATUS Status; 1619 HDC hDC; 1620 USER_REFERENCE_ENTRY Ref; 1621 HDC Ret = NULL; 1622 1623 TRACE("Enter NtUserBeginPaint\n"); 1624 UserEnterExclusive(); 1625 1626 if (!(Window = UserGetWindowObject(hWnd))) 1627 { 1628 goto Cleanup; // Return NULL 1629 } 1630 1631 UserRefObjectCo(Window, &Ref); 1632 1633 hDC = IntBeginPaint(Window, &Ps); 1634 1635 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT)); 1636 if (! NT_SUCCESS(Status)) 1637 { 1638 SetLastNtError(Status); 1639 goto Cleanup; // Return NULL 1640 } 1641 1642 Ret = hDC; 1643 1644 Cleanup: 1645 if (Window) UserDerefObjectCo(Window); 1646 1647 TRACE("Leave NtUserBeginPaint, ret=%p\n", Ret); 1648 UserLeave(); 1649 return Ret; 1650 } 1651 1652 /* 1653 * NtUserEndPaint 1654 * 1655 * Status 1656 * @implemented 1657 */ 1658 1659 BOOL APIENTRY 1660 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs) 1661 { 1662 NTSTATUS Status = STATUS_SUCCESS; 1663 PWND Window; 1664 PAINTSTRUCT Ps; 1665 USER_REFERENCE_ENTRY Ref; 1666 BOOL Ret = FALSE; 1667 1668 TRACE("Enter NtUserEndPaint\n"); 1669 UserEnterExclusive(); 1670 1671 if (!(Window = UserGetWindowObject(hWnd))) 1672 { 1673 goto Cleanup; // Return FALSE 1674 } 1675 1676 UserRefObjectCo(Window, &Ref); // Here for the exception. 1677 1678 _SEH2_TRY 1679 { 1680 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1); 1681 RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT)); 1682 } 1683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1684 { 1685 Status = _SEH2_GetExceptionCode(); 1686 } 1687 _SEH2_END 1688 if (!NT_SUCCESS(Status)) 1689 { 1690 goto Cleanup; // Return FALSE 1691 } 1692 1693 Ret = IntEndPaint(Window, &Ps); 1694 1695 Cleanup: 1696 if (Window) UserDerefObjectCo(Window); 1697 1698 TRACE("Leave NtUserEndPaint, ret=%i\n", Ret); 1699 UserLeave(); 1700 return Ret; 1701 } 1702 1703 /* 1704 * FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND. 1705 */ 1706 /* 1707 * @implemented 1708 */ 1709 BOOL APIENTRY 1710 NtUserFillWindow(HWND hWndParent, 1711 HWND hWnd, 1712 HDC hDC, 1713 HBRUSH hBrush) 1714 { 1715 BOOL ret = FALSE; 1716 PWND pWnd, pWndParent = NULL; 1717 USER_REFERENCE_ENTRY Ref; 1718 1719 TRACE("Enter NtUserFillWindow\n"); 1720 UserEnterExclusive(); 1721 1722 if (!hDC) 1723 { 1724 goto Exit; 1725 } 1726 1727 if (!(pWnd = UserGetWindowObject(hWnd))) 1728 { 1729 goto Exit; 1730 } 1731 1732 if (hWndParent && !(pWndParent = UserGetWindowObject(hWndParent))) 1733 { 1734 goto Exit; 1735 } 1736 1737 UserRefObjectCo(pWnd, &Ref); 1738 ret = IntFillWindow( pWndParent, pWnd, hDC, hBrush ); 1739 UserDerefObjectCo(pWnd); 1740 1741 Exit: 1742 TRACE("Leave NtUserFillWindow, ret=%i\n",ret); 1743 UserLeave(); 1744 return ret; 1745 } 1746 1747 /* 1748 * @implemented 1749 */ 1750 BOOL APIENTRY 1751 NtUserFlashWindowEx(IN PFLASHWINFO pfwi) 1752 { 1753 PWND pWnd; 1754 FLASHWINFO finfo = {0}; 1755 BOOL Ret = FALSE; 1756 1757 UserEnterExclusive(); 1758 1759 _SEH2_TRY 1760 { 1761 ProbeForRead(pfwi, sizeof(FLASHWINFO), 1); 1762 RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO)); 1763 } 1764 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1765 { 1766 SetLastNtError(_SEH2_GetExceptionCode()); 1767 _SEH2_YIELD(goto Exit); 1768 } 1769 _SEH2_END 1770 1771 if (!( pWnd = ValidateHwndNoErr(finfo.hwnd)) || 1772 finfo.cbSize != sizeof(FLASHWINFO) || 1773 finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) ) 1774 { 1775 EngSetLastError(ERROR_INVALID_PARAMETER); 1776 goto Exit; 1777 } 1778 1779 Ret = IntFlashWindowEx(pWnd, &finfo); 1780 1781 Exit: 1782 UserLeave(); 1783 return Ret; 1784 } 1785 1786 /* 1787 GetUpdateRgn, this fails the same as the old one. 1788 */ 1789 INT FASTCALL 1790 co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase) 1791 { 1792 int RegionType; 1793 BOOL Type; 1794 RECTL Rect; 1795 1796 ASSERT_REFS_CO(Window); 1797 1798 if (bErase) 1799 { 1800 USER_REFERENCE_ENTRY Ref; 1801 UserRefObjectCo(Window, &Ref); 1802 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE); 1803 UserDerefObjectCo(Window); 1804 } 1805 1806 Window->state &= ~WNDS_UPDATEDIRTY; 1807 1808 if (Window->hrgnUpdate == NULL) 1809 { 1810 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0); 1811 return NULLREGION; 1812 } 1813 1814 Rect = Window->rcClient; 1815 Type = IntIntersectWithParents(Window, &Rect); 1816 1817 if (Window->hrgnUpdate == HRGN_WINDOW) 1818 { 1819 // Trap it out. 1820 ERR("GURn: Caller is passing Window Region 1\n"); 1821 if (!Type) 1822 { 1823 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0); 1824 return NULLREGION; 1825 } 1826 1827 RegionType = SIMPLEREGION; 1828 1829 if (!UserIsDesktopWindow(Window)) 1830 { 1831 RECTL_vOffsetRect(&Rect, 1832 -Window->rcClient.left, 1833 -Window->rcClient.top); 1834 } 1835 GreSetRectRgnIndirect(hRgn, &Rect); 1836 } 1837 else 1838 { 1839 HRGN hrgnTemp = GreCreateRectRgnIndirect(&Rect); 1840 1841 RegionType = NtGdiCombineRgn(hRgn, hrgnTemp, Window->hrgnUpdate, RGN_AND); 1842 1843 if (RegionType == ERROR || RegionType == NULLREGION) 1844 { 1845 if (hrgnTemp) GreDeleteObject(hrgnTemp); 1846 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0); 1847 return RegionType; 1848 } 1849 1850 if (!UserIsDesktopWindow(Window)) 1851 { 1852 NtGdiOffsetRgn(hRgn, 1853 -Window->rcClient.left, 1854 -Window->rcClient.top); 1855 } 1856 if (hrgnTemp) GreDeleteObject(hrgnTemp); 1857 } 1858 return RegionType; 1859 } 1860 1861 BOOL FASTCALL 1862 co_UserGetUpdateRect(PWND Window, PRECT pRect, BOOL bErase) 1863 { 1864 INT RegionType; 1865 BOOL Ret = TRUE; 1866 1867 if (bErase) 1868 { 1869 USER_REFERENCE_ENTRY Ref; 1870 UserRefObjectCo(Window, &Ref); 1871 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE); 1872 UserDerefObjectCo(Window); 1873 } 1874 1875 Window->state &= ~WNDS_UPDATEDIRTY; 1876 1877 if (Window->hrgnUpdate == NULL) 1878 { 1879 pRect->left = pRect->top = pRect->right = pRect->bottom = 0; 1880 Ret = FALSE; 1881 } 1882 else 1883 { 1884 /* Get the update region bounding box. */ 1885 if (Window->hrgnUpdate == HRGN_WINDOW) 1886 { 1887 *pRect = Window->rcClient; 1888 ERR("GURt: Caller is retrieving Window Region 1\n"); 1889 } 1890 else 1891 { 1892 RegionType = IntGdiGetRgnBox(Window->hrgnUpdate, pRect); 1893 1894 if (RegionType != ERROR && RegionType != NULLREGION) 1895 RECTL_bIntersectRect(pRect, pRect, &Window->rcClient); 1896 } 1897 1898 if (IntIntersectWithParents(Window, pRect)) 1899 { 1900 if (!UserIsDesktopWindow(Window)) 1901 { 1902 RECTL_vOffsetRect(pRect, 1903 -Window->rcClient.left, 1904 -Window->rcClient.top); 1905 } 1906 if (Window->pcls->style & CS_OWNDC) 1907 { 1908 HDC hdc; 1909 //DWORD layout; 1910 hdc = UserGetDCEx(Window, NULL, DCX_USESTYLE); 1911 //layout = NtGdiSetLayout(hdc, -1, 0); 1912 //IntMapWindowPoints( 0, Window, (LPPOINT)pRect, 2 ); 1913 GreDPtoLP( hdc, (LPPOINT)pRect, 2 ); 1914 //NtGdiSetLayout(hdc, -1, layout); 1915 UserReleaseDC(Window, hdc, FALSE); 1916 } 1917 } 1918 else 1919 { 1920 pRect->left = pRect->top = pRect->right = pRect->bottom = 0; 1921 } 1922 } 1923 return Ret; 1924 } 1925 1926 /* 1927 * NtUserGetUpdateRgn 1928 * 1929 * Status 1930 * @implemented 1931 */ 1932 1933 INT APIENTRY 1934 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase) 1935 { 1936 PWND Window; 1937 INT ret = ERROR; 1938 1939 TRACE("Enter NtUserGetUpdateRgn\n"); 1940 UserEnterExclusive(); 1941 1942 Window = UserGetWindowObject(hWnd); 1943 if (Window) 1944 { 1945 ret = co_UserGetUpdateRgn(Window, hRgn, bErase); 1946 } 1947 1948 TRACE("Leave NtUserGetUpdateRgn, ret=%i\n", ret); 1949 UserLeave(); 1950 return ret; 1951 } 1952 1953 /* 1954 * NtUserGetUpdateRect 1955 * 1956 * Status 1957 * @implemented 1958 */ 1959 1960 BOOL APIENTRY 1961 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase) 1962 { 1963 PWND Window; 1964 RECTL Rect; 1965 NTSTATUS Status; 1966 BOOL Ret = FALSE; 1967 1968 TRACE("Enter NtUserGetUpdateRect\n"); 1969 UserEnterExclusive(); 1970 1971 if (!(Window = UserGetWindowObject(hWnd))) 1972 { 1973 goto Exit; // Return FALSE 1974 } 1975 1976 Ret = co_UserGetUpdateRect(Window, &Rect, bErase); 1977 1978 if (UnsafeRect != NULL) 1979 { 1980 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL)); 1981 if (!NT_SUCCESS(Status)) 1982 { 1983 EngSetLastError(ERROR_INVALID_PARAMETER); 1984 Ret = FALSE; 1985 } 1986 } 1987 1988 Exit: 1989 TRACE("Leave NtUserGetUpdateRect, ret=%i\n", Ret); 1990 UserLeave(); 1991 return Ret; 1992 } 1993 1994 /* 1995 * NtUserRedrawWindow 1996 * 1997 * Status 1998 * @implemented 1999 */ 2000 2001 BOOL APIENTRY 2002 NtUserRedrawWindow( 2003 HWND hWnd, 2004 CONST RECT *lprcUpdate, 2005 HRGN hrgnUpdate, 2006 UINT flags) 2007 { 2008 RECTL SafeUpdateRect; 2009 PWND Wnd; 2010 BOOL Ret = FALSE; 2011 USER_REFERENCE_ENTRY Ref; 2012 NTSTATUS Status = STATUS_SUCCESS; 2013 PREGION RgnUpdate = NULL; 2014 2015 TRACE("Enter NtUserRedrawWindow\n"); 2016 UserEnterExclusive(); 2017 2018 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow()))) 2019 { 2020 goto Exit; // Return FALSE 2021 } 2022 2023 if (lprcUpdate) 2024 { 2025 _SEH2_TRY 2026 { 2027 ProbeForRead(lprcUpdate, sizeof(RECTL), 1); 2028 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL)); 2029 } 2030 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2031 { 2032 Status = _SEH2_GetExceptionCode(); 2033 } 2034 _SEH2_END 2035 if (!NT_SUCCESS(Status)) 2036 { 2037 EngSetLastError(RtlNtStatusToDosError(Status)); 2038 goto Exit; // Return FALSE 2039 } 2040 } 2041 2042 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE| 2043 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE| 2044 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) ) 2045 { 2046 /* RedrawWindow fails only in case that flags are invalid */ 2047 EngSetLastError(ERROR_INVALID_FLAGS); 2048 goto Exit; // Return FALSE 2049 } 2050 2051 /* We can't hold lock on GDI objects while doing roundtrips to user mode, 2052 * so it will be copied. 2053 */ 2054 if (hrgnUpdate > HRGN_WINDOW) 2055 { 2056 RgnUpdate = REGION_LockRgn(hrgnUpdate); 2057 if (!RgnUpdate) 2058 { 2059 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); 2060 goto Exit; // Return FALSE 2061 } 2062 REGION_UnlockRgn(RgnUpdate); 2063 } 2064 else if (hrgnUpdate == HRGN_WINDOW) // Trap it out. 2065 { 2066 ERR("NTRW: Caller is passing Window Region 1\n"); 2067 } 2068 2069 UserRefObjectCo(Wnd, &Ref); 2070 2071 Ret = co_UserRedrawWindow( Wnd, 2072 lprcUpdate ? &SafeUpdateRect : NULL, 2073 RgnUpdate, 2074 flags); 2075 2076 UserDerefObjectCo(Wnd); 2077 2078 Exit: 2079 TRACE("Leave NtUserRedrawWindow, ret=%i\n", Ret); 2080 UserLeave(); 2081 return Ret; 2082 } 2083 2084 BOOL 2085 UserDrawCaptionText( 2086 PWND pWnd, 2087 HDC hDc, 2088 const PUNICODE_STRING Text, 2089 const RECTL *lpRc, 2090 UINT uFlags, 2091 HFONT hFont) 2092 { 2093 HFONT hOldFont = NULL; 2094 COLORREF OldTextColor; 2095 NONCLIENTMETRICSW nclm; 2096 NTSTATUS Status; 2097 BOOLEAN bDeleteFont = FALSE; 2098 SIZE Size; 2099 BOOL Ret = TRUE; 2100 ULONG fit = 0, Length; 2101 RECTL r = *lpRc; 2102 2103 TRACE("UserDrawCaptionText: %wZ\n", Text); 2104 2105 nclm.cbSize = sizeof(nclm); 2106 if (!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, nclm.cbSize, &nclm, 0)) 2107 { 2108 ERR("UserSystemParametersInfo() failed!\n"); 2109 return FALSE; 2110 } 2111 2112 if (!hFont) 2113 { 2114 if(uFlags & DC_SMALLCAP) 2115 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont); 2116 else 2117 Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont); 2118 2119 if(!NT_SUCCESS(Status)) 2120 { 2121 ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status); 2122 return FALSE; 2123 } 2124 2125 bDeleteFont = TRUE; 2126 } 2127 2128 IntGdiSetBkMode(hDc, TRANSPARENT); 2129 2130 hOldFont = NtGdiSelectFont(hDc, hFont); 2131 2132 if(uFlags & DC_INBUTTON) 2133 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT)); 2134 else 2135 OldTextColor = IntGdiSetTextColor(hDc, 2136 IntGetSysColor(uFlags & DC_ACTIVE ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT)); 2137 2138 // Adjust for system menu. 2139 if (pWnd && pWnd->style & WS_SYSMENU) 2140 { 2141 r.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1; 2142 if ((pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW)) 2143 { 2144 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1; 2145 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1; 2146 } 2147 } 2148 2149 GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0); 2150 2151 Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1); 2152 2153 if (Text->Length/sizeof(WCHAR) > Length) 2154 { 2155 Ret = FALSE; 2156 } 2157 2158 if (Ret) 2159 { // Faster while in setup. 2160 UserExtTextOutW( hDc, 2161 lpRc->left, 2162 lpRc->top + (lpRc->bottom - lpRc->top - Size.cy) / 2, // DT_SINGLELINE && DT_VCENTER 2163 ETO_CLIPPED, 2164 (RECTL *)lpRc, 2165 Text->Buffer, 2166 Length); 2167 } 2168 else 2169 { 2170 DrawTextW( hDc, 2171 Text->Buffer, 2172 Text->Length/sizeof(WCHAR), 2173 (RECTL *)&r, 2174 DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT); 2175 } 2176 2177 IntGdiSetTextColor(hDc, OldTextColor); 2178 2179 if (hOldFont) 2180 NtGdiSelectFont(hDc, hOldFont); 2181 2182 if (bDeleteFont) 2183 GreDeleteObject(hFont); 2184 2185 return Ret; 2186 } 2187 2188 // 2189 // This draws Buttons, Icons and Text... 2190 // 2191 BOOL UserDrawCaption( 2192 PWND pWnd, 2193 HDC hDc, 2194 RECTL *lpRc, 2195 HFONT hFont, 2196 HICON hIcon, 2197 const PUNICODE_STRING Str, 2198 UINT uFlags) 2199 { 2200 BOOL Ret = FALSE; 2201 HBRUSH hBgBrush, hOldBrush = NULL; 2202 RECTL Rect = *lpRc; 2203 BOOL HasIcon; 2204 2205 RECTL_vMakeWellOrdered(lpRc); 2206 2207 /* Determine whether the icon needs to be displayed */ 2208 if (!hIcon && pWnd != NULL) 2209 { 2210 HasIcon = (uFlags & DC_ICON) && !(uFlags & DC_SMALLCAP) && 2211 (pWnd->style & WS_SYSMENU) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW); 2212 } 2213 else 2214 HasIcon = (hIcon != NULL); 2215 2216 // Draw the caption background 2217 if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON)) 2218 { 2219 static GRADIENT_RECT gcap = {0, 1}; 2220 TRIVERTEX Vertices[2]; 2221 COLORREF Colors[2]; 2222 2223 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ? 2224 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION); 2225 2226 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ? 2227 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION); 2228 2229 Vertices[0].x = Rect.left; 2230 Vertices[0].y = Rect.top; 2231 Vertices[0].Red = (WORD)Colors[0]<<8; 2232 Vertices[0].Green = (WORD)Colors[0] & 0xFF00; 2233 Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00; 2234 Vertices[0].Alpha = 0; 2235 2236 Vertices[1].x = Rect.right; 2237 Vertices[1].y = Rect.bottom; 2238 Vertices[1].Red = (WORD)Colors[1]<<8; 2239 Vertices[1].Green = (WORD)Colors[1] & 0xFF00; 2240 Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00; 2241 Vertices[1].Alpha = 0; 2242 2243 if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H)) 2244 { 2245 ERR("GreGradientFill() failed!\n"); 2246 goto cleanup; 2247 } 2248 } 2249 else 2250 { 2251 if(uFlags & DC_INBUTTON) 2252 hBgBrush = IntGetSysColorBrush(COLOR_3DFACE); 2253 else if(uFlags & DC_ACTIVE) 2254 hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION); 2255 else 2256 hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION); 2257 2258 hOldBrush = NtGdiSelectBrush(hDc, hBgBrush); 2259 2260 if(!hOldBrush) 2261 { 2262 ERR("NtGdiSelectBrush() failed!\n"); 2263 goto cleanup; 2264 } 2265 2266 if(!NtGdiPatBlt(hDc, Rect.left, Rect.top, 2267 Rect.right - Rect.left, 2268 Rect.bottom - Rect.top, 2269 PATCOPY)) 2270 { 2271 ERR("NtGdiPatBlt() failed!\n"); 2272 goto cleanup; 2273 } 2274 } 2275 2276 /* Draw icon */ 2277 if (HasIcon) 2278 { 2279 PCURICON_OBJECT pIcon = NULL; 2280 2281 if (hIcon) 2282 { 2283 pIcon = UserGetCurIconObject(hIcon); 2284 } 2285 else if (pWnd) 2286 { 2287 pIcon = NC_IconForWindow(pWnd); 2288 // FIXME: NC_IconForWindow should reference it for us */ 2289 if (pIcon) 2290 UserReferenceObject(pIcon); 2291 } 2292 2293 if (pIcon) 2294 { 2295 LONG cx = UserGetSystemMetrics(SM_CXSMICON); 2296 LONG cy = UserGetSystemMetrics(SM_CYSMICON); 2297 LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does 2298 LONG y = (Rect.top + Rect.bottom - cy)/2; // center 2299 UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL); 2300 UserDereferenceObject(pIcon); 2301 } 2302 else 2303 { 2304 HasIcon = FALSE; 2305 } 2306 } 2307 2308 if (HasIcon) 2309 Rect.left += Rect.bottom - Rect.top; 2310 2311 if((uFlags & DC_TEXT)) 2312 { 2313 BOOL Set = FALSE; 2314 Rect.left += 2; 2315 2316 if (Str) 2317 Set = UserDrawCaptionText(pWnd, hDc, Str, &Rect, uFlags, hFont); 2318 else if (pWnd != NULL) // FIXME: Windows does not do that 2319 { 2320 UNICODE_STRING ustr; 2321 ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated! 2322 ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT); 2323 ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT); 2324 Set = UserDrawCaptionText(pWnd, hDc, &ustr, &Rect, uFlags, hFont); 2325 } 2326 if (pWnd) 2327 { 2328 if (Set) 2329 pWnd->state2 &= ~WNDS2_CAPTIONTEXTTRUNCATED; 2330 else 2331 pWnd->state2 |= WNDS2_CAPTIONTEXTTRUNCATED; 2332 } 2333 } 2334 2335 Ret = TRUE; 2336 2337 cleanup: 2338 if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush); 2339 2340 return Ret; 2341 } 2342 2343 INT 2344 FASTCALL 2345 UserRealizePalette(HDC hdc) 2346 { 2347 HWND hWnd, hWndDesktop; 2348 DWORD Ret; 2349 2350 Ret = IntGdiRealizePalette(hdc); 2351 if (Ret) // There was a change. 2352 { 2353 hWnd = IntWindowFromDC(hdc); 2354 if (hWnd) // Send broadcast if dc is associated with a window. 2355 { // FYI: Thread locked in CallOneParam. 2356 hWndDesktop = IntGetDesktopWindow(); 2357 if ( hWndDesktop != hWnd ) 2358 { 2359 PWND pWnd = UserGetWindowObject(hWndDesktop); 2360 ERR("RealizePalette Desktop.\n"); 2361 hdc = UserGetWindowDC(pWnd); 2362 IntPaintDesktop(hdc); 2363 UserReleaseDC(pWnd,hdc,FALSE); 2364 } 2365 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0); 2366 } 2367 } 2368 return Ret; 2369 } 2370 2371 BOOL 2372 APIENTRY 2373 NtUserDrawCaptionTemp( 2374 HWND hWnd, 2375 HDC hDC, 2376 LPCRECT lpRc, 2377 HFONT hFont, 2378 HICON hIcon, 2379 const PUNICODE_STRING str, 2380 UINT uFlags) 2381 { 2382 PWND pWnd = NULL; 2383 UNICODE_STRING SafeStr = {0}; 2384 NTSTATUS Status = STATUS_SUCCESS; 2385 RECTL SafeRect; 2386 BOOL Ret; 2387 2388 UserEnterExclusive(); 2389 2390 if (hWnd != NULL) 2391 { 2392 if(!(pWnd = UserGetWindowObject(hWnd))) 2393 { 2394 UserLeave(); 2395 return FALSE; 2396 } 2397 } 2398 2399 _SEH2_TRY 2400 { 2401 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG)); 2402 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL)); 2403 if (str != NULL) 2404 { 2405 SafeStr = ProbeForReadUnicodeString(str); 2406 if (SafeStr.Length != 0) 2407 { 2408 ProbeForRead( SafeStr.Buffer, 2409 SafeStr.Length, 2410 sizeof(WCHAR)); 2411 } 2412 } 2413 } 2414 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2415 { 2416 Status = _SEH2_GetExceptionCode(); 2417 } 2418 _SEH2_END; 2419 2420 if (Status != STATUS_SUCCESS) 2421 { 2422 SetLastNtError(Status); 2423 UserLeave(); 2424 return FALSE; 2425 } 2426 2427 if (str != NULL) 2428 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags); 2429 else 2430 { 2431 if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 ) 2432 { 2433 Ret = TRUE; 2434 if (uFlags & DC_DRAWCAPTIONMD) 2435 { 2436 ERR("NC Caption Mode\n"); 2437 UserDrawCaptionBar(pWnd, hDC, uFlags); 2438 goto Exit; 2439 } 2440 else if (uFlags & DC_DRAWFRAMEMD) 2441 { 2442 ERR("NC Paint Mode\n"); 2443 NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too! 2444 goto Exit; 2445 } 2446 } 2447 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags); 2448 } 2449 Exit: 2450 UserLeave(); 2451 return Ret; 2452 } 2453 2454 BOOL 2455 APIENTRY 2456 NtUserDrawCaption(HWND hWnd, 2457 HDC hDC, 2458 LPCRECT lpRc, 2459 UINT uFlags) 2460 { 2461 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags); 2462 } 2463 2464 INT FASTCALL 2465 co_UserExcludeUpdateRgn(HDC hDC, PWND Window) 2466 { 2467 POINT pt; 2468 RECT rc; 2469 2470 if (Window->hrgnUpdate) 2471 { 2472 if (Window->hrgnUpdate == HRGN_WINDOW) 2473 { 2474 return NtGdiIntersectClipRect(hDC, 0, 0, 0, 0); 2475 } 2476 else 2477 { 2478 INT ret = ERROR; 2479 HRGN hrgn = NtGdiCreateRectRgn(0,0,0,0); 2480 2481 if ( hrgn && GreGetDCPoint( hDC, GdiGetDCOrg, &pt) ) 2482 { 2483 if ( NtGdiGetRandomRgn( hDC, hrgn, CLIPRGN) == NULLREGION ) 2484 { 2485 NtGdiOffsetRgn(hrgn, pt.x, pt.y); 2486 } 2487 else 2488 { 2489 HRGN hrgnScreen; 2490 PMONITOR pm = UserGetPrimaryMonitor(); 2491 hrgnScreen = NtGdiCreateRectRgn(0,0,0,0); 2492 NtGdiCombineRgn(hrgnScreen, hrgnScreen, pm->hrgnMonitor, RGN_OR); 2493 2494 NtGdiCombineRgn(hrgn, hrgnScreen, NULL, RGN_COPY); 2495 2496 GreDeleteObject(hrgnScreen); 2497 } 2498 2499 NtGdiCombineRgn(hrgn, hrgn, Window->hrgnUpdate, RGN_DIFF); 2500 2501 NtGdiOffsetRgn(hrgn, -pt.x, -pt.y); 2502 2503 ret = NtGdiExtSelectClipRgn(hDC, hrgn, RGN_COPY); 2504 2505 GreDeleteObject(hrgn); 2506 } 2507 return ret; 2508 } 2509 } 2510 else 2511 { 2512 return GdiGetClipBox( hDC, &rc); 2513 } 2514 } 2515 2516 INT 2517 APIENTRY 2518 NtUserExcludeUpdateRgn( 2519 HDC hDC, 2520 HWND hWnd) 2521 { 2522 INT ret = ERROR; 2523 PWND pWnd; 2524 2525 TRACE("Enter NtUserExcludeUpdateRgn\n"); 2526 UserEnterExclusive(); 2527 2528 pWnd = UserGetWindowObject(hWnd); 2529 2530 if (hDC && pWnd) 2531 ret = co_UserExcludeUpdateRgn(hDC, pWnd); 2532 2533 TRACE("Leave NtUserExcludeUpdateRgn, ret=%i\n", ret); 2534 2535 UserLeave(); 2536 return ret; 2537 } 2538 2539 BOOL 2540 APIENTRY 2541 NtUserInvalidateRect( 2542 HWND hWnd, 2543 CONST RECT *lpUnsafeRect, 2544 BOOL bErase) 2545 { 2546 UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0); 2547 if (!hWnd) 2548 { 2549 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW; 2550 lpUnsafeRect = NULL; 2551 } 2552 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags); 2553 } 2554 2555 BOOL 2556 APIENTRY 2557 NtUserInvalidateRgn( 2558 HWND hWnd, 2559 HRGN hRgn, 2560 BOOL bErase) 2561 { 2562 if (!hWnd) 2563 { 2564 EngSetLastError( ERROR_INVALID_WINDOW_HANDLE ); 2565 return FALSE; 2566 } 2567 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0)); 2568 } 2569 2570 BOOL 2571 APIENTRY 2572 NtUserPrintWindow( 2573 HWND hwnd, 2574 HDC hdcBlt, 2575 UINT nFlags) 2576 { 2577 PWND Window; 2578 BOOL Ret = FALSE; 2579 2580 UserEnterExclusive(); 2581 2582 if (hwnd) 2583 { 2584 if (!(Window = UserGetWindowObject(hwnd)) || 2585 UserIsDesktopWindow(Window) || UserIsMessageWindow(Window)) 2586 { 2587 goto Exit; 2588 } 2589 2590 if ( Window ) 2591 { 2592 /* Validate flags and check it as a mask for 0 or 1. */ 2593 if ( (nFlags & PW_CLIENTONLY) == nFlags) 2594 Ret = IntPrintWindow( Window, hdcBlt, nFlags); 2595 else 2596 EngSetLastError(ERROR_INVALID_PARAMETER); 2597 } 2598 } 2599 Exit: 2600 UserLeave(); 2601 return Ret; 2602 } 2603 2604 /* ValidateRect gets redirected to NtUserValidateRect: 2605 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */ 2606 BOOL 2607 APIENTRY 2608 NtUserValidateRect( 2609 HWND hWnd, 2610 const RECT *lpRect) 2611 { 2612 UINT flags = RDW_VALIDATE; 2613 if (!hWnd) 2614 { 2615 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW; 2616 lpRect = NULL; 2617 } 2618 return NtUserRedrawWindow(hWnd, lpRect, NULL, flags); 2619 } 2620 2621 /* EOF */ 2622