/*-
 *-----------------------------------------------------------------------
 * miComputeClips --
 *	Recompute the clipList, borderClip, exposed and borderExposed
 *	regions for pParent and its children. Only viewable windows are
 *	taken into account.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	clipList, borderClip, exposed and borderExposed are altered.
 *	A VisibilityNotify event may be generated on the parent window.
 *
 *-----------------------------------------------------------------------
 */

static void
miComputeClips (pParent, pScreen, universe, kind, exposed)
    WindowPtr	pParent;
    ScreenPtr	pScreen;
    RegionPtr	universe;
    VTKind		kind;
    RegionPtr		exposed; /* for intermediate calculations */
{
    int			dx,
			dy;
    RegionRec		childUniverse;
    WindowPtr	pChild;
    unsigned   	  	oldVis;
    BoxRec		borderSize;
    RegionRec		childUnion;
    Bool		overlap;
    RegionPtr		borderVisible;
    Bool		shrunk;
    
    /*
     * Figure out the new visibility of this window.
     * The extent of the universe should be the same as the extent of
     * the borderSize region. If the window is unobscured, this rectangle
     * will be completely inside the universe (the universe will cover it
     * completely). If the window is completely obscured, none of the
     * universe will cover the rectangle.
     */

    borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent);
    borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent);
    dx = (int) pParent->drawable.x + (int) pParent->drawable.width + wBorderWidth(pParent);
    /*
    if (dx > 32767)
	dx = 32767;
    */
    borderSize.x2 = dx;
    dy = (int) pParent->drawable.y + (int) pParent->drawable.height + wBorderWidth(pParent);
    /*
    if (dy > 32767)
	dy = 32767;
    */
    borderSize.y2 = dy;

    oldVis = pParent->visibility;
    switch ((* pScreen->RectIn) (universe, &borderSize)) 
    {
	case rgnIN:
	    pParent->visibility = VisibilityUnobscured;
	    break;
	case rgnPART:
	    pParent->visibility = VisibilityPartiallyObscured;
#ifdef SHAPE
	    {
		RegionPtr   pBounding;

		if ((pBounding = wBoundingShape (pParent)) != 0)
		{
		    switch (miShapedWindowIn (pScreen, universe, pBounding,
					      &borderSize,
					      pParent->drawable.x,
 					      pParent->drawable.y))
		    {
		    case rgnIN:
			pParent->visibility = VisibilityUnobscured;
			break;
		    case rgnOUT:
			pParent->visibility = VisibilityFullyObscured;
			break;
		    }
		}
	    }
#endif
	    break;
	default:
	    pParent->visibility = VisibilityFullyObscured;
	    break;
    }
    if (oldVis != pParent->visibility &&
	((pParent->eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask))
	SendVisibilityNotify(pParent);

    dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x;
    dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y;

    /*
     * avoid computations when dealing with simple operations
     */

    switch (kind) {
    case VTMap:
    case VTStack:
    case VTUnmap:
	break;
    case VTMove:
	if ((oldVis == pParent->visibility) &&
	    ((oldVis == VisibilityFullyObscured) ||
	     (oldVis == VisibilityUnobscured)))
	{
	    pChild = pParent;
	    while (1)
	    {
		if (pChild->viewable)
		{
		    if (pChild->visibility != VisibilityFullyObscured)
		    {
			(* pScreen->TranslateRegion) (&pChild->borderClip,
						      dx, dy);
			(* pScreen->TranslateRegion) (&pChild->clipList,
						      dx, dy);
			pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER;
			if (clipNotify)
			    (* clipNotify) (pChild, dx, dy);

		    }
		    if (pChild->valdata)
		    {
			(* pScreen->RegionInit) (&pChild->valdata->after.borderExposed,
						 NullBox, 0);
			(* pScreen->RegionInit) (&pChild->valdata->after.exposed,
						 NullBox, 0);
		    }
		    if (pChild->firstChild)
		    {
			pChild = pChild->firstChild;
			continue;
		    }
		}
		while (!pChild->nextSib && (pChild != pParent))
		    pChild = pChild->parent;
		if (pChild == pParent)
		    break;
		pChild = pChild->nextSib;
	    }
	    return;
	}
	/* fall through */
    default:
    	/*
     	 * To calculate exposures correctly, we have to translate the old
     	 * borderClip and clipList regions to the window's new location so there
     	 * is a correspondence between pieces of the new and old clipping regions.
     	 */
    	if (dx | dy) 
    	{
	    /*
	     * We translate the old clipList because that will be exposed or copied
	     * if gravity is right.
	     */
	    (* pScreen->TranslateRegion) (&pParent->borderClip, dx, dy);
	    (* pScreen->TranslateRegion) (&pParent->clipList, dx, dy);
    	} 
	break;
    }

    borderVisible = pParent->valdata->before.borderVisible;
    shrunk = pParent->valdata->before.shrunk;
    (* pScreen->RegionInit) (&pParent->valdata->after.borderExposed, NullBox, 0);
    (* pScreen->RegionInit) (&pParent->valdata->after.exposed, NullBox, 0);

    /*
     * Since the borderClip must not be clipped by the children, we do
     * the border exposure first...
     *
     * 'universe' is the window's borderClip. To figure the exposures, remove
     * the area that used to be exposed from the new.
     * This leaves a region of pieces that weren't exposed before.
     */

    if (borderVisible)
    {
	/*
	 * when the border changes shape, the old visible portions
	 * of the border will be saved by DIX in borderVisible --
	 * use that region and destroy it
	 */
	(* pScreen->Subtract) (exposed, universe, borderVisible);
	(* pScreen->RegionDestroy) (borderVisible);
    }
    else
    {
	(* pScreen->Subtract) (exposed, universe, &pParent->borderClip);
    }
    (* pScreen->Subtract) (&pParent->valdata->after.borderExposed,
			   exposed, &pParent->winSize);

    (* pScreen->RegionCopy) (&pParent->borderClip, universe);

    /*
     * To get the right clipList for the parent, and to make doubly sure
     * that no child overlaps the parent's border, we remove the parent's
     * border from the universe before proceeding.
     */

    (* pScreen->Intersect) (universe, universe, &pParent->winSize);
    
    if ((pChild = pParent->firstChild) != 0)
    {
	(*pScreen->RegionInit) (&childUniverse, NullBox, 0);
	(*pScreen->RegionInit) (&childUnion, NullBox, 0);
	if ((pChild->drawable.y < pParent->lastChild->drawable.y) ||
	    ((pChild->drawable.y == pParent->lastChild->drawable.y) &&
	     (pChild->drawable.x < pParent->lastChild->drawable.x)))
	{
	    for (; pChild; pChild = pChild->nextSib)
	    {
		if (pChild->viewable)
		    (* pScreen->RegionAppend)(&childUnion, &pChild->borderSize);
	    }
	}
	else
	{
	    for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib)
	    {
		if (pChild->viewable)
		    (* pScreen->RegionAppend)(&childUnion, &pChild->borderSize);
	    }
	}
	(* pScreen->RegionValidate)(&childUnion, &overlap);

	for (pChild = pParent->firstChild;
	     pChild;
	     pChild = pChild->nextSib)
 	{
	    if (pChild->viewable) {
		/*
		 * If the child is viewable, we want to remove its extents
		 * from the current universe, but we only re-clip it if
		 * it's been marked.
		 */
		if (pChild->valdata) {
		    /*
		     * Figure out the new universe from the child's
		     * perspective and recurse.
		     */
		    (* pScreen->Intersect) (&childUniverse,
					    universe,
					    &pChild->borderSize);
		    miComputeClips (pChild, pScreen, &childUniverse, kind,
				    exposed);
		}
		/*
		 * Once the child has been processed, we remove its extents
		 * from the current universe, thus denying its space to any
		 * other sibling.
		 */
		if (overlap)
		    (* pScreen->Subtract)
			(universe, universe, &pChild->borderSize);
	    }
	}
	if (!overlap)
	    (* pScreen->Subtract) (universe, universe, &childUnion);
	(* pScreen->RegionUninit) (&childUnion);
	(* pScreen->RegionUninit) (&childUniverse);
    } /* if any children */

    /*
     * 'universe' now contains the new clipList for the parent window.
     *
     * To figure the exposure of the window we subtract the old clip from the
     * new, just as for the border.
     */

    (* pScreen->Subtract) (&pParent->valdata->after.exposed,
			   universe, &pParent->clipList);

#ifdef BACKING_STORE
    /*
     * One last thing: backing storage. We have to try to save what parts of
     * the window are about to be obscured. We can just subtract the universe
     * from the old clipList and get the areas that were in the old but aren't
     * in the new and, hence, are about to be obscured.
     */
    if (pParent->backStorage)
    {
	(* pScreen->Subtract) (exposed, &pParent->clipList, universe);
	if (shrunk && (*pScreen->RegionNotEmpty) (exposed))
	{
	    RegionPtr	temp, CreateUnclippedWinSize();
	    
	    temp = CreateUnclippedWinSize (pParent);
	    (* pScreen->Intersect) (exposed, exposed, temp);
	    (* pScreen->RegionDestroy) (temp);
	}
	(* pScreen->SaveDoomedAreas)(pParent, exposed, dx, dy);
    }
#endif
    
    (* pScreen->RegionCopy) (&pParent->clipList, universe);

    pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;

    if (clipNotify)
	(* clipNotify) (pParent, dx, dy);
}

static void
miTreeObscured(pParent)
    WindowPtr pParent;
{
    WindowPtr pChild;
    unsigned  oldVis;

    pChild = pParent;
    while (1)
    {
	if (pChild->viewable)
	{
	    oldVis = pChild->visibility;
	    if (oldVis != (pChild->visibility = VisibilityFullyObscured) &&
		((pChild->eventMask | wOtherEventMasks(pChild)) & VisibilityChangeMask))
		SendVisibilityNotify(pChild);
	    if (pChild->firstChild)
	    {
		pChild = pChild->firstChild;
		continue;
	    }
	}
	while (!pChild->nextSib && (pChild != pParent))
	    pChild = pChild->parent;
	if (pChild == pParent)
	    break;
	pChild = pChild->nextSib;
    }
}
