src/video/x11/SDL_x11image.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 18 Jan 2002 22:02:03 +0000
changeset 270 37fa1484f71b
parent 252 e8157fcb3114
child 297 f6ffac90895c
permissions -rw-r--r--
From: "Mattias Engdeg�rd" <f91-men@nada.kth.se>
To: slouken@devolution.com
Subject: Re: [SDL] Question about SDL_FillRect()

I benchmarked with and without clipping UpdateRects and was unable to find
any difference on my moderately slow machine. Anyway, I haven't added
clipping in this patch, but fixed a couple of bugs and generally cleaned
up some of the X11 image code. Most importantly, UpdateRects now checks
for both zero height and width. Also, I eliminated the entire code to
byteswap X11 images since X11 can do that automatically if you ask it
nicely :-)
     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 = 1;
    48         	return(0);
    49         } else
    50 		return(X_handler(d,e));
    51 }
    52 
    53 static void try_mitshm(_THIS, SDL_Surface *screen)
    54 {
    55 	if(!use_mitshm)
    56 		return;
    57 	shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
    58 			       IPC_CREAT | 0777);
    59 	if ( shminfo.shmid >= 0 ) {
    60 		shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
    61 		shminfo.readOnly = False;
    62 		if ( shminfo.shmaddr != (char *)-1 ) {
    63 			shm_error = False;
    64 			X_handler = XSetErrorHandler(shm_errhandler);
    65 			XShmAttach(SDL_Display, &shminfo);
    66 			XSync(SDL_Display, True);
    67 			XSetErrorHandler(X_handler);
    68 			if (shm_error)
    69 				shmdt(shminfo.shmaddr);
    70 		} else {
    71 			shm_error = True;
    72 		}
    73 		shmctl(shminfo.shmid, IPC_RMID, NULL);
    74 	} else {
    75 		shm_error = True;
    76 	}
    77 	if ( shm_error )
    78 		use_mitshm = 0;
    79 	if ( use_mitshm )
    80 		screen->pixels = shminfo.shmaddr;
    81 }
    82 #endif /* ! NO_SHARED_MEMORY */
    83 
    84 /* Various screen update functions available */
    85 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
    86 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
    87 
    88 int X11_SetupImage(_THIS, SDL_Surface *screen)
    89 {
    90 #ifndef NO_SHARED_MEMORY
    91 	try_mitshm(this, screen);
    92 	if(use_mitshm) {
    93 		SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
    94 					     this->hidden->depth, ZPixmap,
    95 					     shminfo.shmaddr, &shminfo, 
    96 					     screen->w, screen->h);
    97 		if(!SDL_Ximage) {
    98 			XShmDetach(SDL_Display, &shminfo);
    99 			XSync(SDL_Display, False);
   100 			shmdt(shminfo.shmaddr);
   101 			screen->pixels = NULL;
   102 			goto error;
   103 		}
   104 		this->UpdateRects = X11_MITSHMUpdate;
   105 	}
   106 #endif /* not NO_SHARED_MEMORY */
   107 	if(!use_mitshm) {
   108 		int bpp;
   109 		screen->pixels = malloc(screen->h*screen->pitch);
   110 		if ( screen->pixels == NULL ) {
   111 			SDL_OutOfMemory();
   112 			return -1;
   113 		}
   114  	        bpp = screen->format->BytesPerPixel;
   115 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   116 					  this->hidden->depth, ZPixmap, 0,
   117 					  (char *)screen->pixels, 
   118 					  screen->w, screen->h,
   119 					  32, 0);
   120 		if ( SDL_Ximage == NULL )
   121 			goto error;
   122 		/* XPutImage will convert byte sex automatically */
   123 		SDL_Ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   124 			                 ? MSBFirst : LSBFirst;
   125 		this->UpdateRects = X11_NormalUpdate;
   126 	}
   127 	screen->pitch = SDL_Ximage->bytes_per_line;
   128 	return(0);
   129 
   130 error:
   131 	SDL_SetError("Couldn't create XImage");
   132 	return 1;
   133 }
   134 
   135 void X11_DestroyImage(_THIS, SDL_Surface *screen)
   136 {
   137 	if ( SDL_Ximage ) {
   138 		XDestroyImage(SDL_Ximage);
   139 #ifndef NO_SHARED_MEMORY
   140 		if ( use_mitshm ) {
   141 			XShmDetach(SDL_Display, &shminfo);
   142 			XSync(SDL_Display, False);
   143 			shmdt(shminfo.shmaddr);
   144 		}
   145 #endif /* ! NO_SHARED_MEMORY */
   146 		SDL_Ximage = NULL;
   147 	}
   148 	if ( screen ) {
   149 		screen->pixels = NULL;
   150 	}
   151 }
   152 
   153 /* Determine the number of CPUs in the system */
   154 static int num_CPU(void)
   155 {
   156        static int num_cpus = 0;
   157 
   158        if(!num_cpus) {
   159 #if defined(__linux)
   160            char line[BUFSIZ];
   161            FILE *pstat = fopen("/proc/stat", "r");
   162            if ( pstat ) {
   163                while ( fgets(line, sizeof(line), pstat) ) {
   164                    if (memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
   165                        ++num_cpus;
   166                    }
   167                }
   168                fclose(pstat);
   169            }
   170 #elif defined(_SC_NPROCESSORS_ONLN)
   171 	   /* number of processors online (SVR4.0MP compliant machines) */
   172            num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
   173 #elif defined(_SC_NPROCESSORS_CONF)
   174 	   /* number of processors configured (SVR4.0MP compliant machines) */
   175            num_cpus = sysconf(_SC_NPROCESSORS_CONF);
   176 #endif
   177            if ( num_cpus <= 0 ) {
   178                num_cpus = 1;
   179            }
   180        }
   181        return num_cpus;
   182 }
   183 
   184 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
   185 {
   186 	int retval;
   187 
   188 	X11_DestroyImage(this, screen);
   189         if ( flags & SDL_OPENGL ) {  /* No image when using GL */
   190         	retval = 0;
   191         } else {
   192 		retval = X11_SetupImage(this, screen);
   193 		/* We support asynchronous blitting on the display */
   194 		if ( flags & SDL_ASYNCBLIT ) {
   195 			/* This is actually slower on single-CPU systems,
   196 			   probably because of CPU contention between the
   197 			   X server and the application.
   198 			   Note: Is this still true with XFree86 4.0?
   199 			*/
   200 			if ( num_CPU() > 1 ) {
   201 				screen->flags |= SDL_ASYNCBLIT;
   202 			}
   203 		}
   204 	}
   205 	return(retval);
   206 }
   207 
   208 /* We don't actually allow hardware surfaces other than the main one */
   209 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
   210 {
   211 	return(-1);
   212 }
   213 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
   214 {
   215 	return;
   216 }
   217 
   218 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
   219 {
   220 	if ( (surface == SDL_VideoSurface) && blit_queued ) {
   221 		XSync(GFX_Display, False);
   222 		blit_queued = 0;
   223 	}
   224 	return(0);
   225 }
   226 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
   227 {
   228 	return;
   229 }
   230 
   231 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
   232 {
   233 	return(0);
   234 }
   235 
   236 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
   237 {
   238 	int i;
   239 	
   240 	for (i = 0; i < numrects; ++i) {
   241 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
   242 			continue;
   243 		}
   244 		XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   245 			  rects[i].x, rects[i].y,
   246 			  rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   247 	}
   248 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   249 		XFlush(GFX_Display);
   250 		blit_queued = 1;
   251 	} else {
   252 		XSync(GFX_Display, False);
   253 	}
   254 }
   255 
   256 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
   257 {
   258 #ifndef NO_SHARED_MEMORY
   259 	int i;
   260 
   261 	for ( i=0; i<numrects; ++i ) {
   262 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
   263 			continue;
   264 		}
   265 		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   266 				rects[i].x, rects[i].y,
   267 				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
   268 									False);
   269 	}
   270 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   271 		XFlush(GFX_Display);
   272 		blit_queued = 1;
   273 	} else {
   274 		XSync(GFX_Display, False);
   275 	}
   276 #endif /* ! NO_SHARED_MEMORY */
   277 }
   278 
   279 /* There's a problem with the automatic refreshing of the display.
   280    Even though the XVideo code uses the GFX_Display to update the
   281    video memory, it appears that updating the window asynchronously
   282    from a different thread will cause "blackouts" of the window.
   283    This is a sort of a hacked workaround for the problem.
   284 */
   285 static int enable_autorefresh = 1;
   286 
   287 void X11_DisableAutoRefresh(_THIS)
   288 {
   289 	--enable_autorefresh;
   290 }
   291 
   292 void X11_EnableAutoRefresh(_THIS)
   293 {
   294 	++enable_autorefresh;
   295 }
   296 
   297 void X11_RefreshDisplay(_THIS)
   298 {
   299 	/* Don't refresh a display that doesn't have an image (like GL)
   300 	   Instead, post an expose event so the application can refresh.
   301 	 */
   302 	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
   303 		SDL_PrivateExpose();
   304 		return;
   305 	}
   306 #ifndef NO_SHARED_MEMORY
   307 	if ( this->UpdateRects == X11_MITSHMUpdate ) {
   308 		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   309 				0, 0, 0, 0, this->screen->w, this->screen->h,
   310 				False);
   311 	} else
   312 #endif /* ! NO_SHARED_MEMORY */
   313 	{
   314 		XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   315 			  0, 0, 0, 0, this->screen->w, this->screen->h);
   316 	}
   317 	XSync(SDL_Display, False);
   318 }