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