/**
 ** vd_xfdga.c -- DGA2 driver (interface to X11 DGA2)
 **
 ** Copyright (C) 2003 Dimitar Zhekov
 ** [e-mail: jimmy@is-vn.bg]
 **
 ** This file is part of the GRX graphics library.
 **
 ** The GRX graphics library is free software; you can redistribute it
 ** and/or modify it under some conditions; see the "copying.grx" file
 ** for details.
 **
 ** This library is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** Changes by Dimitar Zhekov (jimmy@is-vn.bg) Aug 27 2003
 **   - use Xlib for rendering unless defined XF86DGA_FRAMEBUFFER
 **
 **/

#include "libgrx.h"
#include "libxwin.h"
#include "grdriver.h"
#include "arith.h"
#include "memcopy.h"
#include "memfill.h"

int _XGrWindowedMode = 1;

static int setmode(GrVideoMode *mp,int noclear)
{
	int res;
	XDGADevice *dev;
	GRX_ENTER();
	res = FALSE;
	DBGPRINTF(DBG_DRIVER, ("attempting to set DGA mode %d\n", mp->mode));
#ifndef XF86DGA_FRAMEBUFFER
	if(_XGrPatternGC != None) XFreeGC(_XGrDisplay, _XGrPatternGC);
	if(_XGrPattern != None) XFreePixmap(_XGrDisplay, _XGrPattern);
	if(_XGrBitmapGC != None) XFreeGC(_XGrDisplay, _XGrBitmapGC);
	if(_XGrBitmap != None) XFreePixmap(_XGrDisplay, _XGrBitmap);
	if(_XGrGC != None) XFreeGC(_XGrDisplay, _XGrGC);
	_XGrGC = _XGrBitmapGC = _XGrPatternGC = None;
	_XGrBitmap = _XGrPattern = None;
#endif
	XUngrabPointer(_XGrDisplay, CurrentTime);
	XUngrabKeyboard(_XGrDisplay, CurrentTime);
	dev = XDGASetMode(_XGrDisplay, _XGrScreen, mp->mode);
	if(mp->mode != 0) {
	    if(dev == NULL) {
		DBGPRINTF(DBG_DRIVER, ("can't set DGA mode\n"));
		goto done;
	    }
#ifdef XF86DGA_FRAMEBUFFER
	    mp->extinfo->frame = dev->data;
#else
	    mp->extinfo->frame = (char *) dev->pixmap;
#endif
	    XDGASync(_XGrDisplay, _XGrScreen);
	    XDGASetViewport(_XGrDisplay, _XGrScreen, 0, 0, XDGAFlipRetrace);
	    XGrabKeyboard(_XGrDisplay, _XGrWindow, True, GrabModeAsync,
			  GrabModeAsync, CurrentTime);
	    XGrabPointer(_XGrDisplay, _XGrWindow, True, StructureNotifyMask |
			 ButtonPressMask | ButtonReleaseMask |
			 ButtonMotionMask | PointerMotionMask, GrabModeAsync,
			 GrabModeAsync, None, None, CurrentTime);
	    if(mp->bpp == 8) {
		if(_XGrColormap == None) {
		    /* one colormap for all modes, XFreeColormap() problems */
		    _XGrColormap = XDGACreateColormap(_XGrDisplay, _XGrScreen,
						      dev, AllocAll);
		}
		if(_XGrColormap != None) {
		    XDGAInstallColormap(_XGrDisplay, _XGrScreen, _XGrColormap);
		    _GR_lastFreeColor = 255;
		}
	    }
#ifdef XF86DGA_FRAMEBUFFER
	    if(!noclear) memzero(dev->data, mp->lineoffset * mp->height);
#else
	    _XGrDepth = _XGrBitsPerPixel = mp->bpp;
	    if(_XGrDepth == 32) _XGrDepth = 24;
	    _XGrForeColor = GrNOCOLOR;		/* force XSetForeground */
	    _XGrBackColor = GrNOCOLOR;		/* force XSetBackground */
	    _XGrColorOper = C_WRITE;
	    _XGrGC = XCreateGC(_XGrDisplay, dev->pixmap, 0L, NULL);
	    _XGrBitmap = XCreatePixmap(_XGrDisplay, dev->pixmap, 128, 128, 1);
	    _XGrBitmapGC = XCreateGC(_XGrDisplay, _XGrBitmap, 0L, NULL);
	    _XGrPattern = XCreatePixmap(_XGrDisplay, dev->pixmap, 8, 1, 1);
	    _XGrPatternGC = XCreateGC(_XGrDisplay, _XGrPattern, 0L, NULL);
	    if(!noclear) {
		XSetForeground(_XGrDisplay, _XGrGC, 0);
		XSetFunction(_XGrDisplay, _XGrGC, GXcopy);
		XFillRectangle(_XGrDisplay, dev->pixmap, _XGrGC, 0, 0,
			       mp->width, mp->height);
	    }
#endif
	}
	res = TRUE;
done:	if(dev != NULL) XFree(dev);
	GRX_RETURN(res);
}

static void reset(void)
{
	XDGADevice *dev;
	GRX_ENTER();
	if(_XGrDisplay != NULL) {
	    XUngrabPointer(_XGrDisplay, CurrentTime);
	    XUngrabKeyboard(_XGrDisplay, CurrentTime);
#ifdef XF86DGA_FRAMEBUFFER
	    XDGACloseFramebuffer(_XGrDisplay, _XGrScreen);
#endif
	    dev = XDGASetMode(_XGrDisplay, _XGrScreen, 0);
	    if(dev != NULL) XFree(dev);
	    XCloseDisplay(_XGrDisplay);
	}
	_XGrDisplay = NULL;
	_XGrColormap = None;
	_GR_firstFreeColor =  0;
	_GR_lastFreeColor  = -1;
	GRX_LEAVE();
}

static int detect(void)
{
	int res;
	int event, error;
	int major, minor;
	GRX_ENTER();
	res = FALSE;
#ifdef XF86DGA_FRAMEBUFFER
	if(geteuid()) {
	    DBGPRINTF(DBG_DRIVER, ("root priviledges required\n"));
	    goto done;
	}
#endif
	if((_XGrDisplay = XOpenDisplay("")) == NULL) {
	    DBGPRINTF(DBG_DRIVER, ("can't connect to X server\n"));
	    goto done;
	}
	if(!XDGAQueryExtension(_XGrDisplay, &event, &error)) {
	    DBGPRINTF(DBG_DRIVER, ("can't query DGA extension\n"));
	    goto done;
	}
	if(!XDGAQueryVersion(_XGrDisplay, &major, &minor)) {
	    DBGPRINTF(DBG_DRIVER, ("can't query DGA version\n"));
	    goto done;
	}
	if(major < 2) {
	    DBGPRINTF(DBG_DRIVER, ("required DGA version 2.0, detected %d.%d\n",
				   major, minor));
	    goto done;
	}
	res = TRUE;
done:	if(!res) reset();
	GRX_RETURN(res);
}

#ifndef XF86DGA_FRAMEBUFFER
static void setbank(int bk)
{}

static void setrwbanks(int rb,int wb)
{}
#endif

static void loadcolor(int c,int r,int g,int b)
{
	XColor xcolor;

	if(_XGrColormap != None) {
	    xcolor.pixel = c;
	    xcolor.red   = r * 257;
	    xcolor.green = g * 257;
	    xcolor.blue  = b * 257;
	    xcolor.flags = DoRed | DoGreen | DoBlue;
	    XStoreColor(_XGrDisplay, _XGrColormap, &xcolor);
	}
}

#ifdef XF86DGA_FRAMEBUFFER
#define GRFRAMEDRIVER(bpp) GR_frameSVGA##bpp##_LFB
#else
#define GRFRAMEDRIVER(bpp) GR_frameXWIN##bpp
#endif

static int build_video_mode(XDGAMode *ip, GrVideoMode *mp, GrVideoModeExt *ep)
{
	mp->present    = TRUE;
	mp->bpp        = ip->depth;
	mp->width      = ip->viewportWidth;
	mp->height     = ip->viewportHeight;
	mp->mode       = ip->num;
	mp->lineoffset = ip->bytesPerScanline;
	mp->privdata   = 0;
	mp->extinfo    = NULL;

	ep->drv        = NULL;
	ep->frame      = NULL;
#ifdef XF86DGA_FRAMEBUFFER
	ep->flags      = GR_VMODEF_LINEAR;
#else
	ep->flags      = 0;
#endif
	ep->setup      = setmode;
	ep->setvsize   = NULL;
	ep->scroll     = NULL;
#ifdef XF86DGA_FRAMEBUFFER
	ep->setbank    = NULL;
	ep->setrwbanks = NULL;
#else
	ep->setbank    = setbank;
	ep->setrwbanks = setrwbanks;
#endif
	ep->loadcolor  = NULL;

	if(ip->visualClass != (mp->bpp == 8 ? PseudoColor : TrueColor)) {
	    DBGPRINTF(DBG_DRIVER, ("%d: visual class %s depth %d unsupported\n",
				   ip->num, _XGrClassNames[ip->visualClass],
				   mp->bpp));
	    return(FALSE);
	}

#ifndef XF86DGA_FRAMEBUFFER
	if(!(ip->flags & XDGAPixmap)) {
	    DBGPRINTF(DBG_DRIVER, ("%d: rendering with XLib impossible\n",
				   ip->num));
	    return(FALSE);
	}
#endif

	switch(mp->bpp) {
	    case 8 :
		ep->mode = GRFRAMEDRIVER(8);
		ep->cprec[0] =
		ep->cprec[1] =
		ep->cprec[2] = 6;
		ep->cpos[0]  =
		ep->cpos[1]  =
		ep->cpos[2]  = 0;
		ep->loadcolor = loadcolor;
		break;
	    case 15 :
		ep->mode = GRFRAMEDRIVER(16);
		ep->cprec[0] =
		ep->cprec[1] =
		ep->cprec[2] = 5;
		ep->cpos[0]  = 10;
		ep->cpos[1]  = 5;
		ep->cpos[2]  = 0;
		break;
	    case 16 :
		ep->mode = GRFRAMEDRIVER(16);
		ep->cprec[0] = 5;
		ep->cprec[1] = 6;
		ep->cprec[2] = 5;
		ep->cpos[0]  = 11;
		ep->cpos[1]  = 5;
		ep->cpos[2]  = 0;
		break;
	    case 24 :
		ep->cprec[0]  =
		ep->cprec[1]  =
		ep->cprec[2]  = 8;
		ep->cpos[0]   = 16;
		ep->cpos[1]   = 8;
		ep->cpos[2]   = 0;
		ep->mode = GRFRAMEDRIVER(24);
		if(ip->bitsPerPixel == 32) {
		    mp->bpp = 32;
		    ep->mode = GRFRAMEDRIVER(32L);
		    if(ip->redMask == 0xFF000000) {
			ep->cpos[0]   = 24;
			ep->cpos[1]   = 16;
			ep->cpos[2]   = 8;
			ep->mode = GRFRAMEDRIVER(32H);
		    }
		}
		break;
	    default:
		return(FALSE);
	}

	return(TRUE);
}

GrVideoModeExt grtextextdga = {
    GR_frameText,                       /* frame driver */
    NULL,                               /* frame driver override */
    NULL,                               /* frame buffer address */
    { 0, 0, 0 },                        /* color precisions */
    { 0, 0, 0 },                        /* color component bit positions */
    0,                                  /* mode flag bits */
    setmode,                            /* mode set */
    NULL,                               /* virtual size set */
    NULL,                               /* virtual scroll */
    NULL,                               /* bank set function */
    NULL,                               /* double bank set function */
    NULL                                /* color loader */
};

#define  NUM_MODES    200               /* max # of supported modes */
#define  NUM_EXTS     10                /* max # of mode extensions */

static GrVideoModeExt exts[NUM_EXTS];
static GrVideoMode   modes[NUM_MODES] = {
    /* pres.  bpp wdt   hgt   BIOS   scan  priv. &ext  */
    {  TRUE,  8,   80,   25,  0x00,    80, 1,    &grtextextdga  },
    {  0  }
};

/* from svgalib.c, unmodified */
static void add_video_mode(
    GrVideoMode *mp,  GrVideoModeExt *ep,
    GrVideoMode **mpp,GrVideoModeExt **epp
) {
	if(*mpp < &modes[NUM_MODES]) {
	    if(!mp->extinfo) {
		GrVideoModeExt *etp = &exts[0];
		while(etp < *epp) {
		    if(memcmp(etp,ep,sizeof(GrVideoModeExt)) == 0) {
			mp->extinfo = etp;
			break;
		    }
		    etp++;
		}
		if(!mp->extinfo) {
		    if(etp >= &exts[NUM_EXTS]) return;
		    sttcopy(etp,ep);
		    mp->extinfo = etp;
		    *epp = ++etp;
		}
	    }
	    sttcopy(*mpp,mp);
	    (*mpp)++;
	}
}

static int init(char *options)
{
	int res;
	XDGAMode *modev = NULL;
	int modec, mindex;
	GrVideoMode mode, *modep = &modes[1];
	GrVideoModeExt ext, *extp = &exts[0];
	GRX_ENTER();
	res = FALSE;
	if(detect()) {
	    _XGrScreen = DefaultScreen(_XGrDisplay);
	    _XGrWindow = DefaultRootWindow(_XGrDisplay);
#ifdef XF86DGA_FRAMEBUFFER
	    if(!XDGAOpenFramebuffer(_XGrDisplay, _XGrScreen)) {
		DBGPRINTF(DBG_DRIVER, ("can't open framebuffer\n"));
		goto done;
	    }
#endif
	    modev = XDGAQueryModes(_XGrDisplay, _XGrScreen, &modec);
	    if(modev == NULL) {
		DBGPRINTF(DBG_DRIVER, ("can't query DGA modes"));
		goto done;
	    }
	    memzero(modep,(sizeof(modes) - sizeof(modes[0])));
	    for(mindex = 0; mindex < modec; mindex++) {
		if(!build_video_mode(&modev[mindex], &mode, &ext)) continue;
		add_video_mode(&mode,&ext,&modep,&extp);
	    }
	    _XGrWindowedMode = 0;
	    _XGrColormap = None;
#ifndef XF86DGA_FRAMEBUFFER
	    _XGrColorNumPixels = 0;
#endif
	    res = TRUE;
	}
done:	if(modev != NULL) XFree(modev);
	if(!res) reset();
	GRX_RETURN(res);
}

GrVideoDriver _GrVideoDriverXF86DGA = {
    "xf86dga",                          /* name */
    GR_XWIN,                            /* adapter type */
    NULL,                               /* inherit modes from this driver */
    modes,                              /* mode table */
    itemsof(modes),                     /* # of modes */
    detect,                             /* detection routine */
    init,                               /* initialization routine */
    reset,                              /* reset routine */
    _gr_selectmode,                     /* standard mode select routine */
    0                                   /* no additional capabilities */
};

