src/video/x11/SDL_x11image.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 22 Mar 2006 05:00:59 +0000
changeset 1575 3ba88cb7eb1b
parent 1402 d910939febfa
child 1658 e49147870aac
child 1895 c121d94672cb
child 4159 a1b03ba2fcd0
permissions -rw-r--r--
Updated dynamic X11 code. See details in Bugzilla #170.
     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@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include <stdio.h>
    25 #include <unistd.h>
    26 
    27 #include "SDL_endian.h"
    28 #include "../../events/SDL_events_c.h"
    29 #include "SDL_x11image_c.h"
    30 
    31 #ifndef NO_SHARED_MEMORY
    32 
    33 /* Shared memory error handler routine */
    34 static int shm_error;
    35 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
    36 static int shm_errhandler(Display *d, XErrorEvent *e)
    37 {
    38         if ( e->error_code == BadAccess ) {
    39         	shm_error = True;
    40         	return(0);
    41         } else
    42 		return(X_handler(d,e));
    43 }
    44 
    45 static void try_mitshm(_THIS, SDL_Surface *screen)
    46 {
    47 	/* Dynamic X11 may not have SHM entry points on this box. */
    48 	if ((use_mitshm) && (!SDL_X11_HAVE_SHM))
    49 		use_mitshm = 0;
    50 
    51 	if(!use_mitshm)
    52 		return;
    53 	shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
    54 			       IPC_CREAT | 0777);
    55 	if ( shminfo.shmid >= 0 ) {
    56 		shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
    57 		shminfo.readOnly = False;
    58 		if ( shminfo.shmaddr != (char *)-1 ) {
    59 			shm_error = False;
    60 			X_handler = XSetErrorHandler(shm_errhandler);
    61 			XShmAttach(SDL_Display, &shminfo);
    62 			XSync(SDL_Display, True);
    63 			XSetErrorHandler(X_handler);
    64 			if ( shm_error )
    65 				shmdt(shminfo.shmaddr);
    66 		} else {
    67 			shm_error = True;
    68 		}
    69 		shmctl(shminfo.shmid, IPC_RMID, NULL);
    70 	} else {
    71 		shm_error = True;
    72 	}
    73 	if ( shm_error )
    74 		use_mitshm = 0;
    75 	if ( use_mitshm )
    76 		screen->pixels = shminfo.shmaddr;
    77 }
    78 #endif /* ! NO_SHARED_MEMORY */
    79 
    80 /* Various screen update functions available */
    81 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
    82 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
    83 
    84 int X11_SetupImage(_THIS, SDL_Surface *screen)
    85 {
    86 #ifndef NO_SHARED_MEMORY
    87 	try_mitshm(this, screen);
    88 	if(use_mitshm) {
    89 		SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
    90 					     this->hidden->depth, ZPixmap,
    91 					     shminfo.shmaddr, &shminfo, 
    92 					     screen->w, screen->h);
    93 		if(!SDL_Ximage) {
    94 			XShmDetach(SDL_Display, &shminfo);
    95 			XSync(SDL_Display, False);
    96 			shmdt(shminfo.shmaddr);
    97 			screen->pixels = NULL;
    98 			goto error;
    99 		}
   100 		this->UpdateRects = X11_MITSHMUpdate;
   101 	}
   102 	if(!use_mitshm)
   103 #endif /* not NO_SHARED_MEMORY */
   104 	{
   105 		int bpp;
   106 		screen->pixels = SDL_malloc(screen->h*screen->pitch);
   107 		if ( screen->pixels == NULL ) {
   108 			SDL_OutOfMemory();
   109 			return -1;
   110 		}
   111  	        bpp = screen->format->BytesPerPixel;
   112 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   113 					  this->hidden->depth, ZPixmap, 0,
   114 					  (char *)screen->pixels, 
   115 					  screen->w, screen->h,
   116 					  32, 0);
   117 		if ( SDL_Ximage == NULL )
   118 			goto error;
   119 		/* XPutImage will convert byte sex automatically */
   120 		SDL_Ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   121 			                 ? MSBFirst : LSBFirst;
   122 		this->UpdateRects = X11_NormalUpdate;
   123 	}
   124 	screen->pitch = SDL_Ximage->bytes_per_line;
   125 	return(0);
   126 
   127 error:
   128 	SDL_SetError("Couldn't create XImage");
   129 	return 1;
   130 }
   131 
   132 void X11_DestroyImage(_THIS, SDL_Surface *screen)
   133 {
   134 	if ( SDL_Ximage ) {
   135 		XDestroyImage(SDL_Ximage);
   136 #ifndef NO_SHARED_MEMORY
   137 		if ( use_mitshm ) {
   138 			XShmDetach(SDL_Display, &shminfo);
   139 			XSync(SDL_Display, False);
   140 			shmdt(shminfo.shmaddr);
   141 		}
   142 #endif /* ! NO_SHARED_MEMORY */
   143 		SDL_Ximage = NULL;
   144 	}
   145 	if ( screen ) {
   146 		screen->pixels = NULL;
   147 	}
   148 }
   149 
   150 /* Determine the number of CPUs in the system */
   151 static int num_CPU(void)
   152 {
   153        static int num_cpus = 0;
   154 
   155        if(!num_cpus) {
   156 #if defined(__LINUX__)
   157            char line[BUFSIZ];
   158            FILE *pstat = fopen("/proc/stat", "r");
   159            if ( pstat ) {
   160                while ( fgets(line, sizeof(line), pstat) ) {
   161                    if (SDL_memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
   162                        ++num_cpus;
   163                    }
   164                }
   165                fclose(pstat);
   166            }
   167 #elif defined(__IRIX__)
   168 	   num_cpus = sysconf(_SC_NPROC_ONLN);
   169 #elif defined(_SC_NPROCESSORS_ONLN)
   170 	   /* number of processors online (SVR4.0MP compliant machines) */
   171            num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
   172 #elif defined(_SC_NPROCESSORS_CONF)
   173 	   /* number of processors configured (SVR4.0MP compliant machines) */
   174            num_cpus = sysconf(_SC_NPROCESSORS_CONF);
   175 #endif
   176            if ( num_cpus <= 0 ) {
   177                num_cpus = 1;
   178            }
   179        }
   180        return num_cpus;
   181 }
   182 
   183 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
   184 {
   185 	int retval;
   186 
   187 	X11_DestroyImage(this, screen);
   188         if ( flags & SDL_OPENGL ) {  /* No image when using GL */
   189         	retval = 0;
   190         } else {
   191 		retval = X11_SetupImage(this, screen);
   192 		/* We support asynchronous blitting on the display */
   193 		if ( flags & SDL_ASYNCBLIT ) {
   194 			/* This is actually slower on single-CPU systems,
   195 			   probably because of CPU contention between the
   196 			   X server and the application.
   197 			   Note: Is this still true with XFree86 4.0?
   198 			*/
   199 			if ( num_CPU() > 1 ) {
   200 				screen->flags |= SDL_ASYNCBLIT;
   201 			}
   202 		}
   203 	}
   204 	return(retval);
   205 }
   206 
   207 /* We don't actually allow hardware surfaces other than the main one */
   208 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
   209 {
   210 	return(-1);
   211 }
   212 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
   213 {
   214 	return;
   215 }
   216 
   217 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
   218 {
   219 	if ( (surface == SDL_VideoSurface) && blit_queued ) {
   220 		XSync(GFX_Display, False);
   221 		blit_queued = 0;
   222 	}
   223 	return(0);
   224 }
   225 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
   226 {
   227 	return;
   228 }
   229 
   230 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
   231 {
   232 	return(0);
   233 }
   234 
   235 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
   236 {
   237 	int i;
   238 	
   239 	for (i = 0; i < numrects; ++i) {
   240 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
   241 			continue;
   242 		}
   243 		XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   244 			  rects[i].x, rects[i].y,
   245 			  rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   246 	}
   247 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   248 		XFlush(GFX_Display);
   249 		blit_queued = 1;
   250 	} else {
   251 		XSync(GFX_Display, False);
   252 	}
   253 }
   254 
   255 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
   256 {
   257 #ifndef NO_SHARED_MEMORY
   258 	int i;
   259 
   260 	for ( i=0; i<numrects; ++i ) {
   261 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
   262 			continue;
   263 		}
   264 		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   265 				rects[i].x, rects[i].y,
   266 				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
   267 									False);
   268 	}
   269 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   270 		XFlush(GFX_Display);
   271 		blit_queued = 1;
   272 	} else {
   273 		XSync(GFX_Display, False);
   274 	}
   275 #endif /* ! NO_SHARED_MEMORY */
   276 }
   277 
   278 /* There's a problem with the automatic refreshing of the display.
   279    Even though the XVideo code uses the GFX_Display to update the
   280    video memory, it appears that updating the window asynchronously
   281    from a different thread will cause "blackouts" of the window.
   282    This is a sort of a hacked workaround for the problem.
   283 */
   284 static int enable_autorefresh = 1;
   285 
   286 void X11_DisableAutoRefresh(_THIS)
   287 {
   288 	--enable_autorefresh;
   289 }
   290 
   291 void X11_EnableAutoRefresh(_THIS)
   292 {
   293 	++enable_autorefresh;
   294 }
   295 
   296 void X11_RefreshDisplay(_THIS)
   297 {
   298 	/* Don't refresh a display that doesn't have an image (like GL)
   299 	   Instead, post an expose event so the application can refresh.
   300 	 */
   301 	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
   302 		SDL_PrivateExpose();
   303 		return;
   304 	}
   305 #ifndef NO_SHARED_MEMORY
   306 	if ( this->UpdateRects == X11_MITSHMUpdate ) {
   307 		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   308 				0, 0, 0, 0, this->screen->w, this->screen->h,
   309 				False);
   310 	} else
   311 #endif /* ! NO_SHARED_MEMORY */
   312 	{
   313 		XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   314 			  0, 0, 0, 0, this->screen->w, this->screen->h);
   315 	}
   316 	XSync(SDL_Display, False);
   317 }
   318