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.
 
 
 
 
 
 

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();
}