/****************************** Module Header ******************************\ * Module Name: visrgn.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains User's visible region ('visrgn') manipulation * functions. * * History: * 23-Oct-1990 DarrinM Created. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /* * Globals used to keep track of pwnds which * need to be excluded from the visrgns. */ #define CEXCLUDERECTSMAX 30 #define CEXCLUDEPWNDSMAX 30 BOOL gfVisAlloc; int gcrcVisExclude; int gcrcVisExcludeMax; PWND *gapwndVisExclude; PWND *gapwndVisDefault; /***************************************************************************\ * SetRectRgnIndirect * * Sets a rect region from a rectangle. * * History: * 26-Sep-1996 adams Created. \***************************************************************************/ BOOL SetRectRgnIndirect(HRGN hrgn, LPCRECT lprc) { return GreSetRectRgn(hrgn, lprc->left, lprc->top, lprc->right, lprc->bottom); } /***************************************************************************\ * CreateEmptyRgn * * Creates an empty region. * * History: * 24-Sep-1996 adams Created. \***************************************************************************/ HRGN CreateEmptyRgn(void) { return GreCreateRectRgnIndirect(PZERO(RECT)); } /***************************************************************************\ * CreateEmptyRgnPublic * * Creates an empty region and make it public. * * History: * 24-Sep-1996 adams Created. \***************************************************************************/ HRGN CreateEmptyRgnPublic(void) { HRGN hrgn; if (hrgn = CreateEmptyRgn()) { UserVerify(GreSetRegionOwner(hrgn, OBJECT_OWNER_PUBLIC)); } return hrgn; } /***************************************************************************\ * SetEmptyRgn * * Sets an empty region. * * History: * 26-Sep-1996 adams Created. \***************************************************************************/ BOOL SetEmptyRgn(HRGN hrgn) { return SetRectRgnIndirect(hrgn, PZERO(RECT)); } /***************************************************************************\ * SetOrCreateRectRgnIndirectPublic * * Sets a region to a rectangle, creating it and making it public * if it is not already there. * * History: * 01-Oct-1996 adams Created. \***************************************************************************/ HRGN SetOrCreateRectRgnIndirectPublic(HRGN * phrgn, LPCRECT lprc) { if (*phrgn) { UserVerify(SetRectRgnIndirect(*phrgn, lprc)); } else if (*phrgn = GreCreateRectRgnIndirect((LPRECT) lprc)) { UserVerify(GreSetRegionOwner(*phrgn, OBJECT_OWNER_PUBLIC)); } return *phrgn; } /***************************************************************************\ * ResizeVisExcludeMemory * * This routine is used to resize the vis-rgn memory buffer if the count * is exceeded. * * * History: * 22-Oct-1994 ChrisWil Created * 27-Feb-1997 adams Removed call to UserReallocPool, since the pool * allocator doesn't support realloc. \***************************************************************************/ BOOL ResizeVisExcludeMemory(VOID) { int crcNew; PWND apwndNew; /* * Note (adams): a previous version of the code called UserReallocPool * if memory had already been allocated. Unfortunately, UserReallocPool * just has to allocate more memory and copy the contents, since Rtl * doesn't have a realloc function. If Rtl later gains a Realloc function, * this code should be changed to the previous version. */ crcNew = gcrcVisExcludeMax + CEXCLUDEPWNDSMAX; apwndNew = (PWND)UserAllocPool( crcNew * sizeof(PWND), TAG_VISRGN); if (!apwndNew) return FALSE; UserAssert(gcrcVisExcludeMax == gcrcVisExclude); RtlCopyMemory(apwndNew, gapwndVisExclude, gcrcVisExcludeMax * sizeof(PWND)); if (gfVisAlloc) { UserFreePool(gapwndVisExclude); } else { gfVisAlloc = TRUE; } gcrcVisExcludeMax = crcNew; gapwndVisExclude = (PWND *)apwndNew; return TRUE; } /***************************************************************************\ * ExcludeWindowRects * This routine checks to see if the pwnd needs to be added to the list * of excluded-clip-rects. If so, it appends the pwnd to the array. They * do not need to be sorted, since GreSubtractRgnRectList() sorts them * internally. * * * History: * 05-Nov-1992 DavidPe Created. * 21-Oct-1994 ChrisWil Removed pwnd->pwndNextYX. No longer sorts pwnds. \***************************************************************************/ #define CheckIntersectRect(prc1, prc2) \ ( prc1->left < prc2->right \ && prc2->left < prc1->right \ && prc1->top < prc2->bottom \ && prc2->top < prc1->bottom) #define EmptyRect(prc) \ ( prc->left >= prc->right \ || prc->top >= prc->bottom) BOOL ExcludeWindowRects( PWND pwnd , PWND pwndStop, LPRECT lprcIntersect) { PRECT prc; #if DBG if (pwnd != NULL && pwndStop != NULL && pwnd->spwndParent != pwndStop->spwndParent) { RIPMSG0(RIP_ERROR, "ExcludeWindowRects: bad windows passed in"); } #endif while ((pwnd != NULL) && (pwnd != pwndStop)) { UserAssert(pwnd); prc = &pwnd->rcWindow; if ( TestWF(pwnd, WFVISIBLE) #ifdef REDIRECTION && (TestWF(pwnd, WEFEXTREDIRECTED) == 0) #endif // REDIRECTION && (TestWF(pwnd, WEFLAYERED) == 0) && (TestWF(pwnd, WEFTRANSPARENT) == 0) && CheckIntersectRect(lprcIntersect, prc) && !EmptyRect(prc)) { UserAssert(gcrcVisExclude <= gcrcVisExcludeMax); if (gcrcVisExclude == gcrcVisExcludeMax) { if (!ResizeVisExcludeMemory()) { return FALSE; } } gapwndVisExclude[gcrcVisExclude++] = pwnd; } pwnd = pwnd->spwndNext; } return TRUE; } /***************************************************************************\ * CalcWindowVisRgn * * This routine performs the work of calculating the VisRgn for a window. * * * History: * 02-Nov-1992 DavidPe Created. * 21-Oct-1992 ChrisWil Removed pwnd->pwndNextYX. No longer sorts pwnds. \***************************************************************************/ BOOL CalcWindowVisRgn( PWND pwnd, HRGN *phrgn, DWORD flags) { RECT rcWindow; PWND pwndParent; PWND pwndRoot; PWND pwndLoop; BOOL fClipSiblings; BOOL fRgnParent = FALSE; BOOL fResult; PWND apwndVisDefault[CEXCLUDEPWNDSMAX]; /* * First get the initial window rectangle which will be used for * the basis of exclusion calculations. */ rcWindow = (flags & DCX_WINDOW ? pwnd->rcWindow : pwnd->rcClient); /* * Get the parent of this window. We start at the parent and backtrack * through the window-parent-list until we reach the end of the parent- * list. This will give us the intersect-rectangle which is used as * the basis for checking intersection of the exclusion rects. */ pwndRoot = pwnd->head.rpdesk->pDeskInfo->spwnd->spwndParent; pwndParent = pwnd->spwndParent; /* * The parent can be NULL in the case when pwnd == pwndRoot. In other * cases we should figure why the parent is NULL. */ if (pwndParent == NULL) { #if DBG if (pwnd != pwndRoot) { RIPMSG0(RIP_ERROR, "CalcWindowVisRgn: pwndParent is NULL"); } #endif goto NullRegion; } while (pwndParent != pwndRoot) { /* * Don't clip layered DCs to the desktop. The surface of the layered * DC is the size of the window and we always want to have the image * of the entire window in that surface. */ if ((flags & DCX_REDIRECTED) && (GETFNID(pwndParent) == FNID_DESKTOP)) break; /* * Remember if any of the parents have a window region. */ if (pwndParent->hrgnClip != NULL) fRgnParent = TRUE; /* * Intersect the parent's client rectangle with the window rectangle. */ if (!IntersectRect(&rcWindow, &rcWindow, &pwndParent->rcClient)) goto NullRegion; pwndParent = pwndParent->spwndParent; } /* * Initialize the VisRgn memory-buffer. This is * used to hold the pwnd's. */ gapwndVisDefault = apwndVisDefault; gapwndVisExclude = gapwndVisDefault; gcrcVisExcludeMax = ARRAY_SIZE(apwndVisDefault); gcrcVisExclude = 0; /* * Build the list of exclude-rects. */ fClipSiblings = (BOOL)(flags & DCX_CLIPSIBLINGS); pwndParent = pwnd->spwndParent; pwndLoop = pwnd; while (pwndParent != pwndRoot) { /* * If we reach a redirected window, we can stop excluding any * siblings any siblings of any parents. */ if ((flags & DCX_REDIRECTED) && TestWF(pwndLoop, WEFPREDIRECTED)) { break; } /* * Exclude any siblings if necessary. */ if (fClipSiblings && (pwndParent->spwndChild != pwndLoop)) { if (!ExcludeWindowRects(pwndParent->spwndChild, pwndLoop, &rcWindow)) { goto NullRegion; } } /* * Set this flag for next time through the loop... */ fClipSiblings = TestWF(pwndParent, WFCLIPSIBLINGS); pwndLoop = pwndParent; pwndParent = pwndLoop->spwndParent; } if ((flags & DCX_CLIPCHILDREN) && (pwnd->spwndChild != NULL)) { if (!ExcludeWindowRects(pwnd->spwndChild, NULL, &rcWindow)) { goto NullRegion; } } /* * If there are rectangles to exclude call GDI to create * a region excluding them from the window rectangle. If * not simply call GreSetRectRgn(). */ if (gcrcVisExclude > 0) { RECT arcVisRects[CEXCLUDERECTSMAX]; PRECT arcExclude; int i; int ircVisExclude = 0; int irgnVisExclude = 0; /* * If we need to exclude more rectangles than fit in * the pre-allocated buffer, obviously we have to * allocate one that's big enough. */ if (gcrcVisExclude <= CEXCLUDERECTSMAX) { arcExclude = arcVisRects; } else { arcExclude = (PRECT)UserAllocPoolWithQuota( sizeof(RECT) * gcrcVisExclude, TAG_VISRGN); if (!arcExclude) goto NullRegion; } /* * Now run through the list and put the * window rectangles into the array for the call * to CombineRgnRectList(). */ for (i = 0; i < gcrcVisExclude; i++) { /* * If the window has a clip-rgn associated with * it, then re-use gpwneExcludeList[] entries for * storing them. */ if (gapwndVisExclude[i]->hrgnClip != NULL) { gapwndVisExclude[irgnVisExclude++] = gapwndVisExclude[i]; continue; } /* * This window doesn't have a clipping region; remember its * rect for clipping purposes. */ arcExclude[ircVisExclude++] = gapwndVisExclude[i]->rcWindow; } if (*phrgn == NULL) *phrgn = CreateEmptyRgn(); if (ircVisExclude != 0) { GreSubtractRgnRectList(*phrgn, &rcWindow, arcExclude, ircVisExclude); } else { SetRectRgnIndirect(*phrgn, &rcWindow); } for (i = 0; i < irgnVisExclude; i++) { SetRectRgnIndirect(ghrgnInv2, &gapwndVisExclude[i]->rcWindow); IntersectRgn(ghrgnInv2, ghrgnInv2, gapwndVisExclude[i]->hrgnClip); if (SubtractRgn(*phrgn, *phrgn, ghrgnInv2) == NULLREGION) break; } if (arcExclude != arcVisRects) { UserFreePool((HLOCAL)arcExclude); } } else { /* * If the window was somehow destroyed, then we will return * a null-region. Emptying the rect will accomplish this. */ if (TestWF(pwnd, WFDESTROYED)) { SetRectEmpty(&rcWindow); } /* * If there weren't any rectangles to exclude, simply call * GreSetRectRgn() with the window rectangle. */ SetOrCreateRectRgnIndirectPublic(phrgn, &rcWindow); } /* * Clip out this window's window region */ if (pwnd->hrgnClip != NULL) { IntersectRgn(*phrgn, *phrgn, pwnd->hrgnClip); } /* * Clip out parent's window regions, if there are any. */ if (fRgnParent) { PWND pwndT; for (pwndT = pwnd->spwndParent; pwndT != pwndRoot; pwndT = pwndT->spwndParent) { if (pwndT->hrgnClip != NULL) { if (IntersectRgn(*phrgn, *phrgn, pwndT->hrgnClip) == NULLREGION) break; } } } fResult = TRUE; // fall-through Cleanup: if (gfVisAlloc) { UserFreePool((HLOCAL)gapwndVisExclude); gfVisAlloc = FALSE; } return fResult; NullRegion: SetOrCreateRectRgnIndirectPublic(phrgn, PZERO(RECT)); fResult = FALSE; goto Cleanup; } /***************************************************************************\ * CalcVisRgn * * Will return FALSE if the pwndOrg is not visible, TRUE otherwise. * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL CalcVisRgn( HRGN *phrgn, PWND pwndOrg, PWND pwndClip, DWORD flags) { PDESKTOP pdesk; UserAssert(pwndOrg != NULL); /* * If the window's not visible or is not an active desktop, * or if the clip window is in the process of being destroyed, * the visrgn is empty. */ pdesk = pwndOrg->head.rpdesk; UserAssert(pdesk); /* * Make sure this happens in the IO windowstation */ #if DBG if (grpdeskRitInput != NULL) { UserAssert(pdesk->rpwinstaParent == grpdeskRitInput->rpwinstaParent || !IsVisible(pwndOrg)); } #endif // DBG /* * For redirected windows, if it is on the non I/O desktop, we still need * to pass the application a non empty region in order to update the bitmap * Otherwise, the bitmap will never got the chance to be updated and we will end up * rendering a black window region (for the case of layered windows) by the time * we switch to this desktop. (see bug# 287315). Note that this will not render the window on the screen * because the call to CalcVisRgn from UserVisrgnFromHwnd() should never specify * DCX_REDIRECTEDBITMAP. */ #if DBG if (!TestWF(pwndOrg, WEFPREDIRECTED)) { UserAssert(!(flags & DCX_REDIRECTEDBITMAP)); } #endif if (!IsVisible(pwndOrg) || ((pdesk != grpdeskRitInput) && !(flags & DCX_REDIRECTEDBITMAP))) { goto EmptyRgn; } /* * If LockWindowUpdate() has been called, and this window is a child * of the lock window, always return an empty visrgn. */ if ((gspwndLockUpdate != NULL) && !(flags & DCX_LOCKWINDOWUPDATE) && _IsDescendant(gspwndLockUpdate, pwndOrg)) { goto EmptyRgn; } /* * Now go compute the visrgn for pwndClip. */ return CalcWindowVisRgn(pwndClip, phrgn, flags); EmptyRgn: SetOrCreateRectRgnIndirectPublic(phrgn, PZERO(RECT)); return FALSE; } /***************************************************************************\ * CalcWindowRgn * * * History: * 17-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ int CalcWindowRgn( PWND pwnd, HRGN hrgn, BOOL fClient) { SetRectRgnIndirect(hrgn, (fClient) ? &pwnd->rcClient : &pwnd->rcWindow); /* * If the window has a region, then intersect the rectangle region with * that. If this is low on memory, it'll propagate ERROR back. */ if (pwnd->hrgnClip != NULL) { return IntersectRgn(hrgn, hrgn, pwnd->hrgnClip); } return SIMPLEREGION; }