
#include <dix-config.h>

#include <X11/X.h>
#include <X11/Xmd.h>
#include <X11/extensions/shapeproto.h>

#include "dix/cursor_priv.h"
#include "dix/dix_priv.h"
#include "dix/screen_hooks_priv.h"
#include "dix/screensaver_priv.h"
#include "dix/window_priv.h"
#include "mi/mi_priv.h"

#include "scrnintstr.h"
#include "validate.h"
#include "windowstr.h"
#include "gcstruct.h"
#include "regionstr.h"
#include "privates.h"
#include "mivalidate.h"
#include "mioverlay.h"
#include "migc.h"

#include "globals.h"

typedef struct {
    RegionRec exposed;
    RegionRec borderExposed;
    RegionPtr borderVisible;
    DDXPointRec oldAbsCorner;
} miOverlayValDataRec, *miOverlayValDataPtr;

typedef struct _TreeRec {
    WindowPtr pWin;
    struct _TreeRec *parent;
    struct _TreeRec *firstChild;
    struct _TreeRec *lastChild;
    struct _TreeRec *prevSib;
    struct _TreeRec *nextSib;
    RegionRec borderClip;
    RegionRec clipList;
    unsigned visibility;
    miOverlayValDataPtr valdata;
} miOverlayTreeRec, *miOverlayTreePtr;

typedef struct {
    miOverlayTreePtr tree;
} miOverlayWindowRec, *miOverlayWindowPtr;

typedef struct {
    CreateWindowProcPtr CreateWindow;
    DestroyWindowProcPtr DestroyWindow;
    UnrealizeWindowProcPtr UnrealizeWindow;
    RealizeWindowProcPtr RealizeWindow;
    miOverlayTransFunc MakeTransparent;
    miOverlayInOverlayFunc InOverlay;
    Bool underlayMarked;
    Bool copyUnderlay;
} miOverlayScreenRec, *miOverlayScreenPtr;

static DevPrivateKeyRec miOverlayWindowKeyRec;

#define miOverlayWindowKey (&miOverlayWindowKeyRec)
static DevPrivateKeyRec miOverlayScreenKeyRec;

#define miOverlayScreenKey (&miOverlayScreenKeyRec)

static void RebuildTree(WindowPtr);
static Bool HasUnderlayChildren(WindowPtr);
static void MarkUnderlayWindow(WindowPtr);
static Bool CollectUnderlayChildrenRegions(WindowPtr, RegionPtr);

static void miOverlayCloseScreen(CallbackListPtr *pcbl, ScreenPtr pScreen, void *unused);
static Bool miOverlayCreateWindow(WindowPtr);
static Bool miOverlayDestroyWindow(WindowPtr);
static Bool miOverlayUnrealizeWindow(WindowPtr);
static Bool miOverlayRealizeWindow(WindowPtr);
static void miOverlayMarkWindow(WindowPtr);
static void miOverlayReparentWindow(WindowPtr, WindowPtr);
static void miOverlayRestackWindow(WindowPtr, WindowPtr);
static Bool miOverlayMarkOverlappedWindows(WindowPtr, WindowPtr, WindowPtr *);
static void miOverlayMarkUnrealizedWindow(WindowPtr, WindowPtr, Bool);
static int miOverlayValidateTree(WindowPtr, WindowPtr, VTKind);
static void miOverlayHandleExposures(WindowPtr);
static void miOverlayMoveWindow(WindowPtr, int, int, WindowPtr, VTKind);
static void miOverlayWindowExposures(WindowPtr, RegionPtr);
static void miOverlayResizeWindow(WindowPtr, int, int, unsigned int,
                                  unsigned int, WindowPtr);
static void miOverlayClearToBackground(WindowPtr, int, int, int, int, Bool);

static void miOverlaySetShape(WindowPtr, int);
static void miOverlayChangeBorderWidth(WindowPtr, unsigned int);

#define MIOVERLAY_GET_SCREEN_PRIVATE(pScreen) ((miOverlayScreenPtr) \
	dixLookupPrivate(&(pScreen)->devPrivates, miOverlayScreenKey))
#define MIOVERLAY_GET_WINDOW_PRIVATE(pWin) ((miOverlayWindowPtr) \
	dixLookupPrivate(&(pWin)->devPrivates, miOverlayWindowKey))
#define MIOVERLAY_GET_WINDOW_TREE(pWin) \
	(MIOVERLAY_GET_WINDOW_PRIVATE(pWin)->tree)

#define IN_UNDERLAY(w) MIOVERLAY_GET_WINDOW_TREE(w)
#define IN_OVERLAY(w) !MIOVERLAY_GET_WINDOW_TREE(w)

#define MARK_OVERLAY(w) miMarkWindow(w)
#define MARK_UNDERLAY(w) MarkUnderlayWindow(w)

#define HasParentRelativeBorder(w) (!(w)->borderIsPixel && \
                                    HasBorder(w) && \
                                    (w)->backgroundState == ParentRelative)

Bool
miInitOverlay(ScreenPtr pScreen,
              miOverlayInOverlayFunc inOverlayFunc,
              miOverlayTransFunc transFunc)
{
    miOverlayScreenPtr pScreenPriv;

    if (!inOverlayFunc || !transFunc)
        return FALSE;

    if (!dixRegisterPrivateKey
        (&miOverlayWindowKeyRec, PRIVATE_WINDOW, sizeof(miOverlayWindowRec)))
        return FALSE;

    if (!dixRegisterPrivateKey(&miOverlayScreenKeyRec, PRIVATE_SCREEN, 0))
        return FALSE;

    if (!(pScreenPriv = calloc(1, sizeof(miOverlayScreenRec))))
        return FALSE;

    dixSetPrivate(&pScreen->devPrivates, miOverlayScreenKey, pScreenPriv);
    dixScreenHookClose(pScreen, miOverlayCloseScreen);

    pScreenPriv->InOverlay = inOverlayFunc;
    pScreenPriv->MakeTransparent = transFunc;
    pScreenPriv->underlayMarked = FALSE;

    pScreenPriv->CreateWindow = pScreen->CreateWindow;
    pScreenPriv->DestroyWindow = pScreen->DestroyWindow;
    pScreenPriv->UnrealizeWindow = pScreen->UnrealizeWindow;
    pScreenPriv->RealizeWindow = pScreen->RealizeWindow;

    pScreen->CreateWindow = miOverlayCreateWindow;
    pScreen->DestroyWindow = miOverlayDestroyWindow;
    pScreen->UnrealizeWindow = miOverlayUnrealizeWindow;
    pScreen->RealizeWindow = miOverlayRealizeWindow;

    pScreen->ReparentWindow = miOverlayReparentWindow;
    pScreen->RestackWindow = miOverlayRestackWindow;
    pScreen->MarkOverlappedWindows = miOverlayMarkOverlappedWindows;
    pScreen->MarkUnrealizedWindow = miOverlayMarkUnrealizedWindow;
    pScreen->ValidateTree = miOverlayValidateTree;
    pScreen->HandleExposures = miOverlayHandleExposures;
    pScreen->MoveWindow = miOverlayMoveWindow;
    pScreen->WindowExposures = miOverlayWindowExposures;
    pScreen->ResizeWindow = miOverlayResizeWindow;
    pScreen->MarkWindow = miOverlayMarkWindow;
    pScreen->ClearToBackground = miOverlayClearToBackground;
    pScreen->SetShape = miOverlaySetShape;
    pScreen->ChangeBorderWidth = miOverlayChangeBorderWidth;

    return TRUE;
}

static void miOverlayCloseScreen(CallbackListPtr *pcbl, ScreenPtr pScreen, void *unused)
{
    dixScreenUnhookClose(pScreen, miOverlayCloseScreen);

    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    if (!pScreenPriv)
        return;

    pScreen->CreateWindow = pScreenPriv->CreateWindow;
    pScreen->DestroyWindow = pScreenPriv->DestroyWindow;
    pScreen->UnrealizeWindow = pScreenPriv->UnrealizeWindow;
    pScreen->RealizeWindow = pScreenPriv->RealizeWindow;

    free(pScreenPriv);
    dixSetPrivate(&pScreen->devPrivates, miOverlayScreenKey, NULL);
}

static Bool
miOverlayCreateWindow(WindowPtr pWin)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    miOverlayWindowPtr pWinPriv = MIOVERLAY_GET_WINDOW_PRIVATE(pWin);
    miOverlayTreePtr pTree = NULL;
    Bool result = TRUE;

    pWinPriv->tree = NULL;

    if (!pWin->parent || !((*pScreenPriv->InOverlay) (pWin))) {
        if (!(pTree = (miOverlayTreePtr) calloc(1, sizeof(miOverlayTreeRec))))
            return FALSE;
    }

    if (pScreenPriv->CreateWindow) {
        pScreen->CreateWindow = pScreenPriv->CreateWindow;
        result = (*pScreen->CreateWindow) (pWin);
        pScreen->CreateWindow = miOverlayCreateWindow;
    }

    if (pTree) {
        if (result) {
            pTree->pWin = pWin;
            pTree->visibility = VisibilityNotViewable;
            pWinPriv->tree = pTree;
            if (pWin->parent) {
                RegionNull(&(pTree->borderClip));
                RegionNull(&(pTree->clipList));
                RebuildTree(pWin);
            }
            else {
                BoxRec fullBox;

                fullBox.x1 = 0;
                fullBox.y1 = 0;
                fullBox.x2 = pScreen->width;
                fullBox.y2 = pScreen->height;
                RegionInit(&(pTree->borderClip), &fullBox, 1);
                RegionInit(&(pTree->clipList), &fullBox, 1);
            }
        }
        else
            free(pTree);
    }

    return TRUE;
}

static Bool
miOverlayDestroyWindow(WindowPtr pWin)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    Bool result = TRUE;

    if (pTree) {
        if (pTree->prevSib)
            pTree->prevSib->nextSib = pTree->nextSib;
        else if (pTree->parent)
            pTree->parent->firstChild = pTree->nextSib;

        if (pTree->nextSib)
            pTree->nextSib->prevSib = pTree->prevSib;
        else if (pTree->parent)
            pTree->parent->lastChild = pTree->prevSib;

        RegionUninit(&(pTree->borderClip));
        RegionUninit(&(pTree->clipList));
        free(pTree);
    }

    if (pScreenPriv->DestroyWindow) {
        pScreen->DestroyWindow = pScreenPriv->DestroyWindow;
        result = (*pScreen->DestroyWindow) (pWin);
        pScreen->DestroyWindow = miOverlayDestroyWindow;
    }

    return result;
}

static Bool
miOverlayUnrealizeWindow(WindowPtr pWin)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    Bool result = TRUE;

    if (pTree)
        pTree->visibility = VisibilityNotViewable;

    if (pScreenPriv->UnrealizeWindow) {
        pScreen->UnrealizeWindow = pScreenPriv->UnrealizeWindow;
        result = (*pScreen->UnrealizeWindow) (pWin);
        pScreen->UnrealizeWindow = miOverlayUnrealizeWindow;
    }

    return result;
}

static Bool
miOverlayRealizeWindow(WindowPtr pWin)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    Bool result = TRUE;

    if (pScreenPriv->RealizeWindow) {
        pScreen->RealizeWindow = pScreenPriv->RealizeWindow;
        result = (*pScreen->RealizeWindow) (pWin);
        pScreen->RealizeWindow = miOverlayRealizeWindow;
    }

    /* we only need to catch the root window realization */

    if (result && !pWin->parent && !((*pScreenPriv->InOverlay) (pWin))) {
        BoxRec box;

        box.x1 = box.y1 = 0;
        box.x2 = pWin->drawable.width;
        box.y2 = pWin->drawable.height;
        (*pScreenPriv->MakeTransparent) (pScreen, 1, &box);
    }

    return result;
}

static void
miOverlayReparentWindow(WindowPtr pWin, WindowPtr pPriorParent)
{
    if (IN_UNDERLAY(pWin) || HasUnderlayChildren(pWin)) {
        /* This could probably be more optimal */
        RebuildTree(pWin->drawable.pScreen->root->firstChild);
    }
}

static void
miOverlayRestackWindow(WindowPtr pWin, WindowPtr oldNextSib)
{
    if (IN_UNDERLAY(pWin) || HasUnderlayChildren(pWin)) {
        /* This could probably be more optimal */
        RebuildTree(pWin);
    }
}

static Bool
miOverlayMarkOverlappedWindows(WindowPtr pWin,
                               WindowPtr pFirst, WindowPtr *pLayerWin)
{
    WindowPtr pChild, pLast;
    Bool overMarked, underMarked, doUnderlay, markAll;
    miOverlayTreePtr pTree = NULL, tLast, tChild;
    BoxPtr box;

    overMarked = underMarked = markAll = FALSE;

    if (pLayerWin)
        *pLayerWin = pWin;      /* hah! */

    doUnderlay = (IN_UNDERLAY(pWin) || HasUnderlayChildren(pWin));

    box = RegionExtents(&pWin->borderSize);

    if ((pChild = pFirst)) {
        pLast = pChild->parent->lastChild;
        while (1) {
            if (pChild == pWin)
                markAll = TRUE;

            if (doUnderlay && IN_UNDERLAY(pChild))
                pTree = MIOVERLAY_GET_WINDOW_TREE(pChild);

            if (pChild->viewable) {
                if (RegionBroken(&pChild->winSize))
                    SetWinSize(pChild);
                if (RegionBroken(&pChild->borderSize))
                    SetBorderSize(pChild);

                if (markAll || RegionContainsRect(&pChild->borderSize, box)) {
                    MARK_OVERLAY(pChild);
                    overMarked = TRUE;
                    if (doUnderlay && IN_UNDERLAY(pChild)) {
                        MARK_UNDERLAY(pChild);
                        underMarked = TRUE;
                    }
                    if (pChild->firstChild) {
                        pChild = pChild->firstChild;
                        continue;
                    }
                }
            }
            while (!pChild->nextSib && (pChild != pLast)) {
                pChild = pChild->parent;
                if (doUnderlay && IN_UNDERLAY(pChild))
                    pTree = MIOVERLAY_GET_WINDOW_TREE(pChild);
            }

            if (pChild == pWin)
                markAll = FALSE;

            if (pChild == pLast)
                break;

            pChild = pChild->nextSib;
        }
        if (overMarked)
            MARK_OVERLAY(pWin->parent);
    }

    if (doUnderlay && !pTree) {
        if (!(pTree = MIOVERLAY_GET_WINDOW_TREE(pWin))) {
            pChild = pWin->lastChild;
            while (1) {
                if ((pTree = MIOVERLAY_GET_WINDOW_TREE(pChild)))
                    break;

                if (pChild->lastChild) {
                    pChild = pChild->lastChild;
                    continue;
                }

                while (!pChild->prevSib)
                    pChild = pChild->parent;

                pChild = pChild->prevSib;
            }
        }
    }

    if (pTree && pTree->nextSib) {
        tChild = pTree->parent->lastChild;
        tLast = pTree->nextSib;

        while (1) {
            if (tChild->pWin->viewable) {
                if (RegionBroken(&tChild->pWin->winSize))
                    SetWinSize(tChild->pWin);
                if (RegionBroken(&tChild->pWin->borderSize))
                    SetBorderSize(tChild->pWin);

                if (RegionContainsRect(&(tChild->pWin->borderSize), box)) {
                    MARK_UNDERLAY(tChild->pWin);
                    underMarked = TRUE;
                }
            }

            if (tChild->lastChild) {
                tChild = tChild->lastChild;
                continue;
            }

            while (!tChild->prevSib && (tChild != tLast))
                tChild = tChild->parent;

            if (tChild == tLast)
                break;

            tChild = tChild->prevSib;
        }
    }

    if (underMarked) {
        ScreenPtr pScreen = pWin->drawable.pScreen;

        MARK_UNDERLAY(pTree->parent->pWin);
        MIOVERLAY_GET_SCREEN_PRIVATE(pScreen)->underlayMarked = TRUE;
    }

    return underMarked || overMarked;
}

static void
miOverlayComputeClips(WindowPtr pParent,
                      RegionPtr universe, VTKind kind, RegionPtr exposed)
{
    ScreenPtr pScreen = pParent->drawable.pScreen;
    int oldVis, newVis, dx, dy;
    BoxRec borderSize;
    RegionPtr borderVisible;
    RegionRec childUniverse, childUnion;
    miOverlayTreePtr tParent = MIOVERLAY_GET_WINDOW_TREE(pParent);
    miOverlayTreePtr tChild;
    Bool overlap;

    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 = tParent->visibility;
    switch (RegionContainsRect(universe, &borderSize)) {
    case rgnIN:
        newVis = VisibilityUnobscured;
        break;
    case rgnPART:
        newVis = VisibilityPartiallyObscured;
        {
            RegionPtr pBounding;

            if ((pBounding = wBoundingShape(pParent))) {
                switch (miShapedWindowIn(universe, pBounding,
                                         &borderSize,
                                         pParent->drawable.x,
                                         pParent->drawable.y)) {
                case rgnIN:
                    newVis = VisibilityUnobscured;
                    break;
                case rgnOUT:
                    newVis = VisibilityFullyObscured;
                    break;
                }
            }
        }
        break;
    default:
        newVis = VisibilityFullyObscured;
        break;
    }
    tParent->visibility = newVis;

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

    switch (kind) {
    case VTMap:
    case VTStack:
    case VTUnmap:
        break;
    case VTMove:
        if ((oldVis == newVis) &&
            ((oldVis == VisibilityFullyObscured) ||
             (oldVis == VisibilityUnobscured))) {
            tChild = tParent;
            while (1) {
                if (tChild->pWin->viewable) {
                    if (tChild->visibility != VisibilityFullyObscured) {
                        RegionTranslate(&tChild->borderClip, dx, dy);
                        RegionTranslate(&tChild->clipList, dx, dy);

                        tChild->pWin->drawable.serialNumber =
                            NEXT_SERIAL_NUMBER;
                        if (pScreen->ClipNotify)
                            (*pScreen->ClipNotify) (tChild->pWin, dx, dy);
                    }
                    if (tChild->valdata) {
                        RegionNull(&tChild->valdata->borderExposed);
                        if (HasParentRelativeBorder(tChild->pWin)) {
                            RegionSubtract(&tChild->valdata->borderExposed,
                                           &tChild->borderClip,
                                           &tChild->pWin->winSize);
                        }
                        RegionNull(&tChild->valdata->exposed);
                    }
                    if (tChild->firstChild) {
                        tChild = tChild->firstChild;
                        continue;
                    }
                }
                while (!tChild->nextSib && (tChild != tParent))
                    tChild = tChild->parent;
                if (tChild == tParent)
                    break;
                tChild = tChild->nextSib;
            }
            return;
        }
        /* fall through */
    default:
        if (dx || dy) {
            RegionTranslate(&tParent->borderClip, dx, dy);
            RegionTranslate(&tParent->clipList, dx, dy);
        }
        break;
    case VTBroken:
        RegionEmpty(&tParent->borderClip);
        RegionEmpty(&tParent->clipList);
        break;
    }

    borderVisible = tParent->valdata->borderVisible;
    RegionNull(&tParent->valdata->borderExposed);
    RegionNull(&tParent->valdata->exposed);

    if (HasBorder(pParent)) {
        if (borderVisible) {
            RegionSubtract(exposed, universe, borderVisible);
            RegionDestroy(borderVisible);
        }
        else
            RegionSubtract(exposed, universe, &tParent->borderClip);

        if (HasParentRelativeBorder(pParent) && (dx || dy))
            RegionSubtract(&tParent->valdata->borderExposed,
                           universe, &pParent->winSize);
        else
            RegionSubtract(&tParent->valdata->borderExposed,
                           exposed, &pParent->winSize);

        RegionCopy(&tParent->borderClip, universe);
        RegionIntersect(universe, universe, &pParent->winSize);
    }
    else
        RegionCopy(&tParent->borderClip, universe);

    if ((tChild = tParent->firstChild) && pParent->mapped) {
        RegionNull(&childUniverse);
        RegionNull(&childUnion);

        for (; tChild; tChild = tChild->nextSib) {
            if (tChild->pWin->viewable)
                RegionAppend(&childUnion, &tChild->pWin->borderSize);
        }

        RegionValidate(&childUnion, &overlap);

        for (tChild = tParent->firstChild; tChild; tChild = tChild->nextSib) {
            if (tChild->pWin->viewable) {
                if (tChild->valdata) {
                    RegionIntersect(&childUniverse, universe,
                                    &tChild->pWin->borderSize);
                    miOverlayComputeClips(tChild->pWin, &childUniverse,
                                          kind, exposed);
                }
                if (overlap)
                    RegionSubtract(universe, universe,
                                   &tChild->pWin->borderSize);
            }
        }
        if (!overlap)
            RegionSubtract(universe, universe, &childUnion);
        RegionUninit(&childUnion);
        RegionUninit(&childUniverse);
    }

    if (oldVis == VisibilityFullyObscured || oldVis == VisibilityNotViewable) {
        RegionCopy(&tParent->valdata->exposed, universe);
    }
    else if (newVis != VisibilityFullyObscured &&
             newVis != VisibilityNotViewable) {
        RegionSubtract(&tParent->valdata->exposed,
                       universe, &tParent->clipList);
    }

    /* HACK ALERT - copying contents of regions, instead of regions */
    {
        RegionRec tmp;

        tmp = tParent->clipList;
        tParent->clipList = *universe;
        *universe = tmp;
    }

    pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;

    if (pScreen->ClipNotify)
        (*pScreen->ClipNotify) (pParent, dx, dy);
}

static void
miOverlayMarkWindow(WindowPtr pWin)
{
    miOverlayTreePtr pTree = NULL;
    WindowPtr pChild, pGrandChild;

    miMarkWindow(pWin);

    /* look for UnmapValdata among immediate children */

    if (!(pChild = pWin->firstChild))
        return;

    for (; pChild; pChild = pChild->nextSib) {
        if (pChild->valdata == UnmapValData) {
            if (IN_UNDERLAY(pChild)) {
                pTree = MIOVERLAY_GET_WINDOW_TREE(pChild);
                pTree->valdata = (miOverlayValDataPtr) UnmapValData;
                continue;
            }
            else {
                if (!(pGrandChild = pChild->firstChild))
                    continue;

                while (1) {
                    if (IN_UNDERLAY(pGrandChild)) {
                        pTree = MIOVERLAY_GET_WINDOW_TREE(pGrandChild);
                        pTree->valdata = (miOverlayValDataPtr) UnmapValData;
                    }
                    else if (pGrandChild->firstChild) {
                        pGrandChild = pGrandChild->firstChild;
                        continue;
                    }

                    while (!pGrandChild->nextSib && (pGrandChild != pChild))
                        pGrandChild = pGrandChild->parent;

                    if (pChild == pGrandChild)
                        break;

                    pGrandChild = pGrandChild->nextSib;
                }
            }
        }
    }

    if (pTree) {
        MARK_UNDERLAY(pTree->parent->pWin);
        MIOVERLAY_GET_SCREEN_PRIVATE(pWin->drawable.pScreen)->underlayMarked =
            TRUE;
    }
}

static void
miOverlayMarkUnrealizedWindow(WindowPtr pChild,
                              WindowPtr pWin, Bool fromConfigure)
{
    if ((pChild != pWin) || fromConfigure) {
        miOverlayTreePtr pTree;

        RegionEmpty(&pChild->clipList);
        if (pChild->drawable.pScreen->ClipNotify)
            (*pChild->drawable.pScreen->ClipNotify) (pChild, 0, 0);
        RegionEmpty(&pChild->borderClip);
        if ((pTree = MIOVERLAY_GET_WINDOW_TREE(pChild))) {
            if (pTree->valdata != (miOverlayValDataPtr) UnmapValData) {
                RegionEmpty(&pTree->clipList);
                RegionEmpty(&pTree->borderClip);
            }
        }
    }
}

static int
miOverlayValidateTree(WindowPtr pParent, WindowPtr pChild,      /* first child effected */
                      VTKind kind)
{
    ScreenPtr pScreen = pParent->drawable.pScreen;
    miOverlayScreenPtr pPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    RegionRec totalClip, childClip, exposed;
    miOverlayTreePtr tParent, tChild, tWin;
    Bool overlap;
    WindowPtr newParent;

    if (!pPriv->underlayMarked)
        goto SKIP_UNDERLAY;

    if (!pChild)
        pChild = pParent->firstChild;

    RegionNull(&totalClip);
    RegionNull(&childClip);
    RegionNull(&exposed);

    newParent = pParent;

    while (IN_OVERLAY(newParent))
        newParent = newParent->parent;

    tParent = MIOVERLAY_GET_WINDOW_TREE(newParent);

    if (IN_UNDERLAY(pChild))
        tChild = MIOVERLAY_GET_WINDOW_TREE(pChild);
    else
        tChild = tParent->firstChild;

    if (RegionBroken(&tParent->clipList) && !RegionBroken(&tParent->borderClip)) {
        kind = VTBroken;
        RegionCopy(&totalClip, &tParent->borderClip);
        RegionIntersect(&totalClip, &totalClip, &tParent->pWin->winSize);

        for (tWin = tParent->firstChild; tWin != tChild; tWin = tWin->nextSib) {
            if (tWin->pWin->viewable)
                RegionSubtract(&totalClip, &totalClip, &tWin->pWin->borderSize);
        }
        RegionEmpty(&tParent->clipList);
    }
    else {
        for (tWin = tChild; tWin; tWin = tWin->nextSib) {
            if (tWin->valdata)
                RegionAppend(&totalClip, &tWin->borderClip);
        }
        RegionValidate(&totalClip, &overlap);
    }

    if (kind != VTStack)
        RegionUnion(&totalClip, &totalClip, &tParent->clipList);

    for (tWin = tChild; tWin; tWin = tWin->nextSib) {
        if (tWin->valdata) {
            if (tWin->pWin->viewable) {
                RegionIntersect(&childClip, &totalClip,
                                &tWin->pWin->borderSize);
                miOverlayComputeClips(tWin->pWin, &childClip, kind, &exposed);
                RegionSubtract(&totalClip, &totalClip, &tWin->pWin->borderSize);
            }
            else {              /* Means we are unmapping */
                RegionEmpty(&tWin->clipList);
                RegionEmpty(&tWin->borderClip);
                tWin->valdata = NULL;
            }
        }
    }

    RegionUninit(&childClip);

    if (!((*pPriv->InOverlay) (newParent))) {
        RegionNull(&tParent->valdata->exposed);
        RegionNull(&tParent->valdata->borderExposed);
    }

    switch (kind) {
    case VTStack:
        break;
    default:
        if (!((*pPriv->InOverlay) (newParent)))
            RegionSubtract(&tParent->valdata->exposed, &totalClip,
                           &tParent->clipList);
        /* fall through */
    case VTMap:
        RegionCopy(&tParent->clipList, &totalClip);
        if (!((*pPriv->InOverlay) (newParent)))
            newParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;
        break;
    }

    RegionUninit(&totalClip);
    RegionUninit(&exposed);

 SKIP_UNDERLAY:

    miValidateTree(pParent, pChild, kind);

    return 1;
}

static void
miOverlayHandleExposures(WindowPtr pWin)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    WindowPtr pChild;
    ValidatePtr val;
    WindowExposuresProcPtr WindowExposures;

    WindowExposures = pWin->drawable.pScreen->WindowExposures;
    if (pPriv->underlayMarked) {
        miOverlayTreePtr pTree;
        miOverlayValDataPtr mival;

        pChild = pWin;
        while (IN_OVERLAY(pChild))
            pChild = pChild->parent;

        pTree = MIOVERLAY_GET_WINDOW_TREE(pChild);

        while (1) {
            if ((mival = pTree->valdata)) {
                if (!((*pPriv->InOverlay) (pTree->pWin))) {
                    if (RegionNotEmpty(&mival->borderExposed)) {
                        pScreen->PaintWindow(pTree->pWin, &mival->borderExposed,
                                             PW_BORDER);
                    }
                    RegionUninit(&mival->borderExposed);

                    (*WindowExposures) (pTree->pWin, &mival->exposed);
                    RegionUninit(&mival->exposed);
                }
                free(mival);
                pTree->valdata = NULL;
                if (pTree->firstChild) {
                    pTree = pTree->firstChild;
                    continue;
                }
            }
            while (!pTree->nextSib && (pTree->pWin != pChild))
                pTree = pTree->parent;
            if (pTree->pWin == pChild)
                break;
            pTree = pTree->nextSib;
        }
        pPriv->underlayMarked = FALSE;
    }

    pChild = pWin;
    while (1) {
        if ((val = pChild->valdata)) {
            if (!((*pPriv->InOverlay) (pChild))) {
                RegionUnion(&val->after.exposed, &val->after.exposed,
                            &val->after.borderExposed);

                if (RegionNotEmpty(&val->after.exposed)) {
                    (*(MIOVERLAY_GET_SCREEN_PRIVATE(pScreen)->MakeTransparent))
                        (pScreen, RegionNumRects(&val->after.exposed),
                         RegionRects(&val->after.exposed));
                }
            }
            else {
                if (RegionNotEmpty(&val->after.borderExposed)) {
                    pScreen->PaintWindow(pChild, &val->after.borderExposed,
                                         PW_BORDER);
                }
                (*WindowExposures) (pChild, &val->after.exposed);
            }
            RegionUninit(&val->after.borderExposed);
            RegionUninit(&val->after.exposed);
            free(val);
            pChild->valdata = NULL;
            if (pChild->firstChild) {
                pChild = pChild->firstChild;
                continue;
            }
        }
        while (!pChild->nextSib && (pChild != pWin))
            pChild = pChild->parent;
        if (pChild == pWin)
            break;
        pChild = pChild->nextSib;
    }
}

static void
miOverlayMoveWindow(WindowPtr pWin,
                    int x, int y, WindowPtr pNextSib, VTKind kind)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    WindowPtr pParent, windowToValidate;
    Bool WasViewable = (Bool) (pWin->viewable);
    short bw;
    RegionRec overReg, underReg;
    DDXPointRec oldpt;

    if (!(pParent = pWin->parent))
        return;
    bw = wBorderWidth(pWin);

    oldpt.x = pWin->drawable.x;
    oldpt.y = pWin->drawable.y;
    if (WasViewable) {
        RegionNull(&overReg);
        RegionNull(&underReg);
        if (pTree) {
            RegionCopy(&overReg, &pWin->borderClip);
            RegionCopy(&underReg, &pTree->borderClip);
        }
        else {
            RegionCopy(&overReg, &pWin->borderClip);
            CollectUnderlayChildrenRegions(pWin, &underReg);
        }
        (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);
    }
    pWin->origin.x = x + (int) bw;
    pWin->origin.y = y + (int) bw;
    x = pWin->drawable.x = pParent->drawable.x + x + (int) bw;
    y = pWin->drawable.y = pParent->drawable.y + y + (int) bw;

    SetWinSize(pWin);
    SetBorderSize(pWin);

    (*pScreen->PositionWindow) (pWin, x, y);

    windowToValidate = MoveWindowInStack(pWin, pNextSib);

    ResizeChildrenWinSize(pWin, x - oldpt.x, y - oldpt.y, 0, 0);

    if (WasViewable) {
        miOverlayScreenPtr pPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);

        (*pScreen->MarkOverlappedWindows) (pWin, windowToValidate, NULL);

        (*pScreen->ValidateTree) (pWin->parent, NullWindow, kind);
        if (RegionNotEmpty(&underReg)) {
            pPriv->copyUnderlay = TRUE;
            (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, &underReg);
        }
        RegionUninit(&underReg);
        if (RegionNotEmpty(&overReg)) {
            pPriv->copyUnderlay = FALSE;
            (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, &overReg);
        }
        RegionUninit(&overReg);
        (*pScreen->HandleExposures) (pWin->parent);

        if (pScreen->PostValidateTree)
            (*pScreen->PostValidateTree) (pWin->parent, NullWindow, kind);
    }
    if (pWin->realized)
        WindowsRestructured();
}

#ifndef RECTLIMIT
#define RECTLIMIT 25
#endif

static void
miOverlayWindowExposures(WindowPtr pWin, RegionPtr prgn)
{
    RegionPtr exposures = prgn;
    ScreenPtr pScreen = pWin->drawable.pScreen;

    if (prgn && !RegionNil(prgn)) {
        RegionRec expRec;
        int clientInterested =
            (pWin->eventMask | wOtherEventMasks(pWin)) & ExposureMask;
        if (clientInterested && (RegionNumRects(prgn) > RECTLIMIT)) {
            miOverlayScreenPtr pPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
            BoxRec box;

            box = *RegionExtents(prgn);
            exposures = &expRec;
            RegionInit(exposures, &box, 1);
            RegionReset(prgn, &box);
            /* This is the only reason why we are replacing mi's version
               of this file */

            if (!((*pPriv->InOverlay) (pWin))) {
                miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

                RegionIntersect(prgn, prgn, &pTree->clipList);
            }
            else
                RegionIntersect(prgn, prgn, &pWin->clipList);
        }
        pScreen->PaintWindow(pWin, prgn, PW_BACKGROUND);
        if (clientInterested)
            miSendExposures(pWin, exposures,
                            pWin->drawable.x, pWin->drawable.y);
        if (exposures == &expRec)
            RegionUninit(exposures);
        RegionEmpty(prgn);
    }
}

typedef struct {
    RegionPtr over;
    RegionPtr under;
} miOverlayTwoRegions;

static int
miOverlayRecomputeExposures(WindowPtr pWin, void *value)
{
    miOverlayTwoRegions *pValid = (miOverlayTwoRegions *) value;
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

    if (pWin->valdata) {
        /*
         * compute exposed regions of this window
         */
        RegionSubtract(&pWin->valdata->after.exposed,
                       &pWin->clipList, pValid->over);
        /*
         * compute exposed regions of the border
         */
        RegionSubtract(&pWin->valdata->after.borderExposed,
                       &pWin->borderClip, &pWin->winSize);
        RegionSubtract(&pWin->valdata->after.borderExposed,
                       &pWin->valdata->after.borderExposed, pValid->over);
    }

    if (pTree && pTree->valdata) {
        RegionSubtract(&pTree->valdata->exposed,
                       &pTree->clipList, pValid->under);
        RegionSubtract(&pTree->valdata->borderExposed,
                       &pTree->borderClip, &pWin->winSize);
        RegionSubtract(&pTree->valdata->borderExposed,
                       &pTree->valdata->borderExposed, pValid->under);
    }
    else if (!pWin->valdata)
        return WT_NOMATCH;

    return WT_WALKCHILDREN;
}

static void
miOverlayResizeWindow(WindowPtr pWin,
                      int x, int y,
                      unsigned int w, unsigned int h, WindowPtr pSib)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    WindowPtr pParent;
    miOverlayTreePtr tChild, pTree;
    Bool WasViewable = (Bool) (pWin->viewable);
    unsigned short width = pWin->drawable.width;
    unsigned short height = pWin->drawable.height;
    short oldx = pWin->drawable.x;
    short oldy = pWin->drawable.y;
    int bw = wBorderWidth(pWin);
    short dw, dh;
    DDXPointRec oldpt;
    RegionPtr oldRegion = NULL, oldRegion2 = NULL;
    WindowPtr pFirstChange;
    WindowPtr pChild;
    RegionPtr gravitate[StaticGravity + 1];
    RegionPtr gravitate2[StaticGravity + 1];
    unsigned g;
    int nx, ny;                 /* destination x,y */
    int newx, newy;             /* new inner window position */
    RegionPtr pRegion = NULL;
    RegionPtr destClip, destClip2;
    RegionPtr oldWinClip = NULL, oldWinClip2 = NULL;
    RegionPtr borderVisible = NullRegion;
    RegionPtr borderVisible2 = NullRegion;
    Bool shrunk = FALSE;        /* shrunk in an inner dimension */
    Bool moved = FALSE;         /* window position changed */
    Bool doUnderlay;

    /* if this is a root window, can't be resized */
    if (!(pParent = pWin->parent))
        return;

    pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    doUnderlay = ((pTree) || HasUnderlayChildren(pWin));
    newx = pParent->drawable.x + x + bw;
    newy = pParent->drawable.y + y + bw;
    if (WasViewable) {
        /*
         * save the visible region of the window
         */
        oldRegion = RegionCreate(NullBox, 1);
        RegionCopy(oldRegion, &pWin->winSize);
        if (doUnderlay) {
            oldRegion2 = RegionCreate(NullBox, 1);
            RegionCopy(oldRegion2, &pWin->winSize);
        }

        /*
         * categorize child windows into regions to be moved
         */
        for (g = 0; g <= StaticGravity; g++)
            gravitate[g] = gravitate2[g] = NULL;
        for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) {
            g = pChild->winGravity;
            if (g != UnmapGravity) {
                if (!gravitate[g])
                    gravitate[g] = RegionCreate(NullBox, 1);
                RegionUnion(gravitate[g], gravitate[g], &pChild->borderClip);

                if (doUnderlay) {
                    if (!gravitate2[g])
                        gravitate2[g] = RegionCreate(NullBox, 0);

                    if ((tChild = MIOVERLAY_GET_WINDOW_TREE(pChild))) {
                        RegionUnion(gravitate2[g],
                                    gravitate2[g], &tChild->borderClip);
                    }
                    else
                        CollectUnderlayChildrenRegions(pChild, gravitate2[g]);
                }
            }
            else {
                UnmapWindow(pChild, TRUE);
            }
        }
        (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);

        oldWinClip = oldWinClip2 = NULL;
        if (pWin->bitGravity != ForgetGravity) {
            oldWinClip = RegionCreate(NullBox, 1);
            RegionCopy(oldWinClip, &pWin->clipList);
            if (pTree) {
                oldWinClip2 = RegionCreate(NullBox, 1);
                RegionCopy(oldWinClip2, &pTree->clipList);
            }
        }
        /*
         * if the window is changing size, borderExposed
         * can't be computed correctly without some help.
         */
        if (pWin->drawable.height > h || pWin->drawable.width > w)
            shrunk = TRUE;

        if (newx != oldx || newy != oldy)
            moved = TRUE;

        if ((pWin->drawable.height != h || pWin->drawable.width != w) &&
            HasBorder(pWin)) {
            borderVisible = RegionCreate(NullBox, 1);
            if (pTree)
                borderVisible2 = RegionCreate(NullBox, 1);
            /* for tiled borders, we punt and draw the whole thing */
            if (pWin->borderIsPixel || !moved) {
                if (shrunk || moved)
                    RegionSubtract(borderVisible,
                                   &pWin->borderClip, &pWin->winSize);
                else
                    RegionCopy(borderVisible, &pWin->borderClip);
                if (pTree) {
                    if (shrunk || moved)
                        RegionSubtract(borderVisible,
                                       &pTree->borderClip, &pWin->winSize);
                    else
                        RegionCopy(borderVisible, &pTree->borderClip);
                }
            }
        }
    }
    pWin->origin.x = x + bw;
    pWin->origin.y = y + bw;
    pWin->drawable.height = h;
    pWin->drawable.width = w;

    x = pWin->drawable.x = newx;
    y = pWin->drawable.y = newy;

    SetWinSize(pWin);
    SetBorderSize(pWin);

    dw = (int) w - (int) width;
    dh = (int) h - (int) height;
    ResizeChildrenWinSize(pWin, x - oldx, y - oldy, dw, dh);

    /* let the hardware adjust background and border pixmaps, if any */
    (*pScreen->PositionWindow) (pWin, x, y);

    pFirstChange = MoveWindowInStack(pWin, pSib);

    if (WasViewable) {
        pRegion = RegionCreate(NullBox, 1);

        (*pScreen->MarkOverlappedWindows) (pWin, pFirstChange, NULL);

        pWin->valdata->before.resized = TRUE;
        pWin->valdata->before.borderVisible = borderVisible;
        if (pTree)
            pTree->valdata->borderVisible = borderVisible2;

        (*pScreen->ValidateTree) (pWin->parent, pFirstChange, VTOther);
        /*
         * the entire window is trashed unless bitGravity
         * recovers portions of it
         */
        RegionCopy(&pWin->valdata->after.exposed, &pWin->clipList);
        if (pTree)
            RegionCopy(&pTree->valdata->exposed, &pTree->clipList);
    }

    GravityTranslate(x, y, oldx, oldy, dw, dh, pWin->bitGravity, &nx, &ny);

    if (WasViewable) {
        miOverlayScreenPtr pPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
        miOverlayTwoRegions TwoRegions;

        /* avoid the border */
        if (HasBorder(pWin)) {
            int offx, offy, dx, dy;

            /* kruft to avoid double translates for each gravity */
            offx = 0;
            offy = 0;
            for (g = 0; g <= StaticGravity; g++) {
                if (!gravitate[g] && !gravitate2[g])
                    continue;

                /* align winSize to gravitate[g].
                 * winSize is in new coordinates,
                 * gravitate[g] is still in old coordinates */
                GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny);

                dx = (oldx - nx) - offx;
                dy = (oldy - ny) - offy;
                if (dx || dy) {
                    RegionTranslate(&pWin->winSize, dx, dy);
                    offx += dx;
                    offy += dy;
                }
                if (gravitate[g])
                    RegionIntersect(gravitate[g], gravitate[g], &pWin->winSize);
                if (gravitate2[g])
                    RegionIntersect(gravitate2[g], gravitate2[g],
                                    &pWin->winSize);
            }
            /* get winSize back where it belongs */
            if (offx || offy)
                RegionTranslate(&pWin->winSize, -offx, -offy);
        }
        /*
         * add screen bits to the appropriate bucket
         */

        if (oldWinClip2) {
            RegionCopy(pRegion, oldWinClip2);
            RegionTranslate(pRegion, nx - oldx, ny - oldy);
            RegionIntersect(oldWinClip2, pRegion, &pTree->clipList);

            for (g = pWin->bitGravity + 1; g <= StaticGravity; g++) {
                if (gravitate2[g])
                    RegionSubtract(oldWinClip2, oldWinClip2, gravitate2[g]);
            }
            RegionTranslate(oldWinClip2, oldx - nx, oldy - ny);
            g = pWin->bitGravity;
            if (!gravitate2[g])
                gravitate2[g] = oldWinClip2;
            else {
                RegionUnion(gravitate2[g], gravitate2[g], oldWinClip2);
                RegionDestroy(oldWinClip2);
            }
        }

        if (oldWinClip) {
            /*
             * clip to new clipList
             */
            RegionCopy(pRegion, oldWinClip);
            RegionTranslate(pRegion, nx - oldx, ny - oldy);
            RegionIntersect(oldWinClip, pRegion, &pWin->clipList);
            /*
             * don't step on any gravity bits which will be copied after this
             * region.  Note -- this assumes that the regions will be copied
             * in gravity order.
             */
            for (g = pWin->bitGravity + 1; g <= StaticGravity; g++) {
                if (gravitate[g])
                    RegionSubtract(oldWinClip, oldWinClip, gravitate[g]);
            }
            RegionTranslate(oldWinClip, oldx - nx, oldy - ny);
            g = pWin->bitGravity;
            if (!gravitate[g])
                gravitate[g] = oldWinClip;
            else {
                RegionUnion(gravitate[g], gravitate[g], oldWinClip);
                RegionDestroy(oldWinClip);
            }
        }

        /*
         * move the bits on the screen
         */

        destClip = destClip2 = NULL;

        for (g = 0; g <= StaticGravity; g++) {
            if (!gravitate[g] && !gravitate2[g])
                continue;

            GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny);

            oldpt.x = oldx + (x - nx);
            oldpt.y = oldy + (y - ny);

            /* Note that gravitate[g] is *translated* by CopyWindow */

            /* only copy the remaining useful bits */

            if (gravitate[g])
                RegionIntersect(gravitate[g], gravitate[g], oldRegion);
            if (gravitate2[g])
                RegionIntersect(gravitate2[g], gravitate2[g], oldRegion2);

            /* clip to not overwrite already copied areas */

            if (destClip && gravitate[g]) {
                RegionTranslate(destClip, oldpt.x - x, oldpt.y - y);
                RegionSubtract(gravitate[g], gravitate[g], destClip);
                RegionTranslate(destClip, x - oldpt.x, y - oldpt.y);
            }
            if (destClip2 && gravitate2[g]) {
                RegionTranslate(destClip2, oldpt.x - x, oldpt.y - y);
                RegionSubtract(gravitate2[g], gravitate2[g], destClip2);
                RegionTranslate(destClip2, x - oldpt.x, y - oldpt.y);
            }

            /* and move those bits */

            if (oldpt.x != x || oldpt.y != y) {
                if (gravitate2[g]) {
                    pPriv->copyUnderlay = TRUE;
                    (*pScreen->CopyWindow) (pWin, oldpt, gravitate2[g]);
                }
                if (gravitate[g]) {
                    pPriv->copyUnderlay = FALSE;
                    (*pScreen->CopyWindow) (pWin, oldpt, gravitate[g]);
                }
            }

            /* remove any overwritten bits from the remaining useful bits */

            if (gravitate[g])
                RegionSubtract(oldRegion, oldRegion, gravitate[g]);
            if (gravitate2[g])
                RegionSubtract(oldRegion2, oldRegion2, gravitate2[g]);

            /*
             * recompute exposed regions of child windows
             */

            for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) {
                if (pChild->winGravity != g)
                    continue;

                TwoRegions.over = gravitate[g];
                TwoRegions.under = gravitate2[g];

                TraverseTree(pChild, miOverlayRecomputeExposures,
                             (void *) (&TwoRegions));
            }

            /*
             * remove the successfully copied regions of the
             * window from its exposed region
             */

            if (g == pWin->bitGravity) {
                if (gravitate[g])
                    RegionSubtract(&pWin->valdata->after.exposed,
                                   &pWin->valdata->after.exposed, gravitate[g]);
                if (gravitate2[g] && pTree)
                    RegionSubtract(&pTree->valdata->exposed,
                                   &pTree->valdata->exposed, gravitate2[g]);
            }
            if (gravitate[g]) {
                if (!destClip)
                    destClip = gravitate[g];
                else {
                    RegionUnion(destClip, destClip, gravitate[g]);
                    RegionDestroy(gravitate[g]);
                }
            }
            if (gravitate2[g]) {
                if (!destClip2)
                    destClip2 = gravitate2[g];
                else {
                    RegionUnion(destClip2, destClip2, gravitate2[g]);
                    RegionDestroy(gravitate2[g]);
                }
            }
        }

        RegionDestroy(pRegion);
        RegionDestroy(oldRegion);
        if (doUnderlay)
            RegionDestroy(oldRegion2);
        if (destClip)
            RegionDestroy(destClip);
        if (destClip2)
            RegionDestroy(destClip2);
        (*pScreen->HandleExposures) (pWin->parent);
        if (pScreen->PostValidateTree)
            (*pScreen->PostValidateTree) (pWin->parent, pFirstChange, VTOther);
    }
    if (pWin->realized)
        WindowsRestructured();
}

static void
miOverlaySetShape(WindowPtr pWin, int kind)
{
    Bool WasViewable = (Bool) (pWin->viewable);
    ScreenPtr pScreen = pWin->drawable.pScreen;

    if (kind != ShapeInput) {
        if (WasViewable) {
            (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);

            if (HasBorder(pWin)) {
                RegionPtr borderVisible;

                borderVisible = RegionCreate(NullBox, 1);
                RegionSubtract(borderVisible,
                               &pWin->borderClip, &pWin->winSize);
                pWin->valdata->before.borderVisible = borderVisible;
                pWin->valdata->before.resized = TRUE;
                if (IN_UNDERLAY(pWin)) {
                    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
                    RegionPtr borderVisible2;

                    borderVisible2 = RegionCreate(NULL, 1);
                    RegionSubtract(borderVisible2,
                                   &pTree->borderClip, &pWin->winSize);
                    pTree->valdata->borderVisible = borderVisible2;
                }
            }
        }

        SetWinSize(pWin);
        SetBorderSize(pWin);

        ResizeChildrenWinSize(pWin, 0, 0, 0, 0);

        if (WasViewable) {
            (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);
            (*pScreen->ValidateTree) (pWin->parent, NullWindow, VTOther);
            (*pScreen->HandleExposures) (pWin->parent);
            if (pScreen->PostValidateTree)
                (*pScreen->PostValidateTree) (pWin->parent, NullWindow,
                                              VTOther);
        }
    }
    if (pWin->realized)
        WindowsRestructured();
    CheckCursorConfinement(pWin);
}

static void
miOverlayChangeBorderWidth(WindowPtr pWin, unsigned int width)
{
    int oldwidth;
    ScreenPtr pScreen;
    Bool WasViewable = (Bool) (pWin->viewable);
    Bool HadBorder;

    oldwidth = wBorderWidth(pWin);
    if (oldwidth == width)
        return;
    HadBorder = HasBorder(pWin);
    pScreen = pWin->drawable.pScreen;
    if (WasViewable && (width < oldwidth))
        (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);

    pWin->borderWidth = width;
    SetBorderSize(pWin);

    if (WasViewable) {
        if (width > oldwidth) {
            (*pScreen->MarkOverlappedWindows) (pWin, pWin, NULL);

            if (HadBorder) {
                RegionPtr borderVisible;

                borderVisible = RegionCreate(NULL, 1);
                RegionSubtract(borderVisible,
                               &pWin->borderClip, &pWin->winSize);
                pWin->valdata->before.borderVisible = borderVisible;
                if (IN_UNDERLAY(pWin)) {
                    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
                    RegionPtr borderVisible2;

                    borderVisible2 = RegionCreate(NULL, 1);
                    RegionSubtract(borderVisible2,
                                   &pTree->borderClip, &pWin->winSize);
                    pTree->valdata->borderVisible = borderVisible2;
                }
            }
        }
        (*pScreen->ValidateTree) (pWin->parent, pWin, VTOther);
        (*pScreen->HandleExposures) (pWin->parent);

        if (pScreen->PostValidateTree)
            (*pScreen->PostValidateTree) (pWin->parent, pWin, VTOther);
    }
    if (pWin->realized)
        WindowsRestructured();
}

/*  We need this as an addition since the xf86 common code doesn't
    know about the second tree which is static to this file.  */

void
miOverlaySetRootClip(ScreenPtr pScreen, Bool enable)
{
    WindowPtr pRoot = pScreen->root;
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pRoot);

    MARK_UNDERLAY(pRoot);

    if (enable) {
        BoxRec box;

        box.x1 = 0;
        box.y1 = 0;
        box.x2 = pScreen->width;
        box.y2 = pScreen->height;

        RegionReset(&pTree->borderClip, &box);
    }
    else
        RegionEmpty(&pTree->borderClip);

    RegionBreak(&pTree->clipList);
}

static void
miOverlayClearToBackground(WindowPtr pWin,
                           int x, int y, int w, int h, Bool generateExposures)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    BoxRec box;
    RegionRec reg;
    ScreenPtr pScreen = pWin->drawable.pScreen;
    miOverlayScreenPtr pScreenPriv = MIOVERLAY_GET_SCREEN_PRIVATE(pScreen);
    RegionPtr clipList;
    BoxPtr extents;
    int x1, y1, x2, y2;

    x1 = pWin->drawable.x + x;
    y1 = pWin->drawable.y + y;
    if (w)
        x2 = x1 + (int) w;
    else
        x2 = x1 + (int) pWin->drawable.width - (int) x;
    if (h)
        y2 = y1 + h;
    else
        y2 = y1 + (int) pWin->drawable.height - (int) y;

    clipList = ((*pScreenPriv->InOverlay) (pWin)) ? &pWin->clipList :
        &pTree->clipList;

    extents = RegionExtents(clipList);

    if (x1 < extents->x1)
        x1 = extents->x1;
    if (x2 > extents->x2)
        x2 = extents->x2;
    if (y1 < extents->y1)
        y1 = extents->y1;
    if (y2 > extents->y2)
        y2 = extents->y2;

    if (x2 <= x1 || y2 <= y1)
        x2 = x1 = y2 = y1 = 0;

    box.x1 = x1;
    box.x2 = x2;
    box.y1 = y1;
    box.y2 = y2;

    RegionInit(&reg, &box, 1);

    RegionIntersect(&reg, &reg, clipList);
    if (generateExposures)
        (*pScreen->WindowExposures) (pWin, &reg);
    else if (pWin->backgroundState != None)
        pScreen->PaintWindow(pWin, &reg, PW_BACKGROUND);
    RegionUninit(&reg);
}

/****************************************************************/

/* not used */
Bool
miOverlayGetPrivateClips(WindowPtr pWin,
                         RegionPtr *borderClip, RegionPtr *clipList)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

    if (pTree) {
        *borderClip = &(pTree->borderClip);
        *clipList = &(pTree->clipList);
        return TRUE;
    }

    *borderClip = *clipList = NULL;

    return FALSE;
}

Bool
miOverlayCopyUnderlay(ScreenPtr pScreen)
{
    return MIOVERLAY_GET_SCREEN_PRIVATE(pScreen)->copyUnderlay;
}

void
miOverlayComputeCompositeClip(GCPtr pGC, WindowPtr pWin)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);
    RegionPtr pregWin;
    Bool freeTmpClip, freeCompClip;

    if (!pTree) {
        miComputeCompositeClip(pGC, &pWin->drawable);
        return;
    }

    if (pGC->subWindowMode == IncludeInferiors) {
        pregWin = RegionCreate(NullBox, 1);
        freeTmpClip = TRUE;
        if (pWin->parent || (screenIsSaved != SCREEN_SAVER_ON) ||
            !HasSaverWindow(pGC->pScreen)) {
            RegionIntersect(pregWin, &pTree->borderClip, &pWin->winSize);
        }
    }
    else {
        pregWin = &pTree->clipList;
        freeTmpClip = FALSE;
    }
    freeCompClip = pGC->freeCompClip;
    if (!pGC->clientClip) {
        if (freeCompClip)
            RegionDestroy(pGC->pCompositeClip);
        pGC->pCompositeClip = pregWin;
        pGC->freeCompClip = freeTmpClip;
    }
    else {
        RegionTranslate(pGC->clientClip,
                        pWin->drawable.x + pGC->clipOrg.x,
                        pWin->drawable.y + pGC->clipOrg.y);

        if (freeCompClip) {
            RegionIntersect(pGC->pCompositeClip, pregWin, pGC->clientClip);
            if (freeTmpClip)
                RegionDestroy(pregWin);
        }
        else if (freeTmpClip) {
            RegionIntersect(pregWin, pregWin, pGC->clientClip);
            pGC->pCompositeClip = pregWin;
        }
        else {
            pGC->pCompositeClip = RegionCreate(NullBox, 0);
            RegionIntersect(pGC->pCompositeClip, pregWin, pGC->clientClip);
        }
        pGC->freeCompClip = TRUE;
        RegionTranslate(pGC->clientClip,
                        -(pWin->drawable.x + pGC->clipOrg.x),
                        -(pWin->drawable.y + pGC->clipOrg.y));
    }
}

Bool
miOverlayCollectUnderlayRegions(WindowPtr pWin, RegionPtr *region)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

    if (pTree) {
        *region = &pTree->borderClip;
        return FALSE;
    }

    *region = RegionCreate(NullBox, 0);

    CollectUnderlayChildrenRegions(pWin, *region);

    return TRUE;
}

static miOverlayTreePtr
DoLeaf(WindowPtr pWin, miOverlayTreePtr parent, miOverlayTreePtr prevSib)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

    pTree->parent = parent;
    pTree->firstChild = NULL;
    pTree->lastChild = NULL;
    pTree->prevSib = prevSib;
    pTree->nextSib = NULL;

    if (prevSib)
        prevSib->nextSib = pTree;

    if (!parent->firstChild)
        parent->firstChild = parent->lastChild = pTree;
    else if (parent->lastChild == prevSib)
        parent->lastChild = pTree;

    return pTree;
}

static void
RebuildTree(WindowPtr pWin)
{
    miOverlayTreePtr parent, prevSib, tChild;
    WindowPtr pChild;

    prevSib = tChild = NULL;

    pWin = pWin->parent;

    while (IN_OVERLAY(pWin))
        pWin = pWin->parent;

    parent = MIOVERLAY_GET_WINDOW_TREE(pWin);

    pChild = pWin->firstChild;
    parent->firstChild = parent->lastChild = NULL;

    while (1) {
        if (IN_UNDERLAY(pChild))
            prevSib = tChild = DoLeaf(pChild, parent, prevSib);

        if (pChild->firstChild) {
            if (IN_UNDERLAY(pChild)) {
                parent = tChild;
                prevSib = NULL;
            }
            pChild = pChild->firstChild;
            continue;
        }

        while (!pChild->nextSib) {
            pChild = pChild->parent;
            if (pChild == pWin)
                return;
            if (IN_UNDERLAY(pChild)) {
                prevSib = tChild = MIOVERLAY_GET_WINDOW_TREE(pChild);
                parent = tChild->parent;
            }
        }

        pChild = pChild->nextSib;
    }
}

static Bool
HasUnderlayChildren(WindowPtr pWin)
{
    WindowPtr pChild;

    if (!(pChild = pWin->firstChild))
        return FALSE;

    while (1) {
        if (IN_UNDERLAY(pChild))
            return TRUE;

        if (pChild->firstChild) {
            pChild = pChild->firstChild;
            continue;
        }

        while (!pChild->nextSib && (pWin != pChild))
            pChild = pChild->parent;

        if (pChild == pWin)
            break;

        pChild = pChild->nextSib;
    }

    return FALSE;
}

static Bool
CollectUnderlayChildrenRegions(WindowPtr pWin, RegionPtr pReg)
{
    WindowPtr pChild;
    miOverlayTreePtr pTree;
    Bool hasUnderlay;

    if (!(pChild = pWin->firstChild))
        return FALSE;

    hasUnderlay = FALSE;

    while (1) {
        if ((pTree = MIOVERLAY_GET_WINDOW_TREE(pChild))) {
            RegionAppend(pReg, &pTree->borderClip);
            hasUnderlay = TRUE;
        }
        else if (pChild->firstChild) {
            pChild = pChild->firstChild;
            continue;
        }

        while (!pChild->nextSib && (pWin != pChild))
            pChild = pChild->parent;

        if (pChild == pWin)
            break;

        pChild = pChild->nextSib;
    }

    if (hasUnderlay) {
        Bool overlap;

        RegionValidate(pReg, &overlap);
    }

    return hasUnderlay;
}

static void
MarkUnderlayWindow(WindowPtr pWin)
{
    miOverlayTreePtr pTree = MIOVERLAY_GET_WINDOW_TREE(pWin);

    if (pTree->valdata)
        return;
    pTree->valdata =
        (miOverlayValDataPtr) XNFalloc(sizeof(miOverlayValDataRec));
    pTree->valdata->oldAbsCorner.x = pWin->drawable.x;
    pTree->valdata->oldAbsCorner.y = pWin->drawable.y;
    pTree->valdata->borderVisible = NullRegion;
}
