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

616 lines
17 KiB

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