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@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};
129enum {
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 */
139static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
140static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
141static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
142#ifdef VGA16_FBCON_SUPPORT
143static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
144#endif
145static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
146static void FB_VideoQuit(_THIS);
147
148/* Hardware surface functions */
149static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
150static void FB_FreeHWSurfaces(_THIS);
151static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
152static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
153static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
154static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
155static void FB_WaitVBL(_THIS);
156static void FB_WaitIdle(_THIS);
157static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
158
159/* Internal palette functions */
160static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
161                                  struct fb_var_screeninfo *vinfo);
162static void FB_RestorePalette(_THIS);
163
164/* Shadow buffer functions */
165static FB_bitBlit FB_blit16;
166static FB_bitBlit FB_blit16blocked;
167
168static 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
184static 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
197static 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
219static void FB_DeleteDevice(SDL_VideoDevice *device)
220{
221	SDL_free(device->hidden);
222	SDL_free(device);
223}
224
225static 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
278VideoBootStrap FBCON_bootstrap = {
279	"fbcon", "Linux Framebuffer Console",
280	FB_Available, FB_CreateDevice
281};
282
283#define FB_MODES_DB	"/etc/fb.modes"
284
285static 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
318static 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
385static 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
410static 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
476static 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
486static 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
496static 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
815static 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 */
821static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
822#ifdef VGA16_FBCON_SUPPORT
823static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
824#endif
825
826#ifdef FBCON_DEBUG
827static 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}
857static 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
875static 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
906static 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
939static 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
998static 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
1190void 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
1218static 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}
1250static 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
1263static 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*/
1274if ( surface->pitch > SDL_VideoSurface->pitch ) {
1275	SDL_SetError("Surface requested wider than screen");
1276	return(-1);
1277}
1278surface->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}
1338static 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
1386static 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}
1403static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
1404{
1405	if ( surface == this->screen ) {
1406		SDL_mutexV(hw_lock);
1407	}
1408}
1409
1410static 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
1418static void FB_WaitIdle(_THIS)
1419{
1420	return;
1421}
1422
1423static 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
1445static 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
1469static 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
1496static 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) \
1613outb(index, 0x3CE);           \
1614outb(value, 0x3CF);
1615#define writeSeq(index, value) \
1616outb(index, 0x3C4);            \
1617outb(value, 0x3C5);
1618
1619static 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
1783void 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
1796void 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
1809static 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@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
1852static 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
1862static 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*/
1907static 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