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

6366 lines
206 KiB

/****************************** Module Header ******************************\
* Module Name: swp.c
*
* Copyright (c) 1985 - 1999, 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
VOID FixBogusSWP(PWND pwnd, int * px, int * py, int cx, int cy, UINT flags);
VOID PreventInterMonitorBlts(PCVR pcvr);
/***************************************************************************\
* DBGCheckSMWP
*
* SMWP can be a HM object, a cached structure or just a pool allocation
*
* History:
* 05/21/98 GerardoB Created.
\***************************************************************************/
#if DBG
VOID DBGCheckSMWP(
PSMWP psmwp)
{
if (psmwp->bHandle) {
UserAssert(psmwp->head.h != NULL);
UserAssert(psmwp == HtoPqCat(PtoHq(psmwp)));
UserAssert(psmwp != &gSMWP);
} else {
UserAssert((psmwp->head.h == NULL) && (psmwp->head.cLockObj == 0));
if (psmwp == &gSMWP) {
UserAssert(TEST_PUDF(PUDF_GSMWPINUSE));
}
}
UserAssert(psmwp->ccvr <= psmwp->ccvrAlloc);
UserAssert(psmwp->acvr != NULL);
}
#else
#define DBGCheckSMWP(psmwp)
#endif // DBG
/***************************************************************************\
* DestroySMWP
*
* Destroys an SMWP object.
*
* History:
* 24-Feb-1997 adams Created.
\***************************************************************************/
VOID DestroySMWP(
PSMWP psmwp)
{
BOOL fFree;
CheckCritIn();
DBGCheckSMWP(psmwp);
/*
* First mark the object for destruction. This tells the locking code
* that we want to destroy this object when the lock count goes to 0.
* If this returns FALSE, we can't destroy the object yet.
*/
if (psmwp->bHandle) {
if (!HMMarkObjectDestroy(psmwp)) {
return;
}
fFree = TRUE;
} else {
/*
* Is this the global cached structure?
*/
fFree = (psmwp != &gSMWP);
}
if (psmwp->acvr) {
/*
* Free any hrgnInterMonitor stuff we accumulated.
*/
PCVR pcvr;
int ccvr;
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (pcvr->hrgnInterMonitor != NULL) {
GreDeleteObject(pcvr->hrgnInterMonitor);
}
}
if (fFree) {
UserFreePool(psmwp->acvr);
}
}
/*
* Ok to destroy ... Free the handle (which will free the object
* and the handle).
*/
if (psmwp->bHandle) {
HMFreeObject(psmwp);
} else if (fFree) {
UserFreePool(psmwp);
} else {
UserAssert(TEST_PUDF(PUDF_GSMWPINUSE));
CLEAR_PUDF(PUDF_GSMWPINUSE);
/*
* If acvr grew too much, shrink it.
* Don't use realloc since we don't care about the left over data
* [msadek], should this be ">=8" since we usually grow it from 4->8 in
* _DeferWindowPos?
*/
if (psmwp->ccvrAlloc > 8) {
PCVR pcvr = UserAllocPool(4 * sizeof(CVR), TAG_SWP);
if (pcvr != NULL) {
UserFreePool(psmwp->acvr);
psmwp->acvr = pcvr;
psmwp->ccvrAlloc = 4;
}
}
}
}
#define MW_FLAGS_REDRAW (SWP_NOZORDER | SWP_NOACTIVATE)
#define MW_FLAGS_NOREDRAW (SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW)
/***************************************************************************\
* MoveWindow (API)
*
*
* History:
* 25-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
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;
}
}
/***************************************************************************\
* AllocateCvr
*
* History:
* 05/20/98 GerardoB Extracted from old _BeginDeferWindowPos
\***************************************************************************/
BOOL AllocateCvr(
PSMWP psmwp,
int cwndHint)
{
PCVR acvr;
UserAssert(cwndHint != 0);
if (cwndHint > (INT_MAX / sizeof(CVR))) {
return FALSE;
}
if (psmwp == &gSMWP) {
UserAssert(psmwp->bHandle == FALSE);
acvr = (PCVR)UserAllocPool(sizeof(CVR) * cwndHint, TAG_SWP);
} else {
acvr = (PCVR)UserAllocPoolWithQuota(sizeof(CVR) * cwndHint, TAG_SWP);
}
if (acvr == NULL) {
return FALSE;
}
/*
* Initialize psmwp related fields.
* CVR array is initialized by _DeferWindowPos
*/
psmwp->acvr = acvr;
psmwp->ccvrAlloc = cwndHint;
psmwp->ccvr = 0;
return TRUE;
}
/***************************************************************************\
* InternalBeginDeferWindowPos
*
* History:
* 05/20/98 GerardoB Created
\***************************************************************************/
PSMWP InternalBeginDeferWindowPos(
int cwndHint)
{
PSMWP psmwp;
CheckCritIn();
/*
* If gSMWP in being used, allocate one.
* Note that SMWP is zero init but CVR is not; _DeferWindowPos initializes it.
*/
if (TEST_PUDF(PUDF_GSMWPINUSE) || (cwndHint > gSMWP.ccvrAlloc)) {
psmwp = (PSMWP)UserAllocPoolWithQuotaZInit(sizeof(SMWP), TAG_SWP);
if (psmwp == NULL) {
return NULL;
}
if (!AllocateCvr(psmwp, cwndHint)) {
UserFreePool(psmwp);
return NULL;
}
} else {
SET_PUDF(PUDF_GSMWPINUSE);
psmwp = &gSMWP;
RtlZeroMemory(&gSMWP, FIELD_OFFSET(SMWP, ccvrAlloc));
UserAssert(gSMWP.ccvr == 0);
UserAssert(gSMWP.acvr != NULL);
}
DBGCheckSMWP(psmwp);
return psmwp;
}
/***************************************************************************\
* BeginDeferWindowPos (API)
*
* This must be called from the client side only. Internally we should
* call InternalBeginDeferWindowPos to avoid going through the handle table
* and perhaps even use the cached strucuture.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
PSMWP _BeginDeferWindowPos(
int cwndHint)
{
PSMWP psmwp;
psmwp = (PSMWP)HMAllocObject(PtiCurrent(), NULL, TYPE_SETWINDOWPOS, sizeof(SMWP));
if (psmwp == NULL) {
return NULL;
}
if (cwndHint == 0) {
cwndHint = 8;
}
if (!AllocateCvr(psmwp, cwndHint)) {
HMFreeObject(psmwp);
return NULL;
}
psmwp->bHandle = TRUE;
DBGCheckSMWP(psmwp);
return psmwp;
}
/***************************************************************************\
* PWInsertAfter
*
* History:
* 04-Mar-1992 MikeKe From win31
\***************************************************************************/
PWND PWInsertAfter(
HWND hwnd)
{
PWND pwnd;
/*
* HWND_GROUPTOTOP and HWND_TOPMOST are the same thing.
*/
switch ((ULONG_PTR)hwnd) {
case (ULONG_PTR)HWND_TOP:
case (ULONG_PTR)HWND_BOTTOM:
case (ULONG_PTR)HWND_TOPMOST:
case (ULONG_PTR)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 ((ULONG_PTR)pwnd) {
case (ULONG_PTR)HWND_TOP:
case (ULONG_PTR)HWND_BOTTOM:
case (ULONG_PTR)HWND_TOPMOST:
case (ULONG_PTR)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;
DBGCheckSMWP(psmwp);
if (psmwp->ccvr + 1 > psmwp->ccvrAlloc) {
/*
* Make space for 4 more windows.
*/
DWORD dwNewAlloc = psmwp->ccvrAlloc + 4;
if (psmwp == &gSMWP) {
UserAssert(psmwp->bHandle == FALSE);
pcvr = (PCVR)UserReAllocPoolWithTag(psmwp->acvr,
psmwp->ccvrAlloc * sizeof(CVR),
sizeof(CVR) * dwNewAlloc,
TAG_SWP);
} else {
pcvr = (PCVR)UserReAllocPoolWithQuota(psmwp->acvr,
psmwp->ccvrAlloc * sizeof(CVR),
sizeof(CVR) * dwNewAlloc,
TAG_SWP);
}
if (pcvr == NULL) {
DestroySMWP(psmwp);
return NULL;
}
psmwp->acvr = pcvr;
psmwp->ccvrAlloc = dwNewAlloc;
}
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;
pcvr->hrgnInterMonitor = 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 pwndParent)
{
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)) {
BOOL fTopLevel = (pwnd->spwndParent == PWNDDESKTOP(pwnd));
/*
* Do not z-order destroyed windows
*/
if (TestWF(pwnd, WFDESTROYED))
return FALSE;
hwndInsertAfter = pcvr->pos.hwndInsertAfter;
/*
* If pwndParent is provided, we're about to link this window so we
* need to validate LinkWindow assumptions. We have to do this since
* we callback after determining hwndInsertAfter.
*/
if ((hwndInsertAfter == HWND_TOPMOST) ||
(hwndInsertAfter == HWND_NOTOPMOST)) {
if (!fTopLevel) {
return FALSE;
}
} else if (hwndInsertAfter == HWND_TOP) {
/*
* If pwnd is not topmost, the first child must not be topmost.
*/
if ((pwndParent != NULL) && fTopLevel
&& !FSwpTopmost(pwnd)
&& (pwndParent->spwndChild != NULL)
&& FSwpTopmost(pwndParent->spwndChild)) {
RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwnd is not SWPTopMost."
" pwnd:%#p. hwndInsertAfter:%#p",
pwnd, hwndInsertAfter);
return FALSE;
}
} else if (hwndInsertAfter != HWND_BOTTOM) {
/*
* Ensure pwndInsertAfter is valid
*/
if (((pwndInsertAfter = RevalidateHwnd(hwndInsertAfter)) == NULL) ||
TestWF(pwndInsertAfter, WFDESTROYED)) {
RIPERR1(ERROR_INVALID_HANDLE, RIP_WARNING, "Invalid hwndInsertAfter (%#p)", hwndInsertAfter);
return FALSE;
}
/*
* Ensure that pwndInsertAfter is a sibling of pwnd
*/
if (pwnd == pwndInsertAfter ||
pwnd->spwndParent != pwndInsertAfter->spwndParent) {
RIPMSG2(RIP_WARNING, "hwndInsertAfter (%#p) is not a sibling "
"of hwnd (%#p)", hwndInsertAfter, pcvr->pos.hwnd);
return FALSE;
}
/*
* Ensure proper topmost/nontopmost insert position
*/
if ((pwndParent != NULL) && fTopLevel) {
if (FSwpTopmost(pwnd)) {
/*
* Check if we're trying to insert a topmost window after a non-topmost one.
*/
if (!FSwpTopmost(pwndInsertAfter)) {
RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwndInsertAfter is not SWPTopMost."
" pwnd:%#p. pwndInsertAfter:%#p",
pwnd, pwndInsertAfter);
return FALSE;
}
} else {
/*
* Check if we're trying to insert a non-top most window
* between two top-most ones.
*/
if ((pwndInsertAfter->spwndNext != NULL)
&& FSwpTopmost(pwndInsertAfter->spwndNext)) {
RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwndInsertAfter->spwndNext is SWPTopMost."
" pwnd:%#p. pwndInsertAfter:%#p",
pwnd, pwndInsertAfter);
return FALSE;
}
}
}
}
/*
* Check that the parent hasn't changed.
*/
if (pwndParent != NULL) {
if (pwndParent != pwnd->spwndParent) {
RIPMSG3(RIP_WARNING, "ValidateWindowPos: parent has changed."
" pwnd:%#p. Old Parent:%#p. Current Parent:%#p",
pwnd, pwndParent, pwnd->spwndParent);
return FALSE;
}
}
}
return TRUE;
}
/***************************************************************************\
* IsStillWindowC
*
* Checks if window is valid HWNDC still, and child of proper dude.
*
* History:
\***************************************************************************/
BOOL IsStillWindowC(
HWND hwndc)
{
switch ((ULONG_PTR)hwndc) {
case (ULONG_PTR)HWND_TOP:
case (ULONG_PTR)HWND_BOTTOM:
case (ULONG_PTR)HWND_TOPMOST:
case (ULONG_PTR)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 = RevalidateHwnd(psmwp->acvr[0].pos.hwnd);
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, NULL)) {
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(RevalidateCatHwnd(pcvr->pos.hwnd));
pwnd = PWCat(pcvr->pos.hwnd); // Known to be valid at this point.
/*
* Don't z-order a destroyed window.
*/
if (TestWF(pwnd, WFDESTROYED)) {
return TRUE;
}
UserAssert((HMPheFromObject(pwnd)->bFlags & HANDLEF_DESTROY) == 0);
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;
}
}
/*
* NTRAID#NTBUG9-345299-2001/04/09-jasonsch
*
* If we get to here, pwnd is not in the sibling list.
* REALLY BAD NEWS!
*
* Changing this to a warning since we seem to handle it fine
* and there's a shell dude hitting this. Need to revisit
* this in Blackcomb.
*/
RIPMSG1(RIP_WARNING, "Pwnd 0x%p not found in sibling list.", pwnd);
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 xWindowOldLogical;
int yWindowOld;
int cxWindowOld;
int cyWindowOld;
TL tlpwndParent;
TL tlpwnd;
BOOL fSetZeroDx=FALSE;
BOOL fMirroredParent = FALSE;
/*
* 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(HMRevalidateCatHandle(PtoH(pwndParent)));
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, (LPARAM)&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.
*/
#if DBG
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;
yWindowOld = pwnd->rcWindow.top;
xWindowOldLogical = xWindowOld;
if (pwndParent != PWNDDESKTOP(pwnd)) {
xWindowOld -= pwndParent->rcClient.left;
yWindowOld -= pwndParent->rcClient.top;
fMirroredParent = (TestWF(pwndParent, WEFLAYOUTRTL) && TestwndChild(pwnd));
if (fMirroredParent) {
xWindowOldLogical = pwndParent->rcClient.right - pwnd->rcWindow.right;
} else {
xWindowOldLogical = xWindowOld;
}
}
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 == xWindowOldLogical && pcvr->pos.y == yWindowOld) {
pcvr->pos.flags |= SWP_NOMOVE;
if (fMirroredParent) {
fSetZeroDx = TRUE;
}
}
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 = xWindowOldLogical;
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 (fMirroredParent) {
UserAssert(pwndParent != PWNDDESKTOP(pwnd));
pcvr->pos.x = (pwndParent->rcClient.right - pwndParent->rcClient.left) - pcvr->pos.x - pcvr->pos.cx;
}
/*
* 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;
}
/*
* Child windows inside a composited window can't use screen to
* screen bit copy because this can move translucent bits.
*/
if (!TestWF(pwnd, WEFCOMPOSITED) &&
GetStyleWindow(pwnd, WEFCOMPOSITED) != NULL) {
pcvr->pos.flags |= SWP_NOCOPYBITS;
}
/*
* 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, (PSIZERECT)&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
*/
GetRect(pwnd, &rcWindowOld, GRECT_WINDOW | GRECT_PARENTCOORDS);
/*
* Set up rcClientOld in parent relative coordinates
*/
GetRect(pwnd, &rcClientOld, GRECT_CLIENT | GRECT_PARENTCOORDS);
/*
* 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, (LPARAM)&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...
*/
if (TestWF(pwnd, WEFLAYOUTRTL)) {
pcvr->dxBlt = rcValidDst.right - rcValidSrc.right;
} else {
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.
*/
SetRectEmpty(&pcvr->rcBlt);
ThreadUnlock(&tlpwnd);
continue;
}
/*
* If we are just resizing this window without moving it and its parent
* is mirrored then no need to copy any bits (i.e. empty pcvr->rcBlt).
*/
if (fSetZeroDx) {
goto AllInvalid;
}
/*
* 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)) ^ (!!TestWF(pwnd, WEFLAYOUTRTL)))
rcValidDst.left += ((TestWF(pwnd, WEFLAYOUTRTL) && (cxSrc > cxDst)) ? (cxSrc-cxDst) : (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 = rcValidDst;
if (pwndParent != PWNDDESKTOP(pwnd)) {
OffsetRect(
&pcvr->rcBlt,
pwndParent->rcClient.left,
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) {
SetRectEmpty(&pcvr->rcBlt);
} else {
pcvr->rcBlt.left = pcvr->pos.x;
pcvr->rcBlt.top = pcvr->pos.y;
if (pwndParent != PWNDDESKTOP(pwnd)) {
pcvr->rcBlt.left += pwndParent->rcClient.left;
pcvr->rcBlt.top += pwndParent->rcClient.top;
}
pcvr->rcBlt.right = pcvr->rcBlt.left + pcvr->pos.cx;
pcvr->rcBlt.bottom = pcvr->rcBlt.top + pcvr->pos.cy;
}
/*
* Offset everything by the distance the window moved.
*/
if (TestWF(pwnd, WEFLAYOUTRTL)) {
pcvr->dxBlt = (pcvr->pos.x + pcvr->pos.cx) - (xWindowOld + cxWindowOld);
} else {
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 + pcvr->dxBlt;
pcvr->yClientNew = pwnd->rcClient.top + pcvr->dyBlt;
if (pwndParent != PWNDDESKTOP(pwnd)) {
pcvr->xClientNew -= pwndParent->rcClient.left;
pcvr->yClientNew -= pwndParent->rcClient.top;
}
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;
#if DBG
CheckLock(pwnd);
switch((ULONG_PTR)pwndInsertAfter) {
case 0x0000FFFF:
case (ULONG_PTR)HWND_TOPMOST:
case (ULONG_PTR)HWND_NOTOPMOST:
case (ULONG_PTR)HWND_TOP:
case (ULONG_PTR)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 (flags & SWP_SHOWWINDOW) {
SetWF(pwnd, WEFGHOSTMAKEVISIBLE);
} else {
ClrWF(pwnd, WEFGHOSTMAKEVISIBLE);
}
}
/*
* MULTIMONITOR HACKS
*
* if a app is centering or cliping a hidden owned window
* to the primary monitor we should center the window to the owner
*
* this makes apps that center/position their own dialogs
* work when the app is on a secondary monitor.
*/
if ( !TestWF(pwnd, WFWIN50COMPAT) &&
gpDispInfo->cMonitors > 1 &&
!(flags & SWP_NOMOVE) &&
!TestWF(pwnd, WFCHILD) &&
!TestWF(pwnd, WFVISIBLE) &&
(TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION)) &&
pwnd->spwndOwner &&
TestWF(pwnd->spwndOwner, WFVISIBLE) &&
!IsRectEmpty(&pwnd->spwndOwner->rcWindow)) {
FixBogusSWP(pwnd, &x, &y, cx, cy, flags);
}
if (!(psmwp = InternalBeginDeferWindowPos(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.
*
* Let's stop doing this for NT5 in an effort to close the number
* of ways applications can force a foreground change. This is not
* quite needed anyway, because:
* -If the current thread is already in the foreground, then it doesn't need
* the TIF_ALLOWFOREGROUNDACTIVATE to make a foreground change.
* -Since FRemoveForegroundActive removes this bit, the current thread
* will lose it anyway during the xxxActivateWindow call.
* -But xxxActivateWindow will set it back anyway because we're activating
* a window from a different queue.
* -The destination window/thread will take the foreground
* as a result of the xxxActivateWindow call, hence it doesn't
* need the bit on (if you're in the foreground, you don't need it).
*/
#ifdef DONTDOTHISANYMORE
if ((pti->pq == gpqForeground) && (pti != GETPTI(pwndNewActive))) {
/*
* Allow foreground activate on the source and dest.
*/
pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxSwpActivate set TIF %#p", pti);
GETPTI(pwndNewActive)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxSwpActivate set TIF %#p", GETPTI(pwndNewActive));
}
#endif
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;
}
/***************************************************************************\
* xxxImeWindowPosChanged
*
* Send IME private message to update the composition window position
*
\***************************************************************************/
VOID xxxImeWindowPosChanged(
PSMWP psmwp)
{
PBWL pbwl;
PHWND phwnd;
PWND pwndDesktop = _GetDesktopWindow();
PTHREADINFO ptiCurrent = PtiCurrent();
if (pwndDesktop == NULL) {
return;
}
pbwl = BuildHwndList(pwndDesktop->spwndChild, BWL_ENUMLIST, ptiCurrent);
if (pbwl == NULL) {
return;
}
for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; ++phwnd) {
PWND pwndIme = ValidateHwnd(*phwnd);
TAGMSG1(DBGTAG_IMM, "ImePosC: pwndIme=%p", pwndIme);
/*
* If the thread is going away, just bail out.
*/
if (ptiCurrent->TIF_flags & TIF_INCLEANUP) {
break;
}
if (pwndIme && pwndIme->head.pti == ptiCurrent &&
pwndIme->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
HWND hwnd;
PWND pwnd;
TAGMSG1(DBGTAG_IMM, "ImePosC: OK, pwndIme=%p is one of us.", pwndIme);
try {
hwnd = ProbeAndReadStructure(((PIMEWND)pwndIme)->pimeui, IMEUI).hwndIMC;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
continue;
}
pwnd = RevalidateHwnd(hwnd);
TAGMSG2(DBGTAG_IMM, "ImePosC: hwndImc=%p and its pwnd=%p", hwnd, pwnd);
/*
* Search upward
*/
while (pwnd && pwnd != pwndDesktop) {
PCVR pcvr;
int ccvr;
hwnd = HWq(pwnd);
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (hwnd == pcvr->pos.hwnd) {
TAGMSG1(DBGTAG_IMM, "ImePosC: pwnd=%p in the SWP list.", pwnd);
/*
* Send this private message if the window's size changes
* or the window moves. I.e.
* when (flag & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE).
*/
if (~pcvr->pos.flags & (SWP_NOSIZE | SWP_NOMOVE)) {
TL tl;
TAGMSG1(DBGTAG_IMM, "ImePosC: pwnd=%p is gonna move or resize.", pwnd);
ThreadLockAlwaysWithPti(ptiCurrent, pwndIme, &tl);
xxxSendMessage(pwndIme, WM_IME_SYSTEM, IMS_WINDOWPOS, 0);
ThreadUnlock(&tl);
}
break;
}
}
if (ccvr >= 0) {
break;
}
pwnd = pwnd->spwndParent;
}
}
}
FreeHwndList(pbwl);
}
/***************************************************************************\
* 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);
if (TestCF(pwnd, CFDROPSHADOW) && !(GetAppCompatFlags2ForPti(GETPTI(pwnd), VERMAX) & GACF2_NOSHADOW)) {
if (pcvr->pos.flags & SWP_HIDEWINDOW) {
xxxRemoveShadow(pwnd);
} else if (pcvr->pos.flags & SWP_SHOWWINDOW) {
BOOL fAddShadow = TRUE;
/*
* We don't want to add a shadow to menus that are being slid
* out because they don't use AnimateWindow and do not create
* a window rgn to clip inside during the animation. This means
* that even if we keep the shadow in sync with the menu, it
* won't be visible because it is actually z-ordered below the
* menu.
*/
if ((GETFNID(pwnd) == FNID_MENU) && (!TestALPHA(MENUFADE)) && TestEffectUP(MENUANIMATION)) {
fAddShadow = FALSE;
}
if (fAddShadow) {
xxxAddShadow(pwnd);
}
} else {
if (!(pcvr->pos.flags & SWP_NOSIZE) ||
(pcvr->pos.flags & SWP_FRAMECHANGED)) {
UpdateShadowShape(pwnd);
} else if (!(pcvr->pos.flags & SWP_NOMOVE)) {
MoveShadow(pwnd);
}
if (!(pcvr->pos.flags & SWP_NOZORDER)) {
xxxUpdateShadowZorder(pwnd);
}
}
}
xxxSendMessage(pwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)&pcvr->pos);
/*
* Only send a shape change when moving/sizing/minimizing/restoring/
* maximizing (or framechange for NetMeeting to detect SetWindowRgn)
*/
if (!(pcvr->pos.flags & SWP_NOCLIENTMOVE) ||
!(pcvr->pos.flags & SWP_NOCLIENTSIZE) ||
(pcvr->pos.flags & SWP_STATECHANGE) ||
(pcvr->pos.flags & SWP_FRAMECHANGED)) {
xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
}
ThreadUnlock(&tlpwnd);
} // for (... pcvr ...)
if (IS_IME_ENABLED()) {
xxxImeWindowPosChanged(psmwp);
}
}
/***************************************************************************\
* 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.
\***************************************************************************/
VOID 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;
}
/*
* 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.
*/
if (!PostEventMessage(ptiT, ptiT->pq, QEVENT_SETWINDOWPOS, NULL, 0,
(WPARAM)psmwpNew, (LPARAM)ptiT)) {
// IANJA RIP only to catch what was previously a bug: psmwpNew not freed
RIPMSG1(RIP_WARNING, "PostEventMessage swp to pti %#p failed", ptiT);
UserFreePool(psmwpNew);
}
}
}
/***************************************************************************\
* 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 = InternalBeginDeferWindowPos(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;
/*
* Complete the MultWindowPos operation now that we're on the correct
* context.
*/
xxxEndDeferWindowPosEx(psmwp, FALSE);
/*
* Free the temp SMWP/CVR array.
*/
UserFreePool(psmwpT);
}
#define SWP_BOZO ( SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE )
/***************************************************************************\
* DBGValidateSibblingZOrder
*
* History:
* 04/01/98 GerardoB Created
\***************************************************************************/
#if DBG
VOID DBGValidateSibblingZOrder(
PWND pwndParent)
{
PWND pwndT = pwndParent->spwndChild;
/*
* Check that the sibbling list looks OK right now
* We don't really care about the z-order of message windows.
*/
if ((pwndT != NULL) && (pwndParent != PWNDMESSAGE(pwndParent))) {
BOOL fFoundNonTopMost = !TestWF(pwndT, WEFTOPMOST);
while (pwndT != NULL) {
if (TestWF(pwndT, WEFTOPMOST)) {
UserAssert(!fFoundNonTopMost);
} else {
fFoundNonTopMost = TRUE;
}
pwndT = pwndT->spwndNext;
}
}
}
#else
#define DBGValidateSibblingZOrder(pwndParent)
#endif // DBG
/***************************************************************************\
* zzzChangeStates
*
* History:
* 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID zzzChangeStates(
PWND pwndParent,
PSMWP psmwp)
{
int ccvr;
PCVR pcvr;
PWND pwnd;
TL tlpwnd;
TL tlpwndParent;
int czorder = 0;
BEGINATOMICCHECK();
ThreadLockAlways(pwndParent, &tlpwndParent);
/*
* Check that the sibbling list looks OK right now
*
* Here's the reason why this DBG code is commented out:
* Owned windows are always expected to be on top of the owner.
* However, an app can call SetWindowPos and insert the ownee after
* the owner. IME somehow does this too.
* This causes us to have A to be inserted after B and later in the
* windowpos array, B to be inserted somewhere else. Hence, A won't be in the
* expected position, because B will be moved after A is inserted.
* In other words, a window in hwndInsertAfter must not appear later
* as a hwnd to be z-ordered. Ownees below owners cause this situation.
*/
// DBGValidateSibblingZOrder(pwndParent);
/*
* Now change the window states
*/
for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
if (pcvr->pos.hwnd == NULL)
continue;
UserAssert(0 == (pcvr->pos.flags & SWP_NOTIFYALL));
pwnd = RevalidateHwnd(pcvr->pos.hwnd);
if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
RIPMSG0(RIP_WARNING, "zzzChangeStates: Window went away in middle");
pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
pcvr->pos.hwnd = NULL;
}
#if DBG
/*
* This can happen when we get re-entered during a callback or mulitple
* threads are z-ordering the same window. The tray does stuff like this.
* We would need to keep the toggle state in the windowpos structure to
* have each call have its own state.
*/
if (TestWF(pwnd, WFTOGGLETOPMOST) && (pcvr->pos.flags & SWP_NOZORDER)) {
RIPMSG0(RIP_WARNING, "zzzChangeState: WFTOGGLETOPMOST should not be set");
}
#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;
BOOL fRecreateRedirectionBitmap = FALSE;
int dxWindow, dyWindow, xOldWindow, yOldWindow;
if (TestWF(pwnd, WEFPREDIRECTED)) {
int cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
int cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
if (cx != pcvr->pos.cx || cy != pcvr->pos.cy) {
fRecreateRedirectionBitmap = TRUE;
}
}
/*
* Set up the new window and client rectangles.
*/
xOldWindow = pwnd->rcWindow.left;
yOldWindow = pwnd->rcWindow.top;
pwnd->rcWindow.left = pcvr->pos.x;
pwnd->rcWindow.top = pcvr->pos.y;
if (pwndParent != PWNDDESKTOP(pwnd)) {
pwnd->rcWindow.left += pwndParent->rcClient.left;
pwnd->rcWindow.top += pwndParent->rcClient.top;
}
dxWindow = pwnd->rcWindow.left - xOldWindow;
dyWindow = pwnd->rcWindow.top - yOldWindow;
pwnd->rcWindow.right = pwnd->rcWindow.left + pcvr->pos.cx;
pwnd->rcWindow.bottom = pwnd->rcWindow.top + pcvr->pos.cy;
if (pwnd->rcWindow.right < pwnd->rcWindow.left) {
RIPMSG1(RIP_WARNING, "SWP: cx changed for pwnd %#p", pwnd);
pwnd->rcWindow.right = pwnd->rcWindow.left;
}
if (pwnd->rcWindow.bottom < pwnd->rcWindow.top) {
RIPMSG1(RIP_WARNING, "SWP: cy changed for pwnd %#p", pwnd);
pwnd->rcWindow.bottom = pwnd->rcWindow.top;
}
/*
* 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 - pcvr->xClientNew;
int dy = pcvr->dyBlt + pwnd->rcClient.top - pcvr->yClientNew;
if (pwndParent != PWNDDESKTOP(pwnd))
{
dx -= pwndParent->rcClient.left;
dy -= pwndParent->rcClient.top;
}
if ((dx | dy) != 0) {
pcaret->x += dx;
pcaret->y += dy;
}
}
/*
* Set up the new client rect
* coordinates provided.
*/
pwnd->rcClient.left = pcvr->xClientNew;
pwnd->rcClient.top = pcvr->yClientNew;
if (pwndParent != PWNDDESKTOP(pwnd))
{
pwnd->rcClient.left += pwndParent->rcClient.left;
pwnd->rcClient.top += pwndParent->rcClient.top;
}
pwnd->rcClient.right = pwnd->rcClient.left + pcvr->cxClientNew;
pwnd->rcClient.bottom = pwnd->rcClient.top + pcvr->cyClientNew;
/*
* If the window becomes smaller than the monitor, the system
* allows it to be moved (see SetSysMenu) and so we must remove
* the monitor region.
*/
if (TestWF(pwnd, WFMAXFAKEREGIONAL) && IsSmallerThanScreen(pwnd)) {
SelectWindowRgn(pwnd, NULL);
}
/*
* If the layered window is resizing, try to resize the
* redirection bitmap associated with it.
*/
if (fRecreateRedirectionBitmap) {
RecreateRedirectionBitmap(pwnd);
}
if ((dxWindow != 0) || (dyWindow != 0)) {
if ((pwnd->hrgnClip > HRGN_FULL) && (!TestWF(pwnd, WFMAXFAKEREGIONAL))) {
#ifdef LATER
/*
* LATER: The original USER code was offsetting the window
* region by dxBlt and dyBlt. This had problems for using
* window regions when hiding and showing the menus, so
* dxWindow and dyWindow were added (correctly). However,
* we should be aware of all of the places these values
* don't agree to make sure that we don't introduce
* regressions. Unfortunately in Whistler, we were
* periodically getting too much spew, and didn't have time
* to fully track down each of these cases.
*/
/*
* Change position of window region, if it has one
* and it isn't a monitor region for a maximized window
*/
if ((dxWindow != pcvr->dxBlt) || (dyWindow != pcvr->dyBlt)) {
/*
* If not moving by the same amount as the PCVR indicates,
* give a warning. This is normal when calling xxxSetMenu(),
* but we need to know if it is called in other situations.
*/
RIPMSG1(RIP_WARNING, "SWP: (dxWindow != dxBlt) || (dyWindow != dyBlt) for pwnd %#p", pwnd);
}
#endif
GreOffsetRgn(pwnd->hrgnClip, dxWindow, dyWindow);
}
}
/*
* 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) {
if (pwnd->hrgnUpdate > HRGN_FULL) {
GreOffsetRgn(pwnd->hrgnUpdate, pcvr->dxBlt, pcvr->dyBlt);
}
OffsetChildren(pwnd, pcvr->dxBlt, pcvr->dyBlt, NULL);
/*
* Change the position of the sprite associated with
* this window.
*/
if (TestWF(pwnd, WEFLAYERED)) {
POINT ptPos = {pcvr->pos.x, pcvr->pos.y};
GreUpdateSprite(gpDispInfo->hDev, PtoHq(pwnd), NULL, NULL,
&ptPos, NULL, NULL, NULL, 0, NULL, 0, NULL);
}
}
}
/*
* Change the Z order if the flag is set. Revalidate
* hwndInsertAfter to make sure that it is still valid
*/
if (!(pcvr->pos.flags & SWP_NOZORDER)) {
if (ValidateWindowPos(pcvr, pwndParent)) {
UnlinkWindow(pwnd, pwndParent);
LinkWindow(pwnd,
PWInsertAfter(pcvr->pos.hwndInsertAfter),
pwndParent);
czorder++;
/*
* 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);
}
} else {
pcvr->pos.flags |= SWP_NOZORDER;
ClrWF(pwnd, WFTOGGLETOPMOST);
}
}
/*
* Handle SWP_HIDEWINDOW and SWP_SHOWWINDOW, by clearing or setting
* the WS_VISIBLE bit.
*/
UserAssert(pwndParent != NULL);
ThreadLockAlways(pwnd, &tlpwnd);
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)
zzzCalcStartCursorHide((PW32PROCESS)GETPTI(pwnd)->ppi, 5000);
/*
* Set the WS_VISIBLE bit.
*/
SetVisible(pwnd, SV_SET);
zzzWindowEvent(EVENT_OBJECT_SHOW, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
if (IsTrayWindow(pwnd)) {
#ifdef HUNGAPP_GHOSTING
if((GETFNID(pwnd) == FNID_GHOST)) {
if(TestWF(pwnd, WFFRAMEON)) {
psmwp->bShellNotify = TRUE;
pcvr->pos.flags |= SWP_NOTIFYACTIVATE;
}
}
else
#endif // HUNGAPP_GHOSTING
{
psmwp->bShellNotify = TRUE;
pcvr->pos.flags |= TestWF(pwnd, WFFRAMEON) ? SWP_NOTIFYACTIVATE|SWP_NOTIFYCREATE: SWP_NOTIFYCREATE;
}
} else if (TestWF(pwnd, WFFULLSCREEN)) {
/*
* Wake up the tray so it can notice that there is now
* a fullscreen visible window. This deals with bugs
* 32164, 141563, and 150217.
*/
psmwp->bShellNotify = TRUE;
pcvr->pos.flags |= SWP_NOTIFYFS;
}
/*
* If we're redrawing, create an SPB for this window if
* needed.
*/
if (!(pcvr->pos.flags & SWP_NOREDRAW) ||
(pcvr->pos.flags & SWP_CREATESPB)) {
/*
* 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.
*/
/*
* Make sure this window's desktop is on top !
*/
if (TestCF(pwnd, CFSAVEBITS) &&
pwnd->head.rpdesk == grpdeskRitInput) {
/*
* 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 people 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)
#ifdef HUNGAPP_GHOSTING
&& (GETFNID(pwnd) != FNID_GHOST)
#endif // HUNGAPP_GHOSTING
) {
psmwp->bShellNotify = TRUE;
pcvr->pos.flags |= SWP_NOTIFYDESTROY;
}
/*
* Clear the WS_VISIBLE bit.
*/
SetVisible(pwnd, SV_UNSET | SV_CLRFTRUEVIS);
zzzWindowEvent(EVENT_OBJECT_HIDE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
}
/*
* 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 (TestWF(pwnd, WFVISIBLE)) {
if ((pcvr->pos.flags & SWP_STATECHANGE) ||
(!TestWF(pwnd, WFWIN31COMPAT) && (pcvr->pos.flags & SWP_NOREDRAW))) {
SetWF(pwnd, WFSENDNCPAINT);
}
}
/*
* If this window has a clipping region set it now
*/
if (pcvr->hrgnClip != NULL) {
SelectWindowRgn(pwnd, pcvr->hrgnClip);
}
ThreadUnlock(&tlpwnd);
}
/*
* Check that the sibbling list looks OK now that we're done
*/
// DBGValidateSibblingZOrder(pwndParent);
if (czorder) {
zzzWindowEvent(EVENT_OBJECT_REORDER, pwndParent, OBJID_CLIENT, INDEXID_CONTAINER, 0);
}
ThreadUnlock(&tlpwndParent);
ENDATOMICCHECK();
}
/***************************************************************************\
* 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;
if (!SYSMET(SAMEDISPLAYFORMAT))
PreventInterMonitorBlts(pcvr);
pcvr->fsRE = 0;
pcvr->hrgnVisOld = CreateEmptyRgn();
if (pcvr->hrgnVisOld == NULL ||
!SwpCalcVisRgn(pwnd, pcvr->hrgnVisOld)) {
pcvr->fsRE = RE_VISOLD;
}
}
return (fChangeState ? cIter : 0);
}
/***************************************************************************\
* zzzBltValidBits
*
* NOTE: Although zzzBltValidBits 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. ghrgnValid = offset(hrgnSrc) & hrgnDst
*
* Valid area is the intersection of the destination with the source
* superimposed on the destination.
*
* 3.1 ghrgnValid = ghrgnValid - hrgnInterMonitor
*
* Subtract out any pieces that are moving across monitors.
*
* 4. ghrgnValid -= ghrgnValidSum
*
* 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. ghrgnInvalid = (hrgnSrc | hrgnDst) - ghrgnValid
*
* 6. ghrgnInvalid += RestoreSpb(ghrgnInvalid) (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 ghrgnInvalid. If the SPB valid region doesn't intersect
* ghrgnInvalid, 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 ghrgnValid!
*
* To correct for this, ghrgnValid is adjusted by subtracting off
* the ghrgnInvalid computed by RestoreSpb, if it modified it.
*
* 7. ghrgnInvalidSum |= ghrgnInvalid
*
* We save up the sum of all the invalid areas, and invalidate the
* whole schmear in one fell swoop at the end.
*
* 8. ghrgnValidSum |= ghrgnValid
*
* 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 zzzBltValidBits(
PSMWP psmwp)
{
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;
BOOL fInvalidateLayers = FALSE;
HDC hdcScreen = NULL;
DeferWinEventNotify();
GreLockDisplay(gpDispInfo->hDev);
/*
* 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
*/
zzzChangeStates(pwndParent, psmwp);
/*
* 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.
*
* Note, because IDC_MOVEBLT is set, final completion of WNDOBJ
* notification is delayed until GreClientRgnDone is called.
* This final notification does not happen until after the
* window move blts have completed.
*/
zzzInvalidateDCCache(pwndParent,
IDC_MOVEBLT |
(TestWF(pwndParent, WFCLIPCHILDREN) ?
IDC_CLIENTONLY : IDC_CHILDRENONLY));
/*
* Now, do the bltting or whatever that is required.
*/
fsSumEmpty = RE_VALIDSUM | RE_INVALIDSUM;
hrgnInvalidate = ghrgnInvalidSum;
/*
* 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;
/*
* Sprites should not be invalidated or cause invalidation.
*/
#ifdef REDIRECTION
if (TestWF(pwnd, WEFLAYERED) || TestWF(pwnd, WEFEXTREDIRECTED)) {
#else // REDIRECTION
if (TestWF(pwnd, WEFLAYERED)) {
#endif // REDIRECTION
if (GetRedirectionBitmap(pwnd) == NULL)
goto InvalidEmpty;
/*
* Sizing or showing uncovers new bits for a window, so
* do the normal invalidation in this case. When sizing makes a
* window smaller setting fInvalidateLayers to TRUE has the side
* effect of allowing other layered windows to be invalidated.
* Ideally, it should only allow invlalidating just the windows
* that resized or showed. This would be a bunch of work, but we
* should consider it for later.
*/
if ((pcvr->pos.flags & SWP_NOSIZE) &&
(pcvr->pos.flags & (SWP_SHOWWINDOW | SWP_FRAMECHANGED)) == 0) {
goto InvalidEmpty;
} else {
fInvalidateLayers = TRUE;
}
}
/*
* Calculate the new visrgn
*/
if (!SwpCalcVisRgn(pwnd, ghrgnVisNew))
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 ghrgnValid:
*
* ghrgnValid = OffsetRgn(rcBlt, -dxBlt, -dyBlt) & hrgnVisOld
* ghrgnValid = ghrgnValid - ghrgnValidSum
* OffsetRgn(ghrgnValid, dxBlt, dyBlt);
* ghrgnValid = ghrgnValid - hrgnUpdate
* ghrgnValid = ghrgnValid & 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 == HRGN_FULL)
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(ghrgnSWP1,
pcvr->rcBlt.left - pcvr->dxBlt,
pcvr->rcBlt.top - pcvr->dyBlt,
pcvr->rcBlt.right - pcvr->dxBlt,
pcvr->rcBlt.bottom - pcvr->dyBlt);
switch (IntersectRgn(ghrgnValid, ghrgnSWP1, pcvr->hrgnVisOld)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
if (!(fsSumEmpty & RE_VALIDSUM)) {
switch (SubtractRgn(ghrgnValid, ghrgnValid, ghrgnValidSum)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
}
if ((pcvr->dxBlt | pcvr->dyBlt) != 0)
GreOffsetRgn(ghrgnValid, pcvr->dxBlt, pcvr->dyBlt);
/*
* Now subtract off the update regions of ourself and any
* non-clipchildren parents...
*/
pwndT = pwnd;
do {
if (pwndT->hrgnUpdate == HRGN_FULL)
goto ValidEmpty;
if (pwndT->hrgnUpdate != NULL) {
switch (SubtractRgn(ghrgnValid, ghrgnValid, pwndT->hrgnUpdate)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
break;
}
}
pwndT = pwndT->spwndParent;
} while (pwndT && !TestWF(pwndT, WFCLIPCHILDREN));
/*
* Subtract out the intermonitor blt pieces.
*/
if (pcvr->hrgnInterMonitor != NULL) {
switch (SubtractRgn(ghrgnValid, ghrgnValid, pcvr->hrgnInterMonitor)) {
case NULLREGION:
case ERROR:
goto ValidEmpty;
}
}
switch (IntersectRgn(ghrgnValid, ghrgnValid, ghrgnVisNew)) {
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, ghrgnValid, SVR_COPYNEW);
#ifdef _WINDOWBLT_NOTIFICATION_
/*
* Define _WINDOWBLT_NOTIFICATION_ to turn on Window BLT notification.
* This notification will set a special flag in the SURFOBJ passed to
* drivers when the DrvCopyBits operation is called to move a window.
*
* See also:
* ntgdi\gre\maskblt.cxx
*/
NtGdiBitBlt(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,
GBB_WINDOWBLT);
#else
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);
#endif
}
/*
* Now take care of any SPB bit restoration we need to do.
*
* Calculate the region to clip the RestoreSpb() output to:
*
* ghrgnInvalid = hrgnVisOld - hrgnVisNew
*/
if (TestWF(pwnd, WFHASSPB) &&
!(fsRgnEmpty & RE_VISOLD) &&
CombineOldNewVis(ghrgnInvalid, pcvr->hrgnVisOld, ghrgnVisNew, 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 ghrgnInvalid needs
* to be invalidated.
*/
if ((retRSPB = RestoreSpb(pwnd, ghrgnInvalid, &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 ghrgnValidSum 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(ghrgnValidSum, ghrgnValidSum, ghrgnInvalid);
}
/*
* ghrgnInvalid += hrgnVisNew
*/
if (!(fsRgnEmpty & RE_VISNEW))
UnionRgn(ghrgnInvalid, ghrgnInvalid, ghrgnVisNew);
/*
* 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 ghrgnInvalid returned by RestoreSpb.
*/
// LATER mikeke VALIDSUM / ghrgnValid mismatch
if (!(fsRgnEmpty & RE_VALIDSUM)) {
switch (SubtractRgn(ghrgnValid, ghrgnValid, ghrgnInvalid)) {
case NULLREGION:
case ERROR:
fsRgnEmpty |= RE_VALIDSUM;
break;
}
}
} else {
/*
* No SPB. Simple ghrgnInvalid calculation is:
*
* ghrgnInvalid = hrgnVisNew + hrgnVisOld;
*/
if (pcvr->hrgnVisOld == NULL) {
/*
* If we couldn't create hrgnVisOld, then
* invalidate the entire parent
*/
SetRectRgnIndirect(ghrgnInvalid, &pwndParent->rcWindow);
} else {
if (!CombineOldNewVis(ghrgnInvalid,
pcvr->hrgnVisOld,
ghrgnVisNew,
RGN_OR,
fsRgnEmpty)) {
goto InvalidEmpty;
}
}
}
/*
* Update ghrgnValidSum
*
* ghrgnValidSum += ghrgnValid
*/
if (!(fsRgnEmpty & RE_VALID)) {
/*
* If the sum region is empty, then COPY instead of OR
*/
if (fsSumEmpty & RE_VALIDSUM)
CopyRgn(ghrgnValidSum, ghrgnValid);
else
UnionRgn(ghrgnValidSum, ghrgnValid, ghrgnValidSum);
fsSumEmpty &= ~RE_VALIDSUM;
}
/*
* Subtract ghrgnValidSum from ghrgnInvalid if non-empty,
* otherwise use ghrgnValid. Note, ghrgnValid has been OR'ed
* into ghrgnValidSum already.
*/
if (!(fsSumEmpty & RE_VALIDSUM) || !(fsRgnEmpty & RE_VALID)) {
switch (SubtractRgn(ghrgnInvalid, ghrgnInvalid,
!(fsSumEmpty & RE_VALIDSUM) ? ghrgnValidSum : ghrgnValid)) {
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 ghrgnInvalidSum:
*
* ghrgnInvalidSum += ghrgnInvalid
*/
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
*/
BEGINATOMICCHECK();
xxxInternalInvalidate(pwnd,
HRGN_FULL,
RDW_INVALIDATE |
RDW_FRAME |
RDW_ERASE |
RDW_ALLCHILDREN);
ENDATOMICCHECK();
}
/*
* 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 ghrgnInvalidSum is currently empty,
* then instead of copying ghrgnInvalid to ghrgnInvalidSum,
* just set hrgnInvalidate to ghrgnInvalid. This saves
* a region copy in the single-window case.
*/
if (cIter == 0) {
hrgnInvalidate = ghrgnInvalid;
} else {
CopyRgn(ghrgnInvalidSum, ghrgnInvalid);
}
} else {
UnionRgn(ghrgnInvalidSum, ghrgnInvalid, ghrgnInvalidSum);
}
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
*/
DWORD dwFlags = RDW_INVALIDATE | RDW_ERASE;
if (cwndShowing == psmwp->ccvr &&
pwndParent != PWNDDESKTOP(pwndParent)) {
dwFlags |= RDW_NOCHILDREN;
} else {
dwFlags |= RDW_ALLCHILDREN;
}
if (fInvalidateLayers) {
dwFlags |= RDW_INVALIDATELAYERS;
}
BEGINATOMICCHECK();
xxxInternalInvalidate(pwndParent, hrgnInvalidate, dwFlags);
ENDATOMICCHECK();
}
/*
* Since zzzInvalidateDCCache was called with IDC_MOVEBLT specified,
* we must complete the WNDOBJ notification with a call to
* GreClientRgnDone.
*
* Note: in zzzInvalidateDCCache, it is necessary to call
* GreClientRgnUpdated even if gcountPWO is 0. However,
* GreClientRgnDone only does something if gcountPWO is non-zero,
* so we can optimize slightly.
*/
if (gcountPWO != 0) {
GreClientRgnDone(GCR_WNDOBJEXISTS);
}
UnlockAndExit:
/*
* If necessary, release the screen DC
*/
if (hdcScreen != NULL) {
/*
* Reset the visrgn before we go...
*/
GreSelectVisRgn(hdcScreen, 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 and dispatch any deferred Window Event notifications.
*/
GreUnlockDisplay(gpDispInfo->hDev);
zzzEndDeferWinEventNotify();
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)) {
POINT pt;
PWND pwndParent;
pt.x = pwnd->rcClient.left;
pt.y = pwnd->rcClient.top;
pwndParent = pwnd->spwndParent;
UserAssert(pwndParent);
if (pwndParent != PWNDDESKTOP(pwnd)) {
pt.x -= pwndParent->rcClient.left;
pt.y -= pwndParent->rcClient.top;
}
xxxSendMessage(
pwnd,
WM_MOVE,
FALSE,
MAKELONG(pt.x, pt.y));
}
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->spwndChild 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;
/*
* In xxxCreateWindowEx(), we callback the window proc while the
* window is still not linked yet to the window tree. If it is the
* first child of its parent, then the window spwndParent will point
* to the parent while the parent spwndChild will still be NULL. If
* the window proc called ShowWindow() or SetWindowPos() in response
* to those early callbacks, we will end up here with broken window
* tree. The right fix is to never call back while the window tree
* is in an intermediate state (i.e. linking the window to the tree
* before any callback) but this seem scary to change now because of
* app compat.
*
* Windows Bug #482192.
*/
if (pwnd == NULL) {
RIPMSG1(RIP_WARNING,
"Window tree structure broken at pwnd: 0x%p",
pwndParent);
return NULL;
}
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 zzzChangeStates():
* 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().
*/
UserAssert((fTopmost == TRUE) || (fTopmost == FALSE));
if (!!TestWF(pwndRoot, WEFTOPMOST) ^ fTopmost) {
SetWF(pwndRoot, WFTOGGLETOPMOST);
} else {
ClrWF(pwndRoot, WFTOGGLETOPMOST);
}
pwnd = NULL;
while (pwnd = NextOwnedWindow(pwnd, pwndRoot, pwndRoot->spwndParent)) {
SetTopmost(pwnd, fTopmost);
}
}
/*
* LATER: (hiroyama) #88810
* The IME code here broke the US regression test, so backing it up until we
* hit the problem on NT.
*/
#ifdef LATER
/***************************************************************************\
* IsBottomIMEWindow()
*
* returns TRUE if pwnd is IME window and its toplevel window is BOTTOMMOST
*
* Ported: 18-Apr-1997 Hiroyama from Memphis
\***************************************************************************/
BOOL IsBottomIMEWindow(
PWND pwnd)
{
if (TestCF(pwnd, CFIME) ||
(pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) {
PWND pwndT2 = pwnd;
PWND pwndTopOwner = pwnd;
PWND pwndDesktop;
if (grpdeskRitInput == NULL || grpdeskRitInput->pDeskInfo == NULL) {
// Desktop is being created or not yet created
RIPMSG1(RIP_WARNING, "IsBottomIMEWindow: Desktop is being created or not yet created. pwnd=%#p\n",
pwnd);
return FALSE;
}
pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
UserAssert(pwndDesktop);
/*
* search the toplevel owner window of the IME window.
*/
while (pwndT2 && (pwndT2 != pwndDesktop)) {
pwndTopOwner = pwndT2;
pwndT2 = pwndT2->spwndOwner;
}
/*
* TRUE if the toplevel owner window of the IME window is BOTTOMMOST
*/
return (BOOL)(TestWF(pwndTopOwner, WFBOTTOMMOST));
}
return FALSE;
}
/***************************************************************************\
* ImeCheckBottomIMEWindow()
*
* returns TRUE if pwndT->spwndNext's owner is BOTTOMMOST
*
* Ported: 18-Apr-1997 Hiroyama from Memphis
\***************************************************************************/
BOOL ImeCheckBottomIMEWindow(
PWND pwndT)
{
/*
* pwnd is IME window and its toplevel window is BOTTOMMOST
*/
PWND pwndDesktop;
PWND pwndT2 = pwndT->spwndNext;
PWND pwndTopOwner = pwndT2;
UserAssert(grpdeskRipInput != NULL && grpdeskRitInput->pDeskInfo != NULL);
pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
/*
* check if toplevel owner window of pwnd->spwndNext is bottommost
*/
while (pwndT2 && (pwndT2 != pwndDesktop)) {
pwndTopOwner = pwndT2;
pwndT2 = pwndT2->spwndOwner;
}
if (pwndTopOwner && TestWF(pwndTopOwner, WFBOTTOMMOST)) {
/*
* yes, pwndT is the last one whose toplevel window is *not* BOTTOMMOST
*/
return TRUE;
}
return FALSE;
}
#endif // LATER
/***************************************************************************\
* 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, pwndInsertAfterSave;
PWND pwndT;
PTHREADINFO ptiTop;
#ifdef LATER // see #88810
BOOLEAN fImeOwnerIsBottom = FALSE;
#endif
/*
* 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();
#ifdef LATER // see #88810
if (IS_IME_ENABLED()) {
fImeOwnerIsBottom = IsBottomIMEWindow(pwnd);
if (fImeOwnerIsBottom) {
for (pwndT = pwndInsertAfter; pwndT; pwndT = pwndT->spwndNext) {
if (ImeCheckBottomIMEWindow(pwndT)) {
/*
* toplevel owner of pwndT->spwndNext is BOTTOMMOST
*/
break;
}
pwndInsertAfter = pwndT;
}
}
}
#endif // LATER
}
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);
/*
* Remember the top insert after in case this first loop
* fails to find a window
*/
pwndInsertAfterSave = 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;
}
#ifdef LATER // see #88810
// FE_IME
if (fImeOwnerIsBottom && ImeCheckBottomIMEWindow(pwndT)) {
/*
* owner of pwndT->spwndNext is BOTTOMMOST
* so pwndT is the last one whose owner is not bottommost.
*/
pwndInsertAfter = pwndT;
continue;
}
// end FE_IME
#endif
/*
* 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 we didn't find a window in the previous loop,
* 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) || TestWF(pwndT, WFBOTTOMMOST)) {
/*
* This is our first guess in case nothing works out.
*/
pwndInsertAfter = pwndInsertAfterSave;
/*
* 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;
#ifdef LATER // see #88810
// FE_IME
if (fImeOwnerIsBottom && ImeCheckBottomIMEWindow(pwndT)) {
continue;
}
// end FE_IME
#endif
/*
* Best possible match so far: unowned visible window
* of the foreground thread.
*/
pwndInsertAfter = pwndT;
}
}
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;
/*
* If you hit this assertion, you're probably not using the
* GETTOPMOSTINSERTAFTER macro to make this call.
*/
UserAssert(gHardErrorHandler.pti != NULL);
/*
* 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();
UserAssert(ptiCurrent != NULL);
if (ptiCurrent == gHardErrorHandler.pti || (ptiCurrent->ppi->W32PF_Flags & W32PF_SCREENSAVER)) {
return NULL;
}
/*
* pdesk: Leave the logon desktop alone.
* Make sure the hard error box is on this desktop
*/
pdesk = ptiCurrent->rpdesk;
UserAssert(pdesk != NULL);
UserAssert(pdesk->rpwinstaParent);
UserAssert(pdesk->rpwinstaParent->pTerm);
if ((pdesk == grpdeskLogon)
|| (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.
* Note that the harderror box migth not be created yet.
*/
UserAssert(pdesk->pDeskInfo);
UserAssert(pdesk->pDeskInfo->spwnd);
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 = (NextOwnedWindow(NULL, pwnd, pwnd->spwndParent) != NULL);
/*
* 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;
}
/***************************************************************************\
* TrackBackground
*
* Adjust zorder if we're crossing a TOPMOST boundary. Make sure that a
* non-topmost window in a background thread doesn't come in front of
* non-topmost windows in the foreground thread.
\***************************************************************************/
BOOL TrackBackground(WINDOWPOS *ppos, PWND pwndPrev, PWND pwnd)
{
PWND pwndT;
if (pwndPrev == NULL)
return FALSE;
/*
* 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)
return FALSE;
if (GETPTI(pwnd)->ppi == gptiForeground->ppi)
return FALSE;
} else {
if (GETPTI(pwnd) == gptiForeground)
return FALSE;
}
/*
* Make sure the previous window is either staying or becoming
* topmost. If not, continue: no top most boundary.
*/
if (!FSwpTopmost(pwndPrev))
return FALSE;
/*
* 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))
return FALSE;
/*
* Doing this assign prevents this routine from being called
* twice, since HW() is a conditional macro.
*/
pwndT = CalcForegroundInsertAfter(pwnd);
ppos->hwndInsertAfter = HW(pwndT);
return TRUE;
}
/***************************************************************************\
* TrackZorder, TrackZorderHelper
*
* Set up hwndInsertAfter links to point to the previous window in the
* CVR array and partition them in TOPMOST and non-TOPMOST chains.
*
* 05/16/97 vadimg created
\***************************************************************************/
VOID TrackZorderHelper(
WINDOWPOS *ppos,
HWND *phwnd)
{
/*
* phwnd (hwndTopmost or hwndRegular) have been initialized to NULL before
* the beginning of the scan. This way the first hwndInsertAfter that
* we process remains with the value that was previously calculated.
*/
if (*phwnd != NULL) {
#if DBG
if (ppos->hwndInsertAfter != *phwnd) {
RIPMSG0(RIP_WARNING, "TrackZorder: modified hwndInsertAfter");
}
#endif
ppos->hwndInsertAfter = *phwnd;
}
*phwnd = ppos->hwnd;
}
PWND TrackZorder(
WINDOWPOS* ppos,
PWND pwndPrev,
HWND *phwndTop,
HWND *phwndReg)
{
PWND pwnd = PW(ppos->hwnd);
if (pwnd == NULL)
return NULL;
if (TrackBackground(ppos, pwndPrev, pwnd)) {
*phwndReg = ppos->hwnd;
} else if (FSwpTopmost(pwnd)) {
TrackZorderHelper(ppos, phwndTop);
} else {
TrackZorderHelper(ppos, phwndReg);
}
return pwnd;
}
/***************************************************************************\
* 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;
WINDOWPOS pos;
PTHREADINFO ptiT;
HRGN hrgnClipSave;
/*
* 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;
HWND hwndTopmost;
HWND hwndRegular;
if (psmwp->acvr[0].pos.hwnd == NULL)
continue;
code = CheckTopmost(&psmwp->acvr[0].pos);
/*
* Make a local copy for later...
*
* Why don't we copy all CVR fields? This seems pretty hard to maintain.
* Perhaps because most of them haven't been used yet....
*
*/
pos = psmwp->acvr[0].pos;
ptiT = psmwp->acvr[0].pti;
hrgnClipSave = psmwp->acvr[0].hrgnClip;
/*
* 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;
psmwp->acvr[iTop].hrgnClip = hrgnClipSave;
}
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 DBG
PWND pwndOriginal = pwndRoot;
#endif
/*
* Make sure we're z-ordering this window, or settting topmost
* is bad.
*/
UserAssert(!(pos.flags & SWP_NOZORDER));
/*
* 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;
}
#if DBG
if ((pos.flags & SWP_NOOWNERZORDER)
&& ((pwndOriginal != pwndRoot)
|| (NextOwnedWindow(NULL, pwndRoot, pwndRoot->spwndParent) != NULL))) {
/*
* We're not doing owner z-order but pwndOriginal has an owner and/or
* owns some windows. The problem is, SetTopMost always affects the
* whole owner/ownee group. So we might end up with WFTOGGLETOPMOST
* windows that won't be z-ordered. It has always been like that.
*/
RIPMSG2(RIP_WARNING, "ZOrderByOwner: Topmost change while using SWP_NOOWNERZORDER."
" pwndRoot:%p pwndOriginal:%p",
pwndRoot, pwndOriginal);
}
#endif
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;
hwndTopmost = hwndRegular = 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);
psmwp->acvr[iScan].hrgnClip = hrgnClipSave;
}
pwndT = pwnd;
pwnd = TrackZorder(ppos, pwndT, &hwndTopmost, &hwndRegular);
}
}
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 ptiCurrent = PtiCurrent();
TL tlpwndNewActive;
TL tlpwndParent;
TL tlcuSMWP;
BOOL fForegroundPrev;
UserAssert(IsWinEventNotifyDeferredOK());
DBGCheckSMWP(psmwp);
if (psmwp->bHandle) {
CheckLock(psmwp);
}
/*
* 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);
}
/*
* 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;
} else if (fAsync) {
if (!ValidateSmwp(psmwp, &fSyncPaint)) {
goto lbFinished;
}
/*
* ZOrderByOwner() could possibly add other thread windows to the
* list. Filter them again else we would hung.
*/
AsyncWindowPos(psmwp);
}
}
ThreadLockAlwaysWithPti(ptiCurrent, pwndParent, &tlpwndParent);
ThreadLockPoolCleanup(ptiCurrent, psmwp, &tlcuSMWP, DestroySMWP);
/*
* Calc new window positions.
*/
if (xxxCalcValidRects(psmwp, &hwndNewActive)) {
int i;
pwndNewActive = RevalidateHwnd(hwndNewActive);
ThreadLockWithPti(ptiCurrent, pwndNewActive, &tlpwndNewActive);
cVisWindowsPrev = ptiCurrent->cVisWindows;
fForegroundPrev = (ptiCurrent == gptiForeground);
/*
* The call to zzzBltValidBits will leave the critical section
* if there are any notifications to make.
*/
UserAssert(IsWinEventNotifyDeferredOK());
if (!zzzBltValidBits(psmwp))
fSyncPaint = FALSE;
UserAssert(IsWinEventNotifyDeferredOK());
if (psmwp->bShellNotify) {
for (i = psmwp->ccvr; i-- != 0; ) {
/*
* Loop through the windows, looking for notifications.
*/
if (0 == (psmwp->acvr[i].pos.flags & SWP_NOTIFYALL))
continue;
if (psmwp->acvr[i].pos.flags & SWP_NOTIFYCREATE) {
PostShellHookMessages(HSHELL_WINDOWCREATED,
(LPARAM)psmwp->acvr[i].pos.hwnd);
xxxCallHook(HSHELL_WINDOWCREATED,
(WPARAM)psmwp->acvr[i].pos.hwnd,
(LPARAM)0,
WH_SHELL);
}
if (psmwp->acvr[i].pos.flags & SWP_NOTIFYDESTROY) {
PostShellHookMessages(HSHELL_WINDOWDESTROYED,
(LPARAM)psmwp->acvr[i].pos.hwnd);
xxxCallHook(HSHELL_WINDOWDESTROYED,
(WPARAM)psmwp->acvr[i].pos.hwnd,
(LPARAM)0,
WH_SHELL);
}
if (psmwp->acvr[i].pos.flags & SWP_NOTIFYACTIVATE) {
PWND pwnd = RevalidateHwnd(psmwp->acvr[i].pos.hwnd);
if (pwnd != NULL){
TL tlpwnd;
ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
xxxSetTrayWindow(pwnd->head.rpdesk, pwnd, NULL);
ThreadUnlock(&tlpwnd);
}
}
if (psmwp->acvr[i].pos.flags & SWP_NOTIFYFS) {
xxxSetTrayWindow(ptiCurrent->rpdesk, STW_SAME, NULL);
}
}
}
/*
* 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 && !ptiCurrent->cVisWindows) {
ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
TAGMSG1(DBGTAG_FOREGROUND, "xxxEndDeferWindowPosEx set TIF %#p", ptiCurrent);
/*
* 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.
*/
UserAssert(pwndParent);
if (fSyncPaint) {
xxxDoSyncPaint(pwndParent, DSP_ENUMCLIPPEDCHILDREN);
}
ThreadUnlock(&tlpwndNewActive);
/*
* If SwpActivate() set the NONCPAINT bits, clear them now.
*/
if (fClearBits) {
if (pwndActive = ptiCurrent->pq->spwndActive) {
ClrWF(pwndActive, WFNONCPAINT);
}
if (pwndActivePrev = ptiCurrent->pq->spwndActivePrev) {
ClrWF(pwndActivePrev, WFNONCPAINT);
}
}
/*
* Send WM_WINDOWPOSCHANGED messages
*/
xxxSendChangedMsgs(psmwp);
}
ThreadUnlockPoolCleanup(ptiCurrent, &tlcuSMWP);
ThreadUnlock(&tlpwndParent);
}
lbFinished:
/*
* All done. Free everything up and return.
*/
DestroySMWP(psmwp);
return TRUE;
}
/***************************************************************************\
* IncVisWindows
* DecVisWindows
*
* These routines deal with incrementing/decrementing the visible windows
* on the thread.
*
\***************************************************************************/
#if DBG
BOOL gfVisVerify = FALSE;
VOID VerifycVisWindows(
PWND pwnd)
{
BOOL fShowMeTheWindows = FALSE;
PTHREADINFO pti = GETPTI(pwnd);
PWND pwndNext;
UINT uVisWindows = 0;
if (!gfVisVerify) {
return;
}
/*
* Make sure the count makes sense
*/
if ((int)pti->cVisWindows < 0) {
RIPMSG0(RIP_ERROR, "VerifycVisWindows: pti->cVisWindows underflow!");
fShowMeTheWindows = TRUE;
}
/*
* This window might be owned by a desktop-less service
*/
if (pti->rpdesk == NULL || (pti->TIF_flags & TIF_SYSTEMTHREAD)) {
return;
}
/*
* Child windows don't affect cVisWindows
*/
if (!FTopLevel(pwnd)) {
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:%#p", pwnd);
return;
}
if (fShowMeTheWindows) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: Start window walk at:%#p", 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:%#p", pwndNext);
}
if (!TestWF(pwndNext, WFMINIMIZED)
&& TestWF(pwndNext, WFVISIBLE)) {
uVisWindows++;
if (fShowMeTheWindows) {
RIPMSG1(RIP_WARNING, "VerifycVisWindows: Counted:%#p", pwndNext);
}
}
}
pwndNext = pwndNext->spwndNext;
}
/*
* It must match.
*/
if (pti->cVisWindows != uVisWindows) {
RIPMSG2(RIP_WARNING, "VerifycVisWindows: pti->cVisWindows:%#lx. uVisWindows:%#lx",
pti->cVisWindows, uVisWindows);
/*
* Disable going through the list and make the error into a warning.
* There are many loopholes as to how the cVisWindow count may get
* messed up. See bug 109807.
*/
fShowMeTheWindows = TRUE;
if (!fShowMeTheWindows) {
fShowMeTheWindows = TRUE;
uVisWindows = 0;
goto ShowMeTheWindows;
}
}
}
#endif
/***************************************************************************\
* FVisCountable
*
* Desktops and top-level i.e. whose parent is the desktop) non-minimized
* windows should be counted in the per-thread visible window counts.
\***************************************************************************/
BOOL FVisCountable(
PWND pwnd)
{
if (!TestWF(pwnd, WFDESTROYED)) {
if ((GETFNID(pwnd) == FNID_DESKTOP) ||
(FTopLevel(pwnd) && !TestWF(pwnd, WFMINIMIZED))) {
return TRUE;
}
}
return FALSE;
}
/***************************************************************************\
* IncVisWindows
*
\***************************************************************************/
VOID IncVisWindows(
PWND pwnd)
{
if (FVisCountable(pwnd)) {
GETPTI(pwnd)->cVisWindows++;
}
if (TestWF(pwnd, WEFPREDIRECTED)) {
gnVisibleRedirectedCount++;
if (gnVisibleRedirectedCount == 1) {
InternalSetTimer(gTermIO.spwndDesktopOwner,
IDSYS_LAYER,
100,
xxxSystemTimerProc,
TMRF_SYSTEM | TMRF_PTIWINDOW);
}
}
#if DBG
if (!ISTS()) {
VerifycVisWindows(pwnd);
}
#endif
}
/***************************************************************************\
* cDecVis
*
* An inline that allows debug code to decrement the vis window count
* without doing verification right away. Also alled by DecVisWindows
* to do the actual work.
\***************************************************************************/
__inline VOID cDecVis(
PWND pwnd)
{
UserAssert(pwnd != NULL);
if (FVisCountable(pwnd)) {
GETPTI(pwnd)->cVisWindows--;
}
if (TestWF(pwnd, WEFPREDIRECTED)) {
if (gnVisibleRedirectedCount > 0) {
gnVisibleRedirectedCount--;
if (gnVisibleRedirectedCount == 0) {
_KillSystemTimer(gTermIO.spwndDesktopOwner, IDSYS_LAYER);
}
}
}
}
/***************************************************************************\
* DecVisWindows
*
\***************************************************************************/
VOID DecVisWindows(
PWND pwnd)
{
cDecVis(pwnd);
#if DBG
if (!ISTS()) {
VerifycVisWindows(pwnd);
}
#endif
}
/***************************************************************************\
* SetMiminize
*
* This routine must be used to flip the WS_MIMIMIZE style bit.
* It adjusts the cVisWindows count if appropriate.
*
* 06/06/96 GerardoB Created
\***************************************************************************/
VOID SetMinimize(
PWND pwnd,
UINT uFlags)
{
/*
* 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.
*/
#if DBG
cDecVis(pwnd);
#else
DecVisWindows(pwnd);
#endif
}
SetWF(pwnd, WFMINIMIZED);
#if DBG
VerifycVisWindows(pwnd);
#endif
} 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.
*
* Note that we don't check if the window is already in the (in)visible
* state before setting/clearing the WFVISIBLE bit and calling
* Inc/DecVisWindows. If the window is already in the given state and
* someone calls SetVisible to change into the same state, the VisCount
* will get out of sync. This could happen, for example, if someone
* passed two SWP_SHOWWINDOW for the same hwnd CVR's in the same
* EndDeferWindowPos call. It would be ideal to do the check here, but
* most of the time the caller does the check and we don't want to
* penalize everybody just because of the weird cases.
*
* History:
* 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
\***************************************************************************/
VOID SetVisible(
PWND pwnd,
UINT flags)
{
#ifdef REDIRECTION
PDESKTOP pdesk = pwnd->head.rpdesk;
#endif
if (flags & SV_SET) {
if (TestWF(pwnd, WFINDESTROY)) {
RIPMSG1(RIP_WARNING, "SetVisible: show INDESTROY 0x%p", pwnd);
}
if (TestWF(pwnd, WFVISIBLE)) {
RIPMSG1(RIP_WARNING, "SetVisible: already visible 0x%p", pwnd);
} else {
SetWF(pwnd, WFVISIBLE);
IncVisWindows(pwnd);
#ifdef REDIRECTION
if (((pdesk != NULL && (pdesk->dwDTFlags & DF_REDIRECTED)
&& !(GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTIONHOST))
|| (GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTED))
&& FTopLevel(pwnd)) {
SetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
SetWF(pwnd, WEFEXTREDIRECTED);
}
#endif
}
} else {
if (flags & SV_CLRFTRUEVIS) {
ClrFTrueVis(pwnd);
}
if (TestWF(pwnd, WFDESTROYED)) {
RIPMSG1(RIP_WARNING, "SetVisible: hide DESTROYED 0x%p", pwnd);
}
if (TestWF(pwnd, WFVISIBLE)) {
ClrWF(pwnd, WFVISIBLE);
DecVisWindows(pwnd);
#ifdef REDIRECTION
if (((pdesk != NULL && (pdesk->dwDTFlags & DF_REDIRECTED)
&& !(GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTIONHOST))
|| (GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTED))
&& FTopLevel(pwnd)) {
UnsetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
ClrWF(pwnd, WEFEXTREDIRECTED);
}
#endif
} else {
RIPMSG1(RIP_WARNING, "SetVisible: already hidden 0x%p", pwnd);
}
}
}
/***************************************************************************\
* IsMaxedRect
*
* Determines if a window is "maximizing" to a certain area
*
* History:
\***************************************************************************/
BOOL IsMaxedRect(
LPRECT lprcWithin,
PCSIZERECT psrcMaybe)
{
return(psrcMaybe->x <= lprcWithin->left &&
psrcMaybe->y <= lprcWithin->top &&
psrcMaybe->cx >= lprcWithin->right - lprcWithin->left &&
psrcMaybe->cy >= lprcWithin->bottom - lprcWithin->top);
}
/***************************************************************************\
* xxxCheckFullScreen
*
* 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().
*
* History:
\***************************************************************************/
BOOL xxxCheckFullScreen(
PWND pwnd,
PSIZERECT psrc)
{
BOOL fYielded = FALSE;
PMONITOR pMonitor;
PMONITOR pMonitorPrimary;
TL tlpMonitor;
RECT rc;
BOOL fIsPrimary;
CheckLock(pwnd);
/*
* 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));
pMonitorPrimary = GetPrimaryMonitor();
if (gpDispInfo->cMonitors == 1) {
pMonitor = pMonitorPrimary;
} else {
/*
* In multiple monitor mode, windows that take up the entire
* virtual screen are not considered 'full screen'. 'Full screen'
* means full single monitor only. This detection is so that any
* docked bars--tray, office'95 tools--can get out of the way for
* the application.
*
* There are only three types of windows that ought to go full
* virtual screen. None of them need the tray et al. to get out of
* the way:
* (1) Normal app windows that want a lot of space
* * Those guys just activate and deactivate normally.
* (2) Desktop windows
* * Shell, User desktop sit behind everything else.
* (3) Screen savers, demos, etc.
* * These guys should be WS_EX_TOPMOST to ensure they sit
* over everybody.
*/
if (IsMaxedRect(&gpDispInfo->rcScreen, psrc))
return fYielded;
RECTFromSIZERECT(&rc, psrc);
pMonitor = _MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY);
}
fIsPrimary = (pMonitor == pMonitorPrimary);
ThreadLockAlways(pMonitor, &tlpMonitor);
if (IsMaxedRect(&pMonitor->rcWork, psrc)) {
if (TestWF(pwnd, WFMAXIMIZED)) {
SetWF(pwnd, WFREALLYMAXIMIZABLE);
if (gpDispInfo->cMonitors > 1) {
/*
* This is for XL '95 going fullscreen when already maxed. It
* always uses the primary display. Let's hack them, and any
* other old app that tries to move its truly maximized window.
* They will be clipped otherwise by our fake regional stuff.
*/
PMONITOR pMonitorReal;
pMonitorReal = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
if (pMonitorReal != pMonitor && fIsPrimary) {
/*
* Transfer over the shape to the REAL monitor.
*/
psrc->x += pMonitorReal->rcMonitor.left;
psrc->y += pMonitorReal->rcMonitor.top;
psrc->cx -= (pMonitor->rcMonitor.right - pMonitor->rcMonitor.left) +
(pMonitorReal->rcMonitor.right - pMonitorReal->rcMonitor.left);
psrc->cy -= (pMonitor->rcMonitor.bottom - pMonitor->rcMonitor.top) +
(pMonitorReal->rcMonitor.bottom - pMonitorReal->rcMonitor.top);
ThreadUnlock(&tlpMonitor);
pMonitor = pMonitorReal;
fIsPrimary = FALSE;
ThreadLockAlways(pMonitor, &tlpMonitor);
}
}
}
if ( TestWF(pwnd, WFMAXIMIZED) &&
TestWF(pwnd, WFMAXBOX) &&
(TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION))) {
if ( psrc->y + SYSMET(CYCAPTION) <= pMonitor->rcMonitor.top &&
psrc->y + psrc->cy >= pMonitor->rcMonitor.bottom) {
if (!TestWF(pwnd, WFFULLSCREEN)) {
/*
* Only want to do full screen stuff on the tray
* monitor.
*/
fYielded = xxxAddFullScreen(pwnd, pMonitor);
}
} else {
int iRight;
int iBottom;
int dxy;
if (TestWF(pwnd, WFFULLSCREEN)) {
fYielded = xxxRemoveFullScreen(pwnd, pMonitor);
}
/*
* Despite the code in GetMinMaxInfo() to fix up
* the max rect, we still have to hack old apps.
* Word '95 & XL '95 do weird things when going to/from
* full screen when maximized already.
*
* NOTE: you can have more than one docked bar on a
* monitor. Win '95 code doesn't work right in that
* case.
*/
dxy = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
dxy *= SYSMET(CXBORDER);
psrc->x = pMonitor->rcWork.left - dxy;
psrc->y = pMonitor->rcWork.top - dxy;
dxy *= 2;
iRight = pMonitor->rcWork.right - pMonitor->rcWork.left + dxy;
iBottom = pMonitor->rcWork.bottom - pMonitor->rcWork.top + dxy;
/*
* Let console windows maximze smaller than defaults.
*/
if (pwnd->pcls->atomClassName == gatomConsoleClass) {
psrc->cx = min(iRight, psrc->cx);
psrc->cy = min(iBottom, psrc->cy);
} else {
psrc->cx = iRight;
/*
* 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
*
* BUT THIS CODE DOESN'T WORK FOR MULTIPLE MONITORS, so don't
* do it on secondary dudes. Else, XL '95 flakes out.
*/
if (fIsPrimary && !TestWF(pwnd, WFWIN40COMPAT)) {
psrc->cy = min(iBottom, psrc->cy);
} else {
psrc->cy = iBottom;
}
}
}
} else if (IsMaxedRect(&pMonitor->rcMonitor, psrc)) {
fYielded = xxxAddFullScreen(pwnd, pMonitor);
}
} else {
if (TestWF(pwnd, WFMAXIMIZED)) {
ClrWF(pwnd, WFREALLYMAXIMIZABLE);
}
fYielded = xxxRemoveFullScreen(pwnd, pMonitor);
}
ThreadUnlock(&tlpMonitor);
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)) {
DeleteMaybeSpecialRgn(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;
PWND pwndStop;
if (!pwnd->spwndChild)
return;
pwndStop = pwnd;
pwnd = pwndStop->spwndChild;
for (;;) {
/*
* Skip windows that don't intersect prcHitTest...
*/
if (prcHitTest && !IntersectRect(&rc, prcHitTest, &pwnd->rcWindow))
goto NextWindow;
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 > HRGN_FULL && !TestWF(pwnd, WFMAXFAKEREGIONAL)) {
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);
#ifdef CHILD_LAYERING
if (TestWF(pwnd, WEFLAYERED)) {
POINT ptPos = {pwnd->rcWindow.left, pwnd->rcWindow.top};
GreUpdateSprite(gpDispInfo->hDev, PtoHq(pwnd), NULL, NULL,
&ptPos, NULL, NULL, NULL, 0, NULL, 0, NULL);
}
#endif // CHILD_LAYERING
/*
* Recurse into the child tree if there are children.
*/
if (pwnd->spwndChild) {
pwnd = pwnd->spwndChild;
continue;
}
NextWindow:
if (pwnd->spwndNext) {
/*
* Recurse to the next sibling in the list.
*/
pwnd = pwnd->spwndNext;
} else {
for (;;) {
/*
* We're at the end of the sibling window list.
* Go to the parent's next window.
*/
pwnd = pwnd->spwndParent;
if (pwnd == pwndStop)
return;
if (pwnd->spwndNext) {
pwnd = pwnd->spwndNext;
break;
}
}
}
}
}
/***************************************************************************\
* 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 HRGN_FULL so that SetWindowPos()
* can tell this is what the caller wants.
*/
if (hrgn) {
if ((hrgnClip = UserValidateCopyRgn(hrgn)) == NULL) {
#if DBG
RIPMSG0(RIP_WARNING, "xxxSetWindowRgn: Failed to create region!");
#endif
goto swrClean;
}
MirrorRegion(pwnd, hrgnClip, FALSE);
} else {
hrgnClip = HRGN_FULL;
}
/*
* 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 = InternalBeginDeferWindowPos(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 != HRGN_FULL)) {
swrClean:
GreDeleteObject(hrgnClip);
}
return bRet;
}
/***************************************************************************\
* SelectWindowRgn
*
* This routine does the work of actually selecting in the window region.
*
* 30-Jul-1994 ScottLu Created.
\***************************************************************************/
VOID SelectWindowRgn(
PWND pwnd,
HRGN hrgnClip)
{
/*
* If there is a region already there, delete it becausea new one is
* being set. For maximized windows in multiple monitor mode, we
* always use the monitor HRGN. We don't make a copy. This way, when
* the hrgn changes because of monitor config, the window's monitor
* region automatically gets updated. Clever huh? Also saves memory.
*/
if (pwnd->hrgnClip != NULL) {
if (TestWF(pwnd, WFMAXFAKEREGIONAL)) {
ClrWF(pwnd, WFMAXFAKEREGIONAL);
} else {
/*
* Do NOT select in a monitor region if the window is normally
* regional. The MinMaximize code will always pass HRGN_MONITOR
* to us no matter what. But when we get here, bail out and
* don't destroy the app's region if it has one.
*/
if (hrgnClip == HRGN_MONITOR)
return;
GreDeleteObject(pwnd->hrgnClip);
}
pwnd->hrgnClip = NULL;
}
/*
* NULL or HRGN_FULL 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 > HRGN_FULL) {
if (hrgnClip == HRGN_MONITOR) {
PMONITOR pMonitor;
/*
* Use the monitor region if the window is really maxed
* on a monitor. It's already happened by the time we get here,
* if so. And xxxCheckFullScreen will clear the reallymaximed
* style for a maximized window if it doesn't cover the whole
* max area.
*/
UserAssert(pwnd->spwndParent == PWNDDESKTOP(pwnd));
if (!TestWF(pwnd, WFMAXIMIZED) || !TestWF(pwnd, WFREALLYMAXIMIZABLE))
return;
/*
* Do nothing for windows off screen.
*/
pMonitor = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTONULL);
if (!pMonitor)
return;
hrgnClip = pMonitor->hrgnMonitor;
SetWF(pwnd, WFMAXFAKEREGIONAL);
} else {
if (pwnd != PWNDDESKTOP(pwnd)) {
GreOffsetRgn(hrgnClip, pwnd->rcWindow.left, pwnd->rcWindow.top);
}
GreSetRegionOwner(hrgnClip, OBJECT_OWNER_PUBLIC);
}
pwnd->hrgnClip = hrgnClip;
}
}
/***************************************************************************\
* TestRectBogus
*
* Returns TRUE if the window rect [x,y,cx,cy] is centered or
* clipped to the monitor or work rect [prc], FALSE otherwise.
*
* History:
* 26-Mar-1997 adams Created.
\***************************************************************************/
#define SLOP_X 8
#define SLOP_Y 8
BOOL
TestRectBogus(RECT * prc, int x, int y, int cx, int cy)
{
//
// check for a fullscreen (or offscreen) window
//
if ( x <= prc->left &&
y <= prc->top &&
cx >= (prc->right - prc->left) &&
cy >= (prc->bottom - prc->top)) {
// rect is fullscreen
return FALSE;
}
//
// check for the window being centered to the work area
// use <= for y to catch dialogs centered "high"
// (like the network logon dialog)
//
if ( abs(x - (prc->right + prc->left - cx) / 2) <= SLOP_X &&
abs(y - (prc->bottom + prc->top - cy) / 2) <= SLOP_Y ) {
// rect centered
return TRUE;
}
//
// check for the window being cliped to the work area
//
if ( x == prc->left ||
y == prc->top ||
x == (prc->right - cx) ||
y == (prc->bottom - cy)) {
// rect is clipped
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* IsRectBogus
*
* Returns TRUE if the window rect [x,y,cx,cy] is centered or
* clipped to the monitor or work rect of the primary monitor.
*
* History:
* 26-Mar-1997 adams Created.
\***************************************************************************/
BOOL
IsRectBogus(
int x,
int y,
int cx,
int cy)
{
PMONITOR pMonitorPrimary = GetPrimaryMonitor();
return TestRectBogus(&pMonitorPrimary->rcWork, x, y, cx, cy) ||
TestRectBogus(&pMonitorPrimary->rcMonitor, x, y, cx, cy);
}
/***************************************************************************\
* FixBogusSWP
*
* Detects if a rect is being centered or clipped to the primary monitor,
* and centers it in its owner's window if so. This prevents apps that
* are not multimon aware from having their "main" window displayed on
* one monitor but their dialogs moved to the primary monitor
* because they believe the dialog is offscreen.
*
* History:
* 26-Mar-1997 adams Created.
\***************************************************************************/
VOID
FixBogusSWP(
PWND pwnd,
int * px,
int * py,
int cx,
int cy,
UINT flags)
{
PMONITOR pMonitor;
pMonitor = _MonitorFromWindow(pwnd->spwndOwner, MONITOR_DEFAULTTONEAREST);
//
// only check for a bogus SWP if the owner is not on the primary
//
if (pMonitor != GetPrimaryMonitor()) {
//
// get the current size if SWP_NOSIZE is set
//
if (flags & SWP_NOSIZE) {
cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
}
//
// see if the app is trying to center or clip the window
//
if (IsRectBogus(*px, *py, cx, cy))
{
RECT rc;
#if DBG
int oldX = *px;
int oldY = *py;
#endif
//
// the app wants to center/clip the window
// we will have to do it for them.
//
// get the window rect of the parent and
// intersect that with the work area of
// the owning monitor, then center the
// window to this rect.
//
IntersectRect(&rc, &pMonitor->rcWork, &pwnd->spwndOwner->rcWindow);
//
// new multimonior friendly position.
//
*px = rc.left + (rc.right - rc.left - cx) / 2;
*py = rc.top + (rc.bottom - rc.top - cy) / 2;
//
// now clip to the work area.
//
if (*px + cx > pMonitor->rcWork.right) {
*px = pMonitor->rcWork.right - cx;
}
if (*py + cy > pMonitor->rcWork.bottom) {
*py = pMonitor->rcWork.bottom - cy;
}
if (*px < pMonitor->rcWork.left) {
*px = pMonitor->rcWork.left;
}
if (*py < pMonitor->rcWork.top) {
*py = pMonitor->rcWork.top;
}
RIPMSG0(RIP_WARNING | RIP_THERESMORE, "SetWindowPos detected that your app is centering or clipping");
RIPMSG0(RIP_WARNING | RIP_THERESMORE | RIP_NONAME, "a window to the primary monitor when its owner is on a different monitor.");
RIPMSG0(RIP_WARNING | RIP_THERESMORE | RIP_NONAME, "Consider fixing your app to use the Window Manager Multimonitor APIs.");
RIPMSG4(RIP_WARNING | RIP_NONAME, "SetWindowPos moved the window from (%d,%d) to (%d,%d).\n",
oldX, oldY, *px, *py);
}
}
}
/***************************************************************************\
* PreventInterMonitorBlts()
*
* Prevents monitor-to-monitor blts when they are different caps. This
* way we redraw the part of a window that moves to a different monitor.
* We try to blt as much as possible.
*
* We look at the source rect and what monitor owns it, and how much that
* monitor also contains of the destination rect. Then we compare that
* with the destination rect and what monitor owns that, and how much it
* contains of the source rect. The larger wins.
*
* rcBlt is in screen coordinates and is the DESTINATION.
*
* History:
* 11-11-1997 vadimg ported from Memphis
\***************************************************************************/
VOID PreventInterMonitorBlts(
PCVR pcvr)
{
RECT rcSrc;
RECT rcDst;
RECT rcSrcT;
RECT rcDstT;
PMONITOR pMonitor;
/*
* If the destination is empty do nothing.
*/
if (IsRectEmpty(&pcvr->rcBlt)) {
return;
}
/*
* Get the source rect (rcBlt is the destination, dxBlt/dyBlt are the
* distance moved from the source).
*/
CopyOffsetRect(&rcSrc, &pcvr->rcBlt, -pcvr->dxBlt, -pcvr->dyBlt);
/*
* Split up the source into its monitor pieces. If the source intersects
* a monitor, then figure out where that part will be in the destination.
* Intersect the destination part with the same monitor. The result is
* the amount we can blt from the source to the dest on that monitor.
*
* We do this for each monitor to find the biggest blt rect. We want
* the biggest because we want to repaint as little as possible. We do
* bail out if both the source and dest are fully contained on the same
* monitor.
*/
for (pMonitor = gpDispInfo->pMonitorFirst;
pMonitor != NULL;
pMonitor = pMonitor->pMonitorNext) {
/*
* We're only interested in visible monitors.
*/
if (!(pMonitor->dwMONFlags & MONF_VISIBLE))
continue;
/*
* If this monitor doesn't contain a piece of the source, we don't
* care about it. We won't be doing a same monitor blt on it for sure.
*/
if (!IntersectRect(&rcSrcT, &rcSrc, &pMonitor->rcMonitor))
continue;
/*
* See where this rect would be in the destination.
*/
CopyOffsetRect(&rcDst, &rcSrcT, pcvr->dxBlt, pcvr->dyBlt);
/*
* Intersect this rect with the same monitor rect to see what piece
* can be safely blted on the same monitor.
*/
IntersectRect(&rcDstT, &rcDst, &pMonitor->rcMonitor);
/*
* Is this piece of the source staying on this monitor?
*/
if (EqualRect(&rcDstT, &rcDst)) {
/*
* This source piece is staying completely on this monitor when
* it becomes the destination. Hence there is nothing to add
* to our invalid sum, hrgnInterMonitor.
*/
if (EqualRect(&rcSrcT, &rcSrc)) {
/*
* The source is completely ON one monitor and moving to
* a location also completely ON this monitor. Great, no
* intermonitor blts whatsoever. We are done.
*/
UserAssert(pcvr->hrgnInterMonitor == NULL);
return;
} else {
continue;
}
}
/*
* OK, some piece of the source is moving across monitors. Figure
* out what it is and where that piece is in the destination. That
* piece in the destination must be invalidated and not blted.
*/
if (pcvr->hrgnInterMonitor == NULL) {
pcvr->hrgnInterMonitor = CreateEmptyRgn();
}
/*
* The difference between the transposed source to the dest, and the
* real part of the dest that lies on this monitor, is the amount
* of the source that will move across a monitor boundary. Add this
* to our accumulated invalid region.
*
* rcDst is the whole source chunk, rcDstT is the part on the same
* monitor as the source chunk.
*/
GreSetRectRgn(ghrgnInv2, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
GreSetRectRgn(ghrgnGDC, rcDstT.left, rcDstT.top, rcDstT.right, rcDstT.bottom);
SubtractRgn(ghrgnInv2, ghrgnInv2, ghrgnGDC);
UnionRgn(pcvr->hrgnInterMonitor, pcvr->hrgnInterMonitor, ghrgnInv2);
}
#if DBG
VerifyVisibleMonitorCount();
#endif
}