Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4802 lines
146 KiB

/****************************** Module Header ******************************\
* Module Name: swp.c
*
* Copyright (c) 1985-1996, Microsoft Corporation
*
* Contains the xxxSetWindowPos API and related functions.
*
* History:
* 20-Oct-1990 DarrinM Created.
* 25-Jan-1991 IanJa added window revalidation
* 11-Jul-1991 DarrinM Replaced everything with re-ported Win 3.1 code.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define CTM_NOCHANGE 0
#define CTM_TOPMOST 1
#define CTM_NOTOPMOST 2
typedef struct tagPWNDLIST {
int cnt;
HWND *pahwnd;
} PWNDLIST;
/***************************************************************************\
* MoveWindow (API)
*
*
* History:
* 25-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
#define MW_FLAGS_REDRAW (SWP_NOZORDER | SWP_NOACTIVATE)
#define MW_FLAGS_NOREDRAW (SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW)
BOOL xxxMoveWindow(
PWND pwnd,
int x,
int y,
int cx,
int cy,
BOOL fRedraw)
{
CheckLock(pwnd);
if ((pwnd == PWNDDESKTOP(pwnd)) ||
TestWF(pwnd, WFWIN31COMPAT) ||
(pwnd->spwndParent != PWNDDESKTOP(pwnd))) {
return xxxSetWindowPos(
pwnd,
NULL,
x,
y,
cx,
cy,
(fRedraw ? MW_FLAGS_REDRAW : MW_FLAGS_NOREDRAW));
} else {
/*
* BACKWARD COMPATIBILITY CODE FOR WIN 3.00 AND BELOW
*
* Everyone and their brother seems to depend on this behavior for
* top-level windows. Specific examples are:
*
* AfterDark help window animation
* Finale Speedy Note Editing
*
* If the window is a top-level window and fRedraw is FALSE,
* we must call SetWindowPos with SWP_NOREDRAW CLEAR anyway so that
* the frame and window background get drawn. We then validate the
* entire client rectangle to avoid repainting that.
*/
BOOL fResult = xxxSetWindowPos(pwnd,
NULL,
x,
y,
cx,
cy,
MW_FLAGS_REDRAW);
if (!fRedraw)
xxxValidateRect(pwnd, NULL);
return fResult;
}
}
/***************************************************************************\
* BeginDeferWindowPos (API)
*
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
PSMWP _BeginDeferWindowPos(
int cwndHint)
{
PSMWP psmwp;
PCVR acvr;
if (cwndHint == 0)
cwndHint = 8;
acvr = (PCVR)UserAllocPoolWithQuota(sizeof(CVR) * cwndHint, TAG_SWP);
if (acvr == NULL)
return NULL;
psmwp = (PSMWP)HMAllocObject(PtiCurrent(),
NULL,
TYPE_SETWINDOWPOS,
sizeof(SMWP));
if (psmwp) {
psmwp->acvr = acvr;
psmwp->ccvrAlloc = cwndHint;
psmwp->ccvr = 0;
} else {
UserFreePool(acvr);
}
return psmwp;
}
/***************************************************************************\
* PWInsertAfter
* HWInsertAfter
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PWND PWInsertAfter(
HWND hwnd)
{
PWND pwnd;
/*
* HWND_GROUPTOTOP and HWND_TOPMOST are the same thing.
*/
switch ((UINT)hwnd) {
case (UINT)HWND_TOP:
case (UINT)HWND_BOTTOM:
case (UINT)HWND_TOPMOST:
case (UINT)HWND_NOTOPMOST:
return (PWND)hwnd;
default:
/*
* Don't insert after a destroyed window! It will cause the
* window being z-ordered to become unlinked from it's siblings.
*/
if (pwnd = RevalidateHwnd(hwnd)) {
/*
* Do not insert after a destroyed window. Put it at the
* bottom of the list, if it is z-ordered at all.
*/
if (TestWF(pwnd, WFDESTROYED) || pwnd->spwndParent == NULL)
return NULL;
UserAssert(_IsDescendant(pwnd->spwndParent, pwnd));
return pwnd;
}
return NULL;
}
}
HWND HWInsertAfter(
PWND pwnd)
{
/*
* HWND_GROUPTOTOP and HWND_TOPMOST are the same thing.
*/
switch ((UINT)pwnd) {
case (UINT)HWND_TOP:
case (UINT)HWND_BOTTOM:
case (UINT)HWND_TOPMOST:
case (UINT)HWND_NOTOPMOST:
return (HWND)pwnd;
default:
return HW(pwnd);
}
}
/***************************************************************************\
* DeferWindowPos (API)
*
*
* History:
* 07-11-91 darrinm Ported from Win 3.1 sources.
\***************************************************************************/
PSMWP _DeferWindowPos(
PSMWP psmwp,
PWND pwnd,
PWND pwndInsertAfter,
int x,
int y,
int cx,
int cy,
UINT flags)
{
PWINDOWPOS ppos;
PCVR pcvr;
if (psmwp->ccvr + 1 > psmwp->ccvrAlloc) {
DWORD dwSize = psmwp->ccvrAlloc * sizeof(CVR);
/*
* Make space for 4 more windows
*/
psmwp->ccvrAlloc += 4;
pcvr = (PCVR)UserReAllocPoolWithQuota(psmwp->acvr,
dwSize,
sizeof(CVR) * psmwp->ccvrAlloc,
TAG_SWP);
if (pcvr == NULL) {
HMDestroyObject(psmwp);
return NULL;
}
psmwp->acvr = pcvr;
}
pcvr = &psmwp->acvr[psmwp->ccvr++];
ppos = &pcvr->pos;
ppos->hwnd = HWq(pwnd);
ppos->hwndInsertAfter = (TestWF(pwnd, WFBOTTOMMOST)) ?
HWND_BOTTOM : HWInsertAfter(pwndInsertAfter);
ppos->x = x;
ppos->y = y;
ppos->cx = cx;
ppos->cy = cy;
ppos->flags = flags;
pcvr->hrgnClip = NULL;
return psmwp;
}
/***************************************************************************\
* ValidateWindowPos
*
* checks validity of SWP structure
*
* NOTE: For performance reasons, this routine is only called
* in the DEBUG version of USER.
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL ValidateWindowPos(
PCVR pcvr)
{
PWND pwnd;
PWND pwndInsertAfter;
HWND hwndInsertAfter;
if ((pwnd = RevalidateHwnd(pcvr->pos.hwnd)) == NULL)
return FALSE;
/*
* Save the pti
*/
pcvr->pti = GETPTI(pwnd);
/*
* If the SWP_NOZORDER bit is not set, validate the Insert behind window.
*/
if (!(pcvr->pos.flags & SWP_NOZORDER)) {
/*
* Do not z-order destroyed windows
*/
if (TestWF(pwnd, WFDESTROYED))
return FALSE;
hwndInsertAfter = pcvr->pos.hwndInsertAfter;
/*
* HWND_TOPMOST isn't allowed for child windows.
*/
if ((hwndInsertAfter == HWND_TOPMOST) ||
(hwndInsertAfter == HWND_NOTOPMOST)) {
return pwnd->spwndParent == PWNDDESKTOP(pwnd);
}
if (hwndInsertAfter != HWND_TOP && hwndInsertAfter != HWND_BOTTOM) {
/*
* Ensure pwndInsertAfter is valid
*/
if (((pwndInsertAfter = RevalidateHwnd(hwndInsertAfter)) == NULL) ||
TestWF(pwndInsertAfter, WFDESTROYED)) {
RIPERR1(ERROR_INVALID_HANDLE, RIP_WARNING, "Invalid hwndInsertAfter (0x%lx)", hwndInsertAfter);
return FALSE;
}
/*
* Ensure that pwndInsertAfter is a sibling of pwnd
*/
if (pwnd == pwndInsertAfter ||
pwnd->spwndParent != pwndInsertAfter->spwndParent) {
RIPMSG2(RIP_WARNING, "hwndInsertAfter (0x%lx) is not a sibling "
"of hwnd (0x%lx)", hwndInsertAfter, pcvr->pos.hwnd);
return FALSE;
}
}
}
return TRUE;
}
/***************************************************************************\
* IsStillWindowC
*
* Checks if window is valid HWNDC still, and child of proper dude.
*
* History:
\***************************************************************************/
BOOL IsStillWindowC(
HWND hwndc)
{
switch ((DWORD)hwndc) {
case (UINT)HWND_TOP:
case (UINT)HWND_BOTTOM:
case (UINT)HWND_TOPMOST:
case (UINT)HWND_NOTOPMOST:
return TRUE;
default:
/*
* Make sure we're going to insert after a window that's
* (1) Valid
* (2) Peer
*/
return (RevalidateHwnd(hwndc) != 0);
}
}
/***************************************************************************\
* ValidateSmwp
*
* Validate the SMWP and figure out which window should get activated,
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL ValidateSmwp(
PSMWP psmwp,
BOOL *pfSyncPaint)
{
PCVR pcvr;
PWND pwndParent;
PWND pwndT;
int ccvr;
*pfSyncPaint = TRUE;
pwndT = (PWND)HMValidateHandleNoRip(psmwp->acvr[0].pos.hwnd, TYPE_WINDOW);
if (pwndT == NULL)
return FALSE;
pwndParent = pwndT->spwndParent;
/*
* Validate the passed-in WINDOWPOS structs, and find a window to activate.
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (!ValidateWindowPos(pcvr)) {
pcvr->pos.hwnd = NULL;
continue;
}
/*
* All windows in the pos list must have the same parent.
* If not, yell and return FALSE.
*/
UserAssert(IsStillWindowC(pcvr->pos.hwnd));
UserAssert(PW(pcvr->pos.hwnd));
if (PW(pcvr->pos.hwnd)->spwndParent != pwndParent) {
RIPERR0(ERROR_HWNDS_HAVE_DIFF_PARENT, RIP_VERBOSE, "");
return FALSE;
}
/*
* If SWP_DEFERDRAWING is set for any of the windows, suppress
* DoSyncPaint() call later.
*/
if (pcvr->pos.flags & SWP_DEFERDRAWING)
*pfSyncPaint = FALSE;
}
return TRUE;
}
/***************************************************************************\
* FindValidWindowPos
*
* Some of the windows in the SMWP list may be NULL at ths point (removed
* because they'll be handled by their creator's thread) so we've got to
* look for the first non-NULL window and return it.
*
* History:
* 10-Sep-1991 DarrinM Created.
\***************************************************************************/
PWINDOWPOS FindValidWindowPos(
PSMWP psmwp)
{
int i;
for (i = 0; i < psmwp->ccvr; i++) {
if (psmwp->acvr[i].pos.hwnd != NULL)
return &psmwp->acvr[i].pos;
}
return NULL;
}
/***************************************************************************\
*
* GetLastNonBottomMostWindow()
*
* Returns the last non bottom-most window in the z-order, NULL if
* there isn't one. When figuring out whom to insert after, we want to
* skip ourself. But when figuring out if we're already in place, we don't
* want to skip ourself on enum.
*
* History:
\***************************************************************************/
PWND GetLastNonBottomMostWindow(
PWND pwnd,
BOOL fSkipSelf)
{
PWND pwndT;
PWND pwndLast = NULL;
for (pwndT = pwnd->spwndParent->spwndChild;
pwndT && !TestWF(pwndT, WFBOTTOMMOST);
pwndT = pwndT->spwndNext) {
if (!fSkipSelf || (pwnd != pwndT))
pwndLast = pwndT;
}
return pwndLast;
}
/***************************************************************************\
* ValidateZorder
*
* Checks to see if the specified window is already in the specified Z order
* position, by comparing the current Z position with the specified
* pwndInsertAfter.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL ValidateZorder(
PCVR pcvr)
{
PWND pwnd;
PWND pwndPrev;
PWND pwndInsertAfter;
BYTE bTopmost;
/*
* Validate just to make sure this routine doesn't do anything bogus.
* Its caller will actually redetect and handle the error.
*/
UserAssert(RevalidateHwnd(pcvr->pos.hwnd));
pwnd = PW(pcvr->pos.hwnd); // Known to be valid at this point.
/*
* Don't z-order a destroyed window
*/
if (TestWF(pwnd, WFDESTROYED))
return TRUE;
pwndInsertAfter = PWInsertAfter(pcvr->pos.hwndInsertAfter);
if (pcvr->pos.hwndInsertAfter != NULL && pwndInsertAfter == NULL)
return TRUE;
if (pwndInsertAfter == PWND_BOTTOM) {
if (TestWF(pwnd, WFBOTTOMMOST))
return(pwnd->spwndNext == NULL);
else
return(pwnd == GetLastNonBottomMostWindow(pwnd, FALSE));
}
pwndPrev = pwnd->spwndParent->spwndChild;
if (pwndInsertAfter == PWND_TOP)
return pwndPrev == pwnd;
if (TestWF(pwndInsertAfter, WFDESTROYED))
return TRUE;
/*
* When we compare the state of the window, we must use
* the EVENTUAL state of the window that is moving, but
* the CURRENT state of the window it's inserted behind.
*
* Prevent nonbottommost windows from going behind the bottommost one
*/
if (TestWF(pwndInsertAfter, WFBOTTOMMOST)) {
pcvr->pos.hwndInsertAfter = HWInsertAfter(GetLastNonBottomMostWindow(pwnd, TRUE));
return FALSE;
}
/*
* If we are not topmost, but pwndInsertAfter is, OR
* if we are topmost, but pwndInsertAfter is not,
* we need to adjust pwndInsertAfter to be the last of
* the topmost windows.
*/
bTopmost = TestWF(pwnd, WEFTOPMOST);
if (TestWF(pwnd, WFTOGGLETOPMOST))
bTopmost ^= LOBYTE(WEFTOPMOST);
if (bTopmost != (BYTE)TestWF(pwndInsertAfter, WEFTOPMOST)) {
pwndInsertAfter = GetLastTopMostWindow();
/*
* We're correctly positioned if we're already at the bottom
*/
if (pwndInsertAfter == pwnd)
return TRUE;
pcvr->pos.hwndInsertAfter = HW(pwndInsertAfter);
}
/*
* Look for our previous window in the list...
*/
if (pwndPrev != pwnd) {
for ( ; pwndPrev != NULL; pwndPrev = pwndPrev->spwndNext) {
if (pwndPrev->spwndNext == pwnd)
return pwndInsertAfter == pwndPrev;
}
/*
* If we get to here, pwnd is not in the sibling list.
* REALLY BAD NEWS!
*/
UserAssert(FALSE);
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* xxxCalcValidRects
*
* Based on the WINDOWPOS flags in the fs parameter in each WINDOWPOS structure,
* this routine calcs the new position and size of each window, determines if
* its changing Z order, or whether its showing or hiding. Any redundant
* flags are AND'ed out of the fs parameter. If no redrawing is needed,
* SWP_NOREDRAW is OR'ed into the flags. This is called from EndDeferWindowPos.
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL xxxCalcValidRects(
PSMWP psmwp,
HWND *phwndNewActive)
{
PCVR pcvr;
PWND pwnd;
PWND pwndParent;
HWND hwnd;
HWND hwndNewActive = NULL;
PWINDOWPOS ppos;
BOOL fNoZorder;
BOOL fForceNCCalcSize;
NCCALCSIZE_PARAMS params;
int cxSrc;
int cySrc;
int cxDst;
int cyDst;
int cmd;
int ccvr;
int xClientOld;
int yClientOld;
int cxClientOld;
int cyClientOld;
int xWindowOld;
int yWindowOld;
int cxWindowOld;
int cyWindowOld;
TL tlpwndParent;
TL tlpwnd;
/*
* Some of the windows in the SMWP list may be NULL at ths point
* (removed because they'll be handled by their creator's thread)
* so we've got to look for the first non-NULL window before we can
* execute some of the tests below. FindValidWindowPos returns NULL if
* the list has no valid windows in it.
*/
if ((ppos = FindValidWindowPos(psmwp)) == NULL)
return FALSE;
UserAssert(PW(ppos->hwnd));
pwndParent = PW(ppos->hwnd)->spwndParent;
UserAssert(HMValidateHandle(PtoH(pwndParent), TYPE_WINDOW));
ThreadLock(pwndParent, &tlpwndParent);
fNoZorder = TRUE;
/*
* Go through the SMWP list, enumerating each WINDOWPOS, and compute
* its new window and client rectangles.
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
/*
* This loop may leave the critsect during each iteration so
* we revalidate pos.hwnd before use.
*/
if ((hwnd = pcvr->pos.hwnd) == NULL)
continue;
pwnd = RevalidateHwnd(hwnd);
if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
pcvr->pos.hwnd = NULL;
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
continue;
}
ThreadLockAlways(pwnd, &tlpwnd);
/*
* Used for 3.0 compatibility. 3.0 sent the NCCALCSIZE message even if
* the size of the window wasn't changing.
*/
fForceNCCalcSize = FALSE;
if (!hwndNewActive && !(pcvr->pos.flags & SWP_NOACTIVATE))
hwndNewActive = HWq(pwnd);
if (!(pcvr->pos.flags & SWP_NOSENDCHANGING)) {
PWND pwndT;
xxxSendMessage(pwnd, WM_WINDOWPOSCHANGING, 0, (LONG)&pcvr->pos);
/*
* Don't let them change pcvr->pos.hwnd. It doesn't make sense
* plus it'll mess us up.
* I'm making this RIP_ERROR because we're too close to RTM (7/11/96)
* just to make sure that we won't break anyone. This should be
* changed to a RIP_WARNING after we ship. Use LOWORD to ignore
* "changes" by NTVDM
*/
#ifdef DEBUG
if (LOWORD(pcvr->pos.hwnd) != LOWORD(hwnd)) {
RIPMSG0(RIP_ERROR,
"xxxCalcValidRects: Ignoring pcvr->pos.hwnd change by WM_WINDOWPOSCHANGING");
}
#endif
pcvr->pos.hwnd = hwnd;
/*
* If the window sets again 'hwndInsertAfter' to HWND_NOTOPMOST
* or HWND_TOPMOST, we need to set this member appropriately.
* See CheckTopmost for details.
*/
if (pcvr->pos.hwndInsertAfter == HWND_NOTOPMOST) {
if (TestWF(pwnd, WEFTOPMOST)) {
pwndT = GetLastTopMostWindow();
pcvr->pos.hwndInsertAfter = HW(pwndT);
if (pcvr->pos.hwndInsertAfter == pcvr->pos.hwnd) {
pwndT = _GetWindow(pwnd, GW_HWNDPREV);
pcvr->pos.hwndInsertAfter = HW(pwndT);
}
} else {
pwndT = _GetWindow(pwnd, GW_HWNDPREV);
pcvr->pos.hwndInsertAfter = HW(pwndT);
}
} else if (pcvr->pos.hwndInsertAfter == HWND_TOPMOST) {
pcvr->pos.hwndInsertAfter = HWND_TOP;
}
}
/*
* make sure the rectangle still matches the window's region
*
* Remember the old window rectangle in parent coordinates
*/
xWindowOld = pwnd->rcWindow.left - pwndParent->rcClient.left;
yWindowOld = pwnd->rcWindow.top - pwndParent->rcClient.top;
cxWindowOld = pwnd->rcWindow.right - pwnd->rcWindow.left;
cyWindowOld = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
/*
* Assume the client is not moving or sizing
*/
pcvr->pos.flags |= SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
if (!(pcvr->pos.flags & SWP_NOMOVE)) {
if (pcvr->pos.x == xWindowOld && pcvr->pos.y == yWindowOld)
pcvr->pos.flags |= SWP_NOMOVE;
if (TestWF(pwnd, WFMINIMIZED) && IsTrayWindow(pwnd)) {
pcvr->pos.x = WHERE_NOONE_CAN_SEE_ME;
pcvr->pos.y = WHERE_NOONE_CAN_SEE_ME;
}
} else {
pcvr->pos.x = xWindowOld;
pcvr->pos.y = yWindowOld;
}
if (!(pcvr->pos.flags & SWP_NOSIZE)) {
/*
* Don't allow an invalid window rectangle.
* BOGUS HACK: For Norton Antivirus, they call
* MoveWindow at WM_CREATE Time EVEN though
* the window is minimzed, but they assume its
* restored at WM_CREATE time.... B#11185, t-arthb
*/
if (TestWF(pwnd, WFMINIMIZED) &&
_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL)) {
pcvr->pos.cx = SYSMET(CXMINIMIZED);
pcvr->pos.cy = SYSMET(CYMINIMIZED);
} else {
if (pcvr->pos.cx < 0)
pcvr->pos.cx = 0;
if (pcvr->pos.cy < 0)
pcvr->pos.cy = 0;
}
if (pcvr->pos.cx == cxWindowOld && pcvr->pos.cy == cyWindowOld) {
pcvr->pos.flags |= SWP_NOSIZE;
if (!TestWF(pwnd, WFWIN31COMPAT))
fForceNCCalcSize = TRUE;
}
} else {
pcvr->pos.cx = cxWindowOld;
pcvr->pos.cy = cyWindowOld;
}
/*
* If showing and already visible, or hiding and already hidden,
* turn off the appropriate bit.
*/
if (TestWF(pwnd, WFVISIBLE)) {
pcvr->pos.flags &= ~SWP_SHOWWINDOW;
} else {
pcvr->pos.flags &= ~SWP_HIDEWINDOW;
/*
* If hidden, and we're NOT showing, then we won't be drawing,
* no matter what else is going on.
*/
if (!(pcvr->pos.flags & SWP_SHOWWINDOW))
pcvr->pos.flags |= SWP_NOREDRAW;
}
/*
* Muck with the zorder for bottommost windows, again
* See comment in DeferWindowPos
*/
if (TestWF(pwnd, WFBOTTOMMOST)) {
pcvr->pos.flags &= ~SWP_NOZORDER;
pcvr->pos.hwndInsertAfter = HWND_BOTTOM;
}
/*
* If we're Z-ordering, we can try to remove the Z order
* bit, as long as all previous windows in the WINDOWPOS list
* have SWP_NOZORDER set.
*
* The reason we don't do this for each window individually
* is that a window's eventual Z order depends on changes that
* may have occured on windows earlier in the WINDOWPOS list,
* so we can only call ValidateZorder if none of the previous
* windows have changed.
*/
if (fNoZorder && !(pcvr->pos.flags & SWP_NOZORDER)) {
/*
* If the TOPMOST bit is changing, the Z order is "changing",
* so don't clear the bit even if it's in the right place in the
* list.
*/
fNoZorder = FALSE;
if (!TestWF(pwnd, WFTOGGLETOPMOST) && ValidateZorder(pcvr)) {
fNoZorder = TRUE;
pcvr->pos.flags |= SWP_NOZORDER;
}
}
/*
* If no change is occuring, or if a parent is invisible,
* we won't be redrawing.
*/
if (!(pcvr->pos.flags & SWP_NOREDRAW)) {
if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE ||
!_FChildVisible(pwnd)) {
pcvr->pos.flags |= SWP_NOREDRAW;
}
}
/*
* BACKWARD COMPATIBILITY HACK
*
* In 3.0, if a window was moving but not sizing, we'd send the
* WM_NCCALCSIZE message anyhow. Lotus Notes 2.1 depends on this
* in order to move its "navigation bar" when the main window moves.
*/
if (!(pcvr->pos.flags & SWP_NOMOVE) &&
!TestWF(pwnd, WFWIN31COMPAT) &&
(GetAppCompatFlags(NULL) & GACF_NCCALCSIZEONMOVE)) {
fForceNCCalcSize = TRUE;
}
/*
* If the window rect is sizing, or if the frame has changed,
* send the WM_NCCALCSIZE message and deal with valid areas.
*/
if (((pcvr->pos.flags & (SWP_NOSIZE | SWP_FRAMECHANGED)) != SWP_NOSIZE) ||
fForceNCCalcSize) {
WINDOWPOS pos;
/*
* check for full screen main app window
*/
if (!TestWF(pwnd, WFCHILD) && !TestWF(pwnd, WEFTOOLWINDOW)) {
xxxCheckFullScreen(pwnd, (LPRECT)&pcvr->pos.x);
}
/*
* Set up NCCALCSIZE message parameters (in parent coords)
* wParam = fClientOnly = TRUE
* lParam = &params
*/
pos = pcvr->pos; // Make a local stack copy
params.lppos = &pos;
/*
* params.rgrc[0] = rcWindowNew = New window rectangle
* params.rgrc[1] = rcWindowOld = Old window rectangle
* params.rgrc[2] = rcClientOld = Old client rectangle
*/
#define rcWindowNew params.rgrc[0]
#define rcWindowOld params.rgrc[1]
#define rcClientOld params.rgrc[2]
/*
* Set up rcWindowNew in parent relative coordinates
*/
rcWindowNew.left = pcvr->pos.x;
rcWindowNew.right = rcWindowNew.left + pcvr->pos.cx;
rcWindowNew.top = pcvr->pos.y;
rcWindowNew.bottom = rcWindowNew.top + pcvr->pos.cy;
/*
* Set up rcWindowOld in parent relative coordinates
*/
rcWindowOld.left = pwnd->rcWindow.left - pwndParent->rcClient.left;
rcWindowOld.right = pwnd->rcWindow.right - pwndParent->rcClient.left;
rcWindowOld.top = pwnd->rcWindow.top - pwndParent->rcClient.top;
rcWindowOld.bottom = pwnd->rcWindow.bottom - pwndParent->rcClient.top;
/*
* Set up rcClientOld in parent relative coordinates
*/
rcClientOld.left = pwnd->rcClient.left - pwndParent->rcClient.left;
rcClientOld.right = pwnd->rcClient.right - pwndParent->rcClient.left;
rcClientOld.top = pwnd->rcClient.top - pwndParent->rcClient.top;
rcClientOld.bottom = pwnd->rcClient.bottom - pwndParent->rcClient.top;
/*
* Keep around a copy of the old client position
*/
xClientOld = rcClientOld.left;
cxClientOld = rcClientOld.right - rcClientOld.left;
yClientOld = rcClientOld.top;
cyClientOld = rcClientOld.bottom - rcClientOld.top;
cmd = (UINT)xxxSendMessage(pwnd, WM_NCCALCSIZE, TRUE, (LONG)&params);
if (!IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
ThreadUnlock(&tlpwnd);
ThreadUnlock(&tlpwndParent);
return FALSE;
}
/*
* Upon return from NCCALCSIZE:
*
* params.rgrc[0] = rcClientNew = New client rect
* params.rgrc[1] = rcValidDst = Destination valid rectangle
* params.rgrc[2] = rcValidSrc = Source valid rectangle
*/
#undef rcWindowNew
#undef rcWindowOld
#undef rcClientOld
#define rcClientNew params.rgrc[0]
#define rcValidDst params.rgrc[1]
#define rcValidSrc params.rgrc[2]
/*
* Calculate the distance the window contents are
* moving. If 0 or an invalid value was returned
* from the WM_NCCALCSIZE message, assume the
* entire client area is valid and top-left aligned.
*/
if (cmd < WVR_MINVALID || cmd > WVR_MAXVALID) {
/*
* We don't need to copy rcValidSrc to rcClientOld,
* because it's already stored in rgrc[2].
*
* rcValidSrc = rcClientOld
*/
rcValidDst = rcClientNew;
cmd = WVR_ALIGNTOP | WVR_ALIGNLEFT;
}
/*
* Calculate the distance we'll be shifting bits...
*/
pcvr->dxBlt = rcValidDst.left - rcValidSrc.left;
pcvr->dyBlt = rcValidDst.top - rcValidSrc.top;
/*
* Calculate new client rect size and position
*/
pcvr->xClientNew = rcClientNew.left;
pcvr->yClientNew = rcClientNew.top;
pcvr->cxClientNew = rcClientNew.right - rcClientNew.left;
pcvr->cyClientNew = rcClientNew.bottom - rcClientNew.top;
/*
* Figure out whether the client rectangle is moving or sizing,
* and diddle the appropriate bit if not.
*/
if (xClientOld != rcClientNew.left || yClientOld != rcClientNew.top)
pcvr->pos.flags &= ~SWP_NOCLIENTMOVE;
if (cxClientOld != pcvr->cxClientNew || cyClientOld != pcvr->cyClientNew)
pcvr->pos.flags &= ~SWP_NOCLIENTSIZE;
/*
* If the caller doesn't want us to save any bits, then don't.
*/
if (pcvr->pos.flags & SWP_NOCOPYBITS) {
AllInvalid:
/*
* The entire window is invalid: Set the blt rectangle
* to empty, to ensure nothing gets bltted.
*/
pcvr->rcBlt.left =
pcvr->rcBlt.top =
pcvr->rcBlt.right =
pcvr->rcBlt.bottom = 0;
ThreadUnlock(&tlpwnd);
continue;
}
/*
* If this is a transparent window, be sure to invalidate
* everything, because only some of the window's bits are
* blittable.
*/
if (TestWF(pwnd, WEFTRANSPARENT))
goto AllInvalid;
/*
* If both client and window did not change size, the frame didn't
* change, and the blt rectangle moved the same distance as the
* rectangle, then the entire window area is valid.
*/
if (((pcvr->pos.flags &
(SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_FRAMECHANGED))
== (SWP_NOSIZE | SWP_NOCLIENTSIZE)) &&
pcvr->dxBlt == (pcvr->pos.x - xWindowOld) &&
pcvr->dyBlt == (pcvr->pos.y - yWindowOld)) {
goto AllValid;
}
/*
* Now compute the valid blt rectangle.
*
* Check for horz or vert client size changes
*
* NOTE: Assumes WVR_REDRAW == WVR_HREDRAW | WVR_VREDRAW
*/
if (cxClientOld != pcvr->cxClientNew) {
if ((cmd & WVR_HREDRAW) || TestCF(pwnd, CFHREDRAW))
goto AllInvalid;
}
if (cyClientOld != pcvr->cyClientNew) {
if ((cmd & WVR_VREDRAW) || TestCF(pwnd, CFVREDRAW))
goto AllInvalid;
}
cxSrc = rcValidSrc.right - rcValidSrc.left;
cySrc = rcValidSrc.bottom - rcValidSrc.top;
cxDst = rcValidDst.right - rcValidDst.left;
cyDst = rcValidDst.bottom - rcValidDst.top;
if (cmd & WVR_ALIGNRIGHT)
rcValidDst.left += (cxDst - cxSrc);
if (cmd & WVR_ALIGNBOTTOM)
rcValidDst.top += (cyDst - cySrc);
/*
* Superimpose the source on the destination, and intersect
* the rectangles. This is done by looking at the
* extent of the rectangles, and pinning as appropriate.
*/
if (cxSrc < cxDst)
rcValidDst.right = rcValidDst.left + cxSrc;
if (cySrc < cyDst)
rcValidDst.bottom = rcValidDst.top + cySrc;
/*
* Finally map the blt rectangle to screen coordinates.
*/
pcvr->rcBlt.left = rcValidDst.left + pwndParent->rcClient.left;
pcvr->rcBlt.right = rcValidDst.right + pwndParent->rcClient.left;
pcvr->rcBlt.top = rcValidDst.top + pwndParent->rcClient.top;
pcvr->rcBlt.bottom = rcValidDst.bottom + pwndParent->rcClient.top;
} else { // if !SWP_NOSIZE or SWP_FRAMECHANGED
AllValid:
/*
* No client size change: Blt the entire window,
* including the frame. Offset everything by
* the distance the window rect changed.
*/
if (!(pcvr->pos.flags & SWP_NOCOPYBITS)) {
pcvr->rcBlt.left = pcvr->pos.x + pwndParent->rcClient.left;
pcvr->rcBlt.right = pcvr->rcBlt.left + pcvr->pos.cx;
pcvr->rcBlt.top = pcvr->pos.y + pwndParent->rcClient.top;
pcvr->rcBlt.bottom = pcvr->rcBlt.top + pcvr->pos.cy;
}
/*
* Offset everything by the distance the window moved.
*/
pcvr->dxBlt = pcvr->pos.x - xWindowOld;
pcvr->dyBlt = pcvr->pos.y - yWindowOld;
/*
* If we're moving, we need to set up the client.
*/
if (!(pcvr->pos.flags & SWP_NOMOVE)) {
pcvr->pos.flags &= ~SWP_NOCLIENTMOVE;
pcvr->xClientNew = pwnd->rcClient.left - pwndParent->rcClient.left + pcvr->dxBlt;
pcvr->yClientNew = pwnd->rcClient.top - pwndParent->rcClient.top + pcvr->dyBlt;
pcvr->cxClientNew = pwnd->rcClient.right - pwnd->rcClient.left;
pcvr->cyClientNew = pwnd->rcClient.bottom - pwnd->rcClient.top;
}
}
ThreadUnlock(&tlpwnd);
} // for (... pcvr ...)
ThreadUnlock(&tlpwndParent);
*phwndNewActive = hwndNewActive;
return TRUE;
}
/***************************************************************************\
* GetLastTopMostWindow
*
* Returns the last topmost window in the window list. Returns NULL if no
* topmost windows. Used so that we can fill in the pwndInsertAfter field
* in various SWP calls.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
PWND GetLastTopMostWindow(VOID)
{
PWND pwndT;
PDESKTOP pdesk = PtiCurrent()->rpdesk;
if (pdesk == NULL)
return NULL;
pwndT = pdesk->pDeskInfo->spwnd->spwndChild;
if (!pwndT || !TestWF(pwndT, WEFTOPMOST))
return NULL;
while (pwndT->spwndNext) {
if (!TestWF(pwndT->spwndNext, WEFTOPMOST))
break;
pwndT = pwndT->spwndNext;
}
return pwndT;
}
/***************************************************************************\
* SetWindowPos (API)
*
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL xxxSetWindowPos(
PWND pwnd,
PWND pwndInsertAfter,
int x,
int y,
int cx,
int cy,
UINT flags)
{
PSMWP psmwp;
BOOL fInval = FALSE;
#ifdef DEBUG
CheckLock(pwnd);
switch((DWORD)pwndInsertAfter) {
case 0x0000FFFF:
case (DWORD)HWND_TOPMOST:
case (DWORD)HWND_NOTOPMOST:
case (DWORD)HWND_TOP:
case (DWORD)HWND_BOTTOM:
break;
default:
CheckLock(pwndInsertAfter);
break;
}
#endif
/*
* BACKWARD COMPATIBILITY HACKS
*
* Hack 1: For Win 3.0 and below, SetWindowPos() must ignore the
* move and size flags if SWP_SHOWWINDOW or SWP_HIDEWINDOW
* is specified. KnowledgePro is one application that depends on
* this behavior for the positioning of its MDI icons.
*
* Hack 2: In 3.0, if SetWindowPos() is called with SWP_SHOWWINDOW
* and the window is already visible, then the window was
* completely invalidated anyway. So, we do that here too.
*
* NOTE: The placement of the invalidation AFTER the EndDeferWindowPos()
* call means that if the guy is Z-ordering and showing a 3.0 window,
* it may flash, because EndDefer calls DoSyncPaint, and we invalidate
* again after that. Could be fixed with some major hackery in EndDefer,
* and it's probably not worth the trouble.
*/
if (flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW)) {
if (!TestWF(pwnd, WFWIN31COMPAT)) {
flags |= SWP_NOMOVE | SWP_NOSIZE;
if ((flags & SWP_SHOWWINDOW) && TestWF(pwnd, WFVISIBLE))
fInval = TRUE;
}
}
if (!(psmwp = _BeginDeferWindowPos(1)) ||
!(psmwp = _DeferWindowPos(psmwp,
pwnd,
pwndInsertAfter,
x,
y,
cx,
cy,
flags))) {
return FALSE;
}
if (xxxEndDeferWindowPosEx(psmwp, flags & SWP_ASYNCWINDOWPOS)) {
if (fInval) {
xxxRedrawWindow(
pwnd,
NULL,
NULL,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
}
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* xxxSwpActivate
*
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL xxxSwpActivate(
PWND pwndNewActive)
{
PTHREADINFO pti;
CheckLock(pwndNewActive);
if (pwndNewActive == NULL)
return FALSE;
pti = PtiCurrent();
if (TestwndChild(pwndNewActive)) {
xxxSendMessage(pwndNewActive, WM_CHILDACTIVATE, 0, 0L);
} else if (pti->pq->spwndActive != pwndNewActive) {
/*
* Remember if this window wants to be active. We are either setting
* our own window active (most likely), or setting a window of
* another thread active on purpose. If so that means this thread is
* controlling this window and will probably want to set itself
* active and foreground really soon (for example, a setup
* program doing dde to progman). Allow this thread and the target
* thread to do forground activates.
*/
if ((pti->pq == gpqForeground) && (pti != GETPTI(pwndNewActive))) {
/*
* Allow foreground activate on the source and dest.
*/
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
GETPTI(pwndNewActive)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
}
if (!xxxActivateWindow(pwndNewActive, AW_USE))
return FALSE;
/*
* HACK ALERT: We set these bits to prevent
* the frames from redrawing themselves in
* the later call to DoSyncPaint().
*
* Prevent these captions from being repainted during
* the DoSyncPaint(). (bobgu 6/10/87)
*/
if (pti->pq->spwndActive != NULL)
SetWF(pti->pq->spwndActive, WFNONCPAINT);
if (pti->pq->spwndActivePrev != NULL)
SetWF(pti->pq->spwndActivePrev, WFNONCPAINT);
return TRUE; // Indicate that we diddled these bits
}
return FALSE;
}
/***************************************************************************\
* xxxSendChangedMsgs
*
* Send WM_WINDOWPOSCHANGED messages as needed
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID xxxSendChangedMsgs(
PSMWP psmwp)
{
PWND pwnd;
PCVR pcvr;
int ccvr;
TL tlpwnd;
/*
* Send all the messages that need to be sent...
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (pcvr->pos.hwnd == NULL)
continue;
/*
* If the window's state didn't change, don't send the message.
*/
if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE)
continue;
if ((pwnd = RevalidateHwnd(pcvr->pos.hwnd)) == NULL) {
RIPMSG0(RIP_WARNING, "xxxSendChangedMsgs: window went away in middle");
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
pcvr->pos.hwnd = NULL;
continue;
}
if (!IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
pcvr->pos.hwnd = NULL;
continue;
}
/*
* Send the WM_WINDOWPOSCHANGED message...
*
* Make a frame copy of the WINDOWPOS, because the pcvr
* info may get reused if SetWindowPos()
* is called by the message handler: see the comments in
* AllocSmwp().
*
* WM_SIZE, WM_MOVE and WM_SHOW messages are sent by the
* DefWindowProc() WM_WINDOWPOSCHANGED message processing.
*
* Note: It's okay to destroy the window while processing this
* message, since this is the last call made by the window manager
* with the window handle before returning from SetWindowPos().
* This also means we don't have to revalidate the pwnd.
*/
ThreadLockAlways(pwnd, &tlpwnd);
xxxSendMessage(pwnd, WM_WINDOWPOSCHANGED, 0, (LONG)&pcvr->pos);
ThreadUnlock(&tlpwnd);
} // for (... pcvr ...)
}
/***************************************************************************\
* AsyncWindowPos
*
* This functions pulls from the passed-in SMWP all windows not owned by the
* current thread and passes them off to their owners to be handled. This
* eliminates synchronization where thread B won't get a chance to paint
* until thread A has completed painting (or at least returned from handling
* painting-related messages). Such synchronizations are bad because they
* can cause threads of unrelated process to hang each other.
*
* History:
* 09-10-91 darrinm Created.
\***************************************************************************/
BOOL AsyncWindowPos(
PSMWP psmwp)
{
BOOL fFinished;
PCVR pcvrFirst;
PCVR pcvr;
PCVR pcvrT;
int ccvrRemaining;
int ccvr;
int chwnd;
PTHREADINFO ptiT;
PTHREADINFO ptiCurrent;
PSMWP psmwpNew;
pcvrFirst = psmwp->acvr;
ccvrRemaining = psmwp->ccvr;
ptiCurrent = PtiCurrent();
while (TRUE) {
fFinished = TRUE;
/*
* Loop through all windows in the SMWP list searching for windows
* owned by other threads. Return if none are found.
*/
for (; ccvrRemaining != 0; pcvrFirst++, ccvrRemaining--) {
if (pcvrFirst->pos.hwnd == NULL)
continue;
ptiT = pcvrFirst->pti;
if (ptiT->pq != ptiCurrent->pq) {
fFinished = FALSE;
break;
}
}
if (fFinished)
return TRUE;
/*
* We've found a window of another thread. Count how many other
* windows in the list are owned by the same thread so we can
* allocate a CVR array for them.
*/
chwnd = 0;
for (pcvr = pcvrFirst, ccvr = ccvrRemaining; --ccvr >= 0; pcvr++) {
if (pcvr->pos.hwnd == NULL)
continue;
if (pcvr->pti->pq == ptiT->pq)
chwnd++;
}
/*
* Allocate temp SMWP/CVR structure to be passed to the other thread.
*/
psmwpNew = (PSMWP)UserAllocPool(sizeof(SMWP) + (sizeof(CVR) * chwnd),
TAG_SWP);
/*
* Even if we can't allocate memory to pass the SMWP to another
* thread we still need to remove its windows from the current list.
*/
if (psmwpNew == NULL) {
for (pcvr = pcvrFirst; chwnd != 0; pcvr++) {
if (pcvr->pti->pq == ptiT->pq) {
pcvr->pos.hwnd = NULL;
chwnd--;
}
}
continue;
}
psmwpNew->ccvr = chwnd;
psmwpNew->acvr = (PCVR)((PBYTE)psmwpNew + sizeof(SMWP));
for (pcvr = pcvrFirst, pcvrT = psmwpNew->acvr; chwnd != 0; pcvr++) {
if (pcvr->pos.hwnd == NULL)
continue;
/*
* Copy the appropriate CVR structs into our temp array.
*/
if (pcvr->pti->pq == ptiT->pq) {
*pcvrT++ = *pcvr;
chwnd--;
/*
* Remove this window from the list of windows to be handled
* by the current thread.
*/
pcvr->pos.hwnd = NULL;
}
}
/*
* This lets the other thread know it needs to do some windowposing.
* The other thread is responsible for freeing the temp SMWP/CVR array.
*/
PostEventMessage(ptiT,
ptiT->pq,
QEVENT_SETWINDOWPOS, NULL, 0,
(DWORD)psmwpNew,
(LONG)ptiT);
}
return TRUE;
}
/***************************************************************************\
* xxxProcessSetWindowPosEvent
*
* This function is called from xxxProcessEvent (QUEUE.C) to respond to
* posted QEVENT_SETWINDOWPOS events which originate at the AsyncWindowPos
* function above.
*
* History:
* 10-Sep-1991 DarrinM Created.
\***************************************************************************/
VOID xxxProcessSetWindowPosEvent(
PSMWP psmwpT)
{
PSMWP psmwp;
/*
* Create a bonafide SMWP/CVR array that xxxEndDeferWindowPos can use
* and later free.
*/
if ((psmwp = _BeginDeferWindowPos(psmwpT->ccvr)) == NULL) {
UserFreePool(psmwpT);
return;
}
/*
* Copy the contents of the temp SMWP/CVR array into the real one.
*/
RtlCopyMemory(psmwp->acvr, psmwpT->acvr, sizeof(CVR) * psmwpT->ccvr);
psmwp->ccvr = psmwpT->ccvr;
/*
* Free the temp SMWP/CVR array.
*/
UserFreePool(psmwpT);
/*
* Complete the MultWindowPos operation now that we're on the correct
* context.
*/
xxxEndDeferWindowPosEx(psmwp, FALSE);
}
#define SWP_BOZO ( SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE )
/***************************************************************************\
* ChangeStates
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID ChangeStates(
PWND pwndParent,
PSMWP psmwp,
PWNDLIST *pplistCreate,
PWNDLIST *pplistDestroy,
PWNDLIST *pplistTray)
{
int ccvr;
PCVR pcvr;
PWND pwnd;
/*
* Now change the window states
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (pcvr->pos.hwnd == NULL)
continue;
pwnd = RevalidateHwnd(pcvr->pos.hwnd);
if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
RIPMSG0(RIP_WARNING, "ChangeStates: Window went away in middle");
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
pcvr->pos.hwnd = NULL;
}
#ifdef DEBUG
if (TestWF(pwnd, WFTOGGLETOPMOST) && (pcvr->pos.flags & SWP_NOZORDER))
RIPMSG0(RIP_WARNING, "ChangeState: WFTOGGLETOPMOST should not be set");
// UserAssert(!(TestWF(pwnd, WFTOGGLETOPMOST) && (pcvr->pos.flags & SWP_NOZORDER)));
#endif
/*
* Check to se if there is any state to change. If not, just
* continue.
*/
if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE) {
pcvr->pos.flags |= SWP_NOREDRAW;
continue;
}
/*
* Change the window region if needed
*
* Before we do anything, check to see if we're only Z-ordering.
* If so, then check to see if we're already in the right place,
* and if so, clear the ZORDER flag.
*
* We have to make this test in the state-change loop if previous
* windows in the WINDOWPOS list were Z-ordered, since the test depends
* on any ordering that may have happened previously.
*
* We don't bother to do this redundancy check if there are
* other bits set, because the amount of time saved in that
* case is about as much as the amount of time it takes to
* test for redundancy.
*/
if (((pcvr->pos.flags & SWP_CHANGEMASK) ==
(SWP_NOCHANGE & ~SWP_NOZORDER))) {
/*
* If the window's Z order won't be changing, then
* we can clear the ZORDER bit and set NOREDRAW.
*/
if ((!TestWF(pwnd, WFTOGGLETOPMOST)) && ValidateZorder(pcvr)) {
/*
* The window's already in the right place:
* Set SWP_NOZORDER bit, set SWP_NOREDRAW,
* and destroy the visrgn that we created earlier.
*/
pcvr->pos.flags |= SWP_NOZORDER | SWP_NOREDRAW;
if (pcvr->hrgnVisOld) {
GreDeleteObject(pcvr->hrgnVisOld);
pcvr->hrgnVisOld = NULL;
}
continue;
}
}
/*
* Change the window state, as appropriate...
*/
if ((pcvr->pos.flags &
(SWP_NOMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)) !=
(SWP_NOMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)) {
PCARET pcaret = &PtiCurrent()->pq->caret;
/*
* Set up the new window and client rectangles.
*/
pwnd->rcWindow.left = pwndParent->rcClient.left + pcvr->pos.x;
pwnd->rcWindow.right = pwnd->rcWindow.left + pcvr->pos.cx;
pwnd->rcWindow.top = pwndParent->rcClient.top + pcvr->pos.y;
pwnd->rcWindow.bottom = pwnd->rcWindow.top + pcvr->pos.cy;
/*
* If the client moved relative to its parent,
* offset the caret by the amount that rcBlt moved
* relative to the client rect.
*/
if (pwnd == pcaret->spwnd) {
/*
* Calculate the distance the contents of the client area
* is moving, in client-relative coordinates.
*
* Calculates dBlt + (old position - new position)
*/
int dx = pcvr->dxBlt +
(pwnd->rcClient.left - pwndParent->rcClient.left) -
pcvr->xClientNew;
int dy = pcvr->dyBlt +
(pwnd->rcClient.top - pwndParent->rcClient.top) -
pcvr->yClientNew;
if ((dx | dy) != 0) {
pcaret->x += dx;
pcaret->y += dy;
}
}
/*
* Set up the new client rect
* coordinates provided.
*/
pwnd->rcClient.left = pcvr->xClientNew + pwndParent->rcClient.left;
pwnd->rcClient.right = pwnd->rcClient.left + pcvr->cxClientNew;
pwnd->rcClient.top = pcvr->yClientNew + pwndParent->rcClient.top;
pwnd->rcClient.bottom = pwnd->rcClient.top + pcvr->cyClientNew;
/*
* Offset the absolute positions of the window's update region,
* and the position and update regions of its children.
*/
if ((pcvr->dxBlt | pcvr->dyBlt) != 0) {
/*
* Change position of window region, if it has one
*/
if (pwnd->hrgnClip != NULL)
GreOffsetRgn(pwnd->hrgnClip, pcvr->dxBlt, pcvr->dyBlt);
if (pwnd->hrgnUpdate > MAXREGION)
GreOffsetRgn(pwnd->hrgnUpdate, pcvr->dxBlt, pcvr->dyBlt);
OffsetChildren(pwnd, pcvr->dxBlt, pcvr->dyBlt, NULL);
}
}
/*
* make sure window region is aligned with the window rect
*/
/*
* Change the Z order if the flag is set
* Change the Z order if the flag is set. Revalidate
* hwndInsertAfter to make sure that it is still valid
* and
* make sure window region is aligned with the window rect
*/
if (!(pcvr->pos.flags & SWP_NOZORDER)) {
if (ValidateWindowPos(pcvr)) {
UnlinkWindow(pwnd, &pwndParent->spwndChild);
LinkWindow(pwnd,
PWInsertAfter(pcvr->pos.hwndInsertAfter),
&pwndParent->spwndChild);
} else {
pcvr->pos.flags | SWP_NOZORDER;
}
}
/*
* HACK ALERT MERGE
*
* ValidateZOrder() depends on rational, consistent setting of the
* WEFTOPMOST bit in order for it to work properly. What this means
* is that we can't set or clear these bits ahead of time based on
* where the window is moving to: instead we have to change the bit
* after we've moved it. Enter the WFTOGGLETOPMOST bit: that bit
* is set in ZOrderByOwner() based on what the topmost bit will
* eventually be set to. To maintain a consistent state, we make
* any changes AFTER the window has been Z-ordered.
*/
if (TestWF(pwnd, WFTOGGLETOPMOST)) {
PBYTE pb;
ClrWF(pwnd, WFTOGGLETOPMOST);
pb = ((BYTE *)&pwnd->state);
pb[HIBYTE(WEFTOPMOST)] ^= LOBYTE(WEFTOPMOST);
}
/*
* Handle SWP_HIDEWINDOW and SWP_SHOWWINDOW, by clearing or setting
* the WS_VISIBLE bit.
*/
if (pcvr->pos.flags & SWP_SHOWWINDOW) {
/*
* Window is showing. If this app is still in startup mode,
* (still starting), give the the app starting cursor 5 more
* seconds.
*/
if (GETPTI(pwnd)->ppi->W32PF_Flags & W32PF_APPSTARTING)
CalcStartCursorHide((PW32PROCESS)GETPTI(pwnd)->ppi, 5000);
/*
* Set the WS_VISIBLE bit.
*/
SetVisible(pwnd, SV_SET);
if (IsTrayWindow(pwnd)) {
HWND *pahwnd;
if (pplistCreate->pahwnd == NULL) {
pahwnd = UserAllocPool(sizeof(HWND), TAG_SWP);
} else {
DWORD dwSize = pplistCreate->cnt * sizeof(HWND);
pahwnd = UserReAllocPool(pplistCreate->pahwnd,
dwSize, dwSize + sizeof(HWND),
TAG_SWP);
}
if (pahwnd) {
pahwnd[pplistCreate->cnt++] = HWq(pwnd);
pplistCreate->pahwnd = pahwnd;
}
/*
* This Chicago code is replaced by the preceding code,
* which exits the critical section only after the Gre
* lock is removed. Fritz
*
* xxxCallHook(HSHELL_WINDOWCREATED,
* (WPARAM)HWq(pwnd),
* (LPARAM)0,
* WH_SHELL);
*
* PostShellHookMessages(HSHELL_WINDOWCREATED, pwnd);
*/
if (TestWF(pwnd, WFFRAMEON)) {
HWND *pahwnd;
if (pplistTray->pahwnd == NULL) {
pahwnd = UserAllocPool(sizeof(HWND), TAG_SWP);
} else {
DWORD dwSize = pplistCreate->cnt * sizeof(HWND);
pahwnd = UserReAllocPool(pplistTray->pahwnd,
dwSize, dwSize + sizeof(HWND),
TAG_SWP);
}
if (pahwnd) {
pahwnd[pplistTray->cnt++] = HWq(pwnd);
pplistTray->pahwnd = pahwnd;
}
/*
* This Chicago code is replaced by the preceding code,
* which exits the critical section only after the Gre
* lock is removed. Fritz
*
* xxxSetTrayWindow(pwnd);
*/
}
}
/*
* If we're redrawing, create an SPB for this window if
* needed.
*/
if (!(pcvr->pos.flags & SWP_NOREDRAW)) {
/*
* ONLY create an SPB if this window happens to be
* on TOP of all others. NOTE: We could optimize this by
* passing in the new vis rgn to CreateSpb() so that the
* non-visible part of the window is automatically
* invalid in the SPB.
*/
if (TestCF(pwnd, CFSAVEBITS)) {
/*
* If this window is the topmost VISIBLE window,
* then we can create an SPB.
*/
PWND pwndT;
RECT rcT;
for (pwndT = pwnd->spwndParent->spwndChild ;
pwndT;
pwndT = pwndT->spwndNext) {
if (pwndT == pwnd) {
CreateSpb(pwnd, FALSE, gpDispInfo->hdcScreen);
break;
}
if (TestWF(pwndT, WFVISIBLE)) {
/*
* Does this window intersect the SAVEBITS
* window at all? If so, bail out.
*/
if (IntersectRect(&rcT,
&pwnd->rcWindow,
&pwndT->rcWindow)) {
break;
}
}
}
}
}
} else if (pcvr->pos.flags & SWP_HIDEWINDOW) {
/*
* for idiots like MS-Access 2.0 who SetWindowPos( SWP_BOZO
* and blow away themselves on the shell, then lets
* just ignore their plea to be removed from the tray
*/
if (((pcvr->pos.flags & SWP_BOZO ) != SWP_BOZO) &&
IsTrayWindow(pwnd)) {
HWND *pahwnd;
if (pplistDestroy->pahwnd == NULL) {
pahwnd = UserAllocPool(sizeof(HWND), TAG_SWP);
} else {
DWORD dwSize = pplistDestroy->cnt * sizeof(HWND);
pahwnd = UserReAllocPool(pplistDestroy->pahwnd,
dwSize, dwSize + sizeof(HWND),
TAG_SWP);
}
if (pahwnd) {
pahwnd[pplistDestroy->cnt++] = HWq(pwnd);
pplistDestroy->pahwnd = pahwnd;
}
/*
* This Chicago code is replaced by the preceding code,
* which exits the critical section only after the Gre
* lock is removed. Fritz
*
* xxxCallHook(HSHELL_WINDOWDESTROYED,
* (WPARAM)HWq(pwnd),
* (LPARAM)0,
* WH_SHELL);
*
* PostShellHookMessages(HSHELL_WINDOWDESTROYED, pwnd);
*/
}
/*
* Clear the WS_VISIBLE bit.
*/
SetVisible(pwnd, SV_UNSET | SV_CLRFTRUEVIS);
}
/*
* BACKWARD COMPATIBILITY HACK
*
* Under 3.0, window frames were always redrawn, even if
* SWP_NOREDRAW was specified. If we've gotten this far
* and we're visible, and SWP_NOREDRAW was specified, set
* the WFSENDNCPAINT bit.
*
* Apps such as ABC Flowcharter and 123W assume this.
* Typical offending code is MoveWindow(pwnd, ..., FALSE);
* followed by InvalidateRect(pwnd, NULL, TRUE);
*/
#if 0 //CHRISWIL: old code
if (!TestWF(pwnd, WFWIN31COMPAT) &&
TestWF(pwnd, WFVISIBLE) &&
(pcvr->pos.flags & SWP_NOREDRAW)) {
SetWF(pwnd, WFSENDNCPAINT);
}
#else
if (TestWF(pwnd, WFVISIBLE)) {
if ((pcvr->pos.flags & SWP_STATECHANGE) ||
(!TestWF(pwnd, WFWIN31COMPAT) && (pcvr->pos.flags & SWP_NOREDRAW))) {
SetWF(pwnd, WFSENDNCPAINT);
}
}
#endif
/*
* If this window has a clipping region set it now
*/
if (pcvr->hrgnClip != NULL)
SelectWindowRgn(pwnd, pcvr->hrgnClip);
}
}
/***************************************************************************\
* SwpCalcVisRgn
*
* This routine calculates a non-clipchildren visrgn for pwnd into hrgn.
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL SwpCalcVisRgn(
PWND pwnd,
HRGN hrgn)
{
/*
* If this window is invisible, then
* the visrgn will be empty, so return FALSE.
*/
if (!TestWF(pwnd, WFVISIBLE))
return FALSE;
/*
* Otherwise do it the hard way...
*/
return CalcVisRgn(&hrgn,
pwnd,
pwnd,
(TestWF(pwnd, WFCLIPSIBLINGS) ?
(DCX_CLIPSIBLINGS | DCX_WINDOW) : (DCX_WINDOW)));
}
/***************************************************************************\
* CombineOldNewVis
*
* ORs or DIFFs hrgnOldVis and hrgnNewVis, depending on crgn, and the
* RE_* bits of fsRgnEmpty. Basically, this routine handles the optimization
* where if either region is empty, the other can be copied. Returns FALSE
* if the result is empty.
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL CombineOldNewVis(
HRGN hrgn,
HRGN hrgnVisOld,
HRGN hrgnVisNew,
UINT crgn,
UINT fsRgnEmpty)
{
switch (fsRgnEmpty & (RE_VISOLD | RE_VISNEW)) {
case RE_VISOLD:
/*
* If we're calculating old - new and old is empty, then result is
* empty. Otherwise, result is new.
*/
if (crgn == RGN_DIFF)
return FALSE;
CopyRgn(hrgn, hrgnVisNew);
break;
case RE_VISNEW:
/*
* New is empty: result will be the old.
*/
CopyRgn(hrgn, hrgnVisOld);
break;
case RE_VISNEW | RE_VISOLD:
/*
* Both empty: so's the result.
*/
return FALSE;
case 0:
/*
* Neither are empty: do the real combine.
*/
switch (GreCombineRgn(hrgn, hrgnVisOld, hrgnVisNew, crgn)) {
case NULLREGION:
case ERROR:
return FALSE;
default:
break;
}
break;
}
return TRUE;
}
/***************************************************************************\
* BltValidInit
*
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
int BltValidInit(
PSMWP psmwp)
{
int ccvr;
int cIter = 0;
PCVR pcvr;
PWND pwnd;
BOOL fChangeState = FALSE;
/*
* Before we change any window state, calculate the old visrgn
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
UINT flags = pcvr->pos.flags;
/*
* Make sure this is initialized to NULL; we may be sticking something
* in it, and we want to know later if we need to free that thing.
*/
pcvr->hrgnVisOld = NULL;
if (pcvr->pos.hwnd == NULL)
continue;
pwnd = RevalidateHwnd(pcvr->pos.hwnd);
if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
pcvr->pos.hwnd = NULL;
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
continue;
}
/*
* Before we change any window's state, ensure that any SPBs
* over the window's old location are invalidated if necessary.
* This must be done because no WM_PAINT messages will be
* sent to anyone for the covered area if the area is obscured
* by other windows.
*/
if (AnySpbs() && !(flags & SWP_NOREDRAW))
SpbCheckRect(pwnd, &pwnd->rcWindow, DCX_WINDOW);
/*
* Count the number of passes through the loop
*/
cIter++;
/*
* Remember if any SWPs need their state changed.
*/
if ((flags & SWP_CHANGEMASK) != SWP_NOCHANGE)
fChangeState = TRUE;
/*
* If we're not redrawing, no need to calculate visrgn
*/
if (pcvr->pos.flags & SWP_NOREDRAW)
continue;
pcvr->fsRE = 0;
pcvr->hrgnVisOld = GreCreateRectRgn(0, 0, 0, 0);
if (pcvr->hrgnVisOld == NULL ||
!SwpCalcVisRgn(pwnd, pcvr->hrgnVisOld)) {
pcvr->fsRE = RE_VISOLD;
}
}
return (fChangeState ? cIter : 0);
}
/***************************************************************************\
* BltValidBits
*
* NOTE: Although BltValidBits calls 'xxxInternalInvalidate' it does not
* specify any of the flags that will cause immediate updating. This means
* that it does not actually leave the critsect and therefore is not an 'xxx'
* routine and doesn't have to bother with revalidation.
*
* This is the routine that blts the windows around on the screen, taking
* into account SPBs.
*
* Here is the basic algebra going on here:
*
* ASSUMES: - rcBlt is aligned to the DESTINATION
* - offset() offsets from source to destination
*
* 1. hrgnSrc = offset(rcBlt) & hrgnVisOld
*
* Source region is the blt rectangle aligned with the old visrgn,
* intersected with the old visrgn.
*
* 2. hrgnDst = rcBlt & hrgnVisNew
*
* Dest region is the blt rectangle intersected with the new visrgn.
*
* 3. hrgnValid = offset(hrgnSrc) & hrgnDst
*
* Valid area is the intersection of the destination with the source
* superimposed on the destination.
*
* 4. hrgnValid -= hrgnValidSum
*
* This step takes into account the possibility that another window's
* valid bits were bltted on top of this windows valid bits. So, as we
* blt a window's bits, we accumulate where it went, and subtract it
* from subsequent window's valid area.
*
* 5. hrgnInvalid = (hrgnSrc | hrgnDst) - hrgnValid
*
* 6. hrgnInvalid += RestoreSpb(hrgnInvalid) (sort of)
*
* This is the wild part, because of the grungy way that the device
* driver SaveBits() routine works. We call RestoreSpb() with
* a copy of hrgnInvalid. If the SPB valid region doesn't intersect
* hrgnInvalid, RestoreSpb() does nothing. But if it DOES intersect,
* it blts down the ENTIRE saved SPB bitmap, which may include area
* of the old window position that IS NOT part of hrgnValid!
*
* To correct for this, hrgnValid is adjusted by subtracting off
* the hrgnInvalid computed by RestoreSpb, if it modified it.
*
* 7. hrgnInvalidSum |= hrgnInvalid
*
* We save up the sum of all the invalid areas, and invalidate the
* whole schmear in one fell swoop at the end.
*
* 8. hrgnValidSum |= hrgnValid
*
* We keep track of the valid areas so far, which are subtracted
* in step 4.
*
* The actual steps occur in a slightly different order than above, and
* there are numerous optimizations that are taken advantage of (the
* most important having to do with hiding and showing, and complete
* SPB restoration).
*
* Returns TRUE if some drawing was done, FALSE otherwise.
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL BltValidBits(
PSMWP psmwp,
PWNDLIST *pplistCreate,
PWNDLIST *pplistDestroy,
PWNDLIST *pplistTray)
{
int ccvr;
int cIter;
PCVR pcvr;
PWND pwnd;
PWND pwndParent;
PWND pwndT;
PWINDOWPOS ppos;
HRGN hrgnInvalidate;
UINT fsRgnEmpty;
UINT fsSumEmpty;
int cwndShowing;
BOOL fSyncPaint = FALSE;
HDC hdcScreen = NULL;
GreLockDisplay(gpDispInfo->pDevLock);
/*
* Compute old visrgns and count total CVRs in list. A side-effect of
* BltValidInit is that revalidates all the windows in the SMWP array.
*/
if ((cIter = BltValidInit(psmwp)) == 0) {
CleanupAndExit:
/*
* Go through the cvr list and free the regions that BltValidInit()
* created.
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (pcvr->hrgnVisOld) {
GreDeleteObject(pcvr->hrgnVisOld);
pcvr->hrgnVisOld = NULL;
}
}
goto UnlockAndExit;
}
/*
* We left the crit sect since last time we validated the smwp. Validate
* it again, and find the first WINDOWPOS structure with a non-NULL
* hwnd in it.
*/
ppos = NULL;
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
/*
* Revalidate window and if it's invalid, NULL it out in the WINDOWPOS
* struct.
*/
pwnd = RevalidateHwnd(pcvr->pos.hwnd);
if ((pwnd == NULL) ||
(pwnd->spwndParent == NULL) ||
!IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
pcvr->pos.hwnd = NULL;
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
continue;
}
/*
* Remember the first WINDOWPOS structure that has a non-NULL
* hwnd.
*/
if (ppos == NULL)
ppos = &pcvr->pos;
}
if (ppos == NULL)
goto CleanupAndExit;
UserAssert(PW(ppos->hwnd));
pwndParent = PW(ppos->hwnd)->spwndParent;
UserAssert(pwndParent);
/*
* Go account for any dirty DCs at this point, to ensure that:
* - any drawing done before we create an SPB will not
* later invalidate that SPB
* - the SPB regions reflect the true state of the screen,
* so that we don't validate parts of windows that are dirty.
*
* We must make this call before we change any window state.
*/
if (AnySpbs())
SpbCheck();
/*
* Change the window states
*/
ChangeStates(pwndParent, psmwp, pplistCreate, pplistDestroy, pplistTray);
/*
* move window bits around
*
* Invalidate the DCs for the siblings of this window.
*
* If our parent is not clipchildren, then we don't need to
* invalidate its DCs. If it IS clipchildren, its client visrgn
* will be changing, so we must invalidate it too.
*/
InvalidateDCCache(pwndParent,
TestWF(pwndParent, WFCLIPCHILDREN) ?
IDC_CLIENTONLY : IDC_CHILDRENONLY);
/*
* Now, do the bltting or whatever that is required.
*/
fsSumEmpty = RE_VALIDSUM | RE_INVALIDSUM;
hrgnInvalidate = hrgnInvalidSum;
/*
* Init count of windows being shown with SWP_SHOWWINDOW
* for our backward compatibility hack later.
*/
cwndShowing = 0;
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
/*
* Decrement loop count. When cIter is 0, then
* we're on the last pass through the loop.
*/
cIter--;
if (pcvr->pos.hwnd == NULL)
continue;
/*
* If we're not redrawing, try the next one.
*/
if (pcvr->pos.flags & SWP_NOREDRAW)
continue;
/*
* Some drawing has been done
*/
fSyncPaint = TRUE;
pwnd = PW(pcvr->pos.hwnd);
fsRgnEmpty = pcvr->fsRE;
/*
* Calculate the new visrgn
*/
if (!SwpCalcVisRgn(pwnd, hrgnVisNew))
fsRgnEmpty |= RE_VISNEW;
/*
* If the window is obscured by another window with an SPB,
* we have to ensure that that SPB gets invalidated properly
* since the app may not be getting a WM_PAINT msg or anything
* to invalidate the bits.
*/
if (AnySpbs())
SpbCheckRect(pwnd, &pwnd->rcWindow, DCX_WINDOW);
/*
* Calculate hrgnValid:
*
* hrgnValid = OffsetRgn(rcBlt, -dxBlt, -dyBlt) & hrgnVisOld
* hrgnValid = hrgnValid - hrgnValidSum
* OffsetRgn(hrgnValid, dxBlt, dyBlt);
* hrgnValid = hrgnValid - hrgnUpdate
* hrgnValid = hrgnValid & hrgnVisNew;
*
* If either the old or new visrgns are empty, there
* can be no valid bits...
*/
if (fsRgnEmpty & (RE_VISOLD | RE_VISNEW))
goto ValidEmpty;
/*
* If the entire window is already completely invalid, blow out.
*/
if (pwnd->hrgnUpdate == MAXREGION)
goto ValidEmpty;
/*
* If the blt rectangle is empty, there can be no valid bits.
*/
if ((pcvr->rcBlt.right <= pcvr->rcBlt.left) ||
(pcvr->rcBlt.bottom <= pcvr->rcBlt.top)) {
goto ValidEmpty;
}
GreSetRectRgn(hrgnSWP1,
pcvr->rcBlt.left - pcvr->dxBlt,
pcvr->rcBlt.top - pcvr->dyBlt,
pcvr->rcBlt.right - pcvr->dxBlt,
pcvr->rcBlt.bottom - pcvr->dyBlt);
switch (IntersectRgn(hrgnValid, hrgnSWP1, pcvr->hrgnVisOld)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
if (!(fsSumEmpty & RE_VALIDSUM)) {
switch (SubtractRgn(hrgnValid, hrgnValid, hrgnValidSum)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
}
if ((pcvr->dxBlt | pcvr->dyBlt) != 0)
GreOffsetRgn(hrgnValid, pcvr->dxBlt, pcvr->dyBlt);
/*
* Now subtract off the update regions of ourself and any
* non-clipchildren parents...
*/
pwndT = pwnd;
do {
if (pwndT->hrgnUpdate == MAXREGION)
goto ValidEmpty;
if (pwndT->hrgnUpdate != NULL) {
switch (SubtractRgn(hrgnValid, hrgnValid, pwndT->hrgnUpdate)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
}
pwndT = pwndT->spwndParent;
} while (pwndT && !TestWF(pwndT, WFCLIPCHILDREN));
switch (IntersectRgn(hrgnValid, hrgnValid, hrgnVisNew)) {
case NULLREGION:
case ERROR:
ValidEmpty:
fsRgnEmpty |= RE_VALID;
break;
}
/*
* Before we restore the restore bits over part of our
* image, we need to first copy any valid bits to their
* final destination.
*/
if (!(fsRgnEmpty & RE_VALID) && ((pcvr->dxBlt | pcvr->dyBlt) != 0)) {
if (hdcScreen == NULL)
hdcScreen = gpDispInfo->hdcScreen;
GreSelectVisRgn(hdcScreen, hrgnValid, NULL, SVR_COPYNEW);
GreBitBlt(hdcScreen,
pcvr->rcBlt.left,
pcvr->rcBlt.top,
pcvr->rcBlt.right - pcvr->rcBlt.left,
pcvr->rcBlt.bottom - pcvr->rcBlt.top,
hdcScreen,
pcvr->rcBlt.left - pcvr->dxBlt,
pcvr->rcBlt.top - pcvr->dyBlt,
SRCCOPY,
0);
}
/*
* Now take care of any SPB bit restoration we need to do.
*
* Calculate the region to clip the RestoreSpb() output to:
*
* hrgnInvalid = hrgnVisOld - hrgnVisNew
*/
if (TestWF(pwnd, WFHASSPB) &&
!(fsRgnEmpty & RE_VISOLD) &&
CombineOldNewVis(hrgnInvalid, pcvr->hrgnVisOld, hrgnVisNew, RGN_DIFF, fsRgnEmpty)) {
UINT retRSPB;
/*
* Perform SPB bits restore. We pass RestoreSpb() the region of
* the part of the SPB that got uncovered by this window rearrangement.
* It tries to restore as much of this area as it can from the SPB,
* and returns the area that could not be restored from the SPB.
*
* The device driver's SaveBitmap() function does not clip at all
* when it restores bits, which means that it might write bits
* in an otherwise valid area. This means that the invalid area
* returned by RestoreSpb() may actually be LARGER than the original
* hrgnSpb passed in.
*
* RestoreSpb() returns TRUE if some part of hrgnInvalid needs
* to be invalidated.
*/
if ((retRSPB = RestoreSpb(pwnd, hrgnInvalid, &hdcScreen)) == RSPB_NO_INVALIDATE) {
/*
* If hrgnVisNew is empty, then we know the whole invalid
* area is empty.
*/
if (fsRgnEmpty & RE_VISNEW)
goto InvalidEmpty;
} else if (retRSPB == RSPB_INVALIDATE_SSB) {
/*
* If RestoreSpb actually invalidated some area and we already
* have a hrgnValidSum then subtract the newly invalidated area
* Warning this region subtract is not in the Win 3.1 code but
* they probably did not have the problem as severe because their
* drivers were limited to one level of SaveScreenBits
*/
if (!(fsSumEmpty & RE_VALIDSUM))
SubtractRgn(hrgnValidSum, hrgnValidSum, hrgnInvalid);
}
/*
* hrgnInvalid += hrgnVisNew
*/
if (!(fsRgnEmpty & RE_VISNEW))
UnionRgn(hrgnInvalid, hrgnInvalid, hrgnVisNew);
/*
* Some of the area we calculated as valid may have gotten
* obliterated by the SPB restore. To ensure this isn't
* the case, subtract off the hrgnInvalid returned by RestoreSpb.
*/
// LATER mikeke VALIDSUM / hrgnValid mismatch
if (!(fsRgnEmpty & RE_VALIDSUM)) {
switch (SubtractRgn(hrgnValid, hrgnValid, hrgnInvalid)) {
case NULLREGION:
case ERROR:
fsRgnEmpty |= RE_VALIDSUM;
break;
}
}
} else {
/*
* No SPB. Simple hrgnInvalid calculation is:
*
* hrgnInvalid = hrgnVisNew + hrgnVisOld;
*/
if (pcvr->hrgnVisOld == NULL) {
/*
* If we couldn't create hrgnVisOld, then
* invalidate the entire parent
*/
GreSetRectRgn(hrgnInvalid,
pwndParent->rcWindow.left,
pwndParent->rcWindow.top,
pwndParent->rcWindow.right,
pwndParent->rcWindow.bottom);
} else {
if (!CombineOldNewVis(hrgnInvalid,
pcvr->hrgnVisOld,
hrgnVisNew,
RGN_OR,
fsRgnEmpty)) {
goto InvalidEmpty;
}
}
}
/*
* Update hrgnValidSum
*
* hrgnValidSum += hrgnValid
*/
if (!(fsRgnEmpty & RE_VALID)) {
/*
* If the sum region is empty, then COPY instead of OR
*/
if (fsSumEmpty & RE_VALIDSUM)
CopyRgn(hrgnValidSum, hrgnValid);
else
UnionRgn(hrgnValidSum, hrgnValid, hrgnValidSum);
fsSumEmpty &= ~RE_VALIDSUM;
}
/*
* Subtract hrgnValidSum from hrgnInvalid if non-empty,
* otherwise use hrgnValid. Note, hrgnValid has been OR'ed
* into hrgnValidSum already.
*/
if (!(fsSumEmpty & RE_VALIDSUM) || !(fsRgnEmpty & RE_VALID)) {
switch (SubtractRgn(hrgnInvalid, hrgnInvalid,
!(fsSumEmpty & RE_VALIDSUM) ? hrgnValidSum : hrgnValid)) {
case NULLREGION:
case ERROR:
InvalidEmpty:
fsRgnEmpty |= RE_INVALID;
break;
}
}
/*
* If there are any SPB bits left over, it wasn't just created
* (SWP_SHOWWINDOW), and an operation occured that invalidates
* the spb bits, get rid of the spb. A move, size, hide, or
* zorder operation will invalidate the bits. Note that we do this
* outside of the SWP_NOREDRAW case in case the guy set that flag
* when he had some SPB bits lying around.
*/
if (TestWF(pwnd, WFHASSPB) && !(pcvr->pos.flags & SWP_SHOWWINDOW) &&
(pcvr->pos.flags &
(SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW))
!= (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER)) {
FreeSpb(FindSpb(pwnd));
}
/*
* Finally, free up hrgnVisOld.
*/
if (pcvr->hrgnVisOld) {
GreDeleteObject(pcvr->hrgnVisOld);
pcvr->hrgnVisOld = NULL;
}
/*
* BACKWARD COMPATIBILITY HACK
*
* In 3.0, a ShowWindow() NEVER invalidated any of the children.
* It would invalidate the parent and the window being shown, but
* no others.
*
* We only apply hack (a) to 3.0 apps when all the windows involved
* are doing a SWP_SHOWWINDOW: if any aren't, then we have to make
* sure the siblings are invalidated too. So, we count the windows
* doing a SHOWWINDOW and compare it to the total count in the CVR.
*/
if (!TestWF(pwnd, WFWIN31COMPAT) && (pcvr->pos.flags & SWP_SHOWWINDOW))
cwndShowing++;
/*
* Update hrgnInvalidSum:
*
* hrgnInvalidSum += hrgnInvalid
*/
if (!(fsRgnEmpty & RE_INVALID)) {
/*
* BACKWARD COMPATIBILITY HACK
*
* In many cases including ShowWindow, CS_V/HREDRAW,
* SWP_NOCOPYBITS, etc, 3.0 always invalidated the window with
* (HRGN)1, regardless of how it was clipped by children, siblings,
* or parents. Besides being more efficient, this caused child
* windows that would otherwise not get update regions to get
* invalidated -- see the hack notes in InternalInvalidate2.
*
* This is a performance hack (usually) because (HRGN)1 can save
* a lot of region calculations in the normal case. So, we do this
* for 3.1 apps as well as 3.0 apps.
*
* We detect the case as follows: invalid area not empty,
* valid area empty, and new visrgn not empty.
*/
if ((fsRgnEmpty & RE_VALID) && !(fsRgnEmpty & RE_VISNEW)) {
/*
* With the parameters we use InternalInvalidate() does
* not leave the critical section
*/
xxxInternalInvalidate(pwnd,
MAXREGION,
RDW_INVALIDATE |
RDW_FRAME |
RDW_ERASE |
RDW_ALLCHILDREN);
}
/*
* If the sum region is empty, then COPY instead of OR
*/
if (fsSumEmpty & RE_INVALIDSUM) {
/*
* HACK ALERT:
* If this is the last pass through the loop (cIter == 0)
* and hrgnInvalidSum is currently empty,
* then instead of copying hrgnInvalid to hrgnInvalidSum,
* just set hrgnInvalidate to hrgnInvalid. This saves
* a region copy in the single-window case.
*/
if (cIter == 0) {
hrgnInvalidate = hrgnInvalid;
} else {
CopyRgn(hrgnInvalidSum, hrgnInvalid);
}
} else {
UnionRgn(hrgnInvalidSum, hrgnInvalid, hrgnInvalidSum);
}
fsSumEmpty &= ~RE_INVALIDSUM;
}
} // for (... pcvr ...)
/*
* Now, invalidate as needed.
*/
if (!(fsSumEmpty & RE_INVALIDSUM)) {
/*
* BACKWARD COMPATIBILITY HACK (see above)
*
* If all the windows involved were being shown, then
* invalidate the parent ONLY -- don't enumerate any children.
* (the windows involved have already been invalidated above).
* This hack is only applied to 3.0 apps (see above).
*/
/*
* More hack-o-rama. On Win3.1, the desktop paint would only
* repaint those portions inside the rect returned from GetClipBox().
* Dialogs with spbs outside the rect returned from GetClipBox() would
* not get their spbs invalidated until later, when you clicked on
* them to make them active. The only dialog that wouldn't really loose
* its bits is the control panel desktop dialog, which would restore
* its bad bits when it went away (in certain configurations). On
* NT, the desktop would repaint and then the dialog would go away.
* On Win3.1, the dialog would go away and then the desktop would
* repaint. On NT, because of preemption and little differences in
* painting order between applications, there was an opportunity to
* put bad bits on the screen, on Win3.1 there wasn't.
*
* Now... the below code that passes RDW_NOCHILDREN only gets executed
* if the app is marked as a win3.0 app (latest CorelDraw, also wep
* freecell demonstrates the same problem). This code would get
* executed when a dialog got shown. So for a win3.0 app, spb would get
* saved, the dialog would get shown, the desktop invalidated, the
* desktop would paint, the spb would get clobbered. In short, when
* a win3.0 app would put up a dialog, all spbs would get freed because
* of the new (and correct) way the desktop repaints.
*
* So the desktop check hack will counter-act the invalidate
* RDW_NOCHILDREN case if all windows are hiding / showing and the
* desktop is being invalidated. Note that in the no RDW_NOCHILDREN
* case, the invalid area gets distributed to the children first (in
* this case, children of the desktop), so if the children cover the
* desktop, the desktop won't get any invalid region, which is what
* we want. - scottlu
*/
/*
* With the parameters we use InternalInvalidate() does not leave
* the critical section
*/
xxxInternalInvalidate(
pwndParent,
hrgnInvalidate,
((cwndShowing == psmwp->ccvr &&
pwndParent != PWNDDESKTOP(pwndParent)) ?
(RDW_INVALIDATE | RDW_ERASE | RDW_NOCHILDREN) :
(RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN)));
}
UnlockAndExit:
/*
* If necessary, release the screen DC
*/
if (hdcScreen != NULL) {
/*
* Reset the visrgn before we go...
*/
GreSelectVisRgn(hdcScreen, NULL, NULL, SVR_DELETEOLD);
/*
* Make sure that the drawing we did in this DC does not affect
* any SPBs. Clear the dirty rect.
*/
GreGetBounds(hdcScreen, NULL, 0); // NULL means reset
}
/*
* All the dirty work is done. Ok to leave the critsects we entered
* earlier.
*/
GreUnlockDisplay(gpDispInfo->pDevLock);
return fSyncPaint;
}
/***************************************************************************\
* xxxHandleWindowPosChanged
*
* DefWindowProc() HandleWindowPosChanged handler.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID xxxHandleWindowPosChanged(
PWND pwnd,
PWINDOWPOS ppos)
{
CheckLock(pwnd);
if (!(ppos->flags & SWP_NOCLIENTMOVE)) {
PWND pwndParent = pwnd->spwndParent;
if (pwndParent != NULL) {
xxxSendMessage(
pwnd,
WM_MOVE,
FALSE,
MAKELONG(pwnd->rcClient.left - pwndParent->rcClient.left,
pwnd->rcClient.top - pwndParent->rcClient.top));
}
}
if ((ppos->flags & SWP_STATECHANGE) || !(ppos->flags & SWP_NOCLIENTSIZE)) {
if (TestWF(pwnd, WFMINIMIZED))
xxxSendSizeMessage(pwnd, SIZEICONIC);
else if (TestWF(pwnd, WFMAXIMIZED))
xxxSendSizeMessage(pwnd, SIZEFULLSCREEN);
else
xxxSendSizeMessage(pwnd, SIZENORMAL);
}
}
/***************************************************************************\
* PWND GetRealOwner(pwnd)
*
* Returns the owner of pwnd, normalized so that it shares the same parent
* of pwnd.
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PWND GetRealOwner(
PWND pwnd)
{
PWND pwndParent = pwnd->spwndParent;
/*
* A frame window owned by itself is "unowned"
*/
if (pwnd != pwnd->spwndOwner && (pwnd = pwnd->spwndOwner) != NULL) {
/*
* The NULL test is in case the owner is higher than the
* passed in window (e.g. your owner IS your parent)
*/
while (pwnd != NULL && pwnd->spwndParent != pwndParent)
pwnd = pwnd->spwndParent;
}
return pwnd;
}
/***************************************************************************\
*
* Starting at pwnd (or pwndParent->pwndChild if pwnd == NULL), find
* next window owned by pwndOwner
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PWND NextOwnedWindow(
PWND pwnd,
PWND pwndOwner,
PWND pwndParent)
{
if (pwnd == NULL) {
pwnd = pwndParent->spwndChild;
goto loop;
}
while ((pwnd = pwnd->spwndNext) != NULL) {
loop:
/*
* If owner of pwnd is pwndOwner, break out of here...
*/
if (pwndOwner == GetRealOwner(pwnd))
break;
}
return pwnd;
}
/***************************************************************************\
*
* Recursively enumerate owned windows starting from pwndRoot,
* to set the state of WEFTOPMOST. Doesn't actually diddle
* this bit yet: the work gets done in ChangeStates():
* instead we just set the WFTOGGLETOPMOST bit as appropriate.
*
* We can't diddle the state until the Z order changes are done,
* or else GetTopMostWindow() and the like will do the wrong thing.
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
VOID SetTopmost(
PWND pwndRoot,
BOOL fTopmost)
{
PWND pwnd;
/*
* If the new state is different than the current state,
* then set the TOGGLETOPMOST bit, so it'll get toggled
* in ChangeStates().
*/
ClrWF(pwndRoot, WFTOGGLETOPMOST);
if ((TestWF(pwndRoot, WEFTOPMOST) && !fTopmost) ||
(!TestWF(pwndRoot, WEFTOPMOST) && fTopmost))
SetWF(pwndRoot, WFTOGGLETOPMOST);
pwnd = NULL;
while (pwnd = NextOwnedWindow(pwnd, pwndRoot, pwndRoot->spwndParent))
SetTopmost(pwnd, fTopmost);
}
/***************************************************************************\
* CalcForegroundInsertAfter
*
* Calculates where to zorder a window that doesn't belong to the foreground
* thread and is not topmost but wants to come to the top. This routine
* calculates what "top" means under those conditions.
*
* 14-Sep-1992 ScottLu Created.
\***************************************************************************/
PWND CalcForegroundInsertAfter(
PWND pwnd)
{
PWND pwndInsertAfter;
PWND pwndT;
PTHREADINFO ptiTop;
/*
* If we're allowing this application to make this top
* level window foreground active, then this app may
* not be foreground yet, but we want any windows it
* zorders to appear on top because it is probably about
* to activate them (this is a guess!) If this is the case,
* let it do what it wants. A good example of this is an
* application like toolbook that creates a window without a
* caption, doesn't activate it, and wants that to appear on top.
*/
if (TestWF(pwnd, WFBOTTOMMOST))
pwndInsertAfter = GetLastNonBottomMostWindow(pwnd, TRUE);
else
pwndInsertAfter = GetLastTopMostWindow();
if (!TestwndChild(pwnd)) {
// if (hwnd->hwndParent == hwndDesktop) -- Chicago conditional FritzS
if ((GETPTI(pwnd)->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE) ||
(GETPTI(pwnd)->ppi->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE)) {
return pwndInsertAfter;
}
}
/*
* If there is no foreground thread or this pwnd is of the foreground
* thread, then let it come to the top.
*/
if (gpqForeground == NULL)
return pwndInsertAfter;
if (GETPTI(pwnd)->pq == gpqForeground)
return pwndInsertAfter;
/*
* This thread is not of the foreground queue, so search for a window
* of this thread to zorder above.
*/
pwndT = ((pwndInsertAfter == NULL) ?
pwnd->spwndParent->spwndChild :
pwndInsertAfter);
for (; pwndT != NULL; pwndT = pwndT->spwndNext) {
/*
* This window wants to come to the top if possible.
* If we're passing our own window, get out of this loop:
* by now we already have pwndInsertAfter set up to the
* last available window to insert after.
*/
if ((pwndT == pwnd) || TestWF(pwndT, WFBOTTOMMOST))
break;
/*
* If this window isn't owned by this thread, continue.
*/
if (GETPTI(pwndT) != GETPTI(pwnd)) {
pwndInsertAfter = pwndT;
continue;
}
/*
* Don't want a window zordering below one of its top most windows
* if it isn't foreground.
*/
if (TestWF(pwndT, WEFTOPMOST)) {
pwndInsertAfter = pwndT;
continue;
}
/*
* Ok to change zorder of top level windows because of
* invisible windows laying around, but not children:
* applications would go nuts if we did this.
*/
if (!TestwndChild(pwndT)) {
if (!TestWF(pwndT, WFVISIBLE)) {
pwndInsertAfter = pwndT;
continue;
}
}
break;
}
/*
* If pwndT == NULL, it means that the thread has no
* other sibling windows, so we need to put it after the
* foreground window (foreground thread). Search for the
* first unowned window of the foreground app to zorder
* after.
*/
if (pwndT == NULL) {
/*
* This is our first guess in case nothing works out.
*/
pwndInsertAfter = GetLastTopMostWindow();
/*
* Start below the last topmost or from the top if no
* topmost windows.
*/
if ((pwndT = pwndInsertAfter) == NULL)
pwndT = pwnd->spwndParent->spwndChild;
/*
* ptiTop is the pti of the active window in the foreground queue!
*/
ptiTop = NULL;
if (gpqForeground->spwndActive != NULL)
ptiTop = GETPTI(gpqForeground->spwndActive);
for (; pwndT != NULL; pwndT = pwndT->spwndNext) {
if (TestWF(pwndT, WFBOTTOMMOST))
break;
/*
* If not the top most thread, continue.
*/
if (GETPTI(pwndT) != ptiTop)
continue;
/*
* Found one of the foreground thread. Remember this
* as the next best guess. Try to find an unowned
* visible window, which would indicate the main
* window of the foreground thread. If owned,
* continue.
*/
if (pwndT->spwndOwner != NULL) {
pwndInsertAfter = pwndT;
continue;
}
/*
* Unowned and of the foreground thread. Is it visible?
* If not, get out of here.
*/
if (!TestWF(pwndT, WFVISIBLE))
continue;
/*
* Best possible match so far: unowned visible window
* of the foreground thread.
*/
pwndInsertAfter = pwndT;
break;
}
}
UserAssert(pwnd != pwndInsertAfter);
return pwndInsertAfter;
}
/***************************************************************************\
* GetTopMostInsertAfter
*
* We don't want any one to get in front of a hard error box, except menus,
* screen savers, etc.
*
* Don't call it directly, use the GETTOPMOSTINSERTAFTER macro to avoid
* the call when there is no hard error box up (gHardErrorHandler.pti == NULL).
*
* 04-25-96 GerardoB Created
\***************************************************************************/
PWND GetTopMostInsertAfter (PWND pwnd)
{
PWND pwndT;
PTHREADINFO ptiCurrent;
PDESKTOP pdesk;
WORD wfnid;
/*
* pwnd: Menu and switch (ALT-TAB) windows can go on top.
*/
wfnid = GETFNID(pwnd);
if ((wfnid == FNID_MENU) || (wfnid == FNID_SWITCH)) {
return NULL;
}
/*
* pti: If this is the error handler thread, don't bother any longer.
* Screen saver can go on top too.
*/
ptiCurrent = PtiCurrent();
if ((ptiCurrent == gHardErrorHandler.pti)
|| (ptiCurrent->TIF_flags & TIF_SCREENSAVER)) {
return NULL;
}
/*
* pdesk: Leave the logon desktop alone.
* Make sure the hard error box is on this desktop
*/
pdesk = ptiCurrent->rpdesk;
if ((pdesk == pdesk->rpwinstaParent->rpdeskLogon)
|| (pdesk != gHardErrorHandler.pti->rpdesk)) {
return NULL;
}
/*
* Walk the window list looking for the hard error box.
* Start searching from the current desktop's first child.
*/
for (pwndT = pdesk->pDeskInfo->spwnd->spwndChild;
pwndT != NULL; pwndT = pwndT->spwndNext) {
/*
* Hard error boxes are always top most.
*/
if (!TestWF(pwndT, WEFTOPMOST)) {
break;
}
/*
* If this window was created by the hard error handler thread,
* then this is it
*/
if (gHardErrorHandler.pti == GETPTI(pwndT)) {
return pwndT;
}
}
return NULL;
}
/***************************************************************************\
*
* This routine maps the special HWND_* values of ppos->hwndInsertAfter,
* and returns whether or not the window's owner group should be labelled
* TOPMOST or not, or left alone.
*
* Here are the TOPMOST rules. If pwndInsertAfter is:
*
* 1. HWND_BOTTOM == (HWND)1:
*
* The group is made non-TOPMOST.
*
* 2. HWND_TOPMOST == (HWND)-1:
*
* hwndInsertAfter is set to HWND_TOP, and the group is made TOPMOST.
*
* 3. HWND_NOTOPMOST == (HWND)-2:
*
* Treated same as HWND_TOP, except that the TOPMOST bit is cleared.
* and the entire group is made non-topmost.
* Used to make a topmost window non-topmost, but still leave it at
* the top of the non-topmost pile.
* The group is not changed if the window is already non-topmost.
*
* 4. HWND_TOP == (HWND)NULL:
*
* pwndInsertAfter is set to the last TOPMOST window if pwnd
* is not itself TOPMOST. If pwnd IS TOPMOST, then pwndInsertAfter
* remains HWND_TOP.
*
* 5. A TOPMOST window:
*
* If a window is being inserted among the TOPMOST windows, then
* the group becomes topmost too, EXCEPT if it's being inserted behind
* the bottom-most topmost window: in that case the window retains
* its current TOPMOST bit.
*
* 6. A non-TOPMOST window:
*
* If a window is being inserted among non-TOPMOST windows, the group is made
* non-TOPMOST and inserted there.
*
* Whenever a group is made TOPMOST, only that window and its ownees are made
* topmost. When a group is made NOTOPMOST, the entire window is made non-topmost.
*
* This routine must NOT set SWP_NOZORDER if the topmost state is changing:
* this would prevent the topmost bits from getting toggled in ChangeStates.
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
int CheckTopmost(
PWINDOWPOS ppos)
{
PWND pwnd, pwndInsertAfter, pwndT;
/*
* BACKWARD COMPATIBILITY HACK
*
* If we're activating a window and Z-ordering too, we must ignore the
* specified Z order and bring the window to the top, EXCEPT in the
* following conditions:
*
* 1. The window is already active (in which case, the activation code
* will not be bringing the window to the top)
*
* 2. HWND_TOP or HWND_NOTOPMOST is specified. This allows us to
* activate and move to topmost or nontopmost at the same time.
*
* NOTE: It would have been possible to modify ActivateWindow() to
* take a flag to prevent it from ever doing the BringWindowToTop,
* thus allowing SetWindowPos() to properly honor pwndInsertBehind
* AND activation, but this change was considered too late in the
* game -- there could be problems with existing 3.1 apps, such as
* PenWin, etc.
*/
pwnd = PW(ppos->hwnd);
if (!(ppos->flags & SWP_NOACTIVATE) &&
!(ppos->flags & SWP_NOZORDER) &&
(ppos->hwndInsertAfter != HWND_TOPMOST &&
ppos->hwndInsertAfter != HWND_NOTOPMOST) &&
(pwnd != GETPTI(pwnd)->pq->spwndActive)) {
ppos->hwndInsertAfter = HWND_TOP;
}
/*
* If we're not Z-ordering, don't do anything.
*/
if (ppos->flags & SWP_NOZORDER)
return CTM_NOCHANGE;
if (ppos->hwndInsertAfter == HWND_BOTTOM) {
return CTM_NOTOPMOST;
} else if (ppos->hwndInsertAfter == HWND_NOTOPMOST) {
/*
* If currently topmost, move to top of non-topmost list.
* Otherwise, no change.
*
* Note that we don't set SWP_NOZORDER -- we still need to
* check the TOGGLETOPMOST bits in ChangeStates()
*/
if (TestWF(pwnd, WEFTOPMOST)) {
pwndT = GetLastTopMostWindow();
ppos->hwndInsertAfter = HW(pwndT);
if (ppos->hwndInsertAfter == ppos->hwnd) {
pwndT = _GetWindow(pwnd, GW_HWNDPREV);
ppos->hwndInsertAfter = HW(pwndT);
}
} else {
pwndT = _GetWindow(pwnd, GW_HWNDPREV);
ppos->hwndInsertAfter = HW(pwndT);
}
return CTM_NOTOPMOST;
} else if (ppos->hwndInsertAfter == HWND_TOPMOST) {
pwndT = GETTOPMOSTINSERTAFTER(pwnd);
if (pwndT != NULL) {
ppos->hwndInsertAfter = HW(pwndT);
} else {
ppos->hwndInsertAfter = HWND_TOP;
}
return CTM_TOPMOST;
} else if (ppos->hwndInsertAfter == HWND_TOP) {
/*
* If we're not topmost, position ourself after
* the last topmost window.
* Otherwise, make sure that no one gets in front
* of a hard error box.
*/
if (TestWF(pwnd, WEFTOPMOST)) {
pwndT = GETTOPMOSTINSERTAFTER(pwnd);
if (pwndT != NULL) {
ppos->hwndInsertAfter = HW(pwndT);
}
return CTM_NOCHANGE;
}
/*
* Calculate the window to zorder after for this window, taking
* into account foreground status.
*/
pwndInsertAfter = CalcForegroundInsertAfter(pwnd);
ppos->hwndInsertAfter = HW(pwndInsertAfter);
return CTM_NOCHANGE;
}
/*
* If we're being inserted after the last topmost window,
* then don't change the topmost status.
*/
pwndT = GetLastTopMostWindow();
if (ppos->hwndInsertAfter == HW(pwndT))
return CTM_NOCHANGE;
/*
* Otherwise, if we're inserting a TOPMOST among non-TOPMOST,
* or vice versa, change the status appropriately.
*/
if (TestWF(PW(ppos->hwndInsertAfter), WEFTOPMOST)) {
if (!TestWF(pwnd, WEFTOPMOST)) {
return CTM_TOPMOST;
}
pwndT = GETTOPMOSTINSERTAFTER(pwnd);
if (pwndT != NULL) {
ppos->hwndInsertAfter = HW(pwndT);
}
} else {
if (TestWF(pwnd, WEFTOPMOST))
return CTM_NOTOPMOST;
}
return CTM_NOCHANGE;
}
/***************************************************************************\
* IsOwnee(pwndOwnee, pwndOwner)
*
* Returns TRUE if pwndOwnee is owned by pwndOwner
*
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
BOOL IsOwnee(
PWND pwndOwnee,
PWND pwndOwner)
{
PWND pwnd;
while (pwndOwnee != NULL) {
/*
* See if pwndOwnee is a child of pwndOwner...
*/
for (pwnd = pwndOwnee; pwnd != NULL; pwnd = pwnd->spwndParent) {
if (pwnd == pwndOwner)
return TRUE;
}
/*
* If the window doesn't own itself, then set the owner
* to itself.
*/
pwndOwnee = (pwndOwnee->spwndOwner != pwndOwnee ?
pwndOwnee->spwndOwner : NULL);
}
return FALSE;
}
/***************************************************************************\
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
BOOL IsBehind(
PWND pwnd,
PWND pwndReference)
{
/*
* Starting at pwnd, move down until we reach the end of the window
* list, or until we reach pwndReference. If we encounter pwndReference,
* then pwnd is above pwndReference, so return FALSE. If we get to the
* end of the window list, pwnd is behind, so return TRUE.
*/
if (pwndReference == (PWND)HWND_TOP)
return TRUE;
if (pwndReference == (PWND)HWND_BOTTOM)
return FALSE;
for ( ; pwnd != NULL; pwnd = pwnd->spwndNext) {
if (pwnd == pwndReference)
return FALSE;
}
return TRUE;
}
/***************************************************************************\
*
* Add pwnd to the SMWP. pwndChange is the "real" pwnd being repositioned
* and pwndInsertAfter is the place where it's being inserted.
*
* pwndTopInsert is the window handle where the top of the owner tree should be
* inserted. The special value of (HWND)-2 is used to indicate recursion, in
* which case newly added SWPs are added to the previous element.
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PSMWP AddSelfAndOwnees(
PSMWP psmwp,
PWND pwnd,
PWND pwndChange,
PWND pwndInsertAfter,
int iTop)
{
PWND pwndChgOwnee;
PWND pwndT;
BOOL fChgOwneeInserted;
CVR *pcvr;
/*
* The general idea here is to first add our ownees, then add ourself.
* When we add our ownees though, we add them as appropriate based
* on the pwndInsertAfter parameter.
*
* Find out if any of our ownees are on a direct path between pwndChange
* and the root of the owner tree. If one is, then its Z order relative
* to its owner-siblings will be changing. If none are, then
* we want to add our ownees to the list in their current order.
*/
pwndChgOwnee = pwndChange;
while (pwndChgOwnee != NULL) {
pwndT = GetRealOwner(pwndChgOwnee);
if (pwnd == pwndT)
break;
pwndChgOwnee = pwndT;
}
/*
* Now enumerate all other ownees, and insert them in their
* current order.
*/
fChgOwneeInserted = FALSE;
pwndT = NULL;
while ((pwndT = NextOwnedWindow(pwndT, pwnd, pwnd->spwndParent)) != NULL) {
/*
* If these siblings are to be reordered, compare the sibling's
* current Z order with pwndInsertAfter.
*/
if (pwndChgOwnee == NULL) {
/*
* No Z change for our ownees: just add them in their current order
*/
psmwp = AddSelfAndOwnees(psmwp, pwndT, NULL, NULL, iTop);
} else {
/*
* If we haven't already inserted the ChgOwnee, and the
* enumerated owner-sibling is behind pwndInsertAfter, then
* add ChgOwnee.
*/
if (!fChgOwneeInserted && IsBehind(pwndT, pwndInsertAfter)) {
psmwp = AddSelfAndOwnees(psmwp,
pwndChgOwnee,
pwndChange,
pwndInsertAfter,
iTop);
if (psmwp == NULL)
return NULL;
fChgOwneeInserted = TRUE;
}
if (pwndT != pwndChgOwnee) {
/*
* Not the change ownee: add it in its current order.
*/
psmwp = AddSelfAndOwnees(psmwp, pwndT, NULL, NULL, iTop);
}
}
if (psmwp == NULL)
return NULL;
}
/*
* If we never added the change ownee in the loop, add it now.
*/
if ((pwndChgOwnee != NULL) && !fChgOwneeInserted) {
psmwp = AddSelfAndOwnees(psmwp,
pwndChgOwnee,
pwndChange,
pwndInsertAfter,
iTop);
if (psmwp == NULL)
return NULL;
}
/*
* Finally, add this window to the list.
*/
psmwp = _DeferWindowPos(psmwp, pwnd, NULL,
0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
if (psmwp == NULL)
return NULL;
/*
* If we aren't inserting the topmost entry,
* link this entry to the previous one.
* The topmost entry will get set by our caller.
*/
if (iTop != psmwp->ccvr - 1) {
pcvr = &psmwp->acvr[psmwp->ccvr - 1];
pcvr->pos.hwndInsertAfter = (pcvr - 1)->pos.hwnd;
}
return psmwp;
}
/***************************************************************************\
*
* ZOrderByOwner2 - Add the current window and all it owns to the SWP list,
* and arrange them in the new Z ordering. Called only if the Z order of the
* window is changing.
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PSMWP ZOrderByOwner2(
PSMWP psmwp,
int iTop)
{
PWND pwndT;
PWND pwndOwnerRoot;
PWND pwndTopInsert;
PWINDOWPOS ppos;
PWND pwnd;
PWND pwndInsertAfter;
BOOL fHasOwnees;
ppos = &psmwp->acvr[iTop].pos;
/*
* If inside message box processing, not Z ordering,
* or if SWP_NOOWNERZORDER specified, all done.
*/
// LATER 04-Mar-1992 MikeKe
// do we have a substitue for fMessageBox
if ((ppos->flags & SWP_NOZORDER) ||
(ppos->flags & SWP_NOOWNERZORDER)) {
return psmwp;
}
pwnd = PW(ppos->hwnd);
pwndInsertAfter = PWInsertAfter(ppos->hwndInsertAfter);
fHasOwnees = (BOOL)NextOwnedWindow(NULL, pwnd, pwnd->spwndParent);
/*
* If the window isn't owned, and it doesn't own any other window,
* do nothing.
*/
if (!pwnd->spwndOwner && !fHasOwnees)
return psmwp;
/*
* Find the unowned window to start building the tree from.
* This is easy: just zip upwards until we find a window with no owner.
*/
pwndOwnerRoot = pwndT = pwnd;
while ((pwndT = GetRealOwner(pwndT)) != NULL)
pwndOwnerRoot = pwndT;
/*
* We need to calculate what pwndInsertAfter should be for
* the first (topmost) window of the SWP list.
*
* If pwndInsertAfter is part of the owner tree we'll be building,
* then we want to reorder windows within the owner group, so the
* entire group should maintain it's relative order.
*
* If pwndInsertAfter is part of another owner tree, then we want
* the whole group relative to that.
*
* If pwndInsertAfter is HWND_BOTTOM, then we want the whole
* group to go to the bottom, so we position it relative to
* the bottom most window that is not part of the tree. We also
* want to put pwnd on the bottom relative to its owner siblings.
*
* If pwndInsertAfter is HWND_TOP, then bring the whole group
* to the top, as well as bringing pwnd to the top relative to its
* owner siblings.
*
* Assume the topmost of group is same as topmost
* (true for all cases except where rearranging subtree of group)
*/
pwndTopInsert = pwndInsertAfter;
if (pwndInsertAfter == (PWND)HWND_TOP) {
/*
* Bring the whole group to the top: nothing fancy to do.
*/
} else if (pwndInsertAfter == (PWND)HWND_BOTTOM) {
/*
* Put the whole group on the bottom. pwndTopInsert should
* be the bottommost window unowned by pwndOwnerRoot.
*/
for (pwndT = pwnd->spwndParent->spwndChild;
(pwndT != NULL) && !TestWF(pwndT, WFBOTTOMMOST); pwndT = pwndT->spwndNext) {
/*
* If it's not owned, then this is the bottommost so far.
*/
if (!IsOwnee(pwndT, pwndOwnerRoot))
pwndTopInsert = pwndT;
}
/*
* If there were no other windows not in our tree,
* then there is no Z ordering change to be done.
*/
if (pwndTopInsert == (PWND)HWND_BOTTOM)
ppos->flags |= SWP_NOZORDER;
} else {
/*
* pwndInsertAfter is a window. Compute pwndTopInsert
*/
if (IsOwnee(pwndInsertAfter, pwndOwnerRoot)) {
/*
* SPECIAL CASE: If we do not own any windows, and we're
* being moved within our owner group in such a way that
* we remain above our owner, then no other windows will
* be moving with us, and we can just exit
* without building our tree. This can save a LOT of
* extra work, especially with the MS apps CBT tutorials,
* which do this kind of thing a lot.
*/
if (!fHasOwnees) {
/*
* Make sure we will still be above our owner by searching
* for our owner starting from pwndInsertAfter. If we
* find our owner, then pwndInsertAfter is above it.
*/
for (pwndT = pwndInsertAfter; pwndT != NULL;
pwndT = pwndT->spwndNext) {
if (pwndT == pwnd->spwndOwner)
return psmwp;
}
}
/*
* Part of same group: Find out which window the topmost
* of the group is currently inserted behind.
*/
pwndTopInsert = (PWND)HWND_TOP;
for (pwndT = pwnd->spwndParent->spwndChild; pwndT != NULL;
pwndT = pwndT->spwndNext) {
if (IsOwnee(pwndT, pwndOwnerRoot))
break;
pwndTopInsert = pwndT;
}
}
}
/*
* Okay, now go recursively build the owned window list...
*/
if (!(ppos->flags & SWP_NOZORDER)) {
/*
* First "delete" the last entry (the one we're sorting with)
*/
psmwp->ccvr--;
psmwp = AddSelfAndOwnees(psmwp,
pwndOwnerRoot,
pwnd,
pwndInsertAfter,
iTop);
/*
* Now set the place where the whole group is going.
*/
if (psmwp != NULL)
psmwp->acvr[iTop].pos.hwndInsertAfter = HW(pwndTopInsert);
}
return psmwp;
}
/***************************************************************************\
* ZOrderByOwner
*
* This routine Z-Orders windows by their owners.
*
* LATER
* This code currently assumes that all of the window handles are valid
*
* History:
* 04-Mar-1992 MikeKe from win31
\***************************************************************************/
PSMWP ZOrderByOwner(
PSMWP psmwp)
{
int i;
PWND pwnd;
PWND pwndT;
PWND pwndT2;
WINDOWPOS pos;
PTHREADINFO ptiT;
/*
* Some of the windows in the SMWP list may be NULL at ths point
* (removed because they'll be handled by their creator's thread)
* so we've got to look for the first non-NULL window before we can
* execute some of the tests below. FindValidWindowPos returns NULL if
* the list has no valid windows in it.
*/
if (FindValidWindowPos(psmwp) == NULL)
return psmwp;
/*
* For each SWP in the array, move it to the end of the array
* and generate its entire owner tree in sorted order.
*/
for (i = psmwp->ccvr; i-- != 0; ) {
int iScan;
int iTop;
int code;
WINDOWPOS *ppos;
if (psmwp->acvr[0].pos.hwnd == NULL)
continue;
code = CheckTopmost(&psmwp->acvr[0].pos);
/*
* Make a local copy for later...
*/
pos = psmwp->acvr[0].pos;
ptiT = psmwp->acvr[0].pti;
/*
* Move the CVR to the end (if it isn't already)
*/
iTop = psmwp->ccvr - 1;
if (iTop != 0) {
RtlCopyMemory(&psmwp->acvr[0],
&psmwp->acvr[1],
iTop * sizeof(CVR));
psmwp->acvr[iTop].pos = pos;
psmwp->acvr[iTop].pti = ptiT;
}
if ((psmwp = ZOrderByOwner2(psmwp, iTop)) == NULL)
break;
/*
* Deal with WEFTOPMOST bits. If we're SETTING the TOPMOST bits,
* we want to set them for this window and
* all its owned windows -- the owners stay unchanged. If we're
* CLEARING, though, we need to enumerate ALL the windows, since
* they all need to lose the topmost bit when one loses it.
*
* Note that since a status change doesn't necessarily mean that
* the true Z order of the windows have changed, so ZOrderByOwner2
* may not have enumerated all of the owned and owner windows.
* So, we enumerate them separately here.
*/
if (code != CTM_NOCHANGE) {
PWND pwndRoot = PW(pos.hwnd);
/*
* If we're CLEARING the topmost, then we want to enumerate
* the owners and ownees, so start our enumeration at the root.
*/
if (code == CTM_NOTOPMOST) {
while (pwnd = GetRealOwner(pwndRoot))
pwndRoot = pwnd;
}
SetTopmost(pwndRoot, code == CTM_TOPMOST);
}
/*
* Now scan the list forwards (from the bottom of the
* owner tree towards the root) looking for the window
* we were positioning originally (it may have been in
* the middle of the owner tree somewhere). Update the
* window pos structure stored there with the original
* information (though the z-order info is retained from
* the sort).
*/
pwnd = NULL;
for (iScan = iTop; iScan != psmwp->ccvr; iScan++) {
ppos = &psmwp->acvr[iScan].pos;
if (ppos->hwnd == pos.hwnd) {
ppos->x = pos.x;
ppos->y = pos.y;
ppos->cx = pos.cx;
ppos->cy = pos.cy;
ppos->flags ^= ((ppos->flags ^ pos.flags) & ~SWP_NOZORDER);
}
/*
* Also adjust zorder if we're crossing a TOPMOST boundary:
* don't want non-topmost windows to show themselves
* above the foreground window.
*
* Is there a previous window? If no, continue.
*/
pwndT = pwnd;
pwnd = PW(ppos->hwnd);
if ((pwndT == NULL) || (pwnd == NULL))
continue;
/*
* Is this window foreground? If so, let it go. For wow apps,
* check to see if any thread of the process is foreground.
*/
if (GETPTI(pwnd)->TIF_flags & TIF_16BIT) {
if (gptiForeground == NULL)
continue;
if (GETPTI(pwnd)->ppi == gptiForeground->ppi)
continue;
} else {
if (GETPTI(pwnd) == gptiForeground)
continue;
}
/*
* Make sure the previous window is either staying or becoming
* topmost. If not, continue: no top most boundary.
*/
if (!TestWF(pwndT, WEFTOPMOST) && !TestWF(pwndT, WFTOGGLETOPMOST))
continue;
if (TestWF(pwndT, WEFTOPMOST) && TestWF(pwndT, WFTOGGLETOPMOST))
continue;
/*
* Is this window zordering behind the last window? If not,
* continue.
*/
if (ppos->hwndInsertAfter != HWq(pwndT))
continue;
/*
* Is the current window already top-most? If so then don't
* calculate a special insert after. If we don't check for
* this, then pwnd's insert after may be calculated as what
* pwnd already is, if pwnd is the last top most window. That
* would cause window links to get corrupted.
*/
if (TestWF(pwnd, WEFTOPMOST))
continue;
/*
* Doing this assign prevents this routine from being called
* twice, since HW() is a conditional macro.
*/
pwndT2 = CalcForegroundInsertAfter(pwnd);
ppos->hwndInsertAfter = HW(pwndT2);
}
}
return psmwp;
}
/***************************************************************************\
* xxxEndDeferWindowPosEx
*
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
BOOL xxxEndDeferWindowPosEx(
PSMWP psmwp,
BOOL fAsync)
{
PWND pwndNewActive;
PWND pwndParent;
PWND pwndActive;
PWND pwndActivePrev;
HWND hwndNewActive;
PWINDOWPOS pwp;
BOOL fClearBits;
BOOL fSyncPaint;
UINT cVisWindowsPrev;
PTHREADINFO pti = PtiCurrent();
TL tlpwndNewActive;
TL tlpwndParent;
BOOL fForegroundPrev;
PWNDLIST plistCreate;
PWNDLIST plistDestroy;
PWNDLIST plistTray;
/*
* Validate the window pos structures and find a window to activate.
*/
if ((psmwp->ccvr != 0) && ValidateSmwp(psmwp, &fSyncPaint)) {
if ((pwp = FindValidWindowPos(psmwp)) == NULL)
goto lbFinished;
/*
* Make sure to stop at the mother desktop window. In Win95
* a SetWindowPos() on a desktop window will have a NULL parent
* window. This is not true in NT, but our mother desktop
* window does have a NULL rpdesk, so check it too.
*/
UserAssert(PW(pwp->hwnd));
pwndParent = PW(pwp->hwnd)->spwndParent;
if (pwndParent == NULL || pwndParent->head.rpdesk == NULL)
goto lbFinished;
/*
* Usually all window positioning happens synchronously across threads.
* This is because apps expect that behavior now - if it was async,
* callers could not expect the state to be set once the api returned.
* This is not the semantics of SetWindowPos(). The downside of this
* synchronicity is that a SetWindowPos() on an hwnd created by another
* thread will cause the caller to wait for that thread - even if that
* thread is hung. That's what you get.
*
* We don't want task manager to hang though, no matter who else is
* hung, so when taskman calls, it calls a special entry point for
* tiling / cascading, which does SetWindowPos() asynchronously -
* by posting an event in each thread's queue that makes it set its
* own window position - that way if the thread is hung, who cares -
* it doesn't effect taskman.
*
* Do async window pos positioning before zorder by owner so that
* we retain any cross thread ownership relationships synchronously.
*/
if (fAsync && !AsyncWindowPos(psmwp))
return FALSE;
/*
* If needed, Z order the windows by owner.
* This may grow the SMWP, if new CVRs are added.
*/
if (pwndParent == PWNDDESKTOP(pwndParent)) {
if ((psmwp = ZOrderByOwner(psmwp)) == NULL)
return FALSE;
}
ThreadLockAlwaysWithPti(pti, pwndParent, &tlpwndParent);
/*
* Calc new window positions.
*/
if (xxxCalcValidRects(psmwp, &hwndNewActive)) {
int i;
pwndNewActive = RevalidateHwnd(hwndNewActive);
ThreadLockWithPti(pti, pwndNewActive, &tlpwndNewActive);
cVisWindowsPrev = pti->cVisWindows;
fForegroundPrev = (pti == gptiForeground);
/*
* Shuffle the bits around and distribute update regions
*/
plistCreate.cnt = 0;
plistCreate.pahwnd = NULL;
plistDestroy.cnt = 0;
plistDestroy.pahwnd = NULL;
plistTray.cnt = 0;
plistTray.pahwnd = NULL;
if (!BltValidBits(psmwp, &plistCreate, &plistDestroy, &plistTray))
fSyncPaint = FALSE;
if (plistCreate.pahwnd) {
for (i = 0; i < plistCreate.cnt; i++) {
PostShellHookMessages(HSHELL_WINDOWCREATED,
plistCreate.pahwnd[i]);
xxxCallHook(HSHELL_WINDOWCREATED,
(WPARAM)plistCreate.pahwnd[i],
(LPARAM)0,
WH_SHELL);
}
UserFreePool(plistCreate.pahwnd);
}
if (plistDestroy.pahwnd) {
for (i = 0; i < plistDestroy.cnt; i++) {
PostShellHookMessages(HSHELL_WINDOWDESTROYED,
plistDestroy.pahwnd[i]);
xxxCallHook(HSHELL_WINDOWDESTROYED,
(WPARAM)plistDestroy.pahwnd[i],
(LPARAM)0,
WH_SHELL);
}
UserFreePool(plistDestroy.pahwnd);
}
if (plistTray.pahwnd) {
for (i = 0; i < plistTray.cnt; i++) {
PWND pwnd = HMValidateHandleNoRip(plistTray.pahwnd[i],
TYPE_WINDOW);
if (pwnd != NULL)
xxxSetTrayWindow(pwnd->head.rpdesk, pwnd);
}
UserFreePool(plistTray.pahwnd);
}
/*
* If this process went from some windows to no windows visible
* and it was in the foreground, then let its next activate
* come to the foreground.
*/
if (fForegroundPrev && cVisWindowsPrev && !pti->cVisWindows) {
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
/*
* Also if any apps were in the middle of starting when
* this happened, allow them to foreground activate again.
*/
RestoreForegroundActivate();
}
/*
* Deal with any activation...
*/
fClearBits = FALSE;
if (pwndNewActive != NULL)
fClearBits = xxxSwpActivate(pwndNewActive);
/*
* Now draw frames and erase backgrounds of all the windows
* involved.
*/
if (fSyncPaint && pwndParent)
xxxDoSyncPaint(pwndParent, DSP_ENUMCLIPPEDCHILDREN);
ThreadUnlock(&tlpwndNewActive);
/*
* If SwpActivate() set the NONCPAINT bits, clear them now.
*/
if (fClearBits) {
if (pwndActive = pti->pq->spwndActive)
ClrWF(pwndActive, WFNONCPAINT);
if (pwndActivePrev = pti->pq->spwndActivePrev)
ClrWF(pwndActivePrev, WFNONCPAINT);
}
/*
* Send WM_WINDOWPOSCHANGED messages
*/
xxxSendChangedMsgs(psmwp);
}
ThreadUnlock(&tlpwndParent);
}
lbFinished:
/*
* All done. Free everything up and return.
*/
HMDestroyObject(psmwp);
return TRUE;
}
/***************************************************************************\
* IncVisWindows
* DecVisWindows
*
* These routines deal with incrementing/decrementing the visible windows
* on the thread.
*
\***************************************************************************/
#ifdef DEBUG
void VerifycVisWindows(PWND pwnd)
{
BOOL fShowMeTheWindows = FALSE;
PTHREADINFO pti = GETPTI(pwnd);
PWND pwndNext;
UINT uVisWindows = 0;
/*
* Make sure the count makes sense
*/
if ((int)pti->cVisWindows < 0) {
RIPMSG0(RIP_ERROR, "VerifycVisWindows: pti->cVisWindows underflow!");
fShowMeTheWindows = TRUE;
}
/*
* Child windows don't affect cVisWindows
*/
if (TestwndChild(pwnd)) {
return;
}
/*
* This window might be owned by a desktop-less service
*/
if (pti->rpdesk == NULL) {
return;
}
ShowMeTheWindows:
/*
* We're going to count all the windows owned by this pti
* that should be included in cVisWindows
*/
pwndNext = pti->rpdesk->pDeskInfo->spwnd;
/*
* If this is a top level window, start with the first child.
* If not, it should be a desktop thread window
*/
if (pwndNext == pwnd->spwndParent) {
pwndNext = pwndNext->spwndChild;
} else if (pwndNext->spwndParent != pwnd->spwndParent) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: Non top level window:%#lx", pwnd);
return;
}
if (fShowMeTheWindows) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: Start window walk at:%#lx", pwndNext);
}
/*
* Count the visble-not-minimized windows owned by this pti
*/
while (pwndNext != NULL) {
if (pti == GETPTI(pwndNext)) {
if (fShowMeTheWindows) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: pwndNext:%#lx", pwndNext);
}
if (!TestwndChild(pwndNext)
&& !TestWF(pwndNext, WFMINIMIZED)
&& TestWF(pwndNext, WFVISIBLE)) {
uVisWindows++;
if (fShowMeTheWindows) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: Counted:%#lx", pwndNext);
}
}
}
pwndNext = pwndNext->spwndNext;
}
/*
* It must match.
*/
if (pti->cVisWindows != uVisWindows) {
RIPMSG2(RIP_ERROR, "VerifycVisWindows: pti->cVisWindows:%#lx. uVisWindows:%#lx",
pti->cVisWindows, uVisWindows);
if (!fShowMeTheWindows) {
fShowMeTheWindows = TRUE;
uVisWindows = 0;
goto ShowMeTheWindows;
}
}
}
#endif
VOID IncVisWindows(
PWND pwnd)
{
if (!TestwndChild(pwnd) && !TestWF(pwnd, WFMINIMIZED))
GETPTI(pwnd)->cVisWindows++;
#ifdef DEBUG
VerifycVisWindows(pwnd);
#endif
}
VOID DecVisWindows(
PWND pwnd)
{
if (!TestwndChild(pwnd) && !TestWF(pwnd, WFMINIMIZED))
GETPTI(pwnd)->cVisWindows--;
#ifdef DEBUG
VerifycVisWindows(pwnd);
#endif
}
/***************************************************************************\
* SetMiminize
*
* This routine must be used to flip the WS_MIMIMIZE style bit.
* It adjusts cVisWindows count if appropriate.
*
* 06/06/96 GerardoB Created
\***************************************************************************/
VOID SetMinimize(
PWND pwnd,
UINT uFlags)
{
#ifdef DEBUG
BOOL fCountHack;
#endif
/*
* Note that Dec and IncVisWindows check the WFMINIMIZED flag, so the order
* in which we set/clear the flag and call these functions is important
* If the window is not WFVISIBLE, cVisWindows must not change.
*/
if (uFlags & SMIN_SET) {
UserAssert(!TestWF(pwnd, WFMINIMIZED));
if (TestWF(pwnd, WFVISIBLE)) {
/*
* Decrement the count because the window is not minimized
* and visible, and we're about to mark it as minimized
*/
#ifdef DEBUG
/*
* DEBUG HACK: Since the window is not marked as minimized yet,
* VerifycVisWindows will get upset about this. So let's
* fix up the count if needed.
*/
fCountHack = !TestwndChild(pwnd);
if (fCountHack) {
UserAssert(GETPTI(pwnd)->cVisWindows != 0);
GETPTI(pwnd)->cVisWindows++;
}
#endif
DecVisWindows(pwnd);
#ifdef DEBUG
if (fCountHack) {
GETPTI(pwnd)->cVisWindows--;
}
#endif
}
SetWF(pwnd, WFMINIMIZED);
} else {
UserAssert(TestWF(pwnd, WFMINIMIZED));
ClrWF(pwnd, WFMINIMIZED);
if (TestWF(pwnd, WFVISIBLE)) {
/*
* Increment the count because the window is visible
* and it's no longer marked as minimized
*/
IncVisWindows(pwnd);
}
}
}
/***************************************************************************\
* SetVisible
*
* This routine must be used to set or clear the WS_VISIBLE style bit.
* It also handles the setting or clearing of the WF_TRUEVIS bit.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SetVisible(
PWND pwnd,
UINT flags)
{
if (flags & SV_SET) {
SetWF(pwnd, WFVISIBLE);
if (!TestWF(pwnd, WFDESTROYED))
IncVisWindows(pwnd);
} else {
ClrWF(pwnd, WFVISIBLE);
if (flags & SV_CLRFTRUEVIS)
ClrFTrueVis(pwnd);
if (!TestWF(pwnd, WFDESTROYED))
DecVisWindows(pwnd);
}
}
/***************************************************************************\
* IsMaxedRect
*
* Determines if a window is "maximizing" to a certain area
*
* History:
\***************************************************************************/
BOOL IsMaxedRect(
LPRECT lprcWithin,
LPRECT lprcMaybe)
{
return((lprcMaybe->left <= lprcWithin->left) &&
(lprcMaybe->top <= lprcWithin->top) &&
(lprcMaybe->right >= lprcWithin->right - lprcWithin->left) &&
(lprcMaybe->bottom >= lprcWithin->bottom - lprcWithin->top));
}
/***************************************************************************\
* CheckFullScreen
*
* Sees if a window is really fullscreen or just a maximized window in
* disguise. If the latter, it will be forced to the proper maximized
* size.
*
* This is called from both CalcValidRects() and CreateWindowEx().
*
* The LPRECT passed in isn't a real rect; the top & left are OK but
* the right & bottom are really width/height not end coords.
*
* History:
\***************************************************************************/
BOOL xxxCheckFullScreen(
PWND pwnd,
LPRECT lprc)
{
BOOL fYielded = FALSE;
/*
* SINCE THIS IS ONLY CALLED IN 2 PLACES, make the checks there
* instead of the overhead of calling this function in time critical
* places.
*
* If 3 or more places call it, put the child/toolwindow checks here
*/
UserAssert(!TestWF(pwnd, WFCHILD));
UserAssert(!TestWF(pwnd, WEFTOOLWINDOW));
if (IsMaxedRect(&(gpsi->rcWork), lprc)) {
int dxy;
if (TestWF(pwnd, WFMAXIMIZED) &&
TestWF(pwnd, WFMAXBOX) &&
(TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION))) {
if ((lprc->top + SYSMET(CYCAPTION) <= gpDispInfo->rcScreen.top) &&
(lprc->bottom >= gpDispInfo->rcScreen.bottom + SYSMET(CYCAPTION))) {
if (!TestWF(pwnd, WFFULLSCREEN)) {
dxy = gpsi->rcWork.left - lprc->left;
if (lprc->left > gpDispInfo->rcScreen.left)
lprc->left = gpDispInfo->rcScreen.left - dxy;
if (lprc->right < gpDispInfo->rcScreen.right)
lprc->right = gpDispInfo->rcScreen.right + 2*dxy;
fYielded = xxxAddFullScreen(pwnd);
}
} else {
int iBottom;
if (TestWF(pwnd, WFFULLSCREEN) && xxxRemoveFullScreen(pwnd))
fYielded = TRUE;
dxy = gpsi->rcWork.left ? lprc->top : lprc->left;
lprc->left = gpsi->rcWork.left + dxy;
lprc->top = gpsi->rcWork.top + dxy;
dxy *= 2;
lprc->right = gpsi->rcWork.right - gpsi->rcWork.left - dxy;
/*
* B#14012 save QuickLink II that wants 4 pixels hanging off
* the screen for every edge except the bottom edge, which
* they only want to overhang by 2 pixels -- jeffbog 5/17/95
*/
iBottom = gpsi->rcWork.bottom - gpsi->rcWork.top - dxy;
lprc->bottom = min(iBottom, lprc->bottom);
}
} else if (IsMaxedRect(&gpDispInfo->rcScreen, lprc)) {
fYielded = xxxAddFullScreen(pwnd);
}
} else {
fYielded = xxxRemoveFullScreen(pwnd);
}
return(fYielded);
}
/***************************************************************************\
* ClrFTrueVis
*
* Called when making a window invisible. This routine destroys any update
* regions that may exist, and clears the WF_TRUEVIS of all windows below
* the passed in window.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID ClrFTrueVis(
PWND pwnd)
{
/*
* Destroy pwnd and its children's update regions.
* We do this here to guarantee that a hidden window
* and its children don't have update regions.
*
* This fixes bugs when destroying windows that have
* update regions (SendDestroyMessages) among others
* and allows us to simplify SetParent(). This was
* deemed better than hacking DoPaint() and/or
* DestroyWindow().
*
* We can stop recursing when we find a window that doesn't
* have the visible bit set, because by definition it won't
* have any update regions below it (this routine will have been called)
*/
if (NEEDSPAINT(pwnd)) {
if (pwnd->hrgnUpdate > MAXREGION)
GreDeleteObject(pwnd->hrgnUpdate);
ClrWF(pwnd, WFINTERNALPAINT);
pwnd->hrgnUpdate = NULL;
DecPaintCount(pwnd);
}
for (pwnd = pwnd->spwndChild; pwnd != NULL; pwnd = pwnd->spwndNext) {
/*
* pwnd->fs &= ~WF_TRUEVIS;
*/
if (TestWF(pwnd, WFVISIBLE))
ClrFTrueVis(pwnd);
}
}
/***************************************************************************\
* OffsetChildren
*
* Offsets the window and client rects of all children of hwnd.
* Also deals with the children's update regions and SPB rects.
*
* History:
* 22-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID OffsetChildren(
PWND pwnd,
int dx,
int dy,
LPRECT prcHitTest)
{
RECT rc;
for (pwnd = pwnd->spwndChild; pwnd; pwnd = pwnd->spwndNext) {
/*
* Skip windows that don't intersect prcHitTest...
*/
if (HIWORD(prcHitTest) &&
!IntersectRect(&rc, prcHitTest, &pwnd->rcWindow)) {
continue;
}
pwnd->rcWindow.left += dx;
pwnd->rcWindow.right += dx;
pwnd->rcWindow.top += dy;
pwnd->rcWindow.bottom += dy;
pwnd->rcClient.left += dx;
pwnd->rcClient.right += dx;
pwnd->rcClient.top += dy;
pwnd->rcClient.bottom += dy;
if (pwnd->hrgnUpdate > MAXREGION)
GreOffsetRgn(pwnd->hrgnUpdate, dx, dy);
/*
* Change position of window region, if it has one
*/
if (pwnd->hrgnClip != NULL)
GreOffsetRgn(pwnd->hrgnClip, dx, dy);
if (TestWF(pwnd, WFHASSPB))
OffsetRect(&(FindSpb(pwnd))->rc, dx, dy);
if (pwnd->spwndChild)
OffsetChildren(pwnd, dx, dy, prcHitTest);
}
}
/***************************************************************************\
* SetWindowRgn
*
* Parameters:
* hwnd -- Window handle
* hrgn -- Region to set into window. NULL can be accepted.
* fRedraw -- TRUE to go through SetWindowPos() and calculate
* update regions correctly. If the window is visible
* this will usually be TRUE.
*
* Returns:
* TRUE for success, FALSE for failure
*
* Comments:
* This is a very simple routine to set a window region. It goes through
* SetWindowPos() to get perfect update region calculation, and to deal
* with other related issues like vis rgn change & dc invalidation,
* display lock holding, spb invalidation, etc. Also since it sends
* WM_WINDOWPOSCHANGING & WM_WINDOWPOSCHANGED, we'll be able to expand
* SetWindowPos() in the future to take hrgns directly for efficient
* window state change control (like setting the rect and region at
* the same time, among others) without harming compatibility.
*
* hrgn is in window rect coordinates (not client rect coordinates).
* Once set, hrgn is owned by the system. A copy is not made!
*
* 30-Jul-1994 ScottLu Created.
\***************************************************************************/
#define SWR_FLAGS_REDRAW (SWP_NOCHANGE | SWP_FRAMECHANGED | SWP_NOACTIVATE)
#define SWR_FLAGS_NOREDRAW (SWP_NOCHANGE | SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREDRAW)
BOOL xxxSetWindowRgn(
PWND pwnd,
HRGN hrgn,
BOOL fRedraw)
{
PSMWP psmwp;
HRGN hrgnClip = NULL;
BOOL bRet = FALSE;
/*
* Validate the region handle. We did this for 3.51, so
* we better do it for later versions. Our validation will
* make a copy of the clip-rgn and send it through to the
* SetWIndowRgn code. Once this is set in the kernel, we
* will return to the client and the old region will be deleted
* there.
*
* If the region passed in is NULL, then we get rid of the
* current retion. Map it to MAXREGION so that SetWindowPos()
* can tell this is what the caller wants.
*/
if (hrgn) {
if ((hrgnClip = UserValidateCopyRgn(hrgn)) == NULL) {
#ifdef DEBUG
RIPMSG0(RIP_WARNING, "xxxSetWindowRgn: Failed to create region!");
#endif
goto swrClean;
}
} else {
hrgnClip = MAXREGION;
}
/*
* Get a psmwp, and put the region in it, correctly offset.
* Use SWP_FRAMECHANGED with acts really as a "empty" SetWindowPos
* that still sends WM_WINDOWPOSCHANGING and CHANGED messages.
* SWP_NOCHANGE ensures that we don't size, move, activate, zorder.
*/
if (psmwp = _BeginDeferWindowPos(1)) {
/*
* psmwp gets freed automatically if this routine fails.
*/
if (psmwp = _DeferWindowPos(
psmwp,
pwnd,
PWND_TOP,
0,
0,
0,
0,
fRedraw ? SWR_FLAGS_REDRAW : SWR_FLAGS_NOREDRAW)) {
/*
* Do the operation. Note that hrgn is still in window coordinates.
* SetWindowPos() will change it to screen coordinates before
* selecting into the window.
*/
psmwp->acvr[0].hrgnClip = hrgnClip;
bRet = xxxEndDeferWindowPosEx(psmwp, FALSE);
}
}
/*
* If the call failed, then delete our region we created. A FALSE
* return means it should've never made it to the xxxSelectWindowRgn
* call, so everything should be as it was.
*/
if (!bRet && (hrgnClip != MAXREGION)) {
swrClean:
GreDeleteObject(hrgnClip);
}
return bRet;
}
/***************************************************************************\
* SelectWindowRgn
*
* This routine does the work of actually selecting in the window region.
*
* 30-Jul-1994 ScottLu Created.
\***************************************************************************/
BOOL SelectWindowRgn(
PWND pwnd,
HRGN hrgnClip)
{
/*
* If there is a region already there, delete it because
* a new one is being set.
*/
if (pwnd->hrgnClip != NULL) {
GreDeleteObject(pwnd->hrgnClip);
pwnd->hrgnClip = NULL;
}
/*
* NULL or MAXREGION means "set to NULL". If we have a real region,
* use it. USER needs to own it, and it needs to be in screen
* coordinates.
*/
if (hrgnClip > MAXREGION) {
GreSetRegionOwner(hrgnClip, OBJECT_OWNER_PUBLIC);
GreOffsetRgn(hrgnClip, pwnd->rcWindow.left, pwnd->rcWindow.top);
pwnd->hrgnClip = hrgnClip;
}
return TRUE;
}