/*
 * Acceleration for the Creator and Creator3D framebuffer - Dbe Acceleration.
 *
 * Copyright (C) 2000 David S. Miller (davem@redhat.com)
 *
 * 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 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
 * DAVID MILLER 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.
 *
 */
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/sunffb/ffb_dbe.c,v 1.1 2000/05/23 04:47:44 dawes Exp $ */

#define NEED_REPLIES
#define NEED_EVENTS
#include "X.h"
#include "Xproto.h"
#include "misc.h"
#include "os.h"
#include "windowstr.h"
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "extnsionst.h"
#include "dixstruct.h"
#include "resource.h"
#include "opaque.h"
#include "dbestruct.h"
#include "regionstr.h"
#include "gcstruct.h"
#include "inputstr.h"

#define PSZ 8
#include "cfb.h"
#undef PSZ
#include "cfb32.h"

#include "xf86_ansic.h"
#include "xf86.h"

#include "ffb.h"
#include "ffb_fifo.h"
#include "ffb_rcache.h"

static int	FFBDbePrivPrivGeneration  =  0;
static int	FFBDbeWindowPrivPrivIndex = -1;
static RESTYPE	dbeDrawableResType;
static RESTYPE	dbeWindowPrivResType;
static int	dbeScreenPrivIndex = -1;
static int	dbeWindowPrivIndex = -1;

#define FFB_DBE_WINDOW_PRIV_PRIV(pDbeWindowPriv) \
    (((FFBDbeWindowPrivPrivIndex < 0) || (!pDbeWindowPriv)) ? \
    NULL : \
    ((FFBDbeWindowPrivPrivPtr) \
     ((pDbeWindowPriv)->devPrivates[FFBDbeWindowPrivPrivIndex].ptr)))

#define FFB_DBE_WINDOW_PRIV_PRIV_FROM_WINDOW(pWin) \
    FFB_DBE_WINDOW_PRIV_PRIV(DBE_WINDOW_PRIV(pWin))

typedef struct _FFBDbeWindowPrivPrivRec {
	int HwAccelerated;
	int HwCurrentBuf;	/* 0 = buffer A, 1 = buffer B */

	/* We need also what midbe would use in case we must
	 * revert to unaccelerated dbe.
	 */
	PixmapPtr		pBackBuffer;
	PixmapPtr		pFrontBuffer;

	/* Back pointer to generic DBE layer window private. */
	DbeWindowPrivPtr	pDbeWindowPriv;
} FFBDbeWindowPrivPrivRec, *FFBDbeWindowPrivPrivPtr;

static Bool
FFBDbeGetVisualInfo(ScreenPtr pScreen, XdbeScreenVisualInfo *pScrVisInfo)
{
	XdbeVisualInfo *visInfo;
	DepthPtr pDepth;
	int i, j, k, count;

	/* XXX Should check for double-buffer presence.  But even if
	 * XXX the double-buffer is not present we can still play
	 * XXX tricks with GetWindowPixmap in 8bpp mode, ie. double
	 * XXX buffer between the R and G planes of buffer A. -DaveM
	 */

	/* Determine number of visuals for this screen. */
	for (i = 0, count = 0; i < pScreen->numDepths; i++)
		count += pScreen->allowedDepths[i].numVids;

	/* Allocate an array of XdbeVisualInfo items. */
	if (!(visInfo = (XdbeVisualInfo *)xalloc(count * sizeof(XdbeVisualInfo))))
		return FALSE;

	for (i = 0, k = 0; i < pScreen->numDepths; i++) {
		/* For each depth of this screen, get visual information. */
		pDepth = &pScreen->allowedDepths[i];
		for (j = 0; j < pDepth->numVids; j++) {
			/* For each visual for this depth of this screen, get visual ID
			 * and visual depth.  For now, we will always return
			 * the same performance level for all visuals (0).  A higher
			 * performance level value indicates higher performance.
			 */
			visInfo[k].visual    = pDepth->vids[j];
			visInfo[k].depth     = pDepth->depth;

			/* XXX We should set this appropriately... -DaveM */
			visInfo[k].perflevel = 0;
			k++;
		}
	}

	/* Record the number of visuals and point visual_depth to
	 * the array of visual info.
	 */
	pScrVisInfo->count   = count;
	pScrVisInfo->visinfo = visInfo;

	return TRUE;
}

static void
FFBDbeUpdateWidPlane(WindowPtr pWin, GCPtr pGC)
{
	FFBPtr pFfb = GET_FFB_FROM_SCREEN(pWin->drawable.pScreen);
	CreatorPrivWinPtr pFfbPrivWin = CreatorGetWindowPrivate(pWin);
	RegionPtr prgnClip;
	BoxPtr pboxClipped, pboxClippedBase;
	unsigned int fbc;
	int numRects;
	int x, y, w, h;

	x = pWin->drawable.x;
	y = pWin->drawable.y;
	w = pWin->drawable.width;
	h = pWin->drawable.height;

	fbc = pFfbPrivWin->fbc_base;
	fbc = (fbc & ~FFB_FBC_WB_MASK) | FFB_FBC_WB_AB;
	fbc = (fbc & ~FFB_FBC_XE_MASK) | FFB_FBC_XE_ON;
	fbc = (fbc & ~FFB_FBC_RGBE_MASK) | FFB_FBC_RGBE_OFF;

	prgnClip = cfbGetCompositeClip(pGC);
	numRects = REGION_NUM_RECTS (prgnClip);
	pboxClippedBase = (BoxPtr) ALLOCATE_LOCAL(numRects * sizeof(BoxRec));
	if (!pboxClippedBase)
		return;

	pboxClipped = pboxClippedBase;
	{
		int x1, y1, x2, y2, bx2, by2;
		BoxRec box;
		BoxPtr pextent;

		pextent = REGION_EXTENTS(pGC->pScreen, prgnClip);
		x1 = pextent->x1;
		y1 = pextent->y1;
		x2 = pextent->x2;
		y2 = pextent->y2;

		if ((box.x1 = x) < x1)
			box.x1 = x1;

		if ((box.y1 = y) < y1)
			box.y1 = y1;

		bx2 = (int) x + (int) w;
		if (bx2 > x2)
			bx2 = x2;
		box.x2 = bx2;

		by2 = (int) y + (int) h;
		if (by2 > y2)
			by2 = y2;
		box.y2 = by2;

		if ((box.x1 < box.x2) && (box.y1 < box.y2)) {
			int n = REGION_NUM_RECTS (prgnClip);
			BoxPtr pbox = REGION_RECTS(prgnClip);

			/* Clip the rectangle to each box in the clip region
			 * this is logically equivalent to calling Intersect()
			 */
			while(n--) {
				pboxClipped->x1 = max(box.x1, pbox->x1);
				pboxClipped->y1 = max(box.y1, pbox->y1);
				pboxClipped->x2 = min(box.x2, pbox->x2);
				pboxClipped->y2 = min(box.y2, pbox->y2);
				pbox++;

				/* see if clipping left anything */
				if(pboxClipped->x1 < pboxClipped->x2 &&
				   pboxClipped->y1 < pboxClipped->y2)
					pboxClipped++;
			}
		}
	}

	if (pboxClipped != pboxClippedBase) {
		ffb_fbcPtr ffb = pFfb->regs;
		int num = (pboxClipped - pboxClippedBase);

		FFB_ATTR_RAW(pFfb,
			     FFB_PPC_APE_DISABLE | FFB_PPC_CS_CONST | FFB_PPC_XS_WID,
			     FFB_PPC_APE_MASK | FFB_PPC_CS_MASK | FFB_PPC_XS_MASK,
			     pGC->planemask,
			     ((FFB_ROP_EDIT_BIT | pGC->alu) | (FFB_ROP_NEW << 8)),
			     FFB_DRAWOP_RECTANGLE,
			     pGC->fgPixel,
			     fbc, pFfbPrivWin->wid);

		pboxClipped = pboxClippedBase;
		while (num--) {
			int xx, yy, ww, hh;

			xx = pboxClipped->x1;
			yy = pboxClipped->y1;
			ww = (pboxClipped->x2 - xx);
			hh = (pboxClipped->y2 - yy);
			FFBFifo(pFfb, 4);
			FFB_WRITE64(&ffb->by, yy, xx);
			FFB_WRITE64_2(&ffb->bh, hh, ww);
		}
	}

	DEALLOCATE_LOCAL (pboxClippedBase);
}

static int
FFBDbeAllocBackBufferName(WindowPtr pWin, XID bufId, int swapAction)
{
	ScreenPtr pScreen;
	FFBPtr pFfb;
	DbeWindowPrivPtr pDbeWindowPriv;
	FFBDbeWindowPrivPrivPtr pDbeWindowPrivPriv; 
	DbeScreenPrivPtr pDbeScreenPriv;
	GCPtr pGC;
	xRectangle clearRect;

	pScreen = pWin->drawable.pScreen;
	pDbeWindowPriv = DBE_WINDOW_PRIV(pWin);
	pFfb = GET_FFB_FROM_SCREEN(pScreen);

	pDbeWindowPrivPriv = FFB_DBE_WINDOW_PRIV_PRIV(pDbeWindowPriv);
	if (pDbeWindowPriv->nBufferIDs == 0) {
		/* There is no buffer associated with the window.
		 * We have to create the window priv priv.  Remember, the window
		 * priv was created at the DIX level, so all we need to do is
		 * create the priv priv and attach it to the priv.
		 */
		pDbeScreenPriv = DBE_SCREEN_PRIV(pScreen);

		/* Setup the window priv priv. */
		pDbeWindowPrivPriv->pDbeWindowPriv = pDbeWindowPriv;

		if (!pFfb->NoAccel && pFfb->has_double_buffer) {
			CreatorPrivWinPtr pFfbPrivWin;
			unsigned int wid, fbc;

			/* We just render directly into the hardware, all
			 * that is needed is to swap the rendering attributes
			 * and the WID settings during a swap.
			 */
			if (!AddResource(bufId, dbeDrawableResType,
					 (pointer)&pWin->drawable)) {
				/* Free the buffer and the drawable resource. */
				FreeResource(bufId, RT_NONE);
				return BadAlloc;
			}

			pFfbPrivWin = CreatorGetWindowPrivate(pWin);
			wid = FFBWidUnshare(pFfb, pFfbPrivWin->wid);
			if (wid == (unsigned int)-1)
				return BadAlloc;

			pFfbPrivWin->wid = wid;

			/* Attach the priv priv to the priv. */
			pDbeWindowPriv->devPrivates[FFBDbeWindowPrivPrivIndex].ptr =
				(pointer)pDbeWindowPrivPriv;

			/* Indicate we are doing hw acceleration. */
			pDbeWindowPrivPriv->HwAccelerated = 1;

			/* Start rendering into buffer B. */
			pDbeWindowPrivPriv->HwCurrentBuf = 1;

			/* No back/front temporary pixmaps. */
			pDbeWindowPrivPriv->pFrontBuffer = NULL;
			pDbeWindowPrivPriv->pBackBuffer = NULL;

			/* Switch to writing into buffer B. */
			fbc = pFfbPrivWin->fbc_base;
			fbc &= ~(FFB_FBC_WB_MASK | FFB_FBC_RB_MASK);
			fbc |= (FFB_FBC_WB_B | FFB_FBC_RB_B);
			pFfbPrivWin->fbc_base = fbc;

			pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);

			/* Fill X plane of front and back buffers. */
			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC(&pWin->drawable, pGC);
				FFBDbeUpdateWidPlane(pWin, pGC);
			}

			/* Clear out buffer B. */
			clearRect.x = clearRect.y = 0;
			clearRect.width  = pWin->drawable.width;
			clearRect.height = pWin->drawable.height;
			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC(&pWin->drawable, pGC);
				(*pGC->ops->PolyFillRect)(&pWin->drawable, pGC, 1, &clearRect);
			}

			FreeScratchGC(pGC);
		} else {
			/* Get a front pixmap. */
			if (!(pDbeWindowPrivPriv->pFrontBuffer =
			      (*pScreen->CreatePixmap)(pScreen, pDbeWindowPriv->width,
						       pDbeWindowPriv->height,
						       pWin->drawable.depth)))
				return BadAlloc;

			/* Get a back pixmap. */
			if (!(pDbeWindowPrivPriv->pBackBuffer =
			      (*pScreen->CreatePixmap)(pScreen, pDbeWindowPriv->width,
						       pDbeWindowPriv->height,
						       pWin->drawable.depth))) {
				(*pScreen->DestroyPixmap)(pDbeWindowPrivPriv->pFrontBuffer); 
				return BadAlloc;
			}


			/* Make the back pixmap a DBE drawable resource. */
			if (!AddResource(bufId, dbeDrawableResType,
					 (pointer)pDbeWindowPrivPriv->pBackBuffer)) {
				/* Free the buffer and the drawable resource. */
				FreeResource(bufId, RT_NONE);
				return(BadAlloc);
			}

			/* Attach the priv priv to the priv. */
			pDbeWindowPriv->devPrivates[FFBDbeWindowPrivPrivIndex].ptr =
				(pointer)pDbeWindowPrivPriv;

			/* Indicate we are doing this non-accelerated. */
			pDbeWindowPrivPriv->HwAccelerated = 0;

			/* Clear the back buffer. */
			pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);
			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC((DrawablePtr)pDbeWindowPrivPriv->pBackBuffer, pGC);
				clearRect.x = clearRect.y = 0;
				clearRect.width  = pDbeWindowPrivPriv->pBackBuffer->drawable.width;
				clearRect.height = pDbeWindowPrivPriv->pBackBuffer->drawable.height;
				(*pGC->ops->PolyFillRect)(
					(DrawablePtr)pDbeWindowPrivPriv->pBackBuffer, pGC, 1,
					&clearRect);
			}
			FreeScratchGC(pGC);
		}
	} else {
		pointer cookie;

		/* A buffer is already associated with the window.
		 * Place the new buffer ID information at the head of the ID list.
		 */
		if (pDbeWindowPrivPriv->HwAccelerated != 0)
			cookie = (pointer)&pWin->drawable;
		else
			cookie = (pointer)pDbeWindowPrivPriv->pBackBuffer;

		/* Associate the new ID with an existing pixmap. */
		if (!AddResource(bufId, dbeDrawableResType, cookie))
			return BadAlloc;
	}

	return Success;
}

static void
FFBDbeAliasBuffers(DbeWindowPrivPtr pDbeWindowPriv)
{
	FFBDbeWindowPrivPrivPtr	pDbeWindowPrivPriv =
		FFB_DBE_WINDOW_PRIV_PRIV(pDbeWindowPriv);
	pointer cookie;
	int i;

	if (pDbeWindowPrivPriv->HwAccelerated != 0)
		cookie = (pointer) &pDbeWindowPriv->pWindow->drawable;
	else
		cookie = (pointer) pDbeWindowPrivPriv->pBackBuffer;

	for (i = 0; i < pDbeWindowPriv->nBufferIDs; i++)
		ChangeResourceValue(pDbeWindowPriv->IDs[i], dbeDrawableResType, cookie);
}

static int
FFBDbeSwapBuffers(ClientPtr client, int *pNumWindows, DbeSwapInfoPtr swapInfo)
{
	FFBDbeWindowPrivPrivPtr pDbeWindowPrivPriv; 
	DbeScreenPrivPtr pDbeScreenPriv;
	PixmapPtr pTmpBuffer;
	xRectangle clearRect;
	WindowPtr pWin;
	GCPtr pGC;

	pWin               = swapInfo[0].pWindow;
	pDbeScreenPriv     = DBE_SCREEN_PRIV_FROM_WINDOW(pWin);
	pDbeWindowPrivPriv = FFB_DBE_WINDOW_PRIV_PRIV_FROM_WINDOW(pWin);
	pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);

	if (pDbeWindowPrivPriv->HwAccelerated != 0) {
		FFBPtr pFfb = GET_FFB_FROM_SCREEN(pWin->drawable.pScreen);
		CreatorPrivWinPtr pFfbPrivWin = CreatorGetWindowPrivate(pWin);
		unsigned int fbc;
		int visible;

		/* Unfortunately, this is necessary for correctness. */
		FFBWait(pFfb, pFfb->regs);

		/* Flip front/back in the WID. */
		visible = 0;
		if (pWin->viewable &&
		    pWin->visibility != VisibilityFullyObscured)
			visible = 1;
		FFBWidChangeBuffer(pFfb, pFfbPrivWin->wid, visible);

		/* Indicate where we are rendering now. */
		pDbeWindowPrivPriv->HwCurrentBuf ^= 1;

		/* Update framebuffer controls. */
		fbc = pFfbPrivWin->fbc_base;
		fbc &= ~(FFB_FBC_WB_MASK | FFB_FBC_RB_MASK);
		if (pDbeWindowPrivPriv->HwCurrentBuf == 0)
			fbc |= FFB_FBC_WB_A | FFB_FBC_RB_A;
		else
			fbc |= FFB_FBC_WB_B | FFB_FBC_RB_B;

		/* For XdbeUndefined we do not have to do anything.
		 * This is true for XdbeUntouched as well because we
		 * do in fact retain the unobscured contents of the
		 * front buffer while it is being displayed, thus now
		 * when it has become the back buffer it is still holding
		 * those contents.
		 *
		 * The XdbeUntouched case is important because most apps
		 * using dbe use this type of swap.
		 */

		if (swapInfo[0].swapAction == XdbeCopied) {
			unsigned int fbc_front_to_back;

			/* Do a GCOPY, front to back. */
			fbc_front_to_back = fbc & ~FFB_FBC_RB_MASK;
			if (pDbeWindowPrivPriv->HwCurrentBuf == 0)
				fbc_front_to_back |= FFB_FBC_RB_B;
			else
				fbc_front_to_back |= FFB_FBC_RB_A;

			pFfbPrivWin->fbc_base = fbc_front_to_back;
			ValidateGC(&pWin->drawable, pGC);
			(*pGC->ops->CopyArea)(&pWin->drawable,
					      &pWin->drawable,
					      pGC,
					      0, 0,
					      pWin->drawable.width,
					      pWin->drawable.height,
					      0, 0);
		} else if (swapInfo[0].swapAction == XdbeBackground) {
			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC(&pWin->drawable, pGC);
				clearRect.x = 0;
				clearRect.y = 0;
				clearRect.width = pWin->drawable.width;
				clearRect.height = pWin->drawable.height;
				(*pGC->ops->PolyFillRect)(&pWin->drawable, pGC,
							  1, &clearRect);
			}
		}

		/* Ok, now render with these fb controls. */
		pFfbPrivWin->fbc_base = fbc;
	} else {
		if (swapInfo[0].swapAction == XdbeUntouched) {
			ValidateGC((DrawablePtr)pDbeWindowPrivPriv->pFrontBuffer, pGC);
			(*pGC->ops->CopyArea)((DrawablePtr)pWin,
					      (DrawablePtr)pDbeWindowPrivPriv->pFrontBuffer,
					      pGC, 0, 0, pWin->drawable.width,
					      pWin->drawable.height, 0, 0);
		}

		ValidateGC((DrawablePtr)pWin, pGC);
		(*pGC->ops->CopyArea)((DrawablePtr)pDbeWindowPrivPriv->pBackBuffer,
				      (DrawablePtr)pWin, pGC, 0, 0,
				      pWin->drawable.width, pWin->drawable.height,
				      0, 0);

		if (swapInfo[0].swapAction == XdbeBackground) {
			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC((DrawablePtr)pDbeWindowPrivPriv->pBackBuffer,
					   pGC);
				clearRect.x = 0;
				clearRect.y = 0;
				clearRect.width =
					pDbeWindowPrivPriv->pBackBuffer->drawable.width;
				clearRect.height =
					pDbeWindowPrivPriv->pBackBuffer->drawable.height;
				(*pGC->ops->PolyFillRect)(
					(DrawablePtr)pDbeWindowPrivPriv->pBackBuffer,
					pGC, 1, &clearRect);
			}
		} else if (swapInfo[0].swapAction == XdbeUntouched) {
			/* Swap pixmap pointers. */
			pTmpBuffer = pDbeWindowPrivPriv->pBackBuffer;
			pDbeWindowPrivPriv->pBackBuffer =
				pDbeWindowPrivPriv->pFrontBuffer;
			pDbeWindowPrivPriv->pFrontBuffer = pTmpBuffer;
			FFBDbeAliasBuffers(pDbeWindowPrivPriv->pDbeWindowPriv);
		}
	}

	/* Remove the swapped window from the swap information array and decrement
	 * pNumWindows to indicate to the DIX level how many windows were actually
	 * swapped.
	 */
	if (*pNumWindows > 1) {
		/* We were told to swap more than one window, but we only swapped the
		 * first one.  Remove the first window in the list by moving the last
		 * window to the beginning.
		 */
		swapInfo[0].pWindow    = swapInfo[*pNumWindows - 1].pWindow;
		swapInfo[0].swapAction = swapInfo[*pNumWindows - 1].swapAction;

		/* Clear the last window information just to be safe. */
		swapInfo[*pNumWindows - 1].pWindow    = (WindowPtr)NULL;
		swapInfo[*pNumWindows - 1].swapAction = 0;
	} else {
		/* Clear the window information just to be safe. */
		swapInfo[0].pWindow    = (WindowPtr)NULL;
		swapInfo[0].swapAction = 0;
	}

	(*pNumWindows)--;

	FreeScratchGC(pGC);

	return Success;
}

static void
FFBDbeWinPrivDelete(DbeWindowPrivPtr pDbeWindowPriv, XID bufId)
{
	WindowPtr pWin = pDbeWindowPriv->pWindow;
	FFBDbeWindowPrivPrivPtr pDbeWindowPrivPriv;

	if (pDbeWindowPriv->nBufferIDs != 0) {
		/* We still have at least one more buffer ID associated with this
		 * window.
		 */
		return;
	}

	/* We have no more buffer IDs associated with this window.  We need to
	 * free some stuff.
	 */
	pDbeWindowPrivPriv = FFB_DBE_WINDOW_PRIV_PRIV(pDbeWindowPriv);

	/* If we were accelerating we need to restore the framebuffer
	 * attributes.  We need to also free up the Dbe WID and go
	 * back to using the shared one.
	 */
	if (pDbeWindowPrivPriv->HwAccelerated != 0) {
		FFBPtr pFfb = GET_FFB_FROM_SCREEN(pWin->drawable.pScreen);
		CreatorPrivWinPtr pFfbPrivWin = CreatorGetWindowPrivate(pWin);
		xRectangle clearRect;
		unsigned int fbc;
		GCPtr pGC;

		pFfbPrivWin->wid = FFBWidReshare(pFfb, pFfbPrivWin->wid);

		/* Go back to using buffer A. */
		fbc = pFfbPrivWin->fbc_base;
		fbc &= ~(FFB_FBC_WB_MASK | FFB_FBC_RB_MASK);
		fbc |= FFB_FBC_WB_A | FFB_FBC_RB_A;

		/* Now fixup the WID channel. */
		pFfbPrivWin->fbc_base =
			(fbc & ~FFB_FBC_RGBE_MASK) | FFB_FBC_RGBE_OFF;

		pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);
		clearRect.x = clearRect.y = 0;
		clearRect.width  = pWin->drawable.width;
		clearRect.height = pWin->drawable.height;
		ValidateGC(&pWin->drawable, pGC);
		FFBDbeUpdateWidPlane(pWin, pGC);
		(*pGC->ops->PolyFillRect)(&pWin->drawable, pGC, 1, &clearRect);
		FreeScratchGC(pGC);

		pFfbPrivWin->fbc_base = fbc;
	} else {
		/* Destroy the front and back pixmaps. */
		if (pDbeWindowPrivPriv->pFrontBuffer)
			(*pDbeWindowPriv->pWindow->drawable.pScreen->DestroyPixmap)(
				pDbeWindowPrivPriv->pFrontBuffer);
		if (pDbeWindowPrivPriv->pBackBuffer)
			(*pDbeWindowPriv->pWindow->drawable.pScreen->DestroyPixmap)(
				pDbeWindowPrivPriv->pBackBuffer);
	}
}

static Bool
FFBDbePositionWindow(WindowPtr pWin, int x, int y)
{
	ScreenPtr		pScreen;
	DbeScreenPrivPtr	pDbeScreenPriv;
	DbeWindowPrivPtr	pDbeWindowPriv;
	FFBDbeWindowPrivPrivPtr	pDbeWindowPrivPriv;
	int			width, height;
	int			dx, dy, dw, dh;
	int			sourcex, sourcey;
	int			destx, desty;
	int			savewidth, saveheight;
	PixmapPtr		pFrontBuffer;
	PixmapPtr		pBackBuffer;
	Bool			clear;
	GCPtr			pGC;
	xRectangle		clearRect;
	Bool			ret;

	/* 1. Unwrap the member routine. */
	pScreen                 = pWin->drawable.pScreen;
	pDbeScreenPriv          = DBE_SCREEN_PRIV(pScreen);
	pScreen->PositionWindow = pDbeScreenPriv->PositionWindow;

	/* 2. Do any work necessary before the member routine is called.
	 *
	 *    In this case we do not need to do anything.
	 */
     
	/* 3. Call the member routine, saving its result if necessary. */
	ret = (*pScreen->PositionWindow)(pWin, x, y);

	/* 4. Rewrap the member routine, restoring the wrapper value first in case
	 *    the wrapper (or something that it wrapped) change this value.
	 */
	pDbeScreenPriv->PositionWindow = pScreen->PositionWindow;
	pScreen->PositionWindow = FFBDbePositionWindow;

	/* 5. Do any work necessary after the member routine has been called. */
	if (!(pDbeWindowPriv = DBE_WINDOW_PRIV(pWin)))
		return ret;

	if (pDbeWindowPriv->width  == pWin->drawable.width &&
	    pDbeWindowPriv->height == pWin->drawable.height)
		return ret;

	width  = pWin->drawable.width;
	height = pWin->drawable.height;

	dx = pWin->drawable.x - pDbeWindowPriv->x;
	dy = pWin->drawable.y - pDbeWindowPriv->y;
	dw = width  - pDbeWindowPriv->width;
	dh = height - pDbeWindowPriv->height;

	GravityTranslate (0, 0, -dx, -dy, dw, dh, pWin->bitGravity, &destx, &desty);

	clear = ((pDbeWindowPriv->width  < (unsigned short)width ) ||
		 (pDbeWindowPriv->height < (unsigned short)height) ||
		 (pWin->bitGravity == ForgetGravity));

	sourcex = 0;
	sourcey = 0;
	savewidth  = pDbeWindowPriv->width;
	saveheight = pDbeWindowPriv->height;

	/* Clip rectangle to source and destination. */
	if (destx < 0) {
		savewidth += destx;
		sourcex   -= destx;
		destx      = 0;
	}

	if (destx + savewidth > width)
		savewidth = width - destx;

	if (desty < 0) {
		saveheight += desty;
		sourcey    -= desty;
		desty       = 0;
	}

	if (desty + saveheight > height)
		saveheight = height - desty;

	pDbeWindowPriv->width  = width;
	pDbeWindowPriv->height = height;
	pDbeWindowPriv->x = pWin->drawable.x;
	pDbeWindowPriv->y = pWin->drawable.y;

	pGC = GetScratchGC (pWin->drawable.depth, pScreen);

	if (clear) {
		if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
			clearRect.x = 0;
			clearRect.y = 0;
			clearRect.width  = width;
			clearRect.height = height;
		} else {
			clear = FALSE;
		}
	}

	pDbeWindowPrivPriv = FFB_DBE_WINDOW_PRIV_PRIV(pDbeWindowPriv);
	if (pDbeWindowPrivPriv->HwAccelerated != 0) {
		/* If we're hw accelerating, things are much easier. */
		ValidateGC(&pWin->drawable, pGC);
		FFBDbeUpdateWidPlane(pWin, pGC);
		if (clear) {
			CreatorPrivWinPtr pFfbPrivWin = CreatorGetWindowPrivate(pWin);
			unsigned int fbc, orig_fbc;

			ValidateGC(&pWin->drawable, pGC);
			(*pGC->ops->PolyFillRect)(&pWin->drawable, pGC,
						  1, &clearRect);

			orig_fbc = fbc = pFfbPrivWin->fbc_base;
			fbc &= ~(FFB_FBC_WB_MASK);
			if (pDbeWindowPrivPriv->HwCurrentBuf == 0)
				fbc |= FFB_FBC_WB_B;
			else
				fbc |= FFB_FBC_WB_A;

			pFfbPrivWin->fbc_base = fbc;

			if ((*pDbeScreenPriv->SetupBackgroundPainter)(pWin, pGC)) {
				ValidateGC(&pWin->drawable, pGC);
				clearRect.x = 0;
				clearRect.y = 0;
				clearRect.width  = width;
				clearRect.height = height;
				(*pGC->ops->PolyFillRect)(&pWin->drawable, pGC,
							  1, &clearRect);
			}

			pFfbPrivWin->fbc_base = orig_fbc;
		}

		FreeScratchGC(pGC);
	} else {
		/* Create DBE buffer pixmaps equal to size of resized window. */
		pFrontBuffer = (*pScreen->CreatePixmap)(pScreen, width, height,
							pWin->drawable.depth);

		pBackBuffer = (*pScreen->CreatePixmap)(pScreen, width, height,
						       pWin->drawable.depth);

		if (!pFrontBuffer || !pBackBuffer) {
			/* We failed at creating 1 or 2 of the pixmaps. */
			if (pFrontBuffer)
				(*pScreen->DestroyPixmap)(pFrontBuffer);
			if (pBackBuffer)
				(*pScreen->DestroyPixmap)(pBackBuffer);

			/* Destroy all buffers for this window. */
			while (pDbeWindowPriv) {
				/* DbeWindowPrivDelete() will free the window private
				 * if there no more buffer IDs associated with this
				 * window.
				 */
				FreeResource(pDbeWindowPriv->IDs[0], RT_NONE);
				pDbeWindowPriv = DBE_WINDOW_PRIV(pWin);
			}
			FreeScratchGC(pGC);
			return FALSE;
		} else {
			/* Clear out the new DBE buffer pixmaps. */
			ValidateGC((DrawablePtr)pFrontBuffer, pGC);

			/* I suppose this could avoid quite a bit of work if
			 * it computed the minimal area required.
			 */
			if (clear) {
				(*pGC->ops->PolyFillRect)((DrawablePtr)pFrontBuffer, pGC, 1,
							  &clearRect);
				(*pGC->ops->PolyFillRect)((DrawablePtr)pBackBuffer , pGC, 1,
							  &clearRect);
			}

			/* Copy the contents of the old DBE pixmaps to the new pixmaps. */
			if (pWin->bitGravity != ForgetGravity) {
				(*pGC->ops->CopyArea)((DrawablePtr)pDbeWindowPrivPriv->pFrontBuffer,
						      (DrawablePtr)pFrontBuffer, pGC,
						      sourcex, sourcey,
						      savewidth, saveheight,
						      destx, desty);
				(*pGC->ops->CopyArea)((DrawablePtr)pDbeWindowPrivPriv->pBackBuffer,
						      (DrawablePtr)pBackBuffer, pGC,
						      sourcex, sourcey,
						      savewidth, saveheight, destx, desty);
			}

			/* Destroy the old pixmaps, and point the DBE window priv to the new
			 * pixmaps.
			 */
			(*pScreen->DestroyPixmap)(pDbeWindowPrivPriv->pFrontBuffer);
			(*pScreen->DestroyPixmap)(pDbeWindowPrivPriv->pBackBuffer);

			pDbeWindowPrivPriv->pFrontBuffer = pFrontBuffer;
			pDbeWindowPrivPriv->pBackBuffer  = pBackBuffer;

			/* Make sure all XID are associated with the new back pixmap. */
			FFBDbeAliasBuffers(pDbeWindowPriv);

			FreeScratchGC(pGC);
		}
	}

	return ret;
}

static void
FFBDbeResetProc(ScreenPtr pScreen)
{
	DbeScreenPrivPtr pDbeScreenPriv = DBE_SCREEN_PRIV(pScreen);

	/* Unwrap wrappers */
	pScreen->PositionWindow = pDbeScreenPriv->PositionWindow;
}

static Bool
FFBDbeInit(ScreenPtr pScreen, DbeScreenPrivPtr pDbeScreenPriv)
{
	ScrnInfoPtr pScrn;
	FFBPtr pFfb;

	pScrn = xf86Screens[pScreen->myNum];
	pFfb = GET_FFB_FROM_SCRN(pScrn);
	xf86Msg(X_INFO, "%s: Setting up double-buffer acceleration.\n",
		pFfb->psdp->device);

	/* Copy resource types created by DIX */
	dbeDrawableResType   = pDbeScreenPriv->dbeDrawableResType;
	dbeWindowPrivResType = pDbeScreenPriv->dbeWindowPrivResType;

	/* Copy private indices created by DIX */
	dbeScreenPrivIndex = pDbeScreenPriv->dbeScreenPrivIndex;
	dbeWindowPrivIndex = pDbeScreenPriv->dbeWindowPrivIndex;

	/* Reset the window priv privs if generations do not match. */
	if (FFBDbePrivPrivGeneration != serverGeneration) {
		/* Allocate the window priv priv. */
		FFBDbeWindowPrivPrivIndex = (*pDbeScreenPriv->AllocWinPrivPrivIndex)();

		if (!(*pDbeScreenPriv->AllocWinPrivPriv)(pScreen,
							 FFBDbeWindowPrivPrivIndex,
							 sizeof(FFBDbeWindowPrivPrivRec)))
			return FALSE;

		/* Make sure we only do this code once. */
		FFBDbePrivPrivGeneration = serverGeneration;
	}

	/* Wrap functions. */
	pDbeScreenPriv->PositionWindow = pScreen->PositionWindow;
	pScreen->PositionWindow        = FFBDbePositionWindow;

	/* Initialize the per-screen DBE function pointers. */
	pDbeScreenPriv->GetVisualInfo         = FFBDbeGetVisualInfo;
	pDbeScreenPriv->AllocBackBufferName   = FFBDbeAllocBackBufferName;
	pDbeScreenPriv->SwapBuffers           = FFBDbeSwapBuffers;
	pDbeScreenPriv->BeginIdiom            = 0;
	pDbeScreenPriv->EndIdiom              = 0;
	pDbeScreenPriv->ResetProc             = FFBDbeResetProc;
	pDbeScreenPriv->WinPrivDelete         = FFBDbeWinPrivDelete;

	/* The FFB implementation doesn't need buffer validation. */
	pDbeScreenPriv->ValidateBuffer	  = (void (*)())NoopDDA;

	return TRUE;
}

extern void DbeRegisterFunction(ScreenPtr pScreen, Bool (*funct)(ScreenPtr, DbeScreenPrivPtr));

Bool
FFBDbePreInit(ScreenPtr pScreen)
{
	DbeRegisterFunction(pScreen, FFBDbeInit);
	return TRUE;
}
