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