Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1874 lines
60 KiB

/****************************** 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;
}