📄 sdl_fbvideo.c
字号:
/* SDL - Simple DirectMedia Layer Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga slouken@libsdl.org*/#ifdef SAVE_RCSIDstatic char rcsid = "@(#) $Id: SDL_fbvideo.c,v 1.10 2002/09/02 21:42:15 slouken Exp $";#endif/* Framebuffer console based SDL video driver implementation.*/#include <stdlib.h>#include <stdio.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <asm/page.h> /* For definition of PAGE_SIZE */#include "SDL.h"#include "SDL_error.h"#include "SDL_video.h"#include "SDL_mouse.h"#include "SDL_sysvideo.h"#include "SDL_pixels_c.h"#include "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"#if defined(i386) && defined(FB_TYPE_VGA_PLANES)#define VGA16_FBCON_SUPPORT#ifndef FB_AUX_VGA_PLANES_VGA4#define FB_AUX_VGA_PLANES_VGA4 0#endifstatic 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};/* 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_SUPPORTstatic SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);#endifstatic 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);/* FB driver bootstrap functions */static int FB_Available(void){ int console; const char *SDL_fbdev; SDL_fbdev = getenv("SDL_FBDEV"); if ( SDL_fbdev == NULL ) { SDL_fbdev = "/dev/fb0"; } console = open(SDL_fbdev, O_RDWR, 0); if ( console >= 0 ) { close(console); } return(console >= 0);}static void FB_DeleteDevice(SDL_VideoDevice *device){ free(device->hidden); free(device);}static SDL_VideoDevice *FB_CreateDevice(int devindex){ SDL_VideoDevice *this; /* Initialize all variables that we clean on shutdown */ this = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice)); if ( this ) { memset(this, 0, (sizeof *this)); this->hidden = (struct SDL_PrivateVideoData *) malloc((sizeof *this->hidden)); } if ( (this == NULL) || (this->hidden == NULL) ) { SDL_OutOfMemory(); if ( this ) { free(this); } return(0); } 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};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){ 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 */ next_mode = -1; 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 ) { next_mode = i; break; } } if ( next_mode == -1 ) {#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 *)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 **) realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *)); if ( SDL_modelist[index] == NULL ) { SDL_OutOfMemory(); SDL_nummodes[index] = 0; free(mode); return(-1); } SDL_modelist[index][next_mode] = mode; SDL_modelist[index][next_mode+1] = NULL; SDL_nummodes[index]++; return(0);}static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat){ 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; /* Initialize the library */ SDL_fbdev = 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); }#ifndef DISABLE_THREADS /* 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 = getenv("SDL_FBACCEL"); if ( fb_accel ) { finfo.accel = atoi(fb_accel); } } /* Memory map the device, compensating for buggy PPC mmap() */ mapped_offset = (((long)finfo.smem_start) - (((long)finfo.smem_start)&~(PAGE_SIZE-1))); mapped_memlen = finfo.smem_len+mapped_offset; mapped_mem = 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 = 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; } } /* Query for the list of available video modes */ current_w = vinfo.xres; current_h = vinfo.yres; current_index = ((vinfo.bits_per_pixel+7)/8)-1;#ifdef BROKEN_MODES FB_AddMode(this, current_index, current_w, current_h);#else for ( i=0; i<NUM_MODELISTS; ++i ) { SDL_nummodes[i] = 0; SDL_modelist[i] = NULL; for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) { unsigned int w, h; /* See if we are querying for the current mode */ w = checkres[j].w; h = checkres[j].h; if ( i == current_index ) { if ( (current_w > w) || (current_h > h) ) { /* Only check once */ FB_AddMode(this, i, current_w, current_h); current_index = -1; } } if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) { FB_AddMode(this, i, w, h); } } }#endif /* BROKEN_MODES */ /* Fill in our hardware acceleration capabilities */ this->info.wm_available = 0; this->info.hw_available = 1; this->info.video_mem = finfo.smem_len/1024; if ( mapped_io ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -