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@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/*
60static 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) */
68static 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};
85static 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 */
131static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
132static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
133static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
134#ifdef VGA16_FBCON_SUPPORT
135static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
136#endif
137static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
138static void FB_VideoQuit(_THIS);
139
140/* Hardware surface functions */
141static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
142static void FB_FreeHWSurfaces(_THIS);
143static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
144static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
145static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
146static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
147static void FB_WaitVBL(_THIS);
148static void FB_WaitIdle(_THIS);
149static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
150
151/* Internal palette functions */
152static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
153                                  struct fb_var_screeninfo *vinfo);
154static void FB_RestorePalette(_THIS);
155
156static 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
172static 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
185static 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
207static void FB_DeleteDevice(SDL_VideoDevice *device)
208{
209	SDL_free(device->hidden);
210	SDL_free(device);
211}
212
213static 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
266VideoBootStrap FBCON_bootstrap = {
267	"fbcon", "Linux Framebuffer Console",
268	FB_Available, FB_CreateDevice
269};
270
271#define FB_MODES_DB	"/etc/fb.modes"
272
273static 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
306static 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
373static 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
398static 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
464static 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
474static 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
484static 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
738static 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 */
744static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
745#ifdef VGA16_FBCON_SUPPORT
746static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
747#endif
748
749#ifdef FBCON_DEBUG
750static 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}
780static 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
798static 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
829static 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
862static 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
921static 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
1077void 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
1105static 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}
1137static 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
1150static 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*/
1161if ( surface->pitch > SDL_VideoSurface->pitch ) {
1162	SDL_SetError("Surface requested wider than screen");
1163	return(-1);
1164}
1165surface->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}
1225static 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
1273static 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}
1290static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
1291{
1292	if ( surface == this->screen ) {
1293		SDL_mutexV(hw_lock);
1294	}
1295}
1296
1297static 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
1305static void FB_WaitIdle(_THIS)
1306{
1307	return;
1308}
1309
1310static 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
1332static 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) \
1341outb(index, 0x3CE);           \
1342outb(value, 0x3CF);
1343#define writeSeq(index, value) \
1344outb(index, 0x3C4);            \
1345outb(value, 0x3C5);
1346
1347static 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
1511void 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
1524void 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
1537static 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@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
1580static 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
1590static 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*/
1635static 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