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