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