1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken@libsdl.org 21*/ 22#include "SDL_config.h" 23 24/* SVGAlib based SDL video driver implementation. 25*/ 26 27#include <unistd.h> 28#include <sys/stat.h> 29#include <sys/types.h> 30#include <sys/ioctl.h> 31#include <fcntl.h> 32 33#if defined(__LINUX__) 34#include <linux/vt.h> 35#elif defined(__FREEBSD__) 36#include <sys/consio.h> 37#else 38#error You must choose your operating system here 39#endif 40#include <vga.h> 41#include <vgamouse.h> 42#include <vgakeyboard.h> 43 44#include "SDL_video.h" 45#include "SDL_mouse.h" 46#include "../SDL_sysvideo.h" 47#include "../SDL_pixels_c.h" 48#include "../../events/SDL_events_c.h" 49#include "SDL_svgavideo.h" 50#include "SDL_svgaevents_c.h" 51#include "SDL_svgamouse_c.h" 52 53/* Initialization/Query functions */ 54static int SVGA_VideoInit(_THIS, SDL_PixelFormat *vformat); 55static SDL_Rect **SVGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); 56static SDL_Surface *SVGA_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); 57static int SVGA_SetColors(_THIS, int firstcolor, int ncolors, 58 SDL_Color *colors); 59static void SVGA_VideoQuit(_THIS); 60 61/* Hardware surface functions */ 62static int SVGA_AllocHWSurface(_THIS, SDL_Surface *surface); 63static int SVGA_LockHWSurface(_THIS, SDL_Surface *surface); 64static int SVGA_FlipHWSurface(_THIS, SDL_Surface *surface); 65static void SVGA_UnlockHWSurface(_THIS, SDL_Surface *surface); 66static void SVGA_FreeHWSurface(_THIS, SDL_Surface *surface); 67 68/* SVGAlib driver bootstrap functions */ 69 70static int SVGA_Available(void) 71{ 72 /* Check to see if we are root and stdin is a virtual console */ 73 int console; 74 75 /* SVGALib 1.9.x+ doesn't require root (via /dev/svga) */ 76 int svgalib2 = -1; 77 78 /* See if we are connected to a virtual terminal */ 79 console = STDIN_FILENO; 80#if 0 /* This is no longer needed, SVGAlib can switch consoles for us */ 81 if ( console >= 0 ) { 82 struct stat sb; 83 struct vt_mode dummy; 84 85 if ( (fstat(console, &sb) < 0) || 86 (ioctl(console, VT_GETMODE, &dummy) < 0) ) { 87 console = -1; 88 } 89 } 90#endif /* 0 */ 91 92 /* See if SVGAlib 2.0 is available */ 93 svgalib2 = open("/dev/svga", O_RDONLY); 94 if (svgalib2 != -1) { 95 close(svgalib2); 96 } 97 98 return(((svgalib2 != -1) || (geteuid() == 0)) && (console >= 0)); 99} 100 101static void SVGA_DeleteDevice(SDL_VideoDevice *device) 102{ 103 SDL_free(device->hidden); 104 SDL_free(device); 105} 106 107static SDL_VideoDevice *SVGA_CreateDevice(int devindex) 108{ 109 SDL_VideoDevice *device; 110 111 /* Initialize all variables that we clean on shutdown */ 112 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice)); 113 if ( device ) { 114 SDL_memset(device, 0, (sizeof *device)); 115 device->hidden = (struct SDL_PrivateVideoData *) 116 SDL_malloc((sizeof *device->hidden)); 117 } 118 if ( (device == NULL) || (device->hidden == NULL) ) { 119 SDL_OutOfMemory(); 120 if ( device ) { 121 SDL_free(device); 122 } 123 return(0); 124 } 125 SDL_memset(device->hidden, 0, (sizeof *device->hidden)); 126 127 /* Set the function pointers */ 128 device->VideoInit = SVGA_VideoInit; 129 device->ListModes = SVGA_ListModes; 130 device->SetVideoMode = SVGA_SetVideoMode; 131 device->SetColors = SVGA_SetColors; 132 device->UpdateRects = NULL; 133 device->VideoQuit = SVGA_VideoQuit; 134 device->AllocHWSurface = SVGA_AllocHWSurface; 135 device->CheckHWBlit = NULL; 136 device->FillHWRect = NULL; 137 device->SetHWColorKey = NULL; 138 device->SetHWAlpha = NULL; 139 device->LockHWSurface = SVGA_LockHWSurface; 140 device->UnlockHWSurface = SVGA_UnlockHWSurface; 141 device->FlipHWSurface = SVGA_FlipHWSurface; 142 device->FreeHWSurface = SVGA_FreeHWSurface; 143 device->SetCaption = NULL; 144 device->SetIcon = NULL; 145 device->IconifyWindow = NULL; 146 device->GrabInput = NULL; 147 device->GetWMInfo = NULL; 148 device->InitOSKeymap = SVGA_InitOSKeymap; 149 device->PumpEvents = SVGA_PumpEvents; 150 151 device->free = SVGA_DeleteDevice; 152 153 return device; 154} 155 156VideoBootStrap SVGALIB_bootstrap = { 157 "svgalib", "SVGAlib", 158 SVGA_Available, SVGA_CreateDevice 159}; 160 161static int SVGA_AddMode(_THIS, int mode, int actually_add) 162{ 163 int i, j; 164 vga_modeinfo *modeinfo; 165 166 modeinfo = vga_getmodeinfo(mode); 167 168 i = modeinfo->bytesperpixel-1; 169 if ( i < 0 ) { 170 return 0; 171 } 172 if ( actually_add ) { 173 SDL_Rect saved_rect[2]; 174 int saved_mode[2]; 175 int b; 176 177 /* Add the mode, sorted largest to smallest */ 178 b = 0; 179 j = 0; 180 while ( (SDL_modelist[i][j]->w > modeinfo->width) || 181 (SDL_modelist[i][j]->h > modeinfo->height) ) { 182 ++j; 183 } 184 /* Skip modes that are already in our list */ 185 if ( (SDL_modelist[i][j]->w == modeinfo->width) && 186 (SDL_modelist[i][j]->h == modeinfo->height) ) { 187 return(0); 188 } 189 /* Insert the new mode */ 190 saved_rect[b] = *SDL_modelist[i][j]; 191 saved_mode[b] = SDL_vgamode[i][j]; 192 SDL_modelist[i][j]->w = modeinfo->width; 193 SDL_modelist[i][j]->h = modeinfo->height; 194 SDL_vgamode[i][j] = mode; 195 /* Everybody scoot down! */ 196 if ( saved_rect[b].w && saved_rect[b].h ) { 197 for ( ++j; SDL_modelist[i][j]->w; ++j ) { 198 saved_rect[!b] = *SDL_modelist[i][j]; 199 saved_mode[!b] = SDL_vgamode[i][j]; 200 *SDL_modelist[i][j] = saved_rect[b]; 201 SDL_vgamode[i][j] = saved_mode[b]; 202 b = !b; 203 } 204 *SDL_modelist[i][j] = saved_rect[b]; 205 SDL_vgamode[i][j] = saved_mode[b]; 206 } 207 } else { 208 ++SDL_nummodes[i]; 209 } 210 return(1); 211} 212 213static void SVGA_UpdateVideoInfo(_THIS) 214{ 215 vga_modeinfo *modeinfo; 216 217 this->info.wm_available = 0; 218 this->info.hw_available = (banked ? 0 : 1); 219 modeinfo = vga_getmodeinfo(vga_getcurrentmode()); 220 this->info.video_mem = modeinfo->memory; 221 /* FIXME: Add hardware accelerated blit information */ 222#ifdef SVGALIB_DEBUG 223 printf("Hardware accelerated blit: %savailable\n", modeinfo->haveblit ? "" : "not "); 224#endif 225} 226 227int SVGA_VideoInit(_THIS, SDL_PixelFormat *vformat) 228{ 229 int keyboard; 230 int i, j; 231 int mode, total_modes; 232 233 /* Initialize all variables that we clean on shutdown */ 234 for ( i=0; i<NUM_MODELISTS; ++i ) { 235 SDL_nummodes[i] = 0; 236 SDL_modelist[i] = NULL; 237 SDL_vgamode[i] = NULL; 238 } 239 240 /* Initialize the library */ 241 vga_disabledriverreport(); 242 if ( vga_init() < 0 ) { 243 SDL_SetError("Unable to initialize SVGAlib"); 244 return(-1); 245 } 246 vga_setmode(TEXT); 247 248 /* Enable mouse and keyboard support */ 249 vga_setmousesupport(1); 250 keyboard = keyboard_init_return_fd(); 251 if ( keyboard < 0 ) { 252 SDL_SetError("Unable to initialize keyboard"); 253 return(-1); 254 } 255 if ( SVGA_initkeymaps(keyboard) < 0 ) { 256 return(-1); 257 } 258 keyboard_seteventhandler(SVGA_keyboardcallback); 259 260 /* Determine the current screen size */ 261 this->info.current_w = 0; 262 this->info.current_h = 0; 263 264 /* Determine the screen depth (use default 8-bit depth) */ 265 vformat->BitsPerPixel = 8; 266 267 /* Enumerate the available fullscreen modes */ 268 total_modes = 0; 269 for ( mode=vga_lastmodenumber(); mode; --mode ) { 270 if ( vga_hasmode(mode) ) { 271 if ( SVGA_AddMode(this, mode, 0) ) { 272 ++total_modes; 273 } 274 } 275 } 276 if ( SVGA_AddMode(this, G320x200x256, 0) ) ++total_modes; 277 if ( total_modes == 0 ) { 278 SDL_SetError("No linear video modes available"); 279 return(-1); 280 } 281 for ( i=0; i<NUM_MODELISTS; ++i ) { 282 SDL_vgamode[i] = (int *)SDL_malloc(SDL_nummodes[i]*sizeof(int)); 283 if ( SDL_vgamode[i] == NULL ) { 284 SDL_OutOfMemory(); 285 return(-1); 286 } 287 SDL_modelist[i] = (SDL_Rect **) 288 SDL_malloc((SDL_nummodes[i]+1)*sizeof(SDL_Rect *)); 289 if ( SDL_modelist[i] == NULL ) { 290 SDL_OutOfMemory(); 291 return(-1); 292 } 293 for ( j=0; j<SDL_nummodes[i]; ++j ) { 294 SDL_modelist[i][j]=(SDL_Rect *)SDL_malloc(sizeof(SDL_Rect)); 295 if ( SDL_modelist[i][j] == NULL ) { 296 SDL_OutOfMemory(); 297 return(-1); 298 } 299 SDL_memset(SDL_modelist[i][j], 0, sizeof(SDL_Rect)); 300 } 301 SDL_modelist[i][j] = NULL; 302 } 303 for ( mode=vga_lastmodenumber(); mode; --mode ) { 304 if ( vga_hasmode(mode) ) { 305 SVGA_AddMode(this, mode, 1); 306 } 307 } 308 SVGA_AddMode(this, G320x200x256, 1); 309 310 /* Free extra (duplicated) modes */ 311 for ( i=0; i<NUM_MODELISTS; ++i ) { 312 j = 0; 313 while ( SDL_modelist[i][j] && SDL_modelist[i][j]->w ) { 314 j++; 315 } 316 while ( SDL_modelist[i][j] ) { 317 SDL_free(SDL_modelist[i][j]); 318 SDL_modelist[i][j] = NULL; 319 j++; 320 } 321 } 322 323 /* Fill in our hardware acceleration capabilities */ 324 SVGA_UpdateVideoInfo(this); 325 326 /* We're done! */ 327 return(0); 328} 329 330SDL_Rect **SVGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) 331{ 332 return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]); 333} 334 335/* Various screen update functions available */ 336static void SVGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); 337static void SVGA_BankedUpdate(_THIS, int numrects, SDL_Rect *rects); 338 339SDL_Surface *SVGA_SetVideoMode(_THIS, SDL_Surface *current, 340 int width, int height, int bpp, Uint32 flags) 341{ 342 int mode; 343 int vgamode; 344 vga_modeinfo *modeinfo; 345 int screenpage_len; 346 347 /* Free old pixels if we were in banked mode */ 348 if ( banked && current->pixels ) { 349 free(current->pixels); 350 current->pixels = NULL; 351 } 352 353 /* Try to set the requested linear video mode */ 354 bpp = (bpp+7)/8-1; 355 for ( mode=0; SDL_modelist[bpp][mode]; ++mode ) { 356 if ( (SDL_modelist[bpp][mode]->w == width) && 357 (SDL_modelist[bpp][mode]->h == height) ) { 358 break; 359 } 360 } 361 if ( SDL_modelist[bpp][mode] == NULL ) { 362 SDL_SetError("Couldn't find requested mode in list"); 363 return(NULL); 364 } 365 vgamode = SDL_vgamode[bpp][mode]; 366 vga_setmode(vgamode); 367 vga_setpage(0); 368 369 if ( (vga_setlinearaddressing() < 0) && (vgamode != G320x200x256) ) { 370 banked = 1; 371 } else { 372 banked = 0; 373 } 374 375 modeinfo = vga_getmodeinfo(SDL_vgamode[bpp][mode]); 376 377 /* Update hardware acceleration info */ 378 SVGA_UpdateVideoInfo(this); 379 380 /* Allocate the new pixel format for the screen */ 381 bpp = (bpp+1)*8; 382 if ( (bpp == 16) && (modeinfo->colors == 32768) ) { 383 bpp = 15; 384 } 385 if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) { 386 return(NULL); 387 } 388 389 /* Set up the new mode framebuffer */ 390 current->flags = SDL_FULLSCREEN; 391 if ( !banked ) { 392 current->flags |= SDL_HWSURFACE; 393 } 394 if ( bpp == 8 ) { 395 /* FIXME: What about DirectColor? */ 396 current->flags |= SDL_HWPALETTE; 397 } 398 current->w = width; 399 current->h = height; 400 current->pitch = modeinfo->linewidth; 401 if ( banked ) { 402 current->pixels = SDL_malloc(current->h * current->pitch); 403 if ( !current->pixels ) { 404 SDL_OutOfMemory(); 405 return(NULL); 406 } 407 } else { 408 current->pixels = vga_getgraphmem(); 409 } 410 411 /* set double-buffering */ 412 if ( (flags & SDL_DOUBLEBUF) && !banked ) 413 { 414 /* length of one screen page in bytes */ 415 screenpage_len=current->h*modeinfo->linewidth; 416 417 /* if start address should be aligned */ 418 if ( modeinfo->linewidth_unit ) 419 { 420 if ( screenpage_len % modeinfo->linewidth_unit ) 421 { 422 screenpage_len += modeinfo->linewidth_unit - ( screenpage_len % modeinfo->linewidth_unit ); 423 } 424 } 425 426 /* if we heve enough videomemory = ak je dost videopamete */ 427 if ( modeinfo->memory > ( screenpage_len * 2 / 1024 ) ) 428 { 429 current->flags |= SDL_DOUBLEBUF; 430 flip_page = 0; 431 flip_offset[0] = 0; 432 flip_offset[1] = screenpage_len; 433 flip_address[0] = vga_getgraphmem(); 434 flip_address[1] = flip_address[0]+screenpage_len; 435 SVGA_FlipHWSurface(this,current); 436 } 437 } 438 439 /* Set the blit function */ 440 if ( banked ) { 441 this->UpdateRects = SVGA_BankedUpdate; 442 } else { 443 this->UpdateRects = SVGA_DirectUpdate; 444 } 445 446 /* Set up the mouse handler again (buggy SVGAlib 1.40) */ 447 mouse_seteventhandler(SVGA_mousecallback); 448 449 /* We're done */ 450 return(current); 451} 452 453/* We don't actually allow hardware surfaces other than the main one */ 454static int SVGA_AllocHWSurface(_THIS, SDL_Surface *surface) 455{ 456 return(-1); 457} 458static void SVGA_FreeHWSurface(_THIS, SDL_Surface *surface) 459{ 460 return; 461} 462 463/* We need to wait for vertical retrace on page flipped displays */ 464static int SVGA_LockHWSurface(_THIS, SDL_Surface *surface) 465{ 466 /* The waiting is done in SVGA_FlipHWSurface() */ 467 return(0); 468} 469static void SVGA_UnlockHWSurface(_THIS, SDL_Surface *surface) 470{ 471 return; 472} 473 474static int SVGA_FlipHWSurface(_THIS, SDL_Surface *surface) 475{ 476 if ( !banked ) { 477 vga_setdisplaystart(flip_offset[flip_page]); 478 flip_page=!flip_page; 479 surface->pixels=flip_address[flip_page]; 480 vga_waitretrace(); 481 } 482 return(0); 483} 484 485static void SVGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects) 486{ 487 return; 488} 489 490static void SVGA_BankedUpdate(_THIS, int numrects, SDL_Rect *rects) 491{ 492 int i, j; 493 SDL_Rect *rect; 494 int page, vp; 495 int x, y, w, h; 496 unsigned char *src; 497 unsigned char *dst; 498 int bpp = this->screen->format->BytesPerPixel; 499 int pitch = this->screen->pitch; 500 501 dst = vga_getgraphmem(); 502 for ( i=0; i < numrects; ++i ) { 503 rect = &rects[i]; 504 x = rect->x; 505 y = rect->y; 506 w = rect->w * bpp; 507 h = rect->h; 508 509 vp = y * pitch + x * bpp; 510 src = (unsigned char *)this->screen->pixels + vp; 511 page = vp >> 16; 512 vp &= 0xffff; 513 vga_setpage(page); 514 for (j = 0; j < h; j++) { 515 if (vp + w > 0x10000) { 516 if (vp >= 0x10000) { 517 page++; 518 vga_setpage(page); 519 vp &= 0xffff; 520 } else { 521 SDL_memcpy(dst + vp, src, 0x10000 - vp); 522 page++; 523 vga_setpage(page); 524 SDL_memcpy(dst, src + 0x10000 - vp, 525 (vp + w) & 0xffff); 526 vp = (vp + pitch) & 0xffff; 527 src += pitch; 528 continue; 529 } 530 } 531 SDL_memcpy(dst + vp, src, w); 532 src += pitch; 533 vp += pitch; 534 } 535 } 536} 537 538int SVGA_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) 539{ 540 int i; 541 542 for(i = 0; i < ncolors; i++) { 543 vga_setpalette(firstcolor + i, 544 colors[i].r>>2, 545 colors[i].g>>2, 546 colors[i].b>>2); 547 } 548 return(1); 549} 550 551/* Note: If we are terminated, this could be called in the middle of 552 another SDL video routine -- notably UpdateRects. 553*/ 554void SVGA_VideoQuit(_THIS) 555{ 556 int i, j; 557 558 /* Reset the console video mode */ 559 if ( this->screen && (this->screen->w && this->screen->h) ) { 560 vga_setmode(TEXT); 561 } 562 keyboard_close(); 563 564 /* Free video mode lists */ 565 for ( i=0; i<NUM_MODELISTS; ++i ) { 566 if ( SDL_modelist[i] != NULL ) { 567 for ( j=0; SDL_modelist[i][j]; ++j ) 568 SDL_free(SDL_modelist[i][j]); 569 SDL_free(SDL_modelist[i]); 570 SDL_modelist[i] = NULL; 571 } 572 if ( SDL_vgamode[i] != NULL ) { 573 SDL_free(SDL_vgamode[i]); 574 SDL_vgamode[i] = NULL; 575 } 576 } 577 if ( this->screen ) { 578 if ( banked && this->screen->pixels ) { 579 SDL_free(this->screen->pixels); 580 } 581 this->screen->pixels = NULL; 582 } 583} 584 585