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