Home | History | Annotate | Download | only in dga
      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