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.
 
 
 
 
 
 

1272 lines
39 KiB

/**************************** Module Header ********************************\
* Module Name: spb.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Save Popup Bits (SPB) support routines.
*
* History:
* 18-Jul-1991 DarrinM Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* FBitsTouch
*
* This routine checkes to see if the rectangle *lprcDirty in pwndDirty
* invalidates any bits in the SPB structure at *pspb.
*
* pwndDirty "touches" pwndSpb if:
* 1. pwndDirty is visible AND:
* 2. pwndDirty == or descendent of pwndSpb, and pwndSpb is a LOCKUPDATE
* spb.
* 3. pwndDirty is pwndSpb's parent. (e.g., drawing in the
* desktop window, behind a dialog box).
* 4. A parent of pwndDirty is the sibling of pwndSpb, and the parent
* is lower in the zorder.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL FBitsTouch(
PWND pwndDirty,
LPRECT lprcDirty,
PSPB pspb,
DWORD flags)
{
PWND pwndSpb,
pwndDirtySave;
int fSpbLockUpdate;
/*
* When no window is passed in, skip all the window-related stuff and
* go directly to check the rectangle.
*/
if (pwndDirty == NULL)
goto ProbablyTouch;
/*
* If pwndDirty or its parents are invisible,
* then it can't invalidate any SPBs
*/
if (!IsVisible(pwndDirty))
return FALSE;
pwndSpb = pspb->spwnd;
fSpbLockUpdate = pspb->flags & SPB_LOCKUPDATE;
if (fSpbLockUpdate) {
/*
* If the guy is drawing through a locked window via
* DCX_LOCKWINDOWUPDATE and the spb is a LOCKUPDATE SPB, then
* don't do any invalidation of the SPB. Basically we're trying
* to avoid having the tracking rectangle invalidate the SPB
* since it's drawn via a WinGetClipPS() ps.
*/
if (flags & DCX_LOCKWINDOWUPDATE)
return FALSE;
}
/*
* If pwndDirty is pwndSpb's immediate parent (e.g., drawing in the
* desktop window behind a dialog box), then we may touch: do the
* intersection.
*/
if (pwndDirty == pwndSpb->spwndParent)
goto ProbablyTouch;
/*
* We know that pwndDirty != pwndSpb->spwndParent.
* Now find the parent of pwndDirty that is a sibling of pwndSpb.
*/
pwndDirtySave = pwndDirty;
while (pwndSpb->spwndParent != pwndDirty->spwndParent) {
pwndDirty = pwndDirty->spwndParent;
/*
* If we get to the top of the tree, it's because:
* 1. pwndSpb == pwndDesktop
* 2. pwndDirty is a parent of pwndSpb
* 3. pwndDirty == pwndDesktop
* 4. pwndDirty is a child of some other desktop
* 5. pwndSpb and pwndDirty aren't siblings
*
* In all these cases, pwndDirty can't touch pwndSpb.
*/
if (pwndDirty == NULL)
return FALSE;
}
/*
* If pwndSpb is the same as pwndDirty, then it will invalidate
* only if the SPB is LOCKUPDATE.
*
* Non-LOCKUPDATE SPB's can't be invalidated by their
* own windows, but LOCKUPDATE SPB's can.
*/
if (pwndDirty == pwndSpb) {
if (!fSpbLockUpdate)
return FALSE;
/*
* If pwndSpb itself was drawn in, then we can't
* try subtracting children.
*/
if (pwndDirtySave == pwndSpb)
goto ProbablyTouch;
/*
* We want to calculate the immediate child of pwndSpb
* on the path from pwndDirty to pwndSpb, so we can
* subtract off the rectangles of the children of pwndSpb
* in case there are intervening windows.
*/
while (pwndSpb != pwndDirtySave->spwndParent) {
pwndDirtySave = pwndDirtySave->spwndParent;
}
/*
* The SubtractIntervening loop subtracts the
* window rects starting from pwndSpb and ending
* at the window before pwndDirty, so set up
* our variables appropriately.
*/
pwndDirty = pwndDirtySave;
pwndSpb = pwndSpb->spwndChild;
} else {
/*
* Now compare the Z order of pwndDirty and pwndSpb.
* If pwndDirty is above pwndSpb, then the SPB can't be touched.
*/
pwndDirtySave = pwndDirty;
/*
* Compare the Z order by searching starting at pwndDirty,
* moving DOWN the Z order list. If we encounter pwndSpb,
* then pwndDirty is ABOVE or EQUAL to pwndSpb.
*/
for ( ; pwndDirty != NULL; pwndDirty = pwndDirty->spwndNext) {
if (pwndDirty == pwndSpb) {
return FALSE;
}
}
pwndDirty = pwndDirtySave;
/*
* We don't want to subtract the SPB window itself
*/
pwndSpb = pwndSpb->spwndNext;
}
/*
* Subtract Intervening rectangles.
* pwndDirty is below pwndSpb. If there are any intervening
* windows, subtract their window rects from lprcDirty to see if pwndDirty
* is obscured.
*/
while (pwndSpb && pwndSpb != pwndDirty) {
/*
* If this window has a region selected, hwndDirty may draw through
* it even though it has a full rectangle! We can't subtract its
* rect from the dirty rect in this case.
*/
if ( TestWF(pwndSpb, WFVISIBLE) &&
!pwndSpb->hrgnClip &&
!TestWF(pwndSpb, WEFLAYERED) &&
!SubtractRect(lprcDirty, lprcDirty, &pwndSpb->rcWindow)) {
return FALSE;
}
pwndSpb = pwndSpb->spwndNext;
}
// fall through
ProbablyTouch:
/*
* If the rectangles don't intersect, there is no invalidation.
* (we make this test relatively late because it's expensive compared
* to the tests above).
* Otherwise, *lprcDirty now has the area of bits not obscured
* by intervening windows.
*/
return IntersectRect(lprcDirty, lprcDirty, &pspb->rc);
}
/***************************************************************************\
* SpbCheckRect2
*
* Subtracts lprc in pwnd from pspb's region if lprc touches pspb.
*
* Returns FALSE if there is a memory allocation error, or if lprc
* contains psbp's region; otherwise, returns TRUE.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL SpbCheckRect2(
PSPB pspb,
PWND pwnd,
LPRECT lprc,
DWORD flags)
{
RECT rcTouch = *lprc;
/*
* See if lprc touches any saved bits, taking into account what
* window the drawing is occuring in.
*/
if (FBitsTouch(pwnd, &rcTouch, pspb, flags)) {
/*
* If no SPB region exists, make one for the whole thing
*/
if (!pspb->hrgn && SetOrCreateRectRgnIndirectPublic(
&pspb->hrgn, &pspb->rc) == ERROR) {
goto Error;
}
/*
* Subtract the rectangle that is invalid from the SPB region
*/
SetRectRgnIndirect(ghrgnSCR, &rcTouch);
switch (SubtractRgn(pspb->hrgn, pspb->hrgn, ghrgnSCR)) {
case ERROR:
case NULLREGION:
goto Error;
default:
break;
}
}
return TRUE;
Error:
FreeSpb(pspb);
return FALSE;
}
/***************************************************************************\
* SpbTransfer
*
* Validate the SPB rectangle from a window's update region, after
* subtracting the window's update region from the SPB.
*
* NOTE: Although SpbTransfer calls xxxInternalInvalidate, it doesn't
* specify any flags that will cause immediate updating. Therefore the
* critsect isn't left and we don't consider this an 'xxx' routine.
* Also, no revalidation is necessary.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL SpbTransfer(
PSPB pspb,
PWND pwnd,
BOOL fChildren)
{
RECT rc;
/*
* If the window has an update region...
*/
if (pwnd->hrgnUpdate != NULL) {
/*
* Invalidate its update region rectangle from the SPB
*/
if (pwnd->hrgnUpdate > HRGN_FULL) {
GreGetRgnBox(pwnd->hrgnUpdate, &rc);
} else {
rc = pwnd->rcWindow;
}
/*
* Intersect the update region bounds with the parent client rects,
* to make sure we don't invalidate more than we need to. If
* nothing to validate, return TRUE (because SPB is probably not empty)
* These RDW_ flags won't cause the critical section to be left, nor
* will they provoke WinEvent notifications.
*/
if (IntersectWithParents(pwnd, &rc)) {
BEGINATOMICCHECK();
xxxInternalInvalidate(pwnd,
ghrgnSPB2,
RDW_VALIDATE | RDW_NOCHILDREN);
ENDATOMICCHECK();
/*
* If the SPB vanished, return FALSE.
*/
if (!SpbCheckRect2(pspb, pwnd, &rc, DCX_WINDOW))
return FALSE;
}
}
if (fChildren) {
for (pwnd = pwnd->spwndChild; pwnd != NULL; pwnd = pwnd->spwndNext) {
if (!SpbTransfer(pspb, pwnd, TRUE)) {
return FALSE;
}
}
}
return TRUE;
}
/***************************************************************************\
* CreateSpb
*
* This function, called after the window is created but before it is visible,
* saves the contents of the screen where the window will be drawn in a SPB
* structure, and links the structure into a linked list of SPB structures.
* popup bits. This routine is called from SetWindowPos.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID CreateSpb(
PWND pwnd,
UINT flags,
HDC hdcScreen)
{
PSPB pspb;
int fSpbLockUpdate;
/*
* Non-LOCKWINDOWUPDATE SPBs can only be created for top-level windows.
*
* This is because of the way that the display driver RestoreBits function
* works. It can put bits down in places that aren't even part of the
* window's visrgn, and these bits need to be invalidated. The
* SetWindowPos() code to handle this case only knows how to invalidate
* one of windows (i.e., the window's immediate parent), but all levels
* need to get invalidated. See also the comments in wmswp.c, near the
* call to RestoreSpb().
*
* For example: the Q&E app brings up a copyright dialog that is a child
* of its main window. While this is up, the user alt-f alt-l to execute
* the file login command, which brings up another dialog that is a child
* of the desktop. When the copyright dialog goes away, the display driver
* restores bits on top of the second dialog. The SWP code knows to
* invalidate the bogus stuff in the main window, but not in the desktop.
*
* LOCKUPDATE SPBs are fine, because they don't call RestoreBits.
*/
fSpbLockUpdate = flags & SPB_LOCKUPDATE;
if ( !fSpbLockUpdate &&
pwnd->spwndParent != NULL &&
pwnd->spwndParent != PWNDDESKTOP(pwnd)) {
return;
}
/*
* We go and check all the existing DCs at this point, to handle the
* case where we're saving an image of a window that has a "dirty"
* DC, which would eventually invalidate our saved image (but which
* is really okay).
*/
if (AnySpbs()) {
SpbCheck();
} else {
PDCE pdce;
/*
* Reset the dirty areas of all of the DC's and enable
* bounds accumulation. We're creating a SPB now. This
* is only done if there are no other SPB's in the list.
*/
GreLockDisplay(gpDispInfo->hDev);
for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) {
if (pdce->DCX_flags & DCX_REDIRECTED)
continue;
GreGetBounds(pdce->hdc, NULL, GGB_ENABLE_WINMGR);
}
GreUnlockDisplay(gpDispInfo->hDev);
}
/*
* Create the save popup bits structure
*/
pspb = (PSPB)UserAllocPoolWithQuota(sizeof(SPB), TAG_SPB);
if (!pspb)
return;
pspb->spwnd = NULL;
pspb->rc = pwnd->rcWindow;
/*
* Clip to the screen
*/
if (!IntersectRect(&pspb->rc, &pspb->rc, &gpDispInfo->rcScreen))
goto BMError;
pspb->hrgn = NULL;
pspb->hbm = NULL;
pspb->flags = flags;
Lock(&(pspb->spwnd), pwnd);
if (!fSpbLockUpdate) {
RECT rc = pspb->rc;
if (!SYSMET(SAMEDISPLAYFORMAT)) {
PMONITOR pMonitor = _MonitorFromRect(&pspb->rc, MONITOR_DEFAULTTOPRIMARY);
RECT rcT;
/*
* If the intersection with the monitor isn't the entire visible
* window rectangle, then bail! We don't save SPBs for windows
* that span multiple monitors. Since we do a lot of work to
* pin dialogs and menus, there won't be too many of these
* babies.
*/
if (SubtractRect(&rcT, &pspb->rc, &pMonitor->rcMonitor) &&
GreRectInRegion(gpDispInfo->hrgnScreen, &rcT))
goto BMError2;
/*
* Clip to the window's monitor
*/
if (!IntersectRect(&pspb->rc, &pspb->rc, &pMonitor->rcMonitor))
goto BMError2;
/*
* dont save bits in a mixed bitdepth situtation
* we cant create the exactly correct format bitmap
* in all cases (555/565, and Paletized) so as
* a cop-out dont save bitmaps at all (on secondaries)
* in mixed bit-depth.
*
* the correct fix is to create a compatible
* bitmap for the monitor device and directly
* BitBlt() from/to the device (pMonitor->hdcMonitor)
* but this involves too much code at this time.
*/
if (pMonitor != gpDispInfo->pMonitorPrimary)
goto BMError2;
}
/*
* If this window is a regional window, don't use driver save
* bits. Because it can only restore an entire rectangle,
* invalid region is calculated assuming the old vis rgn was
* rectangular. For regional windows, this would end up always
* invalidating the area of (rcWindow - hrgnWindow) every
* time an spb would be used. On the other hand, the invalid
* area calculated when not using driver save bits is perfect,
* because the restore blt can be correctly clipped to begin with.
*/
if ((pwnd->hrgnClip == NULL) &&
(pspb->ulSaveId = GreSaveScreenBits(gpDispInfo->hDev,
SS_SAVE,
0,
(RECTL *)&rc))) {
/*
* Remember that we copied this bitmap into on board memory.
*/
pspb->flags |= SPB_SAVESCREENBITS;
} else {
HBITMAP hbmSave;
BOOL bRet;
/*
* The following delta byte-aligns the screen bitmap
*/
int dx = pspb->rc.left & 0x0007;
int cx = pspb->rc.right - pspb->rc.left;
int cy = pspb->rc.bottom - pspb->rc.top;
/*
* NOTE: we don't care about setting up a visrgn in
* hdcScreen, because BitBlt ignores it on reads.
*/
pspb->hbm = GreCreateCompatibleBitmap(hdcScreen, cx + dx, cy);
if (!pspb->hbm)
goto BMError2;
hbmSave = (HBITMAP)GreSelectBitmap(ghdcMem, pspb->hbm);
if (!hbmSave)
goto BMError2;
/*
* Copy the contents of the screen to the bitmap in the
* save popup bits structure. If we ever find we run
* into problems with the screen access check we can
* do a bLockDisplay, give this process permission, do
* the BitBlt and then take away permission. GDI
* accesses the screen and that bit only under the
* display semaphore so it is safe. Alternatively
* if it is too hard to change this processes permission
* here we could do it in GDI by marking the psoSrc
* readable temporarily while completing the operation
* and then setting it back to unreadable when done.
* Or we could just fail it like the CreateCompatibleDC
* failed and force a redraw. Basically we can't add
* 3K of code in GDI to do a BitBlt that just does 1
* test differently for this 1 place in User.
*
*/
bRet = GreBitBlt(ghdcMem,
dx,
0,
cx,
cy,
hdcScreen,
pspb->rc.left,
pspb->rc.top,
0x00CC0000,
0);
GreSelectBitmap(ghdcMem, hbmSave);
if (!bRet)
goto BMError2;
GreSetBitmapOwner(pspb->hbm, OBJECT_OWNER_PUBLIC);
}
/*
* Mark that the window has an SPB.
*/
SetWF(pwnd, WFHASSPB);
/*
* non-LOCKUPDATE SPBs are not invalidated by
* drawing in pspb->spwnd, so start the SPB validation
* loop below at the sibling immediately below us.
*/
pwnd = pwnd->spwndNext;
}
/*
* Link the new save popup bits structure into the list.
*/
pspb->pspbNext = gpDispInfo->pspbFirst;
gpDispInfo->pspbFirst = pspb;
/*
* Here we deal with any update regions that may be
* pending in windows underneath the SPB.
*
* For all windows that might affect this SPB:
* - Subtract the SPB rect from the update region
* - Subtract the window from the SPB
*
* Note that we use pspb->spwnd here, in case it has
* no siblings.
*
* ghrgnSPB2 is the region that is used inside of SpbTransfer to
* validate window update regions. Intersect with the window clipping
* region, if it exists. Don't want to intersect with the spb rect if
* a clipping region exists because we'll end up validating more than
* we want to validate.
*/
SetRectRgnIndirect(ghrgnSPB2, &pspb->rc);
if (pspb->spwnd->hrgnClip != NULL) {
/*
* If we get an error bail since an error might result in more
* being validated than we want. Since the below code is only an
* optimizer, this is ok: the window will remain invalid and will
* draw, thereby invalidating the SPB like usual.
*/
if (IntersectRgn(ghrgnSPB2,
ghrgnSPB2,
pspb->spwnd->hrgnClip) == ERROR) {
return;
}
}
if (pspb->spwnd->spwndParent == NULL ||
SpbTransfer(pspb, pspb->spwnd->spwndParent, FALSE)) {
/*
* Do the same for the siblings underneath us...
*/
for ( ; pwnd != NULL; pwnd = pwnd->spwndNext) {
if (!SpbTransfer(pspb, pwnd, TRUE))
break;
}
}
return;
BMError2:
/*
* Error creating the bitmap: clean up and return.
*/
if (pspb->hbm)
GreDeleteObject(pspb->hbm);
Unlock(&pspb->spwnd);
// fall-through
BMError:
UserFreePool(pspb);
}
/***************************************************************************\
* RestoreSpb
*
* Restores the bits associated with pwnd's SPB onto the screen, clipped
* to hrgnUncovered if possible.
*
* Upon return, hrgnUncovered is modified to contain the part of hrgnUncovered
* that must be invalidated by the caller. FALSE is returned if the area
* to be invalidated is empty.
*
* NOTE: Because the device driver SaveBitmap() function can not clip, this
* function may write bits into an area of the screen larger than the passed-in
* hrgnUncovered. In this case, the returned invalid region may be larger
* than the passed-in hrgnUncovered.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
UINT RestoreSpb(
PWND pwnd,
HRGN hrgnUncovered,
HDC *phdcScreen)
{
PSPB pspb;
UINT uInvalidate;
HRGN hrgnRestorable;
/*
* Note that we DON'T call SpbCheck() here --
* SpbCheck() is called by zzzBltValidBits().
*/
pspb = FindSpb(pwnd);
/*
* Assume all of hrgnUncovered was restored, and there's nothing
* for our caller to invalidate.
*/
uInvalidate = RSPB_NO_INVALIDATE;
hrgnRestorable = hrgnUncovered;
/*
* First determine whether or not there is any area at all to restore.
* If hrgnUncovered & pspb->hrgn is empty, then all of hrgnUncovered
* needs to be invalidated, and there's nothing to restore.
*/
if (pspb->hrgn != NULL) {
/*
* At least some of hrgnUncovered needs to be invalidated.
*/
uInvalidate = RSPB_INVALIDATE;
/*
* Calculate the true area of bits to be restored. If it becomes
* empty, then just free the SPB without changing hrgnUncovered,
* which is the area that must be invalidated.
*/
hrgnRestorable = ghrgnSPB1;
switch (IntersectRgn(hrgnRestorable, hrgnUncovered, pspb->hrgn)) {
case ERROR:
case NULLREGION:
goto Error;
default:
break;
}
}
if (pspb->flags & SPB_SAVESCREENBITS) {
RECT rc = pspb->rc;
/*
* Since the restore frees the onboard memory, clear this
* bit so FreeSpb() won't try to free it again (regardless of
* whether we get an error or not)
*/
pspb->flags &= ~SPB_SAVESCREENBITS;
if (!(GreSaveScreenBits(gpDispInfo->hDev,
SS_RESTORE,
pspb->ulSaveId,
(RECTL *)&rc))) {
goto Error;
}
/*
* The SS_RESTORE call will always restore the entire SPB
* rectangle, part of which may fall outside of hrgnUncovered.
* The area that must be invalidated by our caller is simply
* the SPB rectangle minus the area of restorable bits.
*
* If this region is not empty, then the SPB was not completely
* restored, so we must return FALSE.
*/
SetRectRgnIndirect(ghrgnSPB2, &pspb->rc);
if (SubtractRgn(hrgnUncovered, ghrgnSPB2, hrgnRestorable) != NULLREGION) {
uInvalidate = RSPB_INVALIDATE_SSB;
}
} else {
HDC hdcScreen;
HBITMAP hbmSave;
/*
* In the unlikely event we need a screen DC and one wasn't passed in,
* get it now. If we get one, we return the handle in *phdcScreen
* so that our caller can release it later.
*/
if (!*phdcScreen) {
*phdcScreen = gpDispInfo->hdcScreen;
}
hdcScreen = *phdcScreen;
hbmSave = (HBITMAP)GreSelectBitmap(ghdcMem, pspb->hbm);
if (!hbmSave)
goto Error;
/*
* Be sure to clip to the area of restorable bits.
*/
GreSelectVisRgn(hdcScreen, hrgnRestorable, SVR_COPYNEW);
GreBitBlt(hdcScreen,
pspb->rc.left, pspb->rc.top,
pspb->rc.right - pspb->rc.left,
pspb->rc.bottom - pspb->rc.top,
ghdcMem,
pspb->rc.left & 0x0007,
0,
SRCCOPY,
0);
GreSelectBitmap(ghdcMem, hbmSave);
/*
* Now compute the area to be invalidated for return.
* This is simply the original hrgnUncovered - hrgnRestorable
*/
SubtractRgn(hrgnUncovered, hrgnUncovered, hrgnRestorable);
}
if (pwnd->hrgnClip == NULL || !IsVisible(pwnd))
FreeSpb(pspb);
return uInvalidate;
Error:
FreeSpb(pspb);
return RSPB_INVALIDATE;
}
/***************************************************************************\
* LockWindowUpdate2 (API)
*
* Locks gspwndLockUpdate and it's children from updating. If
* gspwndLockUpdate is NULL, then all windows will be unlocked. When
* unlocked, the portions of the screen that would have been written to
* are invalidated so they get repainted. TRUE is returned if the routine
* is successful.
*
* If called when another app has something locked, then this function fails.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL LockWindowUpdate2(
PWND pwndLock,
BOOL fThreadOverride)
{
PSPB pspb;
BOOL fInval;
HRGN hrgn;
PTHREADINFO ptiCurrent = PtiCurrent();
if ( /*
* If we're full screen right now, fail this call.
*/
TEST_PUDF(PUDF_LOCKFULLSCREEN)
||
/*
* If the screen is already locked, and it's being locked
* by some other app, then fail. If fThreadOverride is set
* then we're calling internally and it's okay to cancel
* someone elses LockUpdate.
*/
( gptiLockUpdate != NULL &&
gptiLockUpdate != PtiCurrent() &&
!fThreadOverride)) {
UserAssert(IsWinEventNotifyDeferredOK());
RIPERR0(ERROR_SCREEN_ALREADY_LOCKED,
RIP_WARNING,
"LockWindowUpdate failed because screen is locked by another application.");
return FALSE;
}
if ((pwndLock != NULL) == (gptiLockUpdate != NULL)) {
if (!fThreadOverride) {
RIPERR1(ERROR_INVALID_PARAMETER,
RIP_WARNING,
"LockWindowUpdate failed because it is already %s.",
(pwndLock != NULL) ? "locked" : "unlocked");
}
return FALSE;
}
/*
* This must be done while holding the screen critsec.
* Deadlock if we callback during this, so defer WinEvent notifications
*/
DeferWinEventNotify();
GreLockDisplay(gpDispInfo->hDev);
if (pwndLock != NULL) {
/*
* We're about to make pwndLock and its siblings invisible:
* go invalidate any other affected SPBs.
*/
SpbCheckPwnd(pwndLock);
CreateSpb(pwndLock, SPB_LOCKUPDATE, NULL);
Lock(&(gspwndLockUpdate), pwndLock);
gptiLockUpdate = ptiCurrent;
zzzInvalidateDCCache(pwndLock, IDC_DEFAULT);
} else {
/*
* Flush any accumulated rectangles and invalidate spbs.
*/
SpbCheck();
/*
* Save this in a local before we set it to NULL
*/
pwndLock = gspwndLockUpdate;
gptiLockUpdate = NULL;
Unlock(&gspwndLockUpdate);
zzzInvalidateDCCache(pwndLock, IDC_DEFAULT);
/*
* Assume SPB doesn't exist, or couldn't be created, and that we
* must invalidate the entire window.
*/
fInval = TRUE;
hrgn = HRGN_FULL;
/*
* Find the LOCKUPDATE spb in the list, and if present calculate
* the area that has been invalidated, if any.
*/
for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspb->pspbNext) {
if (pspb->flags & SPB_LOCKUPDATE) {
if (pspb->hrgn == NULL) {
/*
* If no invalid area, then no invalidation needed.
*/
fInval = FALSE;
} else {
/*
* Subtract SPB valid region from SPB rectangle, to
* yield invalid region.
*/
hrgn = ghrgnSPB1;
SetRectRgnIndirect(hrgn, &pspb->rc);
/*
* If spb rect minus the spb valid rgn is empty,
* then there is nothing to invalidate.
*/
fInval = SubtractRgn(hrgn, hrgn, pspb->hrgn) != NULLREGION;
}
FreeSpb(pspb);
/*
* Exit this loop (there can be only one LOCKUPDATE spb)
*/
break;
}
}
if (fInval) {
/*
* When unlocking a Layered window (or a child of a layered
* window), we need to invalidate that layered window specifically
* or the window will ignore the invalidation request. For regular
* windows, we invalidate the desktop instead.
*/
PWND pwndInvalidate;
if ((pwndInvalidate = GetStyleWindow(pwndLock, WEFLAYERED)) == NULL) {
pwndInvalidate = PWNDDESKTOP(pwndLock);
}
BEGINATOMICCHECK();
// want to prevent WinEvent notifies, but this make break asserts
DeferWinEventNotify();
xxxInternalInvalidate(pwndInvalidate,
hrgn,
RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
zzzEndDeferWinEventNotify();
ENDATOMICCHECK();
}
/*
* Invalidate any other SPBs affected by the fact that this window
* and its children are being made visible.
*/
SpbCheckPwnd(pwndLock);
}
GreUnlockDisplay(gpDispInfo->hDev);
zzzEndDeferWinEventNotify();
return TRUE;
}
/***************************************************************************\
* FindSpb
*
* Returns a pointer to the SPB structure associated with the specified
* window or NULL if there is no associated structure.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
PSPB FindSpb(
PWND pwnd)
{
PSPB pspb;
/*
* Walk through the list of save popup bits looking for a match on
* window handle.
*/
for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspb->pspbNext) {
if (pspb->spwnd == pwnd && !(pspb->flags & SPB_LOCKUPDATE))
break;
}
return pspb;
}
/***************************************************************************\
* SpbCheck
*
* Modifies all of the save popup bits structures to reflect changes on the
* screen. This function walks through all of the DC's, and if the DC is
* dirty, then the dirty area is removed from the associated save popup bits
* structure.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SpbCheck(VOID)
{
PDCE pdce;
RECT rcBounds;
if (AnySpbs()) {
GreLockDisplay(gpDispInfo->hDev);
/*
* Walk through all of the DC's, accumulating dirty areas.
*/
for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) {
/*
* Only check valid cache entries...
*/
if (pdce->DCX_flags & (DCX_INVALID | DCX_DESTROYTHIS))
continue;
SpbCheckDce(pdce);
}
/*
* Subtact out DirectDraw dirty rect from all the SPB's. The call to
* GreGetDirectDrawBounds will also reset the accumulated bounds.
*/
if (GreGetDirectDrawBounds(gpDispInfo->hDev, &rcBounds)) {
SpbCheckRect(NULL, &rcBounds, 0);
}
GreUnlockDisplay(gpDispInfo->hDev);
}
}
/***************************************************************************\
* SpbCheckDce
*
* This function retrieves the dirty area of a DC and removes the area from
* the list of SPB structures. The DC is then marked as clean.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SpbCheckDce(
PDCE pdce)
{
RECT rc;
if (pdce->DCX_flags & DCX_REDIRECTED)
return;
/*
* Query the dirty bounds rectangle. Doing this clears the bounds
* as well.
*/
if (GreGetBounds(pdce->hdc, &rc, 0)) {
if (pdce->pMonitor != NULL) {
/*
* Convert the bounds rect to screen coords.
*/
OffsetRect(&rc, pdce->pMonitor->rcMonitor.left,
pdce->pMonitor->rcMonitor.top);
}
/*
* Intersect the returned rectangle with the window rectangle
* in case the guy was drawing outside his window
*/
if (IntersectRect(&rc, &rc, &(pdce->pwndOrg)->rcWindow))
SpbCheckRect(pdce->pwndOrg, &rc, pdce->DCX_flags);
}
}
/***************************************************************************\
* SpbCheckRect
*
* This function removes the passed rectangle from the SPB structures which
* touch it.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SpbCheckRect(
PWND pwnd,
LPRECT lprc,
DWORD flags)
{
PSPB pspb, pspbNext;
/*
* If this window isn't visible, we're done.
*/
if (!IsVisible(pwnd))
return;
for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspbNext) {
/*
* Get the pointer to the next save popup bits structure now
* in case SpbCheckRect2() frees the current one.
*/
pspbNext = pspb->pspbNext;
/*
* In win3.1 they used to exit the function if this function
* returned false. This meant that if one of the spbs was freed
* the rest of the spbs would not be invalidated.
*/
SpbCheckRect2(pspb, pwnd, lprc, flags);
}
}
/***************************************************************************\
* SpbCheckPwnd
*
* This routine checks to see if the window rectangle of PWND affects any SPBs.
* It is called if pwnd or its children are being hidden or shown without
* going through WinSetWindowPos().
*
* Any SPBs for children of pwnd are destroyed.
*
* It must be called while pwnd is still visible.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SpbCheckPwnd(
PWND pwnd)
{
PSPB pspb;
PWND pwndSpb;
PSPB pspbNext;
/*
* First blow away any SPBs owned by this window or its children.
*/
for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspbNext) {
/*
* Get pspbNext now in case we free the SPB
*/
pspbNext = pspb->pspbNext;
/*
* If pspb->spwnd is == pwnd or a child of pwnd, then free the SPB
*/
for (pwndSpb = pspb->spwnd; pwndSpb; pwndSpb = pwndSpb->spwndParent) {
if (pwnd == pwndSpb)
FreeSpb(pspb);
}
}
/*
* Then see if any other SPBs are affected...
*/
if (gpDispInfo->pspbFirst != NULL) {
SpbCheckRect(pwnd, &pwnd->rcWindow, 0);
}
}
/***************************************************************************\
* FreeSpb
*
* This function deletes the bitmap and region assocaited with a save popup
* bits structure and then unlinks and destroys the spb structure itself.
*
* History:
* 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID FreeSpb(
PSPB pspb)
{
PSPB *ppspb;
PDCE pdce;
if (pspb == NULL)
return;
/*
* Delete the bitmap. If saved in screen memory, make special call.
*/
if (pspb->flags & SPB_SAVESCREENBITS) {
GreSaveScreenBits(gpDispInfo->hDev, SS_FREE, pspb->ulSaveId, NULL);
} else if (pspb->hbm != NULL) {
GreDeleteObject(pspb->hbm);
}
/*
* Destroy the region.
*/
if (pspb->hrgn != NULL){
GreDeleteObject(pspb->hrgn);
}
/*
* Forget that there is an attached SPB.
*/
if (pspb->spwnd != NULL) {
ClrWF(pspb->spwnd, WFHASSPB);
Unlock(&pspb->spwnd);
}
/*
* Unlink the spb.
*/
ppspb = &gpDispInfo->pspbFirst;
while (*ppspb != pspb) {
ppspb = &(*ppspb)->pspbNext;
}
*ppspb = pspb->pspbNext;
/*
* Free the save popup bits structure.
*/
UserFreePool(pspb);
/*
* If we no longer have any SPBs then turn off window MGR
* bounds collection.
*/
if (!AnySpbs()) {
GreLockDisplay(gpDispInfo->hDev);
/*
* Reset the dirty areas of all of the DC's. NULL means reset.
*/
for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) {
if (pdce->DCX_flags & DCX_REDIRECTED)
continue;
GreGetBounds(pdce->hdc, NULL, GGB_DISABLE_WINMGR);
}
GreUnlockDisplay(gpDispInfo->hDev);
}
}
/***************************************************************************\
* FreeAllSpbs
*
* This function deletes all spb-bitmaps.
*
* History:
* 07-Oct-1995 ChrisWil Ported from Chicago.
\***************************************************************************/
VOID FreeAllSpbs(void)
{
while(AnySpbs()) {
FreeSpb(gpDispInfo->pspbFirst);
}
gpDispInfo->pspbFirst = NULL;
}