/* $Id: Exp $
***************************************************************************

   X server for LibGLTEX - Handling of input devices

   Copyright (C) 2002 Christopher Alexander North-Keys
                      http://www.talisman.org/~erlkonig/

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

/* The key function in the file is InitOutput,
 * called by main in dix/main.c during server startup
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "X11/Xos.h"

#define PSZ 8
#include "cfb.h"
#include "../../hw/xfree86/common/cfb16.h"
#include "../../hw/xfree86/common/cfb24.h"
#include "../../hw/xfree86/common/cfb32.h"

#include "scrnintstr.h"
#include "servermd.h"
#include "colormapst.h"
#include "mipointer.h"

#include "xggi.h"

#define MAX_COLORS 256 /* For the PseudoColor modes.. */

xgltexGlobalInfo_t xgltexInfo = {
	0,	/* not in use currently */
};

xggiScreenInfo_t xggiScreen;

static int defaultbpp = 0;
static int ggiinit = 0;

#ifdef DDXTIME /* from ServerOSDefines */
CARD32 GetTimeInMillis(void)
{
	struct timeval  tp;
	X_GETTIMEOFDAY(&tp);
#define TVAL2TIME(tv)	((tv).tv_sec*1000 + (tv).tv_usec/1000)
	return TVAL2TIME(tp);
#undef TVAL2TIME
}
#endif

static void *safe_alloc(long siz)
{
	void *ret = xalloc(siz);
	if( ! ret) FatalError("Unable to allocate %d bytes of memory\n", siz);

	return ret;
}


static void _ggiexit(void)
{
	int i;

	if(xggiScreen.vis)
	{
		ggiClose(xggiScreen.vis);
		xggiScreen.vis = NULL;
	}
	if (ggiinit) {
		ggiExit();
		ggiinit = 0;
	}
}
	
void AbortDDX(void)
{
	_ggiexit();
}

void ddxGiveUp(void)
{
	_ggiexit();
}


void OsVendorInit(void)
{
}

void OsVendorFatalError(void)
{
	_ggiexit();
}

void ddxUseMsg(void)
{
	ErrorF("XGLTEX adds the following arguments:\n"
		   "-bpp bitdepth      XFree86 compability option\n"
		   );
}

int ddxProcessArgument(int argc, char *argv[], int i)
{
	if (!strcmp(argv[i], "-bpp")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-bpp' requires an argument\n");
			return 1;
		}
		defaultbpp = atoi(argv[i+1]);
		return 2;
	}
	return 0;
}

static ColormapPtr InstalledMaps[MAXSCREENS];

static int xgltexListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps)
{
	/* By the time we are processing requests, we can guarantee that there
	 * is always a colormap installed */
	*pmaps = InstalledMaps[pScreen->myNum]->mid;
	return (1);
}

static void xgltexInstallColormap(ColormapPtr pmap)
{
	int index = pmap->pScreen->myNum;
	ColormapPtr oldpmap = InstalledMaps[index];

	if (pmap != oldpmap) {
		int entries;
		VisualPtr pVisual;
		Pixel *     ppix;
		xrgb *      prgb;
		xColorItem *defs;
		int i;

		if (oldpmap != (ColormapPtr)None)
			WalkTree(pmap->pScreen, TellLostMap, (char *)&oldpmap->mid);
		/* Install pmap */
		InstalledMaps[index] = pmap;
		WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid);

		entries = pmap->pVisual->ColormapEntries;
		pVisual = pmap->pVisual;

		ppix = (Pixel *)ALLOCATE_LOCAL(entries * sizeof(Pixel));
		prgb = (xrgb *)ALLOCATE_LOCAL(entries * sizeof(xrgb));
		defs = (xColorItem *)ALLOCATE_LOCAL(entries * sizeof(xColorItem));

		for (i = 0; i < entries; i++)  ppix[i] = i;
		/* XXX truecolor */
		QueryColors(pmap, entries, ppix, prgb);

		for (i = 0; i < entries; i++) { /* convert xrgbs to xColorItems */
			defs[i].pixel = ppix[i] & 0xff; /* change pixel to index */
			defs[i].red = prgb[i].red;
			defs[i].green = prgb[i].green;
			defs[i].blue = prgb[i].blue;
			defs[i].flags =  DoRed|DoGreen|DoBlue;
		}
		(*pmap->pScreen->StoreColors)(pmap, entries, defs);

		DEALLOCATE_LOCAL(ppix);
		DEALLOCATE_LOCAL(prgb);
		DEALLOCATE_LOCAL(defs);
	}
}

static void xgltexUninstallColormaps(ColormapPtr pmap)
{
	ColormapPtr curpmap = InstalledMaps[pmap->pScreen->myNum];

	if (pmap == curpmap) {
		if (pmap->mid != pmap->pScreen->defColormap) {
			curpmap = (ColormapPtr)
				LookupIDByType(pmap->pScreen->defColormap,
							   RT_COLORMAP);
			(*pmap->pScreen->InstallColormap)(curpmap);
		}
	}
}

static void xggiStoreColors(ColormapPtr pmap, int ndef, xColorItem  *pdefs)
{
	int i;
	ggi_color cmap[MAX_COLORS];
	int scr;

	if (pmap != InstalledMaps[pmap->pScreen->myNum])
		return;

	if ((pmap->pVisual->class | DynamicClass) == DirectColor)
		return;

	ggiGetPalette(xggiScreen.vis, 0, MAX_COLORS, cmap);

	for (i = 0; i < ndef; i++) {
		if (pdefs[i].pixel >= MAX_COLORS) continue;

		if (pdefs[i].flags & DoRed) {
			cmap[pdefs[i].pixel].r = pdefs[i].red;
		}
		if (pdefs[i].flags & DoGreen) {
			cmap[pdefs[i].pixel].g = pdefs[i].green;
		}
		if (pdefs[i].flags & DoBlue) {
			cmap[pdefs[i].pixel].b = pdefs[i].blue;
		}
	}

	ggiSetPalette(xggiScreen.vis, 0, MAX_COLORS, cmap);
}

static Bool xgltexSaveScreen(ScreenPtr pScreen, int on)
{
#if 0
	switch (what) {
	case SCREEN_SAVER_ON:		    break;
	case SCREEN_SAVER_OFF:		    break;
	case SCREEN_SAVER_FORCER:		break;
	case SCREEN_SAVER_CYCLE:		break;
	}
#endif
	return TRUE;
}


static void do_blit(ScreenPtr pScreen, int bits_per_pixel,
					DrawablePtr src, DrawablePtr dest)
{
	DDXPointRec pixPt;
	BoxRec      pixBox;
	RegionRec   pixReg;

	pixBox.x1 = 0;
	pixBox.x2 = pScreen->width;
	pixBox.y1 = 0;
	pixBox.y2 = pScreen->height;
	pixPt.x = pixPt.y = 0;
	pScreen->RegionInit(&pixReg, &pixBox, 1);

	if (bits_per_pixel == 1) {
		mfbDoBitblt(src, dest, GXcopy, &pixReg, &pixPt);
	} else if (bits_per_pixel == 8) { 
		cfbDoBitblt(src, dest, GXcopy, &pixReg, &pixPt, 0xFF);
	} else if (bits_per_pixel == 16) {
		cfb16DoBitblt(src, dest, GXcopy, &pixReg, &pixPt, 0xFFFF);
	} else if (bits_per_pixel == 24) {
		cfb24DoBitblt(src, dest, GXcopy, &pixReg, &pixPt, 0xFFFFFF);
	} else if (bits_per_pixel == 32) {
		cfb32DoBitblt(src, dest, GXcopy, &pixReg, &pixPt, 0xFFFFFFFF);
	}
}

static void dounmap()
{
	ScreenPtr pScreen = xggiScreen.screen;
	int bits_per_pixel = xggiScreen.bitsPerPixel;
	PixmapPtr frontbuf;

	xggiScreen.backbuffer
		= pScreen->CreatePixmap(pScreen, pScreen->width,
								pScreen->height, pScreen->rootDepth);
	if (!xggiScreen.backbuffer) {
		FatalError("Unable to allocate backbuffer!\n");
	}

	if (bits_per_pixel == 16) {
		frontbuf= (PixmapPtr)pScreen->devPrivates[cfb16ScreenPrivateIndex].ptr;
	} else if (bits_per_pixel == 24) {
		frontbuf= (PixmapPtr)pScreen->devPrivates[cfb24ScreenPrivateIndex].ptr;
	} else if (bits_per_pixel == 32) {
		frontbuf= (PixmapPtr)pScreen->devPrivates[cfb32ScreenPrivateIndex].ptr;
	} else {
		frontbuf= (PixmapPtr)pScreen->devPrivate;
	}

	do_blit(pScreen, bits_per_pixel,
			&frontbuf->drawable,
			&xggiScreen.backbuffer->drawable);

	frontbuf->devPrivate.ptr = xggiScreen.backbuffer->devPrivate.ptr;
	xggiScreen.ismapped = 0;
}

	
static void domap()
{
	ScreenPtr pScreen = xggiScreen.screen;
	int bits_per_pixel = xggiScreen.bitsPerPixel;
	PixmapPtr frontbuf;

	if (bits_per_pixel == 16) {
		frontbuf = (PixmapPtr)pScreen->devPrivates[cfb16ScreenPrivateIndex].ptr;
	} else if (bits_per_pixel == 24) {
		frontbuf = (PixmapPtr)pScreen->devPrivates[cfb24ScreenPrivateIndex].ptr;
	} else if (bits_per_pixel == 32) {
		frontbuf = (PixmapPtr)pScreen->devPrivates[cfb32ScreenPrivateIndex].ptr;
	} else {
		frontbuf = (PixmapPtr)pScreen->devPrivate;
	}

	frontbuf->devPrivate.ptr = xggiScreen.pfbMemory;

	do_blit(pScreen, bits_per_pixel,
			&xggiScreen.backbuffer->drawable,
			&frontbuf->drawable);
	
	pScreen->DestroyPixmap(xggiScreen.backbuffer);
	xggiScreen.backbuffer = NULL;
	xggiScreen.ismapped = 1;
}

void xggiUnmapDisplay(void)
{
	dounmap();
}

void xggiMapDisplay(void)
{
	domap();
}

static Bool xggiCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
{
	/* no interscreen movement for now */
	return FALSE;
}

static void xggiCrossScreen(ScreenPtr pScreen, Bool entering)
{
}

static miPointerScreenFuncRec xggiPointerCursorFuncs =
{
	xggiCursorOffScreen,
	xggiCrossScreen,
	miPointerWarpCursor
};

static Bool xggiFBInitProc(int index, ScreenPtr pScreen, int argc, char **argv)
{
	const int dpix = 100, dpiy = 100;
	int scr = index;
	Bool ret;

#ifdef XGGI_DEBUG
	ErrorF("Index: %d, pScreen %p, depth: %d\n", index, pScreen,
	       xggiScreen.depth);
#endif

	xggiScreen.screen = pScreen;
	
	if (!xggiScreenInit(pScreen, xggiScreen.pfbMemory,
						xggiScreen.width, xggiScreen.height,
						dpix, dpiy, xggiScreen.stride)) {
		ErrorF("xggiScreenInit failed\n");
		return FALSE;
	}

	pScreen->SaveScreen = xgltexSaveScreen;

	switch (xggiScreen.depth) {
	case 1:
		pScreen->InstallColormap        = mfbInstallColormap;
		pScreen->UninstallColormap      = mfbUninstallColormap;
		pScreen->ListInstalledColormaps = mfbListInstalledColormaps;
		pScreen->StoreColors            = (void (*)())NoopDDA;
		break;
	case 15:
	case 16:
	case 24:
	case 32:
		pScreen->InstallColormap        = cfbInstallColormap;
		pScreen->UninstallColormap      = cfbUninstallColormap;
		pScreen->ListInstalledColormaps = cfbListInstalledColormaps;
		pScreen->StoreColors            = (void (*)())NoopDDA;
		break;
	default:
		pScreen->InstallColormap        = xgltexInstallColormap;
		pScreen->UninstallColormap      = xgltexUninstallColormaps;
		pScreen->ListInstalledColormaps = xgltexListInstalledColormaps;
		pScreen->StoreColors            = xggiStoreColors;
		break;
	}

	miDCInitialize(pScreen, &xggiPointerCursorFuncs);

	if (xggiScreen.depth == 1) {
		ret = mfbCreateDefColormap(pScreen);
	} else {
		ret = cfbCreateDefColormap(pScreen);
	}
	if (!ret) ErrorF("[c|m]fbCreateDefColormap failed\n");

	return ret;
} 

static void reset_xggiscreen()
{
	xggiScreen.vis          = NULL;
	xggiScreen.width		= GGI_AUTO;
	xggiScreen.height		= GGI_AUTO;
	xggiScreen.depth		= 0;
	xggiScreen.stride		= 0;
	xggiScreen.bitsPerPixel	= 0;
	/* xggiScreen.mode		= */
	xggiScreen.dbuf         = NULL;
	xggiScreen.pfbMemory	= NULL;
	xggiScreen.screen		= NULL;
	xggiScreen.backbuffer	= NULL;
	xggiScreen.ismapped		= 1;
}


static ggi_graphtype bpp2gt(int bpp)
{
	switch (bpp) {
	case 1:		return GT_1BIT;
	case 2:		return GT_2BIT;
	case 4:		return GT_4BIT;
	case 8:		return GT_8BIT;
	case 15:	return GT_15BIT;
	case 16:	return GT_16BIT;
	case 24:	return GT_24BIT;
	case 32:	return GT_32BIT;
	}
	return GT_AUTO;
}



static void InitGGI(ScreenInfo *screenInfo, int argc, char **argv)
{
	int i, size;
	ggi_graphtype gt;
	const ggi_directbuffer *dbuf = NULL;

	reset_xggiscreen();

	if (ggiInit() < 0) {
		FatalError("Unable to init LibGGI!\n");
	}
	ggiinit = 1;

	{							/* per-screen setup */
		char tgttemp[2048];
		char *currtarget = NULL;
		int j;

		if ((xggiScreen.vis = ggiOpen(currtarget)) == NULL) {
			FatalError("Unable to open LibGGI visual: \"%s\"\n",
					   currtarget ? currtarget : "(NULL)");
		}

#if 0	/* Can we hook in ggiFlush() somewhere? */
		ggiSetFlags(xggiScreen.vis, GGIFLAG_ASYNC);
#endif
		ggiParseMode("", &xggiScreen.mode);

		if (xggiScreen.mode.graphtype == GT_AUTO &&
		    defaultbpp != 0) {
			xggiScreen.mode.graphtype = bpp2gt(defaultbpp);
		}
		ggiCheckMode(xggiScreen.vis, &xggiScreen.mode);

		gt = xggiScreen.mode.graphtype;	/* screen 0 only  */

		if (GT_SIZE(xggiScreen.mode.graphtype) != 1 &&
		    GT_SIZE(xggiScreen.mode.graphtype) != 8 &&
		    GT_SIZE(xggiScreen.mode.graphtype) != 16 &&
		    GT_SIZE(xggiScreen.mode.graphtype) != 24 &&
		    GT_SIZE(xggiScreen.mode.graphtype) != 32) {
			FatalError("XGGI doesn't support %d bpp screens.\n",
					   GT_SIZE(xggiScreen.mode.graphtype));
		}
		if (xggiScreen.mode.graphtype != gt) {
			FatalError("All GGI visuals must be of the same depth.\n");
		}

		xggiScreen.width  = xggiScreen.mode.visible.x;
		xggiScreen.height = xggiScreen.mode.visible.y;
		xggiScreen.depth
			= GT_DEPTH(xggiScreen.mode.graphtype);
		xggiScreen.bitsPerPixel
			= GT_SIZE(xggiScreen.mode.graphtype);

		if (ggiSetMode(xggiScreen.vis, &xggiScreen.mode)) {
			FatalError("LibGGI can not set any modes at all!\n");
		}
	
		size = GT_SIZE(xggiScreen.mode.graphtype);
		for (j = 0 ; (dbuf = ggiDBGetBuffer(xggiScreen.vis, j)) ; ++j) {
			if ((dbuf->type & GGI_DB_SIMPLE_PLB)
			    && ((8*dbuf->buffer.plb.stride) % size) == 0) {
				xggiScreen.dbuf = dbuf;
				break;
			}
		}
		    
		if ( ! xggiScreen.dbuf) {
			FatalError("This mode and target has no suitable DirectBuffer\n");
		}

		if (ggiResourceAcquire(xggiScreen.dbuf->resource,
							   GGI_ACTYPE_WRITE | GGI_ACTYPE_READ)
		    != 0) {
			FatalError("Unable to acquire DirectBuffer\n");
		}

		xggiScreen.pfbMemory = dbuf->write;
		xggiScreen.stride = 8 * dbuf->buffer.plb.stride / size;
		xggiScreen.dbuf = dbuf;
	}
}

/* The argc/argv has already be stripped of items known to dix/main.c
   They\'ve also been copied into argcGlobal and argvGlobal
   The rather alarming Screen structure and screenInfo are set up in
   dix/main.c, but is a global from dix/globals.c
   screenInfo's members are: */
#if 0
typedef struct _ScreenInfo {
    int     imageByteOrder;
    int     bitmapScanlineUnit;
    int     bitmapScanlinePad;
    int     bitmapBitOrder;
    int     numPixmapFormats;
    PixmapFormatRec formats[MAXFORMATS];
    int     arraySize;
    int     numScreens;
    ScreenPtr   screens[MAXSCREENS];
    int     numVideoScreens;
} ScreenInfo;
extern ScreenInfo screenInfo;
#endif  /* 0 */

void InitOutput(ScreenInfo *screenInfo, int argc, char *argv[])
{
	int i;

	if ( ! ggiinit) {
		InitGGI(screenInfo, argc, argv);
	}

	/* add each format, increment the format count as we go
	   each format has only three members (all unsigned char):
	   depth, bitsPerPixel, and scanlinePad
	*/
	screenInfo->numPixmapFormats = 0;  /* incremented below */
	screenInfo->formats[0].depth        = 1;
	screenInfo->formats[0].bitsPerPixel = 1;
	screenInfo->formats[0].scanlinePad  = BITMAP_SCANLINE_PAD;
	++(screenInfo->numPixmapFormats);
	screenInfo->formats[1].depth        = xggiScreen.depth;
	screenInfo->formats[1].bitsPerPixel = xggiScreen.bitsPerPixel;
	screenInfo->formats[1].scanlinePad  = BITMAP_SCANLINE_PAD;
	++(screenInfo->numPixmapFormats);

	screenInfo->imageByteOrder     = IMAGE_BYTE_ORDER;
	screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT;
	screenInfo->bitmapScanlinePad  = BITMAP_SCANLINE_PAD;
	screenInfo->bitmapBitOrder     = BITMAP_BIT_ORDER;

	/* Add screens */
	if (AddScreen(xggiFBInitProc, argc, argv) == -1) {
			FatalError("Couldn't add screen");
	}
}

/*-------------------------------------------------------------- eof --- */

