/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen 		   */
/*								   */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* Multi-chipset support Copyright (C) 1993 Harm Hanemaayer */
/* partially copyrighted (C) 1993 by Hartmut Schirmer */
/* Changes by Michael Weller. */


/* The code is a bit of a mess; also note that the drawing functions */
/* are not speed optimized (the gl functions are much faster). */

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/kd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/vt.h>
#include <sys/wait.h>
#include <errno.h>
#include <ctype.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"
#include "config.h"
#include "mouse/vgamouse.h"


/* Delay in microseconds after a mode is set (screen is blanked during this */
/* time), allows video signals to stabilize */
#define MODESWITCHDELAY 150000

/* Define this to disable video output during mode switches, in addition to */
/* 'turning off the screen', which is always done. */
/* Doesn't look very nice on my Cirrus. */
/* #define DISABLE_VIDEO_OUTPUT */

/* #define DONT_WAIT_VC_ACTIVE */


#define SETSIG(sa, sig, fun) \
	sa.sa_handler = fun; \
	sa.sa_flags = 0; \
	sa.sa_mask = 0; \
	sigaction(sig, &sa, NULL);

#define SETSIG2(sa, sig, fun, oldsa) \
	sa.sa_handler = fun; \
	sa.sa_flags = 0; \
	sa.sa_mask = 0; \
	sigaction(sig, &sa, &oldsa);


/* variables used to shift between monchrome and color emulation */
int CRT_I;			/* current CRT index register address */
int CRT_D;			/* current CRT data register address */
int IS1_R;			/* current input status register address */
static int color_text;		/* true if color text emulation */


struct info infotable[] = {
	{   80,   25,    16,  160, 0 },		/* VGAlib VGA modes */
	{  320,  200,    16,   40, 0 },
	{  640,  200,    16,   80, 0 },
	{  640,  350,    16,   80, 0 },
	{  640,  480,    16,   80, 0 },
	{  320,  200,   256,  320, 1 },
	{  320,  240,   256,   80, 0 },
	{  320,  400,   256,   80, 0 },
	{  360,  480,   256,   90, 0 },
	{  640,  480,     2,   80, 0 },

	{  640,  480,   256,  640, 1 },		/* VGAlib SVGA modes */
	{  800,  600,   256,  800, 1 },
	{ 1024,  768,   256, 1024, 1 },
	{ 1280, 1024,   256, 1280, 1 },

	{  320,  200, 1<<15,  640, 2 },		/* Hicolor/truecolor modes */
	{  320,  200, 1<<16,  640, 2 },
	{  320,  200, 1<<24,  320 * 3, 3 },
	{  640,  480, 1<<15,  640 * 2, 2 },
	{  640,  480, 1<<16,  640 * 2, 2 },
	{  640,  480, 1<<24,  640 * 3, 3 },
	{  800,  600, 1<<15,  800 * 2, 2 },
	{  800,  600, 1<<16,  800 * 2, 2 },
	{  800,  600, 1<<24,  800 * 3, 3 },
	{ 1024,  768, 1<<15, 1024 * 2, 2 },
	{ 1024,  768, 1<<16, 1024 * 2, 2 },
	{ 1024,  768, 1<<24, 1024 * 3, 3 },
	{ 1280, 1024, 1<<15, 1280 * 2, 2 },
	{ 1280, 1024, 1<<16, 1280 * 2, 2 },
	{ 1280, 1024, 1<<24, 1280 * 3, 3 },

	{  800,  600,    16,  100, 0 },		/* SVGA 16-color modes */
	{ 1024,  768,    16,  128, 0 },
	{ 1280, 1024,    16,  160, 0 },

	{  720,  348,     2,   90, 0 },		/* Hercules emulation mode */

	{  320,  200, 1<<24,  320 * 4, 4 },
	{  640,  480, 1<<24,  640 * 4, 4 },
	{  800,  600, 1<<24,  800 * 4, 4 },
	{ 1024,  768, 1<<24, 1024 * 4, 4 },
	{ 1280, 1024, 1<<24, 1280 * 4, 4 },
 
	{    0,    0,     0,    0, 0 },	/* 16 user definable modes */
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 },
	{    0,    0,     0,    0, 0 }
};

#define MAX_MODES (sizeof(infotable) / sizeof(struct info))


/* default palette values */
static const unsigned char default_red[256]   
             = { 0, 0, 0, 0,42,42,42,42,21,21,21,21,63,63,63,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		 0,16,31,47,63,63,63,63,63,63,63,63,63,47,31,16,
		 0, 0, 0, 0, 0, 0, 0, 0,31,39,47,55,63,63,63,63,
		63,63,63,63,63,55,47,39,31,31,31,31,31,31,31,31,
		45,49,54,58,63,63,63,63,63,63,63,63,63,58,54,49,
		45,45,45,45,45,45,45,45, 0, 7,14,21,28,28,28,28,
		28,28,28,28,28,21,14, 7, 0, 0, 0, 0, 0, 0, 0, 0,
		14,17,21,24,28,28,28,28,28,28,28,28,28,24,21,17,
		14,14,14,14,14,14,14,14,20,22,24,26,28,28,28,28,
		28,28,28,28,28,26,24,22,20,20,20,20,20,20,20,20,
		 0, 4, 8,12,16,16,16,16,16,16,16,16,16,12, 8, 4,
		 0, 0, 0, 0, 0, 0, 0, 0, 8,10,12,14,16,16,16,16,
		16,16,16,16,16,14,12,10, 8, 8, 8, 8, 8, 8, 8, 8,
		11,12,13,15,16,16,16,16,16,16,16,16,16,15,13,12,
		11,11,11,11,11,11,11,11, 0, 0, 0, 0, 0, 0, 0, 0};
static const unsigned char default_green[256] 
	     = { 0, 0,42,42, 0, 0,21,42,21,21,63,63,21,21,63,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		 0, 0, 0, 0, 0, 0, 0, 0, 0,16,31,47,63,63,63,63,
		63,63,63,63,63,47,31,16,31,31,31,31,31,31,31,31,
		31,39,47,55,63,63,63,63,63,63,63,63,63,55,47,39,
		45,45,45,45,45,45,45,45,45,49,54,58,63,63,63,63,
		63,63,63,63,63,58,54,49, 0, 0, 0, 0, 0, 0, 0, 0,
		 0, 7,14,21,29,28,28,28,28,28,28,28,28,21,14, 7,
		14,14,14,14,14,14,14,14,14,17,21,24,28,28,28,28,
		28,28,28,28,28,24,21,17,20,20,20,20,20,20,20,20,
		20,22,24,26,28,28,28,28,28,28,28,28,28,26,24,22,
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 8,12,16,16,16,16,
		16,16,16,16,16,12, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8,
		 8,10,12,14,16,16,16,16,16,16,16,16,16,14,12,10,
		11,11,11,11,11,11,11,11,11,12,13,15,16,16,16,16,
		16,16,16,16,16,15,13,12, 0, 0, 0, 0, 0, 0, 0, 0};
static const unsigned char default_blue[256]  
             = { 0,42, 0,42, 0,42, 0,42,21,63,21,63,21,63,21,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		63,63,63,63,63,47,31,16, 0, 0, 0, 0, 0, 0, 0, 0,
		 0,16,31,47,63,63,63,63,63,63,63,63,63,55,47,39,
		31,31,31,31,31,31,31,31,31,39,47,55,63,63,63,63,
		63,63,63,63,63,58,54,49,45,45,45,45,45,45,45,45,
		45,49,54,58,63,63,63,63,28,28,28,28,28,21,14, 7,
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,14,21,28,28,28,28,
		28,28,28,28,28,24,21,17,14,14,14,14,14,14,14,14,
		14,17,21,24,28,28,28,28,28,28,28,28,28,26,24,22,
		20,20,20,20,20,20,20,20,20,22,24,26,28,28,28,28,
		16,16,16,16,16,12, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0,
		 0, 4, 8,12,16,16,16,16,16,16,16,16,16,14,12,10,
		 8, 8, 8, 8, 8, 8, 8, 8, 8,10,12,14,16,16,16,16,
		16,16,16,16,16,15,13,12,11,11,11,11,11,11,11,11,
		11,12,13,15,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0};


static unsigned char text_regs[MAX_REGS];   /* VGA registers for saved text mode */   


/* saved text mode palette values */ 
static unsigned char text_red[256];  
static unsigned char text_green[256];  
static unsigned char text_blue[256];  

/* saved graphics mode palette values */ 
static unsigned char graph_red[256];  
static unsigned char graph_green[256];  
static unsigned char graph_blue[256];  

static int         prv_mode  = TEXT;    /* previous video mode      */
static int         flip_mode = TEXT;    /* flipped video mode       */

int 	    CM  = TEXT;			/* current video mode       */
struct info CI;				/* current video parameters */
int 	    COL;			/* current color            */


static int initialized = 0;   /* flag: initialize() called ?  */
static int flip        = 0;   /* flag: executing vga_flip() ? */

/* svgalib additions: */

int __svgalib_chipset = UNDEFINED;
int __svgalib_driver_report = 1;
	/* report driver used after chipset detection */
int __svgalib_videomemoryused = -1;
int __svgalib_modeX = 0;	/* true after vga_setmodeX() */
int __svgalib_critical = 0;	/* indicates blitter is busy */
int __svgalib_screenon = 1;     /* screen visible if != 0 */
int __svgalib_monitortype = MON1024_43I; /* monitor type */
int __svgalib_modeinfo_linearset = 0;	/* IS_LINEAR handled via extended vga_modeinfo */

static int lastmodenumber = __GLASTMODE; /* Last defined mode */
static int my_pid      = 0;   /* process PID, used with atexit() */
static int currentpage;
static int currentlogicalwidth;
static int currentdisplaystart;
static int mouse_support = 0;
static int mouse_mode = 0;
static int mouse_type = -1;
static int runinbackground = 0;
static int oktowrite = 1;
static int modeinfo_mask= ~0;

static int tty0_fd = -1; 	   /* /dev/tty0 file descriptor */
static FILE* console;		   /* console input stream	*/

int __svgalib_mem_fd = -1;	/* /dev/mem file descriptor  */

/* Dummy buffer for mmapping grahics memory; points to 64K VGA framebuffer. */
unsigned char *__svgalib_graph_mem;
/* Exported variable (read-only) is shadowed from internal variable, for */
/* better shared library performance. */
unsigned char *graph_mem;

static unsigned char* graph_buf = NULL;  /* saves graphics data during flip */

static unsigned char *font_buf1;	/* saved font data - plane 2 */
static unsigned char *font_buf2;	/* saved font data - plane 3 */

static struct termios text_termio;  /* text mode termio parameters     */
static struct termios graph_termio; /* graphics mode termio parameters */

int flipchar = '\x1b';   /* flip character - initially  ESCAPE */


/* Chipset specific functions */

int (**chipsetfunctions)() = vga_chipsetfunctions;

static int (*setpage)();  /* gives little faster vga_setpage() */
static int (*setrdpage)();
static int (*setwrpage)();

static void readconfigfile();

int (**chipsetfunctionslist[])() = {
	NULL,	/* chipset undefined */
	vga_chipsetfunctions,
	#ifdef INCLUDE_ET4000_DRIVER
	et4000_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_CIRRUS_DRIVER
	cirrus_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_TVGA_DRIVER
	tvga8900_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_OAK_DRIVER
	oak_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_EGA_DRIVER
	ega_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_S3_DRIVER
	s3_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCUDE_ET3000_DRIVER
	et3000_chipsetfunctions,
	#else
	NULL,
	#endif
	#ifdef INCLUDE_MACH32_DRIVER
	mach32_chipsetfunctions,
	#else
	NULL,
	#endif
};

static char *driver_names[]={"","VGA","ET4000","Cirrus","TVGA","Oak","EGA","S3","ET3000","Mach32",NULL};

/* Chipset drivers */

/* vgadrv	Standard VGA (also used by drivers below) */
/* et4000	Tseng ET4000 (from original vgalib) */
/* cirrus	Cirrus Logic GD542x */
/* tvga8900     Trident TVGA 8900/9000 (derived from tvgalib) */
/* oak		Oak Technologies 037/067/077 */
/* egadrv	IBM EGA (subset of VGA) */
/* s3		S3 911 */
/* mach32	ATI MACH32 */

/*#define DEBUG*/
/*#define DEBUG_CONF*/ /*Debug config file parsing..*/

#ifdef DEBUG
static void _DEBUG(int dnr) {
  static int first = 1;
  FILE *dfile;

  dfile = fopen("svgalib.debug", (first ? "w" : "a"));
  first = 0;
  if (dfile == NULL) exit(-1);
  fprintf(dfile, "debug #%d\n", dnr);
  fclose(dfile);
  sync();
}
#else
#define _DEBUG(d)
#endif

static void set_graphtermio()
{
        /* set graphics mode termio parameters */
        ioctl(0, TCSETSW, &graph_termio);
}


static void set_texttermio()
{
	/* restore text mode termio parameters */
	ioctl(0, TCSETSW, &text_termio);
}


static void disable_interrupt()
{
    struct termios cur_termio;

    ioctl(0, TCGETS, &cur_termio);
    cur_termio.c_lflag &= ~ISIG;
    ioctl(0, TCSETSW, &cur_termio);
}


static void enable_interrupt()
{
    struct termios cur_termio;

    ioctl(0, TCGETS, &cur_termio);
    cur_termio.c_lflag |= ISIG;
    ioctl(0, TCSETSW, &cur_termio);
}


static void waitvtactive() {
	struct stat chkbuf;
/*	struct vt_stat vtstat; */
	int major, minor;
	int fd;

	/* get console number we are running in */
	fd = dup(2);	/* stderr */
	fstat(fd, &chkbuf);
	major = chkbuf.st_rdev >> 8;
	minor = chkbuf.st_rdev & 0xff;	/* console number */

/*	printf("major = %d, minor = %d\n", major, minor); */

	if (major != 4 || minor >= 64) {
		printf("Not running in graphics-capable virtual console.\n");
		set_texttermio();
		exit(-1);
	}
	close(fd);

	fd = dup(2);
	ioctl(fd, VT_WAITACTIVE, minor);
	close(fd);
}


/* open /dev/mem */
static void open_mem(void) {
    if (__svgalib_mem_fd < 0) 
	if ((__svgalib_mem_fd = open("/dev/mem", O_RDWR) ) < 0) {
	    printf("svgalib: Cannot open /dev/mem.\n");
	    exit (-1);
	}
}


void __vga_get_perm(void)
{
    static int done = 0;

    if (done)
        return;
    done = 1;

    /* get I/O permissions for VGA registers */

    if (ioperm(0x3b4, 0x3df - 0x3b4 + 1, 1)) {
	printf("svgalib: Cannot get I/O permissions.\n");
	exit(-1);
    }

    /* open /dev/mem (needs supervisor rights too) */
    open_mem();
 
    /* color or monochrome text emulation? */
    if (CHIPSET != EGA)
	color_text = port_in(MIS_R)&0x01;
    else
    	color_text = 1;	/* EGA is assumed color */

    /* chose registers for color/monochrome emulation */
    if (color_text) {
	CRT_I = CRT_IC;
	CRT_D = CRT_DC;
	IS1_R = IS1_RC;
    } else {
	CRT_I = CRT_IM;
	CRT_D = CRT_DM;
	IS1_R = IS1_RM;
    }
}


void __vga_delay() {
	int i;
	for (i = 0; i < 10; i++);
}

/* The Xfree server uses a slow copy, may help us too ... */
static void slowcpy(unsigned char *dest, unsigned char *src, unsigned bytes)
{
    while (bytes-- > 0)
	*(dest++) = *(src++);
}

int __vga_saveregs(unsigned char *regs)
{
    int i;

    if (__svgalib_chipset == EGA) {
    	/* Special case: Don't save standard VGA registers. */
        return chipset_saveregs(regs);
    }

    /* save VGA registers */
    for (i = 0; i < CRT_C; i++) {
	 port_out(i, CRT_I); 
	 regs[CRT+i] = port_in(CRT_D); 
    }
    for (i = 0; i < ATT_C; i++) {
      	 port_in(IS1_R);
      	 __vga_delay();
         port_out(i, ATT_IW); 
         __vga_delay();
         regs[ATT+i] = port_in(ATT_R); 
         __vga_delay();
    }
    for (i = 0; i < GRA_C; i++) {
       	 port_out(i, GRA_I); 
       	 regs[GRA+i] = port_in(GRA_D); 
    }
    for (i = 0; i < SEQ_C; i++) {
       	 port_out(i, SEQ_I); 
       	 regs[SEQ+i] = port_in(SEQ_D); 
    }
    regs[MIS] = port_in(MIS_R); 

    i = chipset_saveregs(regs);		/* save chipset-specific registers */
					/* i : additional registers */
    if (!SCREENON) {			/* We turned off the screen */
      	 port_in(IS1_R);
      	 __vga_delay();
         port_out(0x20, ATT_IW);
    }
    return CRT_C + ATT_C + GRA_C + SEQ_C + 1 + i;
}


int __vga_setregs(const unsigned char *regs)
{
    int i;

    if (__svgalib_chipset == EGA) {
        /* Enable graphics register modification */
	port_out(0x00, GRA_E0);
	port_out(0x01, GRA_E1);
    }

    /* update misc output register */
    port_out(regs[MIS], MIS_W);         

    /* synchronous reset on */
    port_out(0x00,SEQ_I); 
    port_out(0x01,SEQ_D);	        
  
    /* write sequencer registers */
    port_out(1, SEQ_I);
    port_out(regs[SEQ+1]|0x20, SEQ_D);
    for (i = 2; i < SEQ_C; i++) {       
	port_out(i, SEQ_I); 
	port_out(regs[SEQ+i], SEQ_D); 
    }

    /* synchronous reset off */
    port_out(0x00, SEQ_I); 
    port_out(0x03, SEQ_D);	        

    if (__svgalib_chipset != EGA) {  
        /* deprotect CRT registers 0-7 */
        port_out(0x11, CRT_I);		  
        port_out(port_in(CRT_D)&0x7F, CRT_D);   
    }
  
    /* write CRT registers */
    for (i = 0; i < CRT_C; i++) {       
	port_out(i, CRT_I); 
	port_out(regs[CRT+i], CRT_D); 
    }

    /* write graphics controller registers */
    for (i = 0; i < GRA_C; i++) {       
	port_out(i, GRA_I); 
	port_out(regs[GRA+i], GRA_D); 
    }
     
    /* write attribute controller registers */
    for (i = 0; i < ATT_C; i++) {       
	port_in(IS1_R);   /* reset flip-flop */
	__vga_delay();
	port_out(i, ATT_IW);
	__vga_delay();
	port_out(regs[ATT+i], ATT_IW);
	__vga_delay();
    }

    return 0;
}

/* We invoke the old interrupt handler after setting text mode */

static struct sigaction old_SIGINT_handler;
static struct sigaction old_SIGFPE_handler;
static struct sigaction old_SIGSEGV_handler;
static struct sigaction old_SIGILL_handler;

static void restoretextmode(void) {
	/* handle unexpected interrupts - restore text mode and exit */
	if (CM != TEXT) 
		vga_setmode(TEXT);
	if (!__svgalib_screenon) 
		vga_screenon();
	if (tty0_fd >= 0)
		ioctl(tty0_fd, KDSETMODE, KD_TEXT);
    	set_texttermio();
}


static void SIGINT_handler( int v ) {
	restoretextmode();
	printf("svgalib: Interrupted (ctrl-c pressed)\n");
	sigaction(SIGINT, &old_SIGINT_handler, NULL);
	raise(SIGINT);
}

static void SIGFPE_handler( int v ) {
	restoretextmode();
	printf("svgalib: Floating point exception or division by zero\n");
	sigaction(SIGFPE, &old_SIGFPE_handler, NULL);
	raise(SIGFPE);
}

static void SIGSEGV_handler( int v ) {
	restoretextmode();
	printf("svgalib: Segment violation\n");
	sigaction(SIGSEGV, &old_SIGSEGV_handler, NULL);
	raise(SIGSEGV);
}

static void SIGILL_handler( int v ) {
	restoretextmode();
	printf("svgalib: Illegal instruction\n");
	sigaction(SIGILL, &old_SIGILL_handler, NULL);
	raise(SIGILL);
}

int __vga_getchipset(void) {
	readconfigfile();	/* Make sure the config file is read. */
	if (CHIPSET == UNDEFINED) {
		__vga_get_perm();
		CHIPSET = VGA; /* Protect against recursion */
		#ifdef INCLUDE_MACH32_DRIVER
		if (mach32_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = MACH32;
		else
		#endif
		#ifdef INCLUDE_EGA_DRIVER
		if (ega_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = EGA;
		else
		#endif
		#ifdef INCLUDE_ET4000_DRIVER
		if (et4000_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = ET4000;
		else
		#endif
		#ifdef INCLUDE_TVGA_DRIVER
		if (tvga8900_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = TVGA8900;
		else
		#endif
		#ifdef INCLUDE_CIRRUS_DRIVER
		/* The Cirrus detection is not very clean. */
		if (cirrus_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = CIRRUS;
		else
		#endif
		#ifdef INCLUDE_OAK_DRIVER
		if (oak_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = OAK;
		else
		#endif
		#ifdef INCLUDE_S3_DRIVER
		if (s3_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = S3;
		else
		#endif
		#ifdef INCLUDE_ET3000_DRIVER
		if (et3000_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = ET3000;
		else
		#endif
		
		if (vga_chipsetfunctions[CHIPSET_TEST]())
			CHIPSET = VGA;
		else
		/* else */
		{
			fprintf(stderr, "svgalib: Cannot find EGA or VGA graphics device.\n");
			exit(1);
		}
		setpage = chipsetfunctions[CHIPSET_SETPAGE];
		setrdpage = chipsetfunctions[CHIPSET_SETRDPAGE];
		setwrpage = chipsetfunctions[CHIPSET_SETWRPAGE];
	}
	return CHIPSET;
}

void vga_setchipset( int c ) {
	CHIPSET = c;
	#ifdef DEBUG
	printf("Setting chipset\n");
	#endif
	if (CHIPSET != UNDEFINED && chipsetfunctionslist[c] != NULL) {
		__vga_get_perm();
		chipsetfunctionslist[c][CHIPSET_INIT](0, 0, 0);
	}
	setpage = chipsetfunctions[CHIPSET_SETPAGE];
	setrdpage = chipsetfunctions[CHIPSET_SETRDPAGE];
	setwrpage = chipsetfunctions[CHIPSET_SETWRPAGE];
}

void vga_setchipsetandfeatures( int c, int par1, int par2 ) {
	CHIPSET = c;
	#ifdef DEBUG
	printf("Forcing chipset and features\n");
	#endif
	__vga_get_perm();
	chipsetfunctionslist[c][CHIPSET_INIT](1, par1, par2);
	#ifdef DEBUG
	printf("Finished forcing chipset and features\n");
	#endif
	setpage = chipsetfunctions[CHIPSET_SETPAGE];
	setrdpage = chipsetfunctions[CHIPSET_SETRDPAGE];
	setwrpage = chipsetfunctions[CHIPSET_SETWRPAGE];
}


static void savepalette(unsigned char *red, unsigned char *green, 
unsigned char *blue) {
	int i;

	if (CHIPSET == EGA)
	    return;

	if ((__svgalib_chipset == MACH32) && SVGAMODE(CM))
           {
           /* Actually the same but we are in 8514 mode and the dac
           does not respond to the VGA circuitry anymore... */

    	   port_out(0, PEL8514_IR); 
    	   for (i = 0; i < 256; i++) {
               __vga_delay();
               *(red++)   = port_in(PEL8514_D);
               __vga_delay();
               *(green++) = port_in(PEL8514_D);
               __vga_delay();
               *(blue++)  = port_in(PEL8514_D);
	       }
	   return;
	   }

    	/* save graphics mode palette - first select palette index 0 */
    	port_out(0, PEL_IR); 

    	/* read RGB components - index is autoincremented */
    	for (i = 0; i < 256; i++) {
            __vga_delay();
            *(red++)   = port_in(PEL_D);
            __vga_delay();
            *(green++) = port_in(PEL_D);
            __vga_delay();
            *(blue++)  = port_in(PEL_D);
        }
}

static void restorepalette( const unsigned char *red,
const unsigned char *green, const unsigned char *blue ) {
	int i;

	if (CHIPSET == EGA)
	    return;

	if ((__svgalib_chipset == MACH32) && SVGAMODE(CM))
           {
           /* Actually the same but we are in 8514 mode and the dac
           does not respond to the VGA circuitry anymore... */

    	   port_out(0, PEL8514_IW); 
    	   for (i = 0; i < 256; i++) {
               __vga_delay();
               port_out(*(red++), PEL8514_D);
               __vga_delay();
               port_out(*(green++), PEL8514_D);
               __vga_delay();
               port_out(*(blue++), PEL8514_D);
	       }
	   return;
	   }

        /* restore saved palette */
    	port_out(0, PEL_IW); 

    	/* read RGB components - index is autoincremented */
    	for (i = 0; i < 256; i++) {
            __vga_delay();
            port_out(*(red++), PEL_D);
            __vga_delay();
            port_out(*(green++), PEL_D);
            __vga_delay();
            port_out(*(blue++), PEL_D);
        }
}


/* Virtual console switching */

static int forbidvtrelease = 0;
static int forbidvtacquire = 0;
static int lock_count = 0;
static int release_flag = 0;

static void takevtcontrol();

static void vga_flipaway();
static void vga_flipback();

static void releasevt_signal( int n ) {
	struct sigaction siga;
	if (lock_count) {
		release_flag = 1;
		return;
	}
	#ifdef DEBUG
	printf("Release request.\n");
	#endif
	forbidvtacquire = 1;
	SETSIG(siga, SIGUSR1, releasevt_signal);
	if (forbidvtrelease) {
		forbidvtacquire = 0;
		ioctl(tty0_fd, VT_RELDISP, 0);
		return;
	}
	vga_flipaway();
	ioctl(tty0_fd, VT_RELDISP, 1);
	#ifdef DEBUG
	printf("Finished release.\n");
	#endif
	forbidvtacquire = 0;

	/* Suspend program until switched to again. */
	#ifdef DEBUG
	printf("Suspended.\n");
	#endif
	oktowrite = 0;
	if (!runinbackground)
		waitvtactive();
	#ifdef DEBUG
	printf("Waked.\n");
	#endif
} 

static void acquirevt_signal( int n ) {
	struct sigaction siga;
	#ifdef DEBUG
	printf("Acquisition request.\n");
	#endif
	forbidvtrelease = 1;
	SETSIG(siga, SIGUSR2, acquirevt_signal);
	if (forbidvtacquire) {
		forbidvtrelease = 0;
		return;
	}
	vga_flipback();
	ioctl(tty0_fd, VT_RELDISP, 2);
	#ifdef DEBUG
	printf("Finished acquisition.\n");
	#endif
	forbidvtrelease = 0;
	oktowrite = 1;
}

static struct vt_mode oldvtmode;
static struct vt_mode newvtmode;

void takevtcontrol() {
    	struct sigaction siga;

    	ioctl(tty0_fd, VT_GETMODE, &oldvtmode);
    	newvtmode = oldvtmode;
    	newvtmode.mode = VT_PROCESS;	/* handle VT changes */
    	newvtmode.relsig = SIGUSR1;	/* I didn't find SIGUSR1/2 anywhere */
    	newvtmode.acqsig = SIGUSR2;	/* in the kernel sources, so I guess */
    					/* they are free */
    	ioctl(tty0_fd, VT_SETMODE, &newvtmode);

	SETSIG(siga, SIGUSR1, releasevt_signal);
    	SETSIG(siga, SIGUSR2, acquirevt_signal);
}

static void __vga_mmap()
{
    #if 1
    /* This may waste 64K; mmap can probably do better by now. */
    /* valloc allocates aligned on page boundary */
    if ((__svgalib_graph_mem = valloc(GRAPH_SIZE)) == NULL) {
	printf("svgalib: allocation error \n");
	exit (-1);
    }
    __svgalib_graph_mem = (unsigned char *)mmap(
	(caddr_t)GM,
	GRAPH_SIZE,
	PROT_READ|PROT_WRITE,
	MAP_SHARED|MAP_FIXED,
	__svgalib_mem_fd,
	GRAPH_BASE
    );
    #endif
    #if 0
    /* This assumes pl10+. */
    /* Still seems to waste 64K, so don't bother. */
    GM = (unsigned char *)mmap(
	(caddr_t) 0,
	GRAPH_SIZE,
	PROT_READ|PROT_WRITE,
	MAP_SHARED,
	__svgalib_mem_fd,
	GRAPH_BASE
    );
    #endif
    graph_mem = __svgalib_graph_mem;	/* Exported variable. */
}

static void __vga_atexit(void) {
    if (getpid() == my_pid) /* protect against forked processes */
	restoretextmode(); 
}

static void setcoloremulation() {
    /* shift to color emulation */
    CRT_I = CRT_IC;
    CRT_D = CRT_DC;
    IS1_R = IS1_RC;
    if (CHIPSET != EGA)
	port_out(port_in(MIS_R)|0x01, MIS_W); 
}

static void initialize( int mode )
{
    struct sigaction siga;

    /* Make sure that textmode is restored at exit(). */
    if (my_pid == 0)
	my_pid = getpid();
    atexit(__vga_atexit);

#ifndef DONT_WAIT_VC_ACTIVE
    waitvtactive();
#endif

    /* save text mode termio parameters */
    ioctl(0, TCGETS, &text_termio);

    graph_termio = text_termio;

    /* change termio parameters to allow our own I/O processing */
    graph_termio.c_iflag &= ~(BRKINT|PARMRK|INPCK|IUCLC|IXON|IXOFF);
    graph_termio.c_iflag |=  (IGNBRK|IGNPAR);

    graph_termio.c_oflag &= ~(ONOCR);

    graph_termio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|NOFLSH);
    graph_termio.c_lflag |= (ISIG);   /* enable interrupt */

    graph_termio.c_cc[VMIN] = 1;
    graph_termio.c_cc[VTIME] = 0;
    graph_termio.c_cc[VSUSP] = 0;   /* disable suspend */

    disable_interrupt();

    __vga_getchipset();	/* make sure a chipset has been selected */
    chipset_unlock();
	
    /* open /dev/tty0 - current virtual console */
    if ((tty0_fd = open("/dev/tty0", O_RDONLY) ) < 0) {
	printf("svgalib: can't open /dev/tty0 \n");
	exit (-1);
    }
    console = fdopen(tty0_fd, "r"); 

#if 0     
    /* Check for EGA/VGA display */
/*   linux/kd.h defines this function but doesn't work (or am I wrong) ? */
    if (ioctl(tty0_fd, KDDISPTYPE, &display_type)){
	printf("VGAlib: can't get display type\n");
	exit (-1);
    }
    if (display_type != KD_EGA) {
	printf("VGAlib: EGA/VGA display required\n");
	exit(-1);
    }
#endif

    /* disable text output to console */
    ioctl(tty0_fd, KDSETMODE, KD_GRAPHICS);

    if (!mouse_support)
	    takevtcontrol();	/* HH: Take control over VT */
    
/*    vga_lockvc(); */

    /* open /dev/mem */
    open_mem();

    /* mmap graphics memory */

    __vga_mmap();

    if ((long)GM < 0) {
	printf("svgalib: mmap error \n");
	exit (-1);
    }

    /* disable video */
    vga_screenoff();

    __vga_saveregs(text_regs);

    /* This appears to fix the Trident 8900 rebooting problem. */
    if (__svgalib_chipset == TVGA8900) {
        port_out(0x0c, SEQ_I);              /* reg 12 */
        text_regs[EXT + 11] = port_in(SEQ_D);
        port_out(0x1f, CRT_I);
        text_regs[EXT + 12] = port_in(CRT_D);
    }

    /* save text mode palette - first select palette index 0 */
    port_out(0, PEL_IR);

    /* read RGB components - index is autoincremented */
    savepalette(text_red, text_green, text_blue);

    /* shift to color emulation */
    setcoloremulation();

    /* save font data - first select a 16 color graphics mode */
    chipsetfunctions[CHIPSET_SETMODE](GPLANE16);

    /* Allocate space for textmode font. */
    font_buf1 = malloc(FONT_SIZE * 2);
    font_buf2 = font_buf1 + FONT_SIZE;

    /* save font data in plane 2 */
    port_out(0x04, GRA_I); 
    port_out(0x02, GRA_D); 
    slowcpy(font_buf1, GM, FONT_SIZE);

    /* save font data in plane 3 */
    port_out(0x04, GRA_I); 
    port_out(0x03, GRA_D);
    slowcpy(font_buf2, GM, FONT_SIZE);

    initialized = 1;
    /* do our own interrupt handling */
	SETSIG2(siga, SIGINT, SIGINT_handler, old_SIGINT_handler);

	/* Restore textmode after segment violation and math exception */
	/* I wonder why vgalib didn't do this */
    	SETSIG2(siga, SIGSEGV, SIGSEGV_handler, old_SIGSEGV_handler);
    	SETSIG2(siga, SIGFPE, SIGFPE_handler, old_SIGFPE_handler);
    	SETSIG2(siga, SIGILL, SIGILL_handler, old_SIGILL_handler);

	if (mode == G320x200x256)
		VMEM = 65536;
	else
	if (STDVGAMODE(mode))
		VMEM = 256 * 1024;
	else {
		vga_modeinfo *modeinfo;
		modeinfo = vga_getmodeinfo(mode);
		VMEM = modeinfo->linewidth * modeinfo->height;
	}

/*	vga_unlockvc(); */
}


inline void vga_setpage( int p ) {
	if (p == currentpage)
		return;
	setpage(p);
	currentpage = p;
}


void vga_setreadpage( int p ) {
	if ( p == currentpage)
		return;
	setrdpage( p);
	currentpage = -1;
}


void vga_setwritepage( int p ) {
	if ( p == currentpage)
		return;
	setwrpage( p);
	currentpage = -1;
}


void vga_safety_fork(void (*shutdown_routine)())
{
	int childpid;

	if (initialized)
	{	
		printf("svgalib: warning: vga_safety_fork() called when already initialized\n");
		return;
	}

	initialize(TEXT); /* XXX why should mode be passed to initialize??? */

	childpid = fork();
	if (childpid < 0)
	{
		printf("VGAlib: warning: can't fork to enhance reliability; proceeding anyway\n");
		return;
	}
	if (childpid)
	{
		while (waitpid(childpid, 0, WUNTRACED) != childpid);
		if (shutdown_routine)
			shutdown_routine();
		vga_setmode(TEXT);
		exit(0); /* XXX pass back true exit status */
	}

	/* These need to be done again because the child doesn't inherit them.  */
	__vga_get_perm();
	__vga_mmap();
}


static void prepareforfontloading() {
	if (__svgalib_chipset == CIRRUS) {
		outb(0x3c4, 0x0f);
		/* Disable CRT FIFO Fast-Page mode. */
		outb(0x3c5, inb(0x3c5) | 0x40);
	}
}

static void fontloadingcomplete() {
	if (__svgalib_chipset == CIRRUS) {
		outb(0x3c4, 0x0f);
		/* Re-enable CRT FIFO Fast-Page mode. */
		outb(0x3c5, inb(0x3c5) & 0xbf);
	}
}


int vga_setmode(int mode) 
{
    if (!initialized)
        initialize(mode);

    if (mode != TEXT && !chipset_modeavailable(mode))
    	return -1;

/*    if (!flip)
	vga_lockvc(); */
    disable_interrupt();

    prv_mode = CM;
    CM       = mode;

    /* disable video */
    vga_screenoff();

    /* Should be more robust (eg. grabbed X modes) */
    if (   __vga_getchipset() == ET4000
	&& prv_mode != G640x480x256
	&& SVGAMODE(prv_mode)           )
	    chipset_setmode(G640x480x256, prv_mode);

    if (mode == TEXT) {
    	/* Returning to textmode. */
    	
    	if (prv_mode != TEXT && mouse_mode == prv_mode) {
    		#ifdef DEBUG
    		printf("svgalib: Closing mouse.\n");
    		#endif
    		mouse_close();
    		mouse_mode = 0;
    		/* vga_unlockvc(); */
    	}
    
	if (SVGAMODE(prv_mode))
		vga_setpage(0);


	/* The extended registers are restored either by the */
	/* chipset setregs function, or the chipset setmode function. */

        /* restore font data - first select a 16 color graphics mode */
        /* Note: this should restore the old extended registers if */
        /* setregs is not defined for the chipset. */
        chipsetfunctions[CHIPSET_SETMODE](GPLANE16, prv_mode);

	if (CHIPSET != EGA)
		/* restore old extended regs */
		chipset_setregs(text_regs, mode);

	/* disable Set/Reset Register */
    	port_out(0x01, GRA_I ); 
    	port_out(0x00, GRA_D );   

	prepareforfontloading();

        /* restore font data in plane 2 - necessary for all VGA's */
    	port_out(0x02, SEQ_I ); 
    	port_out(0x04, SEQ_D );
	slowcpy(GM, font_buf1, FONT_SIZE);

        /* restore font data in plane 3 - necessary for Trident VGA's */
    	port_out(0x02, SEQ_I ); 
    	port_out(0x08, SEQ_D );   
	slowcpy(GM, font_buf2, FONT_SIZE);

	fontloadingcomplete();

        /* change register adresses if monochrome text mode */
        /* EGA is assumed to use color emulation. */
        if (!color_text) {
            CRT_I = CRT_IM;
            CRT_D = CRT_DM;
            IS1_R = IS1_RM;
            port_out(port_in(MIS_R)&0xFE, MIS_W); 
        }

        /* restore saved palette */
        restorepalette(text_red, text_green, text_blue);

	/* restore text mode VGA registers */
    	__vga_setregs(text_regs);

/*    	if (!flip) */
		/* enable text output - restores the screen contents */ 
	      	ioctl(tty0_fd, KDSETMODE, KD_TEXT);

	usleep(MODESWITCHDELAY);	/* wait for signal to stabilize */

        /* enable video */
	vga_screenon();

  	if (!flip)
	        /* restore text mode termio */
        	set_texttermio();
    } else {
        /* Setting a graphics mode. */

	/* disable text output */
	ioctl(tty0_fd, KDSETMODE, KD_GRAPHICS);
	
	if (SVGAMODE(prv_mode) && (!SVGAMODE(mode))) {
		/* The current mode is an SVGA mode, and we now want to */
		/* set a standard VGA mode. Make sure the extended regs */
		/* are restored. */
		vga_setpage(0);
		chipset_setregs(text_regs, mode);
			/* restore old extended regs */
	}

        /* shift to color emulation */
        setcoloremulation();

	chipset_setmode(mode, prv_mode);

        CI.xdim = infotable[mode].xdim;
        CI.ydim = infotable[mode].ydim;
        CI.colors = infotable[mode].colors;
        CI.xbytes = infotable[mode].xbytes;
        CI.bytesperpixel = infotable[mode].bytesperpixel;
        MODEX = 0;

        if (!flip) {
            	/* set default palette */
            	if (CI.colors <= 256) 
		  restorepalette(default_red, default_green, default_blue);
 
            	/* clear screen (sets current color to 15) */
            	currentpage = -1;
            	vga_clear();

            	if (SVGAMODE(__svgalib_cur_mode))
	            	vga_setpage(0);
        }

        currentpage = -1;
        currentlogicalwidth = CI.xbytes;
        currentdisplaystart = 0;

	usleep(MODESWITCHDELAY);	/* wait for signal to stabilize */

        /* enable video */
	if (!flip)
	  vga_screenon();

	if (mouse_support) {
		#ifdef DEBUG
		printf("svgalib: Opening mouse (type = %d).\n", mouse_type);
		#endif
		if (mouse_init("/dev/mouse", mouse_type, MOUSE_DEFAULTSAMPLERATE))
			printf("svgalib: Failed to initialize mouse.\n");
		else {
			/* vga_lockvc(); */
			mouse_setxrange(0, CI.xdim - 1);
			mouse_setyrange(0, CI.ydim - 1);
			mouse_setwrap(MOUSE_NOWRAP);
			mouse_mode = mode;
		}
	}

  	if (!flip)
	        /* set graphics mode termio */
        	set_graphtermio();
        else
        	enable_interrupt();
        	
	{
		vga_modeinfo *modeinfo;
		modeinfo = vga_getmodeinfo(mode);
		MODEX = (modeinfo->flags & IS_MODEX);
	}

    }

/*    if (!flip)
	vga_unlockvc(); */

    return 0;  
}

void vga_gettextfont( void *font ) {
	memcpy(font, font_buf1, FONT_SIZE);
}

void vga_puttextfont( void *font ) {
	memcpy(font_buf1, font, FONT_SIZE);
	memcpy(font_buf2, font, FONT_SIZE);
}

void vga_gettextmoderegs( void *regs ) {
	memcpy(regs, text_regs, MAX_REGS);
}

void vga_settextmoderegs( void *regs ) {
	memcpy(text_regs, regs, MAX_REGS);
}

int vga_getcurrentmode() {
	return CM;
}

int vga_getcurrentchipset() {
	return __vga_getchipset();
}

void vga_disabledriverreport() {
	DREP = 0;
}

vga_modeinfo *vga_getmodeinfo( int mode ) {
	static vga_modeinfo modeinfo;
	int is_modeX = (CM == mode) && MODEX;

	if (mode > vga_lastmodenumber())
	    return NULL;
	__vga_getchipset();
	modeinfo.width = infotable[mode].xdim;
	modeinfo.height = infotable[mode].ydim;
	modeinfo.bytesperpixel = infotable[mode].bytesperpixel;
	modeinfo.colors = infotable[mode].colors;
	if (is_modeX) {
		modeinfo.linewidth = modeinfo.width / 4;
		modeinfo.bytesperpixel = 0;
	}
	else
		modeinfo.linewidth = infotable[mode].xbytes;
	modeinfo.flags = 0;
	if ((STDVGAMODE(mode) && mode != G320x200x256) || is_modeX)
		vga_chipsetfunctions[CHIPSET_GETMODEINFO](mode, &modeinfo);
	else
		/* Get chipset specific info for SVGA modes and */
		/* 320x200x256 (chipsets may support more pages) */
		chipset_getmodeinfo(mode, &modeinfo);

	if (modeinfo.colors == 256 && modeinfo.bytesperpixel == 0)
	  modeinfo.flags |= IS_MODEX;
	if (mode > __GLASTMODE)
	  modeinfo.flags |= IS_DYNAMICMODE;

	/* Maskout CAPABLE_LINEAR if requested by config file */
	modeinfo.flags&=modeinfo_mask;

	/* If all needed info is here, signal if linear support has been enabled */
	if( (modeinfo.flags&(CAPABLE_LINEAR|EXT_INFO_AVAILABLE)) ==
				(CAPABLE_LINEAR|EXT_INFO_AVAILABLE) ) {
		modeinfo.flags |= __svgalib_modeinfo_linearset;
	}

	return &modeinfo;
}

int vga_hasmode(int mode)
{
	readconfigfile();	/* Make sure the config file is read. */
	__vga_getchipset();	/* Make sure the chipset is known. */
	if (mode == TEXT)
		return 1;
	return (chipset_modeavailable(mode) != 0);
}


int vga_lastmodenumber(void)
{
	__vga_getchipset();
	return lastmodenumber;
}


int __vga_addmode(int xdim, int ydim, int cols, int xbytes, int bytespp)
{
	int i;

	for (i=0; i <= lastmodenumber; ++i)
	   if ( infotable[i].xdim   == xdim &&
		infotable[i].ydim   == ydim &&
		infotable[i].colors == cols &&
		infotable[i].bytesperpixel == bytespp &&
		infotable[i].xbytes == xbytes  )
	      return i;
	if (lastmodenumber >= MAX_MODES-1)
	  return -1; /* no more space available */
	++lastmodenumber;
	infotable[lastmodenumber].xdim   = xdim;
	infotable[lastmodenumber].ydim   = ydim;
	infotable[lastmodenumber].colors = cols;
	infotable[lastmodenumber].xbytes = xbytes;
	infotable[lastmodenumber].bytesperpixel = bytespp;
	return lastmodenumber;
}


int vga_setcolor(int color)
{
    switch (CI.colors) {
	case  2: if (color != 0)
		   color = 15;
	case 16: /* update set/reset register */
	    	 port_out(0x00, GRA_I ); 
	         port_out((color&15), GRA_D );
		 break;
	default: COL = color;
                 break;
    }

    return 0;
}


int vga_screenoff()
{
    /* Dunno how to support that in MACH32 modes...*/
    /* But it is used to switch the VGA off for better
       mem access performance...*/
    /* turn off screen for faster VGA memory acces */
    if (CHIPSET != EGA) {
	    port_out(0x01, SEQ_I); 		  
	    port_out(port_in(SEQ_D)|0x20, SEQ_D);
    }
    /* Disable video output */
#ifdef DISABLE_VIDEO_OUTPUT
    port_in(IS1_R);
    __vga_delay();
    port_out(0x00, ATT_IW);
#endif

    SCREENON = 0;
    return 0;
}


int vga_screenon()
{
    /* Dunno how to support that in MACH32 modes...*/
    if ((__svgalib_chipset == MACH32) && SVGAMODE(CM))
	{
	/* Warning! This is also used for faking of vgapal.c!*/
	/* Not needed anymore... had to hack vgapal.c anyway..*/
	/* Let it in anyway to show that std-vga is disabled */

	/* Anyway force Mach32 to ATI mode (used by setmode)*/
	outw(0x4AEE,inw(0x4AEE)|1);
	SCREENON = 0;
	return 0;
	}
    /* turn screen back on */
    if (CHIPSET != EGA) {
	    port_out(0x01, SEQ_I); 		  
	    port_out(port_in(SEQ_D)&0xDF, SEQ_D);
    }

/* #ifdef DISABLE_VIDEO_OUTPUT */
    /* enable video output */
    port_in(IS1_R);
    __vga_delay();
    port_out(0x20, ATT_IW);
/* #endif */

    SCREENON = 1;
    return 0;
}


int vga_getxdim()
{
    return CI.xdim;
}


int vga_getydim()
{
    return CI.ydim;
}


int vga_getcolors()
{
    return CI.colors;
}

int vga_white(void)
{
    switch (CI.colors) {
      case     2:
      case    16:
      case   256: return 15;
      case 1<<15: return 32767;
      case 1<<16: return 65535;
      case 1<<24: return (1<<24)-1;
    }
    return CI.colors-1;
}
  
int vga_claimvideomemory( int m ) {
	vga_modeinfo *modeinfo;
	int cardmemory;

	modeinfo = vga_getmodeinfo(CM);
	if (m < VMEM)
		return 0;
	if (modeinfo->colors == 16)
		cardmemory = modeinfo->maxpixels / 2;
	else
		cardmemory = (modeinfo->maxpixels * modeinfo->bytesperpixel
			+ 2) & 0xffff0000;
	/* maxpixels * bytesperpixel can be 2 less than video memory in */
	/* 3 byte-per-pixel modes; assume memory is multiple of 64K */
	if (m > cardmemory)
		return -1;
	VMEM = m;
	return 0;
}

int vga_setmodeX(void)
{
  switch (CM) {
    case TEXT : 
/*    case G320x200x256: */
    case G320x240x256:
    case G320x400x256:
    case G360x480x256: return 0;
  }
  if (CI.colors == 256 && VMEM < 256*1024) {
    port_out( 4, SEQ_I);                              /* switch from linear to plane memory */
    port_out( (port_in(SEQ_D) & 0xF7) | 0x04, SEQ_D);
    port_out( 0x14, CRT_I);                           /* switch double word mode off */
    port_out( (port_in(CRT_D) & 0xBF), CRT_D);
    port_out( 0x17, CRT_I); 
    port_out( (port_in(CRT_D) | 0x40), CRT_D);
    CI.xbytes = CI.xdim / 4;
    vga_setpage(0);
    MODEX = 1;
    return 1;
  }
  return 0;
}


static int saved_page;
static int saved_logicalwidth;
static int saved_displaystart;
static int saved_modeX;

static void savestate() {
	int i;

	vga_screenoff();

	savepalette(graph_red, graph_green, graph_blue);

	saved_page = currentpage;
	saved_logicalwidth = currentlogicalwidth;
	saved_displaystart = currentdisplaystart;
	saved_modeX = MODEX;

	if (CM == G320x200x256 && VMEM <= 65536) {
		/* 320x200x256 is a special case; only 64K is addressable */
		/* (unless more has been claimed, in which case we assume */
		/* SVGA bank-switching) */
        	if ((graph_buf = malloc(GRAPH_SIZE)) == NULL) {
        		printf("Cannot allocate memory for VGA state\n");
        		vga_setmode(TEXT);
			exit (-1);
	        }
	        memcpy(graph_buf, GM, GRAPH_SIZE);
	}
	else
	if (MODEX || CM==G800x600x16 || (STDVGAMODE(CM) && CM != G320x200x256)) {
		/* for planar VGA modes, save the full 256K */
		vga_chipsetfunctions[CHIPSET_SETMODE](GPLANE16);
        	if ((graph_buf = malloc(4*GRAPH_SIZE)) == NULL) {
        		printf("Cannot allocate memory for VGA state\n");
        		vga_setmode(TEXT);
			exit (-1);
	        }

	        for (i = 0; i < 4; i++) {
        		/* save plane i */
	            	port_out(0x04, GRA_I); 
           		port_out(i, GRA_D); 
            		memcpy(graph_buf + i*GRAPH_SIZE, GM, GRAPH_SIZE);
		}
	}
	else
	if (CI.colors == 16) {
		int page, size, sbytes;
		unsigned char *sp;

		size = VMEM;
		if ((graph_buf = malloc(4*size)) == NULL) {
        		printf("Cannot allocate memory for VGA state\n");
        		vga_setmode(TEXT);
			exit(-1);
	        }

		sp = graph_buf;
		for (page = 0; size > 0; ++page) {
		    vga_setpage(page);
		    sbytes = (size>GRAPH_SIZE) ? GRAPH_SIZE : size;
		    for (i = 0; i < 4; i++) {
        		/* save plane i */
			port_out(0x04, GRA_I); 
			port_out(i, GRA_D);
            		memcpy(sp, GM, sbytes);
			sp += sbytes;
		    }
		    size -= sbytes;
		}
	}
	else {	/* SVGA, and SVGA 320x200x256 if videomemoryused > 65536 */
        	int size;
        	int page;

		size = VMEM;

		#ifdef DEBUG
		printf("Saving %dK of video memory.\n", (size + 2) / 1024);
		#endif
        	if ((graph_buf = malloc(size)) == NULL) {
        		printf("Cannot allocate memory for SVGA state.\n");
        		vga_setmode(TEXT);
			exit(-1);
	        }
	        page = 0;
	        while (size >= 65536) {
	        	vga_setpage(page);
	        	memcpy(graph_buf + page * 65536, GM, 65536);
	        	page++;
	        	size -= 65536;
	        }
	        if (size > 0) {
	        	vga_setpage(page);
	        	memcpy(graph_buf + page * 65536, GM, size);
	        }
	}
}

static void restorestate() {
	int i;

	vga_screenoff();

	if (saved_modeX)
		vga_setmodeX();

	restorepalette(graph_red, graph_green, graph_blue);

	if (CM == G320x200x256 && VMEM <= 65536) {
		memcpy(GM, graph_buf, 65536);
	}
	else
	if (MODEX || CM==G800x600x16 || (STDVGAMODE(CM) && CM != G320x200x256)) {
		int setresetreg, planereg;
		/* disable Set/Reset Register */
    		port_out(0x01, GRA_I); 
    		setresetreg = inb(GRA_D);
	    	port_out(0x00, GRA_D);   
	    	outb(SEQ_I, 0x02);
	    	planereg = inb(SEQ_D);

	        for (i = 0; i < 4; i++) {
        		/* restore plane i */
	    		port_out(0x02, SEQ_I ); 
    			port_out(1<<i, SEQ_D );
            		memcpy(GM, graph_buf + i*GRAPH_SIZE,GRAPH_SIZE);
		}
		outb(GRA_I, 0x01);
		outb(GRA_D, setresetreg);
		outb(SEQ_I, 0x02);
		outb(SEQ_D, planereg);
	} else
	if (CI.colors == 16) {
		int page, size, rbytes;
		unsigned char *rp;
		int setresetreg, planereg;

		/* disable Set/Reset Register */
    		port_out(0x01, GRA_I); 
    		if (CHIPSET == EGA)
    			setresetreg = 0;
    		else
	    		setresetreg = inb(GRA_D);
	    	port_out(0x00, GRA_D);
		port_out(0x02, SEQ_I);
		if (CHIPSET == EGA)
			planereg = 0;
		else
		    	planereg = inb(SEQ_D);

		size = VMEM;
		rp = graph_buf;
		for (page=0; size > 0; ++page) {
		    vga_setpage(page);
		    rbytes = (size>GRAPH_SIZE) ? GRAPH_SIZE : size;
		    for (i = 0; i < 4; i++) {
        		/* save plane i */
			port_out(0x02, SEQ_I); 
			port_out(1<<i, SEQ_D);
            		memcpy(GM, rp, rbytes);
			rp += rbytes;
		    }
		    size -= rbytes;
		}

		outb(GRA_I, 0x01);
		outb(GRA_D, setresetreg);
		outb(SEQ_I, 0x02);
		outb(SEQ_D, planereg);
	}
	else {
/*        	vga_modeinfo *modeinfo; */
        	int size;
        	int page;
		size = VMEM;

		#ifdef DEBUG
		printf("Restoring %dK of video memory.\n", (size + 2) / 1024);
		#endif
	        page = 0;
	        while (size >= 65536) {
	        	vga_setpage(page);
	        	memcpy(GM, graph_buf + page * 65536, 65536);
	        	size -= 65536;
	        	page++;
	        }
	        if (size > 0) {
	        	vga_setpage(page);
	        	memcpy(GM, graph_buf + page * 65536, size);
	        }
	}
	
	if (saved_logicalwidth != CI.xbytes)
		vga_setlogicalwidth(saved_logicalwidth);
	if (saved_page != 0)
		vga_setpage(saved_page);
	if (saved_displaystart != 0)
		vga_setdisplaystart(saved_displaystart);

	vga_screenon();

	free(graph_buf);
}


int vga_getch()
{
	char c;

	if (CM == TEXT)
	    return -1;

	while ((read(tty0_fd, &c, 1) < 0) && (errno == EINTR));

	return c;
}

/* I have kept the slightly funny 'flip' terminology. */

static void vga_flipaway() {
	/* Leaving console. */
	flip_mode = CM;
	if (CM != TEXT) {
		/* Save state and go to textmode. */
		savestate();
		flip = 1;
		vga_setmode(TEXT);
		flip = 0;
	}
}

static void vga_flipback() {
	/* Entering console. */
        /* Hmmm... and how about unlocking anything someone else locked? */
	chipset_unlock();
	if (flip_mode != TEXT) {
		/* Restore graphics mode and state. */
		flip = 1;
		vga_setmode(flip_mode);
		flip = 0;
		restorestate();
	}
}

int vga_flip() {
	if (CM != TEXT) {		/* save state and go to textmode */
		savestate();
		flip_mode = CM;
		flip = 1;
		vga_setmode(TEXT);
		flip = 0;
	}
	else {				/* restore graphics mode and state */
		flip = 1;
		vga_setmode(flip_mode);
		flip = 0;
		restorestate();
	}
	return 0;
}


int vga_setflipchar(int c)
/* This function is obsolete. Retained for VGAlib compatibility. */
{
    flipchar = c;

    return 0;
}

void vga_setlogicalwidth( int w ) {
	chipsetfunctions[CHIPSET_SETLOGICALWIDTH](w);
	currentlogicalwidth = w;
}

void vga_setdisplaystart( int a ) {
	currentdisplaystart = a;
	if (CHIPSET != VGA && CHIPSET != EGA)
		if (MODEX || CI.colors == 16) {
			/* We are currently using a Mode X-like mode on a */
			/* SVGA card, use the standard VGA function */
			/* that works properly for Mode X. */
			/* Same goes for 16 color modes. */
			vga_chipsetfunctions[CHIPSET_SETDISPLAYSTART](a);
			return;
		}

	/* Call the regular display start function for the chipset */
	chipsetfunctions[CHIPSET_SETDISPLAYSTART](a);
}

void vga_bitblt( int srcaddr, int destaddr, int w, int h, int pitch ) {
	chipsetfunctions[CHIPSET_BITBLT](srcaddr, destaddr, w, h, pitch);
}

void vga_imageblt( void *srcaddr, int destaddr, int w, int h, int pitch ) {
	chipsetfunctions[CHIPSET_IMAGEBLT](srcaddr, destaddr, w, h, pitch);
}

void vga_fillblt( int destaddr, int w, int h, int pitch, int c ) {
	chipsetfunctions[CHIPSET_FILLBLT](destaddr, w, h, pitch, c);
}

void vga_hlinelistblt( int ymin, int n, int *xmin, int *xmax, int pitch,
int c ) {
	chipsetfunctions[CHIPSET_HLINELISTBLT](ymin, n, xmin, xmax, pitch, c);
}

/* Parse a string for options.. str is \0-terminated source,
   commands is an array of char ptrs (last one is NULL) containing commands
   to parse for. (if first char is ! case sensitive),
   func is called with ind the index of the detected command.
   func has to return the ptr to the next unhandled token returned by strtok(NULL," ").
   Use strtok(NULL," ") to get the next token from the file..
   mode is 1 when reading from conffile and 0 when parsing the env-vars. This is to
   allow disabling of dangerous (hardware damaging) options when reading the ENV-Vars
   of Joe user.
   Note: We use strtok, that is str is destroyed!*/
static void parse_string(char *str, char **commands, char *(*func)(int ind,int mode),int mode) {
	int index;
	register char *ptr,**curr;

	/*Pass one, delete comments,ensure only whitespace is ' '*/
	for(ptr=str;*ptr;ptr++) {
		if(*ptr=='#') {
			while(*ptr && (*ptr!='\n')) {
				*ptr++ =' ';
			}
			if(*ptr)
				*ptr=' ';
		}
		else if(isspace(*ptr)) {
			*ptr=' ';
		}
	}
	/*Pass two, parse commands*/
	ptr=strtok(str," ");
	while(ptr) {
		#ifdef DEBUG_CONF
		printf("Parsing: %s\n",ptr);
		#endif
		for(curr=commands, index=0; *curr; curr++,index++) {
			#ifdef DEBUG_CONF
			printf("Checking: %s\n",*curr);
			#endif
			if(**curr=='!') {
				if(!strcmp(*curr+1,ptr)) {
					ptr=(*func)(index,mode);
					break;
				}
			}
			else {
				if(!strcasecmp(*curr,ptr)) {
					ptr=(*func)(index,mode);
					break;
				}
			}
		}
		if(!*curr) /*unknow command*/
			ptr=strtok(NULL," ");	/* skip silently til' next command */
	}
}

static int allowoverride = 0;	/* Allow dangerous options in ENV-Var */

/* This is a service function for drivers. commands and func are as above,
   the config file and (afterwards) the contents of the environment variable
   SVGALIB_CONFIG are parsed..*/
void __svgalib_read_options(char **commands, char *(*func)(int ind,int mode)) {
/* Parse configuration file. */
	FILE *f;
	char *buf=NULL,*ptr;
	struct stat st;
	int i;

	st.st_size=0; /*safety for readenv*/

	f = fopen(SVGALIB_CONFIG_FILE, "r");
	if (f == NULL) {
		printf("svgalib: Configuration file %s not found.\n",
			SVGALIB_CONFIG_FILE);
		goto readenv;
	}
	fstat(fileno(f), &st);	/* Some error analysis may be fine here..*/
	buf = alloca(st.st_size+1); /* + a final \0 */
	fread(buf, 1, st.st_size, f);
	fclose(f);
	/*Erase any maybe embedded \0 */
	for(i=0,ptr=buf;i<st.st_size;i++,ptr++)	{
		if(!*ptr)
			*ptr=' ';
	}
	/*Trailing \0*/
	*ptr=0;
	/*parse config file*/
	parse_string(buf,commands,func,1);
	readenv:
	if(!(ptr=getenv("SVGALIB_CONFIG")))
		return;
	if(!(i=strlen(ptr)))
		return;
	if(i>st.st_size) /* Note that we allocated one more char...*/
		buf = alloca(i+1);
	strcpy(buf,ptr); /* Copy for safety and strtok!! */
	/*parse environment variable*/
	parse_string(buf,commands,func,allowoverride);
}
	
/* Configuration file, mouse interface, initialization. */

static int configfileread = 0;	/* Boolean. */

static char *vga_conf_commands[]={
	"mouse","monitor","!m","!M","chipset","overrideenable","!m0","!m1","!m2","!m3",
	"!m4","!m9", "!M0","!M1","!M2","!M3","!M4","!M5","!M6","nolinear",
	"linear","!C0","!C1","!C2","!C3","!C4","!C5","!C6","!C7","!C8","!C9",
	"!c0","!c1","monotext","colortext",
	NULL};

static char *conf_mousenames[]={
	"Microsoft","MouseSystems","MMSeries","Logitech","Busmouse",NULL};

static int check_digit(char *ptr, char *digits) {
	if(ptr==NULL)
		return 0;
	return strlen(ptr)==strspn(ptr,digits);
}

static char *process_option(int command,int mode) {
	static char digits[]=".0123456789";
	char *ptr,**tabptr,*ptb;
	int i,j;
	float f;

#ifdef DEBUG_CONF
	printf("command %d detected.\n",command);
#endif
	switch(command) {
		case 5:
			#ifdef DEBUG_CONF
			puts("Allow override");
			#endif
			if(mode)
				allowoverride=1;
			else	puts("Overrideenable denied. (Gee.. Do you think I'm that silly?)");
			break;
		case 0: /* mouse */
		case 2: /* m */
			ptr=strtok(NULL," ");
			if(ptr==NULL)
				goto inv_mouse;
			if(check_digit(ptr,digits+1)) { /* It is a number.. */
				i=atoi(ptr);
				if((i!=9)&&(i>4))
					goto inv_mouse;
				mouse_type=i;
			}
			else { /* parse for symbolic name.. */
				if(!strcasecmp(ptr,"none")) {
					mouse_type=9;
					break;
				}
				for(i=0,tabptr=conf_mousenames;*tabptr;tabptr++,i++) {
					if(!strcasecmp(ptr,*tabptr)) {
						mouse_type=i;
						goto leave;
					}
				}
				inv_mouse:
				printf("svgalib: Illegal mouse setting: {mouse|m} %s\n"
					"Correct usage: {mouse|m} mousetype\n"
					"where mousetype is one of 0, 1, 2, 3, 4, 9,\n",
					(ptr!=NULL)?ptr:"");
				for(tabptr=conf_mousenames;*tabptr;tabptr++) {
					printf("%s, ",*tabptr);
				}
				puts("or none.");
				return ptr;	/* Allow a second parse of str */
			}
			break;
		case 1: /* monitor */
		case 3: /* M */
			ptr=strtok(NULL," ");
			if( check_digit(ptr,digits+1) ) { /* It is an int.. */
				i=atoi(ptr);
				if(i<7) {
					if(!mode)
						goto mon_denie;
					__svgalib_monitortype = i;
				}
				else {
					f=i;
					goto monkhz;
				}
			}
			else if( check_digit(ptr,digits) ) { /* It is a float.. */
				f=atof(ptr);
				monkhz:
				if(!mode)
					goto mon_denie;
				if(f>=59.0)
					__svgalib_monitortype = 6;
				else if(f>=56.0)
					__svgalib_monitortype = 5;
				else if(f>=48.3)
					__svgalib_monitortype = 4;
				else if(f>=37.9)
					__svgalib_monitortype = 3;
				else if(f>=35.5)
					__svgalib_monitortype = 2;
				else if(f>=35.1)
					__svgalib_monitortype = 1;
				else
					__svgalib_monitortype = 0;
			}
			else {
				printf("svgalib: Illegal monitor setting: {monitor|M} %s\n"
					"Correct usage: {monitor|M} monitortype\n"
					"where monitortype is one of 0, 1, 2, 3, 4, 5, 6, or\n"
					"maximal horz. scan frequency in khz.\n"
					"Example: monitor 36.5\n",
					(ptr!=NULL)?ptr:"");
				return ptr;	/* Allow a second parse of str */
			}
			break;
		case 4: /* chipset */
			ptr=strtok(NULL," ");
			if(ptr==NULL) {
				puts("svgalib: Illegal chipset setting: no chipset given");
				goto chip_us;
			}
			/*First param is chipset*/
			for(i=0,tabptr=driver_names;*tabptr;tabptr++,i++) {
				if( (!strcasecmp(ptr,*tabptr)) && (chipsetfunctionslist[i]!=NULL) ) {
					ptr=strtok(NULL," ");
					if( check_digit(ptr,digits+1) ) {
						j=atoi(ptr);
						ptr=strtok(NULL," ");
						if( check_digit(ptr,digits+1) ) {
							if(mode)
								vga_setchipsetandfeatures(i,j,atoi(ptr));
							else
								{
								chipdenie:
							puts("chipset override from environment denied.");
								}
							return strtok(NULL," ");
						}
						else {
				puts("svgalib: Illegal chipset setting: memory is not a number");
							goto chip_us;
						}
					}
					if(mode)
						vga_setchipset(i);
					else puts("chipset override from environment denied.");
					return ptr;
				}
			}
			printf("svgalib: Illegal chipset setting: chipset %s\n",ptr);
			chip_us:
			puts("Correct usage: chipset driver [par1 par2]\n"
				"where driver is one of:");
			ptb="%s";
			for(i=0,tabptr=driver_names;*tabptr;tabptr++,i++) {
				if(chipsetfunctionslist[i]!=NULL) {
					printf(ptb,*tabptr);
					ptb=", %s";
				}
			}
			puts("\npar1 and par2 are river dependant integers.\n"
				"Example: Chipset VGA    or\n"
				"Chipset VGA 0 512");
			return ptr;
		case 6: /* oldstyle config: m0-m4 */
		case 7:
		case 8:
		case 9:
		case 10:
			mouse_type = command - 6;
			break;
		case 11: /* m9 */
			mouse_type = 9;
			break;
		case 12: /* oldstyle config: M0-M6 */
		case 13:
		case 14:
		case 15:
		case 16:
		case 17:
		case 18:
			if(!mode) {
				mon_denie:
				puts("Monitor setting from environment denied.");
				break;
			}
			__svgalib_monitortype = command - 12;
			break;
		case 19: /*nolinear*/
			modeinfo_mask&= ~CAPABLE_LINEAR;
			break;
		case 20: /*linear*/
			modeinfo_mask|= CAPABLE_LINEAR;
			break;
		case 21: /* oldstyle chipset C0 - C9 */
		case 22:
		case 23:
		case 24:
		case 25:
		case 26:
		case 27:
		case 28:
		case 29:
		case 30:
			if(!mode)
				goto chipdenie;
			vga_setchipset(command-21);
			break;
		case 31: /* c0-c1 color-text selection */
		case 32:
			if(!mode)
				{
				coltexdenie:
				puts("Color/mono text selection from environment denied.");
				break;
				}
			color_text=command-30;
			break;
		case 33:
		case 34:
			if(!mode)
				goto coltexdenie;
			color_text=command-32;
			break;
	}
leave:
return strtok(NULL," ");
}

static void readconfigfile() {
	if (configfileread)
		return;
	configfileread = 1;
	mouse_type = -1;
	__svgalib_monitortype = -1;
	__svgalib_read_options(vga_conf_commands,process_option);
	if (mouse_type == -1) {
		mouse_type = MOUSE_MICROSOFT;	/* Default. */
		puts("svgalib: Assuming Microsoft mouse.");
	}
	if (__svgalib_monitortype == -1)
		{
		/* Default monitor is low end SVGA/8514. */
		__svgalib_monitortype = MON1024_43I;
		puts("svgalib: Assuming low end SVGA/8514 monitor (35.5 KHz).");
	}
	#ifdef DEBUG_CONF
	printf("Mouse is: %d Monitor is: %d\n",mouse_type,__svgalib_monitortype);
	#endif
}

int vga_getmousetype() {
	readconfigfile();
	return mouse_type;
}

int vga_getmonitortype() {
	readconfigfile();
	return __svgalib_monitortype;
}

void vga_setmousesupport( int s ) {
	mouse_support = s;
}

void vga_lockvc() {
	lock_count++;
	if (flip)
		waitvtactive();
}

void vga_unlockvc() {
	if (mouse_support)	/* Not allowed. */
		return;
	if (--lock_count <= 0)
	{
		lock_count = 0;
		if (release_flag)
		{
			release_flag = 0;
			releasevt_signal(SIGUSR1);
		}
	}
}

void vga_runinbackground( int stat ) {
	runinbackground = stat;
}

int vga_oktowrite() {
	return oktowrite;
}

void vga_init() {
	readconfigfile();
	vga_hasmode(TEXT);	/* Force driver message and initialization. */
	setuid(getuid());	/* Don't need supervisor rights anymore. */
	setgid(getgid());
}
