src/video/dga/SDL_dgavideo.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 09 Nov 2002 06:20:46 +0000
changeset 540 4bcfb93e0dfe
parent 462 1be0cdaf8092
child 559 b528214c8c9a
permissions -rw-r--r--
Greatly improved X11 DGA video speed (thanks Cezary!)
     1 /*
     2 	SDL - Simple DirectMedia Layer
     3 	Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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@libsdl.org
    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 <XFree86/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 ( SDL_NAME(XDGAQueryExtension)(dpy, &events, &errors) &&
    91 			     SDL_NAME(XDGAQueryVersion)(dpy, &major, &minor) ) {
    92 				int screen;
    93 
    94 				screen = DefaultScreen(dpy);
    95 				if ( (major >= 2) && 
    96 				     SDL_NAME(XDGAOpenFramebuffer)(dpy, screen) ) {
    97 					available = 1;
    98 					SDL_NAME(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 *SDL_NAME(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)SDL_NAME(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(SDL_NAME(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 SDL_NAME(XDGAMode) *a = (const SDL_NAME(XDGAMode) *)va;
   282     const SDL_NAME(XDGAMode) *b = (const SDL_NAME(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, SDL_NAME(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 	SDL_NAME(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 ( ! SDL_NAME(XDGAQueryExtension)(DGA_Display, &event_base, &error_base) ||
   340 	     ! SDL_NAME(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 ( ! SDL_NAME(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 = SDL_NAME(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 	SDL_NAME(XDGAMode) *modes;
   433 	int i, num_modes;
   434 	SDL_NAME(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 = SDL_NAME(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 = SDL_NAME(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 = SDL_NAME(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 = SDL_NAME(XDGACreateColormap)(DGA_Display, DGA_Screen,
   511 							mode, AllocNone);
   512 	}
   513 	SDL_NAME(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 	SDL_NAME(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 		}
   535 	}
   536 
   537 	/* Allocate memory tracking for hardware surfaces */
   538 	DGA_FreeHWSurfaces(this);
   539 	if ( surfaces_len < 0 ) {
   540 		surfaces_len = 0;
   541 	}
   542 	DGA_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
   543 
   544 	/* Expose the back buffer as surface memory */
   545 	if ( current->flags & SDL_DOUBLEBUF ) {
   546 		this->screen = current;
   547 		DGA_FlipHWSurface(this, current);
   548 		this->screen = NULL;
   549 	}
   550 
   551 	/* Set the update rectangle function */
   552 	this->UpdateRects = DGA_DirectUpdate;
   553 
   554 	/* Enable mouse and keyboard support */
   555 	{ long input_mask;
   556 	  input_mask = (KeyPressMask | KeyReleaseMask);
   557 	  input_mask |= (ButtonPressMask | ButtonReleaseMask);
   558 	  input_mask |= PointerMotionMask;
   559 	  SDL_NAME(XDGASelectInput)(DGA_Display, DGA_Screen, input_mask);
   560 	}
   561 
   562 	/* We're done */
   563 	return(current);
   564 }
   565 
   566 #ifdef DGA_DEBUG
   567 static void DGA_DumpHWSurfaces(_THIS)
   568 {
   569 	vidmem_bucket *bucket;
   570 
   571 	printf("Memory left: %d (%d total)\n", surfaces_memleft, surfaces_memtotal);
   572 	printf("\n");
   573 	printf("         Base  Size\n");
   574 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   575 		printf("Bucket:  %p, %d (%s)\n", bucket->base, bucket->size, bucket->used ? "used" : "free");
   576 		if ( bucket->prev ) {
   577 			if ( bucket->base != bucket->prev->base+bucket->prev->size ) {
   578 				printf("Warning, corrupt bucket list! (prev)\n");
   579 			}
   580 		} else {
   581 			if ( bucket != &surfaces ) {
   582 				printf("Warning, corrupt bucket list! (!prev)\n");
   583 			}
   584 		}
   585 		if ( bucket->next ) {
   586 			if ( bucket->next->base != bucket->base+bucket->size ) {
   587 				printf("Warning, corrupt bucket list! (next)\n");
   588 			}
   589 		}
   590 	}
   591 	printf("\n");
   592 }
   593 #endif
   594 
   595 static int DGA_InitHWSurfaces(_THIS, SDL_Surface *screen, Uint8 *base, int size)
   596 {
   597 	vidmem_bucket *bucket;
   598 
   599 	surfaces_memtotal = size;
   600 	surfaces_memleft = size;
   601 
   602 	if ( surfaces_memleft > 0 ) {
   603 		bucket = (vidmem_bucket *)malloc(sizeof(*bucket));
   604 		if ( bucket == NULL ) {
   605 			SDL_OutOfMemory();
   606 			return(-1);
   607 		}
   608 		bucket->prev = &surfaces;
   609 		bucket->used = 0;
   610 		bucket->dirty = 0;
   611 		bucket->base = base;
   612 		bucket->size = size;
   613 		bucket->next = NULL;
   614 	} else {
   615 		bucket = NULL;
   616 	}
   617 
   618 	surfaces.prev = NULL;
   619 	surfaces.used = 1;
   620 	surfaces.dirty = 0;
   621 	surfaces.base = screen->pixels;
   622 	surfaces.size = (unsigned int)((long)base - (long)surfaces.base);
   623 	surfaces.next = bucket;
   624 	screen->hwdata = (struct private_hwdata *)&surfaces;
   625 	return(0);
   626 }
   627 static void DGA_FreeHWSurfaces(_THIS)
   628 {
   629 	vidmem_bucket *bucket, *freeable;
   630 
   631 	bucket = surfaces.next;
   632 	while ( bucket ) {
   633 		freeable = bucket;
   634 		bucket = bucket->next;
   635 		free(freeable);
   636 	}
   637 	surfaces.next = NULL;
   638 }
   639 
   640 static __inline__ void DGA_AddBusySurface(SDL_Surface *surface)
   641 {
   642 	((vidmem_bucket *)surface->hwdata)->dirty = 1;
   643 }
   644 
   645 static __inline__ int DGA_IsSurfaceBusy(SDL_Surface *surface)
   646 {
   647 	return ((vidmem_bucket *)surface->hwdata)->dirty;
   648 }
   649 
   650 static __inline__ void DGA_WaitBusySurfaces(_THIS)
   651 {
   652 	vidmem_bucket *bucket;
   653 
   654 	/* Wait for graphic operations to complete */
   655 	SDL_NAME(XDGASync)(DGA_Display, DGA_Screen);
   656 
   657 	/* Clear all surface dirty bits */
   658 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   659 		bucket->dirty = 0;
   660 	}
   661 }
   662 
   663 static int DGA_AllocHWSurface(_THIS, SDL_Surface *surface)
   664 {
   665 	vidmem_bucket *bucket;
   666 	int size;
   667 	int extra;
   668 	int retval = 0;
   669 
   670 /* Temporarily, we only allow surfaces the same width as display.
   671    Some blitters require the pitch between two hardware surfaces
   672    to be the same.  Others have interesting alignment restrictions.
   673 */
   674 if ( surface->pitch > SDL_VideoSurface->pitch ) {
   675 	SDL_SetError("Surface requested wider than screen");
   676 	return(-1);
   677 }
   678 surface->pitch = SDL_VideoSurface->pitch;
   679 	size = surface->h * surface->pitch;
   680 #ifdef DGA_DEBUG
   681 	fprintf(stderr, "Allocating bucket of %d bytes\n", size);
   682 #endif
   683 	LOCK_DISPLAY();
   684 
   685 	/* Quick check for available mem */
   686 	if ( size > surfaces_memleft ) {
   687 		SDL_SetError("Not enough video memory");
   688 		retval = -1;
   689 		goto done;
   690 	}
   691 
   692 	/* Search for an empty bucket big enough */
   693 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   694 		if ( ! bucket->used && (size <= bucket->size) ) {
   695 			break;
   696 		}
   697 	}
   698 	if ( bucket == NULL ) {
   699 		SDL_SetError("Video memory too fragmented");
   700 		retval = -1;
   701 		goto done;
   702 	}
   703 
   704 	/* Create a new bucket for left-over memory */
   705 	extra = (bucket->size - size);
   706 	if ( extra ) {
   707 		vidmem_bucket *newbucket;
   708 
   709 #ifdef DGA_DEBUG
   710 	fprintf(stderr, "Adding new free bucket of %d bytes\n", extra);
   711 #endif
   712 		newbucket = (vidmem_bucket *)malloc(sizeof(*newbucket));
   713 		if ( newbucket == NULL ) {
   714 			SDL_OutOfMemory();
   715 			retval = -1;
   716 			goto done;
   717 		}
   718 		newbucket->prev = bucket;
   719 		newbucket->used = 0;
   720 		newbucket->base = bucket->base+size;
   721 		newbucket->size = extra;
   722 		newbucket->next = bucket->next;
   723 		if ( bucket->next ) {
   724 			bucket->next->prev = newbucket;
   725 		}
   726 		bucket->next = newbucket;
   727 	}
   728 
   729 	/* Set the current bucket values and return it! */
   730 	bucket->used = 1;
   731 	bucket->size = size;
   732 	bucket->dirty = 0;
   733 #ifdef DGA_DEBUG
   734 	fprintf(stderr, "Allocated %d bytes at %p\n", bucket->size, bucket->base);
   735 #endif
   736 	surfaces_memleft -= size;
   737 	surface->flags |= SDL_HWSURFACE;
   738 	surface->pixels = bucket->base;
   739 	surface->hwdata = (struct private_hwdata *)bucket;
   740 done:
   741 	UNLOCK_DISPLAY();
   742 	return(retval);
   743 }
   744 static void DGA_FreeHWSurface(_THIS, SDL_Surface *surface)
   745 {
   746 	vidmem_bucket *bucket, *freeable;
   747 
   748 	/* Look for the bucket in the current list */
   749 	for ( bucket=&surfaces; bucket; bucket=bucket->next ) {
   750 		if ( bucket == (vidmem_bucket *)surface->hwdata ) {
   751 			break;
   752 		}
   753 	}
   754 	if ( bucket && bucket->used ) {
   755 		/* Add the memory back to the total */
   756 #ifdef DGA_DEBUG
   757 	printf("Freeing bucket of %d bytes\n", bucket->size);
   758 #endif
   759 		surfaces_memleft += bucket->size;
   760 
   761 		/* Can we merge the space with surrounding buckets? */
   762 		bucket->used = 0;
   763 		if ( bucket->next && ! bucket->next->used ) {
   764 #ifdef DGA_DEBUG
   765 	printf("Merging with next bucket, for %d total bytes\n", bucket->size+bucket->next->size);
   766 #endif
   767 			freeable = bucket->next;
   768 			bucket->size += bucket->next->size;
   769 			bucket->next = bucket->next->next;
   770 			if ( bucket->next ) {
   771 				bucket->next->prev = bucket;
   772 			}
   773 			free(freeable);
   774 		}
   775 		if ( bucket->prev && ! bucket->prev->used ) {
   776 #ifdef DGA_DEBUG
   777 	printf("Merging with previous bucket, for %d total bytes\n", bucket->prev->size+bucket->size);
   778 #endif
   779 			freeable = bucket;
   780 			bucket->prev->size += bucket->size;
   781 			bucket->prev->next = bucket->next;
   782 			if ( bucket->next ) {
   783 				bucket->next->prev = bucket->prev;
   784 			}
   785 			free(freeable);
   786 		}
   787 	}
   788 	surface->pixels = NULL;
   789 	surface->hwdata = NULL;
   790 }
   791 
   792 static __inline__ void DGA_dst_to_xy(_THIS, SDL_Surface *dst, int *x, int *y)
   793 {
   794 	*x = (long)((Uint8 *)dst->pixels - memory_base)%memory_pitch;
   795 	*y = (long)((Uint8 *)dst->pixels - memory_base)/memory_pitch;
   796 }
   797 
   798 static int DGA_FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color)
   799 {
   800 	int x, y;
   801 	unsigned int w, h;
   802 
   803 	/* Don't fill the visible part of the screen, wait until flipped */
   804 	LOCK_DISPLAY();
   805 	if ( was_flipped && (dst == this->screen) ) {
   806 		while ( SDL_NAME(XDGAGetViewportStatus)(DGA_Display, DGA_Screen) )
   807 			/* Keep waiting for the hardware ... */ ;
   808 		was_flipped = 0;
   809 	}
   810 	DGA_dst_to_xy(this, dst, &x, &y);
   811 	x += rect->x;
   812 	y += rect->y;
   813 	w = rect->w;
   814 	h = rect->h;
   815 #if 0
   816   printf("Hardware accelerated rectangle fill: %dx%d at %d,%d\n", w, h, x, y);
   817 #endif
   818 	SDL_NAME(XDGAFillRectangle)(DGA_Display, DGA_Screen, x, y, w, h, color);
   819 	if ( !(this->screen->flags & SDL_DOUBLEBUF) ) {
   820 		XFlush(DGA_Display);
   821 	}
   822 	DGA_AddBusySurface(dst);
   823 	UNLOCK_DISPLAY();
   824 	return(0);
   825 }
   826 
   827 static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect,
   828                        SDL_Surface *dst, SDL_Rect *dstrect)
   829 {
   830 	SDL_VideoDevice *this;
   831 	int srcx, srcy;
   832 	int dstx, dsty;
   833 	unsigned int w, h;
   834 
   835 	this = current_video;
   836 	/* Don't blit to 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, src, &srcx, &srcy);
   844 	srcx += srcrect->x;
   845 	srcy += srcrect->y;
   846 	DGA_dst_to_xy(this, dst, &dstx, &dsty);
   847 	dstx += dstrect->x;
   848 	dsty += dstrect->y;
   849 	w = srcrect->w;
   850 	h = srcrect->h;
   851 #if 0
   852   printf("Blitting %dx%d from %d,%d to %d,%d\n", w, h, srcx, srcy, dstx, dsty);
   853 #endif
   854 	if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
   855 		SDL_NAME(XDGACopyTransparentArea)(DGA_Display, DGA_Screen,
   856 			srcx, srcy, w, h, dstx, dsty, src->format->colorkey);
   857 	} else {
   858 		SDL_NAME(XDGACopyArea)(DGA_Display, DGA_Screen,
   859 			srcx, srcy, w, h, dstx, dsty);
   860 	}
   861 	if ( !(this->screen->flags & SDL_DOUBLEBUF) ) {
   862 		XFlush(DGA_Display);
   863 	}
   864 	DGA_AddBusySurface(src);
   865 	DGA_AddBusySurface(dst);
   866 	UNLOCK_DISPLAY();
   867 	return(0);
   868 }
   869 
   870 static int DGA_CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst)
   871 {
   872 	int accelerated;
   873 
   874 	/* Set initial acceleration on */
   875 	src->flags |= SDL_HWACCEL;
   876 
   877 	/* Set the surface attributes */
   878 	if ( (src->flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
   879 		if ( ! this->info.blit_hw_A ) {
   880 			src->flags &= ~SDL_HWACCEL;
   881 		}
   882 	}
   883 	if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
   884 		if ( ! this->info.blit_hw_CC ) {
   885 			src->flags &= ~SDL_HWACCEL;
   886 		}
   887 	}
   888 
   889 	/* Check to see if final surface blit is accelerated */
   890 	accelerated = !!(src->flags & SDL_HWACCEL);
   891 	if ( accelerated ) {
   892 		src->map->hw_blit = HWAccelBlit;
   893 	}
   894 	return(accelerated);
   895 }
   896 
   897 static __inline__ void DGA_WaitFlip(_THIS)
   898 {
   899 	if ( was_flipped ) {
   900 		while ( SDL_NAME(XDGAGetViewportStatus)(DGA_Display, DGA_Screen) )
   901 			/* Keep waiting for the hardware ... */ ;
   902 		was_flipped = 0;
   903 	}
   904 }
   905 
   906 static int DGA_LockHWSurface(_THIS, SDL_Surface *surface)
   907 {
   908 	if ( surface == this->screen ) {
   909 		SDL_mutexP(hw_lock);
   910 		LOCK_DISPLAY();
   911 		if ( DGA_IsSurfaceBusy(surface) ) {
   912 			DGA_WaitBusySurfaces(this);
   913 		}
   914 		DGA_WaitFlip(this);
   915 		UNLOCK_DISPLAY();
   916 	} else {
   917 		if ( DGA_IsSurfaceBusy(surface) ) {
   918 			LOCK_DISPLAY();
   919 			DGA_WaitBusySurfaces(this);
   920 			UNLOCK_DISPLAY();
   921 		}
   922 	}
   923 	return(0);
   924 }
   925 static void DGA_UnlockHWSurface(_THIS, SDL_Surface *surface)
   926 {
   927 	if ( surface == this->screen ) {
   928 		SDL_mutexV(hw_lock);
   929 	}
   930 }
   931 
   932 static int DGA_FlipHWSurface(_THIS, SDL_Surface *surface)
   933 {
   934 	/* Wait for vertical retrace and then flip display */
   935 	LOCK_DISPLAY();
   936 	if ( DGA_IsSurfaceBusy(this->screen) ) {
   937 		DGA_WaitBusySurfaces(this);
   938 	}
   939 	DGA_WaitFlip(this);
   940 	SDL_NAME(XDGASetViewport)(DGA_Display, DGA_Screen,
   941 	                0, flip_yoffset[flip_page], XDGAFlipRetrace);
   942 	XFlush(DGA_Display);
   943 	UNLOCK_DISPLAY();
   944 	was_flipped = 1;
   945 	flip_page = !flip_page;
   946 
   947 	surface->pixels = flip_address[flip_page];
   948 	return(0);
   949 }
   950 
   951 static void DGA_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
   952 {
   953 	/* The application is already updating the visible video memory */
   954 	return;
   955 }
   956 
   957 static int DGA_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
   958 {
   959         int i;
   960 	XColor  *xcmap;
   961 
   962 	/* This happens on initialization */
   963 	if ( ! DGA_colormap ) {
   964 		return(0);
   965 	}
   966 	xcmap = (XColor *)alloca(ncolors*sizeof(*xcmap));
   967 	for ( i=0; i<ncolors; ++i ) {
   968 		xcmap[i].pixel = firstcolor + i;
   969 		xcmap[i].red   = (colors[i].r<<8)|colors[i].r;
   970 		xcmap[i].green = (colors[i].g<<8)|colors[i].g;
   971 		xcmap[i].blue  = (colors[i].b<<8)|colors[i].b;
   972 		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
   973 	}
   974 	LOCK_DISPLAY();
   975 	XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors);
   976 	XSync(DGA_Display, False);
   977 	UNLOCK_DISPLAY();
   978 
   979 	/* That was easy. :) */
   980 	return(1);
   981 }
   982 
   983 int DGA_SetGammaRamp(_THIS, Uint16 *ramp)
   984 {
   985 	int i, ncolors;
   986 	XColor xcmap[256];
   987 
   988 	/* See if actually setting the gamma is supported */
   989 	if ( DGA_visualClass != DirectColor ) {
   990 	    SDL_SetError("Gamma correction not supported on this visual");
   991 	    return(-1);
   992 	}
   993 
   994 	/* Calculate the appropriate palette for the given gamma ramp */
   995 	if ( this->screen->format->BitsPerPixel <= 16 ) {
   996 		ncolors = 64; /* Is this right? */
   997 	} else {
   998 		ncolors = 256;
   999 	}
  1000 	for ( i=0; i<ncolors; ++i ) {
  1001 		Uint8 c = (256 * i / ncolors);
  1002 		xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
  1003 		xcmap[i].red   = ramp[0*256+c];
  1004 		xcmap[i].green = ramp[1*256+c];
  1005 		xcmap[i].blue  = ramp[2*256+c];
  1006 		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
  1007 	}
  1008 	LOCK_DISPLAY();
  1009 	XStoreColors(DGA_Display, DGA_colormap, xcmap, ncolors);
  1010 	XSync(DGA_Display, False);
  1011 	UNLOCK_DISPLAY();
  1012 	return(0);
  1013 }
  1014 
  1015 void DGA_VideoQuit(_THIS)
  1016 {
  1017 	int i, j;
  1018 
  1019 	if ( DGA_Display ) {
  1020 		/* Free colormap, if necessary */
  1021 		if ( DGA_colormap ) {
  1022 			XFreeColormap(DGA_Display, DGA_colormap);
  1023 			DGA_colormap = 0;
  1024 		}
  1025 
  1026 		/* Unmap memory and reset video mode */
  1027 		SDL_NAME(XDGACloseFramebuffer)(DGA_Display, DGA_Screen);
  1028 		if ( this->screen ) {
  1029 			/* Tell SDL not to free the pixels */
  1030 			this->screen->pixels = NULL;
  1031 		}
  1032 		SDL_NAME(XDGASetMode)(DGA_Display, DGA_Screen, 0);
  1033 
  1034 		/* Clear the lock mutex */
  1035 		if ( hw_lock != NULL ) {
  1036 			SDL_DestroyMutex(hw_lock);
  1037 			hw_lock = NULL;
  1038 		}
  1039 #ifdef LOCK_DGA_DISPLAY
  1040 		if ( event_lock != NULL ) {
  1041 			SDL_DestroyMutex(event_lock);
  1042 			event_lock = NULL;
  1043 		}
  1044 #endif /* LOCK_DGA_DISPLAY */
  1045 
  1046 
  1047 		/* Clean up defined video modes */
  1048 		for ( i=0; i<NUM_MODELISTS; ++i ) {
  1049 			if ( SDL_modelist[i] != NULL ) {
  1050 				for ( j=0; SDL_modelist[i][j]; ++j ) {
  1051 					free(SDL_modelist[i][j]);
  1052 				}
  1053 				free(SDL_modelist[i]);
  1054 				SDL_modelist[i] = NULL;
  1055 			}
  1056 		}
  1057 
  1058 		/* Clean up the memory bucket list */
  1059 		DGA_FreeHWSurfaces(this);
  1060 
  1061 		/* Close up the display */
  1062 		XCloseDisplay(DGA_Display);
  1063 	}
  1064 }