diff src/video/x11/SDL_x11image.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children eb6b76a95f2d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/x11/SDL_x11image.c	Thu Apr 26 16:45:43 2001 +0000
@@ -0,0 +1,456 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@devolution.com
+*/
+
+#ifdef SAVE_RCSID
+static char rcsid =
+ "@(#) $Id$";
+#endif
+
+#include <stdlib.h>
+
+#include "SDL_error.h"
+#include "SDL_endian.h"
+#include "SDL_x11image_c.h"
+
+#if defined(__USLC__)
+#ifdef HAVE_KSTAT
+#undef HAVE_KSTAT
+#endif
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_KSTAT
+#include <kstat.h>
+#endif
+
+#ifndef NO_SHARED_MEMORY
+
+/* Shared memory information */
+extern int XShmQueryExtension(Display *dpy);	/* Not in X11 headers */
+
+/* Shared memory error handler routine */
+static int shm_error;
+static int (*X_handler)(Display *, XErrorEvent *) = NULL;
+static int shm_errhandler(Display *d, XErrorEvent *e)
+{
+        if ( e->error_code == BadAccess ) {
+        	++shm_error;
+        	return(0);
+        } else
+		return(X_handler(d,e));
+}
+#endif /* ! NO_SHARED_MEMORY */
+
+/* Various screen update functions available */
+static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
+static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
+
+int X11_SetupImage(_THIS, SDL_Surface *screen)
+{
+#ifdef NO_SHARED_MEMORY
+	screen->pixels = malloc(screen->h*screen->pitch);
+#else
+	/* Allocate shared memory if possible */
+	if ( use_mitshm ) {
+		shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
+								IPC_CREAT|0777);
+		if ( shminfo.shmid >= 0 ) {
+			shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
+			shminfo.readOnly = False;
+			if ( shminfo.shmaddr != (char *)-1 ) {
+				shm_error = False;
+				X_handler = XSetErrorHandler(shm_errhandler);
+				XShmAttach(SDL_Display, &shminfo);
+				XSync(SDL_Display, True);
+				XSetErrorHandler(X_handler);
+				if ( shm_error == True )
+					shmdt(shminfo.shmaddr);
+			} else {
+				shm_error = True;
+			}
+			shmctl(shminfo.shmid, IPC_RMID, NULL);
+		} else {
+			shm_error = True;
+		}
+		if ( shm_error == True )
+			use_mitshm = 0;
+	}
+	if ( use_mitshm ) {
+		screen->pixels = shminfo.shmaddr;
+	} else {
+		screen->pixels = malloc(screen->h*screen->pitch);
+	}
+#endif /* NO_SHARED_MEMORY */
+	if ( screen->pixels == NULL ) {
+		SDL_OutOfMemory();
+		return(-1);
+	}
+
+#ifdef NO_SHARED_MEMORY
+	{
+ 	        int bpp = screen->format->BytesPerPixel;
+		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
+					  this->hidden->depth, ZPixmap, 0,
+					  (char *)screen->pixels, 
+					  screen->w, screen->h,
+					  (bpp == 3) ? 32 : bpp * 8,
+					  0);
+	}
+#else
+	if ( use_mitshm ) {
+		SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
+					     this->hidden->depth, ZPixmap,
+					     shminfo.shmaddr, &shminfo, 
+					     screen->w, screen->h);
+	} else {
+ 	        int bpp = screen->format->BytesPerPixel;
+		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
+					  this->hidden->depth, ZPixmap, 0,
+					  (char *)screen->pixels, 
+					  screen->w, screen->h,
+					  (bpp == 3) ? 32 : bpp * 8,
+					  0);
+	}
+#endif /* NO_SHARED_MEMORY */
+	if ( SDL_Ximage == NULL ) {
+		SDL_SetError("Couldn't create XImage");
+#ifndef NO_SHARED_MEMORY
+		if ( use_mitshm ) {
+			XShmDetach(SDL_Display, &shminfo);
+			XSync(SDL_Display, False);
+			shmdt(shminfo.shmaddr);
+			screen->pixels = NULL;
+		}
+#endif /* ! NO_SHARED_MEMORY */
+		return(-1);
+	}
+	screen->pitch = SDL_Ximage->bytes_per_line;
+
+	/* Determine what blit function to use */
+#ifdef NO_SHARED_MEMORY
+	this->UpdateRects = X11_NormalUpdate;
+#else
+	if ( use_mitshm ) {
+		this->UpdateRects = X11_MITSHMUpdate;
+	} else {
+		this->UpdateRects = X11_NormalUpdate;
+	}
+#endif
+	return(0);
+}
+
+void X11_DestroyImage(_THIS, SDL_Surface *screen)
+{
+	if ( SDL_Ximage ) {
+		XDestroyImage(SDL_Ximage);
+#ifndef NO_SHARED_MEMORY
+		if ( use_mitshm ) {
+			XShmDetach(SDL_Display, &shminfo);
+			XSync(SDL_Display, False);
+			shmdt(shminfo.shmaddr);
+		}
+#endif /* ! NO_SHARED_MEMORY */
+		SDL_Ximage = NULL;
+	}
+	if ( screen ) {
+		screen->pixels = NULL;
+	}
+}
+
+/* This is a hack to see whether this system has more than 1 CPU */
+static int num_CPU(void)
+{
+       static int num_cpus = 0;
+
+       if(!num_cpus) {
+#ifdef linux
+           char line[BUFSIZ];
+           FILE *pstat = fopen("/proc/stat", "r");
+           if ( pstat ) {
+               while ( fgets(line, sizeof(line), pstat) ) {
+                   if (memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
+                       ++num_cpus;
+                   }
+               }
+               fclose(pstat);
+           }
+#elif defined(HAVE_KSTAT)
+           kstat_ctl_t *kc = kstat_open();
+           kstat_t *ks;
+           kstat_named_t *kn;
+           if(kc) {
+               if((ks = kstat_lookup(kc, "unix", -1, "system_misc"))
+                  && kstat_read(kc, ks, NULL) != -1
+                  && (kn = kstat_data_lookup(ks, "ncpus")))
+#ifdef KSTAT_DATA_UINT32
+                   num_cpus = kn->value.ui32;
+#else
+                   num_cpus = kn->value.ul; /* needed in solaris <2.6 */
+#endif
+               kstat_close(kc);
+           }
+#elif defined(__USLC__)
+           num_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
+#endif
+           if ( num_cpus <= 0 ) {
+               num_cpus = 1;
+           }
+       }
+       return num_cpus;
+}
+
+int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
+{
+	int retval;
+
+	X11_DestroyImage(this, screen);
+        if ( flags & SDL_OPENGL ) {  /* No image when using GL */
+        	retval = 0;
+        } else {
+		retval = X11_SetupImage(this, screen);
+		/* We support asynchronous blitting on the display */
+		if ( flags & SDL_ASYNCBLIT ) {
+			/* This is actually slower on single-CPU systems,
+			   probably because of CPU contention between the
+			   X server and the application.
+			   Note: Is this still true with XFree86 4.0?
+			*/
+			if ( num_CPU() > 1 ) {
+				screen->flags |= SDL_ASYNCBLIT;
+			}
+		}
+	}
+	return(retval);
+}
+
+/* We don't actually allow hardware surfaces other than the main one */
+int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
+{
+	return(-1);
+}
+void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
+{
+	return;
+}
+
+int X11_LockHWSurface(_THIS, SDL_Surface *surface)
+{
+	if ( (surface == SDL_VideoSurface) && blit_queued ) {
+		XSync(GFX_Display, False);
+		blit_queued = 0;
+	}
+	return(0);
+}
+void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
+{
+	return;
+}
+
+int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
+{
+	return(0);
+}
+
+/* Byte-swap the pixels in the display image */
+static void X11_SwapAllPixels(SDL_Surface *screen)
+{
+	int x, y;
+
+	switch (screen->format->BytesPerPixel) {
+	    case 2: {
+		Uint16 *spot;
+		for ( y=0; y<screen->h; ++y ) {
+			spot = (Uint16 *) ((Uint8 *)screen->pixels +
+						y * screen->pitch);
+			for ( x=0; x<screen->w; ++x, ++spot ) {
+				*spot = SDL_Swap16(*spot);
+			}
+		}
+	    }
+	    break;
+
+	    case 4: {
+		Uint32 *spot;
+		for ( y=0; y<screen->h; ++y ) {
+			spot = (Uint32 *) ((Uint8 *)screen->pixels +
+						y * screen->pitch);
+			for ( x=0; x<screen->w; ++x, ++spot ) {
+				*spot = SDL_Swap32(*spot);
+			}
+		}
+	    }
+	    break;
+
+	    default:
+		/* should never get here */
+		break;
+	}
+}
+static void X11_SwapPixels(SDL_Surface *screen, SDL_Rect *rect)
+{
+	int x, minx, maxx;
+	int y, miny, maxy;
+
+	switch (screen->format->BytesPerPixel) {
+	    case 2: {
+		Uint16 *spot;
+		minx = rect->x;
+		maxx = rect->x + rect->w;
+		miny = rect->y;
+		maxy = rect->y + rect->h;
+		for ( y=miny; y<maxy; ++y ) {
+		    spot = (Uint16 *) ((Uint8 *)screen->pixels +
+				       y * screen->pitch + minx * 2);
+		    for ( x=minx; x<maxx; ++x, ++spot ) {
+			*spot = SDL_Swap16(*spot);
+		    }
+		}
+	    }
+	    break;
+
+	    case 4: {
+		Uint32 *spot;
+		minx = rect->x;
+		maxx = rect->x + rect->w;
+		miny = rect->y;
+		maxy = rect->y + rect->h;
+		for ( y=miny; y<maxy; ++y ) {
+		    spot = (Uint32 *) ((Uint8 *)screen->pixels +
+				       y * screen->pitch + minx * 4);
+		    for ( x=minx; x<maxx; ++x, ++spot ) {
+			*spot = SDL_Swap32(*spot);
+		    }
+		}
+	    }
+	    break;
+
+	    default:
+		/* should never get here */
+		break;
+	}
+}
+
+static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
+{
+	int i;
+
+	/* Check for endian-swapped X server, swap if necessary (VERY slow!) */
+	if ( swap_pixels &&
+	     ((this->screen->format->BytesPerPixel%2) == 0) ) {
+		for ( i=0; i<numrects; ++i ) {
+			if ( ! rects[i].w ) { /* Clipped? */
+				continue;
+			}
+		        X11_SwapPixels(this->screen, rects + i);
+			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				rects[i].x, rects[i].y,
+				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
+			X11_SwapPixels(this->screen, rects + i);
+		}
+	} else {
+		for ( i=0; i<numrects; ++i ) {
+			if ( ! rects[i].w ) { /* Clipped? */
+				continue;
+			}
+			XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				rects[i].x, rects[i].y,
+				rects[i].x, rects[i].y, rects[i].w, rects[i].h);
+		}
+	}
+	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
+		XFlush(GFX_Display);
+		++blit_queued;
+	} else {
+		XSync(GFX_Display, False);
+	}
+}
+
+static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
+{
+#ifndef NO_SHARED_MEMORY
+	int i;
+
+	for ( i=0; i<numrects; ++i ) {
+		if ( ! rects[i].w ) { /* Clipped? */
+			continue;
+		}
+		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				rects[i].x, rects[i].y,
+				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
+									False);
+	}
+	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
+		XFlush(GFX_Display);
+		++blit_queued;
+	} else {
+		XSync(GFX_Display, False);
+	}
+#endif /* ! NO_SHARED_MEMORY */
+}
+
+/* There's a problem with the automatic refreshing of the display.
+   Even though the XVideo code uses the GFX_Display to update the
+   video memory, it appears that updating the window asynchronously
+   from a different thread will cause "blackouts" of the window.
+   This is a sort of a hacked workaround for the problem.
+*/
+static int enable_autorefresh = 1;
+
+void X11_DisableAutoRefresh(_THIS)
+{
+	--enable_autorefresh;
+}
+
+void X11_EnableAutoRefresh(_THIS)
+{
+	++enable_autorefresh;
+}
+
+void X11_RefreshDisplay(_THIS)
+{
+	/* Don't refresh a display that doesn't have an image (like GL) */
+	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
+		return;
+	}
+#ifndef NO_SHARED_MEMORY
+	if ( this->UpdateRects == X11_MITSHMUpdate ) {
+		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				0, 0, 0, 0, this->screen->w, this->screen->h,
+				False);
+	} else {
+#else
+	{
+#endif /* ! NO_SHARED_MEMORY */
+		/* Check for endian-swapped X server, swap if necessary */
+		if ( swap_pixels &&
+		     ((this->screen->format->BytesPerPixel%2) == 0) ) {
+			X11_SwapAllPixels(this->screen);
+			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				0, 0, 0, 0, this->screen->w, this->screen->h);
+			X11_SwapAllPixels(this->screen);
+		} else {
+			XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
+				0, 0, 0, 0, this->screen->w, this->screen->h);
+		}
+	}
+	XSync(SDL_Display, False);
+}