Home | History | Annotate | Download | only in fbcon
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 enum {
    130 	FBCON_ROTATE_NONE = 0,
    131 	FBCON_ROTATE_CCW = 90,
    132 	FBCON_ROTATE_UD = 180,
    133 	FBCON_ROTATE_CW = 270
    134 };
    135 
    136 #define min(a,b) ((a)<(b)?(a):(b))
    137 
    138 /* Initialization/Query functions */
    139 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
    140 static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
    141 static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
    142 #ifdef VGA16_FBCON_SUPPORT
    143 static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
    144 #endif
    145 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
    146 static void FB_VideoQuit(_THIS);
    147 
    148 /* Hardware surface functions */
    149 static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
    150 static void FB_FreeHWSurfaces(_THIS);
    151 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
    152 static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
    153 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
    154 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
    155 static void FB_WaitVBL(_THIS);
    156 static void FB_WaitIdle(_THIS);
    157 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
    158 
    159 /* Internal palette functions */
    160 static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
    161                                   struct fb_var_screeninfo *vinfo);
    162 static void FB_RestorePalette(_THIS);
    163 
    164 /* Shadow buffer functions */
    165 static FB_bitBlit FB_blit16;
    166 static FB_bitBlit FB_blit16blocked;
    167 
    168 static int SDL_getpagesize(void)
    169 {
    170 #ifdef HAVE_GETPAGESIZE
    171 	return getpagesize();
    172 #elif defined(PAGE_SIZE)
    173 	return PAGE_SIZE;
    174 #else
    175 #error Can not determine system page size.
    176 	return 4096;  /* this is what it USED to be in Linux... */
    177 #endif
    178 }
    179 
    180 
    181 /* Small wrapper for mmap() so we can play nicely with no-mmu hosts
    182  * (non-mmu hosts disallow the MAP_SHARED flag) */
    183 
    184 static void *do_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
    185 {
    186 	void *ret;
    187 	ret = mmap(start, length, prot, flags, fd, offset);
    188 	if ( ret == (char *)-1 && (flags & MAP_SHARED) ) {
    189 		ret = mmap(start, length, prot,
    190 		           (flags & ~MAP_SHARED) | MAP_PRIVATE, fd, offset);
    191 	}
    192 	return ret;
    193 }
    194 
    195 /* FB driver bootstrap functions */
    196 
    197 static int FB_Available(void)
    198 {
    199 	int console = -1;
    200 	/* Added check for /fb/0 (devfs) */
    201 	/* but - use environment variable first... if it fails, still check defaults */
    202 	int idx = 0;
    203 	const char *SDL_fbdevs[4] = { NULL, "/dev/fb0", "/dev/fb/0", NULL };
    204 
    205 	SDL_fbdevs[0] = SDL_getenv("SDL_FBDEV");
    206 	if( !SDL_fbdevs[0] )
    207 		idx++;
    208 	for( ; SDL_fbdevs[idx]; idx++ )
    209 	{
    210 		console = open(SDL_fbdevs[idx], O_RDWR, 0);
    211 		if ( console >= 0 ) {
    212 			close(console);
    213 			break;
    214 		}
    215 	}
    216 	return(console >= 0);
    217 }
    218 
    219 static void FB_DeleteDevice(SDL_VideoDevice *device)
    220 {
    221 	SDL_free(device->hidden);
    222 	SDL_free(device);
    223 }
    224 
    225 static SDL_VideoDevice *FB_CreateDevice(int devindex)
    226 {
    227 	SDL_VideoDevice *this;
    228 
    229 	/* Initialize all variables that we clean on shutdown */
    230 	this = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
    231 	if ( this ) {
    232 		SDL_memset(this, 0, (sizeof *this));
    233 		this->hidden = (struct SDL_PrivateVideoData *)
    234 				SDL_malloc((sizeof *this->hidden));
    235 	}
    236 	if ( (this == NULL) || (this->hidden == NULL) ) {
    237 		SDL_OutOfMemory();
    238 		if ( this ) {
    239 			SDL_free(this);
    240 		}
    241 		return(0);
    242 	}
    243 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    244 	wait_vbl = FB_WaitVBL;
    245 	wait_idle = FB_WaitIdle;
    246 	mouse_fd = -1;
    247 	keyboard_fd = -1;
    248 
    249 	/* Set the function pointers */
    250 	this->VideoInit = FB_VideoInit;
    251 	this->ListModes = FB_ListModes;
    252 	this->SetVideoMode = FB_SetVideoMode;
    253 	this->SetColors = FB_SetColors;
    254 	this->UpdateRects = NULL;
    255 	this->VideoQuit = FB_VideoQuit;
    256 	this->AllocHWSurface = FB_AllocHWSurface;
    257 	this->CheckHWBlit = NULL;
    258 	this->FillHWRect = NULL;
    259 	this->SetHWColorKey = NULL;
    260 	this->SetHWAlpha = NULL;
    261 	this->LockHWSurface = FB_LockHWSurface;
    262 	this->UnlockHWSurface = FB_UnlockHWSurface;
    263 	this->FlipHWSurface = FB_FlipHWSurface;
    264 	this->FreeHWSurface = FB_FreeHWSurface;
    265 	this->SetCaption = NULL;
    266 	this->SetIcon = NULL;
    267 	this->IconifyWindow = NULL;
    268 	this->GrabInput = NULL;
    269 	this->GetWMInfo = NULL;
    270 	this->InitOSKeymap = FB_InitOSKeymap;
    271 	this->PumpEvents = FB_PumpEvents;
    272 
    273 	this->free = FB_DeleteDevice;
    274 
    275 	return this;
    276 }
    277 
    278 VideoBootStrap FBCON_bootstrap = {
    279 	"fbcon", "Linux Framebuffer Console",
    280 	FB_Available, FB_CreateDevice
    281 };
    282 
    283 #define FB_MODES_DB	"/etc/fb.modes"
    284 
    285 static int read_fbmodes_line(FILE*f, char* line, int length)
    286 {
    287 	int blank;
    288 	char* c;
    289 	int i;
    290 
    291 	blank=0;
    292 	/* find a relevant line */
    293 	do
    294 	{
    295 		if (!fgets(line,length,f))
    296 			return 0;
    297 		c=line;
    298 		while(((*c=='\t')||(*c==' '))&&(*c!=0))
    299 			c++;
    300 
    301 		if ((*c=='\n')||(*c=='#')||(*c==0))
    302 			blank=1;
    303 		else
    304 			blank=0;
    305 	}
    306 	while(blank);
    307 	/* remove whitespace at the begining of the string */
    308 	i=0;
    309 	do
    310 	{
    311 		line[i]=c[i];
    312 		i++;
    313 	}
    314 	while(c[i]!=0);
    315 	return 1;
    316 }
    317 
    318 static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
    319 {
    320 	char line[1024];
    321 	char option[256];
    322 
    323 	/* Find a "geometry" */
    324 	do {
    325 		if (read_fbmodes_line(f, line, sizeof(line))==0)
    326 			return 0;
    327 		if (SDL_strncmp(line,"geometry",8)==0)
    328 			break;
    329 	}
    330 	while(1);
    331 
    332 	SDL_sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres,
    333 			&vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
    334 	if (read_fbmodes_line(f, line, sizeof(line))==0)
    335 		return 0;
    336 
    337 	SDL_sscanf(line, "timings %d %d %d %d %d %d %d", &vinfo->pixclock,
    338 			&vinfo->left_margin, &vinfo->right_margin, &vinfo->upper_margin,
    339 			&vinfo->lower_margin, &vinfo->hsync_len, &vinfo->vsync_len);
    340 
    341 	vinfo->sync=0;
    342 	vinfo->vmode=FB_VMODE_NONINTERLACED;
    343 
    344 	/* Parse misc options */
    345 	do {
    346 		if (read_fbmodes_line(f, line, sizeof(line))==0)
    347 			return 0;
    348 
    349 		if (SDL_strncmp(line,"hsync",5)==0) {
    350 			SDL_sscanf(line,"hsync %s",option);
    351 			if (SDL_strncmp(option,"high",4)==0)
    352 				vinfo->sync |= FB_SYNC_HOR_HIGH_ACT;
    353 		}
    354 		else if (SDL_strncmp(line,"vsync",5)==0) {
    355 			SDL_sscanf(line,"vsync %s",option);
    356 			if (SDL_strncmp(option,"high",4)==0)
    357 				vinfo->sync |= FB_SYNC_VERT_HIGH_ACT;
    358 		}
    359 		else if (SDL_strncmp(line,"csync",5)==0) {
    360 			SDL_sscanf(line,"csync %s",option);
    361 			if (SDL_strncmp(option,"high",4)==0)
    362 				vinfo->sync |= FB_SYNC_COMP_HIGH_ACT;
    363 		}
    364 		else if (SDL_strncmp(line,"extsync",5)==0) {
    365 			SDL_sscanf(line,"extsync %s",option);
    366 			if (SDL_strncmp(option,"true",4)==0)
    367 				vinfo->sync |= FB_SYNC_EXT;
    368 		}
    369 		else if (SDL_strncmp(line,"laced",5)==0) {
    370 			SDL_sscanf(line,"laced %s",option);
    371 			if (SDL_strncmp(option,"true",4)==0)
    372 				vinfo->vmode |= FB_VMODE_INTERLACED;
    373 		}
    374 		else if (SDL_strncmp(line,"double",6)==0) {
    375 			SDL_sscanf(line,"double %s",option);
    376 			if (SDL_strncmp(option,"true",4)==0)
    377 				vinfo->vmode |= FB_VMODE_DOUBLE;
    378 		}
    379 	}
    380 	while(SDL_strncmp(line,"endmode",7)!=0);
    381 
    382 	return 1;
    383 }
    384 
    385 static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
    386                         int index, unsigned int *w, unsigned int *h)
    387 {
    388 	int mode_okay;
    389 
    390 	mode_okay = 0;
    391 	vinfo->bits_per_pixel = (index+1)*8;
    392 	vinfo->xres = *w;
    393 	vinfo->xres_virtual = *w;
    394 	vinfo->yres = *h;
    395 	vinfo->yres_virtual = *h;
    396 	vinfo->activate = FB_ACTIVATE_TEST;
    397 	if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, vinfo) == 0 ) {
    398 #ifdef FBCON_DEBUG
    399 		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);
    400 #endif
    401 		if ( (((vinfo->bits_per_pixel+7)/8)-1) == index ) {
    402 			*w = vinfo->xres;
    403 			*h = vinfo->yres;
    404 			mode_okay = 1;
    405 		}
    406 	}
    407 	return mode_okay;
    408 }
    409 
    410 static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
    411 {
    412 	SDL_Rect *mode;
    413 	int i;
    414 	int next_mode;
    415 
    416 	/* Check to see if we already have this mode */
    417 	if ( SDL_nummodes[index] > 0 ) {
    418 		mode = SDL_modelist[index][SDL_nummodes[index]-1];
    419 		if ( (mode->w == w) && (mode->h == h) ) {
    420 #ifdef FBCON_DEBUG
    421 			fprintf(stderr, "We already have mode %dx%d at %d bytes per pixel\n", w, h, index+1);
    422 #endif
    423 			return(0);
    424 		}
    425 	}
    426 
    427 	/* Only allow a mode if we have a valid timing for it */
    428 	if ( check_timings ) {
    429 		int found_timing = 0;
    430 		for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
    431 			if ( (w == vesa_timings[i].xres) &&
    432 			     (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
    433 				found_timing = 1;
    434 				break;
    435 			}
    436 		}
    437 		if ( !found_timing ) {
    438 #ifdef FBCON_DEBUG
    439 			fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
    440 #endif
    441 			return(0);
    442 		}
    443 	}
    444 
    445 	/* Set up the new video mode rectangle */
    446 	mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
    447 	if ( mode == NULL ) {
    448 		SDL_OutOfMemory();
    449 		return(-1);
    450 	}
    451 	mode->x = 0;
    452 	mode->y = 0;
    453 	mode->w = w;
    454 	mode->h = h;
    455 #ifdef FBCON_DEBUG
    456 	fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
    457 #endif
    458 
    459 	/* Allocate the new list of modes, and fill in the new mode */
    460 	next_mode = SDL_nummodes[index];
    461 	SDL_modelist[index] = (SDL_Rect **)
    462 	       SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *));
    463 	if ( SDL_modelist[index] == NULL ) {
    464 		SDL_OutOfMemory();
    465 		SDL_nummodes[index] = 0;
    466 		SDL_free(mode);
    467 		return(-1);
    468 	}
    469 	SDL_modelist[index][next_mode] = mode;
    470 	SDL_modelist[index][next_mode+1] = NULL;
    471 	SDL_nummodes[index]++;
    472 
    473 	return(0);
    474 }
    475 
    476 static int cmpmodes(const void *va, const void *vb)
    477 {
    478     const SDL_Rect *a = *(const SDL_Rect**)va;
    479     const SDL_Rect *b = *(const SDL_Rect**)vb;
    480     if ( a->h == b->h )
    481         return b->w - a->w;
    482     else
    483         return b->h - a->h;
    484 }
    485 
    486 static void FB_SortModes(_THIS)
    487 {
    488 	int i;
    489 	for ( i=0; i<NUM_MODELISTS; ++i ) {
    490 		if ( SDL_nummodes[i] > 0 ) {
    491 			SDL_qsort(SDL_modelist[i], SDL_nummodes[i], sizeof *SDL_modelist[i], cmpmodes);
    492 		}
    493 	}
    494 }
    495 
    496 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
    497 {
    498 	const int pagesize = SDL_getpagesize();
    499 	struct fb_fix_screeninfo finfo;
    500 	struct fb_var_screeninfo vinfo;
    501 	int i, j;
    502 	int current_index;
    503 	unsigned int current_w;
    504 	unsigned int current_h;
    505 	const char *SDL_fbdev;
    506 	const char *rotation;
    507 	FILE *modesdb;
    508 
    509 	/* Initialize the library */
    510 	SDL_fbdev = SDL_getenv("SDL_FBDEV");
    511 	if ( SDL_fbdev == NULL ) {
    512 		SDL_fbdev = "/dev/fb0";
    513 	}
    514 	console_fd = open(SDL_fbdev, O_RDWR, 0);
    515 	if ( console_fd < 0 ) {
    516 		SDL_SetError("Unable to open %s", SDL_fbdev);
    517 		return(-1);
    518 	}
    519 
    520 #if !SDL_THREADS_DISABLED
    521 	/* Create the hardware surface lock mutex */
    522 	hw_lock = SDL_CreateMutex();
    523 	if ( hw_lock == NULL ) {
    524 		SDL_SetError("Unable to create lock mutex");
    525 		FB_VideoQuit(this);
    526 		return(-1);
    527 	}
    528 #endif
    529 
    530 	/* Get the type of video hardware */
    531 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
    532 		SDL_SetError("Couldn't get console hardware info");
    533 		FB_VideoQuit(this);
    534 		return(-1);
    535 	}
    536 	switch (finfo.type) {
    537 		case FB_TYPE_PACKED_PIXELS:
    538 			/* Supported, no worries.. */
    539 			break;
    540 #ifdef VGA16_FBCON_SUPPORT
    541 		case FB_TYPE_VGA_PLANES:
    542 			/* VGA16 is supported, but that's it */
    543 			if ( finfo.type_aux == FB_AUX_VGA_PLANES_VGA4 ) {
    544 				if ( ioperm(0x3b4, 0x3df - 0x3b4 + 1, 1) < 0 ) {
    545 					SDL_SetError("No I/O port permissions");
    546 					FB_VideoQuit(this);
    547 					return(-1);
    548 				}
    549 				this->SetVideoMode = FB_SetVGA16Mode;
    550 				break;
    551 			}
    552 			/* Fall through to unsupported case */
    553 #endif /* VGA16_FBCON_SUPPORT */
    554 		default:
    555 			SDL_SetError("Unsupported console hardware");
    556 			FB_VideoQuit(this);
    557 			return(-1);
    558 	}
    559 	switch (finfo.visual) {
    560 		case FB_VISUAL_TRUECOLOR:
    561 		case FB_VISUAL_PSEUDOCOLOR:
    562 		case FB_VISUAL_STATIC_PSEUDOCOLOR:
    563 		case FB_VISUAL_DIRECTCOLOR:
    564 			break;
    565 		default:
    566 			SDL_SetError("Unsupported console hardware");
    567 			FB_VideoQuit(this);
    568 			return(-1);
    569 	}
    570 
    571 	/* Check if the user wants to disable hardware acceleration */
    572 	{ const char *fb_accel;
    573 		fb_accel = SDL_getenv("SDL_FBACCEL");
    574 		if ( fb_accel ) {
    575 			finfo.accel = SDL_atoi(fb_accel);
    576 		}
    577 	}
    578 
    579 	/* Memory map the device, compensating for buggy PPC mmap() */
    580 	mapped_offset = (((long)finfo.smem_start) -
    581 	                (((long)finfo.smem_start)&~(pagesize-1)));
    582 	mapped_memlen = finfo.smem_len+mapped_offset;
    583 	mapped_mem = do_mmap(NULL, mapped_memlen,
    584 	                  PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
    585 	if ( mapped_mem == (char *)-1 ) {
    586 		SDL_SetError("Unable to memory map the video hardware");
    587 		mapped_mem = NULL;
    588 		FB_VideoQuit(this);
    589 		return(-1);
    590 	}
    591 
    592 	/* Determine the current screen depth */
    593 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
    594 		SDL_SetError("Couldn't get console pixel format");
    595 		FB_VideoQuit(this);
    596 		return(-1);
    597 	}
    598 	vformat->BitsPerPixel = vinfo.bits_per_pixel;
    599 	if ( vformat->BitsPerPixel < 8 ) {
    600 		/* Assuming VGA16, we handle this via a shadow framebuffer */
    601 		vformat->BitsPerPixel = 8;
    602 	}
    603 	for ( i=0; i<vinfo.red.length; ++i ) {
    604 		vformat->Rmask <<= 1;
    605 		vformat->Rmask |= (0x00000001<<vinfo.red.offset);
    606 	}
    607 	for ( i=0; i<vinfo.green.length; ++i ) {
    608 		vformat->Gmask <<= 1;
    609 		vformat->Gmask |= (0x00000001<<vinfo.green.offset);
    610 	}
    611 	for ( i=0; i<vinfo.blue.length; ++i ) {
    612 		vformat->Bmask <<= 1;
    613 		vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
    614 	}
    615 	saved_vinfo = vinfo;
    616 
    617 	/* Save hardware palette, if needed */
    618 	FB_SavePalette(this, &finfo, &vinfo);
    619 
    620 	/* If the I/O registers are available, memory map them so we
    621 	   can take advantage of any supported hardware acceleration.
    622 	 */
    623 	vinfo.accel_flags = 0;	/* Temporarily reserve registers */
    624 	ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo);
    625 	if ( finfo.accel && finfo.mmio_len ) {
    626 		mapped_iolen = finfo.mmio_len;
    627 		mapped_io = do_mmap(NULL, mapped_iolen, PROT_READ|PROT_WRITE,
    628 		                 MAP_SHARED, console_fd, mapped_memlen);
    629 		if ( mapped_io == (char *)-1 ) {
    630 			/* Hmm, failed to memory map I/O registers */
    631 			mapped_io = NULL;
    632 		}
    633 	}
    634 
    635 	rotate = FBCON_ROTATE_NONE;
    636 	rotation = SDL_getenv("SDL_VIDEO_FBCON_ROTATION");
    637 	if (rotation != NULL) {
    638 		if (SDL_strlen(rotation) == 0) {
    639 			shadow_fb = 0;
    640 			rotate = FBCON_ROTATE_NONE;
    641 #ifdef FBCON_DEBUG
    642 			printf("Not rotating, no shadow\n");
    643 #endif
    644 		} else if (!SDL_strcmp(rotation, "NONE")) {
    645 			shadow_fb = 1;
    646 			rotate = FBCON_ROTATE_NONE;
    647 #ifdef FBCON_DEBUG
    648 			printf("Not rotating, but still using shadow\n");
    649 #endif
    650 		} else if (!SDL_strcmp(rotation, "CW")) {
    651 			shadow_fb = 1;
    652 			rotate = FBCON_ROTATE_CW;
    653 #ifdef FBCON_DEBUG
    654 			printf("Rotating screen clockwise\n");
    655 #endif
    656 		} else if (!SDL_strcmp(rotation, "CCW")) {
    657 			shadow_fb = 1;
    658 			rotate = FBCON_ROTATE_CCW;
    659 #ifdef FBCON_DEBUG
    660 			printf("Rotating screen counter clockwise\n");
    661 #endif
    662 		} else if (!SDL_strcmp(rotation, "UD")) {
    663 			shadow_fb = 1;
    664 			rotate = FBCON_ROTATE_UD;
    665 #ifdef FBCON_DEBUG
    666 			printf("Rotating screen upside down\n");
    667 #endif
    668 		} else {
    669 			SDL_SetError("\"%s\" is not a valid value for "
    670 				 "SDL_VIDEO_FBCON_ROTATION", rotation);
    671 			return(-1);
    672 		}
    673 	}
    674 
    675 	if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
    676 		current_w = vinfo.yres;
    677 		current_h = vinfo.xres;
    678 	} else {
    679 		current_w = vinfo.xres;
    680 		current_h = vinfo.yres;
    681 	}
    682 
    683 	/* Query for the list of available video modes */
    684 	current_index = ((vinfo.bits_per_pixel+7)/8)-1;
    685 	modesdb = fopen(FB_MODES_DB, "r");
    686 	for ( i=0; i<NUM_MODELISTS; ++i ) {
    687 		SDL_nummodes[i] = 0;
    688 		SDL_modelist[i] = NULL;
    689 	}
    690 	if ( SDL_getenv("SDL_FB_BROKEN_MODES") != NULL ) {
    691 		FB_AddMode(this, current_index, current_w, current_h, 0);
    692 	} else if(modesdb) {
    693 		while ( read_fbmodes_mode(modesdb, &vinfo) ) {
    694 			for ( i=0; i<NUM_MODELISTS; ++i ) {
    695 				unsigned int w, h;
    696 
    697 				if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
    698 					w = vinfo.yres;
    699 					h = vinfo.xres;
    700 				} else {
    701 					w = vinfo.xres;
    702 					h = vinfo.yres;
    703 				}
    704 				/* See if we are querying for the current mode */
    705 				if ( i == current_index ) {
    706 					if ( (current_w > w) || (current_h > h) ) {
    707 						/* Only check once */
    708 						FB_AddMode(this, i, current_w, current_h, 0);
    709 						current_index = -1;
    710 					}
    711 				}
    712 				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
    713 					FB_AddMode(this, i, w, h, 0);
    714 				}
    715 			}
    716 		}
    717 		fclose(modesdb);
    718 		FB_SortModes(this);
    719 	} else {
    720 		for ( i=0; i<NUM_MODELISTS; ++i ) {
    721 			for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
    722 				unsigned int w, h;
    723 
    724 				if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
    725 					w = checkres[j].h;
    726 					h = checkres[j].w;
    727 				} else {
    728 					w = checkres[j].w;
    729 					h = checkres[j].h;
    730 				}
    731 				/* See if we are querying for the current mode */
    732 				if ( i == current_index ) {
    733 					if ( (current_w > w) || (current_h > h) ) {
    734 						/* Only check once */
    735 						FB_AddMode(this, i, current_w, current_h, 0);
    736 						current_index = -1;
    737 					}
    738 				}
    739 				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
    740 					FB_AddMode(this, i, w, h, 1);
    741 				}
    742 			}
    743 		}
    744 	}
    745 
    746 	this->info.current_w = current_w;
    747 	this->info.current_h = current_h;
    748 	this->info.wm_available = 0;
    749 	this->info.hw_available = !shadow_fb;
    750 	this->info.video_mem = shadow_fb ? 0 : finfo.smem_len/1024;
    751 	/* Fill in our hardware acceleration capabilities */
    752 	if ( mapped_io ) {
    753 		switch (finfo.accel) {
    754 		    case FB_ACCEL_MATROX_MGA2064W:
    755 		    case FB_ACCEL_MATROX_MGA1064SG:
    756 		    case FB_ACCEL_MATROX_MGA2164W:
    757 		    case FB_ACCEL_MATROX_MGA2164W_AGP:
    758 		    case FB_ACCEL_MATROX_MGAG100:
    759 		    /*case FB_ACCEL_MATROX_MGAG200: G200 acceleration broken! */
    760 		    case FB_ACCEL_MATROX_MGAG400:
    761 #ifdef FBACCEL_DEBUG
    762 			printf("Matrox hardware accelerator!\n");
    763 #endif
    764 			FB_MatroxAccel(this, finfo.accel);
    765 			break;
    766 		    case FB_ACCEL_3DFX_BANSHEE:
    767 #ifdef FBACCEL_DEBUG
    768 			printf("3DFX hardware accelerator!\n");
    769 #endif
    770 			FB_3DfxAccel(this, finfo.accel);
    771 			break;
    772 		    case FB_ACCEL_NV3:
    773 		    case FB_ACCEL_NV4:
    774 #ifdef FBACCEL_DEBUG
    775 			printf("NVidia hardware accelerator!\n");
    776 #endif
    777 			FB_RivaAccel(this, finfo.accel);
    778 			break;
    779 		    default:
    780 #ifdef FBACCEL_DEBUG
    781 			printf("Unknown hardware accelerator.\n");
    782 #endif
    783 			break;
    784 		}
    785 	}
    786 
    787 	if (shadow_fb) {
    788 		shadow_mem = (char *)SDL_malloc(mapped_memlen);
    789 		if (shadow_mem == NULL) {
    790 			SDL_SetError("No memory for shadow");
    791 			return (-1);
    792 		}
    793 	}
    794 
    795 	/* Enable mouse and keyboard support */
    796 	if ( FB_OpenKeyboard(this) < 0 ) {
    797 		FB_VideoQuit(this);
    798 		return(-1);
    799 	}
    800 	if ( FB_OpenMouse(this) < 0 ) {
    801 		const char *sdl_nomouse;
    802 
    803 		sdl_nomouse = SDL_getenv("SDL_NOMOUSE");
    804 		if ( ! sdl_nomouse ) {
    805 			SDL_SetError("Unable to open mouse");
    806 			FB_VideoQuit(this);
    807 			return(-1);
    808 		}
    809 	}
    810 
    811 	/* We're done! */
    812 	return(0);
    813 }
    814 
    815 static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
    816 {
    817 	return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]);
    818 }
    819 
    820 /* Various screen update functions available */
    821 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
    822 #ifdef VGA16_FBCON_SUPPORT
    823 static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
    824 #endif
    825 
    826 #ifdef FBCON_DEBUG
    827 static void print_vinfo(struct fb_var_screeninfo *vinfo)
    828 {
    829 	fprintf(stderr, "Printing vinfo:\n");
    830 	fprintf(stderr, "\txres: %d\n", vinfo->xres);
    831 	fprintf(stderr, "\tyres: %d\n", vinfo->yres);
    832 	fprintf(stderr, "\txres_virtual: %d\n", vinfo->xres_virtual);
    833 	fprintf(stderr, "\tyres_virtual: %d\n", vinfo->yres_virtual);
    834 	fprintf(stderr, "\txoffset: %d\n", vinfo->xoffset);
    835 	fprintf(stderr, "\tyoffset: %d\n", vinfo->yoffset);
    836 	fprintf(stderr, "\tbits_per_pixel: %d\n", vinfo->bits_per_pixel);
    837 	fprintf(stderr, "\tgrayscale: %d\n", vinfo->grayscale);
    838 	fprintf(stderr, "\tnonstd: %d\n", vinfo->nonstd);
    839 	fprintf(stderr, "\tactivate: %d\n", vinfo->activate);
    840 	fprintf(stderr, "\theight: %d\n", vinfo->height);
    841 	fprintf(stderr, "\twidth: %d\n", vinfo->width);
    842 	fprintf(stderr, "\taccel_flags: %d\n", vinfo->accel_flags);
    843 	fprintf(stderr, "\tpixclock: %d\n", vinfo->pixclock);
    844 	fprintf(stderr, "\tleft_margin: %d\n", vinfo->left_margin);
    845 	fprintf(stderr, "\tright_margin: %d\n", vinfo->right_margin);
    846 	fprintf(stderr, "\tupper_margin: %d\n", vinfo->upper_margin);
    847 	fprintf(stderr, "\tlower_margin: %d\n", vinfo->lower_margin);
    848 	fprintf(stderr, "\thsync_len: %d\n", vinfo->hsync_len);
    849 	fprintf(stderr, "\tvsync_len: %d\n", vinfo->vsync_len);
    850 	fprintf(stderr, "\tsync: %d\n", vinfo->sync);
    851 	fprintf(stderr, "\tvmode: %d\n", vinfo->vmode);
    852 	fprintf(stderr, "\tred: %d/%d\n", vinfo->red.length, vinfo->red.offset);
    853 	fprintf(stderr, "\tgreen: %d/%d\n", vinfo->green.length, vinfo->green.offset);
    854 	fprintf(stderr, "\tblue: %d/%d\n", vinfo->blue.length, vinfo->blue.offset);
    855 	fprintf(stderr, "\talpha: %d/%d\n", vinfo->transp.length, vinfo->transp.offset);
    856 }
    857 static void print_finfo(struct fb_fix_screeninfo *finfo)
    858 {
    859 	fprintf(stderr, "Printing finfo:\n");
    860 	fprintf(stderr, "\tsmem_start = %p\n", (char *)finfo->smem_start);
    861 	fprintf(stderr, "\tsmem_len = %d\n", finfo->smem_len);
    862 	fprintf(stderr, "\ttype = %d\n", finfo->type);
    863 	fprintf(stderr, "\ttype_aux = %d\n", finfo->type_aux);
    864 	fprintf(stderr, "\tvisual = %d\n", finfo->visual);
    865 	fprintf(stderr, "\txpanstep = %d\n", finfo->xpanstep);
    866 	fprintf(stderr, "\typanstep = %d\n", finfo->ypanstep);
    867 	fprintf(stderr, "\tywrapstep = %d\n", finfo->ywrapstep);
    868 	fprintf(stderr, "\tline_length = %d\n", finfo->line_length);
    869 	fprintf(stderr, "\tmmio_start = %p\n", (char *)finfo->mmio_start);
    870 	fprintf(stderr, "\tmmio_len = %d\n", finfo->mmio_len);
    871 	fprintf(stderr, "\taccel = %d\n", finfo->accel);
    872 }
    873 #endif
    874 
    875 static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
    876 {
    877 	int matched;
    878 	FILE *modesdb;
    879 	struct fb_var_screeninfo cinfo;
    880 
    881 	matched = 0;
    882 	modesdb = fopen(FB_MODES_DB, "r");
    883 	if ( modesdb ) {
    884 		/* Parse the mode definition file */
    885 		while ( read_fbmodes_mode(modesdb, &cinfo) ) {
    886 			if ( (vinfo->xres == cinfo.xres && vinfo->yres == cinfo.yres) &&
    887 			     (!matched || (vinfo->bits_per_pixel == cinfo.bits_per_pixel)) ) {
    888 				vinfo->pixclock = cinfo.pixclock;
    889 				vinfo->left_margin = cinfo.left_margin;
    890 				vinfo->right_margin = cinfo.right_margin;
    891 				vinfo->upper_margin = cinfo.upper_margin;
    892 				vinfo->lower_margin = cinfo.lower_margin;
    893 				vinfo->hsync_len = cinfo.hsync_len;
    894 				vinfo->vsync_len = cinfo.vsync_len;
    895 				if ( matched ) {
    896 					break;
    897 				}
    898 				matched = 1;
    899 			}
    900 		}
    901 		fclose(modesdb);
    902 	}
    903 	return(matched);
    904 }
    905 
    906 static int choose_vesa_mode(struct fb_var_screeninfo *vinfo)
    907 {
    908 	int matched;
    909 	int i;
    910 
    911 	/* Check for VESA timings */
    912 	matched = 0;
    913 	for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
    914 		if ( (vinfo->xres == vesa_timings[i].xres) &&
    915 		     (vinfo->yres == vesa_timings[i].yres) ) {
    916 #ifdef FBCON_DEBUG
    917 			fprintf(stderr, "Using VESA timings for %dx%d\n",
    918 						vinfo->xres, vinfo->yres);
    919 #endif
    920 			if ( vesa_timings[i].pixclock ) {
    921 				vinfo->pixclock = vesa_timings[i].pixclock;
    922 			}
    923 			vinfo->left_margin = vesa_timings[i].left;
    924 			vinfo->right_margin = vesa_timings[i].right;
    925 			vinfo->upper_margin = vesa_timings[i].upper;
    926 			vinfo->lower_margin = vesa_timings[i].lower;
    927 			vinfo->hsync_len = vesa_timings[i].hslen;
    928 			vinfo->vsync_len = vesa_timings[i].vslen;
    929 			vinfo->sync = vesa_timings[i].sync;
    930 			vinfo->vmode = vesa_timings[i].vmode;
    931 			matched = 1;
    932 			break;
    933 		}
    934 	}
    935 	return(matched);
    936 }
    937 
    938 #ifdef VGA16_FBCON_SUPPORT
    939 static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current,
    940 				int width, int height, int bpp, Uint32 flags)
    941 {
    942 	struct fb_fix_screeninfo finfo;
    943 	struct fb_var_screeninfo vinfo;
    944 
    945 	/* Set the terminal into graphics mode */
    946 	if ( FB_EnterGraphicsMode(this) < 0 ) {
    947 		return(NULL);
    948 	}
    949 
    950 	/* Restore the original palette */
    951 	FB_RestorePalette(this);
    952 
    953 	/* Set the video mode and get the final screen format */
    954 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
    955 		SDL_SetError("Couldn't get console screen info");
    956 		return(NULL);
    957 	}
    958 	cache_vinfo = vinfo;
    959 #ifdef FBCON_DEBUG
    960 	fprintf(stderr, "Printing actual vinfo:\n");
    961 	print_vinfo(&vinfo);
    962 #endif
    963 	if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
    964 		return(NULL);
    965 	}
    966 	current->format->palette->ncolors = 16;
    967 
    968 	/* Get the fixed information about the console hardware.
    969 	   This is necessary since finfo.line_length changes.
    970 	 */
    971 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
    972 		SDL_SetError("Couldn't get console hardware info");
    973 		return(NULL);
    974 	}
    975 #ifdef FBCON_DEBUG
    976 	fprintf(stderr, "Printing actual finfo:\n");
    977 	print_finfo(&finfo);
    978 #endif
    979 
    980 	/* Save hardware palette, if needed */
    981 	FB_SavePalette(this, &finfo, &vinfo);
    982 
    983 	/* Set up the new mode framebuffer */
    984 	current->flags = SDL_FULLSCREEN;
    985 	current->w = vinfo.xres;
    986 	current->h = vinfo.yres;
    987 	current->pitch = current->w;
    988 	current->pixels = SDL_malloc(current->h*current->pitch);
    989 
    990 	/* Set the update rectangle function */
    991 	this->UpdateRects = FB_VGA16Update;
    992 
    993 	/* We're done */
    994 	return(current);
    995 }
    996 #endif /* VGA16_FBCON_SUPPORT */
    997 
    998 static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
    999 				int width, int height, int bpp, Uint32 flags)
   1000 {
   1001 	struct fb_fix_screeninfo finfo;
   1002 	struct fb_var_screeninfo vinfo;
   1003 	int i;
   1004 	Uint32 Rmask;
   1005 	Uint32 Gmask;
   1006 	Uint32 Bmask;
   1007 	char *surfaces_mem;
   1008 	int surfaces_len;
   1009 
   1010 	/* Set the terminal into graphics mode */
   1011 	if ( FB_EnterGraphicsMode(this) < 0 ) {
   1012 		return(NULL);
   1013 	}
   1014 
   1015 	/* Restore the original palette */
   1016 	FB_RestorePalette(this);
   1017 
   1018 	/* Set the video mode and get the final screen format */
   1019 	if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
   1020 		SDL_SetError("Couldn't get console screen info");
   1021 		return(NULL);
   1022 	}
   1023 #ifdef FBCON_DEBUG
   1024 	fprintf(stderr, "Printing original vinfo:\n");
   1025 	print_vinfo(&vinfo);
   1026 #endif
   1027 	/* Do not use double buffering with shadow buffer */
   1028 	if (shadow_fb) {
   1029 		flags &= ~SDL_DOUBLEBUF;
   1030 	}
   1031 
   1032 	if ( (vinfo.xres != width) || (vinfo.yres != height) ||
   1033 	     (vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
   1034 		vinfo.activate = FB_ACTIVATE_NOW;
   1035 		vinfo.accel_flags = 0;
   1036 		vinfo.bits_per_pixel = bpp;
   1037 		vinfo.xres = width;
   1038 		vinfo.xres_virtual = width;
   1039 		vinfo.yres = height;
   1040 		if ( flags & SDL_DOUBLEBUF ) {
   1041 			vinfo.yres_virtual = height*2;
   1042 		} else {
   1043 			vinfo.yres_virtual = height;
   1044 		}
   1045 		vinfo.xoffset = 0;
   1046 		vinfo.yoffset = 0;
   1047 		vinfo.red.length = vinfo.red.offset = 0;
   1048 		vinfo.green.length = vinfo.green.offset = 0;
   1049 		vinfo.blue.length = vinfo.blue.offset = 0;
   1050 		vinfo.transp.length = vinfo.transp.offset = 0;
   1051 		if ( ! choose_fbmodes_mode(&vinfo) ) {
   1052 			choose_vesa_mode(&vinfo);
   1053 		}
   1054 #ifdef FBCON_DEBUG
   1055 		fprintf(stderr, "Printing wanted vinfo:\n");
   1056 		print_vinfo(&vinfo);
   1057 #endif
   1058 		if ( !shadow_fb &&
   1059 				ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
   1060 			vinfo.yres_virtual = height;
   1061 			if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
   1062 				SDL_SetError("Couldn't set console screen info");
   1063 				return(NULL);
   1064 			}
   1065 		}
   1066 	} else {
   1067 		int maxheight;
   1068 
   1069 		/* Figure out how much video memory is available */
   1070 		if ( flags & SDL_DOUBLEBUF ) {
   1071 			maxheight = height*2;
   1072 		} else {
   1073 			maxheight = height;
   1074 		}
   1075 		if ( vinfo.yres_virtual > maxheight ) {
   1076 			vinfo.yres_virtual = maxheight;
   1077 		}
   1078 	}
   1079 	cache_vinfo = vinfo;
   1080 #ifdef FBCON_DEBUG
   1081 	fprintf(stderr, "Printing actual vinfo:\n");
   1082 	print_vinfo(&vinfo);
   1083 #endif
   1084 	Rmask = 0;
   1085 	for ( i=0; i<vinfo.red.length; ++i ) {
   1086 		Rmask <<= 1;
   1087 		Rmask |= (0x00000001<<vinfo.red.offset);
   1088 	}
   1089 	Gmask = 0;
   1090 	for ( i=0; i<vinfo.green.length; ++i ) {
   1091 		Gmask <<= 1;
   1092 		Gmask |= (0x00000001<<vinfo.green.offset);
   1093 	}
   1094 	Bmask = 0;
   1095 	for ( i=0; i<vinfo.blue.length; ++i ) {
   1096 		Bmask <<= 1;
   1097 		Bmask |= (0x00000001<<vinfo.blue.offset);
   1098 	}
   1099 	if ( ! SDL_ReallocFormat(current, vinfo.bits_per_pixel,
   1100 	                                  Rmask, Gmask, Bmask, 0) ) {
   1101 		return(NULL);
   1102 	}
   1103 
   1104 	/* Get the fixed information about the console hardware.
   1105 	   This is necessary since finfo.line_length changes.
   1106 	 */
   1107 	if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
   1108 		SDL_SetError("Couldn't get console hardware info");
   1109 		return(NULL);
   1110 	}
   1111 
   1112 	/* Save hardware palette, if needed */
   1113 	FB_SavePalette(this, &finfo, &vinfo);
   1114 
   1115 	if (shadow_fb) {
   1116 		if (vinfo.bits_per_pixel == 16) {
   1117 			blitFunc = (rotate == FBCON_ROTATE_NONE ||
   1118 					rotate == FBCON_ROTATE_UD) ?
   1119 				FB_blit16 : FB_blit16blocked;
   1120 		} else {
   1121 #ifdef FBCON_DEBUG
   1122 			fprintf(stderr, "Init vinfo:\n");
   1123 			print_vinfo(&vinfo);
   1124 #endif
   1125 			SDL_SetError("Using software buffer, but no blitter "
   1126 					"function is available for %d bpp.",
   1127 					vinfo.bits_per_pixel);
   1128 			return(NULL);
   1129 		}
   1130 	}
   1131 
   1132 	/* Set up the new mode framebuffer */
   1133 	current->flags &= SDL_FULLSCREEN;
   1134 	if (shadow_fb) {
   1135 		current->flags |= SDL_SWSURFACE;
   1136 	} else {
   1137 		current->flags |= SDL_HWSURFACE;
   1138 	}
   1139 	current->w = vinfo.xres;
   1140 	current->h = vinfo.yres;
   1141 	if (shadow_fb) {
   1142 		current->pitch = current->w * ((vinfo.bits_per_pixel + 7) / 8);
   1143 		current->pixels = shadow_mem;
   1144 		physlinebytes = finfo.line_length;
   1145 	} else {
   1146 		current->pitch = finfo.line_length;
   1147 		current->pixels = mapped_mem+mapped_offset;
   1148 	}
   1149 
   1150 	/* Set up the information for hardware surfaces */
   1151 	surfaces_mem = (char *)current->pixels +
   1152 		vinfo.yres_virtual*current->pitch;
   1153 	surfaces_len = (shadow_fb) ?
   1154 		0 : (mapped_memlen-(surfaces_mem-mapped_mem));
   1155 
   1156 	FB_FreeHWSurfaces(this);
   1157 	FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
   1158 
   1159 	/* Let the application know we have a hardware palette */
   1160 	switch (finfo.visual) {
   1161 		case FB_VISUAL_PSEUDOCOLOR:
   1162 		current->flags |= SDL_HWPALETTE;
   1163 		break;
   1164 		default:
   1165 		break;
   1166 	}
   1167 
   1168 	/* Update for double-buffering, if we can */
   1169 	if ( flags & SDL_DOUBLEBUF ) {
   1170 		if ( vinfo.yres_virtual == (height*2) ) {
   1171 			current->flags |= SDL_DOUBLEBUF;
   1172 			flip_page = 0;
   1173 			flip_address[0] = (char *)current->pixels;
   1174 			flip_address[1] = (char *)current->pixels+
   1175 				current->h*current->pitch;
   1176 			this->screen = current;
   1177 			FB_FlipHWSurface(this, current);
   1178 			this->screen = NULL;
   1179 		}
   1180 	}
   1181 
   1182 	/* Set the update rectangle function */
   1183 	this->UpdateRects = FB_DirectUpdate;
   1184 
   1185 	/* We're done */
   1186 	return(current);
   1187 }
   1188 
   1189 #ifdef FBCON_DEBUG
   1190 void FB_DumpHWSurfaces(_THIS)
   1191 {
   1192 	vidmem_bucket *bucket;
   1193 
   1194 	printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
   1195 	printf("\n");
   1196 	printf("         Base  Size\n");
   1197 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1198 		printf("Bucket:  %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
   1199 		if ( bucket->prev ) {
   1200 			if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
   1201 				printf("Warning, corrupt bucket list! (prev)\n");
   1202 			}
   1203 		} else {
   1204 			if ( bucket != &surfaces ) {
   1205 				printf("Warning, corrupt bucket list! (!prev)\n");
   1206 			}
   1207 		}
   1208 		if ( bucket->next ) {
   1209 			if ( bucket->next->base != bucket->base+bucket->size ) {
   1210 				printf("Warning, corrupt bucket list! (next)\n");
   1211 			}
   1212 		}
   1213 	}
   1214 	printf("\n");
   1215 }
   1216 #endif
   1217 
   1218 static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
   1219 {
   1220 	vidmem_bucket *bucket;
   1221 
   1222 	surfaces_memtotal = size;
   1223 	surfaces_memleft = size;
   1224 
   1225 	if ( surfaces_memleft > 0 ) {
   1226 		bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket));
   1227 		if ( bucket == NULL ) {
   1228 			SDL_OutOfMemory();
   1229 			return(-1);
   1230 		}
   1231 		bucket->prev = &surfaces;
   1232 		bucket->used = 0;
   1233 		bucket->dirty = 0;
   1234 		bucket->base = base;
   1235 		bucket->size = size;
   1236 		bucket->next = NULL;
   1237 	} else {
   1238 		bucket = NULL;
   1239 	}
   1240 
   1241 	surfaces.prev = NULL;
   1242 	surfaces.used = 1;
   1243 	surfaces.dirty = 0;
   1244 	surfaces.base = screen->pixels;
   1245 	surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
   1246 	surfaces.next = bucket;
   1247 	screen->hwdata = (struct private_hwdata *)&surfaces;
   1248 	return(0);
   1249 }
   1250 static void FB_FreeHWSurfaces(_THIS)
   1251 {
   1252 	vidmem_bucket *bucket, *freeable;
   1253 
   1254 	bucket = surfaces.next;
   1255 	while ( bucket ) {
   1256 		freeable = bucket;
   1257 		bucket = bucket->next;
   1258 		SDL_free(freeable);
   1259 	}
   1260 	surfaces.next = NULL;
   1261 }
   1262 
   1263 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface)
   1264 {
   1265 	vidmem_bucket *bucket;
   1266 	int size;
   1267 	int extra;
   1268 
   1269 /* Temporarily, we only allow surfaces the same width as display.
   1270    Some blitters require the pitch between two hardware surfaces
   1271    to be the same.  Others have interesting alignment restrictions.
   1272    Until someone who knows these details looks at the code...
   1273 */
   1274 if ( surface->pitch > SDL_VideoSurface->pitch ) {
   1275 	SDL_SetError("Surface requested wider than screen");
   1276 	return(-1);
   1277 }
   1278 surface->pitch = SDL_VideoSurface->pitch;
   1279 	size = surface->h * surface->pitch;
   1280 #ifdef FBCON_DEBUG
   1281 	fprintf(stderr, "Allocating bucket of %d bytes\n", size);
   1282 #endif
   1283 
   1284 	/* Quick check for available mem */
   1285 	if ( size > surfaces_memleft ) {
   1286 		SDL_SetError("Not enough video memory");
   1287 		return(-1);
   1288 	}
   1289 
   1290 	/* Search for an empty bucket big enough */
   1291 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1292 		if ( ! bucket->used && (size <= bucket->size) ) {
   1293 			break;
   1294 		}
   1295 	}
   1296 	if ( bucket == NULL ) {
   1297 		SDL_SetError("Video memory too fragmented");
   1298 		return(-1);
   1299 	}
   1300 
   1301 	/* Create a new bucket for left-over memory */
   1302 	extra = (bucket->size - size);
   1303 	if ( extra ) {
   1304 		vidmem_bucket *newbucket;
   1305 
   1306 #ifdef FBCON_DEBUG
   1307 	fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
   1308 #endif
   1309 		newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket));
   1310 		if ( newbucket == NULL ) {
   1311 			SDL_OutOfMemory();
   1312 			return(-1);
   1313 		}
   1314 		newbucket->prev = bucket;
   1315 		newbucket->used = 0;
   1316 		newbucket->base = bucket->base+size;
   1317 		newbucket->size = extra;
   1318 		newbucket->next = bucket->next;
   1319 		if ( bucket->next ) {
   1320 			bucket->next->prev = newbucket;
   1321 		}
   1322 		bucket->next = newbucket;
   1323 	}
   1324 
   1325 	/* Set the current bucket values and return it! */
   1326 	bucket->used = 1;
   1327 	bucket->size = size;
   1328 	bucket->dirty = 0;
   1329 #ifdef FBCON_DEBUG
   1330 	fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
   1331 #endif
   1332 	surfaces_memleft -= size;
   1333 	surface->flags |= SDL_HWSURFACE;
   1334 	surface->pixels = bucket->base;
   1335 	surface->hwdata = (struct private_hwdata *)bucket;
   1336 	return(0);
   1337 }
   1338 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
   1339 {
   1340 	vidmem_bucket *bucket, *freeable;
   1341 
   1342 	/* Look for the bucket in the current list */
   1343 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   1344 		if ( bucket == (vidmem_bucket *)surface->hwdata ) {
   1345 			break;
   1346 		}
   1347 	}
   1348 	if ( bucket && bucket->used ) {
   1349 		/* Add the memory back to the total */
   1350 #ifdef DGA_DEBUG
   1351 	printf("Freeing bucket of %d bytes\n", bucket->size);
   1352 #endif
   1353 		surfaces_memleft += bucket->size;
   1354 
   1355 		/* Can we merge the space with surrounding buckets? */
   1356 		bucket->used = 0;
   1357 		if ( bucket->next && ! bucket->next->used ) {
   1358 #ifdef DGA_DEBUG
   1359 	printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
   1360 #endif
   1361 			freeable = bucket->next;
   1362 			bucket->size += bucket->next->size;
   1363 			bucket->next = bucket->next->next;
   1364 			if ( bucket->next ) {
   1365 				bucket->next->prev = bucket;
   1366 			}
   1367 			SDL_free(freeable);
   1368 		}
   1369 		if ( bucket->prev && ! bucket->prev->used ) {
   1370 #ifdef DGA_DEBUG
   1371 	printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
   1372 #endif
   1373 			freeable = bucket;
   1374 			bucket->prev->size += bucket->size;
   1375 			bucket->prev->next = bucket->next;
   1376 			if ( bucket->next ) {
   1377 				bucket->next->prev = bucket->prev;
   1378 			}
   1379 			SDL_free(freeable);
   1380 		}
   1381 	}
   1382 	surface->pixels = NULL;
   1383 	surface->hwdata = NULL;
   1384 }
   1385 
   1386 static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
   1387 {
   1388 	if ( switched_away ) {
   1389 		return -2; /* no hardware access */
   1390 	}
   1391 	if ( surface == this->screen ) {
   1392 		SDL_mutexP(hw_lock);
   1393 		if ( FB_IsSurfaceBusy(surface) ) {
   1394 			FB_WaitBusySurfaces(this);
   1395 		}
   1396 	} else {
   1397 		if ( FB_IsSurfaceBusy(surface) ) {
   1398 			FB_WaitBusySurfaces(this);
   1399 		}
   1400 	}
   1401 	return(0);
   1402 }
   1403 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
   1404 {
   1405 	if ( surface == this->screen ) {
   1406 		SDL_mutexV(hw_lock);
   1407 	}
   1408 }
   1409 
   1410 static void FB_WaitVBL(_THIS)
   1411 {
   1412 #ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
   1413 	ioctl(console_fd, FBIOWAITRETRACE, 0);
   1414 #endif
   1415 	return;
   1416 }
   1417 
   1418 static void FB_WaitIdle(_THIS)
   1419 {
   1420 	return;
   1421 }
   1422 
   1423 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
   1424 {
   1425 	if ( switched_away ) {
   1426 		return -2; /* no hardware access */
   1427 	}
   1428 
   1429 	/* Wait for vertical retrace and then flip display */
   1430 	cache_vinfo.yoffset = flip_page*surface->h;
   1431 	if ( FB_IsSurfaceBusy(this->screen) ) {
   1432 		FB_WaitBusySurfaces(this);
   1433 	}
   1434 	wait_vbl(this);
   1435 	if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
   1436 		SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
   1437 		return(-1);
   1438 	}
   1439 	flip_page = !flip_page;
   1440 
   1441 	surface->pixels = flip_address[flip_page];
   1442 	return(0);
   1443 }
   1444 
   1445 static void FB_blit16(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
   1446 		Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
   1447 {
   1448 	int w;
   1449 	Uint16 *src_pos = (Uint16 *)byte_src_pos;
   1450 	Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
   1451 
   1452 	while (height) {
   1453 		Uint16 *src = src_pos;
   1454 		Uint16 *dst = dst_pos;
   1455 		for (w = width; w != 0; w--) {
   1456 			*dst = *src;
   1457 			src += src_right_delta;
   1458 			dst++;
   1459 		}
   1460 		dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes);
   1461 		src_pos += src_down_delta;
   1462 		height--;
   1463 	}
   1464 }
   1465 
   1466 #define BLOCKSIZE_W 32
   1467 #define BLOCKSIZE_H 32
   1468 
   1469 static void FB_blit16blocked(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
   1470 		Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
   1471 {
   1472 	int w;
   1473 	Uint16 *src_pos = (Uint16 *)byte_src_pos;
   1474 	Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
   1475 
   1476 	while (height > 0) {
   1477 		Uint16 *src = src_pos;
   1478 		Uint16 *dst = dst_pos;
   1479 		for (w = width; w > 0; w -= BLOCKSIZE_W) {
   1480 			FB_blit16((Uint8 *)src,
   1481 					src_right_delta,
   1482 					src_down_delta,
   1483 					(Uint8 *)dst,
   1484 					dst_linebytes,
   1485 					min(w, BLOCKSIZE_W),
   1486 					min(height, BLOCKSIZE_H));
   1487 			src += src_right_delta * BLOCKSIZE_W;
   1488 			dst += BLOCKSIZE_W;
   1489 		}
   1490 		dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H);
   1491 		src_pos += src_down_delta * BLOCKSIZE_H;
   1492 		height -= BLOCKSIZE_H;
   1493 	}
   1494 }
   1495 
   1496 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
   1497 {
   1498 	int width = cache_vinfo.xres;
   1499 	int height = cache_vinfo.yres;
   1500 	int bytes_per_pixel = (cache_vinfo.bits_per_pixel + 7) / 8;
   1501 	int i;
   1502 
   1503 	if (!shadow_fb) {
   1504 		/* The application is already updating the visible video memory */
   1505 		return;
   1506 	}
   1507 
   1508 	if (cache_vinfo.bits_per_pixel != 16) {
   1509 		SDL_SetError("Shadow copy only implemented for 16 bpp");
   1510 		return;
   1511 	}
   1512 
   1513 	for (i = 0; i < numrects; i++) {
   1514 		int x1, y1, x2, y2;
   1515 		int scr_x1, scr_y1, scr_x2, scr_y2;
   1516 		int sha_x1, sha_y1;
   1517 		int shadow_right_delta;  /* Address change when moving right in dest */
   1518 		int shadow_down_delta;   /* Address change when moving down in dest */
   1519 		char *src_start;
   1520 		char *dst_start;
   1521 
   1522 		x1 = rects[i].x;
   1523 		y1 = rects[i].y;
   1524 		x2 = x1 + rects[i].w;
   1525 		y2 = y1 + rects[i].h;
   1526 
   1527 		if (x1 < 0) {
   1528 			x1 = 0;
   1529 		} else if (x1 > width) {
   1530 			x1 = width;
   1531 		}
   1532 		if (x2 < 0) {
   1533 			x2 = 0;
   1534 		} else if (x2 > width) {
   1535 			x2 = width;
   1536 		}
   1537 		if (y1 < 0) {
   1538 			y1 = 0;
   1539 		} else if (y1 > height) {
   1540 			y1 = height;
   1541 		}
   1542 		if (y2 < 0) {
   1543 			y2 = 0;
   1544 		} else if (y2 > height) {
   1545 			y2 = height;
   1546 		}
   1547 		if (x2 <= x1 || y2 <= y1) {
   1548 			continue;
   1549 		}
   1550 
   1551 		switch (rotate) {
   1552 			case FBCON_ROTATE_NONE:
   1553 				sha_x1 = scr_x1 = x1;
   1554 				sha_y1 = scr_y1 = y1;
   1555 				scr_x2 = x2;
   1556 				scr_y2 = y2;
   1557 				shadow_right_delta = 1;
   1558 				shadow_down_delta = width;
   1559 				break;
   1560 			case FBCON_ROTATE_CCW:
   1561 				scr_x1 = y1;
   1562 				scr_y1 = width - x2;
   1563 				scr_x2 = y2;
   1564 				scr_y2 = width - x1;
   1565 				sha_x1 = x2 - 1;
   1566 				sha_y1 = y1;
   1567 				shadow_right_delta = width;
   1568 				shadow_down_delta = -1;
   1569 				break;
   1570 			case FBCON_ROTATE_UD:
   1571 				scr_x1 = width - x2;
   1572 				scr_y1 = height - y2;
   1573 				scr_x2 = width - x1;
   1574 				scr_y2 = height - y1;
   1575 				sha_x1 = x2 - 1;
   1576 				sha_y1 = y2 - 1;
   1577 				shadow_right_delta = -1;
   1578 				shadow_down_delta = -width;
   1579 				break;
   1580 			case FBCON_ROTATE_CW:
   1581 				scr_x1 = height - y2;
   1582 				scr_y1 = x1;
   1583 				scr_x2 = height - y1;
   1584 				scr_y2 = x2;
   1585 				sha_x1 = x1;
   1586 				sha_y1 = y2 - 1;
   1587 				shadow_right_delta = -width;
   1588 				shadow_down_delta = 1;
   1589 				break;
   1590 			default:
   1591 				SDL_SetError("Unknown rotation");
   1592 				return;
   1593 		}
   1594 
   1595 		src_start = shadow_mem +
   1596 			(sha_y1 * width + sha_x1) * bytes_per_pixel;
   1597 		dst_start = mapped_mem + mapped_offset + scr_y1 * physlinebytes +
   1598 			scr_x1 * bytes_per_pixel;
   1599 
   1600 		blitFunc((Uint8 *) src_start,
   1601 				shadow_right_delta,
   1602 				shadow_down_delta,
   1603 				(Uint8 *) dst_start,
   1604 				physlinebytes,
   1605 				scr_x2 - scr_x1,
   1606 				scr_y2 - scr_y1);
   1607 	}
   1608 }
   1609 
   1610 #ifdef VGA16_FBCON_SUPPORT
   1611 /* Code adapted with thanks from the XFree86 VGA16 driver! :) */
   1612 #define writeGr(index, value) \
   1613 outb(index, 0x3CE);           \
   1614 outb(value, 0x3CF);
   1615 #define writeSeq(index, value) \
   1616 outb(index, 0x3C4);            \
   1617 outb(value, 0x3C5);
   1618 
   1619 static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects)
   1620 {
   1621     SDL_Surface *screen;
   1622     int width, height, FBPitch, left, i, j, SRCPitch, phase;
   1623     register Uint32 m;
   1624     Uint8  s1, s2, s3, s4;
   1625     Uint32 *src, *srcPtr;
   1626     Uint8  *dst, *dstPtr;
   1627 
   1628     if ( switched_away ) {
   1629         return; /* no hardware access */
   1630     }
   1631 
   1632     screen = this->screen;
   1633     FBPitch = screen->w >> 3;
   1634     SRCPitch = screen->pitch >> 2;
   1635 
   1636     writeGr(0x03, 0x00);
   1637     writeGr(0x05, 0x00);
   1638     writeGr(0x01, 0x00);
   1639     writeGr(0x08, 0xFF);
   1640 
   1641     while(numrects--) {
   1642 	left = rects->x & ~7;
   1643         width = (rects->w + 7) >> 3;
   1644         height = rects->h;
   1645         src = (Uint32*)screen->pixels + (rects->y * SRCPitch) + (left >> 2);
   1646         dst = (Uint8*)mapped_mem + (rects->y * FBPitch) + (left >> 3);
   1647 
   1648 	if((phase = (long)dst & 3L)) {
   1649 	    phase = 4 - phase;
   1650 	    if(phase > width) phase = width;
   1651 	    width -= phase;
   1652 	}
   1653 
   1654         while(height--) {
   1655 	    writeSeq(0x02, 1 << 0);
   1656 	    dstPtr = dst;
   1657 	    srcPtr = src;
   1658 	    i = width;
   1659 	    j = phase;
   1660 	    while(j--) {
   1661 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1662  		*dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1663 		srcPtr += 2;
   1664 	    }
   1665 	    while(i >= 4) {
   1666 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1667  		s1 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1668 		m = (srcPtr[3] & 0x01010101) | ((srcPtr[2] & 0x01010101) << 4);
   1669  		s2 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1670 		m = (srcPtr[5] & 0x01010101) | ((srcPtr[4] & 0x01010101) << 4);
   1671  		s3 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1672 		m = (srcPtr[7] & 0x01010101) | ((srcPtr[6] & 0x01010101) << 4);
   1673  		s4 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1674 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1675 		srcPtr += 8;
   1676 		dstPtr += 4;
   1677 		i -= 4;
   1678 	    }
   1679 	    while(i--) {
   1680 		m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
   1681  		*dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
   1682 		srcPtr += 2;
   1683 	    }
   1684 
   1685 	    writeSeq(0x02, 1 << 1);
   1686 	    dstPtr = dst;
   1687 	    srcPtr = src;
   1688 	    i = width;
   1689 	    j = phase;
   1690 	    while(j--) {
   1691 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1692  		*dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1693 		srcPtr += 2;
   1694 	    }
   1695 	    while(i >= 4) {
   1696 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1697  		s1 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1698 		m = (srcPtr[3] & 0x02020202) | ((srcPtr[2] & 0x02020202) << 4);
   1699  		s2 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1700 		m = (srcPtr[5] & 0x02020202) | ((srcPtr[4] & 0x02020202) << 4);
   1701  		s3 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1702 		m = (srcPtr[7] & 0x02020202) | ((srcPtr[6] & 0x02020202) << 4);
   1703  		s4 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1704 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1705 		srcPtr += 8;
   1706 		dstPtr += 4;
   1707 		i -= 4;
   1708 	    }
   1709 	    while(i--) {
   1710 		m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
   1711  		*dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
   1712 		srcPtr += 2;
   1713 	    }
   1714 
   1715 	    writeSeq(0x02, 1 << 2);
   1716 	    dstPtr = dst;
   1717 	    srcPtr = src;
   1718 	    i = width;
   1719 	    j = phase;
   1720 	    while(j--) {
   1721 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1722  		*dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1723 		srcPtr += 2;
   1724 	    }
   1725 	    while(i >= 4) {
   1726 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1727  		s1 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1728 		m = (srcPtr[3] & 0x04040404) | ((srcPtr[2] & 0x04040404) << 4);
   1729  		s2 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1730 		m = (srcPtr[5] & 0x04040404) | ((srcPtr[4] & 0x04040404) << 4);
   1731  		s3 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1732 		m = (srcPtr[7] & 0x04040404) | ((srcPtr[6] & 0x04040404) << 4);
   1733  		s4 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1734 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1735 		srcPtr += 8;
   1736 		dstPtr += 4;
   1737 		i -= 4;
   1738 	    }
   1739 	    while(i--) {
   1740 		m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
   1741  		*dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
   1742 		srcPtr += 2;
   1743 	    }
   1744 
   1745 	    writeSeq(0x02, 1 << 3);
   1746 	    dstPtr = dst;
   1747 	    srcPtr = src;
   1748 	    i = width;
   1749 	    j = phase;
   1750 	    while(j--) {
   1751 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1752  		*dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1753 		srcPtr += 2;
   1754 	    }
   1755 	    while(i >= 4) {
   1756 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1757  		s1 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1758 		m = (srcPtr[3] & 0x08080808) | ((srcPtr[2] & 0x08080808) << 4);
   1759  		s2 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1760 		m = (srcPtr[5] & 0x08080808) | ((srcPtr[4] & 0x08080808) << 4);
   1761  		s3 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1762 		m = (srcPtr[7] & 0x08080808) | ((srcPtr[6] & 0x08080808) << 4);
   1763  		s4 = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1764 		*((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
   1765 		srcPtr += 8;
   1766 		dstPtr += 4;
   1767 		i -= 4;
   1768 	    }
   1769 	    while(i--) {
   1770 		m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
   1771  		*dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
   1772 		srcPtr += 2;
   1773 	    }
   1774 
   1775             dst += FBPitch;
   1776             src += SRCPitch;
   1777         }
   1778         rects++;
   1779     }
   1780 }
   1781 #endif /* VGA16_FBCON_SUPPORT */
   1782 
   1783 void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area)
   1784 {
   1785 	struct fb_cmap cmap;
   1786 
   1787 	cmap.start = 0;
   1788 	cmap.len = palette_len;
   1789 	cmap.red = &area[0*palette_len];
   1790 	cmap.green = &area[1*palette_len];
   1791 	cmap.blue = &area[2*palette_len];
   1792 	cmap.transp = NULL;
   1793 	ioctl(console_fd, FBIOGETCMAP, &cmap);
   1794 }
   1795 
   1796 void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area)
   1797 {
   1798 	struct fb_cmap cmap;
   1799 
   1800 	cmap.start = 0;
   1801 	cmap.len = palette_len;
   1802 	cmap.red = &area[0*palette_len];
   1803 	cmap.green = &area[1*palette_len];
   1804 	cmap.blue = &area[2*palette_len];
   1805 	cmap.transp = NULL;
   1806 	ioctl(console_fd, FBIOPUTCMAP, &cmap);
   1807 }
   1808 
   1809 static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
   1810                                   struct fb_var_screeninfo *vinfo)
   1811 {
   1812 	int i;
   1813 
   1814 	/* Save hardware palette, if needed */
   1815 	if ( finfo->visual == FB_VISUAL_PSEUDOCOLOR ) {
   1816 		saved_cmaplen = 1<<vinfo->bits_per_pixel;
   1817 		saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
   1818 		if ( saved_cmap != NULL ) {
   1819 			FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
   1820 		}
   1821 	}
   1822 
   1823 	/* Added support for FB_VISUAL_DIRECTCOLOR.
   1824 	   With this mode pixel information is passed through the palette...
   1825 	   Neat fading and gamma correction effects can be had by simply
   1826 	   fooling around with the palette instead of changing the pixel
   1827 	   values themselves... Very neat!
   1828 
   1829 	   Adam Meyerowitz 1/19/2000
   1830 	   ameyerow (at) optonline.com
   1831 	*/
   1832 	if ( finfo->visual == FB_VISUAL_DIRECTCOLOR ) {
   1833 		__u16 new_entries[3*256];
   1834 
   1835 		/* Save the colormap */
   1836 		saved_cmaplen = 256;
   1837 		saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
   1838 		if ( saved_cmap != NULL ) {
   1839 			FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
   1840 		}
   1841 
   1842 		/* Allocate new identity colormap */
   1843 		for ( i=0; i<256; ++i ) {
   1844 	      		new_entries[(0*256)+i] =
   1845 			new_entries[(1*256)+i] =
   1846 			new_entries[(2*256)+i] = (i<<8)|i;
   1847 		}
   1848 		FB_RestorePaletteFrom(this, 256, new_entries);
   1849 	}
   1850 }
   1851 
   1852 static void FB_RestorePalette(_THIS)
   1853 {
   1854 	/* Restore the original palette */
   1855 	if ( saved_cmap ) {
   1856 		FB_RestorePaletteFrom(this, saved_cmaplen, saved_cmap);
   1857 		SDL_free(saved_cmap);
   1858 		saved_cmap = NULL;
   1859 	}
   1860 }
   1861 
   1862 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
   1863 {
   1864 	int i;
   1865 	__u16 r[256];
   1866 	__u16 g[256];
   1867 	__u16 b[256];
   1868 	struct fb_cmap cmap;
   1869 
   1870 	/* Set up the colormap */
   1871 	for (i = 0; i < ncolors; i++) {
   1872 		r[i] = colors[i].r << 8;
   1873 		g[i] = colors[i].g << 8;
   1874 		b[i] = colors[i].b << 8;
   1875 	}
   1876 	cmap.start = firstcolor;
   1877 	cmap.len = ncolors;
   1878 	cmap.red = r;
   1879 	cmap.green = g;
   1880 	cmap.blue = b;
   1881 	cmap.transp = NULL;
   1882 
   1883 	if( (ioctl(console_fd, FBIOPUTCMAP, &cmap) < 0) ||
   1884 	    !(this->screen->flags & SDL_HWPALETTE) ) {
   1885 	        colors = this->screen->format->palette->colors;
   1886 		ncolors = this->screen->format->palette->ncolors;
   1887 		cmap.start = 0;
   1888 		cmap.len = ncolors;
   1889 		SDL_memset(r, 0, sizeof(r));
   1890 		SDL_memset(g, 0, sizeof(g));
   1891 		SDL_memset(b, 0, sizeof(b));
   1892 		if ( ioctl(console_fd, FBIOGETCMAP, &cmap) == 0 ) {
   1893 			for ( i=ncolors-1; i>=0; --i ) {
   1894 				colors[i].r = (r[i]>>8);
   1895 				colors[i].g = (g[i]>>8);
   1896 				colors[i].b = (b[i]>>8);
   1897 			}
   1898 		}
   1899 		return(0);
   1900 	}
   1901 	return(1);
   1902 }
   1903 
   1904 /* Note:  If we are terminated, this could be called in the middle of
   1905    another SDL video routine -- notably UpdateRects.
   1906 */
   1907 static void FB_VideoQuit(_THIS)
   1908 {
   1909 	int i, j;
   1910 
   1911 	if ( this->screen ) {
   1912 		/* Clear screen and tell SDL not to free the pixels */
   1913 
   1914 		const char *dontClearPixels = SDL_getenv("SDL_FBCON_DONT_CLEAR");
   1915 
   1916 		/* If the framebuffer is not to be cleared, make sure that we won't
   1917 		 * display the previous frame when disabling double buffering. */
   1918 		if ( dontClearPixels && flip_page == 0 ) {
   1919 			SDL_memcpy(flip_address[0], flip_address[1], this->screen->pitch * this->screen->h);
   1920 		}
   1921 
   1922 		if ( !dontClearPixels && this->screen->pixels && FB_InGraphicsMode(this) ) {
   1923 #if defined(__powerpc__) || defined(__ia64__)	/* SIGBUS when using SDL_memset() ?? */
   1924 			Uint8 *rowp = (Uint8 *)this->screen->pixels;
   1925 			int left = this->screen->pitch*this->screen->h;
   1926 			while ( left-- ) { *rowp++ = 0; }
   1927 #else
   1928 			SDL_memset(this->screen->pixels,0,this->screen->h*this->screen->pitch);
   1929 #endif
   1930 		}
   1931 		/* This test fails when using the VGA16 shadow memory */
   1932 		if ( ((char *)this->screen->pixels >= mapped_mem) &&
   1933 		     ((char *)this->screen->pixels < (mapped_mem+mapped_memlen)) ) {
   1934 			this->screen->pixels = NULL;
   1935 		}
   1936 	}
   1937 
   1938 	/* Clear the lock mutex */
   1939 	if ( hw_lock ) {
   1940 		SDL_DestroyMutex(hw_lock);
   1941 		hw_lock = NULL;
   1942 	}
   1943 
   1944 	/* Clean up defined video modes */
   1945 	for ( i=0; i<NUM_MODELISTS; ++i ) {
   1946 		if ( SDL_modelist[i] != NULL ) {
   1947 			for ( j=0; SDL_modelist[i][j]; ++j ) {
   1948 				SDL_free(SDL_modelist[i][j]);
   1949 			}
   1950 			SDL_free(SDL_modelist[i]);
   1951 			SDL_modelist[i] = NULL;
   1952 		}
   1953 	}
   1954 
   1955 	/* Clean up the memory bucket list */
   1956 	FB_FreeHWSurfaces(this);
   1957 
   1958 	/* Close console and input file descriptors */
   1959 	if ( console_fd > 0 ) {
   1960 		/* Unmap the video framebuffer and I/O registers */
   1961 		if ( mapped_mem ) {
   1962 			munmap(mapped_mem, mapped_memlen);
   1963 			mapped_mem = NULL;
   1964 		}
   1965 		if ( mapped_io ) {
   1966 			munmap(mapped_io, mapped_iolen);
   1967 			mapped_io = NULL;
   1968 		}
   1969 
   1970 		/* Restore the original video mode and palette */
   1971 		if ( FB_InGraphicsMode(this) ) {
   1972 			FB_RestorePalette(this);
   1973 			ioctl(console_fd, FBIOPUT_VSCREENINFO, &saved_vinfo);
   1974 		}
   1975 
   1976 		/* We're all done with the framebuffer */
   1977 		close(console_fd);
   1978 		console_fd = -1;
   1979 	}
   1980 	FB_CloseMouse(this);
   1981 	FB_CloseKeyboard(this);
   1982 }
   1983