Home | History | Annotate | Download | only in x11
      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 (at) 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 
    319