2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 /* Framebuffer console based SDL video driver implementation.
30 #include <sys/ioctl.h>
33 #ifndef HAVE_GETPAGESIZE
34 #include <asm/page.h> /* For definition of PAGE_SIZE */
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"
51 /*#define FBCON_DEBUG*/
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
60 static inline void outb (unsigned char value, unsigned short port)
62 __asm__ __volatile__ ("outb %b0,%w1"::"a" (value), "Nd" (port));
65 #endif /* FB_TYPE_VGA_PLANES */
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 */
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 */
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.
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 */
130 FBCON_ROTATE_NONE = 0,
131 FBCON_ROTATE_CCW = 90,
132 FBCON_ROTATE_UD = 180,
133 FBCON_ROTATE_CW = 270
136 #define min(a,b) ((a)<(b)?(a):(b))
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);
145 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
146 static void FB_VideoQuit(_THIS);
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);
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);
164 /* Shadow buffer functions */
165 static FB_bitBlit FB_blit16;
166 static FB_bitBlit FB_blit16blocked;
168 static int SDL_getpagesize(void)
170 #ifdef HAVE_GETPAGESIZE
171 return getpagesize();
172 #elif defined(PAGE_SIZE)
175 #error Can not determine system page size.
176 return 4096; /* this is what it USED to be in Linux... */
181 /* Small wrapper for mmap() so we can play nicely with no-mmu hosts
182 * (non-mmu hosts disallow the MAP_SHARED flag) */
184 static void *do_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
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);
195 /* FB driver bootstrap functions */
197 static int FB_Available(void)
200 /* Added check for /fb/0 (devfs) */
201 /* but - use environment variable first... if it fails, still check defaults */
203 const char *SDL_fbdevs[4] = { NULL, "/dev/fb0", "/dev/fb/0", NULL };
205 SDL_fbdevs[0] = SDL_getenv("SDL_FBDEV");
208 for( ; SDL_fbdevs[idx]; idx++ )
210 console = open(SDL_fbdevs[idx], O_RDWR, 0);
211 if ( console >= 0 ) {
216 return(console >= 0);
219 static void FB_DeleteDevice(SDL_VideoDevice *device)
221 SDL_free(device->hidden);
225 static SDL_VideoDevice *FB_CreateDevice(int devindex)
227 SDL_VideoDevice *this;
229 /* Initialize all variables that we clean on shutdown */
230 this = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
232 SDL_memset(this, 0, (sizeof *this));
233 this->hidden = (struct SDL_PrivateVideoData *)
234 SDL_malloc((sizeof *this->hidden));
236 if ( (this == NULL) || (this->hidden == NULL) ) {
243 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
244 wait_vbl = FB_WaitVBL;
245 wait_idle = FB_WaitIdle;
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;
273 this->free = FB_DeleteDevice;
278 VideoBootStrap FBCON_bootstrap = {
279 "fbcon", "Linux Framebuffer Console",
280 FB_Available, FB_CreateDevice
283 #define FB_MODES_DB "/etc/fb.modes"
285 static int read_fbmodes_line(FILE*f, char* line, int length)
292 /* find a relevant line */
295 if (!fgets(line,length,f))
298 while(((*c=='\t')||(*c==' '))&&(*c!=0))
301 if ((*c=='\n')||(*c=='#')||(*c==0))
307 /* remove whitespace at the begining of the string */
318 static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
323 /* Find a "geometry" */
325 if (read_fbmodes_line(f, line, sizeof(line))==0)
327 if (SDL_strncmp(line,"geometry",8)==0)
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)
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);
342 vinfo->vmode=FB_VMODE_NONINTERLACED;
344 /* Parse misc options */
346 if (read_fbmodes_line(f, line, sizeof(line))==0)
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;
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;
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;
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;
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;
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;
380 while(SDL_strncmp(line,"endmode",7)!=0);
385 static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
386 int index, unsigned int *w, unsigned int *h)
391 vinfo->bits_per_pixel = (index+1)*8;
393 vinfo->xres_virtual = *w;
395 vinfo->yres_virtual = *h;
396 vinfo->activate = FB_ACTIVATE_TEST;
397 if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, vinfo) == 0 ) {
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);
401 if ( (((vinfo->bits_per_pixel+7)/8)-1) == index ) {
410 static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
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) ) {
421 fprintf(stderr, "We already have mode %dx%d at %d bytes per pixel\n", w, h, index+1);
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 ) {
437 if ( !found_timing ) {
439 fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
445 /* Set up the new video mode rectangle */
446 mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
447 if ( mode == NULL ) {
456 fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
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 ) {
465 SDL_nummodes[index] = 0;
469 SDL_modelist[index][next_mode] = mode;
470 SDL_modelist[index][next_mode+1] = NULL;
471 SDL_nummodes[index]++;
476 static int cmpmodes(const void *va, const void *vb)
478 const SDL_Rect *a = *(const SDL_Rect**)va;
479 const SDL_Rect *b = *(const SDL_Rect**)vb;
486 static void FB_SortModes(_THIS)
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);
496 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
498 const int pagesize = SDL_getpagesize();
499 struct fb_fix_screeninfo finfo;
500 struct fb_var_screeninfo vinfo;
503 unsigned int current_w;
504 unsigned int current_h;
505 const char *SDL_fbdev;
506 const char *rotation;
509 /* Initialize the library */
510 SDL_fbdev = SDL_getenv("SDL_FBDEV");
511 if ( SDL_fbdev == NULL ) {
512 SDL_fbdev = "/dev/fb0";
514 console_fd = open(SDL_fbdev, O_RDWR, 0);
515 if ( console_fd < 0 ) {
516 SDL_SetError("Unable to open %s", SDL_fbdev);
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");
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");
536 switch (finfo.type) {
537 case FB_TYPE_PACKED_PIXELS:
538 /* Supported, no worries.. */
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");
549 this->SetVideoMode = FB_SetVGA16Mode;
552 /* Fall through to unsupported case */
553 #endif /* VGA16_FBCON_SUPPORT */
555 SDL_SetError("Unsupported console hardware");
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:
566 SDL_SetError("Unsupported console hardware");
571 /* Check if the user wants to disable hardware acceleration */
572 { const char *fb_accel;
573 fb_accel = SDL_getenv("SDL_FBACCEL");
575 finfo.accel = SDL_atoi(fb_accel);
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");
592 /* Determine the current screen depth */
593 if ( ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
594 SDL_SetError("Couldn't get console pixel format");
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;
603 for ( i=0; i<vinfo.red.length; ++i ) {
604 vformat->Rmask <<= 1;
605 vformat->Rmask |= (0x00000001<<vinfo.red.offset);
607 for ( i=0; i<vinfo.green.length; ++i ) {
608 vformat->Gmask <<= 1;
609 vformat->Gmask |= (0x00000001<<vinfo.green.offset);
611 for ( i=0; i<vinfo.blue.length; ++i ) {
612 vformat->Bmask <<= 1;
613 vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
617 /* Save hardware palette, if needed */
618 FB_SavePalette(this, &finfo, &vinfo);
620 /* If the I/O registers are available, memory map them so we
621 can take advantage of any supported hardware acceleration.
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 */
635 rotate = FBCON_ROTATE_NONE;
636 rotation = SDL_getenv("SDL_VIDEO_FBCON_ROTATION");
637 if (rotation != NULL) {
638 if (SDL_strlen(rotation) == 0) {
640 rotate = FBCON_ROTATE_NONE;
642 printf("Not rotating, no shadow\n");
644 } else if (!SDL_strcmp(rotation, "NONE")) {
646 rotate = FBCON_ROTATE_NONE;
648 printf("Not rotating, but still using shadow\n");
650 } else if (!SDL_strcmp(rotation, "CW")) {
652 rotate = FBCON_ROTATE_CW;
654 printf("Rotating screen clockwise\n");
656 } else if (!SDL_strcmp(rotation, "CCW")) {
658 rotate = FBCON_ROTATE_CCW;
660 printf("Rotating screen counter clockwise\n");
662 } else if (!SDL_strcmp(rotation, "UD")) {
664 rotate = FBCON_ROTATE_UD;
666 printf("Rotating screen upside down\n");
669 SDL_SetError("\"%s\" is not a valid value for "
670 "SDL_VIDEO_FBCON_ROTATION", rotation);
675 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
676 current_w = vinfo.yres;
677 current_h = vinfo.xres;
679 current_w = vinfo.xres;
680 current_h = vinfo.yres;
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 ) {
688 SDL_modelist[i] = NULL;
690 if ( SDL_getenv("SDL_FB_BROKEN_MODES") != NULL ) {
691 FB_AddMode(this, current_index, current_w, current_h, 0);
693 while ( read_fbmodes_mode(modesdb, &vinfo) ) {
694 for ( i=0; i<NUM_MODELISTS; ++i ) {
697 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
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);
712 if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
713 FB_AddMode(this, i, w, h, 0);
720 for ( i=0; i<NUM_MODELISTS; ++i ) {
721 for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
724 if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
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);
739 if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
740 FB_AddMode(this, i, w, h, 1);
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 */
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:
762 printf("Matrox hardware accelerator!\n");
764 FB_MatroxAccel(this, finfo.accel);
766 case FB_ACCEL_3DFX_BANSHEE:
768 printf("3DFX hardware accelerator!\n");
770 FB_3DfxAccel(this, finfo.accel);
775 printf("NVidia hardware accelerator!\n");
777 FB_RivaAccel(this, finfo.accel);
781 printf("Unknown hardware accelerator.\n");
788 shadow_mem = (char *)SDL_malloc(mapped_memlen);
789 if (shadow_mem == NULL) {
790 SDL_SetError("No memory for shadow");
795 /* Enable mouse and keyboard support */
796 if ( FB_OpenKeyboard(this) < 0 ) {
800 if ( FB_OpenMouse(this) < 0 ) {
801 const char *sdl_nomouse;
803 sdl_nomouse = SDL_getenv("SDL_NOMOUSE");
804 if ( ! sdl_nomouse ) {
805 SDL_SetError("Unable to open mouse");
815 static SDL_Rect **FB_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
817 return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]);
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);
827 static void print_vinfo(struct fb_var_screeninfo *vinfo)
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);
857 static void print_finfo(struct fb_fix_screeninfo *finfo)
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);
875 static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
879 struct fb_var_screeninfo cinfo;
882 modesdb = fopen(FB_MODES_DB, "r");
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;
906 static int choose_vesa_mode(struct fb_var_screeninfo *vinfo)
911 /* Check for VESA timings */
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) ) {
917 fprintf(stderr, "Using VESA timings for %dx%d\n",
918 vinfo->xres, vinfo->yres);
920 if ( vesa_timings[i].pixclock ) {
921 vinfo->pixclock = vesa_timings[i].pixclock;
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;
938 #ifdef VGA16_FBCON_SUPPORT
939 static SDL_Surface *FB_SetVGA16Mode(_THIS, SDL_Surface *current,
940 int width, int height, int bpp, Uint32 flags)
942 struct fb_fix_screeninfo finfo;
943 struct fb_var_screeninfo vinfo;
945 /* Set the terminal into graphics mode */
946 if ( FB_EnterGraphicsMode(this) < 0 ) {
950 /* Restore the original palette */
951 FB_RestorePalette(this);
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");
960 fprintf(stderr, "Printing actual vinfo:\n");
963 if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) {
966 current->format->palette->ncolors = 16;
968 /* Get the fixed information about the console hardware.
969 This is necessary since finfo.line_length changes.
971 if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
972 SDL_SetError("Couldn't get console hardware info");
976 fprintf(stderr, "Printing actual finfo:\n");
980 /* Save hardware palette, if needed */
981 FB_SavePalette(this, &finfo, &vinfo);
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);
990 /* Set the update rectangle function */
991 this->UpdateRects = FB_VGA16Update;
996 #endif /* VGA16_FBCON_SUPPORT */
998 static SDL_Surface *FB_SetVideoMode(_THIS, SDL_Surface *current,
999 int width, int height, int bpp, Uint32 flags)
1001 struct fb_fix_screeninfo finfo;
1002 struct fb_var_screeninfo vinfo;
1010 /* Set the terminal into graphics mode */
1011 if ( FB_EnterGraphicsMode(this) < 0 ) {
1015 /* Restore the original palette */
1016 FB_RestorePalette(this);
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");
1024 fprintf(stderr, "Printing original vinfo:\n");
1025 print_vinfo(&vinfo);
1027 /* Do not use double buffering with shadow buffer */
1029 flags &= ~SDL_DOUBLEBUF;
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;
1038 vinfo.xres_virtual = width;
1039 vinfo.yres = height;
1040 if ( flags & SDL_DOUBLEBUF ) {
1041 vinfo.yres_virtual = height*2;
1043 vinfo.yres_virtual = height;
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);
1055 fprintf(stderr, "Printing wanted vinfo:\n");
1056 print_vinfo(&vinfo);
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");
1069 /* Figure out how much video memory is available */
1070 if ( flags & SDL_DOUBLEBUF ) {
1071 maxheight = height*2;
1075 if ( vinfo.yres_virtual > maxheight ) {
1076 vinfo.yres_virtual = maxheight;
1079 cache_vinfo = vinfo;
1081 fprintf(stderr, "Printing actual vinfo:\n");
1082 print_vinfo(&vinfo);
1085 for ( i=0; i<vinfo.red.length; ++i ) {
1087 Rmask |= (0x00000001<<vinfo.red.offset);
1090 for ( i=0; i<vinfo.green.length; ++i ) {
1092 Gmask |= (0x00000001<<vinfo.green.offset);
1095 for ( i=0; i<vinfo.blue.length; ++i ) {
1097 Bmask |= (0x00000001<<vinfo.blue.offset);
1099 if ( ! SDL_ReallocFormat(current, vinfo.bits_per_pixel,
1100 Rmask, Gmask, Bmask, 0) ) {
1104 /* Get the fixed information about the console hardware.
1105 This is necessary since finfo.line_length changes.
1107 if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
1108 SDL_SetError("Couldn't get console hardware info");
1112 /* Save hardware palette, if needed */
1113 FB_SavePalette(this, &finfo, &vinfo);
1116 if (vinfo.bits_per_pixel == 16) {
1117 blitFunc = (rotate == FBCON_ROTATE_NONE ||
1118 rotate == FBCON_ROTATE_UD) ?
1119 FB_blit16 : FB_blit16blocked;
1122 fprintf(stderr, "Init vinfo:\n");
1123 print_vinfo(&vinfo);
1125 SDL_SetError("Using software buffer, but no blitter "
1126 "function is available for %d bpp.",
1127 vinfo.bits_per_pixel);
1132 /* Set up the new mode framebuffer */
1133 current->flags &= SDL_FULLSCREEN;
1135 current->flags |= SDL_SWSURFACE;
1137 current->flags |= SDL_HWSURFACE;
1139 current->w = vinfo.xres;
1140 current->h = vinfo.yres;
1142 current->pitch = current->w * ((vinfo.bits_per_pixel + 7) / 8);
1143 current->pixels = shadow_mem;
1144 physlinebytes = finfo.line_length;
1146 current->pitch = finfo.line_length;
1147 current->pixels = mapped_mem+mapped_offset;
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));
1156 FB_FreeHWSurfaces(this);
1157 FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
1159 /* Let the application know we have a hardware palette */
1160 switch (finfo.visual) {
1161 case FB_VISUAL_PSEUDOCOLOR:
1162 current->flags |= SDL_HWPALETTE;
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;
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;
1182 /* Set the update rectangle function */
1183 this->UpdateRects = FB_DirectUpdate;
1190 void FB_DumpHWSurfaces(_THIS)
1192 vidmem_bucket *bucket;
1194 printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
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");
1204 if ( bucket != &surfaces ) {
1205 printf("Warning, corrupt bucket list! (!prev)\n");
1208 if ( bucket->next ) {
1209 if ( bucket->next->base != bucket->base+bucket->size ) {
1210 printf("Warning, corrupt bucket list! (next)\n");
1218 static int FB_InitHWSurfaces(_THIS, SDL_Surface *screen, char *base, int size)
1220 vidmem_bucket *bucket;
1222 surfaces_memtotal = size;
1223 surfaces_memleft = size;
1225 if ( surfaces_memleft > 0 ) {
1226 bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket));
1227 if ( bucket == NULL ) {
1231 bucket->prev = &surfaces;
1234 bucket->base = base;
1235 bucket->size = size;
1236 bucket->next = NULL;
1241 surfaces.prev = NULL;
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;
1250 static void FB_FreeHWSurfaces(_THIS)
1252 vidmem_bucket *bucket, *freeable;
1254 bucket = surfaces.next;
1257 bucket = bucket->next;
1260 surfaces.next = NULL;
1263 static int FB_AllocHWSurface(_THIS, SDL_Surface *surface)
1265 vidmem_bucket *bucket;
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...
1274 if ( surface->pitch > SDL_VideoSurface->pitch ) {
1275 SDL_SetError("Surface requested wider than screen");
1278 surface->pitch = SDL_VideoSurface->pitch;
1279 size = surface->h * surface->pitch;
1281 fprintf(stderr, "Allocating bucket of %d bytes\n", size);
1284 /* Quick check for available mem */
1285 if ( size > surfaces_memleft ) {
1286 SDL_SetError("Not enough video memory");
1290 /* Search for an empty bucket big enough */
1291 for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
1292 if ( ! bucket->used && (size <= bucket->size) ) {
1296 if ( bucket == NULL ) {
1297 SDL_SetError("Video memory too fragmented");
1301 /* Create a new bucket for left-over memory */
1302 extra = (bucket->size - size);
1304 vidmem_bucket *newbucket;
1307 fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
1309 newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket));
1310 if ( newbucket == NULL ) {
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;
1322 bucket->next = newbucket;
1325 /* Set the current bucket values and return it! */
1327 bucket->size = size;
1330 fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
1332 surfaces_memleft -= size;
1333 surface->flags |= SDL_HWSURFACE;
1334 surface->pixels = bucket->base;
1335 surface->hwdata = (struct private_hwdata *)bucket;
1338 static void FB_FreeHWSurface(_THIS, SDL_Surface *surface)
1340 vidmem_bucket *bucket, *freeable;
1342 /* Look for the bucket in the current list */
1343 for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
1344 if ( bucket == (vidmem_bucket *)surface->hwdata ) {
1348 if ( bucket && bucket->used ) {
1349 /* Add the memory back to the total */
1351 printf("Freeing bucket of %d bytes\n", bucket->size);
1353 surfaces_memleft += bucket->size;
1355 /* Can we merge the space with surrounding buckets? */
1357 if ( bucket->next && ! bucket->next->used ) {
1359 printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
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;
1369 if ( bucket->prev && ! bucket->prev->used ) {
1371 printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
1374 bucket->prev->size += bucket->size;
1375 bucket->prev->next = bucket->next;
1376 if ( bucket->next ) {
1377 bucket->next->prev = bucket->prev;
1382 surface->pixels = NULL;
1383 surface->hwdata = NULL;
1386 static int FB_LockHWSurface(_THIS, SDL_Surface *surface)
1388 if ( switched_away ) {
1389 return -2; /* no hardware access */
1391 if ( surface == this->screen ) {
1392 SDL_mutexP(hw_lock);
1393 if ( FB_IsSurfaceBusy(surface) ) {
1394 FB_WaitBusySurfaces(this);
1397 if ( FB_IsSurfaceBusy(surface) ) {
1398 FB_WaitBusySurfaces(this);
1403 static void FB_UnlockHWSurface(_THIS, SDL_Surface *surface)
1405 if ( surface == this->screen ) {
1406 SDL_mutexV(hw_lock);
1410 static void FB_WaitVBL(_THIS)
1412 #ifdef FBIOWAITRETRACE /* Heheh, this didn't make it into the main kernel */
1413 ioctl(console_fd, FBIOWAITRETRACE, 0);
1418 static void FB_WaitIdle(_THIS)
1423 static int FB_FlipHWSurface(_THIS, SDL_Surface *surface)
1425 if ( switched_away ) {
1426 return -2; /* no hardware access */
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);
1435 if ( ioctl(console_fd, FBIOPAN_DISPLAY, &cache_vinfo) < 0 ) {
1436 SDL_SetError("ioctl(FBIOPAN_DISPLAY) failed");
1439 flip_page = !flip_page;
1441 surface->pixels = flip_address[flip_page];
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)
1449 Uint16 *src_pos = (Uint16 *)byte_src_pos;
1450 Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
1453 Uint16 *src = src_pos;
1454 Uint16 *dst = dst_pos;
1455 for (w = width; w != 0; w--) {
1457 src += src_right_delta;
1460 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes);
1461 src_pos += src_down_delta;
1466 #define BLOCKSIZE_W 32
1467 #define BLOCKSIZE_H 32
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)
1473 Uint16 *src_pos = (Uint16 *)byte_src_pos;
1474 Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
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,
1485 min(w, BLOCKSIZE_W),
1486 min(height, BLOCKSIZE_H));
1487 src += src_right_delta * BLOCKSIZE_W;
1490 dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H);
1491 src_pos += src_down_delta * BLOCKSIZE_H;
1492 height -= BLOCKSIZE_H;
1496 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
1498 int width = cache_vinfo.xres;
1499 int height = cache_vinfo.yres;
1500 int bytes_per_pixel = (cache_vinfo.bits_per_pixel + 7) / 8;
1504 /* The application is already updating the visible video memory */
1508 if (cache_vinfo.bits_per_pixel != 16) {
1509 SDL_SetError("Shadow copy only implemented for 16 bpp");
1513 for (i = 0; i < numrects; i++) {
1515 int scr_x1, scr_y1, scr_x2, scr_y2;
1517 int shadow_right_delta; /* Address change when moving right in dest */
1518 int shadow_down_delta; /* Address change when moving down in dest */
1524 x2 = x1 + rects[i].w;
1525 y2 = y1 + rects[i].h;
1529 } else if (x1 > width) {
1534 } else if (x2 > width) {
1539 } else if (y1 > height) {
1544 } else if (y2 > height) {
1547 if (x2 <= x1 || y2 <= y1) {
1552 case FBCON_ROTATE_NONE:
1553 sha_x1 = scr_x1 = x1;
1554 sha_y1 = scr_y1 = y1;
1557 shadow_right_delta = 1;
1558 shadow_down_delta = width;
1560 case FBCON_ROTATE_CCW:
1562 scr_y1 = width - x2;
1564 scr_y2 = width - x1;
1567 shadow_right_delta = width;
1568 shadow_down_delta = -1;
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;
1577 shadow_right_delta = -1;
1578 shadow_down_delta = -width;
1580 case FBCON_ROTATE_CW:
1581 scr_x1 = height - y2;
1583 scr_x2 = height - y1;
1587 shadow_right_delta = -width;
1588 shadow_down_delta = 1;
1591 SDL_SetError("Unknown rotation");
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;
1600 blitFunc((Uint8 *) src_start,
1603 (Uint8 *) dst_start,
1610 #ifdef VGA16_FBCON_SUPPORT
1611 /* Code adapted with thanks from the XFree86 VGA16 driver! :) */
1612 #define writeGr(index, value) \
1613 outb(index, 0x3CE); \
1615 #define writeSeq(index, value) \
1616 outb(index, 0x3C4); \
1619 static void FB_VGA16Update(_THIS, int numrects, SDL_Rect *rects)
1621 SDL_Surface *screen;
1622 int width, height, FBPitch, left, i, j, SRCPitch, phase;
1624 Uint8 s1, s2, s3, s4;
1625 Uint32 *src, *srcPtr;
1626 Uint8 *dst, *dstPtr;
1628 if ( switched_away ) {
1629 return; /* no hardware access */
1632 screen = this->screen;
1633 FBPitch = screen->w >> 3;
1634 SRCPitch = screen->pitch >> 2;
1636 writeGr(0x03, 0x00);
1637 writeGr(0x05, 0x00);
1638 writeGr(0x01, 0x00);
1639 writeGr(0x08, 0xFF);
1642 left = rects->x & ~7;
1643 width = (rects->w + 7) >> 3;
1645 src = (Uint32*)screen->pixels + (rects->y * SRCPitch) + (left >> 2);
1646 dst = (Uint8*)mapped_mem + (rects->y * FBPitch) + (left >> 3);
1648 if((phase = (long)dst & 3L)) {
1650 if(phase > width) phase = width;
1655 writeSeq(0x02, 1 << 0);
1661 m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
1662 *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
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);
1680 m = (srcPtr[1] & 0x01010101) | ((srcPtr[0] & 0x01010101) << 4);
1681 *dstPtr++ = (m >> 24) | (m >> 15) | (m >> 6) | (m << 3);
1685 writeSeq(0x02, 1 << 1);
1691 m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
1692 *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
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);
1710 m = (srcPtr[1] & 0x02020202) | ((srcPtr[0] & 0x02020202) << 4);
1711 *dstPtr++ = (m >> 25) | (m >> 16) | (m >> 7) | (m << 2);
1715 writeSeq(0x02, 1 << 2);
1721 m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
1722 *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
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);
1740 m = (srcPtr[1] & 0x04040404) | ((srcPtr[0] & 0x04040404) << 4);
1741 *dstPtr++ = (m >> 26) | (m >> 17) | (m >> 8) | (m << 1);
1745 writeSeq(0x02, 1 << 3);
1751 m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
1752 *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
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);
1770 m = (srcPtr[1] & 0x08080808) | ((srcPtr[0] & 0x08080808) << 4);
1771 *dstPtr++ = (m >> 27) | (m >> 18) | (m >> 9) | m;
1781 #endif /* VGA16_FBCON_SUPPORT */
1783 void FB_SavePaletteTo(_THIS, int palette_len, __u16 *area)
1785 struct fb_cmap cmap;
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];
1793 ioctl(console_fd, FBIOGETCMAP, &cmap);
1796 void FB_RestorePaletteFrom(_THIS, int palette_len, __u16 *area)
1798 struct fb_cmap cmap;
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];
1806 ioctl(console_fd, FBIOPUTCMAP, &cmap);
1809 static void FB_SavePalette(_THIS, struct fb_fix_screeninfo *finfo,
1810 struct fb_var_screeninfo *vinfo)
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);
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!
1829 Adam Meyerowitz 1/19/2000
1830 ameyerow@optonline.com
1832 if ( finfo->visual == FB_VISUAL_DIRECTCOLOR ) {
1833 __u16 new_entries[3*256];
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);
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;
1848 FB_RestorePaletteFrom(this, 256, new_entries);
1852 static void FB_RestorePalette(_THIS)
1854 /* Restore the original palette */
1856 FB_RestorePaletteFrom(this, saved_cmaplen, saved_cmap);
1857 SDL_free(saved_cmap);
1862 static int FB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1868 struct fb_cmap cmap;
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;
1876 cmap.start = firstcolor;
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;
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);
1904 /* Note: If we are terminated, this could be called in the middle of
1905 another SDL video routine -- notably UpdateRects.
1907 static void FB_VideoQuit(_THIS)
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; }
1919 SDL_memset(this->screen->pixels,0,this->screen->h*this->screen->pitch);
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;
1929 /* Clear the lock mutex */
1931 SDL_DestroyMutex(hw_lock);
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]);
1941 SDL_free(SDL_modelist[i]);
1942 SDL_modelist[i] = NULL;
1946 /* Clean up the memory bucket list */
1947 FB_FreeHWSurfaces(this);
1949 /* Close console and input file descriptors */
1950 if ( console_fd > 0 ) {
1951 /* Unmap the video framebuffer and I/O registers */
1953 munmap(mapped_mem, mapped_memlen);
1957 munmap(mapped_io, mapped_iolen);
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);
1967 /* We're all done with the framebuffer */
1971 FB_CloseMouse(this);
1972 FB_CloseKeyboard(this);