src/video/x11/SDL_x11image.c
author Sam Lantinga <slouken@lokigames.com>
Thu, 26 Apr 2001 16:45:43 +0000
changeset 0 74212992fb08
child 161 eb6b76a95f2d
permissions -rw-r--r--
Initial revision
     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@devolution.com
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 #include <stdlib.h>
    29 
    30 #include "SDL_error.h"
    31 #include "SDL_endian.h"
    32 #include "SDL_x11image_c.h"
    33 
    34 #if defined(__USLC__)
    35 #ifdef HAVE_KSTAT
    36 #undef HAVE_KSTAT
    37 #endif
    38 #include <unistd.h>
    39 #endif
    40 
    41 #ifdef HAVE_KSTAT
    42 #include <kstat.h>
    43 #endif
    44 
    45 #ifndef NO_SHARED_MEMORY
    46 
    47 /* Shared memory information */
    48 extern int XShmQueryExtension(Display *dpy);	/* Not in X11 headers */
    49 
    50 /* Shared memory error handler routine */
    51 static int shm_error;
    52 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
    53 static int shm_errhandler(Display *d, XErrorEvent *e)
    54 {
    55         if ( e->error_code == BadAccess ) {
    56         	++shm_error;
    57         	return(0);
    58         } else
    59 		return(X_handler(d,e));
    60 }
    61 #endif /* ! NO_SHARED_MEMORY */
    62 
    63 /* Various screen update functions available */
    64 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
    65 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
    66 
    67 int X11_SetupImage(_THIS, SDL_Surface *screen)
    68 {
    69 #ifdef NO_SHARED_MEMORY
    70 	screen->pixels = malloc(screen->h*screen->pitch);
    71 #else
    72 	/* Allocate shared memory if possible */
    73 	if ( use_mitshm ) {
    74 		shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
    75 								IPC_CREAT|0777);
    76 		if ( shminfo.shmid >= 0 ) {
    77 			shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
    78 			shminfo.readOnly = False;
    79 			if ( shminfo.shmaddr != (char *)-1 ) {
    80 				shm_error = False;
    81 				X_handler = XSetErrorHandler(shm_errhandler);
    82 				XShmAttach(SDL_Display, &shminfo);
    83 				XSync(SDL_Display, True);
    84 				XSetErrorHandler(X_handler);
    85 				if ( shm_error == True )
    86 					shmdt(shminfo.shmaddr);
    87 			} else {
    88 				shm_error = True;
    89 			}
    90 			shmctl(shminfo.shmid, IPC_RMID, NULL);
    91 		} else {
    92 			shm_error = True;
    93 		}
    94 		if ( shm_error == True )
    95 			use_mitshm = 0;
    96 	}
    97 	if ( use_mitshm ) {
    98 		screen->pixels = shminfo.shmaddr;
    99 	} else {
   100 		screen->pixels = malloc(screen->h*screen->pitch);
   101 	}
   102 #endif /* NO_SHARED_MEMORY */
   103 	if ( screen->pixels == NULL ) {
   104 		SDL_OutOfMemory();
   105 		return(-1);
   106 	}
   107 
   108 #ifdef NO_SHARED_MEMORY
   109 	{
   110  	        int bpp = screen->format->BytesPerPixel;
   111 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   112 					  this->hidden->depth, ZPixmap, 0,
   113 					  (char *)screen->pixels, 
   114 					  screen->w, screen->h,
   115 					  (bpp == 3) ? 32 : bpp * 8,
   116 					  0);
   117 	}
   118 #else
   119 	if ( use_mitshm ) {
   120 		SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
   121 					     this->hidden->depth, ZPixmap,
   122 					     shminfo.shmaddr, &shminfo, 
   123 					     screen->w, screen->h);
   124 	} else {
   125  	        int bpp = screen->format->BytesPerPixel;
   126 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
   127 					  this->hidden->depth, ZPixmap, 0,
   128 					  (char *)screen->pixels, 
   129 					  screen->w, screen->h,
   130 					  (bpp == 3) ? 32 : bpp * 8,
   131 					  0);
   132 	}
   133 #endif /* NO_SHARED_MEMORY */
   134 	if ( SDL_Ximage == NULL ) {
   135 		SDL_SetError("Couldn't create 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 			screen->pixels = NULL;
   142 		}
   143 #endif /* ! NO_SHARED_MEMORY */
   144 		return(-1);
   145 	}
   146 	screen->pitch = SDL_Ximage->bytes_per_line;
   147 
   148 	/* Determine what blit function to use */
   149 #ifdef NO_SHARED_MEMORY
   150 	this->UpdateRects = X11_NormalUpdate;
   151 #else
   152 	if ( use_mitshm ) {
   153 		this->UpdateRects = X11_MITSHMUpdate;
   154 	} else {
   155 		this->UpdateRects = X11_NormalUpdate;
   156 	}
   157 #endif
   158 	return(0);
   159 }
   160 
   161 void X11_DestroyImage(_THIS, SDL_Surface *screen)
   162 {
   163 	if ( SDL_Ximage ) {
   164 		XDestroyImage(SDL_Ximage);
   165 #ifndef NO_SHARED_MEMORY
   166 		if ( use_mitshm ) {
   167 			XShmDetach(SDL_Display, &shminfo);
   168 			XSync(SDL_Display, False);
   169 			shmdt(shminfo.shmaddr);
   170 		}
   171 #endif /* ! NO_SHARED_MEMORY */
   172 		SDL_Ximage = NULL;
   173 	}
   174 	if ( screen ) {
   175 		screen->pixels = NULL;
   176 	}
   177 }
   178 
   179 /* This is a hack to see whether this system has more than 1 CPU */
   180 static int num_CPU(void)
   181 {
   182        static int num_cpus = 0;
   183 
   184        if(!num_cpus) {
   185 #ifdef linux
   186            char line[BUFSIZ];
   187            FILE *pstat = fopen("/proc/stat", "r");
   188            if ( pstat ) {
   189                while ( fgets(line, sizeof(line), pstat) ) {
   190                    if (memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
   191                        ++num_cpus;
   192                    }
   193                }
   194                fclose(pstat);
   195            }
   196 #elif defined(HAVE_KSTAT)
   197            kstat_ctl_t *kc = kstat_open();
   198            kstat_t *ks;
   199            kstat_named_t *kn;
   200            if(kc) {
   201                if((ks = kstat_lookup(kc, "unix", -1, "system_misc"))
   202                   && kstat_read(kc, ks, NULL) != -1
   203                   && (kn = kstat_data_lookup(ks, "ncpus")))
   204 #ifdef KSTAT_DATA_UINT32
   205                    num_cpus = kn->value.ui32;
   206 #else
   207                    num_cpus = kn->value.ul; /* needed in solaris <2.6 */
   208 #endif
   209                kstat_close(kc);
   210            }
   211 #elif defined(__USLC__)
   212            num_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
   213 #endif
   214            if ( num_cpus <= 0 ) {
   215                num_cpus = 1;
   216            }
   217        }
   218        return num_cpus;
   219 }
   220 
   221 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
   222 {
   223 	int retval;
   224 
   225 	X11_DestroyImage(this, screen);
   226         if ( flags & SDL_OPENGL ) {  /* No image when using GL */
   227         	retval = 0;
   228         } else {
   229 		retval = X11_SetupImage(this, screen);
   230 		/* We support asynchronous blitting on the display */
   231 		if ( flags & SDL_ASYNCBLIT ) {
   232 			/* This is actually slower on single-CPU systems,
   233 			   probably because of CPU contention between the
   234 			   X server and the application.
   235 			   Note: Is this still true with XFree86 4.0?
   236 			*/
   237 			if ( num_CPU() > 1 ) {
   238 				screen->flags |= SDL_ASYNCBLIT;
   239 			}
   240 		}
   241 	}
   242 	return(retval);
   243 }
   244 
   245 /* We don't actually allow hardware surfaces other than the main one */
   246 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
   247 {
   248 	return(-1);
   249 }
   250 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
   251 {
   252 	return;
   253 }
   254 
   255 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
   256 {
   257 	if ( (surface == SDL_VideoSurface) && blit_queued ) {
   258 		XSync(GFX_Display, False);
   259 		blit_queued = 0;
   260 	}
   261 	return(0);
   262 }
   263 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
   264 {
   265 	return;
   266 }
   267 
   268 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
   269 {
   270 	return(0);
   271 }
   272 
   273 /* Byte-swap the pixels in the display image */
   274 static void X11_SwapAllPixels(SDL_Surface *screen)
   275 {
   276 	int x, y;
   277 
   278 	switch (screen->format->BytesPerPixel) {
   279 	    case 2: {
   280 		Uint16 *spot;
   281 		for ( y=0; y<screen->h; ++y ) {
   282 			spot = (Uint16 *) ((Uint8 *)screen->pixels +
   283 						y * screen->pitch);
   284 			for ( x=0; x<screen->w; ++x, ++spot ) {
   285 				*spot = SDL_Swap16(*spot);
   286 			}
   287 		}
   288 	    }
   289 	    break;
   290 
   291 	    case 4: {
   292 		Uint32 *spot;
   293 		for ( y=0; y<screen->h; ++y ) {
   294 			spot = (Uint32 *) ((Uint8 *)screen->pixels +
   295 						y * screen->pitch);
   296 			for ( x=0; x<screen->w; ++x, ++spot ) {
   297 				*spot = SDL_Swap32(*spot);
   298 			}
   299 		}
   300 	    }
   301 	    break;
   302 
   303 	    default:
   304 		/* should never get here */
   305 		break;
   306 	}
   307 }
   308 static void X11_SwapPixels(SDL_Surface *screen, SDL_Rect *rect)
   309 {
   310 	int x, minx, maxx;
   311 	int y, miny, maxy;
   312 
   313 	switch (screen->format->BytesPerPixel) {
   314 	    case 2: {
   315 		Uint16 *spot;
   316 		minx = rect->x;
   317 		maxx = rect->x + rect->w;
   318 		miny = rect->y;
   319 		maxy = rect->y + rect->h;
   320 		for ( y=miny; y<maxy; ++y ) {
   321 		    spot = (Uint16 *) ((Uint8 *)screen->pixels +
   322 				       y * screen->pitch + minx * 2);
   323 		    for ( x=minx; x<maxx; ++x, ++spot ) {
   324 			*spot = SDL_Swap16(*spot);
   325 		    }
   326 		}
   327 	    }
   328 	    break;
   329 
   330 	    case 4: {
   331 		Uint32 *spot;
   332 		minx = rect->x;
   333 		maxx = rect->x + rect->w;
   334 		miny = rect->y;
   335 		maxy = rect->y + rect->h;
   336 		for ( y=miny; y<maxy; ++y ) {
   337 		    spot = (Uint32 *) ((Uint8 *)screen->pixels +
   338 				       y * screen->pitch + minx * 4);
   339 		    for ( x=minx; x<maxx; ++x, ++spot ) {
   340 			*spot = SDL_Swap32(*spot);
   341 		    }
   342 		}
   343 	    }
   344 	    break;
   345 
   346 	    default:
   347 		/* should never get here */
   348 		break;
   349 	}
   350 }
   351 
   352 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
   353 {
   354 	int i;
   355 
   356 	/* Check for endian-swapped X server, swap if necessary (VERY slow!) */
   357 	if ( swap_pixels &&
   358 	     ((this->screen->format->BytesPerPixel%2) == 0) ) {
   359 		for ( i=0; i<numrects; ++i ) {
   360 			if ( ! rects[i].w ) { /* Clipped? */
   361 				continue;
   362 			}
   363 		        X11_SwapPixels(this->screen, rects + i);
   364 			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   365 				rects[i].x, rects[i].y,
   366 				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   367 			X11_SwapPixels(this->screen, rects + i);
   368 		}
   369 	} else {
   370 		for ( i=0; i<numrects; ++i ) {
   371 			if ( ! rects[i].w ) { /* Clipped? */
   372 				continue;
   373 			}
   374 			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   375 				rects[i].x, rects[i].y,
   376 				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
   377 		}
   378 	}
   379 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   380 		XFlush(GFX_Display);
   381 		++blit_queued;
   382 	} else {
   383 		XSync(GFX_Display, False);
   384 	}
   385 }
   386 
   387 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
   388 {
   389 #ifndef NO_SHARED_MEMORY
   390 	int i;
   391 
   392 	for ( i=0; i<numrects; ++i ) {
   393 		if ( ! rects[i].w ) { /* Clipped? */
   394 			continue;
   395 		}
   396 		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
   397 				rects[i].x, rects[i].y,
   398 				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
   399 									False);
   400 	}
   401 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
   402 		XFlush(GFX_Display);
   403 		++blit_queued;
   404 	} else {
   405 		XSync(GFX_Display, False);
   406 	}
   407 #endif /* ! NO_SHARED_MEMORY */
   408 }
   409 
   410 /* There's a problem with the automatic refreshing of the display.
   411    Even though the XVideo code uses the GFX_Display to update the
   412    video memory, it appears that updating the window asynchronously
   413    from a different thread will cause "blackouts" of the window.
   414    This is a sort of a hacked workaround for the problem.
   415 */
   416 static int enable_autorefresh = 1;
   417 
   418 void X11_DisableAutoRefresh(_THIS)
   419 {
   420 	--enable_autorefresh;
   421 }
   422 
   423 void X11_EnableAutoRefresh(_THIS)
   424 {
   425 	++enable_autorefresh;
   426 }
   427 
   428 void X11_RefreshDisplay(_THIS)
   429 {
   430 	/* Don't refresh a display that doesn't have an image (like GL) */
   431 	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
   432 		return;
   433 	}
   434 #ifndef NO_SHARED_MEMORY
   435 	if ( this->UpdateRects == X11_MITSHMUpdate ) {
   436 		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   437 				0, 0, 0, 0, this->screen->w, this->screen->h,
   438 				False);
   439 	} else {
   440 #else
   441 	{
   442 #endif /* ! NO_SHARED_MEMORY */
   443 		/* Check for endian-swapped X server, swap if necessary */
   444 		if ( swap_pixels &&
   445 		     ((this->screen->format->BytesPerPixel%2) == 0) ) {
   446 			X11_SwapAllPixels(this->screen);
   447 			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   448 				0, 0, 0, 0, this->screen->w, this->screen->h);
   449 			X11_SwapAllPixels(this->screen);
   450 		} else {
   451 			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
   452 				0, 0, 0, 0, this->screen->w, this->screen->h);
   453 		}
   454 	}
   455 	XSync(SDL_Display, False);
   456 }