/****************************** 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 }