/*
   Copyright (C) 1999.  The XFree86 Project Inc.

   Written by David S. Miller (davem@redhat.com)

   Based largely upon the xf8_16bpp module which is
   Mark Vojkovich's work.
*/

/* $XFree86$ */

#include "X.h"
#include "Xmd.h"
#include "misc.h"
#include "servermd.h"
#include "scrnintstr.h"
#include "pixmapstr.h"
#include "resource.h"
#include "colormap.h"
#include "colormapst.h"
#define PSZ 8
#include "cfb.h"
#undef PSZ
#include "cfb32.h"
#include "cfb8_32wid.h"
#include "mi.h"
#include "micmap.h"
#include "mistruct.h"
#include "gcstruct.h"
#include "dix.h"
#include "mibstore.h"
#include "xf86str.h"
#include "xf86.h"

/* CAUTION:  We require that cfb8 and cfb32 were NOT 
	compiled with CFB_NEED_SCREEN_PRIVATE */

int cfb8_32WidScreenPrivateIndex;
int cfb8_32WidGetScreenPrivateIndex(void)
{
    return cfb8_32WidScreenPrivateIndex;
}

static unsigned long cfb8_32WidGeneration = 0;
extern WindowPtr *WindowTable;

static PixmapPtr cfb8_32WidGetWindowPixmap(WindowPtr pWin);

static void
cfb8_32WidSaveAreas(
    PixmapPtr	  	pPixmap,
    RegionPtr	  	prgnSave, 
    int	    	  	xorg,
    int	    	  	yorg,
    WindowPtr		pWin
);

static void
cfb8_32WidRestoreAreas(
    PixmapPtr	  	pPixmap, 
    RegionPtr	  	prgnRestore,
    int	    	  	xorg,
    int	    	  	yorg,
    WindowPtr		pWin
);

static BSFuncRec cfb8_32WidBSFuncRec = {
    cfb8_32WidSaveAreas,
    cfb8_32WidRestoreAreas,
    (BackingStoreSetClipmaskRgnProcPtr) 0,
    (BackingStoreGetImagePixmapProcPtr) 0,
    (BackingStoreGetSpansPixmapProcPtr) 0,
};

static void
cfb8_32WidGetSpans(
   DrawablePtr pDraw,
   int wMax,
   DDXPointPtr ppt,
   int *pwidth,
   int nspans,
   char *pchardstStart
);

static void
cfb8_32WidGetImage (
    DrawablePtr pDraw,
    int sx, int sy, int w, int h,
    unsigned int format,
    unsigned long planeMask,
    char *pdstLine
);

static Bool cfb8_32WidCreateGC(GCPtr pGC);
static void cfb8_32WidEnableDisableFBAccess(int index, Bool enable);


static Bool
cfb8_32WidAllocatePrivates(ScreenPtr pScreen)
{
	cfb8_32WidScreenPtr pScreenPriv;

	if (cfb8_32WidGeneration != serverGeneration) {
		if ((cfb8_32WidScreenPrivateIndex = AllocateScreenPrivateIndex()) < 0)
			return FALSE;
		cfb8_32WidGeneration = serverGeneration;
	}

	if (!(pScreenPriv = xalloc(sizeof(cfb8_32WidScreenRec))))
		return FALSE;

	pScreen->devPrivates[cfb8_32WidScreenPrivateIndex].ptr = (pointer)pScreenPriv;
   
	/* All cfb will have the same GC and Window private indicies */
	if (!mfbAllocatePrivates(pScreen, &cfbWindowPrivateIndex, &cfbGCPrivateIndex))
		return FALSE;

	/* The cfb indicies are the mfb indicies. Reallocating them resizes them */ 
	if (!AllocateWindowPrivate(pScreen, cfbWindowPrivateIndex, sizeof(cfbPrivWin)))
		return FALSE;

	if (!AllocateGCPrivate(pScreen, cfbGCPrivateIndex, sizeof(cfbPrivGC)))
		return FALSE;

	return TRUE;
}

static Bool
cfb8_32WidSetupScreen(
    ScreenPtr pScreen,
    int xsize, int ysize,	/* in pixels */
    int dpix, int dpiy
){
	if (!cfb8_32WidAllocatePrivates(pScreen))
		return FALSE;

	pScreen->defColormap = FakeClientID(0);

	/* let CreateDefColormap do whatever it wants for pixels */ 
	pScreen->blackPixel = pScreen->whitePixel = (Pixel) 0;
	pScreen->QueryBestSize = mfbQueryBestSize;

	/* SaveScreen */
	pScreen->GetImage = cfb8_32WidGetImage;
	pScreen->GetSpans = cfb8_32WidGetSpans;
	pScreen->CreateWindow = cfb8_32WidCreateWindow;
	pScreen->DestroyWindow = cfb8_32WidDestroyWindow;	
	pScreen->PositionWindow = cfb8_32WidPositionWindow;
	pScreen->ChangeWindowAttributes = cfb8_32WidChangeWindowAttributes;
	pScreen->RealizeWindow = cfb32MapWindow;		/* OK */
	pScreen->UnrealizeWindow = cfb32UnmapWindow;		/* OK */
	pScreen->PaintWindowBackground = cfb8_32WidPaintWindow;
	pScreen->PaintWindowBorder = cfb8_32WidPaintWindow;
	pScreen->CopyWindow = cfb8_32WidCopyWindow;
	pScreen->CreatePixmap = cfb32CreatePixmap;		/* OK */
	pScreen->DestroyPixmap = cfb32DestroyPixmap; 		/* OK */
	pScreen->RealizeFont = mfbRealizeFont;
	pScreen->UnrealizeFont = mfbUnrealizeFont;
	pScreen->CreateGC = cfb8_32WidCreateGC;
	pScreen->CreateColormap = miInitializeColormap;
	pScreen->DestroyColormap = (void (*)())NoopDDA;
	pScreen->InstallColormap = miInstallColormap;
	pScreen->UninstallColormap = miUninstallColormap;
	pScreen->ListInstalledColormaps = miListInstalledColormaps;
	pScreen->StoreColors = (void (*)())NoopDDA;
	pScreen->ResolveColor = miResolveColor;
	pScreen->BitmapToRegion = mfbPixmapToRegion;

	mfbRegisterCopyPlaneProc (pScreen, cfbCopyPlane);
	return TRUE;
}

static Bool
cfb8_32WidCreateScreenResources(ScreenPtr pScreen)
{
	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
	cfb8_32WidScreenPtr pScreenPriv = CFB8_32WID_GET_SCREEN_PRIVATE(pScreen);
	PixmapPtr pix8, pix32, pixWid;
    
	xfree(pScreen->devPrivate); /* freeing miScreenInitParmsRec */

	pix8 = (*pScreen->CreatePixmap)(pScreen, 0, 0, 8);
	pix32 = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScrn->depth);
	pixWid = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreenPriv->bitsPerWid);
	if (!pix32 || !pix8 || !pixWid)
		return FALSE;

	pix8->drawable.width = pScreen->width;
	pix8->drawable.height = pScreen->height;
	pix8->devKind = pScreenPriv->width8;
	pix8->devPrivate.ptr = pScreenPriv->pix8;

	pix32->drawable.width = pScreen->width;
	pix32->drawable.height = pScreen->height;
	pix32->devKind = pScreenPriv->width32 * 4;
	pix32->devPrivate.ptr = pScreenPriv->pix32;

	pixWid->drawable.width = pScreen->width;
	pixWid->drawable.height = pScreen->height;
	pixWid->devKind = (pScreenPriv->widthWid * pScreenPriv->bitsPerWid) / 8;
	pixWid->devPrivate.ptr = pScreenPriv->pixWid;

	pScreenPriv->pix8   = (pointer) pix8;
	pScreenPriv->pix32  = (pointer) pix32;
	pScreenPriv->pixWid = (pointer) pixWid;

	pScreen->devPrivate = (pointer) pix32;

	return TRUE;
}


static Bool
cfb8_32WidCloseScreen (int i, ScreenPtr pScreen)
{
	cfb8_32WidScreenPtr pScreenPriv = CFB8_32WID_GET_SCREEN_PRIVATE(pScreen);

	xfree((pointer) pScreenPriv);

	return cfb32CloseScreen(i, pScreen);
}

static Bool
cfb8_32WidFinishScreenInit(
    ScreenPtr pScreen,
    int xsize, int ysize,	/* in pixels */
    int dpix, int dpiy		/* dots per inch */
){
	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
	VisualPtr	visuals;
	DepthPtr	depths;
	int		nvisuals;
	int		ndepths;
	int		rootdepth;
	VisualID	defaultVisual;

	rootdepth = 0;
	if (!miInitVisuals(&visuals, &depths, &nvisuals, &ndepths, &rootdepth,
			    &defaultVisual,((unsigned long)1<<(32-1)), 8, -1))
		return FALSE;

	if (!miScreenInit(pScreen, NULL, xsize, ysize, dpix, dpiy, 0,
			  rootdepth, ndepths, depths,
			  defaultVisual, nvisuals, visuals))
		return FALSE;

	pScreen->BackingStoreFuncs = cfb8_32WidBSFuncRec;
	pScreen->CreateScreenResources = cfb8_32WidCreateScreenResources;
	pScreen->CloseScreen = cfb8_32WidCloseScreen;
	pScreen->GetWindowPixmap = cfb8_32WidGetWindowPixmap;
	pScreen->WindowExposures = cfb8_32WidWindowExposures;

	pScrn->EnableDisableFBAccess = cfb8_32WidEnableDisableFBAccess;

	return TRUE;
}

Bool
cfb8_32WidScreenInit(
    ScreenPtr pScreen,
    pointer pbits32,		/* pointer to screen bitmap */
    pointer pbits8,
    pointer pbitsWid,		/* pointer to WID bitmap */
    int xsize, int ysize,	/* in pixels */
    int dpix, int dpiy,		/* dots per inch */
    int width32,		/* pixel width of frame buffer */
    int width8,
    int widthWid,
    int bitsPerWid,
    cfb8_32WidOps *WIDOps
){
	cfb8_32WidScreenPtr pScreenPriv;

	if (!WIDOps || !WIDOps->WidGet || !WIDOps->WidAlloc || !WIDOps->WidFree)
		return FALSE;

	if (!cfb8_32WidSetupScreen(pScreen, xsize, ysize, dpix, dpiy))
		return FALSE;

	pScreenPriv = CFB8_32WID_GET_SCREEN_PRIVATE(pScreen);
	pScreenPriv->pix8 = pbits8;
	pScreenPriv->pix32 = pbits32;
	pScreenPriv->pixWid = pbitsWid;
	pScreenPriv->width8 = width8;
	pScreenPriv->width32 = width32;
	pScreenPriv->widthWid = widthWid;
	pScreenPriv->bitsPerWid = bitsPerWid;

	pScreenPriv->WIDOps = xalloc(sizeof(cfb8_32WidOps));
	if (!pScreenPriv->WIDOps)
		return FALSE;

	*(pScreenPriv->WIDOps) = *WIDOps;

	if (!WIDOps->WidFillBox || !WIDOps->WidCopyArea)
		cfb8_32WidGenericOpsInit(pScreenPriv);

	return cfb8_32WidFinishScreenInit(pScreen, xsize, ysize, dpix, dpiy);
}

static PixmapPtr
cfb8_32WidGetWindowPixmap(WindowPtr pWin)
{
	cfb8_32WidScreenPtr pScreenPriv = 
		CFB8_32WID_GET_SCREEN_PRIVATE(pWin->drawable.pScreen);

	return ((pWin->drawable.bitsPerPixel == 8) ?
		(PixmapPtr) pScreenPriv->pix8 : (PixmapPtr) pScreenPriv->pix32);
}

static void
cfb8_32WidGetImage (DrawablePtr pDraw, int sx, int sy, int w, int h,
		    unsigned int format, unsigned long planemask, char *pdstLine)
{
	if (!w || !h)
		return;

	if (pDraw->bitsPerPixel == 8)
		cfbGetImage(pDraw, sx, sy, w, h, format, planemask, pdstLine);
	else
		cfb32GetImage(pDraw, sx, sy, w, h, format, planemask, pdstLine);
}

static void
cfb8_32WidGetSpans(DrawablePtr pDraw, int wMax, DDXPointPtr ppt, int *pwidth, int nspans, char *pDst)
{
	if (pDraw->bitsPerPixel == 8)
		cfbGetSpans(pDraw, wMax, ppt, pwidth, nspans, pDst);
	else
		cfb32GetSpans(pDraw, wMax, ppt, pwidth, nspans, pDst);
}

static void
cfb8_32WidSaveAreas(PixmapPtr pPixmap, RegionPtr prgnSave, int xorg, int yorg, WindowPtr pWin)
{
	if (pWin->drawable.bitsPerPixel == 8)
		cfbSaveAreas(pPixmap, prgnSave, xorg, yorg, pWin);
	else
		cfb32SaveAreas(pPixmap, prgnSave, xorg, yorg, pWin);
}

static void
cfb8_32WidRestoreAreas(PixmapPtr pPixmap, RegionPtr prgnRestore, int xorg, int yorg, WindowPtr pWin)
{
	if (pWin->drawable.bitsPerPixel == 8)
		cfbRestoreAreas(pPixmap, prgnRestore, xorg, yorg, pWin);
	else
		cfb32RestoreAreas(pPixmap, prgnRestore, xorg, yorg, pWin);
}


static Bool
cfb8_32WidCreateGC(GCPtr pGC)
{
	if (pGC->depth == 8)
		return cfbCreateGC(pGC);
	else
		return cfb32CreateGC(pGC);
}

static void
cfb8_32WidEnableDisableFBAccess(int index, Bool enable)
{
	ScreenPtr pScreen = xf86Screens[index]->pScreen;
	cfb8_32WidScreenPtr pScreenPriv = CFB8_32WID_GET_SCREEN_PRIVATE(pScreen);
	static DevUnion devPrivates8[MAXSCREENS];
	static DevUnion devPrivates32[MAXSCREENS];
	PixmapPtr	pix8, pix32;

	pix8 = (PixmapPtr) pScreenPriv->pix8;
	pix32 = (PixmapPtr) pScreenPriv->pix32;

	if (enable) {
		pix8->devPrivate = devPrivates8[index];
		pix32->devPrivate = devPrivates32[index];
	}

	xf86EnableDisableFBAccess (index, enable);

	if (!enable) {
		devPrivates8[index] = pix8->devPrivate;
		pix8->devPrivate.ptr = 0;
		devPrivates32[index] = pix32->devPrivate;
		pix32->devPrivate.ptr = 0;
	}
}
