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

   X server for LibGLTEX - primary output handling functions

   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 "xgltex.h"
#include "xggi.h"

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

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)
{
	xgltexUsage();
}

int ddxProcessArgument(int argc, char *argv[], int argi)
{
	return xgltexProcessArg(argc, argv, argi);
}

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 ggimap[MAX_COLORS];

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

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

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

	for (i = 0; i < ndef; i++) {
		if (pdefs[i].pixel >= MAX_COLORS) continue;
		if (pdefs[i].flags & DoRed)  ggimap[pdefs[i].pixel].r = pdefs[i].red;
		if (pdefs[i].flags & DoGreen)ggimap[pdefs[i].pixel].g = pdefs[i].green;
		if (pdefs[i].flags & DoBlue) ggimap[pdefs[i].pixel].b = pdefs[i].blue;
	}

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

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 Bool xgltexCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
{
	/* no interscreen movement for now */
	return FALSE;
}

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

static miPointerScreenFuncRec xgltexPointerCursorFuncs =
{
	xgltexCursorOffScreen,
	xgltexCrossScreen,
	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("idx: %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, &xgltexPointerCursorFuncs);

	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;
}

static void InitGGI(ScreenInfo *screenInfo, int argc, char **argv)
{
	/* this function sets up the rendering area, but doesn't inform X */

	/* XGLTEX drawing area initialization */
	{
	xgltexVarsPrint(stderr);
	xgltexVarsActivate();		/* create/attach the texture area */
	/* memory to use is at xgltexVars.shmaddr */
	}

	/* XGGI drawing area initialization */
	{
		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 (NULL == (xggiScreen.vis = ggiOpen(currtarget)))
				FatalError("Unable to open LibGGI visual: \"%s\"\n",
						   currtarget ? currtarget : "(NULL)");
			ggiParseMode("", &xggiScreen.mode);

			if ((xggiScreen.mode.graphtype == GT_AUTO) && (defaultbpp != 0))
			{
				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;
				}
				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/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 --- */

