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 (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 /* DGA 2.0 based SDL video driver implementation. 25 */ 26 27 #include <stdio.h> 28 29 #include <X11/Xlib.h> 30 #include "../Xext/extensions/xf86dga.h" 31 32 #include "SDL_video.h" 33 #include "SDL_mouse.h" 34 #include "../SDL_sysvideo.h" 35 #include "../SDL_pixels_c.h" 36 #include "../../events/SDL_events_c.h" 37 #include "SDL_dgavideo.h" 38 #include "SDL_dgamouse_c.h" 39 #include "SDL_dgaevents_c.h" 40 41 /* get function pointers... */ 42 #include "../x11/SDL_x11dyn.h" 43 44 /*#define DGA_DEBUG*/ 45 46 /* Initialization/Query functions */ 47 static int DGA_VideoInit(_THIS, SDL_PixelFormat *vformat); 48 static SDL_Rect **DGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); 49 static SDL_Surface *DGA_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); 50 static int DGA_SetColors(_THIS, int firstcolor, int ncolors, 51 SDL_Color *colors); 52 static int DGA_SetGammaRamp(_THIS, Uint16 *ramp); 53 static void DGA_VideoQuit(_THIS); 54 55 /* Hardware surface functions */ 56 static int DGA_InitHWSurfaces(_THIS, SDL_Surface *screen, Uint8 *base, int size); 57 static void DGA_FreeHWSurfaces(_THIS); 58 static int DGA_AllocHWSurface(_THIS, SDL_Surface *surface); 59 static int DGA_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color); 60 static int DGA_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst); 61 static int DGA_LockHWSurface(_THIS, SDL_Surface *surface); 62 static void DGA_UnlockHWSurface(_THIS, SDL_Surface *surface); 63 static void DGA_FreeHWSurface(_THIS, SDL_Surface *surface); 64 static int DGA_FlipHWSurface(_THIS, SDL_Surface *surface); 65 66 /* DGA driver bootstrap functions */ 67 68 static int DGA_Available(void) 69 { 70 const char *display = NULL; 71 Display *dpy = NULL; 72 int available = 0; 73 74 /* The driver is available is available if the display is local 75 and the DGA 2.0+ extension is available, and we can map mem. 76 */ 77 if ( SDL_X11_LoadSymbols() ) { 78 if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) || 79 (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) { 80 dpy = XOpenDisplay(display); 81 if ( dpy ) { 82 int events, errors, major, minor; 83 84 if ( SDL_NAME(XDGAQueryExtension)(dpy, &events, &errors) && 85 SDL_NAME(XDGAQueryVersion)(dpy, &major, &minor) ) { 86 int screen; 87 88 screen = DefaultScreen(dpy); 89 if ( (major >= 2) && 90 SDL_NAME(XDGAOpenFramebuffer)(dpy, screen) ) { 91 available = 1; 92 SDL_NAME(XDGACloseFramebuffer)(dpy, screen); 93 } 94 } 95 XCloseDisplay(dpy); 96 } 97 } 98 SDL_X11_UnloadSymbols(); 99 } 100 return(available); 101 } 102 103 static void DGA_DeleteDevice(SDL_VideoDevice *device) 104 { 105 if (device != NULL) { 106 SDL_free(device->hidden); 107 SDL_free(device); 108 SDL_X11_UnloadSymbols(); 109 } 110 } 111 112 static SDL_VideoDevice *DGA_CreateDevice(int devindex) 113 { 114 SDL_VideoDevice *device = NULL; 115 116 /* Initialize all variables that we clean on shutdown */ 117 if (SDL_X11_LoadSymbols()) { 118 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice)); 119 if ( device ) { 120 SDL_memset(device, 0, (sizeof *device)); 121 device->hidden = (struct SDL_PrivateVideoData *) 122 SDL_malloc((sizeof *device->hidden)); 123 } 124 if ( (device == NULL) || (device->hidden == NULL) ) { 125 SDL_OutOfMemory(); 126 if ( device ) { 127 SDL_free(device); 128 } 129 SDL_X11_UnloadSymbols(); 130 return(0); 131 } 132 SDL_memset(device->hidden, 0, (sizeof *device->hidden)); 133 134 /* Set the function pointers */ 135 device->VideoInit = DGA_VideoInit; 136 device->ListModes = DGA_ListModes; 137 device->SetVideoMode = DGA_SetVideoMode; 138 device->SetColors = DGA_SetColors; 139 device->UpdateRects = NULL; 140 device->VideoQuit = DGA_VideoQuit; 141 device->AllocHWSurface = DGA_AllocHWSurface; 142 device->CheckHWBlit = DGA_CheckHWBlit; 143 device->FillHWRect = DGA_FillHWRect; 144 device->SetHWColorKey = NULL; 145 device->SetHWAlpha = NULL; 146 device->LockHWSurface = DGA_LockHWSurface; 147 device->UnlockHWSurface = DGA_UnlockHWSurface; 148 device->FlipHWSurface = DGA_FlipHWSurface; 149 device->FreeHWSurface = DGA_FreeHWSurface; 150 device->SetGammaRamp = DGA_SetGammaRamp; 151 device->GetGammaRamp = NULL; 152 device->SetCaption = NULL; 153 device->SetIcon = NULL; 154 device->IconifyWindow = NULL; 155 device->GrabInput = NULL; 156 device->GetWMInfo = NULL; 157 device->InitOSKeymap = DGA_InitOSKeymap; 158 device->PumpEvents = DGA_PumpEvents; 159 160 device->free = DGA_DeleteDevice; 161 } 162 163 return device; 164 } 165 166 VideoBootStrap DGA_bootstrap = { 167 "dga", "XFree86 DGA 2.0", 168 DGA_Available, DGA_CreateDevice 169 }; 170 171 static int DGA_AddMode(_THIS, int bpp, int w, int h) 172 { 173 SDL_Rect *mode; 174 int index; 175 int next_mode; 176 177 /* Check to see if we already have this mode */ 178 if ( bpp < 8 ) { /* Not supported */ 179 return(0); 180 } 181 index = ((bpp+7)/8)-1; 182 if ( SDL_nummodes[index] > 0 ) { 183 mode = SDL_modelist[index][SDL_nummodes[index]-1]; 184 if ( (mode->w == w) && (mode->h == h) ) { 185 return(0); 186 } 187 } 188 189 /* Set up the new video mode rectangle */ 190 mode = (SDL_Rect *)SDL_malloc(sizeof *mode); 191 if ( mode == NULL ) { 192 SDL_OutOfMemory(); 193 return(-1); 194 } 195 mode->x = 0; 196 mode->y = 0; 197 mode->w = w; 198 mode->h = h; 199 200 /* Allocate the new list of modes, and fill in the new mode */ 201 next_mode = SDL_nummodes[index]; 202 SDL_modelist[index] = (SDL_Rect **) 203 SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *)); 204 if ( SDL_modelist[index] == NULL ) { 205 SDL_OutOfMemory(); 206 SDL_nummodes[index] = 0; 207 SDL_free(mode); 208 return(-1); 209 } 210 SDL_modelist[index][next_mode] = mode; 211 SDL_modelist[index][next_mode+1] = NULL; 212 SDL_nummodes[index]++; 213 214 return(0); 215 } 216 217 /* This whole function is a hack. :) */ 218 static Uint32 get_video_size(_THIS) 219 { 220 /* This is a non-exported function from libXxf86dga.a */ 221 extern unsigned char *SDL_NAME(XDGAGetMappedMemory)(int screen); 222 FILE *proc; 223 unsigned long mem; 224 unsigned start, stop; 225 char line[BUFSIZ]; 226 Uint32 size; 227 228 mem = (unsigned long)SDL_NAME(XDGAGetMappedMemory)(DGA_Screen); 229 size = 0; 230 proc = fopen("/proc/self/maps", "r"); 231 if ( proc ) { 232 while ( fgets(line, sizeof(line)-1, proc) ) { 233 SDL_sscanf(line, "%x-%x", &start, &stop); 234 if ( start == mem ) { 235 size = (Uint32)((stop-start)/1024); 236 break; 237 } 238 } 239 fclose(proc); 240 } 241 return(size); 242 } 243 244 #ifdef DGA_DEBUG 245 static void PrintMode(SDL_NAME(XDGAMode) *mode) 246 { 247 printf("Mode: %s (%dx%d) at %d bpp (%f refresh, %d pitch) num: %d\n", 248 mode->name, 249 mode->viewportWidth, mode->viewportHeight, 250 mode->depth == 24 ? mode->bitsPerPixel : mode->depth, 251 mode->verticalRefresh, mode->bytesPerScanline, mode->num); 252 printf("\tRGB: 0x%8.8x 0x%8.8x 0x%8.8x (%d - %s)\n", 253 mode->redMask, mode->greenMask, mode->blueMask, 254 mode->visualClass, 255 mode->visualClass == TrueColor ? "truecolor" : 256 mode->visualClass == DirectColor ? "directcolor" : 257 mode->visualClass == PseudoColor ? "pseudocolor" : "unknown"); 258 printf("\tFlags: "); 259 if ( mode->flags & XDGAConcurrentAccess ) 260 printf(" XDGAConcurrentAccess"); 261 if ( mode->flags & XDGASolidFillRect ) 262 printf(" XDGASolidFillRect"); 263 if ( mode->flags & XDGABlitRect ) 264 printf(" XDGABlitRect"); 265 if ( mode->flags & XDGABlitTransRect ) 266 printf(" XDGABlitTransRect"); 267 if ( mode->flags & XDGAPixmap ) 268 printf(" XDGAPixmap"); 269 if ( mode->flags & XDGAInterlaced ) 270 printf(" XDGAInterlaced"); 271 if ( mode->flags & XDGADoublescan ) 272 printf(" XDGADoublescan"); 273 if ( mode->viewportFlags & XDGAFlipRetrace ) 274 printf(" XDGAFlipRetrace"); 275 if ( mode->viewportFlags & XDGAFlipImmediate ) 276 printf(" XDGAFlipImmediate"); 277 printf("\n"); 278 } 279 #endif /* DGA_DEBUG */ 280 281 static int cmpmodes(const void *va, const void *vb) 282 { 283 const SDL_NAME(XDGAMode) *a = (const SDL_NAME(XDGAMode) *)va; 284 const SDL_NAME(XDGAMode) *b = (const SDL_NAME(XDGAMode) *)vb; 285 286 if ( (a->viewportWidth == b->viewportWidth) && 287 (b->viewportHeight == a->viewportHeight) ) { 288 /* Prefer 32 bpp over 24 bpp, 16 bpp over 15 bpp */ 289 int a_bpp = a->depth == 24 ? a->bitsPerPixel : a->depth; 290 int b_bpp = b->depth == 24 ? b->bitsPerPixel : b->depth; 291 if ( a_bpp != b_bpp ) { 292 return b_bpp - a_bpp; 293 } 294 /* Prefer DirectColor visuals, for gamma support */ 295 if ( a->visualClass == DirectColor && b->visualClass != DirectColor ) 296 return -1; 297 if ( b->visualClass == DirectColor && a->visualClass != DirectColor ) 298 return 1; 299 /* Maintain server refresh rate sorting */ 300 return a->num - b->num; 301 } else if ( a->viewportWidth == b->viewportWidth ) { 302 return b->viewportHeight - a->viewportHeight; 303 } else { 304 return b->viewportWidth - a->viewportWidth; 305 } 306 } 307 static void UpdateHWInfo(_THIS, SDL_NAME(XDGAMode) *mode) 308 { 309 this->info.wm_available = 0; 310 this->info.hw_available = 1; 311 if ( mode->flags & XDGABlitRect ) { 312 this->info.blit_hw = 1; 313 } else { 314 this->info.blit_hw = 0; 315 } 316 if ( mode->flags & XDGABlitTransRect ) { 317 this->info.blit_hw_CC = 1; 318 } else { 319 this->info.blit_hw_CC = 0; 320 } 321 if ( mode->flags & XDGASolidFillRect ) { 322 this->info.blit_fill = 1; 323 } else { 324 this->info.blit_fill = 0; 325 } 326 this->info.video_mem = get_video_size(this); 327 } 328 329 static int DGA_VideoInit(_THIS, SDL_PixelFormat *vformat) 330 { 331 const char *env; 332 const char *display; 333 int event_base, error_base; 334 int major_version, minor_version; 335 Visual *visual; 336 SDL_NAME(XDGAMode) *modes; 337 int i, num_modes; 338 339 /* Open the X11 display */ 340 display = NULL; /* Get it from DISPLAY environment variable */ 341 342 DGA_Display = XOpenDisplay(display); 343 if ( DGA_Display == NULL ) { 344 SDL_SetError("Couldn't open X11 display"); 345 return(-1); 346 } 347 348 /* Check for the DGA extension */ 349 if ( ! SDL_NAME(XDGAQueryExtension)(DGA_Display, &event_base, &error_base) || 350 ! SDL_NAME(XDGAQueryVersion)(DGA_Display, &major_version, &minor_version) ) { 351 SDL_SetError("DGA extension not available"); 352 XCloseDisplay(DGA_Display); 353 return(-1); 354 } 355 if ( major_version < 2 ) { 356 SDL_SetError("DGA driver requires DGA 2.0 or newer"); 357 XCloseDisplay(DGA_Display); 358 return(-1); 359 } 360 DGA_event_base = event_base; 361 362 /* Determine the current screen size */ 363 this->info.current_w = DisplayWidth(DGA_Display, DGA_Screen); 364 this->info.current_h = DisplayHeight(DGA_Display, DGA_Screen); 365 366 /* Determine the current screen depth */ 367 visual = DefaultVisual(DGA_Display, DGA_Screen); 368 { 369 XPixmapFormatValues *pix_format; 370 int i, num_formats; 371 372 vformat->BitsPerPixel = DefaultDepth(DGA_Display, DGA_Screen); 373 pix_format = XListPixmapFormats(DGA_Display, &num_formats); 374 if ( pix_format == NULL ) { 375 SDL_SetError("Couldn't determine screen formats"); 376 XCloseDisplay(DGA_Display); 377 return(-1); 378 } 379 for ( i=0; i<num_formats; ++i ) { 380 if ( vformat->BitsPerPixel == pix_format[i].depth ) 381 break; 382 } 383 if ( i != num_formats ) 384 vformat->BitsPerPixel = pix_format[i].bits_per_pixel; 385 XFree((char *)pix_format); 386 } 387 if ( vformat->BitsPerPixel > 8 ) { 388 vformat->Rmask = visual->red_mask; 389 vformat->Gmask = visual->green_mask; 390 vformat->Bmask = visual->blue_mask; 391 } 392 393 /* Open access to the framebuffer */ 394 if ( ! SDL_NAME(XDGAOpenFramebuffer)(DGA_Display, DGA_Screen) ) { 395 SDL_SetError("Unable to map the video memory"); 396 XCloseDisplay(DGA_Display); 397 return(-1); 398 } 399 400 /* Allow environment override of screensaver disable. */ 401 env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER"); 402 if ( env ) { 403 allow_screensaver = SDL_atoi(env); 404 } else { 405 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER 406 allow_screensaver = 0; 407 #else 408 allow_screensaver = 1; 409 #endif 410 } 411 412 /* Query for the list of available video modes */ 413 modes = SDL_NAME(XDGAQueryModes)(DGA_Display, DGA_Screen, &num_modes); 414 SDL_qsort(modes, num_modes, sizeof *modes, cmpmodes); 415 for ( i=0; i<num_modes; ++i ) { 416 if ( ((modes[i].visualClass == PseudoColor) || 417 (modes[i].visualClass == DirectColor) || 418 (modes[i].visualClass == TrueColor)) && 419 !(modes[i].flags & (XDGAInterlaced|XDGADoublescan)) ) { 420 #ifdef DGA_DEBUG 421 PrintMode(&modes[i]); 422 #endif 423 DGA_AddMode(this, modes[i].bitsPerPixel, 424 modes[i].viewportWidth, 425 modes[i].viewportHeight); 426 } 427 } 428 UpdateHWInfo(this, modes); 429 XFree(modes); 430 431 /* Create the hardware surface lock mutex */ 432 hw_lock = SDL_CreateMutex(); 433 if ( hw_lock == NULL ) { 434 SDL_SetError("Unable to create lock mutex"); 435 DGA_VideoQuit(this); 436 return(-1); 437 } 438 439 #ifdef LOCK_DGA_DISPLAY 440 /* Create the event lock so we're thread-safe.. :-/ */ 441 event_lock = SDL_CreateMutex(); 442 #endif /* LOCK_DGA_DISPLAY */ 443 444 /* We're done! */ 445 return(0); 446 } 447 448 SDL_Rect **DGA_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) 449 { 450 return(SDL_modelist[((format->BitsPerPixel+7)/8)-1]); 451 } 452 453 /* Various screen update functions available */ 454 static void DGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects); 455 456 SDL_Surface *DGA_SetVideoMode(_THIS, SDL_Surface *current, 457 int width, int height, int bpp, Uint32 flags) 458 { 459 SDL_NAME(XDGAMode) *modes; 460 int i, num_modes; 461 SDL_NAME(XDGADevice) *mode; 462 int screen_len; 463 Uint8 *surfaces_mem; 464 int surfaces_len; 465 466 /* Free any previous colormap */ 467 if ( DGA_colormap ) { 468 XFreeColormap(DGA_Display, DGA_colormap); 469 DGA_colormap = 0; 470 } 471 472 /* Search for a matching video mode */ 473 modes = SDL_NAME(XDGAQueryModes)(DGA_Display, DGA_Screen, &num_modes); 474 SDL_qsort(modes, num_modes, sizeof *modes, cmpmodes); 475 for ( i=0; i<num_modes; ++i ) { 476 int depth; 477 478 depth = modes[i].depth; 479 if ( depth == 24 ) { /* Distinguish between 24 and 32 bpp */ 480 depth = modes[i].bitsPerPixel; 481 } 482 if ( (depth == bpp) && 483 (modes[i].viewportWidth == width) && 484 (modes[i].viewportHeight == height) && 485 ((modes[i].visualClass == PseudoColor) || 486 (modes[i].visualClass == DirectColor) || 487 (modes[i].visualClass == TrueColor)) && 488 !(modes[i].flags & (XDGAInterlaced|XDGADoublescan)) ) { 489 break; 490 } 491 } 492 if ( i == num_modes ) { 493 SDL_SetError("No matching video mode found"); 494 return(NULL); 495 } 496 #ifdef DGA_DEBUG 497 PrintMode(&modes[i]); 498 #endif 499 500 /* Set the video mode */ 501 mode = SDL_NAME(XDGASetMode)(DGA_Display, DGA_Screen, modes[i].num); 502 XFree(modes); 503 if ( mode == NULL ) { 504 SDL_SetError("Unable to switch to requested mode"); 505 return(NULL); 506 } 507 DGA_visualClass = mode->mode.visualClass; 508 memory_base = (Uint8 *)mode->data; 509 memory_pitch = mode->mode.bytesPerScanline; 510 511 /* Set up the new mode framebuffer */ 512 current->flags = (SDL_FULLSCREEN|SDL_HWSURFACE); 513 current->w = mode->mode.viewportWidth; 514 current->h = mode->mode.viewportHeight; 515 current->pitch = memory_pitch; 516 current->pixels = memory_base; 517 if ( ! SDL_ReallocFormat(current, mode->mode.bitsPerPixel, 518 mode->mode.redMask, 519 mode->mode.greenMask, 520 mode->mode.blueMask, 0) ) { 521 return(NULL); 522 } 523 screen_len = current->h*current->pitch; 524 525 /* Create a colormap if necessary */ 526 if ( (DGA_visualClass == PseudoColor) || 527 (DGA_visualClass == DirectColor) ) { 528 DGA_colormap = SDL_NAME(XDGACreateColormap)(DGA_Display, DGA_Screen, 529 mode, AllocAll); 530 if ( DGA_visualClass == PseudoColor ) { 531 current->flags |= SDL_HWPALETTE; 532 } else { 533 /* Initialize the colormap to the identity mapping */ 534 SDL_GetGammaRamp(0, 0, 0); 535 this->screen = current; 536 DGA_SetGammaRamp(this, this->gamma); 537 this->screen = NULL; 538 } 539 } else { 540 DGA_colormap = SDL_NAME(XDGACreateColormap)(DGA_Display, DGA_Screen, 541 mode, AllocNone); 542 } 543 SDL_NAME(XDGAInstallColormap)(DGA_Display, DGA_Screen, DGA_colormap); 544 545 /* Update the hardware capabilities */ 546 UpdateHWInfo(this, &mode->mode); 547 548 /* Set up the information for hardware surfaces */ 549 surfaces_mem = (Uint8 *)current->pixels + screen_len; 550 surfaces_len = (mode->mode.imageHeight*current->pitch - screen_len); 551 552 /* Update for double-buffering, if we can */ 553 SDL_NAME(XDGASetViewport)(DGA_Display, DGA_Screen, 0, 0, XDGAFlipRetrace); 554 if ( flags & SDL_DOUBLEBUF ) { 555 if ( mode->mode.imageHeight >= (current->h*2) ) { 556 current->flags |= SDL_DOUBLEBUF; 557 flip_page = 0; 558 flip_yoffset[0] = 0; 559 flip_yoffset[1] = current->h; 560 flip_address[0] = memory_base; 561 flip_address[1] = memory_base+screen_len; 562 surfaces_mem += screen_len; 563 surfaces_len -= screen_len; 564 } 565 } 566 567 /* Allocate memory tracking for hardware surfaces */ 568 DGA_FreeHWSurfaces(this); 569 if ( surfaces_len < 0 ) { 570 surfaces_len = 0; 571 } 572 DGA_InitHWSurfaces(this, current, surfaces_mem, surfaces_len); 573 574 /* Expose the back buffer as surface memory */ 575 if ( current->flags & SDL_DOUBLEBUF ) { 576 this->screen = current; 577 DGA_FlipHWSurface(this, current); 578 this->screen = NULL; 579 } 580 581 /* Set the update rectangle function */ 582 this->UpdateRects = DGA_DirectUpdate; 583 584 /* Enable mouse and keyboard support */ 585 { long input_mask; 586 input_mask = (KeyPressMask | KeyReleaseMask); 587 input_mask |= (ButtonPressMask | ButtonReleaseMask); 588 input_mask |= PointerMotionMask; 589 SDL_NAME(XDGASelectInput)(DGA_Display, DGA_Screen, input_mask); 590 } 591 592 /* We're done */ 593 return(current); 594 } 595 596 #ifdef DGA_DEBUG 597 static void DGA_DumpHWSurfaces(_THIS) 598 { 599 vidmem_bucket *bucket; 600 601 printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal); 602 printf("\n"); 603 printf(" Base Size\n"); 604 for ( bucket=&surfaces; bucket; bucket=bucket->next ) { 605 printf("Bucket: %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free"); 606 if ( bucket->prev ) { 607 if ( bucket->base != bucket->prev->base+bucket->prev->size ) { 608 printf("Warning, corrupt bucket list! (prev)\n"); 609 } 610 } else { 611 if ( bucket != &surfaces ) { 612 printf("Warning, corrupt bucket list! (!prev)\n"); 613 } 614 } 615 if ( bucket->next ) { 616 if ( bucket->next->base != bucket->base+bucket->size ) { 617 printf("Warning, corrupt bucket list! (next)\n"); 618 } 619 } 620 } 621 printf("\n"); 622 } 623 #endif 624 625 static int DGA_InitHWSurfaces(_THIS, SDL_Surface *screen, Uint8 *base, int size) 626 { 627 vidmem_bucket *bucket; 628 629 surfaces_memtotal = size; 630 surfaces_memleft = size; 631 632 if ( surfaces_memleft > 0 ) { 633 bucket = (vidmem_bucket *)SDL_malloc(sizeof(*bucket)); 634 if ( bucket == NULL ) { 635 SDL_OutOfMemory(); 636 return(-1); 637 } 638 bucket->prev = &surfaces; 639 bucket->used = 0; 640 bucket->dirty = 0; 641 bucket->base = base; 642 bucket->size = size; 643 bucket->next = NULL; 644 } else { 645 bucket = NULL; 646 } 647 648 surfaces.prev = NULL; 649 surfaces.used = 1; 650 surfaces.dirty = 0; 651 surfaces.base = screen->pixels; 652 surfaces.size = (unsigned int)((long)base - (long)surfaces.base); 653 surfaces.next = bucket; 654 screen->hwdata = (struct private_hwdata *)((char*)&surfaces); 655 return(0); 656 } 657 static void DGA_FreeHWSurfaces(_THIS) 658 { 659 vidmem_bucket *bucket, *freeable; 660 661 bucket = surfaces.next; 662 while ( bucket ) { 663 freeable = bucket; 664 bucket = bucket->next; 665 SDL_free(freeable); 666 } 667 surfaces.next = NULL; 668 } 669 670 static __inline__ void DGA_AddBusySurface(SDL_Surface *surface) 671 { 672 ((vidmem_bucket *)surface->hwdata)->dirty = 1; 673 } 674 675 static __inline__ int DGA_IsSurfaceBusy(SDL_Surface *surface) 676 { 677 return ((vidmem_bucket *)surface->hwdata)->dirty; 678 } 679 680 static __inline__ void DGA_WaitBusySurfaces(_THIS) 681 { 682 vidmem_bucket *bucket; 683 684 /* Wait for graphic operations to complete */ 685 SDL_NAME(XDGASync)(DGA_Display, DGA_Screen); 686 687 /* Clear all surface dirty bits */ 688 for ( bucket=&surfaces; bucket; bucket=bucket->next ) { 689 bucket->dirty = 0; 690 } 691 } 692 693 static int DGA_AllocHWSurface(_THIS, SDL_Surface *surface) 694 { 695 vidmem_bucket *bucket; 696 int size; 697 int extra; 698 int retval = 0; 699 700 /* Temporarily, we only allow surfaces the same width as display. 701 Some blitters require the pitch between two hardware surfaces 702 to be the same. Others have interesting alignment restrictions. 703 */ 704 if ( surface->pitch > SDL_VideoSurface->pitch ) { 705 SDL_SetError("Surface requested wider than screen"); 706 return(-1); 707 } 708 surface->pitch = SDL_VideoSurface->pitch; 709 size = surface->h * surface->pitch; 710 #ifdef DGA_DEBUG 711 fprintf(stderr, "Allocating bucket of %d bytes\n", size); 712 #endif 713 LOCK_DISPLAY(); 714 715 /* Quick check for available mem */ 716 if ( size > surfaces_memleft ) { 717 SDL_SetError("Not enough video memory"); 718 retval = -1; 719 goto done; 720 } 721 722 /* Search for an empty bucket big enough */ 723 for ( bucket=&surfaces; bucket; bucket=bucket->next ) { 724 if ( ! bucket->used && (size <= bucket->size) ) { 725 break; 726 } 727 } 728 if ( bucket == NULL ) { 729 SDL_SetError("Video memory too fragmented"); 730 retval = -1; 731 goto done; 732 } 733 734 /* Create a new bucket for left-over memory */ 735 extra = (bucket->size - size); 736 if ( extra ) { 737 vidmem_bucket *newbucket; 738 739 #ifdef DGA_DEBUG 740 fprintf(stderr, "Adding new free bucket of %d bytes\n", extra); 741 #endif 742 newbucket = (vidmem_bucket *)SDL_malloc(sizeof(*newbucket)); 743 if ( newbucket == NULL ) { 744 SDL_OutOfMemory(); 745 retval = -1; 746 goto done; 747 } 748 newbucket->prev = bucket; 749 newbucket->used = 0; 750 newbucket->base = bucket->base+size; 751 newbucket->size = extra; 752 newbucket->next = bucket->next; 753 if ( bucket->next ) { 754 bucket->next->prev = newbucket; 755 } 756 bucket->next = newbucket; 757 } 758 759 /* Set the current bucket values and return it! */ 760 bucket->used = 1; 761 bucket->size = size; 762 bucket->dirty = 0; 763 #ifdef DGA_DEBUG 764 fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base); 765 #endif 766 surfaces_memleft -= size; 767 surface->flags |= SDL_HWSURFACE; 768 surface->pixels = bucket->base; 769 surface->hwdata = (struct private_hwdata *)bucket; 770 done: 771 UNLOCK_DISPLAY(); 772 return(retval); 773 } 774 static void DGA_FreeHWSurface(_THIS, SDL_Surface *surface) 775 { 776 vidmem_bucket *bucket, *freeable; 777 778 /* Wait for any pending operations involving this surface */ 779 if ( DGA_IsSurfaceBusy(surface) ) { 780 LOCK_DISPLAY(); 781 DGA_WaitBusySurfaces(this); 782 UNLOCK_DISPLAY(); 783 } 784 785 /* Look for the bucket in the current list */ 786 for ( bucket=&surfaces; bucket; bucket=bucket->next ) { 787 if ( bucket == (vidmem_bucket *)surface->hwdata ) { 788 break; 789 } 790 } 791 if ( bucket && bucket->used ) { 792 /* Add the memory back to the total */ 793 #ifdef DGA_DEBUG 794 printf("Freeing bucket of %d bytes\n", bucket->size); 795 #endif 796 surfaces_memleft += bucket->size; 797 798 /* Can we merge the space with surrounding buckets? */ 799 bucket->used = 0; 800 if ( bucket->next && ! bucket->next->used ) { 801 #ifdef DGA_DEBUG 802 printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size); 803 #endif 804 freeable = bucket->next; 805 bucket->size += bucket->next->size; 806 bucket->next = bucket->next->next; 807 if ( bucket->next ) { 808 bucket->next->prev = bucket; 809 } 810 SDL_free(freeable); 811 } 812 if ( bucket->prev && ! bucket->prev->used ) { 813 #ifdef DGA_DEBUG 814 printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size); 815 #endif 816 freeable = bucket; 817 bucket->prev->size += bucket->size; 818 bucket->prev->next = bucket->next; 819 if ( bucket->next ) { 820 bucket->next->prev = bucket->prev; 821 } 822 SDL_free(freeable); 823 } 824 } 825 surface->pixels = NULL; 826 surface->hwdata = NULL; 827 } 828 829 static __inline__ void DGA_dst_to_xy(_THIS, SDL_Surface *dst, int *x, int *y) 830 { 831 *x = (long)((Uint8 *)dst->pixels - memory_base)%memory_pitch; 832 *y = (long)((Uint8 *)dst->pixels - memory_base)/memory_pitch; 833 } 834 835 static int DGA_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color) 836 { 837 int x, y; 838 unsigned int w, h; 839 840 /* Don't fill the visible part of the screen, wait until flipped */ 841 LOCK_DISPLAY(); 842 if ( was_flipped && (dst == this->screen) ) { 843 while ( SDL_NAME(XDGAGetViewportStatus)(DGA_Display, DGA_Screen) ) 844 /* Keep waiting for the hardware ... */ ; 845 was_flipped = 0; 846 } 847 DGA_dst_to_xy(this, dst, &x, &y); 848 x += rect->x; 849 y += rect->y; 850 w = rect->w; 851 h = rect->h; 852 #if 0 853 printf("Hardware accelerated rectangle fill: %dx%d at %d,%d\n", w, h, x, y); 854 #endif 855 SDL_NAME(XDGAFillRectangle)(DGA_Display, DGA_Screen, x, y, w, h, color); 856 if ( !(this->screen->flags & SDL_DOUBLEBUF) ) { 857 XFlush(DGA_Display); 858 } 859 DGA_AddBusySurface(dst); 860 UNLOCK_DISPLAY(); 861 return(0); 862 } 863 864 static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect, 865 SDL_Surface *dst, SDL_Rect *dstrect) 866 { 867 SDL_VideoDevice *this; 868 int srcx, srcy; 869 int dstx, dsty; 870 unsigned int w, h; 871 872 this = current_video; 873 /* Don't blit to the visible part of the screen, wait until flipped */ 874 LOCK_DISPLAY(); 875 if ( was_flipped && (dst == this->screen) ) { 876 while ( SDL_NAME(XDGAGetViewportStatus)(DGA_Display, DGA_Screen) ) 877 /* Keep waiting for the hardware ... */ ; 878 was_flipped = 0; 879 } 880 DGA_dst_to_xy(this, src, &srcx, &srcy); 881 srcx += srcrect->x; 882 srcy += srcrect->y; 883 DGA_dst_to_xy(this, dst, &dstx, &dsty); 884 dstx += dstrect->x; 885 dsty += dstrect->y; 886 w = srcrect->w; 887 h = srcrect->h; 888 #if 0 889 printf("Blitting %dx%d from %d,%d to %d,%d\n", w, h, srcx, srcy, dstx, dsty); 890 #endif 891 if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { 892 SDL_NAME(XDGACopyTransparentArea)(DGA_Display, DGA_Screen, 893 srcx, srcy, w, h, dstx, dsty, src->format->colorkey); 894 } else { 895 SDL_NAME(XDGACopyArea)(DGA_Display, DGA_Screen, 896 srcx, srcy, w, h, dstx, dsty); 897 } 898 if ( !(this->screen->flags & SDL_DOUBLEBUF) ) { 899 XFlush(DGA_Display); 900 } 901 DGA_AddBusySurface(src); 902 DGA_AddBusySurface(dst); 903 UNLOCK_DISPLAY(); 904 return(0); 905 } 906 907 static int DGA_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst) 908 { 909 int accelerated; 910 911 /* Set initial acceleration on */ 912 src->flags |= SDL_HWACCEL; 913 914 /* Set the surface attributes */ 915 if ( (src->flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { 916 if ( ! this->info.blit_hw_A ) { 917 src->flags &= ~SDL_HWACCEL; 918 } 919 } 920 if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { 921 if ( ! this->info.blit_hw_CC ) { 922 src->flags &= ~SDL_HWACCEL; 923 } 924 } 925 926 /* Check to see if final surface blit is accelerated */ 927 accelerated = !!(src->flags & SDL_HWACCEL); 928 if ( accelerated ) { 929 src->map->hw_blit = HWAccelBlit; 930 } 931 return(accelerated); 932 } 933 934 static __inline__ void DGA_WaitFlip(_THIS) 935 { 936 if ( was_flipped ) { 937 while ( SDL_NAME(XDGAGetViewportStatus)(DGA_Display, DGA_Screen) ) 938 /* Keep waiting for the hardware ... */ ; 939 was_flipped = 0; 940 } 941 } 942 943 static int DGA_LockHWSurface(_THIS, SDL_Surface *surface) 944 { 945 if ( surface == this->screen ) { 946 SDL_mutexP(hw_lock); 947 LOCK_DISPLAY(); 948 if ( DGA_IsSurfaceBusy(surface) ) { 949 DGA_WaitBusySurfaces(this); 950 } 951 DGA_WaitFlip(this); 952 UNLOCK_DISPLAY(); 953 } else { 954 if ( DGA_IsSurfaceBusy(surface) ) { 955 LOCK_DISPLAY(); 956 DGA_WaitBusySurfaces(this); 957 UNLOCK_DISPLAY(); 958 } 959 } 960 return(0); 961 } 962 static void DGA_UnlockHWSurface(_THIS, SDL_Surface *surface) 963 { 964 if ( surface == this->screen ) { 965 SDL_mutexV(hw_lock); 966 } 967 } 968 969 static int DGA_FlipHWSurface(_THIS, SDL_Surface *surface) 970 { 971 /* Wait for vertical retrace and then flip display */ 972 LOCK_DISPLAY(); 973 if ( DGA_IsSurfaceBusy(this->screen) ) { 974 DGA_WaitBusySurfaces(this); 975 } 976 DGA_WaitFlip(this); 977 SDL_NAME(XDGASetViewport)(DGA_Display, DGA_Screen, 978 0, flip_yoffset[flip_page], XDGAFlipRetrace); 979 XFlush(DGA_Display); 980 UNLOCK_DISPLAY(); 981 was_flipped = 1; 982 flip_page = !flip_page; 983 984 surface->pixels = flip_address[flip_page]; 985 return(0); 986 } 987 988 static void DGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects) 989 { 990 /* The application is already updating the visible video memory */ 991 return; 992 } 993 994 static int DGA_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) 995 { 996 int i; 997 XColor *xcmap; 998 999 /* This happens on initialization */ 1000 if ( ! DGA_colormap ) { 1001 return(0); 1002 } 1003 xcmap = SDL_stack_alloc(XColor, ncolors); 1004 for ( i=0; i<ncolors; ++i ) { 1005 xcmap[i].pixel = firstcolor + i; 1006 xcmap[i].red = (colors[i].r<<8)|colors[i].r; 1007 xcmap[i].green = (colors[i].g<<8)|colors[i].g; 1008 xcmap[i].blue = (colors[i].b<<8)|colors[i].b; 1009 xcmap[i].flags = (DoRed|DoGreen|DoBlue); 1010 } 1011 LOCK_DISPLAY(); 1012 XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors); 1013 XSync(DGA_Display, False); 1014 UNLOCK_DISPLAY(); 1015 SDL_stack_free(xcmap); 1016 1017 /* That was easy. :) */ 1018 return(1); 1019 } 1020 1021 int DGA_SetGammaRamp(_THIS, Uint16 *ramp) 1022 { 1023 int i, ncolors; 1024 XColor xcmap[256]; 1025 1026 /* See if actually setting the gamma is supported */ 1027 if ( DGA_visualClass != DirectColor ) { 1028 SDL_SetError("Gamma correction not supported on this visual"); 1029 return(-1); 1030 } 1031 1032 /* Calculate the appropriate palette for the given gamma ramp */ 1033 if ( this->screen->format->BitsPerPixel <= 16 ) { 1034 ncolors = 64; /* Is this right? */ 1035 } else { 1036 ncolors = 256; 1037 } 1038 for ( i=0; i<ncolors; ++i ) { 1039 Uint8 c = (256 * i / ncolors); 1040 xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c); 1041 xcmap[i].red = ramp[0*256+c]; 1042 xcmap[i].green = ramp[1*256+c]; 1043 xcmap[i].blue = ramp[2*256+c]; 1044 xcmap[i].flags = (DoRed|DoGreen|DoBlue); 1045 } 1046 LOCK_DISPLAY(); 1047 XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors); 1048 XSync(DGA_Display, False); 1049 UNLOCK_DISPLAY(); 1050 return(0); 1051 } 1052 1053 void DGA_VideoQuit(_THIS) 1054 { 1055 int i, j; 1056 1057 if ( DGA_Display ) { 1058 /* Free colormap, if necessary */ 1059 if ( DGA_colormap ) { 1060 XFreeColormap(DGA_Display, DGA_colormap); 1061 DGA_colormap = 0; 1062 } 1063 1064 /* Unmap memory and reset video mode */ 1065 SDL_NAME(XDGACloseFramebuffer)(DGA_Display, DGA_Screen); 1066 if ( this->screen ) { 1067 /* Tell SDL not to free the pixels */ 1068 DGA_FreeHWSurface(this, this->screen); 1069 } 1070 SDL_NAME(XDGASetMode)(DGA_Display, DGA_Screen, 0); 1071 1072 /* Clear the lock mutex */ 1073 if ( hw_lock != NULL ) { 1074 SDL_DestroyMutex(hw_lock); 1075 hw_lock = NULL; 1076 } 1077 #ifdef LOCK_DGA_DISPLAY 1078 if ( event_lock != NULL ) { 1079 SDL_DestroyMutex(event_lock); 1080 event_lock = NULL; 1081 } 1082 #endif /* LOCK_DGA_DISPLAY */ 1083 1084 /* Clean up defined video modes */ 1085 for ( i=0; i<NUM_MODELISTS; ++i ) { 1086 if ( SDL_modelist[i] != NULL ) { 1087 for ( j=0; SDL_modelist[i][j]; ++j ) { 1088 SDL_free(SDL_modelist[i][j]); 1089 } 1090 SDL_free(SDL_modelist[i]); 1091 SDL_modelist[i] = NULL; 1092 } 1093 } 1094 1095 /* Clean up the memory bucket list */ 1096 DGA_FreeHWSurfaces(this); 1097 1098 /* Close up the display */ 1099 XCloseDisplay(DGA_Display); 1100 } 1101 } 1102