/* $Id: graphics.c,v 1.7 2000/10/15 18:18:28 marcus Exp $
***************************************************************************

   X server for LibGGI - Handling of graphics output

   Copyright (C) 1997	   Jason McMullan	[jmcc@cs.cmu.edu]
   Copyright (C) 1997	   Michael Krause	[rawstyle@ms.demo.org]
   Copyright (C) 1998-2000 Marcus Sundberg	[marcus@ggi-project.org]

   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
   THE AUTHOR(S) 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.

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

#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.. */

struct xggiGlobalInfo xggiInfo = {
	0,	/* Number of screens */
	1, 1,	/* Screen rows and columns */
	1,	/* Allow zapping ? */
	1,	/* Automaticly set GII_CTRLALT_VTSWITCH */
	0	/* Don't open new VT by default. */
};

struct xggiScreenInfo *xggiScreens;

static char *targetlist = NULL, *modelist = NULL;
static int defaultbpp = 0;
static int ggiinit = 0;


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;

	for (i = 0; i < xggiInfo.numscreens; i++) {
		if (xggiScreens[i].vis != NULL) {
			ggiClose(xggiScreens[i].vis);
			xggiScreens[i].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(
		   "XGGI adds the following arguments:\n"
		   "-targets targets   the target(s) we should open the screen(s) on\n"
		   "-modes modelist    the ggi mode(s) we should use\n"
		   "-bpp bitdepth      XFree86 compability option\n"
		   "-rows numrows      number of screen rows in multihead setups\n"
#if 0 /* Rows are enough to describe the layout */
		   "-columns           number of screen columns in multihead setups\n"
#endif
		   "-buttons nr-bttns  specify number of pointer buttons\n"
		   "-dontzap           disable <Crtl><Alt><BS> server abort sequence\n"
		   "-ggivtswitch       don't set GII_CTRLALT_VTSWITCH automaticly\n"
		   "-newvt             set GGI_NEWVT environment variable\n"
		   "-noxfreeemu        provided only for backward compability\n"
		   );
}

int ddxProcessArgument(int argc, char *argv[], int i)
{
	if (!strcmp(argv[i], "-targets")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-targets' requires an argument\n");
			return 1;
		}
		targetlist = safe_alloc(strlen(argv[i+1])+1);
		strcpy(targetlist, argv[i+1]);
		return 2;
	}
	if (!strcmp(argv[i], "-modes")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-modes' requires an argument\n");
			return 1;
		}
		modelist = safe_alloc(strlen(argv[i+1])+1);
		strcpy(modelist, argv[i+1]);
		return 2;
	}
	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;
	}
	if (!strcmp(argv[i], "-rows")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-rows' requires an argument\n");
			return 1;
		}
		xggiInfo.rows = atoi(argv[i+1]);
		return 2;
	}
#if 0
	if (!strcmp(argv[i], "-columns")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-columns' requires an argument\n");
			return 1;
		}
		xggiInfo.columns = atoi(argv[i+1]);
		return 2;
	}
#endif
	if (!strcmp(argv[i], "-buttons")) {
		if (i + 1 >= argc) {
			ErrorF("Option `-buttons' requires an argument\n");
			return 1;
		}
		xggiScreens[0].ptrbuttons = atoi(argv[i+1]);
		return 2;
	}
	if (!strcmp(argv[i], "-dontzap")) {
		xggiInfo.allowzap = 0;
		return 1;
	}
	if (!strcmp(argv[i], "-ggivtswitch")) {
		xggiInfo.ctrlalt_vtswitch = 0;
		return 1;
	}	
	if (!strcmp(argv[i], "-newvt")) {
		xggiInfo.newvt = 1;
		return 1;
	}	
	if (!strcmp(argv[i], "-noxfreeemu")) {
		return 1;
	}	

	return 0;
}

#ifdef DDXTIME /* from ServerOSDefines */
CARD32 GetTimeInMillis(void)
{
	struct timeval  tp;

	X_GETTIMEOFDAY(&tp);
	return TVAL2TIME(tp);
}
#endif

int xggiGetScreenIdx(ScreenPtr screen)
{
	int i;

	for (i = 0; i < xggiInfo.numscreens; i++) {
		if (xggiScreens[i].screen == screen) return i;
	}

	*((uint32*)0) = 43;
	FatalError("Tried to operate on invalid screen\n");

	/* Never reached */
	return -1;
}


static ColormapPtr InstalledMaps[MAXSCREENS];

static int xggiListInstalledColormaps(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 xggiInstallColormap(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 xggiUninstallColormap(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;
	}

	scr = xggiGetScreenIdx(pmap->pScreen);

	ggiGetPalette(xggiScreens[scr].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(xggiScreens[scr].vis, 0, MAX_COLORS, cmap);
}

static Bool xggiSaveScreen(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(int idx)
{
	ScreenPtr pScreen = xggiScreens[idx].screen;
	int bits_per_pixel = xggiScreens[idx].bitsPerPixel;
	PixmapPtr frontbuf;

	xggiScreens[idx].backbuffer
		= pScreen->CreatePixmap(pScreen, pScreen->width,
								pScreen->height, pScreen->rootDepth);
	if (!xggiScreens[idx].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,
			&xggiScreens[idx].backbuffer->drawable);

	frontbuf->devPrivate.ptr = xggiScreens[idx].backbuffer->devPrivate.ptr;
	xggiScreens[idx].ismapped = 0;
}

	
static void domap(int idx)
{
	ScreenPtr pScreen = xggiScreens[idx].screen;
	int bits_per_pixel = xggiScreens[idx].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 = xggiScreens[idx].pfbMemory;

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


void xggiUnmapDisplay(void)
{
	int i;

	for (i = 0; i < xggiInfo.numscreens; i++) {
		dounmap(i);
	}
}


void xggiMapDisplay(void)
{
	int i;

	for (i = 0; i < xggiInfo.numscreens; i++) {
		domap(i);
	}
}


#define XYtoI(x,y)	((y) * xggiInfo.columns + (x))
#define ItoXY(x,y,i) \
do { \
	(x) = (i) % xggiInfo.columns; \
	(y) = (i) / xggiInfo.columns; \
}while(0)

static Bool xggiCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
{
	int scr = xggiGetScreenIdx(*ppScreen);
	int maxscr = xggiInfo.numscreens-1;
	int newscr;
	int xs, ys;
	
	if (maxscr < 1) return FALSE;

	if (*x >= xggiScreens[scr].mode.visible.x) {
		ItoXY(xs, ys, scr);
		xs++;
		if (xs >= xggiInfo.columns) return FALSE;
		newscr = XYtoI(xs, ys);
		if (newscr > maxscr) return FALSE;

		*ppScreen = xggiScreens[newscr].screen;
		*x = 0;
		return TRUE;
	} else if (*x < 0) {
		ItoXY(xs, ys, scr);
		xs--;
		if (xs < 0) return FALSE;
		newscr = XYtoI(xs, ys);
		if (newscr < 0) return FALSE;

		*ppScreen = xggiScreens[newscr].screen;
		*x = xggiScreens[newscr].mode.visible.x - 1;
		return TRUE;
	} else if (*y >= xggiScreens[scr].mode.visible.y) {
		ItoXY(xs, ys, scr);
		ys++;
		if (ys >= xggiInfo.rows) return FALSE;
		newscr = XYtoI(xs, ys);
		if (newscr > maxscr) return FALSE;

		*ppScreen = xggiScreens[newscr].screen;
		*y = 0;
		return TRUE;
	} else if (*y < 0) {
		ItoXY(xs, ys, scr);
		ys--;
		if (ys < 0) return FALSE;
		newscr = XYtoI(xs, ys);
		if (newscr < 0) return FALSE;

		*ppScreen = xggiScreens[newscr].screen;
		*y = xggiScreens[newscr].mode.visible.y - 1;
		return TRUE;
	}

	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,
	       xggiScreens[scr].depth);
#endif

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

	pScreen->SaveScreen = xggiSaveScreen;

	switch (xggiScreens[scr].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 = xggiInstallColormap;
		pScreen->UninstallColormap = xggiUninstallColormap;
		pScreen->ListInstalledColormaps = xggiListInstalledColormaps;
		pScreen->StoreColors = xggiStoreColors;
		break;
	}

	miDCInitialize(pScreen, &xggiPointerCursorFuncs);

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

	return ret;
} 


static int count_targets(char *targets)
{
	char tgttemp[2048];
	int count = 0;

	while (1) {
		targets = ggParseTarget(targets, tgttemp, 2048);

		if (targets == NULL) break;

		count++;

		while (*targets && isspace((int)*targets)) targets++;
		if (*targets == '\0') {
			break;
		}

		if (*targets != ':') {
			FatalError("Missing ':' between targets: %c\n", *targets);
		}

		targets++;  /* skip ':' */
	}

	return count;
}	


static void reset_xggiscreen(int i)
{
	xggiScreens[i].vis		= NULL;
	xggiScreens[i].ptrbuttons	= DEFAULT_PTRBUTTONS;
	xggiScreens[i].width		= GGI_AUTO;
	xggiScreens[i].height		= GGI_AUTO;
	xggiScreens[i].depth		= 0;
	xggiScreens[i].stride		= 0;
	xggiScreens[i].bitsPerPixel	= 0;
	/* xggiScreens[i].mode		= */
	xggiScreens[i].dbuf		= NULL;
	xggiScreens[i].pfbMemory	= NULL;
	xggiScreens[i].screen		= NULL;
	xggiScreens[i].backbuffer	= NULL;
	xggiScreens[i].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;
	char *targets = targetlist;
	char *modes = modelist;

	if (targets != NULL) {
		xggiInfo.numscreens = count_targets(targets);
		if (xggiInfo.rows < 1) {
			xggiInfo.rows = 1;
			xggiInfo.columns = xggiInfo.numscreens;
		} else if (xggiInfo.rows > xggiInfo.numscreens) {
			xggiInfo.rows = xggiInfo.numscreens;
			xggiInfo.columns = 1;
		} else {
			xggiInfo.columns = xggiInfo.numscreens / xggiInfo.rows;
			if (xggiInfo.numscreens % xggiInfo.rows) {
				xggiInfo.columns++;
			}
		}
	} else {
		/* One screen on default LibGGI visual */
		xggiInfo.numscreens = xggiInfo.rows = xggiInfo.columns = 1;
	}

	xggiScreens = safe_alloc(sizeof(struct xggiScreenInfo)
							 * xggiInfo.numscreens);

	for (i = 0; i < xggiInfo.numscreens; i++) {
		reset_xggiscreen(i);
	}

	if (xggiInfo.ctrlalt_vtswitch) {
		putenv("GII_CTRLALT_VTSWITCH=1");
	}
	if (xggiInfo.newvt) {
		putenv("GGI_NEWVT=1");
	}

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

	for (i = 0; i < xggiInfo.numscreens; i++) {
		char tgttemp[2048];
		char *currtarget = NULL;
		int j;

		if (targets) {
			targets = ggParseTarget(targets, tgttemp, 2048);

			if (*tgttemp != '\0') {
				currtarget = tgttemp;
			}

			while (*targets && isspace((int)*targets)) targets++;
			targets++;  /* skip ':' */
		}

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

#if 0	/* Can we hook in ggiFlush() somewhere? */
		ggiSetFlags(xggiScreens[i].vis, GGIFLAG_ASYNC);
#endif

		if (modes != NULL &&
		    (modes = ggParseTarget(modes, tgttemp, 2048)) != NULL) {
			ggiParseMode(tgttemp, &xggiScreens[i].mode);
			
			while (*modes && isspace((int)*modes)) modes++;
			if (*modes == '\0') {
				modes = NULL;
				goto end_of_modeparsing;
			}
			if (*modes != ':') {
				FatalError("Missing ':' between modes\n");
			}

			modes++;  /* skip ':' */
		} else {
			ggiParseMode("", &xggiScreens[i].mode);
		}
  end_of_modeparsing:

		if (xggiScreens[i].mode.graphtype == GT_AUTO &&
		    defaultbpp != 0) {
			xggiScreens[i].mode.graphtype = bpp2gt(defaultbpp);
		}
		ggiCheckMode(xggiScreens[i].vis, &xggiScreens[i].mode);

		if (i == 0) {
			gt = xggiScreens[i].mode.graphtype;
		}

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

		xggiScreens[i].width  = xggiScreens[i].mode.visible.x;
		xggiScreens[i].height = xggiScreens[i].mode.visible.y;
		xggiScreens[i].depth
			= GT_DEPTH(xggiScreens[i].mode.graphtype);
		xggiScreens[i].bitsPerPixel
			= GT_SIZE(xggiScreens[i].mode.graphtype);

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

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

		xggiScreens[i].pfbMemory = dbuf->write;
		xggiScreens[i].stride = 8 * dbuf->buffer.plb.stride / size;
		xggiScreens[i].dbuf = dbuf;
	}
}


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

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

	screenInfo->formats[0].depth = 1;
	screenInfo->formats[0].bitsPerPixel = 1;
	screenInfo->formats[0].scanlinePad = BITMAP_SCANLINE_PAD;
	screenInfo->formats[1].depth = xggiScreens[0].depth;
	screenInfo->formats[1].bitsPerPixel = xggiScreens[0].bitsPerPixel;
	screenInfo->formats[1].scanlinePad = BITMAP_SCANLINE_PAD;

	screenInfo->numPixmapFormats = 2;

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

	/* Add screens */
	for (i = 0; i < xggiInfo.numscreens; i++) {
		if (AddScreen(xggiFBInitProc, argc, argv) == -1) {
			FatalError("Couldn't add screen");
		}
	}
}

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

