src/video/x11/SDL_x11image.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Dec 2001 12:38:15 +0000
changeset 252 e8157fcb3114
parent 166 39877400bd1e
child 270 37fa1484f71b
permissions -rw-r--r--
Updated the source with the correct e-mail address
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include <stdlib.h>
    29 #include <unistd.h>
    30 
    31 #include "SDL_error.h"
    32 #include "SDL_endian.h"
    33 #include "SDL_events_c.h"
    34 #include "SDL_x11image_c.h"
    35 
    36 #ifndef NO_SHARED_MEMORY
    37 
    38 /* Shared memory information */
    39 extern int XShmQueryExtension(Display *dpy);	/* Not in X11 headers */
    40 
    41 /* Shared memory error handler routine */
    42 static int shm_error;
    43 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
    44 static int shm_errhandler(Display *d, XErrorEvent *e)
    45 {
    46         if ( e->error_code == BadAccess ) {
    47         	++shm_error;
    48         	return(0);
    49         } else
    50 		return(X_handler(d,e));
    51 }
    52 #endif /* ! NO_SHARED_MEMORY */
    53 
    54 /* Various screen update functions available */
    55 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
    56 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
    57 
    58 int X11_SetupImage(_THIS, SDL_Surface *screen)
    59 {
    60 #ifdef NO_SHARED_MEMORY
    61 	screen->pixels = malloc(screen->h*screen->pitch);
    62 #else
    63 	/* Allocate shared memory if possible */
    64 	if ( use_mitshm ) {
    65 		shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
    66 								IPC_CREAT|0777);
    67 		if ( shminfo.shmid >= 0 ) {
    68 			shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
    69 			shminfo.readOnly = False;
    70 			if ( shminfo.shmaddr != (char *)-1 ) {
    71 				shm_error = False;
    72 				X_handler = XSetErrorHandler(shm_errhandler);
    73 				XShmAttach(SDL_Display, &shminfo);
    74 				XSync(SDL_Display, True);
    75 				XSetErrorHandler(X_handler);
    76 				if ( shm_error == True )
    77 					shmdt(shminfo.shmaddr);
    78 			} else {
    79 				shm_error = True;
    80 			}
    81 			shmctl(shminfo.shmid, IPC_RMID, NULL);
    82 		} else {
    83 			shm_error = True;
    84 		}
    85 		if ( shm_error == True )
    86 			use_mitshm = 0;
    87 	}
    88 	if ( use_mitshm ) {
    89 		screen->pixels = shminfo.shmaddr;
    90 	} else {
    91 		screen->pixels = malloc(screen->h*screen->pitch);
    92 	}
    93 #endif /* NO_SHARED_MEMORY */
    94 	if ( screen->pixels == NULL ) {
    95 		SDL_OutOfMemory();
    96 		return(-1);
    97 	}
    98 
    99 #ifdef NO_SHARED_MEMORY
   100 	{
   101  	        int bpp = screen->format->BytesPerPixel;
   102 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   103 					  this->hidden->depth, ZPixmap, 0,
   104 					  (char *)screen->pixels, 
   105 					  screen->w, screen->h,
   106 					  (bpp == 3) ? 32 : bpp * 8,
   107 					  0);
   108 	}
   109 #else
   110 	if ( use_mitshm ) {
   111 		SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
   112 					     this->hidden->depth, ZPixmap,
   113 					     shminfo.shmaddr, &shminfo, 
   114 					     screen->w, screen->h);
   115 	} else {
   116  	        int bpp = screen->format->BytesPerPixel;
   117 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   118 					  this->hidden->depth, ZPixmap, 0,
   119 					  (char *)screen->pixels, 
   120 					  screen->w, screen->h,
   121 					  (bpp == 3) ? 32 : bpp * 8,
   122 					  0);
   123 	}
   124 #endif /* NO_SHARED_MEMORY */
   125 	if ( SDL_Ximage == NULL ) {
   126 		SDL_SetError("Couldn't create XImage");
   127 #ifndef NO_SHARED_MEMORY
   128 		if ( use_mitshm ) {
   129 			XShmDetach(SDL_Display, &shminfo);
   130 			XSync(SDL_Display, False);
   131 			shmdt(shminfo.shmaddr);
   132 			screen->pixels = NULL;
   133 		}
   134 #endif /* ! NO_SHARED_MEMORY */
   135 		return(-1);
   136 	}
   137 	screen->pitch = SDL_Ximage->bytes_per_line;
   138 
   139 	/* Determine what blit function to use */
   140 #ifdef NO_SHARED_MEMORY
   141 	this->UpdateRects = X11_NormalUpdate;
   142 #else
   143 	if ( use_mitshm ) {
   144 		this->UpdateRects = X11_MITSHMUpdate;
   145 	} else {
   146 		this->UpdateRects = X11_NormalUpdate;
   147 	}
   148 #endif
   149 	return(0);
   150 }
   151 
   152 void X11_DestroyImage(_THIS, SDL_Surface *screen)
   153 {
   154 	if ( SDL_Ximage ) {
   155 		XDestroyImage(SDL_Ximage);
   156 #ifndef NO_SHARED_MEMORY
   157 		if ( use_mitshm ) {
   158 			XShmDetach(SDL_Display, &shminfo);
   159 			XSync(SDL_Display, False);
   160 			shmdt(shminfo.shmaddr);
   161 		}
   162 #endif /* ! NO_SHARED_MEMORY */
   163 		SDL_Ximage = NULL;
   164 	}
   165 	if ( screen ) {
   166 		screen->pixels = NULL;
   167 	}
   168 }
   169 
   170 /* Determine the number of CPUs in the system */
   171 static int num_CPU(void)
   172 {
   173        static int num_cpus = 0;
   174 
   175        if(!num_cpus) {
   176 #if defined(__linux)
   177            char line[BUFSIZ];
   178            FILE *pstat = fopen("/proc/stat", "r");
   179            if ( pstat ) {
   180                while ( fgets(line, sizeof(line), pstat) ) {
   181                    if (memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
   182                        ++num_cpus;
   183                    }
   184                }
   185                fclose(pstat);
   186            }
   187 #elif defined(_SC_NPROCESSORS_ONLN)
   188 	   /* number of processors online (SVR4.0MP compliant machines) */
   189            num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
   190 #elif defined(_SC_NPROCESSORS_CONF)
   191 	   /* number of processors configured (SVR4.0MP compliant machines) */
   192            num_cpus = sysconf(_SC_NPROCESSORS_CONF);
   193 #endif
   194            if ( num_cpus <= 0 ) {
   195                num_cpus = 1;
   196            }
   197        }
   198        return num_cpus;
   199 }
   200 
   201 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
   202 {
   203 	int retval;
   204 
   205 	X11_DestroyImage(this, screen);
   206         if ( flags & SDL_OPENGL ) {  /* No image when using GL */
   207         	retval = 0;
   208         } else {
   209 		retval = X11_SetupImage(this, screen);
   210 		/* We support asynchronous blitting on the display */
   211 		if ( flags & SDL_ASYNCBLIT ) {
   212 			/* This is actually slower on single-CPU systems,
   213 			   probably because of CPU contention between the
   214 			   X server and the application.
   215 			   Note: Is this still true with XFree86 4.0?
   216 			*/
   217 			if ( num_CPU() > 1 ) {
   218 				screen->flags |= SDL_ASYNCBLIT;
   219 			}
   220 		}
   221 	}
   222 	return(retval);
   223 }
   224 
   225 /* We don't actually allow hardware surfaces other than the main one */
   226 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
   227 {
   228 	return(-1);
   229 }
   230 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
   231 {
   232 	return;
   233 }
   234 
   235 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
   236 {
   237 	if ( (surface == SDL_VideoSurface) && blit_queued ) {
   238 		XSync(GFX_Display, False);
   239 		blit_queued = 0;
   240 	}
   241 	return(0);
   242 }
   243 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
   244 {
   245 	return;
   246 }
   247 
   248 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
   249 {
   250 	return(0);
   251 }
   252 
   253 /* Byte-swap the pixels in the display image */
   254 static void X11_SwapAllPixels(SDL_Surface *screen)
   255 {
   256 	int x, y;
   257 
   258 	switch (screen->format->BytesPerPixel) {
   259 	    case 2: {
   260 		Uint16 *spot;
   261 		for ( y=0; y<screen->h; ++y ) {
   262 			spot = (Uint16 *) ((Uint8 *)screen->pixels +
   263 						y * screen->pitch);
   264 			for ( x=0; x<screen->w; ++x, ++spot ) {
   265 				*spot = SDL_Swap16(*spot);
   266 			}
   267 		}
   268 	    }
   269 	    break;
   270 
   271 	    case 4: {
   272 		Uint32 *spot;
   273 		for ( y=0; y<screen->h; ++y ) {
   274 			spot = (Uint32 *) ((Uint8 *)screen->pixels +
   275 						y * screen->pitch);
   276 			for ( x=0; x<screen->w; ++x, ++spot ) {
   277 				*spot = SDL_Swap32(*spot);
   278 			}
   279 		}
   280 	    }
   281 	    break;
   282 
   283 	    default:
   284 		/* should never get here */
   285 		break;
   286 	}
   287 }
   288 static void X11_SwapPixels(SDL_Surface *screen, SDL_Rect *rect)
   289 {
   290 	int x, minx, maxx;
   291 	int y, miny, maxy;
   292 
   293 	switch (screen->format->BytesPerPixel) {
   294 	    case 2: {
   295 		Uint16 *spot;
   296 		minx = rect->x;
   297 		maxx = rect->x + rect->w;
   298 		miny = rect->y;
   299 		maxy = rect->y + rect->h;
   300 		for ( y=miny; y<maxy; ++y ) {
   301 		    spot = (Uint16 *) ((Uint8 *)screen->pixels +
   302 				       y * screen->pitch + minx * 2);
   303 		    for ( x=minx; x<maxx; ++x, ++spot ) {
   304 			*spot = SDL_Swap16(*spot);
   305 		    }
   306 		}
   307 	    }
   308 	    break;
   309 
   310 	    case 4: {
   311 		Uint32 *spot;
   312 		minx = rect->x;
   313 		maxx = rect->x + rect->w;
   314 		miny = rect->y;
   315 		maxy = rect->y + rect->h;
   316 		for ( y=miny; y<maxy; ++y ) {
   317 		    spot = (Uint32 *) ((Uint8 *)screen->pixels +
   318 				       y * screen->pitch + minx * 4);
   319 		    for ( x=minx; x<maxx; ++x, ++spot ) {
   320 			*spot = SDL_Swap32(*spot);
   321 		    }
   322 		}
   323 	    }
   324 	    break;
   325 
   326 	    default:
   327 		/* should never get here */
   328 		break;
   329 	}
   330 }
   331 
   332 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
   333 {
   334 	int i;
   335 
   336 	/* Check for endian-swapped X server, swap if necessary (VERY slow!) */
   337 	if ( swap_pixels &&
   338 	     ((this->screen->format->BytesPerPixel%2) == 0) ) {
   339 		for ( i=0; i<numrects; ++i ) {
   340 			if ( ! rects[i].w ) { /* Clipped? */
   341 				continue;
   342 			}
   343 		        X11_SwapPixels(this->screen, rects + i);
   344 			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   345 				rects[i].x, rects[i].y,
   346 				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   347 			X11_SwapPixels(this->screen, rects + i);
   348 		}
   349 	} else {
   350 		for ( i=0; i<numrects; ++i ) {
   351 			if ( ! rects[i].w ) { /* Clipped? */
   352 				continue;
   353 			}
   354 			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   355 				rects[i].x, rects[i].y,
   356 				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   357 		}
   358 	}
   359 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   360 		XFlush(GFX_Display);
   361 		++blit_queued;
   362 	} else {
   363 		XSync(GFX_Display, False);
   364 	}
   365 }
   366 
   367 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
   368 {
   369 #ifndef NO_SHARED_MEMORY
   370 	int i;
   371 
   372 	for ( i=0; i<numrects; ++i ) {
   373 		if ( ! rects[i].w ) { /* Clipped? */
   374 			continue;
   375 		}
   376 		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   377 				rects[i].x, rects[i].y,
   378 				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
   379 									False);
   380 	}
   381 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   382 		XFlush(GFX_Display);
   383 		++blit_queued;
   384 	} else {
   385 		XSync(GFX_Display, False);
   386 	}
   387 #endif /* ! NO_SHARED_MEMORY */
   388 }
   389 
   390 /* There's a problem with the automatic refreshing of the display.
   391    Even though the XVideo code uses the GFX_Display to update the
   392    video memory, it appears that updating the window asynchronously
   393    from a different thread will cause "blackouts" of the window.
   394    This is a sort of a hacked workaround for the problem.
   395 */
   396 static int enable_autorefresh = 1;
   397 
   398 void X11_DisableAutoRefresh(_THIS)
   399 {
   400 	--enable_autorefresh;
   401 }
   402 
   403 void X11_EnableAutoRefresh(_THIS)
   404 {
   405 	++enable_autorefresh;
   406 }
   407 
   408 void X11_RefreshDisplay(_THIS)
   409 {
   410 	/* Don't refresh a display that doesn't have an image (like GL)
   411 	   Instead, post an expose event so the application can refresh.
   412 	 */
   413 	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
   414 		SDL_PrivateExpose();
   415 		return;
   416 	}
   417 #ifndef NO_SHARED_MEMORY
   418 	if ( this->UpdateRects == X11_MITSHMUpdate ) {
   419 		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   420 				0, 0, 0, 0, this->screen->w, this->screen->h,
   421 				False);
   422 	} else {
   423 #else
   424 	{
   425 #endif /* ! NO_SHARED_MEMORY */
   426 		/* Check for endian-swapped X server, swap if necessary */
   427 		if ( swap_pixels &&
   428 		     ((this->screen->format->BytesPerPixel%2) == 0) ) {
   429 			X11_SwapAllPixels(this->screen);
   430 			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   431 				0, 0, 0, 0, this->screen->w, this->screen->h);
   432 			X11_SwapAllPixels(this->screen);
   433 		} else {
   434 			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   435 				0, 0, 0, 0, this->screen->w, this->screen->h);
   436 		}
   437 	}
   438 	XSync(SDL_Display, False);
   439 }