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