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 /* libvga based SDL video driver implementation. 25 */ 26 27 #include <err.h> 28 #include <osreldate.h> 29 #include <unistd.h> 30 #include <sys/stat.h> 31 32 #include <sys/fbio.h> 33 #include <sys/consio.h> 34 #include <sys/kbio.h> 35 #include <vgl.h> 36 37 #include "SDL_video.h" 38 #include "SDL_mouse.h" 39 #include "../SDL_sysvideo.h" 40 #include "../SDL_pixels_c.h" 41 #include "../../events/SDL_events_c.h" 42 #include "SDL_vglvideo.h" 43 #include "SDL_vglevents_c.h" 44 #include "SDL_vglmouse_c.h" 45 46 47 /* Initialization/Query functions */ 48 static int VGL_VideoInit(_THIS, SDL_PixelFormat *vformat); 49 static SDL_Rect **VGL_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); 50 static SDL_Surface *VGL_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); 51 static int VGL_SetColors(_THIS, int firstcolor, int ncolors, 52 SDL_Color *colors); 53 static void VGL_VideoQuit(_THIS); 54 55 /* Hardware surface functions */ 56 static int VGL_AllocHWSurface(_THIS, SDL_Surface *surface); 57 static int VGL_LockHWSurface(_THIS, SDL_Surface *surface); 58 static int VGL_FlipHWSurface(_THIS, SDL_Surface *surface); 59 static void VGL_UnlockHWSurface(_THIS, SDL_Surface *surface); 60 static void VGL_FreeHWSurface(_THIS, SDL_Surface *surface); 61 62 /* Misc function */ 63 static VGLMode ** VGLListModes(int depth, int mem_model); 64 static void VGLWaitRetrace(void); 65 66 /* VGL driver bootstrap functions */ 67 68 static int VGL_Available(void) 69 { 70 /* 71 * Check to see if we are root and stdin is a 72 * virtual console. Also try to ensure that 73 * modes other than 320x200 are available 74 */ 75 int console, hires_available, i; 76 VGLMode **modes; 77 78 console = STDIN_FILENO; 79 if ( console >= 0 ) { 80 struct stat sb; 81 struct vt_mode dummy; 82 83 if ( (fstat(console, &sb) < 0) || 84 (ioctl(console, VT_GETMODE, &dummy) < 0) ) { 85 console = -1; 86 } 87 } 88 if (geteuid() != 0 && console == -1) 89 return 0; 90 91 modes = VGLListModes(8, V_INFO_MM_DIRECT | V_INFO_MM_PACKED); 92 hires_available = 0; 93 for (i = 0; modes[i] != NULL; i++) { 94 if ((modes[i]->ModeInfo.Xsize > 320) && 95 (modes[i]->ModeInfo.Ysize > 200) && 96 ((modes[i]->ModeInfo.Type == VIDBUF8) || 97 (modes[i]->ModeInfo.Type == VIDBUF16) || 98 (modes[i]->ModeInfo.Type == VIDBUF32))) { 99 hires_available = 1; 100 break; 101 } 102 } 103 return hires_available; 104 } 105 106 static void VGL_DeleteDevice(SDL_VideoDevice *device) 107 { 108 SDL_free(device->hidden); 109 SDL_free(device); 110 } 111 112 static SDL_VideoDevice *VGL_CreateDevice(int devindex) 113 { 114 SDL_VideoDevice *device; 115 116 /* Initialize all variables that we clean on shutdown */ 117 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice)); 118 if ( device ) { 119 SDL_memset(device, 0, (sizeof *device)); 120 device->hidden = (struct SDL_PrivateVideoData *) 121 SDL_malloc((sizeof *device->hidden)); 122 } 123 if ( (device == NULL) || (device->hidden == NULL) ) { 124 SDL_OutOfMemory(); 125 if ( device ) { 126 SDL_free(device); 127 } 128 return(0); 129 } 130 SDL_memset(device->hidden, 0, (sizeof *device->hidden)); 131 132 /* Set the function pointers */ 133 device->VideoInit = VGL_VideoInit; 134 device->ListModes = VGL_ListModes; 135 device->SetVideoMode = VGL_SetVideoMode; 136 device->SetColors = VGL_SetColors; 137 device->UpdateRects = NULL; 138 device->VideoQuit = VGL_VideoQuit; 139 device->AllocHWSurface = VGL_AllocHWSurface; 140 device->CheckHWBlit = NULL; 141 device->FillHWRect = NULL; 142 device->SetHWColorKey = NULL; 143 device->SetHWAlpha = NULL; 144 device->LockHWSurface = VGL_LockHWSurface; 145 device->UnlockHWSurface = VGL_UnlockHWSurface; 146 device->FlipHWSurface = VGL_FlipHWSurface; 147 device->FreeHWSurface = VGL_FreeHWSurface; 148 device->SetIcon = NULL; 149 device->SetCaption = NULL; 150 device->GetWMInfo = NULL; 151 device->FreeWMCursor = VGL_FreeWMCursor; 152 device->CreateWMCursor = VGL_CreateWMCursor; 153 device->ShowWMCursor = VGL_ShowWMCursor; 154 device->WarpWMCursor = VGL_WarpWMCursor; 155 device->InitOSKeymap = VGL_InitOSKeymap; 156 device->PumpEvents = VGL_PumpEvents; 157 158 device->free = VGL_DeleteDevice; 159 160 return device; 161 } 162 163 VideoBootStrap VGL_bootstrap = { 164 "vgl", "FreeBSD libVGL", 165 VGL_Available, VGL_CreateDevice 166 }; 167 168 static int VGL_AddMode(_THIS, VGLMode *inmode) 169 { 170 SDL_Rect *mode; 171 172 int i, index; 173 int next_mode; 174 175 /* Check to see if we already have this mode */ 176 if (inmode->Depth < 8) { /* Not supported */ 177 return 0; 178 } 179 index = ((inmode->Depth + 7) / 8) - 1; 180 for (i=0; i<SDL_nummodes[index]; ++i) { 181 mode = SDL_modelist[index][i]; 182 if ((mode->w == inmode->ModeInfo.Xsize) && 183 (mode->h == inmode->ModeInfo.Ysize)) 184 return 0; 185 } 186 187 /* Set up the new video mode rectangle */ 188 mode = (SDL_Rect *)SDL_malloc(sizeof *mode); 189 if (mode == NULL) { 190 SDL_OutOfMemory(); 191 return -1; 192 } 193 mode->x = 0; 194 mode->y = 0; 195 mode->w = inmode->ModeInfo.Xsize; 196 mode->h = inmode->ModeInfo.Ysize; 197 198 /* Allocate the new list of modes, and fill in the new mode */ 199 next_mode = SDL_nummodes[index]; 200 SDL_modelist[index] = (SDL_Rect **) 201 SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *)); 202 if (SDL_modelist[index] == NULL) { 203 SDL_OutOfMemory(); 204 SDL_nummodes[index] = 0; 205 SDL_free(mode); 206 return -1; 207 } 208 SDL_modelist[index][next_mode] = mode; 209 SDL_modelist[index][next_mode+1] = NULL; 210 SDL_nummodes[index]++; 211 212 return 0; 213 } 214 215 static void VGL_UpdateVideoInfo(_THIS) 216 { 217 this->info.wm_available = 0; 218 this->info.hw_available = 1; 219 this->info.video_mem = 0; 220 if (VGLCurMode == NULL) { 221 return; 222 } 223 if (VGLCurMode->ModeInfo.PixelBytes > 0) { 224 this->info.video_mem = VGLCurMode->ModeInfo.PixelBytes * 225 VGLCurMode->ModeInfo.Xsize * 226 VGLCurMode->ModeInfo.Ysize; 227 } 228 } 229 230 int VGL_VideoInit(_THIS, SDL_PixelFormat *vformat) 231 { 232 int i; 233 int total_modes; 234 VGLMode **modes; 235 236 /* Initialize all variables that we clean on shutdown */ 237 for ( i=0; i<NUM_MODELISTS; ++i ) { 238 SDL_nummodes[i] = 0; 239 SDL_modelist[i] = NULL; 240 } 241 242 /* Enable mouse and keyboard support */ 243 if (SDL_getenv("SDL_NO_RAWKBD") == NULL) { 244 if (VGLKeyboardInit(VGL_CODEKEYS) != 0) { 245 SDL_SetError("Unable to initialize keyboard"); 246 return -1; 247 } 248 } else { 249 warnx("Requiest to put keyboard into a raw mode ignored"); 250 } 251 if (VGL_initkeymaps(STDIN_FILENO) != 0) { 252 SDL_SetError("Unable to initialize keymap"); 253 return -1; 254 } 255 if (VGL_initmouse(STDIN_FILENO) != 0) { 256 SDL_SetError("Unable to initialize mouse"); 257 return -1; 258 } 259 260 /* Determine the current screen size */ 261 if (VGLCurMode != NULL) { 262 this->info.current_w = VGLCurMode->ModeInfo.Xsize; 263 this->info.current_h = VGLCurMode->ModeInfo.Ysize; 264 } 265 266 /* Determine the screen depth */ 267 if (VGLCurMode != NULL) 268 vformat->BitsPerPixel = VGLCurMode->Depth; 269 else 270 vformat->BitsPerPixel = 16; /* Good default */ 271 272 /* Query for the list of available video modes */ 273 total_modes = 0; 274 modes = VGLListModes(-1, V_INFO_MM_DIRECT | V_INFO_MM_PACKED); 275 for (i = 0; modes[i] != NULL; i++) { 276 if ((modes[i]->ModeInfo.Type == VIDBUF8) || 277 (modes[i]->ModeInfo.Type == VIDBUF16) || 278 (modes[i]->ModeInfo.Type == VIDBUF32)) { 279 VGL_AddMode(this, modes[i]); 280 total_modes++; 281 } 282 } 283 if (total_modes == 0) { 284 SDL_SetError("No linear video modes available"); 285 return -1; 286 } 287 288 /* Fill in our hardware acceleration capabilities */ 289 VGL_UpdateVideoInfo(this); 290 291 /* Create the hardware surface lock mutex */ 292 hw_lock = SDL_CreateMutex(); 293 if (hw_lock == NULL) { 294 SDL_SetError("Unable to create lock mutex"); 295 VGL_VideoQuit(this); 296 return -1; 297 } 298 299 /* We're done! */ 300 return 0; 301 } 302 303 SDL_Rect **VGL_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) 304 { 305 return SDL_modelist[((format->BitsPerPixel+7)/8)-1]; 306 } 307 308 /* Various screen update functions available */ 309 static void VGL_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); 310 static void VGL_BankedUpdate(_THIS, int numrects, SDL_Rect *rects); 311 312 SDL_Surface *VGL_SetVideoMode(_THIS, SDL_Surface *current, 313 int width, int height, int bpp, Uint32 flags) 314 { 315 int mode_found; 316 int i; 317 VGLMode **modes; 318 319 modes = VGLListModes(bpp, V_INFO_MM_DIRECT | V_INFO_MM_PACKED); 320 mode_found = 0; 321 for (i = 0; modes[i] != NULL; i++) { 322 if ((modes[i]->ModeInfo.Xsize == width) && 323 (modes[i]->ModeInfo.Ysize == height) && 324 ((modes[i]->ModeInfo.Type == VIDBUF8) || 325 (modes[i]->ModeInfo.Type == VIDBUF16) || 326 (modes[i]->ModeInfo.Type == VIDBUF32))) { 327 mode_found = 1; 328 break; 329 } 330 } 331 if (mode_found == 0) { 332 SDL_SetError("No matching video mode found"); 333 return NULL; 334 } 335 336 /* Shutdown previous videomode (if any) */ 337 if (VGLCurMode != NULL) 338 VGLEnd(); 339 340 /* Try to set the requested linear video mode */ 341 if (VGLInit(modes[i]->ModeId) != 0) { 342 SDL_SetError("Unable to switch to requested mode"); 343 return NULL; 344 } 345 346 VGLCurMode = SDL_realloc(VGLCurMode, sizeof(VGLMode)); 347 VGLCurMode->ModeInfo = *VGLDisplay; 348 VGLCurMode->Depth = modes[i]->Depth; 349 VGLCurMode->ModeId = modes[i]->ModeId; 350 VGLCurMode->Rmask = modes[i]->Rmask; 351 VGLCurMode->Gmask = modes[i]->Gmask; 352 VGLCurMode->Bmask = modes[i]->Bmask; 353 354 /* Workaround a bug in libvgl */ 355 if (VGLCurMode->ModeInfo.PixelBytes == 0) 356 (VGLCurMode->ModeInfo.PixelBytes = 1); 357 358 current->w = VGLCurMode->ModeInfo.Xsize; 359 current->h = VGLCurMode->ModeInfo.Ysize; 360 current->pixels = VGLCurMode->ModeInfo.Bitmap; 361 current->pitch = VGLCurMode->ModeInfo.Xsize * 362 VGLCurMode->ModeInfo.PixelBytes; 363 current->flags = (SDL_FULLSCREEN | SDL_HWSURFACE); 364 365 /* Check if we are in a pseudo-color mode */ 366 if (VGLCurMode->ModeInfo.Type == VIDBUF8) 367 current->flags |= SDL_HWPALETTE; 368 369 /* Check if we can do doublebuffering */ 370 if (flags & SDL_DOUBLEBUF) { 371 if (VGLCurMode->ModeInfo.Xsize * 2 <= 372 VGLCurMode->ModeInfo.VYsize) { 373 current->flags |= SDL_DOUBLEBUF; 374 flip_page = 0; 375 flip_address[0] = (byte *)current->pixels; 376 flip_address[1] = (byte *)current->pixels + 377 current->h * current->pitch; 378 VGL_FlipHWSurface(this, current); 379 } 380 } 381 382 if (! SDL_ReallocFormat(current, modes[i]->Depth, VGLCurMode->Rmask, 383 VGLCurMode->Gmask, VGLCurMode->Bmask, 0)) { 384 return NULL; 385 } 386 387 /* Update hardware acceleration info */ 388 VGL_UpdateVideoInfo(this); 389 390 /* Set the blit function */ 391 this->UpdateRects = VGL_DirectUpdate; 392 393 /* We're done */ 394 return current; 395 } 396 397 /* We don't actually allow hardware surfaces other than the main one */ 398 static int VGL_AllocHWSurface(_THIS, SDL_Surface *surface) 399 { 400 return -1; 401 } 402 static void VGL_FreeHWSurface(_THIS, SDL_Surface *surface) 403 { 404 return; 405 } 406 407 /* We need to wait for vertical retrace on page flipped displays */ 408 static int VGL_LockHWSurface(_THIS, SDL_Surface *surface) 409 { 410 if (surface == SDL_VideoSurface) { 411 SDL_mutexP(hw_lock); 412 } 413 return 0; 414 } 415 static void VGL_UnlockHWSurface(_THIS, SDL_Surface *surface) 416 { 417 if (surface == SDL_VideoSurface) { 418 SDL_mutexV(hw_lock); 419 } 420 } 421 422 static int VGL_FlipHWSurface(_THIS, SDL_Surface *surface) 423 { 424 // VGLWaitRetrace(); 425 if (VGLPanScreen(VGLDisplay, 0, flip_page * surface->h) < 0) { 426 SDL_SetError("VGLPanSreen() failed"); 427 return -1; 428 } 429 430 flip_page = !flip_page; 431 surface->pixels = flip_address[flip_page]; 432 433 return 0; 434 } 435 436 static void VGL_DirectUpdate(_THIS, int numrects, SDL_Rect *rects) 437 { 438 return; 439 } 440 441 static void VGL_BankedUpdate(_THIS, int numrects, SDL_Rect *rects) 442 { 443 return; 444 } 445 446 int VGL_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) 447 { 448 int i; 449 450 for(i = 0; i < ncolors; i++) { 451 VGLSetPaletteIndex(firstcolor + i, 452 colors[i].r>>2, 453 colors[i].g>>2, 454 colors[i].b>>2); 455 } 456 return 1; 457 } 458 459 /* Note: If we are terminated, this could be called in the middle of 460 another SDL video routine -- notably UpdateRects. 461 */ 462 void VGL_VideoQuit(_THIS) 463 { 464 int i, j; 465 466 /* Return the keyboard to the normal state */ 467 VGLKeyboardEnd(); 468 469 /* Reset the console video mode if we actually initialised one */ 470 if (VGLCurMode != NULL) { 471 VGLEnd(); 472 SDL_free(VGLCurMode); 473 VGLCurMode = NULL; 474 } 475 476 /* Clear the lock mutex */ 477 if (hw_lock != NULL) { 478 SDL_DestroyMutex(hw_lock); 479 hw_lock = NULL; 480 } 481 482 /* Free video mode lists */ 483 for (i = 0; i < NUM_MODELISTS; i++) { 484 if (SDL_modelist[i] != NULL) { 485 for (j = 0; SDL_modelist[i][j] != NULL; ++j) { 486 SDL_free(SDL_modelist[i][j]); 487 } 488 SDL_free(SDL_modelist[i]); 489 SDL_modelist[i] = NULL; 490 } 491 } 492 493 if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) { 494 /* Direct screen access, not a memory buffer */ 495 this->screen->pixels = NULL; 496 } 497 } 498 499 #define VGL_RED_INDEX 0 500 #define VGL_GREEN_INDEX 1 501 #define VGL_BLUE_INDEX 2 502 503 static VGLMode ** 504 VGLListModes(int depth, int mem_model) 505 { 506 static VGLMode **modes = NULL; 507 508 VGLBitmap *vminfop; 509 VGLMode **modesp, *modescp; 510 video_info_t minfo; 511 int adptype, i, modenum; 512 513 if (modes == NULL) { 514 modes = SDL_malloc(sizeof(VGLMode *) * M_VESA_MODE_MAX); 515 bzero(modes, sizeof(VGLMode *) * M_VESA_MODE_MAX); 516 } 517 modesp = modes; 518 519 for (modenum = 0; modenum < M_VESA_MODE_MAX; modenum++) { 520 minfo.vi_mode = modenum; 521 if (ioctl(0, CONS_MODEINFO, &minfo) || ioctl(0, CONS_CURRENT, &adptype)) 522 continue; 523 if (minfo.vi_mode != modenum) 524 continue; 525 if ((minfo.vi_flags & V_INFO_GRAPHICS) == 0) 526 continue; 527 if ((mem_model != -1) && ((minfo.vi_mem_model & mem_model) == 0)) 528 continue; 529 if ((depth > 1) && (minfo.vi_depth != depth)) 530 continue; 531 532 /* reallocf can fail */ 533 if ((*modesp = reallocf(*modesp, sizeof(VGLMode))) == NULL) 534 return NULL; 535 modescp = *modesp; 536 537 vminfop = &(modescp->ModeInfo); 538 bzero(vminfop, sizeof(VGLBitmap)); 539 540 vminfop->Type = NOBUF; 541 542 vminfop->PixelBytes = 1; /* Good default value */ 543 switch (minfo.vi_mem_model) { 544 case V_INFO_MM_PLANAR: 545 /* we can handle EGA/VGA planar modes only */ 546 if (!(minfo.vi_depth != 4 || minfo.vi_planes != 4 547 || (adptype != KD_EGA && adptype != KD_VGA))) 548 vminfop->Type = VIDBUF4; 549 break; 550 case V_INFO_MM_PACKED: 551 /* we can do only 256 color packed modes */ 552 if (minfo.vi_depth == 8) 553 vminfop->Type = VIDBUF8; 554 break; 555 case V_INFO_MM_VGAX: 556 vminfop->Type = VIDBUF8X; 557 break; 558 #if defined(__FREEBSD__) && (defined(__DragonFly__) || __FreeBSD_version >= 500000) 559 case V_INFO_MM_DIRECT: 560 vminfop->PixelBytes = minfo.vi_pixel_size; 561 switch (vminfop->PixelBytes) { 562 case 2: 563 vminfop->Type = VIDBUF16; 564 break; 565 #if notyet 566 case 3: 567 vminfop->Type = VIDBUF24; 568 break; 569 #endif 570 case 4: 571 vminfop->Type = VIDBUF32; 572 break; 573 default: 574 break; 575 } 576 #endif 577 default: 578 break; 579 } 580 if (vminfop->Type == NOBUF) 581 continue; 582 583 switch (vminfop->Type) { 584 case VIDBUF16: 585 case VIDBUF32: 586 modescp->Rmask = ((1 << minfo.vi_pixel_fsizes[VGL_RED_INDEX]) - 1) << 587 minfo.vi_pixel_fields[VGL_RED_INDEX]; 588 modescp->Gmask = ((1 << minfo.vi_pixel_fsizes[VGL_GREEN_INDEX]) - 1) << 589 minfo.vi_pixel_fields[VGL_GREEN_INDEX]; 590 modescp->Bmask = ((1 << minfo.vi_pixel_fsizes[VGL_BLUE_INDEX]) - 1) << 591 minfo.vi_pixel_fields[VGL_BLUE_INDEX]; 592 break; 593 594 default: 595 break; 596 } 597 598 vminfop->Xsize = minfo.vi_width; 599 vminfop->Ysize = minfo.vi_height; 600 modescp->Depth = minfo.vi_depth; 601 602 /* XXX */ 603 if (minfo.vi_mode >= M_VESA_BASE) 604 modescp->ModeId = _IO('V', minfo.vi_mode - M_VESA_BASE); 605 else 606 modescp->ModeId = _IO('S', minfo.vi_mode); 607 608 /* Sort list */ 609 for (i = 0; modes + i < modesp ; i++) { 610 if (modes[i]->ModeInfo.Xsize * modes[i]->ModeInfo.Ysize > 611 vminfop->Xsize * modes[i]->ModeInfo.Ysize) 612 continue; 613 if ((modes[i]->ModeInfo.Xsize * modes[i]->ModeInfo.Ysize == 614 vminfop->Xsize * vminfop->Ysize) && 615 (modes[i]->Depth >= modescp->Depth)) 616 continue; 617 *modesp = modes[i]; 618 modes[i] = modescp; 619 modescp = *modesp; 620 vminfop = &(modescp->ModeInfo); 621 } 622 623 modesp++; 624 } 625 626 if (*modesp != NULL) { 627 SDL_free(*modesp); 628 *modesp = NULL; 629 } 630 631 return modes; 632 } 633 634 static void 635 VGLWaitRetrace(void) 636 { 637 while (!(inb(0x3DA) & 8)); 638 while (inb(0x3DA) & 8); 639 } 640 641