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.
1538 lines
46 KiB
1538 lines
46 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: paint.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains the APIs used to begin and end window painting.
|
|
*
|
|
* History:
|
|
* 27-Oct-1990 DarrinM Created.
|
|
* 12-Feb-1991 IanJa HWND revalidation added
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/***************************************************************************\
|
|
* xxxFillWindow (not an API)
|
|
*
|
|
* pwndBrush - The brush is aligned with with client rect of this window.
|
|
* It is usually either pwndPaint or pwndPaint's parent.
|
|
*
|
|
* pwndPaint - The window to paint.
|
|
* hdc - The DC to paint in.
|
|
* hbr - The brush to use.
|
|
*
|
|
* Returns TRUE if successful, FALSE if not.
|
|
*
|
|
* History:
|
|
* 15-Nov-1990 DarrinM Ported from Win 3.0 sources.
|
|
* 21-Jan-1991 IanJa Prefix '_' denoting exported function.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxFillWindow(
|
|
PWND pwndBrush,
|
|
PWND pwndPaint,
|
|
HDC hdc,
|
|
HBRUSH hbr)
|
|
{
|
|
RECT rc;
|
|
|
|
CheckLock(pwndBrush);
|
|
CheckLock(pwndPaint);
|
|
|
|
/*
|
|
* If there is no pwndBrush (sometimes the parent), use pwndPaint.
|
|
*/
|
|
if (pwndBrush == NULL)
|
|
pwndBrush = pwndPaint;
|
|
|
|
if (UT_GetParentDCClipBox(pwndPaint, hdc, &rc))
|
|
return xxxPaintRect(pwndBrush, pwndPaint, hdc, hbr, &rc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxPaintRect
|
|
*
|
|
* pwndBrush - The brush is aligned with with client rect of this window.
|
|
* It is usually either pwndPaint or pwndPaint's parent.
|
|
*
|
|
* pwndPaint - The window to paint in.
|
|
* hdc - The DC to paint in.
|
|
* hbr - The brush to use.
|
|
* lprc - The rectangle to paint.
|
|
*
|
|
* History:
|
|
* 15-Nov-1990 DarrinM Ported from Win 3.0 sources.
|
|
* 21-Jan-1991 IanJa Prefix '_' denoting exported function.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxPaintRect(
|
|
PWND pwndBrush,
|
|
PWND pwndPaint,
|
|
HDC hdc,
|
|
HBRUSH hbr,
|
|
LPRECT lprc)
|
|
{
|
|
POINT ptOrg;
|
|
|
|
CheckLock(pwndBrush);
|
|
CheckLock(pwndPaint);
|
|
|
|
if (pwndBrush == NULL) {
|
|
pwndBrush = PtiCurrent()->rpdesk->pDeskInfo->spwnd;
|
|
}
|
|
|
|
if (pwndBrush == PWNDDESKTOP(pwndBrush)) {
|
|
GreSetBrushOrg(
|
|
hdc,
|
|
0,
|
|
0,
|
|
&ptOrg);
|
|
} else {
|
|
GreSetBrushOrg(
|
|
hdc,
|
|
pwndBrush->rcClient.left - pwndPaint->rcClient.left,
|
|
pwndBrush->rcClient.top - pwndPaint->rcClient.top,
|
|
&ptOrg);
|
|
}
|
|
|
|
/*
|
|
* If hbr < CTLCOLOR_MAX, it isn't really a brush but is one of our
|
|
* special color values. Translate it to the appropriate WM_CTLCOLOR
|
|
* message and send it off to get back a real brush. The translation
|
|
* process assumes the CTLCOLOR*** and WM_CTLCOLOR*** values map directly.
|
|
*/
|
|
if (hbr < (HBRUSH)CTLCOLOR_MAX) {
|
|
hbr = xxxGetControlColor(pwndBrush,
|
|
pwndPaint,
|
|
hdc,
|
|
HandleToUlong(hbr) + WM_CTLCOLORMSGBOX);
|
|
}
|
|
|
|
FillRect(hdc, lprc, hbr);
|
|
|
|
GreSetBrushOrg(hdc, ptOrg.x, ptOrg.y, NULL);
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DeleteMaybeSpecialRgn
|
|
*
|
|
* Deletes a GDI region, making sure it is not a special region.
|
|
*
|
|
* History:
|
|
* 26-Feb-1992 MikeKe from win3.1
|
|
\***************************************************************************/
|
|
|
|
VOID DeleteMaybeSpecialRgn(
|
|
HRGN hrgn)
|
|
{
|
|
if (hrgn > HRGN_SPECIAL_LAST) {
|
|
GreDeleteObject(hrgn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* GetNCUpdateRgn
|
|
*
|
|
* Gets the update region which includes the non-client area.
|
|
*
|
|
* History:
|
|
* 26-Feb-1992 MikeKe From win3.1
|
|
\***************************************************************************/
|
|
|
|
HRGN GetNCUpdateRgn(
|
|
PWND pwnd,
|
|
BOOL fValidateFrame)
|
|
{
|
|
HRGN hrgnUpdate;
|
|
|
|
if (pwnd->hrgnUpdate > HRGN_FULL) {
|
|
|
|
/*
|
|
* We must make a copy of our update region, because
|
|
* it could change if we send a message, and we want to
|
|
* make sure the whole thing is used for drawing our
|
|
* frame and background. We can't use a global
|
|
* temporary region, because more than one app may
|
|
* be calling this routine.
|
|
*/
|
|
hrgnUpdate = CreateEmptyRgnPublic();
|
|
|
|
if (hrgnUpdate == NULL) {
|
|
hrgnUpdate = HRGN_FULL;
|
|
} else if (CopyRgn(hrgnUpdate, pwnd->hrgnUpdate) == ERROR) {
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = HRGN_FULL;
|
|
}
|
|
|
|
if (fValidateFrame) {
|
|
|
|
/*
|
|
* Now that we've taken care of any frame drawing,
|
|
* intersect the update region with the window's
|
|
* client area. Otherwise, apps that do ValidateRects()
|
|
* to draw themselves (e.g., WinWord) won't ever
|
|
* subtract off the part of the update region that
|
|
* overlaps the frame but not the client.
|
|
*/
|
|
CalcWindowRgn(pwnd, ghrgnInv2, TRUE);
|
|
|
|
switch (IntersectRgn(pwnd->hrgnUpdate,
|
|
pwnd->hrgnUpdate,
|
|
ghrgnInv2)) {
|
|
case ERROR:
|
|
/*
|
|
* If an error occured, we can't leave things as
|
|
* they are: invalidate the whole window and let
|
|
* BeginPaint() take care of it.
|
|
*/
|
|
GreDeleteObject(pwnd->hrgnUpdate);
|
|
pwnd->hrgnUpdate = HRGN_FULL;
|
|
break;
|
|
|
|
case NULLREGION:
|
|
/*
|
|
* There is nothing in the client area to repaint.
|
|
* Blow the region away, and decrement the paint count
|
|
* if possible.
|
|
*/
|
|
GreDeleteObject(pwnd->hrgnUpdate);
|
|
pwnd->hrgnUpdate = NULL;
|
|
ClrWF(pwnd, WFUPDATEDIRTY);
|
|
if (!TestWF(pwnd, WFINTERNALPAINT))
|
|
DecPaintCount(pwnd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
hrgnUpdate = pwnd->hrgnUpdate;
|
|
}
|
|
|
|
return hrgnUpdate;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSendNCPaint
|
|
*
|
|
* Sends a WM_NCPAINT message to a window.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxSendNCPaint(
|
|
PWND pwnd,
|
|
HRGN hrgnUpdate)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Clear the WFSENDNCPAINT bit...
|
|
*/
|
|
ClrWF(pwnd, WFSENDNCPAINT);
|
|
|
|
/*
|
|
* If the window is active, but its FRAMEON bit hasn't
|
|
* been set yet, set it and make sure that the entire frame
|
|
* gets redrawn when we send the NCPAINT.
|
|
*/
|
|
if ((pwnd == PtiCurrent()->pq->spwndActive) && !TestWF(pwnd, WFFRAMEON)) {
|
|
SetWF(pwnd, WFFRAMEON);
|
|
hrgnUpdate = HRGN_FULL;
|
|
ClrWF(pwnd, WFNONCPAINT);
|
|
}
|
|
|
|
/*
|
|
* If PixieHack() has set the WM_NCPAINT bit, we must be sure
|
|
* to send with hrgnClip == HRGN_FULL. (see PixieHack() in wmupdate.c)
|
|
*/
|
|
if (TestWF(pwnd, WFPIXIEHACK)) {
|
|
ClrWF(pwnd, WFPIXIEHACK);
|
|
hrgnUpdate = HRGN_FULL;
|
|
}
|
|
|
|
if (hrgnUpdate)
|
|
xxxSendMessage(pwnd, WM_NCPAINT, (WPARAM)hrgnUpdate, 0L);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSendChildNCPaint
|
|
*
|
|
* Sends WM_NCPAINT message to the immediate children of a window.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxSendChildNCPaint(
|
|
PWND pwnd)
|
|
{
|
|
TL tlpwnd;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
ThreadLockNever(&tlpwnd);
|
|
pwnd = pwnd->spwndChild;
|
|
while (pwnd != NULL) {
|
|
if ((pwnd->hrgnUpdate == NULL) && TestWF(pwnd, WFSENDNCPAINT)) {
|
|
ThreadLockExchangeAlways(pwnd, &tlpwnd);
|
|
xxxSendNCPaint(pwnd, HRGN_FULL);
|
|
}
|
|
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxBeginPaint
|
|
*
|
|
* Revalidation Note:
|
|
* We MUST return NULL if the window is deleted during xxxBeginPaint because
|
|
* its DCs are released upon deletion, and we shouldn't return a *released* DC!
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
HDC xxxBeginPaint(
|
|
PWND pwnd,
|
|
LPPAINTSTRUCT lpps)
|
|
{
|
|
HRGN hrgnUpdate;
|
|
HDC hdc;
|
|
BOOL fSendEraseBkgnd;
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
if (TEST_PUDF(PUDF_DRAGGINGFULLWINDOW))
|
|
SetWF(pwnd, WFSTARTPAINT);
|
|
|
|
/*
|
|
* We're processing a WM_PAINT message: clear this flag.
|
|
*/
|
|
ClrWF(pwnd, WFPAINTNOTPROCESSED);
|
|
|
|
/*
|
|
* If this bit gets set while we are drawing the frame we will need
|
|
* to redraw it.
|
|
*
|
|
* If necessary, send our WM_NCPAINT message now.
|
|
*
|
|
* please heed these notes
|
|
*
|
|
* We have to send this message BEFORE we diddle hwnd->hrgnUpdate,
|
|
* because an app may call ValidateRect or InvalidateRect in its
|
|
* handler, and it expects what it does to affect what gets drawn
|
|
* in the later WM_PAINT.
|
|
*
|
|
* It is possible to get an invalidate when we leave the critical
|
|
* section below, therefore we loop until UPDATEDIRTY is clear
|
|
* meaning there were no additional invalidates.
|
|
*/
|
|
if (TestWF(pwnd, WFSENDNCPAINT)) {
|
|
|
|
do {
|
|
ClrWF(pwnd, WFUPDATEDIRTY);
|
|
hrgnUpdate = GetNCUpdateRgn(pwnd, FALSE);
|
|
xxxSendNCPaint(pwnd, hrgnUpdate);
|
|
DeleteMaybeSpecialRgn(hrgnUpdate);
|
|
} while (TestWF(pwnd, WFUPDATEDIRTY));
|
|
|
|
} else {
|
|
ClrWF(pwnd, WFUPDATEDIRTY);
|
|
}
|
|
|
|
/*
|
|
* Hide the caret if needed. Do this before we get the DC so
|
|
* that if HideCaret() gets and releases a DC we will be able
|
|
* to reuse it later here.
|
|
* No need to DeferWinEventNotify() since pwnd is locked.
|
|
*/
|
|
if (pwnd == PtiCurrent()->pq->caret.spwnd)
|
|
zzzInternalHideCaret();
|
|
|
|
/*
|
|
* Send the check for sending an WM_ERASEBKGND to the
|
|
* window.
|
|
*/
|
|
if (fSendEraseBkgnd = TestWF(pwnd, WFSENDERASEBKGND)) {
|
|
ClrWF(pwnd, WFERASEBKGND);
|
|
ClrWF(pwnd, WFSENDERASEBKGND);
|
|
}
|
|
|
|
/*
|
|
* Validate the entire window.
|
|
*/
|
|
if (NEEDSPAINT(pwnd))
|
|
DecPaintCount(pwnd);
|
|
|
|
ClrWF(pwnd, WFINTERNALPAINT);
|
|
|
|
hrgnUpdate = pwnd->hrgnUpdate;
|
|
pwnd->hrgnUpdate = NULL;
|
|
|
|
if (TestWF(pwnd, WFDONTVALIDATE)) {
|
|
|
|
if (ghrgnUpdateSave == NULL) {
|
|
ghrgnUpdateSave = CreateEmptyRgn();
|
|
}
|
|
|
|
if (ghrgnUpdateSave != NULL) {
|
|
UnionRgn(ghrgnUpdateSave, ghrgnUpdateSave, hrgnUpdate);
|
|
gnUpdateSave++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear these flags for backward compatibility
|
|
*/
|
|
lpps->fIncUpdate =
|
|
lpps->fRestore = FALSE;
|
|
|
|
lpps->hdc =
|
|
hdc = _GetDCEx(pwnd,
|
|
hrgnUpdate,
|
|
DCX_USESTYLE | DCX_INTERSECTRGN);
|
|
|
|
if (UT_GetParentDCClipBox(pwnd, hdc, &lpps->rcPaint)) {
|
|
|
|
/*
|
|
* If necessary, erase our background, and possibly deal with
|
|
* our children's frames and backgrounds.
|
|
*/
|
|
if (fSendEraseBkgnd)
|
|
xxxSendEraseBkgnd(pwnd, hdc, hrgnUpdate);
|
|
}
|
|
|
|
/*
|
|
* Now that we're completely erased, see if there are any children
|
|
* that couldn't draw their own frames because their update regions
|
|
* got deleted.
|
|
*/
|
|
xxxSendChildNCPaint(pwnd);
|
|
|
|
/*
|
|
* The erase and frame operation has occured. Clear the WFREDRAWIFHUNG
|
|
* bit here. We don't want to clear it until we know the erase and
|
|
* frame has occured, so we know we always have a consistent looking
|
|
* window.
|
|
*/
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
|
|
lpps->fErase = (TestWF(pwnd, WFERASEBKGND) != 0);
|
|
|
|
return hdc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxEndPaint (API)
|
|
*
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxEndPaint(
|
|
PWND pwnd,
|
|
LPPAINTSTRUCT lpps)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
ReleaseCacheDC(lpps->hdc, TRUE);
|
|
|
|
if (TestWF(pwnd, WFDONTVALIDATE)) {
|
|
|
|
if (ghrgnUpdateSave != NULL) {
|
|
|
|
InternalInvalidate3(pwnd,
|
|
ghrgnUpdateSave,
|
|
RDW_INVALIDATE | RDW_ERASE);
|
|
|
|
if (--gnUpdateSave == 0) {
|
|
GreDeleteObject(ghrgnUpdateSave);
|
|
ghrgnUpdateSave = NULL;
|
|
}
|
|
}
|
|
|
|
ClrWF(pwnd, WFDONTVALIDATE);
|
|
}
|
|
|
|
ClrWF(pwnd, WFWMPAINTSENT);
|
|
|
|
/*
|
|
* This used to check that the update-region was empty before
|
|
* doing the clear. However, this caused a problem with WOW
|
|
* amipro/approach hanging. They were invalidating rects in
|
|
* their WM_PAINT handler, and allowing the defwindowproc to
|
|
* perform the validation for them. Since we were blocking
|
|
* the BeginPaint in this case, it sent them into a infinite
|
|
* loop (see bug 19036).
|
|
*/
|
|
ClrWF(pwnd, WFSTARTPAINT);
|
|
|
|
/*
|
|
* Reshow the caret if needed, but AFTER we've released the DC.
|
|
* This way ShowCaret() can reuse the DC we just released.
|
|
*/
|
|
if (pwnd == PtiCurrent()->pq->caret.spwnd)
|
|
zzzInternalShowCaret();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetLastChild
|
|
*
|
|
\***************************************************************************/
|
|
|
|
PWND GetLastChild(PWND pwnd)
|
|
{
|
|
PWND pwndLast;
|
|
|
|
pwnd = pwnd->spwndChild;
|
|
pwndLast = pwnd;
|
|
|
|
while (pwnd != NULL) {
|
|
pwndLast = pwnd;
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
|
|
return pwndLast;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCompositedTraverse
|
|
*
|
|
* Uses pre-order traversal starting with the last child to render the
|
|
* windows in a bottom-up order.
|
|
*
|
|
* 9/30/1999 vadimg created
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxCompositedTraverse(PWND pwnd)
|
|
{
|
|
TL tlpwnd;
|
|
BOOL fPainted = FALSE;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
if (NEEDSPAINT(pwnd)) {
|
|
xxxSendMessage(pwnd, WM_PAINT, 0, 0);
|
|
fPainted = TRUE;
|
|
}
|
|
|
|
pwnd = GetLastChild(pwnd);
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
while (pwnd != NULL) {
|
|
|
|
if (xxxCompositedTraverse(pwnd)) {
|
|
fPainted = TRUE;
|
|
}
|
|
pwnd = pwnd->spwndPrev;
|
|
|
|
if (ThreadLockExchange(pwnd, &tlpwnd) == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
return fPainted;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCompositedPaint
|
|
*
|
|
* 9/30/1999 vadimg created
|
|
\***************************************************************************/
|
|
|
|
VOID xxxCompositedPaint(PWND pwnd)
|
|
{
|
|
BOOL fPainted;
|
|
HBITMAP hbm, hbmOld;
|
|
PREDIRECT prdr;
|
|
HDC hdc;
|
|
LPRECT prc;
|
|
SIZE size;
|
|
POINT pt;
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(TestWF(pwnd, WEFCOMPOSITED));
|
|
|
|
SetWF(pwnd, WEFPCOMPOSITING);
|
|
|
|
/*
|
|
* Render the child windows in a bottom-up order.
|
|
*/
|
|
fPainted = xxxCompositedTraverse(pwnd);
|
|
|
|
ClrWF(pwnd, WEFPCOMPOSITING);
|
|
|
|
/*
|
|
* While we were compositing, an invalid region may have accumulated.
|
|
* So, let's go and invalidate that area of the window.
|
|
*/
|
|
BEGINATOMICCHECK();
|
|
prdr = _GetProp(pwnd, PROP_LAYER, TRUE);
|
|
if (prdr != NULL && prdr->hrgnComp != NULL) {
|
|
|
|
xxxInternalInvalidate(pwnd, prdr->hrgnComp, RDW_INVALIDATE |
|
|
RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
|
|
|
|
if (prdr->hrgnComp != HRGN_FULL) {
|
|
GreDeleteObject(prdr->hrgnComp);
|
|
}
|
|
|
|
prdr->hrgnComp = NULL;
|
|
}
|
|
ENDATOMICCHECK();
|
|
|
|
#ifdef REDIRECTION
|
|
if (TestWF(pwnd, WEFEXTREDIRECTED)) {
|
|
return;
|
|
}
|
|
#endif // REDIRECTION
|
|
|
|
BEGINATOMICCHECK();
|
|
|
|
if (fPainted && TestWF(pwnd, WEFPREDIRECTED)) {
|
|
|
|
prdr = (PREDIRECT)_GetProp(pwnd, PROP_LAYER, TRUE);
|
|
prc = &prdr->rcUpdate;
|
|
hbm = prdr->hbm;
|
|
UserAssert(hbm != NULL);
|
|
|
|
if (TestWF(pwnd, WEFLAYERED)) {
|
|
|
|
hbmOld = GreSelectBitmap(ghdcMem, hbm);
|
|
|
|
size.cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
|
|
size.cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
|
|
|
|
pt.x = pt.y = 0;
|
|
GreUpdateSprite(gpDispInfo->hDev, PtoHq(pwnd), NULL, NULL, NULL,
|
|
&size, ghdcMem, &pt, 0, NULL, ULW_DEFAULT_ATTRIBUTES, prc);
|
|
|
|
GreSelectBitmap(ghdcMem, hbmOld);
|
|
} else {
|
|
|
|
/*
|
|
* Temporarily clear the redirected bit so that we can get the dc
|
|
* with proper screen clipping.
|
|
*/
|
|
ClrWF(pwnd, WEFPREDIRECTED);
|
|
|
|
hbmOld = GreSelectBitmap(ghdcMem, hbm);
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_CACHE);
|
|
|
|
/*
|
|
* Transfer the bits for the window from the redirection bitmap
|
|
* to the screen.
|
|
*/
|
|
GreBitBlt(hdc, prc->left, prc->top, prc->right - prc->left,
|
|
prc->bottom - prc->top, ghdcMem,
|
|
prc->left, prc->top, SRCCOPY, 0);
|
|
|
|
_ReleaseDC(hdc);
|
|
GreSelectBitmap(ghdcMem, hbmOld);
|
|
|
|
/*
|
|
* Restore the redirection bit on the window.
|
|
*/
|
|
SetWF(pwnd, WEFPREDIRECTED);
|
|
}
|
|
|
|
SetRectEmpty(prc);
|
|
}
|
|
|
|
ENDATOMICCHECK();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalDoPaint
|
|
*
|
|
* Return a window equal to or below pwnd, created by the current thread,
|
|
* which needs painting.
|
|
*
|
|
* pwnd - Window to start searching from. Search is depth first.
|
|
* ptiCurrent - The current thread.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
PWND xxxInternalDoPaint(
|
|
PWND pwnd,
|
|
PTHREADINFO ptiCurrent)
|
|
{
|
|
PWND pwndT;
|
|
TL tlpwnd;
|
|
|
|
/*
|
|
* Enumerate all windows, top-down, looking for one that
|
|
* needs repainting. Skip windows of other tasks.
|
|
*/
|
|
while (pwnd != NULL) {
|
|
|
|
if (GETPTI(pwnd) == ptiCurrent) {
|
|
|
|
if (TestWF(pwnd, WEFCOMPOSITED)) {
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
xxxCompositedPaint(pwnd);
|
|
pwnd = pwnd->spwndNext;
|
|
|
|
if (ThreadUnlock(&tlpwnd) == NULL) {
|
|
return NULL;
|
|
}
|
|
continue;
|
|
|
|
} else if (NEEDSPAINT(pwnd)) {
|
|
|
|
/*
|
|
* If this window is transparent, we don't want to
|
|
* send it a WM_PAINT until all its siblings below it
|
|
* have been repainted. If we find an unpainted sibling
|
|
* below, return it instead.
|
|
*/
|
|
if (TestWF(pwnd, WEFTRANSPARENT)) {
|
|
|
|
pwndT = pwnd;
|
|
while ((pwndT = pwndT->spwndNext) != NULL) {
|
|
|
|
/*
|
|
* Make sure sibling window belongs to same app
|
|
*/
|
|
if ((GETPTI(pwndT) == ptiCurrent) && NEEDSPAINT(pwndT)) {
|
|
|
|
if (TestWF(pwndT, WEFTRANSPARENT))
|
|
continue;
|
|
|
|
return pwndT;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pwnd;
|
|
}
|
|
}
|
|
|
|
if (pwnd->spwndChild &&
|
|
(pwndT = xxxInternalDoPaint(pwnd->spwndChild, ptiCurrent))) {
|
|
|
|
return pwndT;
|
|
}
|
|
|
|
pwnd = pwnd->spwndNext;
|
|
}
|
|
|
|
return pwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DoPaint
|
|
*
|
|
* Looks at all the desktops for the window needing a paint and places a
|
|
* WM_PAINT in its queue.
|
|
*
|
|
* History:
|
|
* 16-Jul-91 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxDoPaint(
|
|
PWND pwndFilter,
|
|
LPMSG lpMsg)
|
|
{
|
|
PWND pwnd;
|
|
PWND pwndT;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
CheckLock(pwndFilter);
|
|
|
|
#if 0 // CHRISWIL: WIN95 SPECIFIC
|
|
|
|
/*
|
|
* If there is a system modal up and it is attached to another task,
|
|
* DON'T do paints. We don't want to return a message for a window in
|
|
* another task!
|
|
*/
|
|
if (hwndSysModal && (hwndSysModal->hq != hqCurrent)) {
|
|
|
|
/*
|
|
* Poke this guy so he wakes up at some point in the future,
|
|
* otherwise he may never wake up to realize he should paint.
|
|
* Causes hangs - e.g. Photoshop installation program
|
|
* PostThreadMessage32(Lpq(hqCurrent)->idThread, WM_NULL, 0, 0, 0);
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* If this is a system thread, then walk the windowstation desktop-list
|
|
* to find the window which needs painting. For other threads, we
|
|
* reference off the thread-desktop.
|
|
*/
|
|
if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
|
|
|
|
PWINDOWSTATION pwinsta;
|
|
PDESKTOP pdesk;
|
|
|
|
if ((pwinsta = ptiCurrent->pwinsta) == NULL) {
|
|
RIPMSG0(RIP_ERROR, "DoPaint: SYSTEMTHREAD does not have (pwinsta)");
|
|
return FALSE;
|
|
}
|
|
|
|
pwnd = pwinsta->pTerm->spwndDesktopOwner;
|
|
if (!NEEDSPAINT(pwnd)) {
|
|
|
|
pwnd = NULL;
|
|
for(pdesk = pwinsta->rpdeskList; pdesk; pdesk = pdesk->rpdeskNext) {
|
|
|
|
if (pwnd = xxxInternalDoPaint(pdesk->pDeskInfo->spwnd, ptiCurrent))
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
pwnd = xxxInternalDoPaint(ptiCurrent->rpdesk->pDeskInfo->spwnd,
|
|
ptiCurrent);
|
|
}
|
|
|
|
if (pwnd != NULL) {
|
|
|
|
if (!CheckPwndFilter(pwnd, pwndFilter))
|
|
return FALSE;
|
|
|
|
/*
|
|
* We're returning a WM_PAINT message, so clear WFINTERNALPAINT so
|
|
* it won't get sent again later.
|
|
*/
|
|
if (TestWF(pwnd, WFINTERNALPAINT)) {
|
|
|
|
ClrWF(pwnd, WFINTERNALPAINT);
|
|
|
|
/*
|
|
* If there is no update region, then no more paint for this
|
|
* window.
|
|
*/
|
|
if (pwnd->hrgnUpdate == NULL)
|
|
DecPaintCount(pwnd);
|
|
}
|
|
|
|
/*
|
|
* Set the STARTPAINT so that any other calls to BeginPaint while
|
|
* painting is begin performed, will prevent painting on those
|
|
* windows.
|
|
*
|
|
* Clear the UPDATEDIRTY since some apps (DBFast) don't call
|
|
* GetUpdateRect, BeginPaint/EndPaint.
|
|
*/
|
|
ClrWF(pwnd, WFSTARTPAINT);
|
|
ClrWF(pwnd, WFUPDATEDIRTY);
|
|
|
|
/*
|
|
* If we get an invalidate between now and the time the app calls
|
|
* BeginPaint() and the windows parent is not CLIPCHILDREN, then
|
|
* the parent will paint in the wrong order. So we are going to
|
|
* cause the child to paint again. Look in beginpaint and internal
|
|
* invalidate for other parts of this fix.
|
|
*
|
|
* Set a flag to signify that we are in the bad zone.
|
|
*
|
|
* Must go up the parent links to make sure all parents have
|
|
* WFCLIPCHILDREN set otherwise set the WFWMPAINTSENT flag.
|
|
* This is to fix Excel spreadsheet and fulldrag. The speadsheet
|
|
* parent window (class XLDESK) has WFCLIPCHILDREN set but it's
|
|
* parent (class XLMAIN) doesn't. So the main window erases the
|
|
* background after the child window paints.
|
|
*
|
|
* JOHANNEC : 27-Jul-1994
|
|
*/
|
|
|
|
/*
|
|
* NT Bug 400167: As we walk up the tree, we need to stop short of
|
|
* desktop windows and mother desktop windows. We can't do a test
|
|
* for WFCLIPCHILDREN on the mother desktop window's parent because
|
|
* it doesn't exist. This means that no desktop window will get
|
|
* WFWMPAINTSENT set, but the message window will be able to get
|
|
* WFWMPAINTSENT set.
|
|
*/
|
|
|
|
pwndT = pwnd;
|
|
while (pwndT && (GETFNID(pwndT) != FNID_DESKTOP)) {
|
|
|
|
if (!TestWF(pwndT->spwndParent, WFCLIPCHILDREN)) {
|
|
SetWF(pwnd, WFWMPAINTSENT);
|
|
break;
|
|
}
|
|
|
|
pwndT = pwndT->spwndParent;
|
|
}
|
|
|
|
/*
|
|
* If the top level "tiled" owner/parent of this window is iconed,
|
|
* send a WM_PAINTICON rather than a WM_PAINT. The wParam
|
|
* is TRUE if this is the tiled window and FALSE if it is a
|
|
* child/owned popup of the minimized window.
|
|
*
|
|
* BACKWARD COMPATIBILITY HACK
|
|
*
|
|
* 3.0 sent WM_PAINTICON with wParam == TRUE for no apparent
|
|
* reason. Lotus Notes 2.1 depends on this for some reason
|
|
* to properly change its icon when new mail arrives.
|
|
*/
|
|
if (!TestWF(pwnd, WFWIN40COMPAT) &&
|
|
TestWF(pwnd, WFMINIMIZED) &&
|
|
(pwnd->pcls->spicn != NULL)) {
|
|
|
|
StoreMessage(lpMsg, pwnd, WM_PAINTICON, (DWORD)TRUE, 0L, 0L);
|
|
|
|
} else {
|
|
|
|
StoreMessage(lpMsg, pwnd, WM_PAINT, 0, 0L, 0L);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSimpleDoSyncPaint
|
|
*
|
|
* Process the sync-paint for this window. This can send either a NCPAINT
|
|
* or an ERASEBKGND. This assumes no recursion and flags == 0.
|
|
*
|
|
* History:
|
|
* 26-Oct-1993 MikeKe Created
|
|
\***************************************************************************/
|
|
|
|
VOID xxxSimpleDoSyncPaint(
|
|
PWND pwnd)
|
|
{
|
|
HRGN hrgnUpdate;
|
|
DWORD flags = 0;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* No syncpaints for composited windows, it messes up their painting
|
|
* since erasebkgnds and ncpaints will be sent in the wrong order.
|
|
*/
|
|
if (GetStyleWindow(pwnd, WEFCOMPOSITED) != NULL)
|
|
return;
|
|
|
|
/*
|
|
* Since we're taking care of the frame drawing, we can consider
|
|
* this WM_PAINT message processed.
|
|
*/
|
|
ClrWF(pwnd, WFPAINTNOTPROCESSED);
|
|
|
|
/*
|
|
* Make copies of these flags, because their state might
|
|
* change after we send a message, and we don't want
|
|
* to "lose" them.
|
|
*/
|
|
if (TestWF(pwnd, WFSENDNCPAINT))
|
|
flags |= DSP_FRAME;
|
|
|
|
if (TestWF(pwnd, WFSENDERASEBKGND))
|
|
flags |= DSP_ERASE;
|
|
|
|
if (flags & (DSP_ERASE | DSP_FRAME)) {
|
|
|
|
if (!TestWF(pwnd, WFVISIBLE)) {
|
|
|
|
/*
|
|
* If there is no update region, just clear the bits.
|
|
*/
|
|
ClrWF(pwnd, WFSENDNCPAINT);
|
|
ClrWF(pwnd, WFSENDERASEBKGND);
|
|
ClrWF(pwnd, WFPIXIEHACK);
|
|
ClrWF(pwnd, WFERASEBKGND);
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
|
|
} else {
|
|
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* If there is no update region, we don't have to
|
|
* do any erasing, but we may need to send an NCPAINT.
|
|
*/
|
|
if (pwnd->hrgnUpdate == NULL) {
|
|
ClrWF(pwnd, WFSENDERASEBKGND);
|
|
ClrWF(pwnd, WFERASEBKGND);
|
|
flags &= ~DSP_ERASE;
|
|
}
|
|
|
|
/*
|
|
* Only mess with windows owned by the current thread.
|
|
* NOTE: This means that WM_NCPAINT and WM_ERASEBKGND are
|
|
* only sent intra-thread.
|
|
*/
|
|
if (GETPTI(pwnd) == ptiCurrent) {
|
|
|
|
hrgnUpdate = GetNCUpdateRgn(pwnd, TRUE);
|
|
|
|
if (flags & DSP_FRAME) {
|
|
|
|
/*
|
|
* If the message got sent before we got here then do
|
|
* nothing.
|
|
*/
|
|
if (TestWF(pwnd, WFSENDNCPAINT))
|
|
xxxSendNCPaint(pwnd, hrgnUpdate);
|
|
}
|
|
|
|
if (flags & DSP_ERASE) {
|
|
|
|
if (TestWF(pwnd, WFSENDNCPAINT)) {
|
|
/*
|
|
* If we got another invalidate during the NCPAINT
|
|
* callback get the new update region
|
|
*/
|
|
DeleteMaybeSpecialRgn(hrgnUpdate);
|
|
hrgnUpdate = GetNCUpdateRgn(pwnd, FALSE);
|
|
}
|
|
|
|
/*
|
|
* If the message got sent before we got here
|
|
* (e.g.: an UpdateWindow() inside WM_NCPAINT handler,
|
|
* for example), don't do anything.
|
|
*
|
|
* WINPROJ.EXE (version 1.0) calls UpdateWindow() in
|
|
* the WM_NCPAINT handlers for its subclassed listboxes
|
|
* in the open dialog.
|
|
*/
|
|
if (TestWF(pwnd, WFSENDERASEBKGND)) {
|
|
ClrWF(pwnd, WFSENDERASEBKGND);
|
|
ClrWF(pwnd, WFERASEBKGND);
|
|
xxxSendEraseBkgnd(pwnd, NULL, hrgnUpdate);
|
|
}
|
|
|
|
/*
|
|
* The erase and frame operation has occured. Clear the
|
|
* WFREDRAWIFHUNG bit here. We don't want to clear it until we
|
|
* know the erase and frame has occured, so we know we always
|
|
* have a consistent looking window.
|
|
*/
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
}
|
|
|
|
DeleteMaybeSpecialRgn(hrgnUpdate);
|
|
|
|
} else if (!TestwndChild(pwnd) &&
|
|
(pwnd != grpdeskRitInput->pDeskInfo->spwnd) &&
|
|
FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT) &&
|
|
TestWF(pwnd, WFREDRAWIFHUNG)) {
|
|
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
xxxRedrawHungWindow(pwnd, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxInternalDoSyncPaint
|
|
*
|
|
* Mostly the same functionality as the old xxxDoSyncPaint.
|
|
*
|
|
*
|
|
* This function is called to erase the background of a window, and
|
|
* possibly frame and erase the children too.
|
|
*
|
|
* WM_SYNCPAINT(wParam)/DoSyncPaint(flags) values:
|
|
*
|
|
* DSP_ERASE - Erase background
|
|
* DSP_FRAME - Draw child frames
|
|
* DSP_ENUMCLIPPEDCHILDREN - Recurse if children are clipped
|
|
* DSP_NOCHECKPARENTS - Don't check
|
|
*
|
|
*
|
|
* Normally, only the DSP_ENUMCLIPPEDCHILDREN bit of flags is
|
|
* significant on entry. If DSP_WM_SYNCPAINT is set, then hrgnUpdate
|
|
* and the rest of the flags bits are valid.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxInternalDoSyncPaint(
|
|
PWND pwnd,
|
|
DWORD flags)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* Do the paint for this window.
|
|
*/
|
|
xxxSimpleDoSyncPaint(pwnd);
|
|
|
|
/*
|
|
* Normally we like to enumerate all of this window's children and have
|
|
* them erase their backgrounds synchronously. However, this is a bad
|
|
* thing to do if the window is NOT CLIPCHLIDREN. Here's the scenario
|
|
* we want to to avoid:
|
|
*
|
|
* 1) Window 'A' is invalidated
|
|
* 2) 'A' erases itself (or not, doesn't matter)
|
|
* 3) 'A's children are enumerated and they erase themselves.
|
|
* 4) 'A' paints over its children (remember, 'A' isn't CLIPCHILDREN)
|
|
* 5) 'A's children paint but their backgrounds aren't their ERASEBKND
|
|
* color (because 'A' painted over them) and everything looks like
|
|
* dirt.
|
|
*/
|
|
if ((flags & DSP_ALLCHILDREN) ||
|
|
((flags & DSP_ENUMCLIPPEDCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))) {
|
|
|
|
TL tlpwnd;
|
|
PBWL pbwl;
|
|
HWND *phwnd;
|
|
|
|
if (pbwl = BuildHwndList(pwnd->spwndChild, BWL_ENUMLIST, NULL)) {
|
|
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
HWND hwnd;
|
|
|
|
/*
|
|
* If the client dies during a callback, the hwnd list
|
|
* will be freed in xxxDestroyThreadInfo.
|
|
*/
|
|
for (phwnd = pbwl->rghwnd; (hwnd = *phwnd) != (HWND)1; phwnd++) {
|
|
|
|
if (hwnd == NULL)
|
|
continue;
|
|
|
|
if ((pwnd = (PWND)RevalidateHwnd(hwnd)) == NULL)
|
|
continue;
|
|
|
|
/*
|
|
* Note: testing if a window is a child automatically
|
|
* excludes the desktop window.
|
|
*/
|
|
if (TestWF(pwnd, WFCHILD) && (ptiCurrent != GETPTI(pwnd))) {
|
|
|
|
/*
|
|
* Don't cause any more intertask sendmessages cause it
|
|
* does bad things to cbt's windowproc hooks. (Due to
|
|
* SetParent allowing child windows in the topwindow
|
|
* hierarchy.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Note that we pass only certain bits down as we recurse:
|
|
* the other bits pertain to the current window only.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
|
|
xxxInternalDoSyncPaint(pwnd, flags);
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
FreeHwndList(pbwl);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DoQueuedSyncPaint
|
|
*
|
|
* Queues WM_SYNCPAINT messages for top level windows not of the specified
|
|
* thread.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
VOID DoQueuedSyncPaint(
|
|
PWND pwnd,
|
|
DWORD flags,
|
|
PTHREADINFO pti)
|
|
{
|
|
PTHREADINFO ptiPwnd = GETPTI(pwnd);
|
|
|
|
if ((ptiPwnd != pti) &&
|
|
TestWF(pwnd, WFSENDNCPAINT) &&
|
|
TestWF(pwnd, WFSENDERASEBKGND) &&
|
|
TestWF(pwnd, WFVISIBLE)) {
|
|
|
|
PSMS psms = ptiPwnd->psmsReceiveList;
|
|
|
|
/*
|
|
* If this window already has a WM_SYNCPAINT queue'd up, then there's
|
|
* no need to send another one. Also protects our heap from getting
|
|
* chewed up.
|
|
*/
|
|
while (psms != NULL) {
|
|
|
|
if ((psms->message == WM_SYNCPAINT) && (psms->spwnd == pwnd)) {
|
|
break;
|
|
}
|
|
|
|
psms = psms->psmsReceiveNext;
|
|
}
|
|
|
|
if (psms == NULL) {
|
|
/*
|
|
* This will give this message the semantics of a notify
|
|
* message (sendmessage no wait), without calling back
|
|
* the WH_CALLWNDPROC hook. We don't want to do that
|
|
* because that'll let all these processes with invalid
|
|
* windows to process paint messages before they process
|
|
* "synchronous" erasing or framing needs.
|
|
*
|
|
* Hi word of wParam must be zero or wow will drop it
|
|
*
|
|
* LATER mikeke
|
|
* Do we need to send down the flags with DWP_ERASE and DSP_FRAME
|
|
* in it?
|
|
*/
|
|
UserAssert(HIWORD(flags) == 0);
|
|
QueueNotifyMessage(pwnd, WM_SYNCPAINT, flags, 0);
|
|
|
|
/*
|
|
* Set our syncpaint-pending flag, since we queued one up. This
|
|
* will be used to check when we validate-parents for windows
|
|
* without clipchildren.
|
|
*/
|
|
SetWF(pwnd, WFSYNCPAINTPENDING);
|
|
}
|
|
|
|
/*
|
|
* If we posted a WM_SYNCPAINT for a top-level window that is not
|
|
* of the current thread we're done; we'll pick up the children
|
|
* when we process the message for real. If we're the desktop
|
|
* however make sure we get all it children.
|
|
*/
|
|
if (pwnd != PWNDDESKTOP(pwnd))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Normally we like to enumerate all of this window's children and have
|
|
* them erase their backgrounds synchronously. However, this is a bad
|
|
* thing to do if the window is NOT CLIPCHLIDREN. Here's the scenario
|
|
* we want to to avoid:
|
|
*
|
|
* 1. Window 'A' is invalidated
|
|
* 2. 'A' erases itself (or not, doesn't matter)
|
|
* 3. 'A's children are enumerated and they erase themselves.
|
|
* 4. 'A' paints over its children (remember, 'A' isn't CLIPCHILDREN)
|
|
* 5. 'A's children paint but their backgrounds aren't their ERASEBKND
|
|
* color (because 'A' painted over them) and everything looks like
|
|
* dirt.
|
|
*/
|
|
if ((flags & DSP_ALLCHILDREN) ||
|
|
((flags & DSP_ENUMCLIPPEDCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))) {
|
|
|
|
PWND pwndT;
|
|
|
|
for (pwndT = pwnd->spwndChild; pwndT; pwndT = pwndT->spwndNext) {
|
|
|
|
/*
|
|
* Don't cause any more intertask sendmessages cause it does
|
|
* bad things to cbt's windowproc hooks. (Due to SetParent
|
|
* allowing child windows in the topwindow hierarchy.
|
|
* The child bit also catches the desktop window; we want to
|
|
*/
|
|
if (TestWF(pwndT, WFCHILD) && (pti != GETPTI(pwndT)))
|
|
continue;
|
|
|
|
/*
|
|
* Note that we pass only certain bits down as we recurse:
|
|
* the other bits pertain to the current window only.
|
|
*/
|
|
DoQueuedSyncPaint(pwndT, flags, pti);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxDoSyncPaint
|
|
*
|
|
* This funstion is only called for the initial syncpaint so we always
|
|
* queue syncpaints to other threads in this funtion.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
VOID xxxDoSyncPaint(
|
|
PWND pwnd,
|
|
DWORD flags)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* If any of our non-clipchildren parents have an update region, don't
|
|
* do anything. This way we won't redraw our background or frame out
|
|
* of order, only to have it get obliterated when our parent erases his
|
|
* background.
|
|
*/
|
|
if (ParentNeedsPaint(pwnd))
|
|
return;
|
|
|
|
/*
|
|
* First of all if we are going to be queueing any WM_SYNCPAINT messages
|
|
* to windows of another thread do it first while the window's update
|
|
* regions are still in sync. This way there is no chance the update
|
|
* region will be incorrect (through window movement during callbacks of
|
|
* the WM_ERASEBKGND|WM_NCPAINT messages).
|
|
*/
|
|
DoQueuedSyncPaint(pwnd, flags, PtiCurrent());
|
|
xxxInternalDoSyncPaint(pwnd, flags);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ParentNeedsPaint
|
|
*
|
|
* Return a non-zero PWND if a non-CLIPCHILDREN parent requires a WM_PAINT
|
|
* message.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
|
|
PWND ParentNeedsPaint(
|
|
PWND pwnd)
|
|
{
|
|
while ((pwnd = pwnd->spwndParent) != NULL) {
|
|
|
|
if (TestWF(pwnd, WFCLIPCHILDREN))
|
|
break;
|
|
|
|
if (NEEDSPAINT(pwnd))
|
|
return pwnd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSendEraseBkgnd
|
|
*
|
|
* Sends a WM_ERASEBKGROUND event to the window. This contains the paintDC
|
|
* with the update-region selected into it. If there's no update region
|
|
* then we prevent this event from making it to the window.
|
|
*
|
|
* History:
|
|
* 16-Jul-1991 DarrinM Ported from Win 3.1 sources.
|
|
* 15-Dec-1996 ChrisWil Merged Chicago Functionality (no erase on min).
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxSendEraseBkgnd(
|
|
PWND pwnd,
|
|
HDC hdcBeginPaint,
|
|
HRGN hrgnUpdate)
|
|
{
|
|
PTHREADINFO ptiCurrent;
|
|
BOOL fErased;
|
|
HDC hdc;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* For minimized dudes in win3.1, we would've sent an
|
|
* WM_ICONERASEBKGND and cleared the erase bit. Now that min
|
|
* windows in 4.0 are all nonclient, don't bother erasing at
|
|
* all. Pretend like we did.
|
|
*
|
|
* NOTE:
|
|
* For < 4.0 windows, we may have to send a fake WM_ICONERASEKBGND
|
|
* to keep 'em happy. Saves time not to though. Getting a DC and
|
|
* sending the message ain't speedy.
|
|
*/
|
|
if ((hrgnUpdate == NULL) || TestWF(pwnd, WFMINIMIZED))
|
|
return FALSE;
|
|
|
|
/*
|
|
* If a DC to use was not passed in, get one.
|
|
* We want one clipped to this window's update region.
|
|
*/
|
|
if (hdcBeginPaint == NULL) {
|
|
|
|
hdc = _GetDCEx(pwnd,
|
|
hrgnUpdate,
|
|
DCX_USESTYLE | DCX_INTERSECTRGN | DCX_NODELETERGN);
|
|
} else {
|
|
|
|
hdc = hdcBeginPaint;
|
|
}
|
|
|
|
/*
|
|
* If we're send the WM_ERASEBKGND to another process
|
|
* we need to change the DC owner.
|
|
*
|
|
* We'd like to change the owner to pwnd->pti->idProcess, but
|
|
* GDI won't let us assign ownership back to ourselves later.
|
|
*/
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
if (GETPTI(pwnd)->ppi != ptiCurrent->ppi)
|
|
GreSetDCOwner(hdc, OBJECT_OWNER_PUBLIC);
|
|
|
|
/*
|
|
* Send the event to the window. This contains the DC clipped to
|
|
* the update-region.
|
|
*/
|
|
fErased = (BOOL)xxxSendMessage(pwnd, WM_ERASEBKGND, (WPARAM)hdc, 0L);
|
|
|
|
/*
|
|
* If we've changed the DC owner, change it back to
|
|
* the current process.
|
|
*/
|
|
if (GETPTI(pwnd)->ppi != ptiCurrent->ppi)
|
|
GreSetDCOwner(hdc, OBJECT_OWNER_CURRENT);
|
|
|
|
/*
|
|
* If the WM_ERASEBKGND message did not erase the
|
|
* background, then set this flag to let BeginPaint()
|
|
* know to ask the caller to do it via the fErase
|
|
* flag in the PAINTSTRUCT.
|
|
*/
|
|
if (!fErased) {
|
|
SetWF(pwnd, WFERASEBKGND);
|
|
if (!TestWF(pwnd, WFWIN31COMPAT))
|
|
SetWF(pwnd, WFSENDERASEBKGND);
|
|
}
|
|
|
|
/*
|
|
* If we got a cache DC in this routine, release it.
|
|
*/
|
|
if (hdcBeginPaint == NULL) {
|
|
ReleaseCacheDC(hdc, TRUE);
|
|
}
|
|
|
|
return fErased;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* IncPaintCount
|
|
*
|
|
* EFFECTS:
|
|
* If cPaintsReady changes from 0 to 1, the QS_PAINT bit is set for
|
|
* associated queue and we wake up task so repaint will occur.
|
|
*
|
|
* IMPLEMENTATION:
|
|
* Get the queue handle from the window handle, bump the paint count, and
|
|
* if paint count is one, Set the wakebit.
|
|
*
|
|
* History:
|
|
* 17-Jul-1991 DarrinM Translated Win 3.1 ASM code.
|
|
\***************************************************************************/
|
|
|
|
VOID IncPaintCount(
|
|
PWND pwnd)
|
|
{
|
|
PTHREADINFO pti = GETPTI(pwnd);
|
|
|
|
if (pti->cPaintsReady++ == 0)
|
|
SetWakeBit(pti, QS_PAINT);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DecPaintCount
|
|
*
|
|
* EFFECTS:
|
|
* If cPaintsReady changes from 1 to 0, the QS_PAINT bit is cleared so
|
|
* that no more paints will occur.
|
|
*
|
|
* IMPLEMENTATION:
|
|
* Get the queue handle from the window handle, decrement the paint count,
|
|
* and if paint count is zero, clear the wakebit.
|
|
*
|
|
* History:
|
|
* 17-Jul-1991 DarrinM Translated Win 3.1 ASM code.
|
|
\***************************************************************************/
|
|
|
|
VOID DecPaintCount(
|
|
PWND pwnd)
|
|
{
|
|
PTHREADINFO pti = GETPTI(pwnd);
|
|
|
|
if (--pti->cPaintsReady == 0) {
|
|
pti->pcti->fsWakeBits &= ~QS_PAINT;
|
|
pti->pcti->fsChangeBits &= ~QS_PAINT;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UT_GetParentDCClipBox
|
|
*
|
|
* Return rectangle coordinates of the parent clip-rect. If the window
|
|
* isn't using a parentDC for drawing then return normal clipbox.
|
|
*
|
|
* History:
|
|
* 31-Oct-1990 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
int UT_GetParentDCClipBox(
|
|
PWND pwnd,
|
|
HDC hdc,
|
|
LPRECT lprc)
|
|
{
|
|
RECT rc;
|
|
|
|
if (GreGetClipBox(hdc, lprc, TRUE) == NULLREGION)
|
|
return FALSE;
|
|
|
|
if ((pwnd == NULL) || !TestCF(pwnd, CFPARENTDC))
|
|
return TRUE;
|
|
|
|
GetRect(pwnd, &rc, GRECT_CLIENT | GRECT_CLIENTCOORDS);
|
|
|
|
return IntersectRect(lprc, lprc, &rc);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UserRedrawDesktop
|
|
*
|
|
* Redraw the desktop and its children. This is called from GDI for DCI
|
|
* related unlocks, so that all visrgns are recalculated for the apps.
|
|
*
|
|
* History:
|
|
* 08-Jan-1996 ChrisWil Created.
|
|
\***************************************************************************/
|
|
|
|
VOID UserRedrawDesktop(VOID)
|
|
{
|
|
TL tlpwnd;
|
|
PWND pwndDesk;
|
|
|
|
EnterCrit();
|
|
|
|
pwndDesk = PtiCurrent()->rpdesk->pDeskInfo->spwnd;
|
|
|
|
ThreadLockAlways(pwndDesk, &tlpwnd);
|
|
|
|
xxxInternalInvalidate(pwndDesk,
|
|
HRGN_FULL,
|
|
RDW_INVALIDATE |
|
|
RDW_ERASE |
|
|
RDW_FRAME |
|
|
RDW_ALLCHILDREN);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
LeaveCrit();
|
|
}
|