|
|
/****************************** Module Header ******************************\
* Module Name: update.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains the APIs used to invalidate, validate, and force * updating of windows. * * History: * 27-Oct-1990 DarrinM Created. * 25-Jan-1991 IanJa Revalidation added * 16-Jul-1991 DarrinM Recreated from Win 3.1 sources. \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* Local Constants. */ #define UW_ENUMCHILDREN 0x0001
#define UW_RECURSED 0x0004
#define RIR_OUTSIDE 0
#define RIR_INTERSECT 1
#define RIR_INSIDE 2
#define RDW_IGNOREUPDATEDIRTY 0x8000
/***************************************************************************\
* xxxInvalidateRect (API) * * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxInvalidateRect( PWND pwnd, LPRECT lprcInvalid, BOOL fErase) { CheckLock(pwnd);
/*
* BACKWARD COMPATIBILITY HACK * * In Windows 3.0 and less, ValidateRect/InvalidateRect() call with * hwnd == NULL always INVALIDATED and ERASED the entire desktop, and * synchronously sent WM_ERASEBKGND and WM_NCPAINT messages before * returning. The Rgn() calls did not have this behavior. */ if (pwnd == NULL) { return xxxRedrawWindow( pwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE | RDW_ERASENOW); } else { return xxxRedrawWindow( pwnd, lprcInvalid, NULL, fErase ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE); } }
/***************************************************************************\
* xxxValidateRect (API) * * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxValidateRect( PWND pwnd, LPRECT lprcValid) { CheckLock(pwnd);
/*
* BACKWARD COMPATIBILITY HACK * * In Windows 3.0 and less, ValidateRect/InvalidateRect() call with * hwnd == NULL always INVALIDATED and ERASED the entire desktop, and * synchronously sent WM_ERASEBKGND and WM_NCPAINT messages before * returning. The Rgn() calls did not have this behavior. */ if (pwnd == NULL) { return xxxRedrawWindow( pwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE | RDW_ERASENOW); } else { return xxxRedrawWindow(pwnd, lprcValid, NULL, RDW_VALIDATE); } }
/***************************************************************************\
* xxxInvalidateRgn (API) * * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxInvalidateRgn( PWND pwnd, HRGN hrgnInvalid, BOOL fErase) { CheckLock(pwnd);
return xxxRedrawWindow( pwnd, NULL, hrgnInvalid, fErase ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE); }
/***************************************************************************\
* xxxValidateRgn (API) * * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxValidateRgn( PWND pwnd, HRGN hrgnValid) { CheckLock(pwnd);
return xxxRedrawWindow(pwnd, NULL, hrgnValid, RDW_VALIDATE); }
/***************************************************************************\
* SmartRectInRegion * * This routine is similar to RectInRegion, except that it also determines * whether or not *lprc is completely within hrgn or not. * * RIR_OUTSIDE - no intersection * RIR_INTERSECT - *lprc intersects hrgn, but not completely inside * RIR_INSIDE - *lprc is completely within hrgn. * * LATER: * It would be MUCH faster to put this functionality into GDI's RectInRegion * call (a la PM's RectInRegion) * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
UINT SmartRectInRegion( HRGN hrgn, LPRECT lprc) { RECT rc;
if (!GreRectInRegion(hrgn, lprc)) return RIR_OUTSIDE;
/*
* Algorithm: if the intersection of hrgn and *lprc is the * same as *lprc, then *lprc is completely within hrgn. * * If the region is a rectangular one, then do it the easy way. */ if (GreGetRgnBox(hrgn, &rc) == SIMPLEREGION) {
if (!IntersectRect(&rc, &rc, lprc)) return RIR_OUTSIDE;
if (EqualRect(lprc, &rc)) return RIR_INSIDE;
} else {
SetRectRgnIndirect(ghrgnInv2, lprc);
switch (IntersectRgn(ghrgnInv2, ghrgnInv2, hrgn)) {
case SIMPLEREGION: GreGetRgnBox(ghrgnInv2, &rc); if (EqualRect(lprc, &rc)) return RIR_INSIDE; break;
#define RECTINREGION_BUG
#ifdef RECTINREGION_BUG
/*
* NOTE: RectInRegion has a BUG, where it sometimes returns TRUE * even if the rectangles of a region touch only on the edges * with no overlap. This will result in an empty region after * the combination above. */ case NULLREGION: return RIR_OUTSIDE; break; #endif
default: break; } }
return RIR_INTERSECT; }
/***************************************************************************\
* PixieHack * * BACKWARD COMPATIBILITY HACK * * In 3.0, WM_NCPAINT messages would be sent to any child window that was * inside the bounding rectangle of a window management operation involving * any other child, even if the intersection of that region with the child * is empty. * * Some apps such as Pixie 2.3 and CA Cricket Presents rely on this to ensure * that their tool windows stay on top of other child windows. When the tool * window gets a WM_NCPAINT, it brings itself to the top of the pile. * * Borland ObjectVision depends on getting the WM_NCPAINT after an * invalidation of its parent window in an area that include the non-client * area of the child. When it recieves the WM_NCPAINT, it must get a * clip region of HRGN_FULL, or nothing gets drawn. * * History: * 02-Mar-1992 MikeKe Ported from Win 3.1 sources. \***************************************************************************/
VOID PixieHack( PWND pwnd, LPRECT prcBounds) { /*
* If a child intersects the update region, and it isn't already * getting an NCPAINT, then make sure it gets one later. * * Don't apply this hack to top level windows. */ if ((pwnd != _GetDesktopWindow()) && TestWF(pwnd, WFCLIPCHILDREN) && !TestWF(pwnd, WFMINIMIZED)) {
RECT rc;
for (pwnd = pwnd->spwndChild; pwnd; pwnd = pwnd->spwndNext) {
/*
* If the window isn't already getting an NCPAINT message, * and it has a caption, and it's inside the bounding rect, * make sure it gets a WM_NCPAINT with wParam == HRGN_FULL. */ if (!TestWF(pwnd, WFSENDNCPAINT) && (TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION)) && IntersectRect(&rc, prcBounds, &pwnd->rcWindow)) {
/*
* Sync paint count is incremented when * (senderasebkgnd | sendncpaint) goes from 0 to != 0. * (should make a routine out of this!) */ SetWF(pwnd, WFSENDNCPAINT);
/*
* Force HRGN_FULL clip rgn. */ SetWF(pwnd, WFPIXIEHACK); } } } }
/***************************************************************************\
* xxxRedrawWindow (API) * * Forward to xxxInvalidateWindow if the window is visible. * * BACKWARD COMPATIBILITY HACK * * In Windows 3.0 and less, ValidateRect/InvalidateRect() call with pwnd == NULL * always INVALIDATED and ERASED all windows, and synchronously sent * WM_ERASEBKGND and WM_NCPAINT messages before returning. The Rgn() calls * did not have this behavior. This case is handled in * InvalidateRect/ValidateRect. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxRedrawWindow( PWND pwnd, LPRECT lprcUpdate, HRGN hrgnUpdate, DWORD flags) { CheckLock(pwnd);
/*
* Always map NULL to the desktop. */ if (pwnd == NULL) { pwnd = PtiCurrent()->rpdesk->pDeskInfo->spwnd; }
UserAssert(pwnd != NULL);
if (IsVisible(pwnd)) {
TL tlpwnd; HRGN hrgn = hrgnUpdate;
if (flags & (RDW_VALIDATE | RDW_INVALIDATE)) {
/*
* Create a (in)validate region in client window coordinates. */ if (hrgn == NULL) { if (!lprcUpdate) { hrgn = HRGN_FULL; } else { hrgn = ghrgnInv0;
if (TestWF(pwnd, WEFLAYOUTRTL)) { MirrorClientRect(pwnd, lprcUpdate); }
if (pwnd == PWNDDESKTOP(pwnd)) { SetRectRgnIndirect(hrgn, lprcUpdate); } else { GreSetRectRgn( hrgn, lprcUpdate->left + pwnd->rcClient.left, lprcUpdate->top + pwnd->rcClient.top, lprcUpdate->right + pwnd->rcClient.left, lprcUpdate->bottom + pwnd->rcClient.top); } } } else { /*
* If necessary, make a copy of the passed-in region, because * we'll be trashing it... */ if (hrgn != HRGN_FULL) { CopyRgn(ghrgnInv0, hrgn); MirrorRegion(pwnd, ghrgnInv0, TRUE); hrgn = ghrgnInv0; }
if (pwnd != PWNDDESKTOP(pwnd)) { GreOffsetRgn(hrgn, pwnd->rcClient.left, pwnd->rcClient.top); } } }
ThreadLock(pwnd, &tlpwnd); xxxInternalInvalidate(pwnd, hrgn, flags | RDW_REDRAWWINDOW); ThreadUnlock(&tlpwnd); }
return TRUE; }
/***************************************************************************\
* InternalInvalidate2 * * (In)validates hrgn in pwnd and in child windows of pwnd. Child windows * also subtract their visible region from hrgnSubtract. * * pwnd - The window to (in)validate. * hrng - The region to (in)validate. * hrgnSubtract - The region to subtract the visible region of * child windows from. * prcParents - Contains the intersection of pwnd's client or window rect * with the client rectangles of its parents. May just be * the window's client or window rect. * * flags - RDW_ flags. * * Returns FALSE if hrgnSubtract becomes a NULLREGION, TRUE otherwise. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL InternalInvalidate2( PWND pwnd, HRGN hrgn, HRGN hrgnSubtract, LPRECT prcParents, DWORD flags) { /*
* NOTE: Uses ghrgnInv2 */ RECT rcOurShare; DWORD flagsChildren; PWND pwndT;
/*
* This routine is called recursively down the parent/child chain. * Remember if on the way one of the windows has a clipping region. * This info is used later on to optimize out a loop in the common * case. */ if (pwnd->hrgnClip != NULL) { flags |= RDW_HASWINDOWRGN; }
/*
* If we recurse, make sure our children subtract themselves off. */ flagsChildren = flags | RDW_SUBTRACTSELF; CopyRect(&rcOurShare, &pwnd->rcWindow);
/*
* If we're invalidating, we only want to deal with the part of * our window rectangle that intersects our parents. * This way, we don't end up validating or invalidating more than our * fair share. If we're completely obscured by our parents, then there is * nothing to do. * * We don't perform this intersection if we're validating, because there * are cases where a child and its update region may exist but be obscured * by parents, and we want to make sure validation will work in these * cases. ScrollWindow() can cause this when children are offset, as can * various 3.0 compatibility hacks. */
if (flags & RDW_INVALIDATE) { /*
* Don't subtract out any sprite windows from the invalid region. * Behave as if it's not there. However, always allow layered window * invalidation when RDW_INVALIDATELAYERS is passed in. */ #ifdef REDIRECTION
if ((TestWF(pwnd, WEFLAYERED) || TestWF(pwnd, WEFEXTREDIRECTED)) && #else // REDIRECTION
if ((TestWF(pwnd, WEFLAYERED)) && #endif // REDIRECTION
!(flags & RDW_INVALIDATELAYERS)) return TRUE;
if (!IntersectRect(&rcOurShare, &rcOurShare, prcParents)) {
/*
* BACKWARD COMPATIBILITY HACK: If hrgn is (HRGN)1, we need to * invalidate ALL child windows, even if they're not visible. This * is a bummer, because it'll result in all sorts of repaints that * aren't necessary. * * Various apps, including WordStar for Windows and WaveEdit, * depend on this behavior. Here's how WaveEdit relies on this: it * has a CS_HDREDRAW | CS_VREDRAW window, that moves its children * around with MoveWindow( ..., fRedraw = FALSE). The windows * not part of the new client area didn't get invalidated. */ if (!TestWF(pwnd, WFWIN31COMPAT) && (hrgn == HRGN_FULL)) {
/*
* For purposes of hit-testing, our share is our window * rectangle. However, we don't want to diddle the region * passed to us, because by rights we're really clipped out! */ flags &= ~RDW_SUBTRACTSELF; flagsChildren &= ~RDW_SUBTRACTSELF;
} else { return TRUE; } }
/*
* If our window rect doesn't intersect the valid/invalid region, * nothing further to do. */ if (hrgn > HRGN_FULL) {
switch (SmartRectInRegion(hrgn, &rcOurShare)) { case RIR_OUTSIDE: return TRUE;
case RIR_INTERSECT:
/*
* The update region can be within the window rect but not * touch the window region; in this case we don't want this * update region to be distributed to this window. If this * is the case, return TRUE as if RIR_OUTSIDE. * * If RDW_HASWINDOWRGN is set, either this window or * one of its parents has a window clipping region. This * flag is just an optimization so that this loop isn't * executed all the time. * * A future optimization may be to calculate this parent * clipped region as part of recursion like prcParents is * calculated. It is not super important though because this * case rarely happens (a window with a region), and even * more rare, a regional window that is a child of a regional * window parent. */ if (flags & RDW_HASWINDOWRGN) {
/*
* Clip to the window's clipping region and parents! * If we don't clip to parents, we may get a case where * a child clips out some update region that was meant to * go to a sibling of the parent. */ SetRectRgnIndirect(ghrgnInv2, &rcOurShare); for (pwndT = pwnd; pwndT != NULL; pwndT = pwndT->spwndParent) {
if (pwndT->hrgnClip != NULL) {
/*
* An error at this stage would possibly result * in more being subtracted out of the clipping * region that we'd like. */ IntersectRgn(ghrgnInv2, ghrgnInv2, pwndT->hrgnClip); } }
if (IntersectRgn(ghrgnInv2, ghrgnInv2, hrgn) == NULLREGION) return TRUE; } break;
case RIR_INSIDE: /*
* If the rectangle is completely within hrgn, then we can use * HRGN_FULL, which is much faster and easier to deal with. * * COMPAT HACK: There are some apps (PP, MSDRAW) that depend * on some weirdities of the 3.0 GetUpdateRect in order to * paint properly. Since this stuff hinges on whether the * update region is 1 or a real region, we need to simulate * when 3.0 would generate a HRGN(1) update region. The * following optimization was not made in 3.0, so we yank it * in 3.1 for these apps. (win31 bugs 8235,10380) */ if (!(GetAppCompatFlags(GETPTI(pwnd)) & GACF_NOHRGN1)) hrgn = HRGN_FULL; break; } }
/*
* While we are in the middle of compositing, no invalidation should * happen, or it will mess up our painting order. This is because on * this compositing pass we may validate some of the new invalid area * and the invalid area that didn't get validated will bleed through * on the next compositing pass. So we will remember an accumulated * invalid area which will really get invalidated once the compositing * pass is completed. */ if (TestWF(pwnd, WEFPCOMPOSITING)) { PREDIRECT prdr = _GetProp(pwnd, PROP_LAYER, TRUE);
if (prdr != NULL) { HRGN hrgnComp = prdr->hrgnComp;
if (hrgnComp == NULL) { if ((hrgnComp = CreateEmptyRgnPublic()) == NULL) { hrgnComp = HRGN_FULL; } }
SetRectRgnIndirect(ghrgnInv2, &rcOurShare);
if (hrgnComp != HRGN_FULL) { GreCombineRgn(hrgnComp, hrgnComp, ghrgnInv2, RGN_OR); }
prdr->hrgnComp = hrgnComp;
if (SubtractRgn(hrgnSubtract, hrgnSubtract, ghrgnInv2) == NULLREGION) { return FALSE; }
return TRUE; } } }
/*
* If not CLIPCHILDREN, go diddle the update region BEFORE our clipped * children have done their thing to hrgnSubtract. Otherwise, * we'll diddle after we recurse. */ if (!TestWF(pwnd, WFCLIPCHILDREN)) { InternalInvalidate3(pwnd, hrgn, flags); }
/*
* If this is a GACF_ALWAYSSENDNCPAINT app, take care of it... */ if (TestWF(pwnd, WFALWAYSSENDNCPAINT)) PixieHack(pwnd, &rcOurShare);
/*
* Recurse on our children if necessary. * * By default, our children are enumerated if we are not CLIPCHILDREN. * Don't bother with children if we're minimized. */ if ((pwnd->spwndChild != NULL) && !TestWF(pwnd, WFMINIMIZED) && !(flags & RDW_NOCHILDREN) && ((flags & RDW_ALLCHILDREN) || !TestWF(pwnd, WFCLIPCHILDREN))) {
RECT rcChildrenShare; PWND pwndChild;
/*
* If we're invalidating, make sure our children * erase and frame themselves. Also, tell children to subtract * themselves from hrgnSubtract. */ if (flags & RDW_INVALIDATE) { flagsChildren |= RDW_ERASE | RDW_FRAME; }
/*
* Our children are clipped to our client rect, so reflect * that in the rectangle we give them. */ if (IntersectRect(&rcChildrenShare, &rcOurShare, &pwnd->rcClient) || (!TestWF(pwnd, WFWIN31COMPAT) && (hrgn == HRGN_FULL))) {
for (pwndChild = pwnd->spwndChild; pwndChild != NULL; pwndChild = pwndChild->spwndNext) {
if (!TestWF(pwndChild, WFVISIBLE)) continue;
if (!InternalInvalidate2(pwndChild, hrgn, hrgnSubtract, &rcChildrenShare, flagsChildren)) {
/*
* The children swallowed the region: * If there are no update region related things * to do then we can just return with FALSE */ if (!(flags & (RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))) return FALSE;
/*
* We have to enumerate the rest of the children because * one of the RDW_NO/INTERNALPAINT bits is set. Since * there's no longer any update region to worry about, * strip out the update region bits from the parent * and child fiags. Also, tell the children not to * bother subtracting themselves from the region. */ flags &= ~(RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_VALIDATE | RDW_NOERASE | RDW_NOFRAME);
flagsChildren &= ~(RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_VALIDATE | RDW_NOERASE | RDW_NOFRAME | RDW_SUBTRACTSELF); } } } }
/*
* Go diddle the update region (AFTER our clipped children may have * done their thing to hrgnSubtract) */ if (TestWF(pwnd, WFCLIPCHILDREN)) InternalInvalidate3(pwnd, hrgn, flags);
/*
* If we're invalidating and we're supposed to, * try to subtract off our window area from the region. * * This way our parent and our siblings below us will not * get any update region for areas that don't need one. */ if (flags & RDW_SUBTRACTSELF) {
/*
* Subtract our visible region from the update rgn only if: * a) we're not a transparent window * b) we are clipsiblings * c) we're validating OR our parent is clipchildren. * * The check for validation is a backward-compatibility hack: this * is what 3.0 did, so this is what we do here. * * BACKWARD COMPATIBILITY HACK * * In 3.0, we subtracted this window from the update rgn if it * was clipsiblings, even if the parent was not clipchildren. * This causes a compatibility problem for Lotus Notes 3.1: it * has a combobox dropdown in a dialog that is a WS_CLIPSIBLING * sibling of the other dialog controls, which are not WS_CLIPSIBLINGs. * The dialog is not WS_CLIPCHILDREN. What happens is that a listbox * underneath the dropdown also gets a paint msg (since we didn't * do this subtraction), and, since it's not CLIPSIBLINGS, it * obliterates the dropdown. * * This is a very obscure difference, and it's too late in the * project to make this change now, so we're leaving the code as is * and using a compatibility hack to enable the 3.0-compatible * behavior. It's quite likely that this code works the way it does * for other compatibility reasons. Sigh (neilk). */ if (!TestWF(pwnd, WEFTRANSPARENT) && TestWF(pwnd, WFCLIPSIBLINGS) && ((flags & RDW_VALIDATE) || ((pwnd->spwndParent != NULL) && (TestWF(pwnd->spwndParent, WFCLIPCHILDREN) || (GetAppCompatFlags(GETPTI(pwnd)) & GACF_SUBTRACTCLIPSIBS))))) {
/*
* Intersect with our visible area. * * Don't worry about errors: an error will result in more, not less * area being invalidated, which is okay. */ SetRectRgnIndirect(ghrgnInv2, &rcOurShare);
/*
* If RDW_HASWINDOWRGN is set, either this window or * one of its parents has a window clipping region. This * flag is just an optimization so that this loop isn't * executed all the time. */ if (flags & RDW_HASWINDOWRGN) {
/*
* Clip to the window's clipping region and parents! * If we don't clip to parents, we may get a case where * a child clips out some update region that was meant to * go to a sibling of the parent. */ for (pwndT = pwnd; pwndT != NULL; pwndT = pwndT->spwndParent) {
if (pwndT->hrgnClip != NULL) {
/*
* An error at this stage would possibly result in more * being subtracted out of the clipping region that * we'd like. */ IntersectRgn(ghrgnInv2, ghrgnInv2, pwndT->hrgnClip); } } }
#if 1
/*
* TEMP HACK!!! RE-ENABLE this code when regions work again */ if (SubtractRgn(hrgnSubtract, hrgnSubtract, ghrgnInv2) == NULLREGION) return FALSE; #else
{ DWORD iRet;
iRet = SubtractRgn(hrgnSubtract, hrgnSubtract, ghrgnInv2);
if (iRet == NULLREGION) return FALSE;
if (iRet == SIMPLEREGION) { RECT rcSub; GreGetRgnBox(hrgnSubtract, &rcSub); if (rcSub.left > rcSub.right) return FALSE; } } #endif
} }
return TRUE; }
/***************************************************************************\
* InternalInvalidate3 * * Adds or subtracts hrgn to the windows update region and sets appropriate * painting state flags. * * pwnd - The window. * hrng - The region to add to the update region. * flags - RDW_ flags. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
VOID InternalInvalidate3( PWND pwnd, HRGN hrgn, DWORD flags) { BOOL fNeededPaint;
fNeededPaint = NEEDSPAINT(pwnd);
if (flags & (RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ERASE | RDW_FRAME)) {
if (flags & RDW_INTERNALPAINT) SetWF(pwnd, WFINTERNALPAINT);
if (flags & RDW_INVALIDATE) {
/*
* Make sure that the NONCPAINT bit is cleared * to ensure that the caption will redraw when we update. */ ClrWF(pwnd, WFNONCPAINT);
/*
* If another app is invalidating this window, then set the * UPDATEDIRTY flag. * * Solves critical section where thread A draws, then validates, * but thread B goes and invalidates before A validates. * See comments later in RDW_VALIDATE code. */ if (GETPTI(pwnd) != PtiCurrent()) {
SetWF(pwnd, WFUPDATEDIRTY);
/*
* Paint order problem, see paint.c */ if (TestWF(pwnd, WFWMPAINTSENT)) { SetWF(pwnd, WFDONTVALIDATE); } }
/*
* BACKWARD COMPATIBILITY HACK * * In 3.0, InvalidateRect(pwnd, NULL, FALSE) would always * clear the WFSENDERASEBKGND flag, even if it was previously * set from an InvalidateRect(pwnd, NULL, TRUE). This is bogus, * because it can cause you to "lose" WM_ERASEBKGND messages, but * AttachMate Extra (and maybe other apps) depend on this behavior. */ if ((hrgn == HRGN_FULL) && !TestWF(pwnd, WFWIN31COMPAT)) ClrWF(pwnd, WFSENDERASEBKGND);
if (flags & RDW_ERASE) SetWF(pwnd, WFSENDERASEBKGND);
if ((flags & (RDW_FRAME | RDW_ERASE)) && !TestWF(pwnd, WEFTRANSPARENT)) SetHungFlag(pwnd, WFREDRAWIFHUNG);
if (flags & RDW_FRAME) SetWF(pwnd, WFSENDNCPAINT);
/*
* If window is already completely invalidated, * no need to do any further invalidation. */ if (pwnd->hrgnUpdate != HRGN_FULL) {
if (hrgn == HRGN_FULL) { InvalidateAll: DeleteMaybeSpecialRgn(pwnd->hrgnUpdate); pwnd->hrgnUpdate = HRGN_FULL;
} else { if (pwnd->hrgnUpdate == NULL) {
if (!(pwnd->hrgnUpdate = CreateEmptyRgnPublic())) goto InvalidateAll;
if (CopyRgn(pwnd->hrgnUpdate, hrgn) == ERROR) goto InvalidateAll;
} else { // pwnd->hrgnUpdate is a region
if (UnionRgn(pwnd->hrgnUpdate, pwnd->hrgnUpdate, hrgn) == ERROR) {
goto InvalidateAll; } } } } }
if (!fNeededPaint && NEEDSPAINT(pwnd)) IncPaintCount(pwnd);
} else if (flags & (RDW_VALIDATE | RDW_NOINTERNALPAINT | RDW_NOERASE | RDW_NOFRAME)) {
/*
* Validation: * * Do not allow validation if this window has been invalidated from * another process - because this window may be validating just * after another process invalidated, thereby validating invalid * bits. * * Sometimes applications draw stuff, then validate what they drew. * If another app invalidated some area during the drawing operation, * then it will need another paint message. * * This wouldn't be necessary if people validated BEFORE they drew. */ if (TestWF(pwnd, WFUPDATEDIRTY) && !(flags & RDW_IGNOREUPDATEDIRTY)) return;
if (flags & RDW_NOINTERNALPAINT) ClrWF(pwnd, WFINTERNALPAINT);
if (flags & RDW_VALIDATE) {
if (flags & RDW_NOERASE) ClrWF(pwnd, WFSENDERASEBKGND);
if (flags & RDW_NOFRAME) { ClrWF(pwnd, WFSENDNCPAINT); ClrWF(pwnd, WFPIXIEHACK); }
if (flags & (RDW_NOERASE | RDW_NOFRAME)) ClearHungFlag(pwnd, WFREDRAWIFHUNG);
if (pwnd->hrgnUpdate != NULL) {
/*
* If WFSENDNCPAINT is set, then all or part of the * window border still needs to be drawn. This means * that we must subtract off the client rectangle only. * Convert HRGN_FULL to the client region. */ if (TestWF(pwnd, WFSENDNCPAINT) && (hrgn == HRGN_FULL)) { hrgn = ghrgnInv2; CalcWindowRgn(pwnd, hrgn, TRUE); }
if (hrgn == HRGN_FULL) { ValidateAll:
/*
* We're validating the entire window. Just * blow away the update region. */ DeleteMaybeSpecialRgn(pwnd->hrgnUpdate); pwnd->hrgnUpdate = (HRGN)NULL;
/*
* No need to erase the background... */ ClrWF(pwnd, WFSENDERASEBKGND); ClearHungFlag(pwnd, WFREDRAWIFHUNG);
} else {
/*
* Subtracting some region from pwnd->hrgnUpdate. * Be sure pwnd->hrgnUpdate is a real region. */ if (pwnd->hrgnUpdate == HRGN_FULL) {
/*
* If the WFSENDNCPAINT bit is set, * the update region must include the entire window * area. Otherwise it includes only the client. */ pwnd->hrgnUpdate = CreateEmptyRgnPublic();
/*
* If the creation failed, punt by * invalidating the entire window. */ if (pwnd->hrgnUpdate == NULL) goto InvalidateAll;
if (CalcWindowRgn(pwnd, pwnd->hrgnUpdate, !(TestWF(pwnd, WFSENDNCPAINT))) == ERROR) {
goto InvalidateAll; } }
/*
* Subtract off the region. If we get an error, * punt by invalidating everything. If the * region becomes empty, then validate everything. */ switch (SubtractRgn(pwnd->hrgnUpdate, pwnd->hrgnUpdate, hrgn)) { case ERROR: goto InvalidateAll;
case NULLREGION: goto ValidateAll; } } } }
if (fNeededPaint && !NEEDSPAINT(pwnd)) DecPaintCount(pwnd); } }
/***************************************************************************\
* ValidateParents * * This routine validates hrgn from the update regions of the parent windows * between pwnd and its first clip children parent. * If hrgn is NULL, then the window rect (intersected with all parents) * is validated. * * This routine is called when a window is being drawn in * UpdateWindow() so that non-CLIPCHILDREN parents * of windows being redrawn won't draw on their valid children. * * Returns FALSE if fRecurse is TRUE and a non-CLIPCHILDREN parent * has an update region; otherwise, returns TRUE. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL ValidateParents( PWND pwnd, BOOL fRecurse) { RECT rcParents; RECT rc; PWND pwndParent = pwnd; BOOL fInit = FALSE;
/*
* This is checking whether we are in an in-between state, just before * a WM_SYNCPAINT is about to arrive. If not, then ValidateParents() * needs to work like it did in Win 3.1. */ while (TestWF(pwndParent, WFCHILD)) pwndParent = pwndParent->spwndParent;
if (!TestWF(pwndParent, WFSYNCPAINTPENDING)) fRecurse = FALSE;
pwndParent = pwnd;
while ((pwndParent = pwndParent->spwndParent) != NULL) {
/*
* Stop when we find a clipchildren parent */ if (TestWF(pwndParent, WFCLIPCHILDREN)) break;
/*
* Subtract the region from this parent's update region, * if it has one. */ if (pwndParent->hrgnUpdate != NULL) { if (fRecurse) { return FALSE; } if (!fInit) { fInit = TRUE;
/*
* Do initial setup. If our window rectangle is * completely obscured, get out. */ rc = pwnd->rcWindow; if (!IntersectWithParents(pwnd, &rc)) break;
SetRectRgnIndirect(ghrgnInv1, &rc);
/*
* If this window has a region, make sure the piece being validated * is within this region. */ if (pwnd->hrgnClip != NULL) {
/*
* If we get NULLREGION back, there is nothing to validate * against parents, so break out. If ERROR gets returned, * there is not much we can do: the best "wrong" thing * to do is just continue and validate a little more * from the parent. */ if (!IntersectRgn(ghrgnInv1, ghrgnInv1, pwnd->hrgnClip)) break; } }
/*
* Calculate the rcParents parameter to * pass up to InternalInvalidate2. */ rcParents = pwndParent->rcWindow;
if (!IntersectWithParents(pwndParent, &rcParents)) break;
InternalInvalidate2( pwndParent, ghrgnInv1, ghrgnInv1, &rcParents, RDW_VALIDATE | RDW_NOCHILDREN | RDW_IGNOREUPDATEDIRTY); } }
return TRUE; }
/***************************************************************************\
* xxxUpdateWindow2 * * Sends a WM_PAINT message to the window if it needs painting, * then sends the message to its children. * * Always returns TRUE. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
void xxxUpdateWindow2( PWND pwnd, DWORD flags) { TL tlpwnd;
CheckLock(pwnd);
if (NEEDSPAINT(pwnd)) {
/*
* Punch a hole in our parent's update region, if we have one. */ if (pwnd->hrgnUpdate) { if (ValidateParents(pwnd, flags & UW_RECURSED) == FALSE) { return; } }
/*
* Now that we're sending the message, clear the * internal paint bit if it was previously set. */ if (TestWF(pwnd, WFINTERNALPAINT)) {
ClrWF(pwnd, WFINTERNALPAINT);
/*
* If there is no update region, then no further paint messages * are pending, so we must dec the paint count. */ if (pwnd->hrgnUpdate == NULL) DecPaintCount(pwnd); }
/*
* Set a flag indicating that a paint message was not processed * (but should be). */ SetWF(pwnd, WFPAINTNOTPROCESSED);
/*
* Clear this bit, for apps (like MicroLink) that don't call * BeginPaint or GetUpdateRect/Rgn (but DO call ValidateRect) * when handling their WM_PAINT message. */ ClrWF(pwnd, WFUPDATEDIRTY);
/*
* BACKWARD COMPATIBILITY HACK * * Win 3.0 always sent WM_PAINTICON with wParam == TRUE for no good * reason, and Lotus Notes has come to depend on this. */ if (!TestWF(pwnd, WFWIN40COMPAT) && TestWF(pwnd, WFMINIMIZED) && (pwnd->pcls->spicn != NULL)) {
xxxSendMessage(pwnd, WM_PAINTICON, TRUE, 0L);
} else {
xxxSendMessage(pwnd, WM_PAINT, 0, 0L); }
/*
* If the guy didn't call BeginPaint/EndPaint(), or GetUpdateRect/Rgn * with fErase == TRUE, then we have to clean up for him here. */ if (TestWF(pwnd, WFPAINTNOTPROCESSED)) {
RIPMSG0(RIP_VERBOSE, "App didn't call BeginPaint() or GetUpdateRect/Rgn(fErase == TRUE) in WM_PAINT");
xxxSimpleDoSyncPaint(pwnd); } }
/*
* For desktop window, do not force the top level window repaint at this * this point. We are calling UpdateWindow() for the desktop before * size/move is sent for the top level windows. * * BUG: The comment above seems a bit random. Is there really a problem? * If nothing else this has to remain this way because it is * how Win 3.0 worked (neilk) */ if ((flags & UW_ENUMCHILDREN) && (pwnd != PWNDDESKTOP(pwnd))) {
/*
* Update any children... */ ThreadLockNever(&tlpwnd); pwnd = pwnd->spwndChild; while (pwnd != NULL) {
/*
* If there is a transparent window that needs painting, * skip it if another window below it needs to paint. */ if (TestWF(pwnd, WEFTRANSPARENT) && NEEDSPAINT(pwnd)) {
PWND pwndT = pwnd;
while ((pwndT = pwndT->spwndNext) != NULL) { if (NEEDSPAINT(pwndT)) break; }
if (pwndT != NULL) { pwnd = pwnd->spwndNext; continue; } }
ThreadLockExchangeAlways(pwnd, &tlpwnd); xxxUpdateWindow2(pwnd, flags | UW_RECURSED); pwnd = pwnd->spwndNext; }
ThreadUnlock(&tlpwnd); }
return; }
/***************************************************************************\
* xxxInternalUpdateWindow * * Sends a WM_PAINT message to the window if it needs painting, * then sends the message to its children. Won't send WM_PAINT * if the window is transparent and has siblings that need * painting. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
void xxxInternalUpdateWindow( PWND pwnd, DWORD flags) { PWND pwndComp;
CheckLock(pwnd);
if ((pwndComp = GetStyleWindow(pwnd, WEFCOMPOSITED)) != NULL) { TL tlpwnd; ThreadLockAlways(pwndComp, &tlpwnd); xxxCompositedPaint(pwndComp); ThreadUnlock(&tlpwnd); return; }
/*
* If the passed-in window is transparent and a sibling below * needs repainting, don't do anything. */ if (TestWF(pwnd, WEFTRANSPARENT)) {
PWND pwndT = pwnd; PTHREADINFO ptiCurrent = GETPTI(pwnd);
while ((pwndT = pwndT->spwndNext) != NULL) {
/*
* Make sure sibling window belongs to same app. */ if (GETPTI(pwndT) != ptiCurrent) continue;
if (NEEDSPAINT(pwndT)) return; } }
/*
* Enumerate pwnd and all its children, sending WM_PAINTs as needed. */ xxxUpdateWindow2(pwnd, flags); }
/***************************************************************************\
* xxxInternalInvalidate * * (In)validates hrgnUpdate and updates the window. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
VOID xxxInternalInvalidate( PWND pwnd, HRGN hrgnUpdate, DWORD flags) { RECT rcParents; HRGN hrgnSubtract; PWND pwndComp = NULL; PWND pwndSave; HRGN hrgnComp;
#if DBG
if (flags & (RDW_ERASENOW | RDW_UPDATENOW)) { CheckLock(pwnd); } #endif
/*
* For children of composited windows, invalidate starting from the * composited window itself. */ if (flags & RDW_INVALIDATE) { if ((pwndComp = GetStyleWindow(pwnd, WEFCOMPOSITED)) != NULL) {
if (hrgnUpdate == HRGN_FULL) { hrgnComp = GreCreateRectRgnIndirect(&pwnd->rcWindow);
if (hrgnComp != NULL) { hrgnUpdate = hrgnComp; } } else { hrgnComp = NULL; }
pwndSave = pwnd; pwnd = pwndComp;
flags |= RDW_ALLCHILDREN; } }
/*
* Allow invalidation of a layered window when someone specifically * invalidates it. This will also prevent invalidation of layered * windows during recursive desktop invalidations. */ #ifdef REDIRECTION
if (TestWF(pwnd, WEFLAYERED) || TestWF(pwnd, WEFEXTREDIRECTED)) { #else // REDIRECTION
if (TestWF(pwnd, WEFLAYERED)) { #endif // REDIRECTION
flags |= RDW_INVALIDATELAYERS; }
/*
* Ensure that hrgnSubtract is a valid region: if it's NULLREGION, * use the client region. */ rcParents = (flags & RDW_FRAME ? pwnd->rcWindow : pwnd->rcClient);
if (flags & (RDW_VALIDATE | RDW_INVALIDATE)) {
hrgnSubtract = hrgnUpdate;
if (hrgnSubtract == HRGN_FULL) {
hrgnSubtract = ghrgnInv1; CalcWindowRgn(pwnd, hrgnSubtract, (flags & RDW_FRAME) ? FALSE : TRUE); }
/*
* Calculate the bounding rectangle of our screen real estate, * by intersecting with our parent rectangles. While we're at * it, check the visibility of ourself and our parents. * * If we're validating we want to skip this, since there * are a number of cases where obscured windows may have * update regions to be validated -- in particular, after * a ScrollWindow() call where a child window was offset * by OffsetChildren() to a new, obscured position. Some of * the 3.0 compatibility hacks also can lead to this situation. */ if ((flags & RDW_INVALIDATE) && !IntersectWithParents(pwnd, &rcParents)) return;
} else { /*
* hrgnsubtract needs to be a real region even if * we are not invalidating or validating. It really doesn't * matter what the region is, but we set it to null so the code * has less degrees of freedom. */ hrgnSubtract = ghrgnInv1; SetEmptyRgn(hrgnSubtract); }
/*
* If we're invalidating, and we're being called by the app, * we need to invalidate any SPBs that might be affected by * drawing in the client area of this window. * We have to do this because there is no guarantee that the * application will draw in an area that is invalidated * (e.g., if the window is completely obscured by another). */ if ( (flags & (RDW_INVALIDATE | RDW_REDRAWWINDOW)) == (RDW_INVALIDATE | RDW_REDRAWWINDOW) && AnySpbs()) {
RECT rcInvalid;
/*
* Intersect the parent's rect with the region bounds... */ GreGetRgnBox(hrgnSubtract, &rcInvalid); IntersectRect(&rcInvalid, &rcInvalid, &rcParents); SpbCheckRect(pwnd, &rcInvalid, 0); }
/*
* Now go do the recursive update region calculations... */ InternalInvalidate2(pwnd, hrgnUpdate, hrgnSubtract, &rcParents, flags);
if (pwndComp != NULL) { pwnd = pwndSave;
if (hrgnComp != NULL) { GreDeleteObject(hrgnComp); } }
/*
* Finally handle any needed drawing. * * (NOTE: RDW_UPDATENOW implies RDW_ERASENOW) */ if (flags & RDW_UPDATENOW) {
xxxInternalUpdateWindow(pwnd, flags & RDW_NOCHILDREN ? 0 : UW_ENUMCHILDREN);
} else if (flags & RDW_ERASENOW) {
UINT flagsDSP;
if (flags & RDW_NOCHILDREN) { flagsDSP = 0; } else if (flags & RDW_ALLCHILDREN) { flagsDSP = DSP_ALLCHILDREN; } else { flagsDSP = DSP_ENUMCLIPPEDCHILDREN; }
xxxDoSyncPaint(pwnd, flagsDSP); } }
/***************************************************************************\
* UpdateWindow (API) * * Updates the window and all its children. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxUpdateWindow( PWND pwnd) { CheckLock(pwnd);
xxxInternalUpdateWindow(pwnd, UW_ENUMCHILDREN);
/*
* This function needs to return a value, since it is * called through NtUserCallHwndLock. */ return TRUE; }
/***************************************************************************\
* ExcludeUpdateRgn (API) * * ENTRY: hdc - DC to exclude from * pwnd - window handle * * EXIT: GDI region type * * WARNINGS: The DC is assumed to correspond to the client area of the window. * * The map mode of hdc MUST be text mode (0, 0 is top left corner, * one pixel per unit, ascending down and to right) or things won't * work. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
int _ExcludeUpdateRgn( HDC hdc, PWND pwnd) { POINT pt;
if (pwnd->hrgnUpdate == NULL) {
RECT rc;
/*
* Pass FALSE for fXForm since &rc isn't used. */ return GreGetClipBox(hdc, &rc, FALSE);
} else if (pwnd->hrgnUpdate == HRGN_FULL) {
return GreIntersectClipRect(hdc, 0, 0, 0, 0);
} else {
/*
* If no clip rgn exists, then subtract from a device-sized clip rgn. * (GetClipRgn returns clip rgn in screen coordinates). */ GreGetDCOrg(hdc, &pt); if (GreGetRandomRgn(hdc, ghrgnInv1, 1) != 1) { CopyRgn(ghrgnInv1, gpDispInfo->hrgnScreen); } else {
/*
* Gets returned in dc coords - translate to screen. */ GreOffsetRgn(ghrgnInv1, pt.x, pt.y); }
SubtractRgn(ghrgnInv1, ghrgnInv1, pwnd->hrgnUpdate);
/*
* Map to dc coords before select */ GreOffsetRgn(ghrgnInv1, -pt.x, -pt.y);
return GreExtSelectClipRgn(hdc, ghrgnInv1, RGN_COPY); } }
/***************************************************************************\
* GetUpdateRect (API) * * Returns the bounding rectangle of the update region, or an empty rectangle * if there is no update region. Rectangle is in client-relative coordinates. * * Returns TRUE if the update region is non-empty, FALSE if there is no * update region. * * lprc may be NULL to query whether or not an update region exists at all * or not. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL xxxGetUpdateRect( PWND pwnd, LPRECT lprc, BOOL fErase) { RECT rc;
CheckLock(pwnd);
if (fErase) xxxSimpleDoSyncPaint(pwnd);
/*
* The app is looking at the update region: okay to allow window * validation. */ ClrWF(pwnd, WFUPDATEDIRTY);
if (pwnd->hrgnUpdate == NULL) {
if (lprc) { SetRectEmpty(lprc); }
return FALSE;
} else {
/*
* We must handle the case where a window has an update region, * but it is completely obscured by its parents. In this case, we * must validate the window and all its children, and return FALSE. * * An OffsetChildren() call resulting from SetWindowPos() or * ScrollWindowEx() will cause this to happen. Update regions are * just offset without checking their new positions to see if they * are obscured by the parent(s). This is too painful to check in * those cases, so we instead handle it here. * * BeginPaint() handles this case correctly by returning an empty * rectangle, so nothing special need be done there. */ if (pwnd->hrgnUpdate == HRGN_FULL) {
rc = pwnd->rcClient;
} else {
switch (GreGetRgnBox(pwnd->hrgnUpdate, &rc)) { case ERROR: case NULLREGION: SetRectEmpty(&rc); break;
case SIMPLEREGION: case COMPLEXREGION: break; }
IntersectRect(&rc, &rc, &pwnd->rcClient); }
if (IntersectWithParents(pwnd, &rc)) {
if (pwnd != PWNDDESKTOP(pwnd)) { OffsetRect(&rc, -pwnd->rcClient.left, -pwnd->rcClient.top); }
/*
* If the window is CS_OWNDC, then we must map the returned * rectangle with DPtoLP, to ensure that the rectangle is * in the same coordinate system as the rectangle returned * by BeginPaint(). * * BUT ONLY IF hwnd->hrgnUpdate != HRGN_FULL! For true * compatibility with 3.0. */ if (TestCF(pwnd, CFOWNDC) && (TestWF(pwnd, WFWIN31COMPAT) || pwnd->hrgnUpdate != HRGN_FULL)) {
PDCE pdce;
/*
* Look up this window's DC in the cache, and use it to * map the returned rectangle. */ for (pdce = gpDispInfo->pdceFirst; pdce; pdce = pdce->pdceNext) {
if (pdce->pwndOrg == pwnd && !(pdce->DCX_flags & DCX_CACHE)) { GreDPtoLP(pdce->hdc, (LPPOINT)&rc, 2); break; } } }
} else { SetRectEmpty(&rc); } }
if (lprc) { if (TestWF(pwnd, WEFLAYOUTRTL)) { MirrorClientRect(pwnd, &rc); } *lprc = rc; }
/*
* If we're in the process a dragging a full window, mark the start * of the application painting. This is to make sure that if the * application calls DefWindowProc on the WM_PAINT after painting, we * won't erase the newly painted areas. Visual Slick calls GetUpdateRect * and then DefWindowProc. * See other comments for xxxBeginPaint and xxxDWP_Paint. * 8/3/94 johannec * * NOTE: This causes other problems in vslick where some controls * won't paint. Since the app doesn't call BeginPaint/EndPaint * to truly set/clear the STARTPAINT flag, we do not clear this * bit. (6-27-1996 : ChrisWil). * * * if (TEST_PUDF(PUDF_DRAGGINGFULLWINDOW)) { * SetWF(pwnd, WFSTARTPAINT); * } */
return TRUE; }
/***************************************************************************\
* GetUpdateRgn (API) * * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
int xxxGetUpdateRgn( PWND pwnd, HRGN hrgn, BOOL fErase) { RECT rc; int code; BOOL fNotEmpty;
CheckLock(pwnd);
if (fErase) xxxSimpleDoSyncPaint(pwnd);
/*
* The application is looking at the update region: okay to * allow validation */ ClrWF(pwnd, WFUPDATEDIRTY);
if (pwnd->hrgnUpdate == NULL) goto ReturnEmpty;
rc = pwnd->rcClient; fNotEmpty = IntersectWithParents(pwnd, &rc);
if (pwnd->hrgnUpdate == HRGN_FULL) {
/*
* Since the update region may be larger than the window * rectangle, intersect it with the window rectangle. */ if (!fNotEmpty) goto ReturnEmpty;
code = SIMPLEREGION;
/*
* Normalize the rectangle\region relative to the unclipped window */ if (pwnd != PWNDDESKTOP(pwnd)) { OffsetRect(&rc, -pwnd->rcClient.left, -pwnd->rcClient.top); }
SetRectRgnIndirect(hrgn, &rc);
} else {
SetRectRgnIndirect(ghrgnInv2, &rc); code = IntersectRgn(hrgn, ghrgnInv2, pwnd->hrgnUpdate);
switch (code) { case NULLREGION: case ERROR: goto ReturnEmpty;
default: if (pwnd != PWNDDESKTOP(pwnd)) { GreOffsetRgn(hrgn, -pwnd->rcClient.left, -pwnd->rcClient.top); } break; } }
MirrorRegion(pwnd, hrgn, TRUE);
/*
* If we're in the process a dragging a full window, mark the start * of the application painting. This is to make sure that if the * application calls DefWindowProc on the WM_PAINT after painting, we * won't erase the newly painted areas. * See other comments for xxxBeginPaint and xxxDWP_Paint. * 8/3/94 johannec * * NOTE: This causes other problems in vslick where some controls * won't paint. Since the app doesn't call BeginPaint/EndPaint * to truly set/clear the STARTPAINT flag, we do not clear this * bit. (6-27-1996 : ChrisWil). * * if (TEST(PUDF(PUDF_DRAGGINGFULLWINDOW)) { * SetWF(pwnd, WFSTARTPAINT); * } */
return code;
ReturnEmpty: SetEmptyRgn(hrgn); return NULLREGION; }
/***************************************************************************\
* IntersectWithParents * * This routine calculates the intersection of a rectangle with the client * rectangles of all of pwnd's parents. Returns FALSE if the intersection * is empty, a window is invisible, or a parent is minimized. * * Stop the intesesection if the window itself or any of its parents are * layered windows, so we always have a complete bitmap of them. * * History: * 16-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/
BOOL IntersectWithParents( PWND pwnd, LPRECT lprc) { if (TestWF(pwnd, WEFPREDIRECTED)) return TRUE;
while ((pwnd = pwnd->spwndParent) != NULL) {
if (!TestWF(pwnd, WFVISIBLE) || TestWF(pwnd, WFMINIMIZED)) return FALSE;
if (!IntersectRect(lprc, lprc, &pwnd->rcClient)) return FALSE;
if (TestWF(pwnd, WEFPREDIRECTED)) return TRUE; }
return TRUE; }
|