/*
 * Copyright © 2006 Sun Microsystems, Inc.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Copyright © 2003 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "compint.h"

#ifdef COMPOSITE_DEBUG
static int
compCheckWindow (WindowPtr pWin, pointer data)
{
    ScreenPtr	pScreen = pWin->drawable.pScreen;
    PixmapPtr	pWinPixmap = (*pScreen->GetWindowPixmap) (pWin);
    PixmapPtr	pParentPixmap = pWin->parent ? (*pScreen->GetWindowPixmap) (pWin->parent) : 0;
    PixmapPtr	pScreenPixmap = (*pScreen->GetScreenPixmap) (pScreen);

    if (!pWin->parent)
    {
	assert (pWin->redirectDraw == RedirectDrawNone);
	assert (pWinPixmap == pScreenPixmap);
    }
    else if (pWin->redirectDraw != RedirectDrawNone)
    {
	assert (pWinPixmap != pParentPixmap);
	assert (pWinPixmap != pScreenPixmap);
    }
    else
    {
	assert (pWinPixmap == pParentPixmap);
    }
    assert (0 < pWinPixmap->refcnt && pWinPixmap->refcnt < 3);
    assert (0 < pScreenPixmap->refcnt && pScreenPixmap->refcnt < 3);
    if (pParentPixmap)
	assert (0 <= pParentPixmap->refcnt && pParentPixmap->refcnt < 3);
    return WT_WALKCHILDREN;
}

void
compCheckTree (ScreenPtr pScreen)
{
    WalkTree (pScreen, compCheckWindow, 0);
}
#endif

typedef struct _compPixmapVisit {
    WindowPtr	pWindow;
    PixmapPtr	pPixmap;
} CompPixmapVisitRec, *CompPixmapVisitPtr;

static Bool
compRepaintBorder (ClientPtr pClient, pointer closure)
{
    WindowPtr pWindow;
    int rc = dixLookupWindow(&pWindow, (XID)(intptr_t)closure, pClient, DixWriteAccess);

    if (rc == Success) {
	RegionRec exposed;

	REGION_NULL(pScreen, &exposed);
	REGION_SUBTRACT(pScreen, &exposed, &pWindow->borderClip, &pWindow->winSize);
	miPaintWindow(pWindow, &exposed, PW_BORDER);
	REGION_UNINIT(pScreen, &exposed);
    }
    return TRUE;
}

static int
compSetPixmapVisitWindow (WindowPtr pWindow, pointer data)
{
    CompPixmapVisitPtr	pVisit = (CompPixmapVisitPtr) data;
    ScreenPtr		pScreen = pWindow->drawable.pScreen;

    if (pWindow != pVisit->pWindow && pWindow->redirectDraw != RedirectDrawNone)
	return WT_DONTWALKCHILDREN;
    (*pScreen->SetWindowPixmap) (pWindow, pVisit->pPixmap);
    /*
     * Recompute winSize and borderSize.  This is duplicate effort
     * when resizing pixmaps, but necessary when changing redirection.
     * Might be nice to fix this.
     */
    SetWinSize (pWindow);
    SetBorderSize (pWindow);
    if (HasBorder (pWindow))
	QueueWorkProc (compRepaintBorder, serverClient,
		       (pointer)(intptr_t) pWindow->drawable.id);
    return WT_WALKCHILDREN;
}

void
compSetPixmap (WindowPtr pWindow, PixmapPtr pPixmap)
{
    CompPixmapVisitRec	visitRec;

    visitRec.pWindow = pWindow;
    visitRec.pPixmap = pPixmap;
    TraverseTree (pWindow, compSetPixmapVisitWindow, (pointer) &visitRec);
    compCheckTree (pWindow->drawable.pScreen);
}

Bool
compCheckRedirect (WindowPtr pWin)
{
    CompWindowPtr   cw = GetCompWindow (pWin);
    CompScreenPtr   cs = GetCompScreen(pWin->drawable.pScreen);
    Bool	    should;

    should = pWin->realized && (pWin->drawable.class != InputOnly) &&
	     (cw != NULL) && (pWin->parent != NULL);

    /* Never redirect the overlay window */
    if (cs->pOverlayWin != NULL) {
	if (pWin == cs->pOverlayWin) {
	    should = FALSE;
	}
    }

    if (should != (pWin->redirectDraw != RedirectDrawNone))
    {
	if (should)
	    return compAllocPixmap (pWin);
	else
	    compFreePixmap (pWin);
    }
    return TRUE;
}

static int
updateOverlayWindow(ScreenPtr pScreen)
{
	CompScreenPtr cs;
	WindowPtr pWin; /* overlay window */
	XID vlist[2];

	cs = GetCompScreen(pScreen);
	if ((pWin = cs->pOverlayWin) != NULL) {
		if ((pWin->drawable.width == pScreen->width) &&
			(pWin->drawable.height == pScreen->height))
			return Success;

		/* Let's resize the overlay window. */
		vlist[0] = pScreen->width;
		vlist[1] = pScreen->height;
		return ConfigureWindow(pWin, CWWidth | CWHeight, vlist, wClient(pWin));
	}

	/* Let's be on the safe side and not assume an overlay window is always allocated. */
	return Success;
}

Bool
compPositionWindow (WindowPtr pWin, int x, int y)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    CompScreenPtr   cs = GetCompScreen (pScreen);
    Bool	    ret = TRUE;

    pScreen->PositionWindow = cs->PositionWindow;
    /*
     * "Shouldn't need this as all possible places should be wrapped
     *
    compCheckRedirect (pWin);
     */
#ifdef COMPOSITE_DEBUG
    if ((pWin->redirectDraw != RedirectDrawNone) !=
	(pWin->viewable && (GetCompWindow(pWin) != NULL)))
	OsAbort ();
#endif
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	PixmapPtr   pPixmap = (*pScreen->GetWindowPixmap) (pWin);
	int	    bw = wBorderWidth (pWin);
	int	    nx = pWin->drawable.x - bw;
	int	    ny = pWin->drawable.y - bw;

	if (pPixmap->screen_x != nx || pPixmap->screen_y != ny)
	{
	    pPixmap->screen_x = nx;
	    pPixmap->screen_y = ny;
	    pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
	}
    }

    if (!(*pScreen->PositionWindow) (pWin, x, y))
	ret = FALSE;
    cs->PositionWindow = pScreen->PositionWindow;
    pScreen->PositionWindow = compPositionWindow;
    compCheckTree (pWin->drawable.pScreen);
    if (updateOverlayWindow(pScreen) != Success)
	ret = FALSE;
    return ret;
}

Bool
compRealizeWindow (WindowPtr pWin)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    CompScreenPtr   cs = GetCompScreen (pScreen);
    Bool	    ret = TRUE;

    pScreen->RealizeWindow = cs->RealizeWindow;
    compCheckRedirect (pWin);
    if (!(*pScreen->RealizeWindow) (pWin))
	ret = FALSE;
    cs->RealizeWindow = pScreen->RealizeWindow;
    pScreen->RealizeWindow = compRealizeWindow;
    compCheckTree (pWin->drawable.pScreen);
    return ret;
}

Bool
compUnrealizeWindow (WindowPtr pWin)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    CompScreenPtr   cs = GetCompScreen (pScreen);
    Bool	    ret = TRUE;

    pScreen->UnrealizeWindow = cs->UnrealizeWindow;
    compCheckRedirect (pWin);
    if (!(*pScreen->UnrealizeWindow) (pWin))
	ret = FALSE;
    cs->UnrealizeWindow = pScreen->UnrealizeWindow;
    pScreen->UnrealizeWindow = compUnrealizeWindow;
    compCheckTree (pWin->drawable.pScreen);
    return ret;
}

/*
 * Called after the borderClip for the window has settled down
 * We use this to make sure our extra borderClip has the right origin
 */

void
compClipNotify (WindowPtr pWin, int dx, int dy)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);
    CompWindowPtr	cw = GetCompWindow (pWin);

    if (cw)
    {
	if (cw->borderClipX != pWin->drawable.x ||
	    cw->borderClipY != pWin->drawable.y)
	{
	    REGION_TRANSLATE (pScreen, &cw->borderClip,
			      pWin->drawable.x - cw->borderClipX,
			      pWin->drawable.y - cw->borderClipY);
	    cw->borderClipX = pWin->drawable.x;
	    cw->borderClipY = pWin->drawable.y;
	}
    }
    if (cs->ClipNotify)
    {
	pScreen->ClipNotify = cs->ClipNotify;
	(*pScreen->ClipNotify) (pWin, dx, dy);
	cs->ClipNotify = pScreen->ClipNotify;
	pScreen->ClipNotify = compClipNotify;
    }
}

/*
 * Returns TRUE if the window needs server-provided automatic redirect,
 * which is true if the child and parent aren't both regular or ARGB visuals
 */

static Bool
compIsAlternateVisual (ScreenPtr    pScreen,
		       XID	    visual)
{
    CompScreenPtr	cs = GetCompScreen (pScreen);
    int			i;

    for (i = 0; i < cs->numAlternateVisuals; i++)
	if (cs->alternateVisuals[i] == visual)
	    return TRUE;
    return FALSE;
}

static Bool
compImplicitRedirect (WindowPtr pWin, WindowPtr pParent)
{
    if (pParent)
    {
	ScreenPtr	pScreen = pWin->drawable.pScreen;
	XID		winVisual = wVisual (pWin);
	XID		parentVisual = wVisual (pParent);

	if (winVisual != parentVisual &&
	    (compIsAlternateVisual (pScreen, winVisual) ||
	     compIsAlternateVisual (pScreen, parentVisual)))
	    return TRUE;
    }
    return FALSE;
}

void
compMoveWindow (WindowPtr pWin, int x, int y, WindowPtr pSib, VTKind kind)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);

    compCheckTree (pScreen);
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	WindowPtr		pParent;
	int			draw_x, draw_y;
	unsigned int		w, h, bw;

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

	bw = wBorderWidth (pWin);
	draw_x = pParent->drawable.x + x + (int)bw;
	draw_y = pParent->drawable.y + y + (int)bw;
	w = pWin->drawable.width;
	h = pWin->drawable.height;
	compReallocPixmap (pWin, draw_x, draw_y, w, h, bw);
    }
    compCheckTree (pScreen);

    pScreen->MoveWindow = cs->MoveWindow;
    (*pScreen->MoveWindow) (pWin, x, y, pSib, kind);
    cs->MoveWindow = pScreen->MoveWindow;
    pScreen->MoveWindow = compMoveWindow;

    if (pWin->redirectDraw != RedirectDrawNone)
    {
	CompWindowPtr	cw = GetCompWindow (pWin);
	if (cw->pOldPixmap)
	{
	    (*pScreen->DestroyPixmap) (cw->pOldPixmap);
	    cw->pOldPixmap = NullPixmap;
	}
    }

    compCheckTree (pScreen);
}

void
compResizeWindow (WindowPtr pWin, int x, int y,
		  unsigned int w, unsigned int h, WindowPtr pSib)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);

    compCheckTree (pScreen);
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	WindowPtr		pParent;
	int			draw_x, draw_y;
	unsigned int		bw;

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

	bw = wBorderWidth (pWin);
	draw_x = pParent->drawable.x + x + (int)bw;
	draw_y = pParent->drawable.y + y + (int)bw;
	compReallocPixmap (pWin, draw_x, draw_y, w, h, bw);
    }
    compCheckTree (pScreen);

    pScreen->ResizeWindow = cs->ResizeWindow;
    (*pScreen->ResizeWindow) (pWin, x, y, w, h, pSib);
    cs->ResizeWindow = pScreen->ResizeWindow;
    pScreen->ResizeWindow = compResizeWindow;
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	CompWindowPtr	cw = GetCompWindow (pWin);
	if (cw->pOldPixmap)
	{
	    (*pScreen->DestroyPixmap) (cw->pOldPixmap);
	    cw->pOldPixmap = NullPixmap;
	}
    }
    compCheckTree (pWin->drawable.pScreen);
}

void
compChangeBorderWidth (WindowPtr pWin, unsigned int bw)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);

    compCheckTree (pScreen);
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	WindowPtr		pParent;
	int			draw_x, draw_y;
	unsigned int		w, h;

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

	draw_x = pWin->drawable.x;
	draw_y = pWin->drawable.y;
	w = pWin->drawable.width;
	h = pWin->drawable.height;
	compReallocPixmap (pWin, draw_x, draw_y, w, h, bw);
    }
    compCheckTree (pScreen);

    pScreen->ChangeBorderWidth = cs->ChangeBorderWidth;
    (*pScreen->ChangeBorderWidth) (pWin, bw);
    cs->ChangeBorderWidth = pScreen->ChangeBorderWidth;
    pScreen->ChangeBorderWidth = compChangeBorderWidth;
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	CompWindowPtr	cw = GetCompWindow (pWin);
	if (cw->pOldPixmap)
	{
	    (*pScreen->DestroyPixmap) (cw->pOldPixmap);
	    cw->pOldPixmap = NullPixmap;
	}
    }
    compCheckTree (pWin->drawable.pScreen);
}

void
compReparentWindow (WindowPtr pWin, WindowPtr pPriorParent)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);

    pScreen->ReparentWindow = cs->ReparentWindow;
    /*
     * Remove any implicit redirect due to synthesized visual
     */
    if (compImplicitRedirect (pWin, pPriorParent))
	compUnredirectWindow (serverClient, pWin, CompositeRedirectAutomatic);
    /*
     * Handle subwindows redirection
     */
    compUnredirectOneSubwindow (pPriorParent, pWin);
    compRedirectOneSubwindow (pWin->parent, pWin);
    /*
     * Add any implict redirect due to synthesized visual
     */
    if (compImplicitRedirect (pWin, pWin->parent))
	compRedirectWindow (serverClient, pWin, CompositeRedirectAutomatic);

    /*
     * Allocate any necessary redirect pixmap
     * (this actually should never be true; pWin is always unmapped)
     */
    compCheckRedirect (pWin);

    /*
     * Reset pixmap pointers as appropriate
     */
    if (pWin->parent && pWin->redirectDraw == RedirectDrawNone)
	compSetPixmap (pWin, (*pScreen->GetWindowPixmap) (pWin->parent));
    /*
     * Call down to next function
     */
    if (pScreen->ReparentWindow)
	(*pScreen->ReparentWindow) (pWin, pPriorParent);
    cs->ReparentWindow = pScreen->ReparentWindow;
    pScreen->ReparentWindow = compReparentWindow;
    compCheckTree (pWin->drawable.pScreen);
}

void
compCopyWindow (WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    CompScreenPtr   cs = GetCompScreen (pScreen);
    int		    dx = 0, dy = 0;

    if (pWin->redirectDraw != RedirectDrawNone)
    {
	PixmapPtr	pPixmap = (*pScreen->GetWindowPixmap) (pWin);
	CompWindowPtr	cw = GetCompWindow (pWin);

	assert (cw->oldx != COMP_ORIGIN_INVALID);
	assert (cw->oldy != COMP_ORIGIN_INVALID);
	if (cw->pOldPixmap)
	{
	    /*
	     * Ok, the old bits are available in pOldPixmap and
	     * need to be copied to pNewPixmap.
	     */
	    RegionRec	rgnDst;
	    PixmapPtr	pPixmap = (*pScreen->GetWindowPixmap) (pWin);
	    GCPtr	pGC;

	    dx = ptOldOrg.x - pWin->drawable.x;
	    dy = ptOldOrg.y - pWin->drawable.y;
	    REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy);

	    REGION_NULL (pWin->drawable.pScreen, &rgnDst);

	    REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst,
			     &pWin->borderClip, prgnSrc);

	    REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst,
			      -pPixmap->screen_x, -pPixmap->screen_y);

	    dx = dx + pPixmap->screen_x - cw->oldx;
	    dy = dy + pPixmap->screen_y - cw->oldy;
	    pGC = GetScratchGC (pPixmap->drawable.depth, pScreen);
	    if (pGC)
	    {
		BoxPtr	pBox = REGION_RECTS (&rgnDst);
		int	nBox = REGION_NUM_RECTS (&rgnDst);

		ValidateGC(&pPixmap->drawable, pGC);
		while (nBox--)
		{
		    (void) (*pGC->ops->CopyArea) (&cw->pOldPixmap->drawable,
						  &pPixmap->drawable,
						  pGC,
						  pBox->x1 + dx, pBox->y1 + dy,
						  pBox->x2 - pBox->x1,
						  pBox->y2 - pBox->y1,
						  pBox->x1, pBox->y1);
		    pBox++;
		}
		FreeScratchGC (pGC);
	    }
	    return;
	}
	dx = pPixmap->screen_x - cw->oldx;
	dy = pPixmap->screen_y - cw->oldy;
	ptOldOrg.x += dx;
	ptOldOrg.y += dy;
    }

    pScreen->CopyWindow = cs->CopyWindow;
    if (ptOldOrg.x != pWin->drawable.x || ptOldOrg.y != pWin->drawable.y)
    {
	if (dx || dy)
	    REGION_TRANSLATE (pScreen, prgnSrc, dx, dy);
	(*pScreen->CopyWindow) (pWin, ptOldOrg, prgnSrc);
	if (dx || dy)
	    REGION_TRANSLATE (pScreen, prgnSrc, -dx, -dy);
    }
    else
    {
	ptOldOrg.x -= dx;
	ptOldOrg.y -= dy;
	REGION_TRANSLATE (prgnSrc, prgnSrc,
			  pWin->drawable.x - ptOldOrg.x,
			  pWin->drawable.y - ptOldOrg.y);
	DamageRegionAppend(&pWin->drawable, prgnSrc);
    }
    cs->CopyWindow = pScreen->CopyWindow;
    pScreen->CopyWindow = compCopyWindow;
    compCheckTree (pWin->drawable.pScreen);
}

Bool
compCreateWindow (WindowPtr pWin)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);
    Bool		ret;

    pScreen->CreateWindow = cs->CreateWindow;
    ret = (*pScreen->CreateWindow) (pWin);
    if (pWin->parent && ret)
    {
	CompSubwindowsPtr	csw = GetCompSubwindows (pWin->parent);
        CompClientWindowPtr	ccw;

	(*pScreen->SetWindowPixmap) (pWin, (*pScreen->GetWindowPixmap) (pWin->parent));
	if (csw)
	    for (ccw = csw->clients; ccw; ccw = ccw->next)
		compRedirectWindow (clients[CLIENT_ID(ccw->id)],
				    pWin, ccw->update);
	if (compImplicitRedirect (pWin, pWin->parent))
	    compRedirectWindow (serverClient, pWin, CompositeRedirectAutomatic);
    }
    cs->CreateWindow = pScreen->CreateWindow;
    pScreen->CreateWindow = compCreateWindow;
    compCheckTree (pWin->drawable.pScreen);
    return ret;
}

Bool
compDestroyWindow (WindowPtr pWin)
{
    ScreenPtr		pScreen = pWin->drawable.pScreen;
    CompScreenPtr	cs = GetCompScreen (pScreen);
    CompWindowPtr	cw;
    CompSubwindowsPtr	csw;
    Bool		ret;

    pScreen->DestroyWindow = cs->DestroyWindow;
    while ((cw = GetCompWindow (pWin)))
	FreeResource (cw->clients->id, RT_NONE);
    while ((csw = GetCompSubwindows (pWin)))
	FreeResource (csw->clients->id, RT_NONE);

    if (pWin->redirectDraw != RedirectDrawNone)
	compFreePixmap (pWin);
    ret = (*pScreen->DestroyWindow) (pWin);
    cs->DestroyWindow = pScreen->DestroyWindow;
    pScreen->DestroyWindow = compDestroyWindow;
/*    compCheckTree (pWin->drawable.pScreen); can't check -- tree isn't good*/
    return ret;
}

void
compSetRedirectBorderClip (WindowPtr pWin, RegionPtr pRegion)
{
    CompWindowPtr   cw = GetCompWindow (pWin);
    RegionRec	    damage;

    REGION_NULL (pScreen, &damage);
    /*
     * Align old border clip with new border clip
     */
    REGION_TRANSLATE (pScreen, &cw->borderClip,
		      pWin->drawable.x - cw->borderClipX,
		      pWin->drawable.y - cw->borderClipY);
    /*
     * Compute newly visible portion of window for repaint
     */
    REGION_SUBTRACT (pScreen, &damage, pRegion, &cw->borderClip);
    /*
     * Report that as damaged so it will be redrawn
     */
    DamageRegionAppend(&pWin->drawable, &damage);
    REGION_UNINIT (pScreen, &damage);
    /*
     * Save the new border clip region
     */
    REGION_COPY (pScreen, &cw->borderClip, pRegion);
    cw->borderClipX = pWin->drawable.x;
    cw->borderClipY = pWin->drawable.y;
}

RegionPtr
compGetRedirectBorderClip (WindowPtr pWin)
{
    CompWindowPtr   cw = GetCompWindow (pWin);

    return &cw->borderClip;
}

static VisualPtr
compGetWindowVisual (WindowPtr pWin)
{
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    VisualID	    vid = wVisual (pWin);
    int		    i;

    for (i = 0; i < pScreen->numVisuals; i++)
	if (pScreen->visuals[i].vid == vid)
	    return &pScreen->visuals[i];
    return 0;
}

PictFormatPtr
compWindowFormat (WindowPtr pWin)
{
    ScreenPtr	pScreen = pWin->drawable.pScreen;

    return PictureMatchVisual (pScreen, pWin->drawable.depth,
			       compGetWindowVisual (pWin));
}

static void
compWindowUpdateAutomatic (WindowPtr pWin)
{
    CompWindowPtr   cw = GetCompWindow (pWin);
    ScreenPtr	    pScreen = pWin->drawable.pScreen;
    WindowPtr	    pParent = pWin->parent;
    PixmapPtr	    pSrcPixmap = (*pScreen->GetWindowPixmap) (pWin);
    PictFormatPtr   pSrcFormat = compWindowFormat (pWin);
    PictFormatPtr   pDstFormat = compWindowFormat (pWin->parent);
    int		    error;
    RegionPtr	    pRegion = DamageRegion (cw->damage);
    PicturePtr	    pSrcPicture = CreatePicture (0, &pSrcPixmap->drawable,
						 pSrcFormat,
						 0, 0,
						 serverClient,
						 &error);
    XID		    subwindowMode = IncludeInferiors;
    PicturePtr	    pDstPicture = CreatePicture (0, &pParent->drawable,
						 pDstFormat,
						 CPSubwindowMode,
						 &subwindowMode,
						 serverClient,
						 &error);

    /*
     * First move the region from window to screen coordinates
     */
    REGION_TRANSLATE (pScreen, pRegion,
		      pWin->drawable.x, pWin->drawable.y);

    /*
     * Clip against the "real" border clip
     */
    REGION_INTERSECT (pScreen, pRegion, pRegion, &cw->borderClip);

    /*
     * Now translate from screen to dest coordinates
     */
    REGION_TRANSLATE (pScreen, pRegion,
		      -pParent->drawable.x, -pParent->drawable.y);

    /*
     * Clip the picture
     */
    SetPictureClipRegion (pDstPicture, 0, 0, pRegion);

    /*
     * And paint
     */
    CompositePicture (PictOpSrc,
		      pSrcPicture,
		      0,
		      pDstPicture,
		      0, 0, /* src_x, src_y */
		      0, 0, /* msk_x, msk_y */
		      pSrcPixmap->screen_x - pParent->drawable.x,
		      pSrcPixmap->screen_y - pParent->drawable.y,
		      pSrcPixmap->drawable.width,
		      pSrcPixmap->drawable.height);
    FreePicture (pSrcPicture, 0);
    FreePicture (pDstPicture, 0);
    /*
     * Empty the damage region.  This has the nice effect of
     * rendering the translations above harmless
     */
    DamageEmpty (cw->damage);
}

void
compWindowUpdate (WindowPtr pWin)
{
    WindowPtr	pChild;

    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
	compWindowUpdate (pChild);
    if (pWin->redirectDraw != RedirectDrawNone)
    {
	CompWindowPtr	cw = GetCompWindow(pWin);

	if (cw->damaged)
	{
	    compWindowUpdateAutomatic (pWin);
	    cw->damaged = FALSE;
	}
    }
}

WindowPtr
CompositeRealChildHead (WindowPtr pWin)
{
    WindowPtr pChild, pChildBefore;
    CompScreenPtr cs;

    if (!pWin->parent &&
	(screenIsSaved == SCREEN_SAVER_ON) &&
	(HasSaverWindow (pWin->drawable.pScreen->myNum))) {

	/* First child is the screen saver; see if next child is the overlay */
	pChildBefore = pWin->firstChild;
	pChild = pChildBefore->nextSib;

    } else {
	pChildBefore = NullWindow;
	pChild = pWin->firstChild;
    }

    if (!pChild) {
	return NullWindow;
    }

    cs = GetCompScreen(pWin->drawable.pScreen);
    if (pChild == cs->pOverlayWin) {
	return pChild;
    } else {
	return pChildBefore;
    }
}