/*--------------------------------------------------------------------------
 * File: vesa.c
 * Written by: Alexander Boczar, 1997-04-11
 * Description: A VESA driver for any 8 bit resolution.
 *
 * Updates:
 * -- Date -- | ----- Name ----- |-- Did what....
 * 1997-05-27 | Alexander Boczar | Added a pragma pack(1) to be sure to have matching vesa structures
 * 1997-08-16 | Fredrik Kling    | Added xcopybuff (eXtended buffer copy...)
 *
 -------------------------------------------------------------------------------*/

#include <conio.h>
#include <i86.h>
#include <dos.h>
#include <float.h>
#include "system/dpmi.h"
#include "system/xstdio.h"
#include "system/xstring.h"
#include "drivers/drv8.h"
#include "draw8/draw8.h"

#pragma pack( push, 1)

/* ######################################################################### */

typedef struct s_VESAINFO VESAINFO;
typedef struct s_MODEINFO MODEINFO;
typedef struct s_GFXMODE GFXMODE;
typedef enum
{
	ERR_NOERR,
	ERR_NODOSMEM,
	ERR_NOMEM,
	ERR_VESAERR,
	ERR_NOVESA,
	ERR_INVMODE,
	ERR_MAPFAIL,
	ERR_NOMOUSE
} ERRORS;

struct s_VESAINFO
{
  BYTE vbesignature[4];		// VBE Signature
	union
	{
		WORD vbeversion;				// VBE Version
		struct
		{
			BYTE vbeversion_low;
			BYTE vbeversion_high;
		};
	};
  BYTE *oemstringptr;			// Pointer to OEM String
	union
	{
		DWORD capabilities;			// Capabilities of graphics cont.
		struct
		{
			DWORD capabilities_notfixwidth:1;
			DWORD capabilities_notvgacomp:1;
			DWORD capabilities_specialramdac:1;
			DWORD capabilities_1:29;
		};
	};
  WORD *videomodeptr;			// Pointer to Video Mode List
  WORD totalmemory;				// Number of 64kb memory blocks

	// Added for VBE 2.0

	union
	{
		WORD oemsoftwarerev;				// VBE implementation Software revision
		struct
		{
			BYTE oemsoftwarerev_low;
			BYTE oemsoftwarerev_high;
		};
	};
  BYTE *oemvendornameptr;	// Pointer to Vendor Name String
  BYTE *oemproductnameptr;// Pointer to Product Name String
  BYTE *oemproductrevptr;	// Pointer to Product Revision String
  BYTE reserved[222];			// Reserved for VBE implementation, scratch area
  BYTE oemdata[256];			// Data Area for OEM Strings
};

struct s_MODEINFO
{
	// Mandatory information for all VBE revisions
	union
	{
		WORD modeattributes;		// mode attributes
		struct
		{
			WORD modeattributes_hdwsup:1;
			WORD modeattributes_1:1;
			WORD modeattributes_biossup:1;
			WORD modeattributes_color:1;
			WORD modeattributes_gfx:1;
			WORD modeattributes_novgacomp:1;
			WORD modeattributes_novgacompmem:1;
			WORD modeattributes_linear:1;
			WORD modeattributes_2:8;
		};
	};
	union
	{
		BYTE winaattributes;		// window A attributes
		struct
		{
			BYTE winaattributes_relocatable:1;
			BYTE winaattributes_readable:1;
			BYTE winaattributes_writeable:1;
			BYTE winaattributes_1:5;
		};
	};
	union
	{
		BYTE winbattributes;		// window B attributes
		struct
		{
			BYTE winbattributes_relocatable:1;
			BYTE winbattributes_readable:1;
			BYTE winbattributes_writeable:1;
			BYTE winbattributes_1:5;
		};
	};
  WORD wingranularity;		//	window granularity
  WORD winsize;						//	window size
  WORD winasegment;				//	window A start segment
  WORD winbsegment;				//	window B start segment
  void *winfuncptr;				//	pointer to window function
  WORD bytesperscanline;	//	bytes per scan line

  // Mandatory information for VBE 1.2 and above
  WORD xresolution;				//	horizontal resolution in pixels or chars
  WORD yresolution;				//	vertical resolution in pixels or chars
  BYTE xcharsize;					//	character cell width in pixels
  BYTE ycharsize;					//	character cell height in pixels
  BYTE numberofplanes;		//	number of memory planes
  BYTE bitsperpixel;			//	bits per pixel
  BYTE numberofbanks;			//	number of banks
  BYTE memorymodel;				//	memory model type
  BYTE banksize;					//	bank size in KB
  BYTE numberofimagepages;//	number of images
  BYTE reserved;					//	reserved for page function

  // Direct Color fields (required for direct/6 and YUV/7 memory models)
  BYTE redmasksize;				//	size of direct color red mask in bits
  BYTE redfieldposition;	//	bit position of lsb of red mask
  BYTE greenmasksize;			//	size of direct color green mask in bits
  BYTE greenfieldposition;//	bit position of lsb of green mask
  BYTE bluemasksize;			//	size of direct color blue mask in bits
  BYTE bluefieldposition;	//	bit position of lsb of blue mask
  BYTE rsvdmasksize;			//	size of direct color reserved mask in bits
  BYTE rsvdfieldposition;	//	bit position of lsb of reserved mask
  BYTE directcolormodeinfo;//	direct color mode attributes

  // Mandatory information for VBE 2.0 and above
  BYTE *physbaseptr;			//	physical address for flat frame buffer
  BYTE *offscreenmemoffset;//	pointer to start of off screen memory
  WORD offscreenmemsize;	//	amount of off screen memory in 1k units
  BYTE _reserved[206]; 		//	remainder of modeinfoblock
};

struct s_GFXMODE
{
	int width,height;
	DWORD mode;
};

#pragma pack( pop)

extern DRV vesaDRV;

/* ######################################################################### */

static ERRORS error = ERR_NOERR;

static int mouseactive = FALSE;

static DWORD modes = 0;
static GFXMODE modelist[256];

static BYTE *videoptr;
static DWORD bytesperline;

static void *segofs2ptr( void *segofs)
{
	return( (void *)((((DWORD)segofs >> 12) & 0xffff0) + ((DWORD)segofs & 0xffff)));
}

/* ######################################################################### */

static int setup()
{
	RMREGS regs;
	VESAINFO far *vesainfo;
	MODEINFO far *modeinfo;
	WORD vesainfo_sel,vesainfo_seg;
	WORD modeinfo_sel,modeinfo_seg;
	WORD *modeptr;
	int i;


	if( !(_dpmi_dosalloc( sizeof( VESAINFO), &vesainfo_sel, &vesainfo_seg)) )
	{
		error = ERR_NODOSMEM;
		return( FALSE);
	}

	vesainfo = MK_FP( vesainfo_sel, 0);
	vesainfo->vbesignature[0] = 'V';
	vesainfo->vbesignature[1] = 'B';
	vesainfo->vbesignature[2] = 'E';
	vesainfo->vbesignature[3] = '2';


	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f00;
	regs.es = vesainfo_seg;
	regs.di = 0;
	_dpmi_realmodeint( 0x10, &regs);

	if( regs.ax != 0x004f)
	{
		_dpmi_dosfree( vesainfo_sel);
		error = ERR_NOVESA;
		return( FALSE);
	}

	if( !(_dpmi_dosalloc( sizeof( MODEINFO), &modeinfo_sel, &modeinfo_seg)) )
	{
		_dpmi_dosfree( vesainfo_sel);
		error = ERR_NODOSMEM;
		return( FALSE);
	}
	modeinfo = MK_FP( modeinfo_sel, 0);

	modes = 0;
	modeptr = (WORD *)segofs2ptr( vesainfo->videomodeptr);
	while( *modeptr != 0xffff)
	{
		memset( &regs, 0, sizeof( RMREGS));
		regs.ax = 0x4f01;
		regs.cx = *modeptr;
		regs.es = modeinfo_seg;
		regs.di = 0;
		_dpmi_realmodeint( 0x10, &regs);

		if( regs.ax != 0x004f)
		{
			_dpmi_dosfree( modeinfo_sel);
			_dpmi_dosfree( vesainfo_sel);
			error = ERR_VESAERR;
			return( FALSE);
		}

		/*
		 *
		 * 0x04 - packed pixel mode!!
		 *
		 * check if 8 bps...
		 *
		 */
		if( modeinfo->memorymodel == 0x04)
		{
			modelist[modes].width = modeinfo->xresolution;
			modelist[modes].height = modeinfo->yresolution;
			modelist[modes].mode = *modeptr;
			modes++;
		}
		modeptr++;
	}

	_dpmi_dosfree( modeinfo_sel);
	_dpmi_dosfree( vesainfo_sel);


	vesaDRV.modes = modes;
	vesaDRV.modelist = (DRVMODE *)xmalloc( modes * sizeof( DRVMODE));
	for( i=0; i<modes; i++)
		vesaDRV.modelist[i].width = modelist[i].width,
		vesaDRV.modelist[i].height = modelist[i].height;

	return( TRUE);
}


static int init( int width, int height, int config)
{
	RMREGS regs;
	MODEINFO far *modeinfo;
	WORD modeinfo_sel,modeinfo_seg;
	DWORD mode = 0;
	int i;


	if( modes == 0)
		if(	!setup())
			return( FALSE);

	vesaDRV.palette = (RGB *)xmalloc( 256 * sizeof( RGB));

	for( i=0; i<modes; i++)
	{
		if( (modelist[i].width == width) && (modelist[i].height == height))
		{
			mode = modelist[i].mode;
			break;
		}
	}

	if( mode == 0)
	{
		error = ERR_INVMODE;
		return(FALSE);
	}

	if( !(_dpmi_dosalloc( sizeof( MODEINFO), &modeinfo_sel, &modeinfo_seg)) )
	{
		error = ERR_NODOSMEM;
		return( FALSE);
	}
	modeinfo = MK_FP( modeinfo_sel, 0);

	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f01;
	regs.cx = mode | 0x4000;
	regs.es = modeinfo_seg;
	regs.di = 0;
	_dpmi_realmodeint( 0x10, &regs);

	if( regs.ax != 0x004f)
	{
		_dpmi_dosfree( modeinfo_sel);
		error = ERR_VESAERR;
		return( FALSE);
	}

	videoptr = modeinfo->physbaseptr;
	bytesperline = modeinfo->bytesperscanline;
	if ( (videoptr = _dpmi_mapmem( videoptr, bytesperline * height)) == NULL)
	{
		_dpmi_dosfree( modeinfo_sel);
		error = ERR_MAPFAIL;
		return( FALSE);
	}

	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f02;
	regs.bx = mode | 0x4000;
	_dpmi_realmodeint( 0x10, &regs);

	vesaDRV.width = modeinfo->xresolution;
	vesaDRV.height = modeinfo->yresolution;
	vesaDRV.minx = vesaDRV.miny = 0;
	vesaDRV.maxx = vesaDRV.width - 1;
	vesaDRV.maxy = vesaDRV.height - 1;
	vesaDRV.config = config;

	_dpmi_dosfree( modeinfo_sel);

	return( TRUE);
}

static int reinit(int width, int height)
{
	RMREGS regs;
	MODEINFO far *modeinfo;
	WORD modeinfo_sel,modeinfo_seg;
	DWORD mode = 0;
	int i;

	for( i=0; i<modes; i++)
	{
		if( (modelist[i].width == width) && (modelist[i].height == height))
		{
			mode = modelist[i].mode;
			break;
		}
	}

	if( mode == 0)
	{
		error = ERR_INVMODE;
		return(FALSE);
	}

	if( !(_dpmi_dosalloc( sizeof( MODEINFO), &modeinfo_sel, &modeinfo_seg)) )
	{
		error = ERR_NODOSMEM;
		return( FALSE);
	}
	modeinfo = MK_FP( modeinfo_sel, 0);

	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f01;
	regs.cx = mode | 0x4000;
	regs.es = modeinfo_seg;
	regs.di = 0;
	_dpmi_realmodeint( 0x10, &regs);

	if( regs.ax != 0x004f)
	{
		_dpmi_dosfree( modeinfo_sel);
		error = ERR_VESAERR;
		return( FALSE);
	}

	videoptr = modeinfo->physbaseptr;
	bytesperline = modeinfo->bytesperscanline;
	if ( (videoptr = _dpmi_mapmem( videoptr, bytesperline * height)) == NULL)
	{
		_dpmi_dosfree( modeinfo_sel);
		error = ERR_MAPFAIL;
		return( FALSE);
	}

	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f02;
	regs.bx = mode | 0x4000;
	_dpmi_realmodeint( 0x10, &regs);

	vesaDRV.width = modeinfo->xresolution;
	vesaDRV.height = modeinfo->yresolution;
	vesaDRV.minx = vesaDRV.miny = 0;
	vesaDRV.maxx = vesaDRV.width - 1;
	vesaDRV.maxy = vesaDRV.height - 1;

	_dpmi_dosfree( modeinfo_sel);

	mouseinit( 0, 0, vesaDRV.width - 1, vesaDRV.height - 1);
	setpalette( NULL);

	return( TRUE);
}

static void exit()
{
	union REGS r;

	r.h.ah = 0x0;
	r.h.al = 0x03;
	int386( 0x10, &r, &r);
}

static void setpalette( RGB *newpal)
{
	RMREGS regs;
	BGRA far *tmppal;
	WORD tmppal_sel,tmppal_seg;
  int i;

	if( newpal != NULL)
		memcpy( vesaDRV.palette, newpal, sizeof(RGB) * 256);

	_dpmi_dosalloc( 256 * sizeof( BGRA), &tmppal_sel, &tmppal_seg);
	tmppal = MK_FP( tmppal_sel, 0);

	for( i=0; i<256; i++)
	{
		tmppal[i].r = vesaDRV.palette[i].r >> 2;
		tmppal[i].g = vesaDRV.palette[i].g >> 2;
		tmppal[i].b = vesaDRV.palette[i].b >> 2;
		tmppal[i].a = 0;
	}

	memset( &regs, 0, sizeof( RMREGS));
	regs.ax = 0x4f09;
	regs.bx = 0x0000;
	regs.cx = 256;
	regs.dx = 0;
	regs.es = tmppal_seg;
	regs.di = 0;
	_dpmi_realmodeint( 0x10, &regs);

	_dpmi_dosfree( tmppal_sel);

}

static void vsync()
{
/*
	while ( !(inp( 0x3da) & 0x08));
	while ( (inp( 0x3da) & 0x08));
*/
}

static void clearscreen( BYTE col)
{
	int y;
	BYTE *screen;

	if( vesaDRV.width == bytesperline)
    memset4( videoptr, (col | (col << 8) | (col << 16) | (col << 24)), (vesaDRV.width * vesaDRV.height) >> 2);
	else
	{
		screen = videoptr;
		for( y=0; y<vesaDRV.height; y++)
		{
      memset4( screen, (col | (col << 8) | (col << 16) | (col << 24)), vesaDRV.width);
			screen += bytesperline;
		}
	}
}

/* ######################################################################### */

static BUFF *createbuff( int width, int height)
{
	BUFF *buff, *oldactive;
	int i;

	if( (buff = (BUFF *)xmalloc( sizeof(BUFF))) == NULL)
	{
		error = ERR_NOMEM;
		return( NULL);
	}

	buff->drv = &vesaDRV;

	if( (buff->image = (BYTE *)xmalloc( sizeof( BYTE) * width * height)) == NULL)
	{
		xfree( buff);
		error = ERR_NOMEM;
		return( NULL);
	}

	if( (buff->ytab = (int *)xmalloc( sizeof( int) * height)) == NULL)
	{
		xfree( buff->image);
		xfree( buff);
		error = ERR_NOMEM;
		return( NULL);
	}

	if( vesaDRV.config & DRVCFG_ZBUFFER)
	{
    if( (buff->zbuffer = (int *)xmalloc( sizeof( int) * width * height)) == NULL)
		{
			xfree( buff->ytab);
			xfree( buff->image);
			xfree( buff);
			error = ERR_NOMEM;
			return( NULL);
		}
	}

	for (i=0; i<height; i++)
		buff->ytab[i] = i * width;

	buff->width = width;
	buff->height = height;

	buff->xorigo = width/2;
	buff->yorigo = height/2;

	buff->minx = 0;
	buff->maxx = width - 1;
	buff->miny = 0;
	buff->maxy = height - 1;

	buff->lighttab = NULL;
	buff->transtab = NULL;

	oldactive = vesaDRV.activebuff;
	vesaDRV.activebuff = buff;
	clearbuff();
	vesaDRV.activebuff = oldactive;

	return( buff);
}

static void destroybuff( BUFF *buff)
{
	if( buff != NULL)
	{
		if( buff->zbuffer != NULL) xfree( buff->zbuffer);
		if( buff->ytab != NULL) xfree( buff->ytab);
		if( buff->image != NULL) xfree( buff->image);
		xfree( buff);
	}
}

static void setbuff( BUFF *buff)
{
	vesaDRV.activebuff = buff;
}

static void xcopybuff (int x1, int y1, int x2, int y2)
{
	int sxs,ixs,xlen;
	int sys,iys,ylen;
	register int i,j;
	BYTE *image;
	BYTE *screen;


	// Check boundaries...

	if ( x1 <= vesaDRV.minx)  x1 = vesaDRV.minx;
	if ( x2 >= vesaDRV.maxx) x2 = vesaDRV.maxx;

	sxs = x1;
	ixs = x1;
	xlen = x2 - x1;

	if ( y1 <= vesaDRV.miny)	y1 = vesaDRV.miny;
	if ( y2 >= vesaDRV.maxy)  y2 = vesaDRV.maxy;

	sys = y1;
	iys = y1;
	ylen = y2 - y1;


	for ( j=0; j<ylen; j++)
	{
		image = &vesaDRV.activebuff->image[ ixs + vesaDRV.activebuff->ytab[j + iys]];
		screen = &videoptr[ sxs + bytesperline * (j + sys)];
		for ( i=0; i<xlen; i++)
			*(screen++) = *(image++);
	}
}
static void xmovebuff (int x1, int y1, int x2, int y2,int col)
{
	int sxs,ixs,xlen;
	int sys,iys,ylen;
	register int i,j;
	BYTE *image;
	BYTE *screen;


	// Check boundaries...

	if ( x1 <= vesaDRV.minx)  x1 = vesaDRV.minx;
	if ( x2 >= vesaDRV.maxx) x2 = vesaDRV.maxx;

	sxs = x1;
	ixs = x1;
	xlen = x2 - x1;

	if ( y1 <= vesaDRV.miny)	y1 = vesaDRV.miny;
	if ( y2 >= vesaDRV.maxy)  y2 = vesaDRV.maxy;

	sys = y1;
	iys = y1;
	ylen = y2 - y1;


	for ( j=0; j<ylen; j++)
	{
		image = &vesaDRV.activebuff->image[ ixs + vesaDRV.activebuff->ytab[j + iys]];
		screen = &videoptr[ sxs + bytesperline * (j + sys)];
		for ( i=0; i<xlen; i++)
		{
			screen[i] = image[i];
			image[i]=0;
		}
	}
}
static void copybuff( int x, int y)
{
	int i,j;

	if( (x == y == 0) && (vesaDRV.activebuff->width == vesaDRV.width) && (vesaDRV.activebuff->height == vesaDRV.height))
	{
		BYTE *image = vesaDRV.activebuff->image;
		BYTE *screen = videoptr;

		for ( j=0; j<vesaDRV.height; j++)
		{
			if( (vesaDRV.width & 0x0f) == 0)
			{
				memcpy16( screen, image, vesaDRV.width >> 4);
				image += vesaDRV.width;
				screen += bytesperline;
			}
			else if( (vesaDRV.width & 0x03) == 0)
			{
				memcpy4( screen, image, vesaDRV.width >> 2);
				image += vesaDRV.width;
				screen += bytesperline;
			}
			else
			{
				memcpy4( screen, image, vesaDRV.width >> 2);
				image += vesaDRV.width ^ 0x03;
				screen += vesaDRV.width ^ 0x03;

				for ( i=0; i<(vesaDRV.width & 0x03); i++)
					*(screen++) = *(image++);
				image += vesaDRV.width & 0x03;
				screen += vesaDRV.width & 0x03 + (bytesperline - vesaDRV.width);
			}
		}
	}
	else
	{
		int sxs,ixs,xlen;
		int sys,iys,ylen;
		register int i,j;
		BYTE *image;
		BYTE *screen;

		// Check X

		if ( x >= vesaDRV.minx )
		{
			sxs = x;
			ixs = 0;
			if ( (x + vesaDRV.activebuff->width) <= vesaDRV.maxx)
				xlen = vesaDRV.activebuff->width;
			else
				xlen = vesaDRV.maxx - x;
		}
		else
		{
			sxs = 0;
			ixs = vesaDRV.minx - x;
			if ( (x + vesaDRV.activebuff->width) <= vesaDRV.maxx)
				xlen = (x + vesaDRV.activebuff->width) - vesaDRV.minx;
			else
				xlen = vesaDRV.maxx - vesaDRV.minx;
		}

		// Check Y

		if ( y >= vesaDRV.miny )
		{
			sys = y;
			iys = 0;
			if ( (y + vesaDRV.activebuff->height) <= vesaDRV.maxy)
				ylen = vesaDRV.activebuff->height;
			else
				ylen = vesaDRV.maxy - y;
		}
		else
		{
			sys = 0;
			iys = vesaDRV.miny - y;
			if ( (y + vesaDRV.activebuff->height) <= vesaDRV.maxy)
				ylen = (y + vesaDRV.activebuff->height) - vesaDRV.miny;
			else
				ylen = vesaDRV.maxy - vesaDRV.miny;
		}

		for ( j=0; j<ylen; j++)
		{
			image = &vesaDRV.activebuff->image[ ixs + vesaDRV.activebuff->ytab[j + iys]];
			screen = &videoptr[ sxs + bytesperline * (j + sys)];
			for ( i=0; i<xlen; i++)
				*(screen++) = *(image++);
		}
	}
}

static void clearbuff( BYTE col)
{
  memset4( vesaDRV.activebuff->image, (col | (col << 8) | (col << 16) | (col << 24)), (vesaDRV.activebuff->width * vesaDRV.activebuff->height) >> 2);
	if ( vesaDRV.config & DRVCFG_ZBUFFER)
    memset4( vesaDRV.activebuff->zbuffer, 0x7fffffff, (vesaDRV.activebuff->width * vesaDRV.activebuff->height));
}

static void movebuff( int x, int y, BYTE col)
{
  copybuff( x, y);
  clearbuff( col);
}

/* ######################################################################### */

static void _plot( XYZ p, BYTE col)
{
  plot( vesaDRV.activebuff, p, col);
}

static void _line( XYZ p1, XYZ p2, BYTE col)
{
  line( vesaDRV.activebuff, p1, p2, col);
}

static void _poly( XYZ p1, XYZ p2, XYZ p3, char col )
{
  poly( vesaDRV.activebuff, p1, p2, p3, col);
}

static void _tpoly( XYZ p1, XYZ p2, XYZ p3, BYTE *data )
{
  tpoly( vesaDRV.activebuff, p1, p2, p3, data );
}

static void _blit( XYZ p, BYTE *image, int *zimage, int width, int height)
{
  blit( vesaDRV.activebuff, p, image, zimage, width, height);
}

/* ######################################################################### */

static int mouseinit( int minx, int miny, int maxx, int maxy)
{
	union REGS r;

	if( mouseactive)
	{
		r.w.ax = 0x0007;
		r.w.cx = minx;
		r.w.dx = maxx;
		int386( 0x33, &r, &r);

		r.w.ax = 0x0008;
		r.w.cx = miny;
		r.w.dx = maxy;
		int386( 0x33, &r, &r);
		return( TRUE);
	}

	r.w.ax = 0x0;
	int386( 0x33, &r, &r);
	if ( r.w.ax == 0x0ffff)
	{
		r.w.ax = 0x0007;
		r.w.cx = minx;
		r.w.dx = maxx;
		int386( 0x33, &r, &r);

		r.w.ax = 0x0008;
		r.w.cx = miny;
		r.w.dx = maxy;
		int386( 0x33, &r, &r);

		r.w.ax = 0x0004;
		r.w.cx = minx + ((maxx - minx) >> 1);
		r.w.dx = miny + ((maxy - miny) >> 1);
		int386( 0x33, &r, &r);

		mouseactive = TRUE;
		return( TRUE);
	}
	else
	{
		mouseactive = FALSE;
		error = ERR_NOMOUSE;
		return( FALSE);
	}
}

static int mousecallback( void ( *callback)( DRVMOUSE change))
{
	return( TRUE);
}

static void getmousepos( int *x, int *y)
{
	union REGS r;

	if( mouseactive)
	{
		r.w.ax = 0x0003;
		int386( 0x33, &r, &r);
    *x = (signed short)r.w.cx;
    *y = (signed short)r.w.dx;
	}
}

static void setmousepos( int x, int y)
{
	union REGS r;

	if( mouseactive)
	{
		r.w.ax = 0x0004;
		r.w.cx = x;
		r.w.dx = y;
		int386( 0x33, &r, &r);
	}
}

static void getmousediff( int *x, int *y)
{
	union REGS r;

	if( mouseactive)
	{
		r.w.ax = 0x000b;
		int386( 0x33, &r, &r);
		*x = (signed short)r.w.cx;
		*y = (signed short)r.w.dx;
	}
}


/* ######################################################################### */

static char getkey()
{
	return( getch());
}

/* ######################################################################### */

static char *geterror()
{
	ERRORS olderr = error;
	error = ERR_NOERR;
	switch( olderr)
	{
		case ERR_NOMEM: return( "Not enough memory");
		case ERR_NOMOUSE: return( "Mouse driver not found");
		default : return( "Unknown error");
	}
}

/* ######################################################################### */


DRV vesaDRV =
{
	"Vesa 1.2/2.0 Driver",

	0,
	NULL,

	0,0,
	0,0,0,0,
	1,1,

	0,/*config*/

	NULL,

	NULL,

	setup,
	init,
	reinit,
	exit,

	setpalette,
	vsync,
	clearscreen,

	createbuff,
	destroybuff,
	setbuff,

	xcopybuff,
	xmovebuff,
	copybuff,
	clearbuff,
	movebuff,

  _plot,
  _line,
  _poly,
  _tpoly,
  _blit,

	mouseinit,
	mousecallback,
	getmousepos,
	setmousepos,
	getmousediff,

	kbhit,
	getkey,

	geterror,
};














