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
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 = ¶ms
|
|
*/
|
|
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)¶ms);
|
|
|
|
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
|
|
}
|