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