aboutsummaryrefslogtreecommitdiffstats
path: root/distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c
diff options
context:
space:
mode:
Diffstat (limited to 'distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c')
-rw-r--r--distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c1982
1 files changed, 1982 insertions, 0 deletions
diff --git a/distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c b/distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c
new file mode 100644
index 0000000..5e58809
--- /dev/null
+++ b/distrib/sdl-1.2.15/src/video/fbcon/SDL_fbvideo.c
@@ -0,0 +1,1982 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* Framebuffer console based SDL video driver implementation.
+*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#ifndef HAVE_GETPAGESIZE
+#include <asm/page.h> /* For definition of PAGE_SIZE */
+#endif
+
+#include <linux/vt.h>
+
+#include "SDL_video.h"
+#include "SDL_mouse.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_pixels_c.h"
+#include "../../events/SDL_events_c.h"
+#include "SDL_fbvideo.h"
+#include "SDL_fbmouse_c.h"
+#include "SDL_fbevents_c.h"
+#include "SDL_fb3dfx.h"
+#include "SDL_fbmatrox.h"
+#include "SDL_fbriva.h"
+
+/*#define FBCON_DEBUG*/
+
+#if defined(i386) && defined(FB_TYPE_VGA_PLANES)
+#define VGA16_FBCON_SUPPORT
+#include <sys/io.h> /* For ioperm() */
+#ifndef FB_AUX_VGA_PLANES_VGA4
+#define FB_AUX_VGA_PLANES_VGA4 0
+#endif
+/*
+static inline void outb (unsigned char value, unsigned short port)
+{
+ __asm__ __volatile__ ("outb %b0,%w1"::"a" (value), "Nd" (port));
+}
+*/
+#endif /* FB_TYPE_VGA_PLANES */
+
+/* A list of video resolutions that we query for (sorted largest to smallest) */
+static const SDL_Rect checkres[] = {
+ { 0, 0, 1600, 1200 }, /* 16 bpp: 0x11E, or 286 */
+ { 0, 0, 1408, 1056 }, /* 16 bpp: 0x19A, or 410 */
+ { 0, 0, 1280, 1024 }, /* 16 bpp: 0x11A, or 282 */
+ { 0, 0, 1152, 864 }, /* 16 bpp: 0x192, or 402 */
+ { 0, 0, 1024, 768 }, /* 16 bpp: 0x117, or 279 */
+ { 0, 0, 960, 720 }, /* 16 bpp: 0x18A, or 394 */
+ { 0, 0, 800, 600 }, /* 16 bpp: 0x114, or 276 */
+ { 0, 0, 768, 576 }, /* 16 bpp: 0x182, or 386 */
+ { 0, 0, 720, 576 }, /* PAL */
+ { 0, 0, 720, 480 }, /* NTSC */
+ { 0, 0, 640, 480 }, /* 16 bpp: 0x111, or 273 */
+ { 0, 0, 640, 400 }, /* 8 bpp: 0x100, or 256 */
+ { 0, 0, 512, 384 },
+ { 0, 0, 320, 240 },
+ { 0, 0, 320, 200 }
+};
+static const struct {
+ int xres;
+ int yres;
+ int pixclock;
+ int left;
+ int right;
+ int upper;
+ int lower;
+ int hslen;
+ int vslen;
+ int sync;
+ int vmode;
+} vesa_timings[] = {
+#ifdef USE_VESA_TIMINGS /* Only tested on Matrox Millenium I */
+ { 640, 400, 39771, 48, 16, 39, 8, 96, 2, 2, 0 }, /* 70 Hz */
+ { 640, 480, 39683, 48, 16, 33, 10, 96, 2, 0, 0 }, /* 60 Hz */
+ { 768, 576, 26101, 144, 16, 28, 6, 112, 4, 0, 0 }, /* 60 Hz */
+ { 800, 600, 24038, 144, 24, 28, 8, 112, 6, 0, 0 }, /* 60 Hz */
+ { 960, 720, 17686, 144, 24, 28, 8, 112, 4, 0, 0 }, /* 60 Hz */
+ { 1024, 768, 15386, 160, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
+ { 1152, 864, 12286, 192, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
+ { 1280, 1024, 9369, 224, 32, 32, 4, 136, 4, 0, 0 }, /* 60 Hz */
+ { 1408, 1056, 8214, 256, 40, 32, 5, 144, 5, 0, 0 }, /* 60 Hz */
+ { 1600, 1200,/*?*/0, 272, 48, 32, 5, 152, 5, 0, 0 }, /* 60 Hz */
+#else
+ /* You can generate these timings from your XF86Config file using
+ the 'modeline2fb' perl script included with the fbset package.
+ These timings were generated for Matrox Millenium I, 15" monitor.
+ */
+ { 320, 200, 79440, 16, 16, 20, 4, 48, 1, 0, 2 }, /* 70 Hz */
+ { 320, 240, 63492, 16, 16, 16, 4, 48, 2, 0, 2 }, /* 72 Hz */
+ { 512, 384, 49603, 48, 16, 16, 1, 64, 3, 0, 0 }, /* 78 Hz */
+ { 640, 400, 31746, 96, 32, 41, 1, 64, 3, 2, 0 }, /* 85 Hz */
+ { 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, 0 }, /* 75 Hz */
+ { 768, 576, 26101, 144, 16, 28, 6, 112, 4, 0, 0 }, /* 60 Hz */
+ { 800, 600, 20000, 64, 56, 23, 37, 120, 6, 3, 0 }, /* 72 Hz */
+ { 960, 720, 17686, 144, 24, 28, 8, 112, 4, 0, 0 }, /* 60 Hz */
+ { 1024, 768, 13333, 144, 24, 29, 3, 136, 6, 0, 0 }, /* 70 Hz */
+ { 1152, 864, 12286, 192, 32, 30, 4, 128, 4, 0, 0 }, /* 60 Hz */
+ { 1280, 1024, 9369, 224, 32, 32, 4, 136, 4, 0, 0 }, /* 60 Hz */
+ { 1408, 1056, 8214, 256, 40, 32, 5, 144, 5, 0, 0 }, /* 60 Hz */
+ { 1600, 1200,/*?*/0, 272, 48, 32, 5, 152, 5, 0, 0 }, /* 60 Hz */
+#endif
+};
+enum {
+ FBCON_ROTATE_NONE = 0,
+ FBCON_ROTATE_CCW = 90,
+ FBCON_ROTATE_UD = 180,
+ FBCON_ROTATE_CW = 270
+};
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/* Initialization/Query functions */
+static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
+static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
+static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
+#ifdef VGA16_FBCON_SUPPORT
+static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
+#endif
+static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
+static void FB_VideoQuit(_THIS);
+
+/* Hardware surface functions */
+static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size);
+static void FB_FreeHWSurfaces(_THIS);
+static int FB_AllocHWSurface(_THIS, SDL_Surface *surface);
+static int FB_LockHWSurface(_THIS, SDL_Surface *surface);
+static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface);
+static void FB_FreeHWSurface(_THIS, SDL_Surface *surface);
+static void FB_WaitVBL(_THIS);
+static void FB_WaitIdle(_THIS);
+static int FB_FlipHWSurface(_THIS, SDL_Surface *surface);
+
+/* Internal palette functions */
+static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
+ struct fb_var_screeninfo *vinfo);
+static void FB_RestorePalette(_THIS);
+
+/* Shadow buffer functions */
+static FB_bitBlit FB_blit16;
+static FB_bitBlit FB_blit16blocked;
+
+static int SDL_getpagesize(void)
+{
+#ifdef HAVE_GETPAGESIZE
+ return getpagesize();
+#elif defined(PAGE_SIZE)
+ return PAGE_SIZE;
+#else
+#error Can not determine system page size.
+ return 4096; /* this is what it USED to be in Linux... */
+#endif
+}
+
+
+/* Small wrapper for mmap() so we can play nicely with no-mmu hosts
+ * (non-mmu hosts disallow the MAP_SHARED flag) */
+
+static void *do_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
+{
+ void *ret;
+ ret = mmap(start, length, prot, flags, fd, offset);
+ if ( ret == (char *)-1 && (flags & MAP_SHARED) ) {
+ ret = mmap(start, length, prot,
+ (flags & ~MAP_SHARED) | MAP_PRIVATE, fd, offset);
+ }
+ return ret;
+}
+
+/* FB driver bootstrap functions */
+
+static int FB_Available(void)
+{
+ int console = -1;
+ /* Added check for /fb/0 (devfs) */
+ /* but - use environment variable first... if it fails, still check defaults */
+ int idx = 0;
+ const char *SDL_fbdevs[4] = { NULL, "/dev/fb0", "/dev/fb/0", NULL };
+
+ SDL_fbdevs[0] = SDL_getenv("SDL_FBDEV");
+ if( !SDL_fbdevs[0] )
+ idx++;
+ for( ; SDL_fbdevs[idx]; idx++ )
+ {
+ console = open(SDL_fbdevs[idx], O_RDWR, 0);
+ if ( console >= 0 ) {
+ close(console);
+ break;
+ }
+ }
+ return(console >= 0);
+}
+
+static void FB_DeleteDevice(SDL_VideoDevice *device)
+{
+ SDL_free(device->hidden);
+ SDL_free(device);
+}
+
+static SDL_VideoDevice *FB_CreateDevice(int devindex)
+{
+ SDL_VideoDevice *this;
+
+ /* Initialize all variables that we clean on shutdown */
+ this = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
+ if ( this ) {
+ SDL_memset(this, 0, (sizeof *this));
+ this->hidden = (struct SDL_PrivateVideoData *)
+ SDL_malloc((sizeof *this->hidden));
+ }
+ if ( (this == NULL) || (this->hidden == NULL) ) {
+ SDL_OutOfMemory();
+ if ( this ) {
+ SDL_free(this);
+ }
+ return(0);
+ }
+ SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+ wait_vbl = FB_WaitVBL;
+ wait_idle = FB_WaitIdle;
+ mouse_fd = -1;
+ keyboard_fd = -1;
+
+ /* Set the function pointers */
+ this->VideoInit = FB_VideoInit;
+ this->ListModes = FB_ListModes;
+ this->SetVideoMode = FB_SetVideoMode;
+ this->SetColors = FB_SetColors;
+ this->UpdateRects = NULL;
+ this->VideoQuit = FB_VideoQuit;
+ this->AllocHWSurface = FB_AllocHWSurface;
+ this->CheckHWBlit = NULL;
+ this->FillHWRect = NULL;
+ this->SetHWColorKey = NULL;
+ this->SetHWAlpha = NULL;
+ this->LockHWSurface = FB_LockHWSurface;
+ this->UnlockHWSurface = FB_UnlockHWSurface;
+ this->FlipHWSurface = FB_FlipHWSurface;
+ this->FreeHWSurface = FB_FreeHWSurface;
+ this->SetCaption = NULL;
+ this->SetIcon = NULL;
+ this->IconifyWindow = NULL;
+ this->GrabInput = NULL;
+ this->GetWMInfo = NULL;
+ this->InitOSKeymap = FB_InitOSKeymap;
+ this->PumpEvents = FB_PumpEvents;
+
+ this->free = FB_DeleteDevice;
+
+ return this;
+}
+
+VideoBootStrap FBCON_bootstrap = {
+ "fbcon", "Linux Framebuffer Console",
+ FB_Available, FB_CreateDevice
+};
+
+#define FB_MODES_DB "/etc/fb.modes"
+
+static int read_fbmodes_line(FILE*f, char* line, int length)
+{
+ int blank;
+ char* c;
+ int i;
+
+ blank=0;
+ /* find a relevant line */
+ do
+ {
+ if (!fgets(line,length,f))
+ return 0;
+ c=line;
+ while(((*c=='\t')||(*c==' '))&&(*c!=0))
+ c++;
+
+ if ((*c=='\n')||(*c=='#')||(*c==0))
+ blank=1;
+ else
+ blank=0;
+ }
+ while(blank);
+ /* remove whitespace at the begining of the string */
+ i=0;
+ do
+ {
+ line[i]=c[i];
+ i++;
+ }
+ while(c[i]!=0);
+ return 1;
+}
+
+static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
+{
+ char line[1024];
+ char option[256];
+
+ /* Find a "geometry" */
+ do {
+ if (read_fbmodes_line(f, line, sizeof(line))==0)
+ return 0;
+ if (SDL_strncmp(line,"geometry",8)==0)
+ break;
+ }
+ while(1);
+
+ SDL_sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres,
+ &vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
+ if (read_fbmodes_line(f, line, sizeof(line))==0)
+ return 0;
+
+ SDL_sscanf(line, "timings %d %d %d %d %d %d %d", &vinfo->pixclock,
+ &vinfo->left_margin, &vinfo->right_margin, &vinfo->upper_margin,
+ &vinfo->lower_margin, &vinfo->hsync_len, &vinfo->vsync_len);
+
+ vinfo->sync=0;
+ vinfo->vmode=FB_VMODE_NONINTERLACED;
+
+ /* Parse misc options */
+ do {
+ if (read_fbmodes_line(f, line, sizeof(line))==0)
+ return 0;
+
+ if (SDL_strncmp(line,"hsync",5)==0) {
+ SDL_sscanf(line,"hsync %s",option);
+ if (SDL_strncmp(option,"high",4)==0)
+ vinfo->sync |= FB_SYNC_HOR_HIGH_ACT;
+ }
+ else if (SDL_strncmp(line,"vsync",5)==0) {
+ SDL_sscanf(line,"vsync %s",option);
+ if (SDL_strncmp(option,"high",4)==0)
+ vinfo->sync |= FB_SYNC_VERT_HIGH_ACT;
+ }
+ else if (SDL_strncmp(line,"csync",5)==0) {
+ SDL_sscanf(line,"csync %s",option);
+ if (SDL_strncmp(option,"high",4)==0)
+ vinfo->sync |= FB_SYNC_COMP_HIGH_ACT;
+ }
+ else if (SDL_strncmp(line,"extsync",5)==0) {
+ SDL_sscanf(line,"extsync %s",option);
+ if (SDL_strncmp(option,"true",4)==0)
+ vinfo->sync |= FB_SYNC_EXT;
+ }
+ else if (SDL_strncmp(line,"laced",5)==0) {
+ SDL_sscanf(line,"laced %s",option);
+ if (SDL_strncmp(option,"true",4)==0)
+ vinfo->vmode |= FB_VMODE_INTERLACED;
+ }
+ else if (SDL_strncmp(line,"double",6)==0) {
+ SDL_sscanf(line,"double %s",option);
+ if (SDL_strncmp(option,"true",4)==0)
+ vinfo->vmode |= FB_VMODE_DOUBLE;
+ }
+ }
+ while(SDL_strncmp(line,"endmode",7)!=0);
+
+ return 1;
+}
+
+static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
+ int index, unsigned int *w, unsigned int *h)
+{
+ int mode_okay;
+
+ mode_okay = 0;
+ vinfo->bits_per_pixel = (index+1)*8;
+ vinfo->xres = *w;
+ vinfo->xres_virtual = *w;
+ vinfo->yres = *h;
+ vinfo->yres_virtual = *h;
+ vinfo->activate = FB_ACTIVATE_TEST;
+ if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, vinfo) == 0 ) {
+#ifdef FBCON_DEBUG
+ 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);
+#endif
+ if ( (((vinfo->bits_per_pixel+7)/8)-1) == index ) {
+ *w = vinfo->xres;
+ *h = vinfo->yres;
+ mode_okay = 1;
+ }
+ }
+ return mode_okay;
+}
+
+static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
+{
+ SDL_Rect *mode;
+ int i;
+ int next_mode;
+
+ /* Check to see if we already have this mode */
+ if ( SDL_nummodes[index] > 0 ) {
+ mode = SDL_modelist[index][SDL_nummodes[index]-1];
+ if ( (mode->w == w) && (mode->h == h) ) {
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "We already have mode %dx%d at %d bytes per pixel\n", w, h, index+1);
+#endif
+ return(0);
+ }
+ }
+
+ /* Only allow a mode if we have a valid timing for it */
+ if ( check_timings ) {
+ int found_timing = 0;
+ for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
+ if ( (w == vesa_timings[i].xres) &&
+ (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
+ found_timing = 1;
+ break;
+ }
+ }
+ if ( !found_timing ) {
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
+#endif
+ return(0);
+ }
+ }
+
+ /* Set up the new video mode rectangle */
+ mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
+ if ( mode == NULL ) {
+ SDL_OutOfMemory();
+ return(-1);
+ }
+ mode->x = 0;
+ mode->y = 0;
+ mode->w = w;
+ mode->h = h;
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
+#endif
+
+ /* Allocate the new list of modes, and fill in the new mode */
+ next_mode = SDL_nummodes[index];
+ SDL_modelist[index] = (SDL_Rect **)
+ SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *));
+ if ( SDL_modelist[index] == NULL ) {
+ SDL_OutOfMemory();
+ SDL_nummodes[index] = 0;
+ SDL_free(mode);
+ return(-1);
+ }
+ SDL_modelist[index][next_mode] = mode;
+ SDL_modelist[index][next_mode+1] = NULL;
+ SDL_nummodes[index]++;
+
+ return(0);
+}
+
+static int cmpmodes(const void *va, const void *vb)
+{
+ const SDL_Rect *a = *(const SDL_Rect**)va;
+ const SDL_Rect *b = *(const SDL_Rect**)vb;
+ if ( a->h == b->h )
+ return b->w - a->w;
+ else
+ return b->h - a->h;
+}
+
+static void FB_SortModes(_THIS)
+{
+ int i;
+ for ( i=0; i<NUM_MODELISTS; ++i ) {
+ if ( SDL_nummodes[i] > 0 ) {
+ SDL_qsort(SDL_modelist[i], SDL_nummodes[i], sizeof *SDL_modelist[i], cmpmodes);
+ }
+ }
+}
+
+static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
+{
+ const int pagesize = SDL_getpagesize();
+ struct fb_fix_screeninfo finfo;
+ struct fb_var_screeninfo vinfo;
+ int i, j;
+ int current_index;
+ unsigned int current_w;
+ unsigned int current_h;
+ const char *SDL_fbdev;
+ const char *rotation;
+ FILE *modesdb;
+
+ /* Initialize the library */
+ SDL_fbdev = SDL_getenv("SDL_FBDEV");
+ if ( SDL_fbdev == NULL ) {
+ SDL_fbdev = "/dev/fb0";
+ }
+ console_fd = open(SDL_fbdev, O_RDWR, 0);
+ if ( console_fd < 0 ) {
+ SDL_SetError("Unable to open %s", SDL_fbdev);
+ return(-1);
+ }
+
+#if !SDL_THREADS_DISABLED
+ /* Create the hardware surface lock mutex */
+ hw_lock = SDL_CreateMutex();
+ if ( hw_lock == NULL ) {
+ SDL_SetError("Unable to create lock mutex");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+#endif
+
+ /* Get the type of video hardware */
+ if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
+ SDL_SetError("Couldn't get console hardware info");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ switch (finfo.type) {
+ case FB_TYPE_PACKED_PIXELS:
+ /* Supported, no worries.. */
+ break;
+#ifdef VGA16_FBCON_SUPPORT
+ case FB_TYPE_VGA_PLANES:
+ /* VGA16 is supported, but that's it */
+ if ( finfo.type_aux == FB_AUX_VGA_PLANES_VGA4 ) {
+ if ( ioperm(0x3b4, 0x3df - 0x3b4 + 1, 1) < 0 ) {
+ SDL_SetError("No I/O port permissions");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ this->SetVideoMode = FB_SetVGA16Mode;
+ break;
+ }
+ /* Fall through to unsupported case */
+#endif /* VGA16_FBCON_SUPPORT */
+ default:
+ SDL_SetError("Unsupported console hardware");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ switch (finfo.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_DIRECTCOLOR:
+ break;
+ default:
+ SDL_SetError("Unsupported console hardware");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+
+ /* Check if the user wants to disable hardware acceleration */
+ { const char *fb_accel;
+ fb_accel = SDL_getenv("SDL_FBACCEL");
+ if ( fb_accel ) {
+ finfo.accel = SDL_atoi(fb_accel);
+ }
+ }
+
+ /* Memory map the device, compensating for buggy PPC mmap() */
+ mapped_offset = (((long)finfo.smem_start) -
+ (((long)finfo.smem_start)&~(pagesize-1)));
+ mapped_memlen = finfo.smem_len+mapped_offset;
+ mapped_mem = do_mmap(NULL, mapped_memlen,
+ PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);
+ if ( mapped_mem == (char *)-1 ) {
+ SDL_SetError("Unable to memory map the video hardware");
+ mapped_mem = NULL;
+ FB_VideoQuit(this);
+ return(-1);
+ }
+
+ /* Determine the current screen depth */
+ if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
+ SDL_SetError("Couldn't get console pixel format");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ vformat->BitsPerPixel = vinfo.bits_per_pixel;
+ if ( vformat->BitsPerPixel < 8 ) {
+ /* Assuming VGA16, we handle this via a shadow framebuffer */
+ vformat->BitsPerPixel = 8;
+ }
+ for ( i=0; i<vinfo.red.length; ++i ) {
+ vformat->Rmask <<= 1;
+ vformat->Rmask |= (0x00000001<<vinfo.red.offset);
+ }
+ for ( i=0; i<vinfo.green.length; ++i ) {
+ vformat->Gmask <<= 1;
+ vformat->Gmask |= (0x00000001<<vinfo.green.offset);
+ }
+ for ( i=0; i<vinfo.blue.length; ++i ) {
+ vformat->Bmask <<= 1;
+ vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
+ }
+ saved_vinfo = vinfo;
+
+ /* Save hardware palette, if needed */
+ FB_SavePalette(this, &finfo, &vinfo);
+
+ /* If the I/O registers are available, memory map them so we
+ can take advantage of any supported hardware acceleration.
+ */
+ vinfo.accel_flags = 0; /* Temporarily reserve registers */
+ ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo);
+ if ( finfo.accel && finfo.mmio_len ) {
+ mapped_iolen = finfo.mmio_len;
+ mapped_io = do_mmap(NULL, mapped_iolen, PROT_READ|PROT_WRITE,
+ MAP_SHARED, console_fd, mapped_memlen);
+ if ( mapped_io == (char *)-1 ) {
+ /* Hmm, failed to memory map I/O registers */
+ mapped_io = NULL;
+ }
+ }
+
+ rotate = FBCON_ROTATE_NONE;
+ rotation = SDL_getenv("SDL_VIDEO_FBCON_ROTATION");
+ if (rotation != NULL) {
+ if (SDL_strlen(rotation) == 0) {
+ shadow_fb = 0;
+ rotate = FBCON_ROTATE_NONE;
+#ifdef FBCON_DEBUG
+ printf("Not rotating, no shadow\n");
+#endif
+ } else if (!SDL_strcmp(rotation, "NONE")) {
+ shadow_fb = 1;
+ rotate = FBCON_ROTATE_NONE;
+#ifdef FBCON_DEBUG
+ printf("Not rotating, but still using shadow\n");
+#endif
+ } else if (!SDL_strcmp(rotation, "CW")) {
+ shadow_fb = 1;
+ rotate = FBCON_ROTATE_CW;
+#ifdef FBCON_DEBUG
+ printf("Rotating screen clockwise\n");
+#endif
+ } else if (!SDL_strcmp(rotation, "CCW")) {
+ shadow_fb = 1;
+ rotate = FBCON_ROTATE_CCW;
+#ifdef FBCON_DEBUG
+ printf("Rotating screen counter clockwise\n");
+#endif
+ } else if (!SDL_strcmp(rotation, "UD")) {
+ shadow_fb = 1;
+ rotate = FBCON_ROTATE_UD;
+#ifdef FBCON_DEBUG
+ printf("Rotating screen upside down\n");
+#endif
+ } else {
+ SDL_SetError("\"%s\" is not a valid value for "
+ "SDL_VIDEO_FBCON_ROTATION", rotation);
+ return(-1);
+ }
+ }
+
+ if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+ current_w = vinfo.yres;
+ current_h = vinfo.xres;
+ } else {
+ current_w = vinfo.xres;
+ current_h = vinfo.yres;
+ }
+
+ /* Query for the list of available video modes */
+ current_index = ((vinfo.bits_per_pixel+7)/8)-1;
+ modesdb = fopen(FB_MODES_DB, "r");
+ for ( i=0; i<NUM_MODELISTS; ++i ) {
+ SDL_nummodes[i] = 0;
+ SDL_modelist[i] = NULL;
+ }
+ if ( SDL_getenv("SDL_FB_BROKEN_MODES") != NULL ) {
+ FB_AddMode(this, current_index, current_w, current_h, 0);
+ } else if(modesdb) {
+ while ( read_fbmodes_mode(modesdb, &vinfo) ) {
+ for ( i=0; i<NUM_MODELISTS; ++i ) {
+ unsigned int w, h;
+
+ if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+ w = vinfo.yres;
+ h = vinfo.xres;
+ } else {
+ w = vinfo.xres;
+ h = vinfo.yres;
+ }
+ /* See if we are querying for the current mode */
+ if ( i == current_index ) {
+ if ( (current_w > w) || (current_h > h) ) {
+ /* Only check once */
+ FB_AddMode(this, i, current_w, current_h, 0);
+ current_index = -1;
+ }
+ }
+ if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
+ FB_AddMode(this, i, w, h, 0);
+ }
+ }
+ }
+ fclose(modesdb);
+ FB_SortModes(this);
+ } else {
+ for ( i=0; i<NUM_MODELISTS; ++i ) {
+ for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
+ unsigned int w, h;
+
+ if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+ w = checkres[j].h;
+ h = checkres[j].w;
+ } else {
+ w = checkres[j].w;
+ h = checkres[j].h;
+ }
+ /* See if we are querying for the current mode */
+ if ( i == current_index ) {
+ if ( (current_w > w) || (current_h > h) ) {
+ /* Only check once */
+ FB_AddMode(this, i, current_w, current_h, 0);
+ current_index = -1;
+ }
+ }
+ if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
+ FB_AddMode(this, i, w, h, 1);
+ }
+ }
+ }
+ }
+
+ this->info.current_w = current_w;
+ this->info.current_h = current_h;
+ this->info.wm_available = 0;
+ this->info.hw_available = !shadow_fb;
+ this->info.video_mem = shadow_fb ? 0 : finfo.smem_len/1024;
+ /* Fill in our hardware acceleration capabilities */
+ if ( mapped_io ) {
+ switch (finfo.accel) {
+ case FB_ACCEL_MATROX_MGA2064W:
+ case FB_ACCEL_MATROX_MGA1064SG:
+ case FB_ACCEL_MATROX_MGA2164W:
+ case FB_ACCEL_MATROX_MGA2164W_AGP:
+ case FB_ACCEL_MATROX_MGAG100:
+ /*case FB_ACCEL_MATROX_MGAG200: G200 acceleration broken! */
+ case FB_ACCEL_MATROX_MGAG400:
+#ifdef FBACCEL_DEBUG
+ printf("Matrox hardware accelerator!\n");
+#endif
+ FB_MatroxAccel(this, finfo.accel);
+ break;
+ case FB_ACCEL_3DFX_BANSHEE:
+#ifdef FBACCEL_DEBUG
+ printf("3DFX hardware accelerator!\n");
+#endif
+ FB_3DfxAccel(this, finfo.accel);
+ break;
+ case FB_ACCEL_NV3:
+ case FB_ACCEL_NV4:
+#ifdef FBACCEL_DEBUG
+ printf("NVidia hardware accelerator!\n");
+#endif
+ FB_RivaAccel(this, finfo.accel);
+ break;
+ default:
+#ifdef FBACCEL_DEBUG
+ printf("Unknown hardware accelerator.\n");
+#endif
+ break;
+ }
+ }
+
+ if (shadow_fb) {
+ shadow_mem = (char *)SDL_malloc(mapped_memlen);
+ if (shadow_mem == NULL) {
+ SDL_SetError("No memory for shadow");
+ return (-1);
+ }
+ }
+
+ /* Enable mouse and keyboard support */
+ if ( FB_OpenKeyboard(this) < 0 ) {
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ if ( FB_OpenMouse(this) < 0 ) {
+ const char *sdl_nomouse;
+
+ sdl_nomouse = SDL_getenv("SDL_NOMOUSE");
+ if ( ! sdl_nomouse ) {
+ SDL_SetError("Unable to open mouse");
+ FB_VideoQuit(this);
+ return(-1);
+ }
+ }
+
+ /* We're done! */
+ return(0);
+}
+
+static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
+{
+ return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]);
+}
+
+/* Various screen update functions available */
+static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects);
+#ifdef VGA16_FBCON_SUPPORT
+static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects);
+#endif
+
+#ifdef FBCON_DEBUG
+static void print_vinfo(struct fb_var_screeninfo *vinfo)
+{
+ fprintf(stderr, "Printing vinfo:\n");
+ fprintf(stderr, "\txres: %d\n", vinfo->xres);
+ fprintf(stderr, "\tyres: %d\n", vinfo->yres);
+ fprintf(stderr, "\txres_virtual: %d\n", vinfo->xres_virtual);
+ fprintf(stderr, "\tyres_virtual: %d\n", vinfo->yres_virtual);
+ fprintf(stderr, "\txoffset: %d\n", vinfo->xoffset);
+ fprintf(stderr, "\tyoffset: %d\n", vinfo->yoffset);
+ fprintf(stderr, "\tbits_per_pixel: %d\n", vinfo->bits_per_pixel);
+ fprintf(stderr, "\tgrayscale: %d\n", vinfo->grayscale);
+ fprintf(stderr, "\tnonstd: %d\n", vinfo->nonstd);
+ fprintf(stderr, "\tactivate: %d\n", vinfo->activate);
+ fprintf(stderr, "\theight: %d\n", vinfo->height);
+ fprintf(stderr, "\twidth: %d\n", vinfo->width);
+ fprintf(stderr, "\taccel_flags: %d\n", vinfo->accel_flags);
+ fprintf(stderr, "\tpixclock: %d\n", vinfo->pixclock);
+ fprintf(stderr, "\tleft_margin: %d\n", vinfo->left_margin);
+ fprintf(stderr, "\tright_margin: %d\n", vinfo->right_margin);
+ fprintf(stderr, "\tupper_margin: %d\n", vinfo->upper_margin);
+ fprintf(stderr, "\tlower_margin: %d\n", vinfo->lower_margin);
+ fprintf(stderr, "\thsync_len: %d\n", vinfo->hsync_len);
+ fprintf(stderr, "\tvsync_len: %d\n", vinfo->vsync_len);
+ fprintf(stderr, "\tsync: %d\n", vinfo->sync);
+ fprintf(stderr, "\tvmode: %d\n", vinfo->vmode);
+ fprintf(stderr, "\tred: %d/%d\n", vinfo->red.length, vinfo->red.offset);
+ fprintf(stderr, "\tgreen: %d/%d\n", vinfo->green.length, vinfo->green.offset);
+ fprintf(stderr, "\tblue: %d/%d\n", vinfo->blue.length, vinfo->blue.offset);
+ fprintf(stderr, "\talpha: %d/%d\n", vinfo->transp.length, vinfo->transp.offset);
+}
+static void print_finfo(struct fb_fix_screeninfo *finfo)
+{
+ fprintf(stderr, "Printing finfo:\n");
+ fprintf(stderr, "\tsmem_start = %p\n", (char *)finfo->smem_start);
+ fprintf(stderr, "\tsmem_len = %d\n", finfo->smem_len);
+ fprintf(stderr, "\ttype = %d\n", finfo->type);
+ fprintf(stderr, "\ttype_aux = %d\n", finfo->type_aux);
+ fprintf(stderr, "\tvisual = %d\n", finfo->visual);
+ fprintf(stderr, "\txpanstep = %d\n", finfo->xpanstep);
+ fprintf(stderr, "\typanstep = %d\n", finfo->ypanstep);
+ fprintf(stderr, "\tywrapstep = %d\n", finfo->ywrapstep);
+ fprintf(stderr, "\tline_length = %d\n", finfo->line_length);
+ fprintf(stderr, "\tmmio_start = %p\n", (char *)finfo->mmio_start);
+ fprintf(stderr, "\tmmio_len = %d\n", finfo->mmio_len);
+ fprintf(stderr, "\taccel = %d\n", finfo->accel);
+}
+#endif
+
+static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
+{
+ int matched;
+ FILE *modesdb;
+ struct fb_var_screeninfo cinfo;
+
+ matched = 0;
+ modesdb = fopen(FB_MODES_DB, "r");
+ if ( modesdb ) {
+ /* Parse the mode definition file */
+ while ( read_fbmodes_mode(modesdb, &cinfo) ) {
+ if ( (vinfo->xres == cinfo.xres && vinfo->yres == cinfo.yres) &&
+ (!matched || (vinfo->bits_per_pixel == cinfo.bits_per_pixel)) ) {
+ vinfo->pixclock = cinfo.pixclock;
+ vinfo->left_margin = cinfo.left_margin;
+ vinfo->right_margin = cinfo.right_margin;
+ vinfo->upper_margin = cinfo.upper_margin;
+ vinfo->lower_margin = cinfo.lower_margin;
+ vinfo->hsync_len = cinfo.hsync_len;
+ vinfo->vsync_len = cinfo.vsync_len;
+ if ( matched ) {
+ break;
+ }
+ matched = 1;
+ }
+ }
+ fclose(modesdb);
+ }
+ return(matched);
+}
+
+static int choose_vesa_mode(struct fb_var_screeninfo *vinfo)
+{
+ int matched;
+ int i;
+
+ /* Check for VESA timings */
+ matched = 0;
+ for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
+ if ( (vinfo->xres == vesa_timings[i].xres) &&
+ (vinfo->yres == vesa_timings[i].yres) ) {
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Using VESA timings for %dx%d\n",
+ vinfo->xres, vinfo->yres);
+#endif
+ if ( vesa_timings[i].pixclock ) {
+ vinfo->pixclock = vesa_timings[i].pixclock;
+ }
+ vinfo->left_margin = vesa_timings[i].left;
+ vinfo->right_margin = vesa_timings[i].right;
+ vinfo->upper_margin = vesa_timings[i].upper;
+ vinfo->lower_margin = vesa_timings[i].lower;
+ vinfo->hsync_len = vesa_timings[i].hslen;
+ vinfo->vsync_len = vesa_timings[i].vslen;
+ vinfo->sync = vesa_timings[i].sync;
+ vinfo->vmode = vesa_timings[i].vmode;
+ matched = 1;
+ break;
+ }
+ }
+ return(matched);
+}
+
+#ifdef VGA16_FBCON_SUPPORT
+static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current,
+ int width, int height, int bpp, Uint32 flags)
+{
+ struct fb_fix_screeninfo finfo;
+ struct fb_var_screeninfo vinfo;
+
+ /* Set the terminal into graphics mode */
+ if ( FB_EnterGraphicsMode(this) < 0 ) {
+ return(NULL);
+ }
+
+ /* Restore the original palette */
+ FB_RestorePalette(this);
+
+ /* Set the video mode and get the final screen format */
+ if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
+ SDL_SetError("Couldn't get console screen info");
+ return(NULL);
+ }
+ cache_vinfo = vinfo;
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Printing actual vinfo:\n");
+ print_vinfo(&vinfo);
+#endif
+ if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
+ return(NULL);
+ }
+ current->format->palette->ncolors = 16;
+
+ /* Get the fixed information about the console hardware.
+ This is necessary since finfo.line_length changes.
+ */
+ if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
+ SDL_SetError("Couldn't get console hardware info");
+ return(NULL);
+ }
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Printing actual finfo:\n");
+ print_finfo(&finfo);
+#endif
+
+ /* Save hardware palette, if needed */
+ FB_SavePalette(this, &finfo, &vinfo);
+
+ /* Set up the new mode framebuffer */
+ current->flags = SDL_FULLSCREEN;
+ current->w = vinfo.xres;
+ current->h = vinfo.yres;
+ current->pitch = current->w;
+ current->pixels = SDL_malloc(current->h*current->pitch);
+
+ /* Set the update rectangle function */
+ this->UpdateRects = FB_VGA16Update;
+
+ /* We're done */
+ return(current);
+}
+#endif /* VGA16_FBCON_SUPPORT */
+
+static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
+ int width, int height, int bpp, Uint32 flags)
+{
+ struct fb_fix_screeninfo finfo;
+ struct fb_var_screeninfo vinfo;
+ int i;
+ Uint32 Rmask;
+ Uint32 Gmask;
+ Uint32 Bmask;
+ char *surfaces_mem;
+ int surfaces_len;
+
+ /* Set the terminal into graphics mode */
+ if ( FB_EnterGraphicsMode(this) < 0 ) {
+ return(NULL);
+ }
+
+ /* Restore the original palette */
+ FB_RestorePalette(this);
+
+ /* Set the video mode and get the final screen format */
+ if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
+ SDL_SetError("Couldn't get console screen info");
+ return(NULL);
+ }
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Printing original vinfo:\n");
+ print_vinfo(&vinfo);
+#endif
+ /* Do not use double buffering with shadow buffer */
+ if (shadow_fb) {
+ flags &= ~SDL_DOUBLEBUF;
+ }
+
+ if ( (vinfo.xres != width) || (vinfo.yres != height) ||
+ (vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
+ vinfo.activate = FB_ACTIVATE_NOW;
+ vinfo.accel_flags = 0;
+ vinfo.bits_per_pixel = bpp;
+ vinfo.xres = width;
+ vinfo.xres_virtual = width;
+ vinfo.yres = height;
+ if ( flags & SDL_DOUBLEBUF ) {
+ vinfo.yres_virtual = height*2;
+ } else {
+ vinfo.yres_virtual = height;
+ }
+ vinfo.xoffset = 0;
+ vinfo.yoffset = 0;
+ vinfo.red.length = vinfo.red.offset = 0;
+ vinfo.green.length = vinfo.green.offset = 0;
+ vinfo.blue.length = vinfo.blue.offset = 0;
+ vinfo.transp.length = vinfo.transp.offset = 0;
+ if ( ! choose_fbmodes_mode(&vinfo) ) {
+ choose_vesa_mode(&vinfo);
+ }
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Printing wanted vinfo:\n");
+ print_vinfo(&vinfo);
+#endif
+ if ( !shadow_fb &&
+ ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
+ vinfo.yres_virtual = height;
+ if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
+ SDL_SetError("Couldn't set console screen info");
+ return(NULL);
+ }
+ }
+ } else {
+ int maxheight;
+
+ /* Figure out how much video memory is available */
+ if ( flags & SDL_DOUBLEBUF ) {
+ maxheight = height*2;
+ } else {
+ maxheight = height;
+ }
+ if ( vinfo.yres_virtual > maxheight ) {
+ vinfo.yres_virtual = maxheight;
+ }
+ }
+ cache_vinfo = vinfo;
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Printing actual vinfo:\n");
+ print_vinfo(&vinfo);
+#endif
+ Rmask = 0;
+ for ( i=0; i<vinfo.red.length; ++i ) {
+ Rmask <<= 1;
+ Rmask |= (0x00000001<<vinfo.red.offset);
+ }
+ Gmask = 0;
+ for ( i=0; i<vinfo.green.length; ++i ) {
+ Gmask <<= 1;
+ Gmask |= (0x00000001<<vinfo.green.offset);
+ }
+ Bmask = 0;
+ for ( i=0; i<vinfo.blue.length; ++i ) {
+ Bmask <<= 1;
+ Bmask |= (0x00000001<<vinfo.blue.offset);
+ }
+ if ( ! SDL_ReallocFormat(current, vinfo.bits_per_pixel,
+ Rmask, Gmask, Bmask, 0) ) {
+ return(NULL);
+ }
+
+ /* Get the fixed information about the console hardware.
+ This is necessary since finfo.line_length changes.
+ */
+ if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
+ SDL_SetError("Couldn't get console hardware info");
+ return(NULL);
+ }
+
+ /* Save hardware palette, if needed */
+ FB_SavePalette(this, &finfo, &vinfo);
+
+ if (shadow_fb) {
+ if (vinfo.bits_per_pixel == 16) {
+ blitFunc = (rotate == FBCON_ROTATE_NONE ||
+ rotate == FBCON_ROTATE_UD) ?
+ FB_blit16 : FB_blit16blocked;
+ } else {
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Init vinfo:\n");
+ print_vinfo(&vinfo);
+#endif
+ SDL_SetError("Using software buffer, but no blitter "
+ "function is available for %d bpp.",
+ vinfo.bits_per_pixel);
+ return(NULL);
+ }
+ }
+
+ /* Set up the new mode framebuffer */
+ current->flags &= SDL_FULLSCREEN;
+ if (shadow_fb) {
+ current->flags |= SDL_SWSURFACE;
+ } else {
+ current->flags |= SDL_HWSURFACE;
+ }
+ current->w = vinfo.xres;
+ current->h = vinfo.yres;
+ if (shadow_fb) {
+ current->pitch = current->w * ((vinfo.bits_per_pixel + 7) / 8);
+ current->pixels = shadow_mem;
+ physlinebytes = finfo.line_length;
+ } else {
+ current->pitch = finfo.line_length;
+ current->pixels = mapped_mem+mapped_offset;
+ }
+
+ /* Set up the information for hardware surfaces */
+ surfaces_mem = (char *)current->pixels +
+ vinfo.yres_virtual*current->pitch;
+ surfaces_len = (shadow_fb) ?
+ 0 : (mapped_memlen-(surfaces_mem-mapped_mem));
+
+ FB_FreeHWSurfaces(this);
+ FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
+
+ /* Let the application know we have a hardware palette */
+ switch (finfo.visual) {
+ case FB_VISUAL_PSEUDOCOLOR:
+ current->flags |= SDL_HWPALETTE;
+ break;
+ default:
+ break;
+ }
+
+ /* Update for double-buffering, if we can */
+ if ( flags & SDL_DOUBLEBUF ) {
+ if ( vinfo.yres_virtual == (height*2) ) {
+ current->flags |= SDL_DOUBLEBUF;
+ flip_page = 0;
+ flip_address[0] = (char *)current->pixels;
+ flip_address[1] = (char *)current->pixels+
+ current->h*current->pitch;
+ this->screen = current;
+ FB_FlipHWSurface(this, current);
+ this->screen = NULL;
+ }
+ }
+
+ /* Set the update rectangle function */
+ this->UpdateRects = FB_DirectUpdate;
+
+ /* We're done */
+ return(current);
+}
+
+#ifdef FBCON_DEBUG
+void FB_DumpHWSurfaces(_THIS)
+{
+ vidmem_bucket *bucket;
+
+ printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
+ printf("\n");
+ printf(" Base Size\n");
+ for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
+ printf("Bucket: %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
+ if ( bucket->prev ) {
+ if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
+ printf("Warning, corrupt bucket list! (prev)\n");
+ }
+ } else {
+ if ( bucket != &surfaces ) {
+ printf("Warning, corrupt bucket list! (!prev)\n");
+ }
+ }
+ if ( bucket->next ) {
+ if ( bucket->next->base != bucket->base+bucket->size ) {
+ printf("Warning, corrupt bucket list! (next)\n");
+ }
+ }
+ }
+ printf("\n");
+}
+#endif
+
+static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
+{
+ vidmem_bucket *bucket;
+
+ surfaces_memtotal = size;
+ surfaces_memleft = size;
+
+ if ( surfaces_memleft > 0 ) {
+ bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket));
+ if ( bucket == NULL ) {
+ SDL_OutOfMemory();
+ return(-1);
+ }
+ bucket->prev = &surfaces;
+ bucket->used = 0;
+ bucket->dirty = 0;
+ bucket->base = base;
+ bucket->size = size;
+ bucket->next = NULL;
+ } else {
+ bucket = NULL;
+ }
+
+ surfaces.prev = NULL;
+ surfaces.used = 1;
+ surfaces.dirty = 0;
+ surfaces.base = screen->pixels;
+ surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
+ surfaces.next = bucket;
+ screen->hwdata = (struct private_hwdata *)&surfaces;
+ return(0);
+}
+static void FB_FreeHWSurfaces(_THIS)
+{
+ vidmem_bucket *bucket, *freeable;
+
+ bucket = surfaces.next;
+ while ( bucket ) {
+ freeable = bucket;
+ bucket = bucket->next;
+ SDL_free(freeable);
+ }
+ surfaces.next = NULL;
+}
+
+static int FB_AllocHWSurface(_THIS, SDL_Surface *surface)
+{
+ vidmem_bucket *bucket;
+ int size;
+ int extra;
+
+/* Temporarily, we only allow surfaces the same width as display.
+ Some blitters require the pitch between two hardware surfaces
+ to be the same. Others have interesting alignment restrictions.
+ Until someone who knows these details looks at the code...
+*/
+if ( surface->pitch > SDL_VideoSurface->pitch ) {
+ SDL_SetError("Surface requested wider than screen");
+ return(-1);
+}
+surface->pitch = SDL_VideoSurface->pitch;
+ size = surface->h * surface->pitch;
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Allocating bucket of %d bytes\n", size);
+#endif
+
+ /* Quick check for available mem */
+ if ( size > surfaces_memleft ) {
+ SDL_SetError("Not enough video memory");
+ return(-1);
+ }
+
+ /* Search for an empty bucket big enough */
+ for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
+ if ( ! bucket->used && (size <= bucket->size) ) {
+ break;
+ }
+ }
+ if ( bucket == NULL ) {
+ SDL_SetError("Video memory too fragmented");
+ return(-1);
+ }
+
+ /* Create a new bucket for left-over memory */
+ extra = (bucket->size - size);
+ if ( extra ) {
+ vidmem_bucket *newbucket;
+
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
+#endif
+ newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket));
+ if ( newbucket == NULL ) {
+ SDL_OutOfMemory();
+ return(-1);
+ }
+ newbucket->prev = bucket;
+ newbucket->used = 0;
+ newbucket->base = bucket->base+size;
+ newbucket->size = extra;
+ newbucket->next = bucket->next;
+ if ( bucket->next ) {
+ bucket->next->prev = newbucket;
+ }
+ bucket->next = newbucket;
+ }
+
+ /* Set the current bucket values and return it! */
+ bucket->used = 1;
+ bucket->size = size;
+ bucket->dirty = 0;
+#ifdef FBCON_DEBUG
+ fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
+#endif
+ surfaces_memleft -= size;
+ surface->flags |= SDL_HWSURFACE;
+ surface->pixels = bucket->base;
+ surface->hwdata = (struct private_hwdata *)bucket;
+ return(0);
+}
+static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
+{
+ vidmem_bucket *bucket, *freeable;
+
+ /* Look for the bucket in the current list */
+ for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
+ if ( bucket == (vidmem_bucket *)surface->hwdata ) {
+ break;
+ }
+ }
+ if ( bucket && bucket->used ) {
+ /* Add the memory back to the total */
+#ifdef DGA_DEBUG
+ printf("Freeing bucket of %d bytes\n", bucket->size);
+#endif
+ surfaces_memleft += bucket->size;
+
+ /* Can we merge the space with surrounding buckets? */
+ bucket->used = 0;
+ if ( bucket->next && ! bucket->next->used ) {
+#ifdef DGA_DEBUG
+ printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
+#endif
+ freeable = bucket->next;
+ bucket->size += bucket->next->size;
+ bucket->next = bucket->next->next;
+ if ( bucket->next ) {
+ bucket->next->prev = bucket;
+ }
+ SDL_free(freeable);
+ }
+ if ( bucket->prev && ! bucket->prev->used ) {
+#ifdef DGA_DEBUG
+ printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
+#endif
+ freeable = bucket;
+ bucket->prev->size += bucket->size;
+ bucket->prev->next = bucket->next;
+ if ( bucket->next ) {
+ bucket->next->prev = bucket->prev;
+ }
+ SDL_free(freeable);
+ }
+ }
+ surface->pixels = NULL;
+ surface->hwdata = NULL;
+}
+
+static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
+{
+ if ( switched_away ) {
+ return -2; /* no hardware access */
+ }
+ if ( surface == this->screen ) {
+ SDL_mutexP(hw_lock);
+ if ( FB_IsSurfaceBusy(surface) ) {
+ FB_WaitBusySurfaces(this);
+ }
+ } else {
+ if ( FB_IsSurfaceBusy(surface) ) {
+ FB_WaitBusySurfaces(this);
+ }
+ }
+ return(0);
+}
+static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
+{
+ if ( surface == this->screen ) {
+ SDL_mutexV(hw_lock);
+ }
+}
+
+static void FB_WaitVBL(_THIS)
+{
+#ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
+ ioctl(console_fd, FBIOWAITRETRACE, 0);
+#endif
+ return;
+}
+
+static void FB_WaitIdle(_THIS)
+{
+ return;
+}
+
+static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
+{
+ if ( switched_away ) {
+ return -2; /* no hardware access */
+ }
+
+ /* Wait for vertical retrace and then flip display */
+ cache_vinfo.yoffset = flip_page*surface->h;
+ if ( FB_IsSurfaceBusy(this->screen) ) {
+ FB_WaitBusySurfaces(this);
+ }
+ wait_vbl(this);
+ if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
+ SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
+ return(-1);
+ }
+ flip_page = !flip_page;
+
+ surface->pixels = flip_address[flip_page];
+ return(0);
+}
+
+static void FB_blit16(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
+ Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
+{
+ int w;
+ Uint16 *src_pos = (Uint16 *)byte_src_pos;
+ Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
+
+ while (height) {
+ Uint16 *src = src_pos;
+ Uint16 *dst = dst_pos;
+ for (w = width; w != 0; w--) {
+ *dst = *src;
+ src += src_right_delta;
+ dst++;
+ }
+ dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes);
+ src_pos += src_down_delta;
+ height--;
+ }
+}
+
+#define BLOCKSIZE_W 32
+#define BLOCKSIZE_H 32
+
+static void FB_blit16blocked(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
+ Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
+{
+ int w;
+ Uint16 *src_pos = (Uint16 *)byte_src_pos;
+ Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
+
+ while (height > 0) {
+ Uint16 *src = src_pos;
+ Uint16 *dst = dst_pos;
+ for (w = width; w > 0; w -= BLOCKSIZE_W) {
+ FB_blit16((Uint8 *)src,
+ src_right_delta,
+ src_down_delta,
+ (Uint8 *)dst,
+ dst_linebytes,
+ min(w, BLOCKSIZE_W),
+ min(height, BLOCKSIZE_H));
+ src += src_right_delta * BLOCKSIZE_W;
+ dst += BLOCKSIZE_W;
+ }
+ dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H);
+ src_pos += src_down_delta * BLOCKSIZE_H;
+ height -= BLOCKSIZE_H;
+ }
+}
+
+static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
+{
+ int width = cache_vinfo.xres;
+ int height = cache_vinfo.yres;
+ int bytes_per_pixel = (cache_vinfo.bits_per_pixel + 7) / 8;
+ int i;
+
+ if (!shadow_fb) {
+ /* The application is already updating the visible video memory */
+ return;
+ }
+
+ if (cache_vinfo.bits_per_pixel != 16) {
+ SDL_SetError("Shadow copy only implemented for 16 bpp");
+ return;
+ }
+
+ for (i = 0; i < numrects; i++) {
+ int x1, y1, x2, y2;
+ int scr_x1, scr_y1, scr_x2, scr_y2;
+ int sha_x1, sha_y1;
+ int shadow_right_delta; /* Address change when moving right in dest */
+ int shadow_down_delta; /* Address change when moving down in dest */
+ char *src_start;
+ char *dst_start;
+
+ x1 = rects[i].x;
+ y1 = rects[i].y;
+ x2 = x1 + rects[i].w;
+ y2 = y1 + rects[i].h;
+
+ if (x1 < 0) {
+ x1 = 0;
+ } else if (x1 > width) {
+ x1 = width;
+ }
+ if (x2 < 0) {
+ x2 = 0;
+ } else if (x2 > width) {
+ x2 = width;
+ }
+ if (y1 < 0) {
+ y1 = 0;
+ } else if (y1 > height) {
+ y1 = height;
+ }
+ if (y2 < 0) {
+ y2 = 0;
+ } else if (y2 > height) {
+ y2 = height;
+ }
+ if (x2 <= x1 || y2 <= y1) {
+ continue;
+ }
+
+ switch (rotate) {
+ case FBCON_ROTATE_NONE:
+ sha_x1 = scr_x1 = x1;
+ sha_y1 = scr_y1 = y1;
+ scr_x2 = x2;
+ scr_y2 = y2;
+ shadow_right_delta = 1;
+ shadow_down_delta = width;
+ break;
+ case FBCON_ROTATE_CCW:
+ scr_x1 = y1;
+ scr_y1 = width - x2;
+ scr_x2 = y2;
+ scr_y2 = width - x1;
+ sha_x1 = x2 - 1;
+ sha_y1 = y1;
+ shadow_right_delta = width;
+ shadow_down_delta = -1;
+ break;
+ case FBCON_ROTATE_UD:
+ scr_x1 = width - x2;
+ scr_y1 = height - y2;
+ scr_x2 = width - x1;
+ scr_y2 = height - y1;
+ sha_x1 = x2 - 1;
+ sha_y1 = y2 - 1;
+ shadow_right_delta = -1;
+ shadow_down_delta = -width;
+ break;
+ case FBCON_ROTATE_CW:
+ scr_x1 = height - y2;
+ scr_y1 = x1;
+ scr_x2 = height - y1;
+ scr_y2 = x2;
+ sha_x1 = x1;
+ sha_y1 = y2 - 1;
+ shadow_right_delta = -width;
+ shadow_down_delta = 1;
+ break;
+ default:
+ SDL_SetError("Unknown rotation");
+ return;
+ }
+
+ src_start = shadow_mem +
+ (sha_y1 * width + sha_x1) * bytes_per_pixel;
+ dst_start = mapped_mem + mapped_offset + scr_y1 * physlinebytes +
+ scr_x1 * bytes_per_pixel;
+
+ blitFunc((Uint8 *) src_start,
+ shadow_right_delta,
+ shadow_down_delta,
+ (Uint8 *) dst_start,
+ physlinebytes,
+ scr_x2 - scr_x1,
+ scr_y2 - scr_y1);
+ }
+}
+
+#ifdef VGA16_FBCON_SUPPORT
+/* Code adapted with thanks from the XFree86 VGA16 driver! :) */
+#define writeGr(index, value) \
+outb(index, 0x3CE); \
+outb(value, 0x3CF);
+#define writeSeq(index, value) \
+outb(index, 0x3C4); \
+outb(value, 0x3C5);
+
+static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects)
+{
+ SDL_Surface *screen;
+ int width, height, FBPitch, left, i, j, SRCPitch, phase;
+ register Uint32 m;
+ Uint8 s1, s2, s3, s4;
+ Uint32 *src, *srcPtr;
+ Uint8 *dst, *dstPtr;
+
+ if ( switched_away ) {
+ return; /* no hardware access */
+ }
+
+ screen = this->screen;
+ FBPitch = screen->w >> 3;
+ SRCPitch = screen->pitch >> 2;
+
+ writeGr(0x03, 0x00);
+ writeGr(0x05, 0x00);
+ writeGr(0x01, 0x00);
+ writeGr(0x08, 0xFF);
+
+ while(numrects--) {
+ left = rects->x & ~7;
+ width = (rects->w + 7) >> 3;
+ height = rects->h;
+ src = (Uint32*)screen->pixels + (rects->y * SRCPitch) + (left >> 2);
+ dst = (Uint8*)mapped_mem + (rects->y * FBPitch) + (left >> 3);
+
+ if((phase = (long)dst & 3L)) {
+ phase = 4 - phase;
+ if(phase > width) phase = width;
+ width -= phase;
+ }
+
+ while(height--) {
+ writeSeq(0x02, 1 << 0);
+ dstPtr = dst;
+ srcPtr = src;
+ i = width;
+ j = phase;
+ while(j--) {
+ m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
+ *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ srcPtr += 2;
+ }
+ while(i >= 4) {
+ m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
+ s1 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ m = (srcPtr[3] & 0x01010101) | ((srcPtr[2] & 0x01010101) << 4);
+ s2 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ m = (srcPtr[5] & 0x01010101) | ((srcPtr[4] & 0x01010101) << 4);
+ s3 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ m = (srcPtr[7] & 0x01010101) | ((srcPtr[6] & 0x01010101) << 4);
+ s4 = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
+ srcPtr += 8;
+ dstPtr += 4;
+ i -= 4;
+ }
+ while(i--) {
+ m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
+ *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
+ srcPtr += 2;
+ }
+
+ writeSeq(0x02, 1 << 1);
+ dstPtr = dst;
+ srcPtr = src;
+ i = width;
+ j = phase;
+ while(j--) {
+ m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
+ *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ srcPtr += 2;
+ }
+ while(i >= 4) {
+ m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
+ s1 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ m = (srcPtr[3] & 0x02020202) | ((srcPtr[2] & 0x02020202) << 4);
+ s2 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ m = (srcPtr[5] & 0x02020202) | ((srcPtr[4] & 0x02020202) << 4);
+ s3 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ m = (srcPtr[7] & 0x02020202) | ((srcPtr[6] & 0x02020202) << 4);
+ s4 = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
+ srcPtr += 8;
+ dstPtr += 4;
+ i -= 4;
+ }
+ while(i--) {
+ m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
+ *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
+ srcPtr += 2;
+ }
+
+ writeSeq(0x02, 1 << 2);
+ dstPtr = dst;
+ srcPtr = src;
+ i = width;
+ j = phase;
+ while(j--) {
+ m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
+ *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ srcPtr += 2;
+ }
+ while(i >= 4) {
+ m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
+ s1 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ m = (srcPtr[3] & 0x04040404) | ((srcPtr[2] & 0x04040404) << 4);
+ s2 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ m = (srcPtr[5] & 0x04040404) | ((srcPtr[4] & 0x04040404) << 4);
+ s3 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ m = (srcPtr[7] & 0x04040404) | ((srcPtr[6] & 0x04040404) << 4);
+ s4 = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
+ srcPtr += 8;
+ dstPtr += 4;
+ i -= 4;
+ }
+ while(i--) {
+ m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
+ *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
+ srcPtr += 2;
+ }
+
+ writeSeq(0x02, 1 << 3);
+ dstPtr = dst;
+ srcPtr = src;
+ i = width;
+ j = phase;
+ while(j--) {
+ m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
+ *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ srcPtr += 2;
+ }
+ while(i >= 4) {
+ m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
+ s1 = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ m = (srcPtr[3] & 0x08080808) | ((srcPtr[2] & 0x08080808) << 4);
+ s2 = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ m = (srcPtr[5] & 0x08080808) | ((srcPtr[4] & 0x08080808) << 4);
+ s3 = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ m = (srcPtr[7] & 0x08080808) | ((srcPtr[6] & 0x08080808) << 4);
+ s4 = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ *((Uint32*)dstPtr) = s1 | (s2 << 8) | (s3 << 16) | (s4 << 24);
+ srcPtr += 8;
+ dstPtr += 4;
+ i -= 4;
+ }
+ while(i--) {
+ m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
+ *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
+ srcPtr += 2;
+ }
+
+ dst += FBPitch;
+ src += SRCPitch;
+ }
+ rects++;
+ }
+}
+#endif /* VGA16_FBCON_SUPPORT */
+
+void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area)
+{
+ struct fb_cmap cmap;
+
+ cmap.start = 0;
+ cmap.len = palette_len;
+ cmap.red = &area[0*palette_len];
+ cmap.green = &area[1*palette_len];
+ cmap.blue = &area[2*palette_len];
+ cmap.transp = NULL;
+ ioctl(console_fd, FBIOGETCMAP, &cmap);
+}
+
+void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area)
+{
+ struct fb_cmap cmap;
+
+ cmap.start = 0;
+ cmap.len = palette_len;
+ cmap.red = &area[0*palette_len];
+ cmap.green = &area[1*palette_len];
+ cmap.blue = &area[2*palette_len];
+ cmap.transp = NULL;
+ ioctl(console_fd, FBIOPUTCMAP, &cmap);
+}
+
+static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
+ struct fb_var_screeninfo *vinfo)
+{
+ int i;
+
+ /* Save hardware palette, if needed */
+ if ( finfo->visual == FB_VISUAL_PSEUDOCOLOR ) {
+ saved_cmaplen = 1<<vinfo->bits_per_pixel;
+ saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
+ if ( saved_cmap != NULL ) {
+ FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
+ }
+ }
+
+ /* Added support for FB_VISUAL_DIRECTCOLOR.
+ With this mode pixel information is passed through the palette...
+ Neat fading and gamma correction effects can be had by simply
+ fooling around with the palette instead of changing the pixel
+ values themselves... Very neat!
+
+ Adam Meyerowitz 1/19/2000
+ ameyerow@optonline.com
+ */
+ if ( finfo->visual == FB_VISUAL_DIRECTCOLOR ) {
+ __u16 new_entries[3*256];
+
+ /* Save the colormap */
+ saved_cmaplen = 256;
+ saved_cmap=(__u16 *)SDL_malloc(3*saved_cmaplen*sizeof(*saved_cmap));
+ if ( saved_cmap != NULL ) {
+ FB_SavePaletteTo(this, saved_cmaplen, saved_cmap);
+ }
+
+ /* Allocate new identity colormap */
+ for ( i=0; i<256; ++i ) {
+ new_entries[(0*256)+i] =
+ new_entries[(1*256)+i] =
+ new_entries[(2*256)+i] = (i<<8)|i;
+ }
+ FB_RestorePaletteFrom(this, 256, new_entries);
+ }
+}
+
+static void FB_RestorePalette(_THIS)
+{
+ /* Restore the original palette */
+ if ( saved_cmap ) {
+ FB_RestorePaletteFrom(this, saved_cmaplen, saved_cmap);
+ SDL_free(saved_cmap);
+ saved_cmap = NULL;
+ }
+}
+
+static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
+{
+ int i;
+ __u16 r[256];
+ __u16 g[256];
+ __u16 b[256];
+ struct fb_cmap cmap;
+
+ /* Set up the colormap */
+ for (i = 0; i < ncolors; i++) {
+ r[i] = colors[i].r << 8;
+ g[i] = colors[i].g << 8;
+ b[i] = colors[i].b << 8;
+ }
+ cmap.start = firstcolor;
+ cmap.len = ncolors;
+ cmap.red = r;
+ cmap.green = g;
+ cmap.blue = b;
+ cmap.transp = NULL;
+
+ if( (ioctl(console_fd, FBIOPUTCMAP, &cmap) < 0) ||
+ !(this->screen->flags & SDL_HWPALETTE) ) {
+ colors = this->screen->format->palette->colors;
+ ncolors = this->screen->format->palette->ncolors;
+ cmap.start = 0;
+ cmap.len = ncolors;
+ SDL_memset(r, 0, sizeof(r));
+ SDL_memset(g, 0, sizeof(g));
+ SDL_memset(b, 0, sizeof(b));
+ if ( ioctl(console_fd, FBIOGETCMAP, &cmap) == 0 ) {
+ for ( i=ncolors-1; i>=0; --i ) {
+ colors[i].r = (r[i]>>8);
+ colors[i].g = (g[i]>>8);
+ colors[i].b = (b[i]>>8);
+ }
+ }
+ return(0);
+ }
+ return(1);
+}
+
+/* Note: If we are terminated, this could be called in the middle of
+ another SDL video routine -- notably UpdateRects.
+*/
+static void FB_VideoQuit(_THIS)
+{
+ int i, j;
+
+ if ( this->screen ) {
+ /* Clear screen and tell SDL not to free the pixels */
+
+ const char *dontClearPixels = SDL_getenv("SDL_FBCON_DONT_CLEAR");
+
+ /* If the framebuffer is not to be cleared, make sure that we won't
+ * display the previous frame when disabling double buffering. */
+ if ( dontClearPixels && flip_page == 0 ) {
+ SDL_memcpy(flip_address[0], flip_address[1], this->screen->pitch * this->screen->h);
+ }
+
+ if ( !dontClearPixels && this->screen->pixels && FB_InGraphicsMode(this) ) {
+#if defined(__powerpc__) || defined(__ia64__) /* SIGBUS when using SDL_memset() ?? */
+ Uint8 *rowp = (Uint8 *)this->screen->pixels;
+ int left = this->screen->pitch*this->screen->h;
+ while ( left-- ) { *rowp++ = 0; }
+#else
+ SDL_memset(this->screen->pixels,0,this->screen->h*this->screen->pitch);
+#endif
+ }
+ /* This test fails when using the VGA16 shadow memory */
+ if ( ((char *)this->screen->pixels >= mapped_mem) &&
+ ((char *)this->screen->pixels < (mapped_mem+mapped_memlen)) ) {
+ this->screen->pixels = NULL;
+ }
+ }
+
+ /* Clear the lock mutex */
+ if ( hw_lock ) {
+ SDL_DestroyMutex(hw_lock);
+ hw_lock = NULL;
+ }
+
+ /* Clean up defined video modes */
+ for ( i=0; i<NUM_MODELISTS; ++i ) {
+ if ( SDL_modelist[i] != NULL ) {
+ for ( j=0; SDL_modelist[i][j]; ++j ) {
+ SDL_free(SDL_modelist[i][j]);
+ }
+ SDL_free(SDL_modelist[i]);
+ SDL_modelist[i] = NULL;
+ }
+ }
+
+ /* Clean up the memory bucket list */
+ FB_FreeHWSurfaces(this);
+
+ /* Close console and input file descriptors */
+ if ( console_fd > 0 ) {
+ /* Unmap the video framebuffer and I/O registers */
+ if ( mapped_mem ) {
+ munmap(mapped_mem, mapped_memlen);
+ mapped_mem = NULL;
+ }
+ if ( mapped_io ) {
+ munmap(mapped_io, mapped_iolen);
+ mapped_io = NULL;
+ }
+
+ /* Restore the original video mode and palette */
+ if ( FB_InGraphicsMode(this) ) {
+ FB_RestorePalette(this);
+ ioctl(console_fd, FBIOPUT_VSCREENINFO, &saved_vinfo);
+ }
+
+ /* We're all done with the framebuffer */
+ close(console_fd);
+ console_fd = -1;
+ }
+ FB_CloseMouse(this);
+ FB_CloseKeyboard(this);
+}