Home | History | Annotate | Download | only in fbcon
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 /* Framebuffer console based SDL video driver implementation.
     25 */
     26 
     27 #include <stdio.h>
     28 #include <fcntl.h>
     29 #include <unistd.h>
     30 #include <sys/ioctl.h>
     31 #include <sys/mman.h>
     32 
     33 #ifndef HAVE_GETPAGESIZE
     34 #include <asm/page.h>		/* For definition of PAGE_SIZE */
     35 #endif
     36 
     37 #include <linux/vt.h>
     38 
     39 #include "SDL_video.h"
     40 #include "SDL_mouse.h"
     41 #include "../SDL_sysvideo.h"
     42 #include "../SDL_pixels_c.h"
     43 #include "../../events/SDL_events_c.h"
     44 #include "SDL_fbvideo.h"
     45 #include "SDL_fbmouse_c.h"
     46 #include "SDL_fbevents_c.h"
     47 #include "SDL_fb3dfx.h"
     48 #include "SDL_fbmatrox.h"
     49 #include "SDL_fbriva.h"
     50 
     51 /*#define FBCON_DEBUG*/
     52 
     53 #if defined(i386) && defined(FB_TYPE_VGA_PLANES)
     54 #define VGA16_FBCON_SUPPORT
     55 #include <sys/io.h>		/* For ioperm() */
     56 #ifndef FB_AUX_VGA_PLANES_VGA4
     57 #define FB_AUX_VGA_PLANES_VGA4	0
     58 #endif
     59 /*
     60 static inline void outb (unsigned char value, unsigned short port)
     61 {
     62   __asm__ __volatile__ ("outb %b0,%w1"::"a" (value), "Nd" (port));
     63 }
     64 */
     65 #endif /* FB_TYPE_VGA_PLANES */
     66 
     67 /* A list of video resolutions that we query for (sorted largest to smallest) */
     68 static const SDL_Rect checkres[] = {
     69 	{  0, 0, 1600, 1200 },		/* 16 bpp: 0x11E, or 286 */
     70 	{  0, 0, 1408, 1056 },		/* 16 bpp: 0x19A, or 410 */
     71 	{  0, 0, 1280, 1024 },		/* 16 bpp: 0x11A, or 282 */
     72 	{  0, 0, 1152,  864 },		/* 16 bpp: 0x192, or 402 */
     73 	{  0, 0, 1024,  768 },		/* 16 bpp: 0x117, or 279 */
     74 	{  0, 0,  960,  720 },		/* 16 bpp: 0x18A, or 394 */
     75 	{  0, 0,  800,  600 },		/* 16 bpp: 0x114, or 276 */
     76 	{  0, 0,  768,  576 },		/* 16 bpp: 0x182, or 386 */
     77 	{  0, 0,  720,  576 },		/* PAL */
     78 	{  0, 0,  720,  480 },		/* NTSC */
     79 	{  0, 0,  640,  480 },		/* 16 bpp: 0x111, or 273 */
     80 	{  0, 0,  640,  400 },		/*  8 bpp: 0x100, or 256 */
     81 	{  0, 0,  512,  384 },
     82 	{  0, 0,  320,  240 },
     83 	{  0, 0,  320,  200 }
     84 };
     85 static const struct {
     86 	int xres;
     87 	int yres;
     88 	int pixclock;
     89 	int left;
     90 	int right;
     91 	int upper;
     92 	int lower;
     93 	int hslen;
     94 	int vslen;
     95 	int sync;
     96 	int vmode;
     97 } vesa_timings[] = {
     98 #ifdef USE_VESA_TIMINGS	/* Only tested on Matrox Millenium I */
     99 	{  640,  400, 39771,  48, 16, 39,  8,  96, 2, 2, 0 },	/* 70 Hz */
    100 	{  640,  480, 39683,  48, 16, 33, 10,  96, 2, 0, 0 },	/* 60 Hz */
    101 	{  768,  576, 26101, 144, 16, 28,  6, 112, 4, 0, 0 },	/* 60 Hz */
    102 	{  800,  600, 24038, 144, 24, 28,  8, 112, 6, 0, 0 },	/* 60 Hz */
    103 	{  960,  720, 17686, 144, 24, 28,  8, 112, 4, 0, 0 },	/* 60 Hz */
    104 	{ 1024,  768, 15386, 160, 32, 30,  4, 128, 4, 0, 0 },	/* 60 Hz */
    105 	{ 1152,  864, 12286, 192, 32, 30,  4, 128, 4, 0, 0 },	/* 60 Hz */
    106 	{ 1280, 1024,  9369, 224, 32, 32,  4, 136, 4, 0, 0 },	/* 60 Hz */
    107 	{ 1408, 1056,  8214, 256, 40, 32,  5, 144, 5, 0, 0 },	/* 60 Hz */
    108 	{ 1600, 1200,/*?*/0, 272, 48, 32,  5, 152, 5, 0, 0 },	/* 60 Hz */
    109 #else
    110 	/* You can generate these timings from your XF86Config file using
    111 	   the 'modeline2fb' perl script included with the fbset package.
    112 	   These timings were generated for Matrox Millenium I, 15" monitor.
    113 	*/
    114 	{  320,  200, 79440,  16, 16, 20,  4,  48, 1, 0, 2 },	/* 70 Hz */
    115 	{  320,  240, 63492,  16, 16, 16,  4,  48, 2, 0, 2 },	/* 72 Hz */
    116 	{  512,  384, 49603,  48, 16, 16,  1,  64, 3, 0, 0 },	/* 78 Hz */
    117 	{  640,  400, 31746,  96, 32, 41,  1,  64, 3, 2, 0 },	/* 85 Hz */
    118 	{  640,  480, 31746, 120, 16, 16,  1,  64, 3, 0, 0 },	/* 75 Hz */
    119 	{  768,  576, 26101, 144, 16, 28,  6, 112, 4, 0, 0 },	/* 60 Hz */
    120 	{  800,  600, 20000,  64, 56, 23, 37, 120, 6, 3, 0 },	/* 72 Hz */
    121 	{  960,  720, 17686, 144, 24, 28,  8, 112, 4, 0, 0 },	/* 60 Hz */
    122 	{ 1024,  768, 13333, 144, 24, 29,  3, 136, 6, 0, 0 },	/* 70 Hz */
    123 	{ 1152,  864, 12286, 192, 32, 30,  4, 128, 4, 0, 0 },	/* 60 Hz */
    124 	{ 1280, 1024,  9369, 224, 32, 32,  4, 136, 4, 0, 0 },	/* 60 Hz */
    125 	{ 1408, 1056,  8214, 256, 40, 32,  5, 144, 5, 0, 0 },	/* 60 Hz */
    126 	{ 1600, 1200,/*?*/0, 272, 48, 32,  5, 152, 5, 0, 0 },	/* 60 Hz */
    127 #endif
    128 };
    129 
    130 /* Initialization/Query functions */
    131 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
    132 static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
    133 static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
    134 #ifdef VGA16_FBCON_SUPPORT
    135 static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
    136 #endif
    137 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
    138 static void FB_VideoQuit(_THIS);
    139 
    140 /* Hardware surface functions */
    141 static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
    142 static void FB_FreeHWSurfaces(_THIS);
    143 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
    144 static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
    145 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
    146 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
    147 static void FB_WaitVBL(_THIS);
    148 static void FB_WaitIdle(_THIS);
    149 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
    150 
    151 /* Internal palette functions */
    152 static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
    153                                   struct fb_var_screeninfo *vinfo);
    154 static void FB_RestorePalette(_THIS);
    155 
    156 static int SDL_getpagesize(void)
    157 {
    158 #ifdef HAVE_GETPAGESIZE
    159 	return getpagesize();
    160 #elif defined(PAGE_SIZE)
    161 	return PAGE_SIZE;
    162 #else
    163 #error Can not determine system page size.
    164 	return 4096;  /* this is what it USED to be in Linux... */
    165 #endif
    166 }
    167 
    168 
    169 /* Small wrapper for mmap() so we can play nicely with no-mmu hosts
    170  * (non-mmu hosts disallow the MAP_SHARED flag) */
    171 
    172 static void *do_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
    173 {
    174 	void *ret;
    175 	ret = mmap(start, length, prot, flags, fd, offset);
    176 	if ( ret == (char *)-1 && (flags & MAP_SHARED) ) {
    177 		ret = mmap(start, length, prot,
    178 		           (flags & ~MAP_SHARED) | MAP_PRIVATE, fd, offset);
    179 	}
    180 	return ret;
    181 }
    182 
    183 /* FB driver bootstrap functions */
    184 
    185 static int FB_Available(void)
    186 {
    187 	int console = -1;
    188 	/* Added check for /fb/0 (devfs) */
    189 	/* but - use environment variable first... if it fails, still check defaults */
    190 	int idx = 0;
    191 	const char *SDL_fbdevs[4] = { NULL, "/dev/fb0", "/dev/fb/0", NULL };
    192 
    193 	SDL_fbdevs[0] = SDL_getenv("SDL_FBDEV");
    194 	if( !SDL_fbdevs[0] )
    195 		idx++;
    196 	for( ; SDL_fbdevs[idx]; idx++ )
    197 	{
    198 		console = open(SDL_fbdevs[idx], O_RDWR, 0);
    199 		if ( console >= 0 ) {
    200 			close(console);
    201 			break;
    202 		}
    203 	}
    204 	return(console >= 0);
    205 }
    206 
    207 static void FB_DeleteDevice(SDL_VideoDevice *device)
    208 {
    209 	SDL_free(device->hidden);
    210 	SDL_free(device);
    211 }
    212 
    213 static SDL_VideoDevice *FB_CreateDevice(int devindex)
    214 {
    215 	SDL_VideoDevice *this;
    216 
    217 	/* Initialize all variables that we clean on shutdown */
    218 	this = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
    219 	if ( this ) {
    220 		SDL_memset(this, 0, (sizeof *this));
    221 		this->hidden = (struct SDL_PrivateVideoData *)
    222 				SDL_malloc((sizeof *this->hidden));
    223 	}
    224 	if ( (this == NULL) || (this->hidden == NULL) ) {
    225 		SDL_OutOfMemory();
    226 		if ( this ) {
    227 			SDL_free(this);
    228 		}
    229 		return(0);
    230 	}
    231 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    232 	wait_vbl = FB_WaitVBL;
    233 	wait_idle = FB_WaitIdle;
    234 	mouse_fd = -1;
    235 	keyboard_fd = -1;
    236 
    237 	/* Set the function pointers */
    238 	this->VideoInit = FB_VideoInit;
    239 	this->ListModes = FB_ListModes;
    240 	this->SetVideoMode = FB_SetVideoMode;
    241 	this->SetColors = FB_SetColors;
    242 	this->UpdateRects = NULL;
    243 	this->VideoQuit = FB_VideoQuit;
    244 	this->AllocHWSurface = FB_AllocHWSurface;
    245 	this->CheckHWBlit = NULL;
    246 	this->FillHWRect = NULL;
    247 	this->SetHWColorKey = NULL;
    248 	this->SetHWAlpha = NULL;
    249 	this->LockHWSurface = FB_LockHWSurface;
    250 	this->UnlockHWSurface = FB_UnlockHWSurface;
    251 	this->FlipHWSurface = FB_FlipHWSurface;
    252 	this->FreeHWSurface = FB_FreeHWSurface;
    253 	this->SetCaption = NULL;
    254 	this->SetIcon = NULL;
    255 	this->IconifyWindow = NULL;
    256 	this->GrabInput = NULL;
    257 	this->GetWMInfo = NULL;
    258 	this->InitOSKeymap = FB_InitOSKeymap;
    259 	this->PumpEvents = FB_PumpEvents;
    260 
    261 	this->free = FB_DeleteDevice;
    262 
    263 	return this;
    264 }
    265 
    266 VideoBootStrap FBCON_bootstrap = {
    267 	"fbcon", "Linux Framebuffer Console",
    268 	FB_Available, FB_CreateDevice
    269 };
    270 
    271 #define FB_MODES_DB	"/etc/fb.modes"
    272 
    273 static int read_fbmodes_line(FILE*f, char* line, int length)
    274 {
    275 	int blank;
    276 	char* c;
    277 	int i;
    278 
    279 	blank=0;
    280 	/* find a relevant line */
    281 	do
    282 	{
    283 		if (fgets(line,length,f)<=0)
    284 			return 0;
    285 		c=line;
    286 		while(((*c=='\t')||(*c==' '))&&(*c!=0))
    287 			c++;
    288 
    289 		if ((*c=='\n')||(*c=='#')||(*c==0))
    290 			blank=1;
    291 		else
    292 			blank=0;
    293 	}
    294 	while(blank);
    295 	/* remove whitespace at the begining of the string */
    296 	i=0;
    297 	do
    298 	{
    299 		line[i]=c[i];
    300 		i++;
    301 	}
    302 	while(c[i]!=0);
    303 	return 1;
    304 }
    305 
    306 static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
    307 {
    308 	char line[1024];
    309 	char option[256];
    310 
    311 	/* Find a "geometry" */
    312 	do {
    313 		if (read_fbmodes_line(f, line, sizeof(line))==0)
    314 			return 0;
    315 		if (SDL_strncmp(line,"geometry",8)==0)
    316 			break;
    317 	}
    318 	while(1);
    319 
    320 	SDL_sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres,
    321 			&vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
    322 	if (read_fbmodes_line(f, line, sizeof(line))==0)
    323 		return 0;
    324 
    325 	SDL_sscanf(line, "timings %d %d %d %d %d %d %d", &vinfo->pixclock,
    326 			&vinfo->left_margin, &vinfo->right_margin, &vinfo->upper_margin,
    327 			&vinfo->lower_margin, &vinfo->hsync_len, &vinfo->vsync_len);
    328 
    329 	vinfo->sync=0;
    330 	vinfo->vmode=FB_VMODE_NONINTERLACED;
    331 
    332 	/* Parse misc options */
    333 	do {
    334 		if (read_fbmodes_line(f, line, sizeof(line))==0)
    335 			return 0;
    336 
    337 		if (SDL_strncmp(line,"hsync",5)==0) {
    338 			SDL_sscanf(line,"hsync %s",option);
    339 			if (SDL_strncmp(option,"high",4)==0)
    340 				vinfo->sync |= FB_SYNC_HOR_HIGH_ACT;
    341 		}
    342 		else if (SDL_strncmp(line,"vsync",5)==0) {
    343 			SDL_sscanf(line,"vsync %s",option);
    344 			if (SDL_strncmp(option,"high",4)==0)
    345 				vinfo->sync |= FB_SYNC_VERT_HIGH_ACT;
    346 		}
    347 		else if (SDL_strncmp(line,"csync",5)==0) {
    348 			SDL_sscanf(line,"csync %s",option);
    349 			if (SDL_strncmp(option,"high",4)==0)
    350 				vinfo->sync |= FB_SYNC_COMP_HIGH_ACT;
    351 		}
    352 		else if (SDL_strncmp(line,"extsync",5)==0) {
    353 			SDL_sscanf(line,"extsync %s",option);
    354 			if (SDL_strncmp(option,"true",4)==0)
    355 				vinfo->sync |= FB_SYNC_EXT;
    356 		}
    357 		else if (SDL_strncmp(line,"laced",5)==0) {
    358 			SDL_sscanf(line,"laced %s",option);
    359 			if (SDL_strncmp(option,"true",4)==0)
    360 				vinfo->vmode |= FB_VMODE_INTERLACED;
    361 		}
    362 		else if (SDL_strncmp(line,"double",6)==0) {
    363 			SDL_sscanf(line,"double %s",option);
    364 			if (SDL_strncmp(option,"true",4)==0)
    365 				vinfo->vmode |= FB_VMODE_DOUBLE;
    366 		}
    367 	}
    368 	while(SDL_strncmp(line,"endmode",7)!=0);
    369 
    370 	return 1;
    371 }
    372 
    373 static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
    374                         int index, unsigned int *w, unsigned int *h)
    375 {
    376 	int mode_okay;
    377 
    378 	mode_okay = 0;
    379 	vinfo->bits_per_pixel = (index+1)*8;
    380 	vinfo->xres = *w;
    381 	vinfo->xres_virtual = *w;
    382 	vinfo->yres = *h;
    383 	vinfo->yres_virtual = *h;
    384 	vinfo->activate = FB_ACTIVATE_TEST;
    385 	if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, vinfo) == 0 ) {
    386 #ifdef FBCON_DEBUG
    387 		fprintf(stderr, "Checked mode %dx%d at %d bpp, got mode %dx%d at %d bpp\n", *w, *h, (index+1)*8, vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
    388 #endif
    389 		if ( (((vinfo->bits_per_pixel+7)/8)-1) == index ) {
    390 			*w = vinfo->xres;
    391 			*h = vinfo->yres;
    392 			mode_okay = 1;
    393 		}
    394 	}
    395 	return mode_okay;
    396 }
    397 
    398 static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
    399 {
    400 	SDL_Rect *mode;
    401 	int i;
    402 	int next_mode;
    403 
    404 	/* Check to see if we already have this mode */
    405 	if ( SDL_nummodes[index] > 0 ) {
    406 		mode = SDL_modelist[index][SDL_nummodes[index]-1];
    407 		if ( (mode->w == w) && (mode->h == h) ) {
    408 #ifdef FBCON_DEBUG
    409 			fprintf(stderr, "We already have mode %dx%d at %d bytes per pixel\n", w, h, index+1);
    410 #endif
    411 			return(0);
    412 		}
    413 	}
    414 
    415 	/* Only allow a mode if we have a valid timing for it */
    416 	if ( check_timings ) {
    417 		int found_timing = 0;
    418 		for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
    419 			if ( (w == vesa_timings[i].xres) &&
    420 			     (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
    421 				found_timing = 1;
    422 				break;
    423 			}
    424 		}
    425 		if ( !found_timing ) {
    426 #ifdef FBCON_DEBUG
    427 			fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
    428 #endif
    429 			return(0);
    430 		}
    431 	}
    432 
    433 	/* Set up the new video mode rectangle */
    434 	mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
    435 	if ( mode == NULL ) {
    436 		SDL_OutOfMemory();
    437 		return(-1);
    438 	}
    439 	mode->x = 0;
    440 	mode->y = 0;
    441 	mode->w = w;
    442 	mode->h = h;
    443 #ifdef FBCON_DEBUG
    444 	fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
    445 #endif
    446 
    447 	/* Allocate the new list of modes, and fill in the new mode */
    448 	next_mode = SDL_nummodes[index];
    449 	SDL_modelist[index] = (SDL_Rect **)
    450 	       SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *));
    451 	if ( SDL_modelist[index] == NULL ) {
    452 		SDL_OutOfMemory();
    453 		SDL_nummodes[index] = 0;
    454 		SDL_free(mode);
    455 		return(-1);
    456 	}
    457 	SDL_modelist[index][next_mode] = mode;
    458 	SDL_modelist[index][next_mode+1] = NULL;
    459 	SDL_nummodes[index]++;
    460 
    461 	return(0);
    462 }
    463 
    464 static int cmpmodes(const void *va, const void *vb)
    465 {
    466     const SDL_Rect *a = *(const SDL_Rect**)va;
    467     const SDL_Rect *b = *(const SDL_Rect**)vb;
    468     if ( a->h == b->h )
    469         return b->w - a->w;
    470     else
    471         return b->h - a->h;
    472 }
    473 
    474 static void FB_SortModes(_THIS)
    475 {
    476 	int i;
    477 	for ( i=0; i<NUM_MODELISTS; ++i ) {
    478 		if ( SDL_nummodes[i] > 0 ) {
    479 			SDL_qsort(SDL_modelist[i], SDL_nummodes[i], sizeof *SDL_modelist[i], cmpmodes);
    480 		}
    481 	}
    482 }
    483 
    484 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
    485 {
    486 	const int pagesize = SDL_getpagesize();
    487 	struct fb_fix_screeninfo finfo;
    488 	struct fb_var_screeninfo vinfo;
    489 	int i, j;
    490 	int current_index;
    491 	unsigned int current_w;
    492 	unsigned int current_h;
    493 	const char *SDL_fbdev;
    494 	FILE *modesdb;
    495 
    496 	/* Initialize the library */
    497 	SDL_fbdev = SDL_getenv("SDL_FBDEV");
    498 	if ( SDL_fbdev == NULL ) {
    499 		SDL_fbdev = "/dev/fb0";
    500 	}
    501 	console_fd = open(SDL_fbdev, O_RDWR, 0);
    502 	if ( console_fd < 0 ) {
    503 		SDL_SetError("Unable to open %s", SDL_fbdev);
    504 		return(-1);
    505 	}
    506 
    507 #if !SDL_THREADS_DISABLED
    508 	/* Create the hardware surface lock mutex */
    509 	hw_lock = SDL_CreateMutex();
    510 	if ( hw_lock == NULL ) {
    511 		SDL_SetError("Unable to create lock mutex");
    512 		FB_VideoQuit(this);
    513 		return(-1);
    514 	}
    515 #endif
    516 
    517 	/* Get the type of video hardware */
    518 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
    519 		SDL_SetError("Couldn't get console hardware info");
    520 		FB_VideoQuit(this);
    521 		return(-1);
    522 	}
    523 	switch (finfo.type) {
    524 		case FB_TYPE_PACKED_PIXELS:
    525 			/* Supported, no worries.. */
    526 			break;
    527 #ifdef VGA16_FBCON_SUPPORT
    528 		case FB_TYPE_VGA_PLANES:
    529 			/* VGA16 is supported, but that's it */
    530 			if ( finfo.type_aux == FB_AUX_VGA_PLANES_VGA4 ) {
    531 				if ( ioperm(0x3b4, 0x3df - 0x3b4 + 1, 1) < 0 ) {
    532 					SDL_SetError("No I/O port permissions");
    533 					FB_VideoQuit(this);
    534 					return(-1);
    535 				}
    536 				this->SetVideoMode = FB_SetVGA16Mode;
    537 				break;
    538 			}
    539 			/* Fall through to unsupported case */
    540 #endif /* VGA16_FBCON_SUPPORT */
    541 		default:
    542 			SDL_SetError("Unsupported console hardware");
    543 			FB_VideoQuit(this);
    544 			return(-1);
    545 	}
    546 	switch (finfo.visual) {
    547 		case FB_VISUAL_TRUECOLOR:
    548 		case FB_VISUAL_PSEUDOCOLOR:
    549 		case FB_VISUAL_STATIC_PSEUDOCOLOR:
    550 		case FB_VISUAL_DIRECTCOLOR:
    551 			break;
    552 		default:
    553 			SDL_SetError("Unsupported console hardware");
    554 			FB_VideoQuit(this);
    555 			return(-1);
    556 	}
    557 
    558 	/* Check if the user wants to disable hardware acceleration */
    559 	{ const char *fb_accel;
    560 		fb_accel = SDL_getenv("SDL_FBACCEL");
    561 		if ( fb_accel ) {
    562 			finfo.accel = SDL_atoi(fb_accel);
    563 		}
    564 	}
    565 
    566 	/* Memory map the device, compensating for buggy PPC mmap() */
    567 	mapped_offset = (((long)finfo.smem_start) -
    568 	                (((long)finfo.smem_start)&~(pagesize-1)));
    569 	mapped_memlen = finfo.smem_len+mapped_offset;
    570 	mapped_mem = do_mmap(NULL, mapped_memlen,
    571 	                  PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
    572 	if ( mapped_mem == (char *)-1 ) {
    573 		SDL_SetError("Unable to memory map the video hardware");
    574 		mapped_mem = NULL;
    575 		FB_VideoQuit(this);
    576 		return(-1);
    577 	}
    578 
    579 	/* Determine the current screen depth */
    580 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
    581 		SDL_SetError("Couldn't get console pixel format");
    582 		FB_VideoQuit(this);
    583 		return(-1);
    584 	}
    585 	vformat->BitsPerPixel = vinfo.bits_per_pixel;
    586 	if ( vformat->BitsPerPixel < 8 ) {
    587 		/* Assuming VGA16, we handle this via a shadow framebuffer */
    588 		vformat->BitsPerPixel = 8;
    589 	}
    590 	for ( i=0; i<vinfo.red.length; ++i ) {
    591 		vformat->Rmask <<= 1;
    592 		vformat->Rmask |= (0x00000001<<vinfo.red.offset);
    593 	}
    594 	for ( i=0; i<vinfo.green.length; ++i ) {
    595 		vformat->Gmask <<= 1;
    596 		vformat->Gmask |= (0x00000001<<vinfo.green.offset);
    597 	}
    598 	for ( i=0; i<vinfo.blue.length; ++i ) {
    599 		vformat->Bmask <<= 1;
    600 		vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
    601 	}
    602 	saved_vinfo = vinfo;
    603 
    604 	/* Save hardware palette, if needed */
    605 	FB_SavePalette(this, &finfo, &vinfo);
    606 
    607 	/* If the I/O registers are available, memory map them so we
    608 	   can take advantage of any supported hardware acceleration.
    609 	 */
    610 	vinfo.accel_flags = 0;	/* Temporarily reserve registers */
    611 	ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo);
    612 	if ( finfo.accel && finfo.mmio_len ) {
    613 		mapped_iolen = finfo.mmio_len;
    614 		mapped_io = do_mmap(NULL, mapped_iolen, PROT_READ|PROT_WRITE,
    615 		                 MAP_SHARED, console_fd, mapped_memlen);
    616 		if ( mapped_io == (char *)-1 ) {
    617 			/* Hmm, failed to memory map I/O registers */
    618 			mapped_io = NULL;
    619 		}
    620 	}
    621 
    622 	/* Query for the list of available video modes */
    623 	current_w = vinfo.xres;
    624 	current_h = vinfo.yres;
    625 	current_index = ((vinfo.bits_per_pixel+7)/8)-1;
    626 	modesdb = fopen(FB_MODES_DB, "r");
    627 	for ( i=0; i<NUM_MODELISTS; ++i ) {
    628 		SDL_nummodes[i] = 0;
    629 		SDL_modelist[i] = NULL;
    630 	}
    631 	if ( SDL_getenv("SDL_FB_BROKEN_MODES") != NULL ) {
    632 		FB_AddMode(this, current_index, current_w, current_h, 0);
    633 	} else if(modesdb) {
    634 		while ( read_fbmodes_mode(modesdb, &vinfo) ) {
    635 			for ( i=0; i<NUM_MODELISTS; ++i ) {
    636 				unsigned int w, h;
    637 
    638 				/* See if we are querying for the current mode */
    639 				w = vinfo.xres;
    640 				h = vinfo.yres;
    641 				if ( i == current_index ) {
    642 					if ( (current_w > w) || (current_h > h) ) {
    643 						/* Only check once */
    644 						FB_AddMode(this, i, current_w, current_h, 0);
    645 						current_index = -1;
    646 					}
    647 				}
    648 				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
    649 					FB_AddMode(this, i, w, h, 0);
    650 				}
    651 			}
    652 		}
    653 		fclose(modesdb);
    654 		FB_SortModes(this);
    655 	} else {
    656 		for ( i=0; i<NUM_MODELISTS; ++i ) {
    657 			for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
    658 				unsigned int w, h;
    659 
    660 				/* See if we are querying for the current mode */
    661 				w = checkres[j].w;
    662 				h = checkres[j].h;
    663 				if ( i == current_index ) {
    664 					if ( (current_w > w) || (current_h > h) ) {
    665 						/* Only check once */
    666 						FB_AddMode(this, i, current_w, current_h, 0);
    667 						current_index = -1;
    668 					}
    669 				}
    670 				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
    671 					FB_AddMode(this, i, w, h, 1);
    672 				}
    673 			}
    674 		}
    675 	}
    676 
    677 	/* Fill in our hardware acceleration capabilities */
    678 	this->info.current_w = current_w;
    679 	this->info.current_h = current_h;
    680 	this->info.wm_available = 0;
    681 	this->info.hw_available = 1;
    682 	this->info.video_mem = finfo.smem_len/1024;
    683 	if ( mapped_io ) {
    684 		switch (finfo.accel) {
    685 		    case FB_ACCEL_MATROX_MGA2064W:
    686 		    case FB_ACCEL_MATROX_MGA1064SG:
    687 		    case FB_ACCEL_MATROX_MGA2164W:
    688 		    case FB_ACCEL_MATROX_MGA2164W_AGP:
    689 		    case FB_ACCEL_MATROX_MGAG100:
    690 		    /*case FB_ACCEL_MATROX_MGAG200: G200 acceleration broken! */
    691 		    case FB_ACCEL_MATROX_MGAG400:
    692 #ifdef FBACCEL_DEBUG
    693 			printf("Matrox hardware accelerator!\n");
    694 #endif
    695 			FB_MatroxAccel(this, finfo.accel);
    696 			break;
    697 		    case FB_ACCEL_3DFX_BANSHEE:
    698 #ifdef FBACCEL_DEBUG
    699 			printf("3DFX hardware accelerator!\n");
    700 #endif
    701 			FB_3DfxAccel(this, finfo.accel);
    702 			break;
    703 		    case FB_ACCEL_NV3:
    704 		    case FB_ACCEL_NV4:
    705 #ifdef FBACCEL_DEBUG
    706 			printf("NVidia hardware accelerator!\n");
    707 #endif
    708 			FB_RivaAccel(this, finfo.accel);
    709 			break;
    710 		    default:
    711 #ifdef FBACCEL_DEBUG
    712 			printf("Unknown hardware accelerator.\n");
    713 #endif
    714 			break;
    715 		}
    716 	}
    717 
    718 	/* Enable mouse and keyboard support */
    719 	if ( FB_OpenKeyboard(this) < 0 ) {
    720 		FB_VideoQuit(this);
    721 		return(-1);
    722 	}
    723 	if ( FB_OpenMouse(this) < 0 ) {
    724 		const char *sdl_nomouse;
    725 
    726 		sdl_nomouse = SDL_getenv("SDL_NOMOUSE");
    727 		if ( ! sdl_nomouse ) {
    728 			SDL_SetError("Unable to open mouse");
    729 			FB_VideoQuit(this);
    730 			return(-1);
    731 		}
    732 	}
    733 
    734 	/* We're done! */
    735 	return(0);
    736 }
    737 
    738 static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
    739 {
    740 	return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]);
    741 }
    742 
    743 /* Various screen update functions available */
    744 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
    745 #ifdef VGA16_FBCON_SUPPORT
    746 static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
    747 #endif
    748 
    749 #ifdef FBCON_DEBUG
    750 static void print_vinfo(struct fb_var_screeninfo *vinfo)
    751 {
    752 	fprintf(stderr, "Printing vinfo:\n");
    753 	fprintf(stderr, "\txres: %d\n", vinfo->xres);
    754 	fprintf(stderr, "\tyres: %d\n", vinfo->yres);
    755 	fprintf(stderr, "\txres_virtual: %d\n", vinfo->xres_virtual);
    756 	fprintf(stderr, "\tyres_virtual: %d\n", vinfo->yres_virtual);
    757 	fprintf(stderr, "\txoffset: %d\n", vinfo->xoffset);
    758 	fprintf(stderr, "\tyoffset: %d\n", vinfo->yoffset);
    759 	fprintf(stderr, "\tbits_per_pixel: %d\n", vinfo->bits_per_pixel);
    760 	fprintf(stderr, "\tgrayscale: %d\n", vinfo->grayscale);
    761 	fprintf(stderr, "\tnonstd: %d\n", vinfo->nonstd);
    762 	fprintf(stderr, "\tactivate: %d\n", vinfo->activate);
    763 	fprintf(stderr, "\theight: %d\n", vinfo->height);
    764 	fprintf(stderr, "\twidth: %d\n", vinfo->width);
    765 	fprintf(stderr, "\taccel_flags: %d\n", vinfo->accel_flags);
    766 	fprintf(stderr, "\tpixclock: %d\n", vinfo->pixclock);
    767 	fprintf(stderr, "\tleft_margin: %d\n", vinfo->left_margin);
    768 	fprintf(stderr, "\tright_margin: %d\n", vinfo->right_margin);
    769 	fprintf(stderr, "\tupper_margin: %d\n", vinfo->upper_margin);
    770 	fprintf(stderr, "\tlower_margin: %d\n", vinfo->lower_margin);
    771 	fprintf(stderr, "\thsync_len: %d\n", vinfo->hsync_len);
    772 	fprintf(stderr, "\tvsync_len: %d\n", vinfo->vsync_len);
    773 	fprintf(stderr, "\tsync: %d\n", vinfo->sync);
    774 	fprintf(stderr, "\tvmode: %d\n", vinfo->vmode);
    775 	fprintf(stderr, "\tred: %d/%d\n", vinfo->red.length, vinfo->red.offset);
    776 	fprintf(stderr, "\tgreen: %d/%d\n", vinfo->green.length, vinfo->green.offset);
    777 	fprintf(stderr, "\tblue: %d/%d\n", vinfo->blue.length, vinfo->blue.offset);
    778 	fprintf(stderr, "\talpha: %d/%d\n", vinfo->transp.length, vinfo->transp.offset);
    779 }
    780 static void print_finfo(struct fb_fix_screeninfo *finfo)
    781 {
    782 	fprintf(stderr, "Printing finfo:\n");
    783 	fprintf(stderr, "\tsmem_start = %p\n", (char *)finfo->smem_start);
    784 	fprintf(stderr, "\tsmem_len = %d\n", finfo->smem_len);
    785 	fprintf(stderr, "\ttype = %d\n", finfo->type);
    786 	fprintf(stderr, "\ttype_aux = %d\n", finfo->type_aux);
    787 	fprintf(stderr, "\tvisual = %d\n", finfo->visual);
    788 	fprintf(stderr, "\txpanstep = %d\n", finfo->xpanstep);
    789 	fprintf(stderr, "\typanstep = %d\n", finfo->ypanstep);
    790 	fprintf(stderr, "\tywrapstep = %d\n", finfo->ywrapstep);
    791 	fprintf(stderr, "\tline_length = %d\n", finfo->line_length);
    792 	fprintf(stderr, "\tmmio_start = %p\n", (char *)finfo->mmio_start);
    793 	fprintf(stderr, "\tmmio_len = %d\n", finfo->mmio_len);
    794 	fprintf(stderr, "\taccel = %d\n", finfo->accel);
    795 }
    796 #endif
    797 
    798 static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
    799 {
    800 	int matched;
    801 	FILE *modesdb;
    802 	struct fb_var_screeninfo cinfo;
    803 
    804 	matched = 0;
    805 	modesdb = fopen(FB_MODES_DB, "r");
    806 	if ( modesdb ) {
    807 		/* Parse the mode definition file */
    808 		while ( read_fbmodes_mode(modesdb, &cinfo) ) {
    809 			if ( (vinfo->xres == cinfo.xres && vinfo->yres == cinfo.yres) &&
    810 			     (!matched || (vinfo->bits_per_pixel == cinfo.bits_per_pixel)) ) {
    811 				vinfo->pixclock = cinfo.pixclock;
    812 				vinfo->left_margin = cinfo.left_margin;
    813 				vinfo->right_margin = cinfo.right_margin;
    814 				vinfo->upper_margin = cinfo.upper_margin;
    815 				vinfo->lower_margin = cinfo.lower_margin;
    816 				vinfo->hsync_len = cinfo.hsync_len;
    817 				vinfo->vsync_len = cinfo.vsync_len;
    818 				if ( matched ) {
    819 					break;
    820 				}
    821 				matched = 1;
    822 			}
    823 		}
    824 		fclose(modesdb);
    825 	}
    826 	return(matched);
    827 }
    828 
    829 static int choose_vesa_mode(struct fb_var_screeninfo *vinfo)
    830 {
    831 	int matched;
    832 	int i;
    833 
    834 	/* Check for VESA timings */
    835 	matched = 0;
    836 	for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
    837 		if ( (vinfo->xres == vesa_timings[i].xres) &&
    838 		     (vinfo->yres == vesa_timings[i].yres) ) {
    839 #ifdef FBCON_DEBUG
    840 			fprintf(stderr, "Using VESA timings for %dx%d\n",
    841 						vinfo->xres, vinfo->yres);
    842 #endif
    843 			if ( vesa_timings[i].pixclock ) {
    844 				vinfo->pixclock = vesa_timings[i].pixclock;
    845 			}
    846 			vinfo->left_margin = vesa_timings[i].left;
    847 			vinfo->right_margin = vesa_timings[i].right;
    848 			vinfo->upper_margin = vesa_timings[i].upper;
    849 			vinfo->lower_margin = vesa_timings[i].lower;
    850 			vinfo->hsync_len = vesa_timings[i].hslen;
    851 			vinfo->vsync_len = vesa_timings[i].vslen;
    852 			vinfo->sync = vesa_timings[i].sync;
    853 			vinfo->vmode = vesa_timings[i].vmode;
    854 			matched = 1;
    855 			break;
    856 		}
    857 	}
    858 	return(matched);
    859 }
    860 
    861 #ifdef VGA16_FBCON_SUPPORT
    862 static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current,
    863 				int width, int height, int bpp, Uint32 flags)
    864 {
    865 	struct fb_fix_screeninfo finfo;
    866 	struct fb_var_screeninfo vinfo;
    867 
    868 	/* Set the terminal into graphics mode */
    869 	if ( FB_EnterGraphicsMode(this) < 0 ) {
    870 		return(NULL);
    871 	}
    872 
    873 	/* Restore the original palette */
    874 	FB_RestorePalette(this);
    875 
    876 	/* Set the video mode and get the final screen format */
    877 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
    878 		SDL_SetError("Couldn't get console screen info");
    879 		return(NULL);
    880 	}
    881 	cache_vinfo = vinfo;
    882 #ifdef FBCON_DEBUG
    883 	fprintf(stderr, "Printing actual vinfo:\n");
    884 	print_vinfo(&vinfo);
    885 #endif
    886 	if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
    887 		return(NULL);
    888 	}
    889 	current->format->palette->ncolors = 16;
    890 
    891 	/* Get the fixed information about the console hardware.
    892 	   This is necessary since finfo.line_length changes.
    893 	 */
    894 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
    895 		SDL_SetError("Couldn't get console hardware info");
    896 		return(NULL);
    897 	}
    898 #ifdef FBCON_DEBUG
    899 	fprintf(stderr, "Printing actual finfo:\n");
    900 	print_finfo(&finfo);
    901 #endif
    902 
    903 	/* Save hardware palette, if needed */
    904 	FB_SavePalette(this, &finfo, &vinfo);
    905 
    906 	/* Set up the new mode framebuffer */
    907 	current->flags = SDL_FULLSCREEN;
    908 	current->w = vinfo.xres;
    909 	current->h = vinfo.yres;
    910 	current->pitch = current->w;
    911 	current->pixels = SDL_malloc(current->h*current->pitch);
    912 
    913 	/* Set the update rectangle function */
    914 	this->UpdateRects = FB_VGA16Update;
    915 
    916 	/* We're done */
    917 	return(current);
    918 }
    919 #endif /* VGA16_FBCON_SUPPORT */
    920 
    921 static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
    922 				int width, int height, int bpp, Uint32 flags)
    923 {
    924 	struct fb_fix_screeninfo finfo;
    925 	struct fb_var_screeninfo vinfo;
    926 	int i;
    927 	Uint32 Rmask;
    928 	Uint32 Gmask;
    929 	Uint32 Bmask;
    930 	char *surfaces_mem;
    931 	int surfaces_len;
    932 
    933 	/* Set the terminal into graphics mode */
    934 	if ( FB_EnterGraphicsMode(this) < 0 ) {
    935 		return(NULL);
    936 	}
    937 
    938 	/* Restore the original palette */
    939 	FB_RestorePalette(this);
    940 
    941 	/* Set the video mode and get the final screen format */
    942 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
    943 		SDL_SetError("Couldn't get console screen info");
    944 		return(NULL);
    945 	}
    946 #ifdef FBCON_DEBUG
    947 	fprintf(stderr, "Printing original vinfo:\n");
    948 	print_vinfo(&vinfo);
    949 #endif
    950 	if ( (vinfo.xres != width) || (vinfo.yres != height) ||
    951 	     (vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
    952 		vinfo.activate = FB_ACTIVATE_NOW;
    953 		vinfo.accel_flags = 0;
    954 		vinfo.bits_per_pixel = bpp;
    955 		vinfo.xres = width;
    956 		vinfo.xres_virtual = width;
    957 		vinfo.yres = height;
    958 		if ( flags & SDL_DOUBLEBUF ) {
    959 			vinfo.yres_virtual = height*2;
    960 		} else {
    961 			vinfo.yres_virtual = height;
    962 		}
    963 		vinfo.xoffset = 0;
    964 		vinfo.yoffset = 0;
    965 		vinfo.red.length = vinfo.red.offset = 0;
    966 		vinfo.green.length = vinfo.green.offset = 0;
    967 		vinfo.blue.length = vinfo.blue.offset = 0;
    968 		vinfo.transp.length = vinfo.transp.offset = 0;
    969 		if ( ! choose_fbmodes_mode(&vinfo) ) {
    970 			choose_vesa_mode(&vinfo);
    971 		}
    972 #ifdef FBCON_DEBUG
    973 		fprintf(stderr, "Printing wanted vinfo:\n");
    974 		print_vinfo(&vinfo);
    975 #endif
    976 		if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
    977 			vinfo.yres_virtual = height;
    978 			if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
    979 				SDL_SetError("Couldn't set console screen info");
    980 				return(NULL);
    981 			}
    982 		}
    983 	} else {
    984 		int maxheight;
    985 
    986 		/* Figure out how much video memory is available */
    987 		if ( flags & SDL_DOUBLEBUF ) {
    988 			maxheight = height*2;
    989 		} else {
    990 			maxheight = height;
    991 		}
    992 		if ( vinfo.yres_virtual > maxheight ) {
    993 			vinfo.yres_virtual = maxheight;
    994 		}
    995 	}
    996 	cache_vinfo = vinfo;
    997 #ifdef FBCON_DEBUG
    998 	fprintf(stderr, "Printing actual vinfo:\n");
    999 	print_vinfo(&vinfo);
   1000 #endif
   1001 	Rmask = 0;
   1002 	for ( i=0; i<vinfo.red.length; ++i ) {
   1003 		Rmask <<= 1;
   1004 		Rmask |= (0x00000001<<vinfo.red.offset);
   1005 	}
   1006 	Gmask = 0;
   1007 	for ( i=0; i<vinfo.green.length; ++i ) {
   1008 		Gmask <<= 1;
   1009 		Gmask |= (0x00000001<<vinfo.green.offset);
   1010 	}
   1011 	Bmask = 0;
   1012 	for ( i=0; i<vinfo.blue.length; ++i ) {
   1013 		Bmask <<= 1;
   1014 		Bmask |= (0x00000001<<vinfo.blue.offset);
   1015 	}
   1016 	if ( ! SDL_ReallocFormat(current, vinfo.bits_per_pixel,
   1017 	                                  Rmask, Gmask, Bmask, 0) ) {
   1018 		return(NULL);
   1019 	}
   1020 
   1021 	/* Get the fixed information about the console hardware.
   1022 	   This is necessary since finfo.line_length changes.
   1023 	 */
   1024 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
   1025 		SDL_SetError("Couldn't get console hardware info");
   1026 		return(NULL);
   1027 	}
   1028 
   1029 	/* Save hardware palette, if needed */
   1030 	FB_SavePalette(this, &finfo, &vinfo);
   1031 
   1032 	/* Set up the new mode framebuffer */
   1033 	current->flags = (SDL_FULLSCREEN|SDL_HWSURFACE);
   1034 	current->w = vinfo.xres;
   1035 	current->h = vinfo.yres;
   1036 	current->pitch = finfo.line_length;
   1037 	current->pixels = mapped_mem+mapped_offset;
   1038 
   1039 	/* Set up the information for hardware surfaces */
   1040 	surfaces_mem = (char *)current->pixels +
   1041 	                        vinfo.yres_virtual*current->pitch;
   1042 	surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));
   1043 	FB_FreeHWSurfaces(this);
   1044 	FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
   1045 
   1046 	/* Let the application know we have a hardware palette */
   1047 	switch (finfo.visual) {
   1048 	    case FB_VISUAL_PSEUDOCOLOR:
   1049 		current->flags |= SDL_HWPALETTE;
   1050 		break;
   1051 	    default:
   1052 		break;
   1053 	}
   1054 
   1055 	/* Update for double-buffering, if we can */
   1056 	if ( flags & SDL_DOUBLEBUF ) {
   1057 		if ( vinfo.yres_virtual == (height*2) ) {
   1058 			current->flags |= SDL_DOUBLEBUF;
   1059 			flip_page = 0;
   1060 			flip_address[0] = (char *)current->pixels;
   1061 			flip_address[1] = (char *)current->pixels+
   1062 			                          current->h*current->pitch;
   1063 			this->screen = current;
   1064 			FB_FlipHWSurface(this, current);
   1065 			this->screen = NULL;
   1066 		}
   1067 	}
   1068 
   1069 	/* Set the update rectangle function */
   1070 	this->UpdateRects = FB_DirectUpdate;
   1071 
   1072 	/* We're done */
   1073 	return(current);
   1074 }
   1075 
   1076 #ifdef FBCON_DEBUG
   1077 void FB_DumpHWSurfaces(_THIS)
   1078 {
   1079 	vidmem_bucket *bucket;
   1080 
   1081 	printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
   1082 	printf("\n");
   1083 	printf("         Base  Size\n");
   1084 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1085 		printf("Bucket:  %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
   1086 		if ( bucket->prev ) {
   1087 			if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
   1088 				printf("Warning, corrupt bucket list! (prev)\n");
   1089 			}
   1090 		} else {
   1091 			if ( bucket != &surfaces ) {
   1092 				printf("Warning, corrupt bucket list! (!prev)\n");
   1093 			}
   1094 		}
   1095 		if ( bucket->next ) {
   1096 			if ( bucket->next->base != bucket->base+bucket->size ) {
   1097 				printf("Warning, corrupt bucket list! (next)\n");
   1098 			}
   1099 		}
   1100 	}
   1101 	printf("\n");
   1102 }
   1103 #endif
   1104 
   1105 static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
   1106 {
   1107 	vidmem_bucket *bucket;
   1108 
   1109 	surfaces_memtotal = size;
   1110 	surfaces_memleft = size;
   1111 
   1112 	if ( surfaces_memleft > 0 ) {
   1113 		bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket));
   1114 		if ( bucket == NULL ) {
   1115 			SDL_OutOfMemory();
   1116 			return(-1);
   1117 		}
   1118 		bucket->prev = &surfaces;
   1119 		bucket->used = 0;
   1120 		bucket->dirty = 0;
   1121 		bucket->base = base;
   1122 		bucket->size = size;
   1123 		bucket->next = NULL;
   1124 	} else {
   1125 		bucket = NULL;
   1126 	}
   1127 
   1128 	surfaces.prev = NULL;
   1129 	surfaces.used = 1;
   1130 	surfaces.dirty = 0;
   1131 	surfaces.base = screen->pixels;
   1132 	surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
   1133 	surfaces.next = bucket;
   1134 	screen->hwdata = (struct private_hwdata *)&surfaces;
   1135 	return(0);
   1136 }
   1137 static void FB_FreeHWSurfaces(_THIS)
   1138 {
   1139 	vidmem_bucket *bucket, *freeable;
   1140 
   1141 	bucket = surfaces.next;
   1142 	while ( bucket ) {
   1143 		freeable = bucket;
   1144 		bucket = bucket->next;
   1145 		SDL_free(freeable);
   1146 	}
   1147 	surfaces.next = NULL;
   1148 }
   1149 
   1150 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface)
   1151 {
   1152 	vidmem_bucket *bucket;
   1153 	int size;
   1154 	int extra;
   1155 
   1156 /* Temporarily, we only allow surfaces the same width as display.
   1157    Some blitters require the pitch between two hardware surfaces
   1158    to be the same.  Others have interesting alignment restrictions.
   1159    Until someone who knows these details looks at the code...
   1160 */
   1161 if ( surface->pitch > SDL_VideoSurface->pitch ) {
   1162 	SDL_SetError("Surface requested wider than screen");
   1163 	return(-1);
   1164 }
   1165 surface->pitch = SDL_VideoSurface->pitch;
   1166 	size = surface->h * surface->pitch;
   1167 #ifdef FBCON_DEBUG
   1168 	fprintf(stderr, "Allocating bucket of %d bytes\n", size);
   1169 #endif
   1170 
   1171 	/* Quick check for available mem */
   1172 	if ( size > surfaces_memleft ) {
   1173 		SDL_SetError("Not enough video memory");
   1174 		return(-1);
   1175 	}
   1176 
   1177 	/* Search for an empty bucket big enough */
   1178 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1179 		if ( ! bucket->used && (size <= bucket->size) ) {
   1180 			break;
   1181 		}
   1182 	}
   1183 	if ( bucket == NULL ) {
   1184 		SDL_SetError("Video memory too fragmented");
   1185 		return(-1);
   1186 	}
   1187 
   1188 	/* Create a new bucket for left-over memory */
   1189 	extra = (bucket->size - size);
   1190 	if ( extra ) {
   1191 		vidmem_bucket *newbucket;
   1192 
   1193 #ifdef FBCON_DEBUG
   1194 	fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
   1195 #endif
   1196 		newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket));
   1197 		if ( newbucket == NULL ) {
   1198 			SDL_OutOfMemory();
   1199 			return(-1);
   1200 		}
   1201 		newbucket->prev = bucket;
   1202 		newbucket->used = 0;
   1203 		newbucket->base = bucket->base+size;
   1204 		newbucket->size = extra;
   1205 		newbucket->next = bucket->next;
   1206 		if ( bucket->next ) {
   1207 			bucket->next->prev = newbucket;
   1208 		}
   1209 		bucket->next = newbucket;
   1210 	}
   1211 
   1212 	/* Set the current bucket values and return it! */
   1213 	bucket->used = 1;
   1214 	bucket->size = size;
   1215 	bucket->dirty = 0;
   1216 #ifdef FBCON_DEBUG
   1217 	fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
   1218 #endif
   1219 	surfaces_memleft -= size;
   1220 	surface->flags |= SDL_HWSURFACE;
   1221 	surface->pixels = bucket->base;
   1222 	surface->hwdata = (struct private_hwdata *)bucket;
   1223 	return(0);
   1224 }
   1225 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
   1226 {
   1227 	vidmem_bucket *bucket, *freeable;
   1228 
   1229 	/* Look for the bucket in the current list */
   1230 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1231 		if ( bucket == (vidmem_bucket *)surface->hwdata ) {
   1232 			break;
   1233 		}
   1234 	}
   1235 	if ( bucket && bucket->used ) {
   1236 		/* Add the memory back to the total */
   1237 #ifdef DGA_DEBUG
   1238 	printf("Freeing bucket of %d bytes\n", bucket->size);
   1239 #endif
   1240 		surfaces_memleft += bucket->size;
   1241 
   1242 		/* Can we merge the space with surrounding buckets? */
   1243 		bucket->used = 0;
   1244 		if ( bucket->next && ! bucket->next->used ) {
   1245 #ifdef DGA_DEBUG
   1246 	printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
   1247 #endif
   1248 			freeable = bucket->next;
   1249 			bucket->size += bucket->next->size;
   1250 			bucket->next = bucket->next->next;
   1251 			if ( bucket->next ) {
   1252 				bucket->next->prev = bucket;
   1253 			}
   1254 			SDL_free(freeable);
   1255 		}
   1256 		if ( bucket->prev && ! bucket->prev->used ) {
   1257 #ifdef DGA_DEBUG
   1258 	printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
   1259 #endif
   1260 			freeable = bucket;
   1261 			bucket->prev->size += bucket->size;
   1262 			bucket->prev->next = bucket->next;
   1263 			if ( bucket->next ) {
   1264 				bucket->next->prev = bucket->prev;
   1265 			}
   1266 			SDL_free(freeable);
   1267 		}
   1268 	}
   1269 	surface->pixels = NULL;
   1270 	surface->hwdata = NULL;
   1271 }
   1272 
   1273 static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
   1274 {
   1275 	if ( switched_away ) {
   1276 		return -2; /* no hardware access */
   1277 	}
   1278 	if ( surface == this->screen ) {
   1279 		SDL_mutexP(hw_lock);
   1280 		if ( FB_IsSurfaceBusy(surface) ) {
   1281 			FB_WaitBusySurfaces(this);
   1282 		}
   1283 	} else {
   1284 		if ( FB_IsSurfaceBusy(surface) ) {
   1285 			FB_WaitBusySurfaces(this);
   1286 		}
   1287 	}
   1288 	return(0);
   1289 }
   1290 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
   1291 {
   1292 	if ( surface == this->screen ) {
   1293 		SDL_mutexV(hw_lock);
   1294 	}
   1295 }
   1296 
   1297 static void FB_WaitVBL(_THIS)
   1298 {
   1299 #ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
   1300 	ioctl(console_fd, FBIOWAITRETRACE, 0);
   1301 #endif
   1302 	return;
   1303 }
   1304 
   1305 static void FB_WaitIdle(_THIS)
   1306 {
   1307 	return;
   1308 }
   1309 
   1310 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
   1311 {
   1312 	if ( switched_away ) {
   1313 		return -2; /* no hardware access */
   1314 	}
   1315 
   1316 	/* Wait for vertical retrace and then flip display */
   1317 	cache_vinfo.yoffset = flip_page*surface->h;
   1318 	if ( FB_IsSurfaceBusy(this->screen) ) {
   1319 		FB_WaitBusySurfaces(this);
   1320 	}
   1321 	wait_vbl(this);
   1322 	if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
   1323 		SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
   1324 		return(-1);
   1325 	}
   1326 	flip_page = !flip_page;
   1327 
   1328 	surface->pixels = flip_address[flip_page];
   1329 	return(0);
   1330 }
   1331 
   1332 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
   1333 {
   1334 	/* The application is already updating the visible video memory */
   1335 	return;
   1336 }
   1337 
   1338 #ifdef VGA16_FBCON_SUPPORT
   1339 /* Code adapted with thanks from the XFree86 VGA16 driver! :) */
   1340 #define writeGr(index, value) \
   1341 outb(index, 0x3CE);           \
   1342 outb(value, 0x3CF);
   1343 #define writeSeq(index, value) \
   1344 outb(index, 0x3C4);            \
   1345 outb(value, 0x3C5);
   1346 
   1347 static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects)
   1348 {
   1349     SDL_Surface *screen;
   1350     int width, height, FBPitch, left, i, j, SRCPitch, phase;
   1351     register Uint32 m;
   1352     Uint8  s1, s2, s3, s4;
   1353     Uint32 *src, *srcPtr;
   1354     Uint8  *dst, *dstPtr;
   1355 
   1356     if ( switched_away ) {
   1357         return; /* no hardware access */
   1358     }
   1359 
   1360     screen = this->screen;
   1361     FBPitch = screen->w >> 3;
   1362     SRCPitch = screen->pitch >> 2;
   1363 
   1364     writeGr(0x03, 0x00);
   1365     writeGr(0x05, 0x00);
   1366     writeGr(0x01, 0x00);
   1367     writeGr(0x08, 0xFF);
   1368 
   1369     while(numrects--) {
   1370 	left = rects->x & ~7;
   1371         width = (rects->w + 7) >> 3;
   1372         height = rects->h;
   1373         src = (Uint32*)screen->pixels + (rects->y * SRCPitch) + (left >> 2);
   1374         dst = (Uint8*)mapped_mem + (rects->y * FBPitch) + (left >> 3);
   1375 
   1376 	if((phase = (long)dst & 3L)) {
   1377 	    phase = 4 - phase;
   1378 	    if(phase > width) phase = width;
   1379 	    width -= phase;
   1380 	}
   1381 
   1382         while(height--) {
   1383 	    writeSeq(0x02, 1 << 0);
   1384 	    dstPtr = dst;
   1385 	    srcPtr = src;
   1386 	    i = width;
   1387 	    j = phase;
   1388 	    while(j--) {
   1389 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1390  		*dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1391 		srcPtr += 2;
   1392 	    }
   1393 	    while(i >= 4) {
   1394 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1395  		s1 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1396 		m = (srcPtr[3] & 0x01010101) | ((srcPtr[2] & 0x01010101) << 4);
   1397  		s2 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1398 		m = (srcPtr[5] & 0x01010101) | ((srcPtr[4] & 0x01010101) << 4);
   1399  		s3 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1400 		m = (srcPtr[7] & 0x01010101) | ((srcPtr[6] & 0x01010101) << 4);
   1401  		s4 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1402 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1403 		srcPtr += 8;
   1404 		dstPtr += 4;
   1405 		i -= 4;
   1406 	    }
   1407 	    while(i--) {
   1408 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1409  		*dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1410 		srcPtr += 2;
   1411 	    }
   1412 
   1413 	    writeSeq(0x02, 1 << 1);
   1414 	    dstPtr = dst;
   1415 	    srcPtr = src;
   1416 	    i = width;
   1417 	    j = phase;
   1418 	    while(j--) {
   1419 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1420  		*dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1421 		srcPtr += 2;
   1422 	    }
   1423 	    while(i >= 4) {
   1424 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1425  		s1 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1426 		m = (srcPtr[3] & 0x02020202) | ((srcPtr[2] & 0x02020202) << 4);
   1427  		s2 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1428 		m = (srcPtr[5] & 0x02020202) | ((srcPtr[4] & 0x02020202) << 4);
   1429  		s3 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1430 		m = (srcPtr[7] & 0x02020202) | ((srcPtr[6] & 0x02020202) << 4);
   1431  		s4 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1432 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1433 		srcPtr += 8;
   1434 		dstPtr += 4;
   1435 		i -= 4;
   1436 	    }
   1437 	    while(i--) {
   1438 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1439  		*dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1440 		srcPtr += 2;
   1441 	    }
   1442 
   1443 	    writeSeq(0x02, 1 << 2);
   1444 	    dstPtr = dst;
   1445 	    srcPtr = src;
   1446 	    i = width;
   1447 	    j = phase;
   1448 	    while(j--) {
   1449 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1450  		*dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1451 		srcPtr += 2;
   1452 	    }
   1453 	    while(i >= 4) {
   1454 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1455  		s1 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1456 		m = (srcPtr[3] & 0x04040404) | ((srcPtr[2] & 0x04040404) << 4);
   1457  		s2 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1458 		m = (srcPtr[5] & 0x04040404) | ((srcPtr[4] & 0x04040404) << 4);
   1459  		s3 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1460 		m = (srcPtr[7] & 0x04040404) | ((srcPtr[6] & 0x04040404) << 4);
   1461  		s4 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1462 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1463 		srcPtr += 8;
   1464 		dstPtr += 4;
   1465 		i -= 4;
   1466 	    }
   1467 	    while(i--) {
   1468 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1469  		*dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1470 		srcPtr += 2;
   1471 	    }
   1472 
   1473 	    writeSeq(0x02, 1 << 3);
   1474 	    dstPtr = dst;
   1475 	    srcPtr = src;
   1476 	    i = width;
   1477 	    j = phase;
   1478 	    while(j--) {
   1479 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1480  		*dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1481 		srcPtr += 2;
   1482 	    }
   1483 	    while(i >= 4) {
   1484 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1485  		s1 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1486 		m = (srcPtr[3] & 0x08080808) | ((srcPtr[2] & 0x08080808) << 4);
   1487  		s2 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1488 		m = (srcPtr[5] & 0x08080808) | ((srcPtr[4] & 0x08080808) << 4);
   1489  		s3 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1490 		m = (srcPtr[7] & 0x08080808) | ((srcPtr[6] & 0x08080808) << 4);
   1491  		s4 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1492 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1493 		srcPtr += 8;
   1494 		dstPtr += 4;
   1495 		i -= 4;
   1496 	    }
   1497 	    while(i--) {
   1498 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1499  		*dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1500 		srcPtr += 2;
   1501 	    }
   1502 
   1503             dst += FBPitch;
   1504             src += SRCPitch;
   1505         }
   1506         rects++;
   1507     }
   1508 }
   1509 #endif /* VGA16_FBCON_SUPPORT */
   1510 
   1511 void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area)
   1512 {
   1513 	struct fb_cmap cmap;
   1514 
   1515 	cmap.start = 0;
   1516 	cmap.len = palette_len;
   1517 	cmap.red = &area[0*palette_len];
   1518 	cmap.green = &area[1*palette_len];
   1519 	cmap.blue = &area[2*palette_len];
   1520 	cmap.transp = NULL;
   1521 	ioctl(console_fd, FBIOGETCMAP, &cmap);
   1522 }
   1523 
   1524 void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area)
   1525 {
   1526 	struct fb_cmap cmap;
   1527 
   1528 	cmap.start = 0;
   1529 	cmap.len = palette_len;
   1530 	cmap.red = &area[0*palette_len];
   1531 	cmap.green = &area[1*palette_len];
   1532 	cmap.blue = &area[2*palette_len];
   1533 	cmap.transp = NULL;
   1534 	ioctl(console_fd, FBIOPUTCMAP, &cmap);
   1535 }
   1536 
   1537 static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
   1538                                   struct fb_var_screeninfo *vinfo)
   1539 {
   1540 	int i;
   1541 
   1542 	/* Save hardware palette, if needed */
   1543 	if ( finfo->visual == FB_VISUAL_PSEUDOCOLOR ) {
   1544 		saved_cmaplen = 1<<vinfo->bits_per_pixel;
   1545 		saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
   1546 		if ( saved_cmap != NULL ) {
   1547 			FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
   1548 		}
   1549 	}
   1550 
   1551 	/* Added support for FB_VISUAL_DIRECTCOLOR.
   1552 	   With this mode pixel information is passed through the palette...
   1553 	   Neat fading and gamma correction effects can be had by simply
   1554 	   fooling around with the palette instead of changing the pixel
   1555 	   values themselves... Very neat!
   1556 
   1557 	   Adam Meyerowitz 1/19/2000
   1558 	   ameyerow (at) optonline.com
   1559 	*/
   1560 	if ( finfo->visual == FB_VISUAL_DIRECTCOLOR ) {
   1561 		__u16 new_entries[3*256];
   1562 
   1563 		/* Save the colormap */
   1564 		saved_cmaplen = 256;
   1565 		saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
   1566 		if ( saved_cmap != NULL ) {
   1567 			FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
   1568 		}
   1569 
   1570 		/* Allocate new identity colormap */
   1571 		for ( i=0; i<256; ++i ) {
   1572 	      		new_entries[(0*256)+i] =
   1573 			new_entries[(1*256)+i] =
   1574 			new_entries[(2*256)+i] = (i<<8)|i;
   1575 		}
   1576 		FB_RestorePaletteFrom(this, 256, new_entries);
   1577 	}
   1578 }
   1579 
   1580 static void FB_RestorePalette(_THIS)
   1581 {
   1582 	/* Restore the original palette */
   1583 	if ( saved_cmap ) {
   1584 		FB_RestorePaletteFrom(this, saved_cmaplen, saved_cmap);
   1585 		SDL_free(saved_cmap);
   1586 		saved_cmap = NULL;
   1587 	}
   1588 }
   1589 
   1590 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
   1591 {
   1592 	int i;
   1593 	__u16 r[256];
   1594 	__u16 g[256];
   1595 	__u16 b[256];
   1596 	struct fb_cmap cmap;
   1597 
   1598 	/* Set up the colormap */
   1599 	for (i = 0; i < ncolors; i++) {
   1600 		r[i] = colors[i].r << 8;
   1601 		g[i] = colors[i].g << 8;
   1602 		b[i] = colors[i].b << 8;
   1603 	}
   1604 	cmap.start = firstcolor;
   1605 	cmap.len = ncolors;
   1606 	cmap.red = r;
   1607 	cmap.green = g;
   1608 	cmap.blue = b;
   1609 	cmap.transp = NULL;
   1610 
   1611 	if( (ioctl(console_fd, FBIOPUTCMAP, &cmap) < 0) ||
   1612 	    !(this->screen->flags & SDL_HWPALETTE) ) {
   1613 	        colors = this->screen->format->palette->colors;
   1614 		ncolors = this->screen->format->palette->ncolors;
   1615 		cmap.start = 0;
   1616 		cmap.len = ncolors;
   1617 		SDL_memset(r, 0, sizeof(r));
   1618 		SDL_memset(g, 0, sizeof(g));
   1619 		SDL_memset(b, 0, sizeof(b));
   1620 		if ( ioctl(console_fd, FBIOGETCMAP, &cmap) == 0 ) {
   1621 			for ( i=ncolors-1; i>=0; --i ) {
   1622 				colors[i].r = (r[i]>>8);
   1623 				colors[i].g = (g[i]>>8);
   1624 				colors[i].b = (b[i]>>8);
   1625 			}
   1626 		}
   1627 		return(0);
   1628 	}
   1629 	return(1);
   1630 }
   1631 
   1632 /* Note:  If we are terminated, this could be called in the middle of
   1633    another SDL video routine -- notably UpdateRects.
   1634 */
   1635 static void FB_VideoQuit(_THIS)
   1636 {
   1637 	int i, j;
   1638 
   1639 	if ( this->screen ) {
   1640 		/* Clear screen and tell SDL not to free the pixels */
   1641 		if ( this->screen->pixels && FB_InGraphicsMode(this) ) {
   1642 #if defined(__powerpc__) || defined(__ia64__)	/* SIGBUS when using SDL_memset() ?? */
   1643 			Uint8 *rowp = (Uint8 *)this->screen->pixels;
   1644 			int left = this->screen->pitch*this->screen->h;
   1645 			while ( left-- ) { *rowp++ = 0; }
   1646 #else
   1647 			SDL_memset(this->screen->pixels,0,this->screen->h*this->screen->pitch);
   1648 #endif
   1649 		}
   1650 		/* This test fails when using the VGA16 shadow memory */
   1651 		if ( ((char *)this->screen->pixels >= mapped_mem) &&
   1652 		     ((char *)this->screen->pixels < (mapped_mem+mapped_memlen)) ) {
   1653 			this->screen->pixels = NULL;
   1654 		}
   1655 	}
   1656 
   1657 	/* Clear the lock mutex */
   1658 	if ( hw_lock ) {
   1659 		SDL_DestroyMutex(hw_lock);
   1660 		hw_lock = NULL;
   1661 	}
   1662 
   1663 	/* Clean up defined video modes */
   1664 	for ( i=0; i<NUM_MODELISTS; ++i ) {
   1665 		if ( SDL_modelist[i] != NULL ) {
   1666 			for ( j=0; SDL_modelist[i][j]; ++j ) {
   1667 				SDL_free(SDL_modelist[i][j]);
   1668 			}
   1669 			SDL_free(SDL_modelist[i]);
   1670 			SDL_modelist[i] = NULL;
   1671 		}
   1672 	}
   1673 
   1674 	/* Clean up the memory bucket list */
   1675 	FB_FreeHWSurfaces(this);
   1676 
   1677 	/* Close console and input file descriptors */
   1678 	if ( console_fd > 0 ) {
   1679 		/* Unmap the video framebuffer and I/O registers */
   1680 		if ( mapped_mem ) {
   1681 			munmap(mapped_mem, mapped_memlen);
   1682 			mapped_mem = NULL;
   1683 		}
   1684 		if ( mapped_io ) {
   1685 			munmap(mapped_io, mapped_iolen);
   1686 			mapped_io = NULL;
   1687 		}
   1688 
   1689 		/* Restore the original video mode and palette */
   1690 		if ( FB_InGraphicsMode(this) ) {
   1691 			FB_RestorePalette(this);
   1692 			ioctl(console_fd, FBIOPUT_VSCREENINFO, &saved_vinfo);
   1693 		}
   1694 
   1695 		/* We're all done with the framebuffer */
   1696 		close(console_fd);
   1697 		console_fd = -1;
   1698 	}
   1699 	FB_CloseMouse(this);
   1700 	FB_CloseKeyboard(this);
   1701 }
   1702