Home | History | Annotate | Download | only in x11
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 		screen->pixels = SDL_malloc(screen->h*screen->pitch);
    106 		if ( screen->pixels == NULL ) {
    107 			SDL_OutOfMemory();
    108 			return -1;
    109 		}
    110 		SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
    111 					  this->hidden->depth, ZPixmap, 0,
    112 					  (char *)screen->pixels,
    113 					  screen->w, screen->h,
    114 					  32, 0);
    115 		if ( SDL_Ximage == NULL )
    116 			goto error;
    117 		/* XPutImage will convert byte sex automatically */
    118 		SDL_Ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN)
    119 			                 ? MSBFirst : LSBFirst;
    120 		this->UpdateRects = X11_NormalUpdate;
    121 	}
    122 	screen->pitch = SDL_Ximage->bytes_per_line;
    123 	return(0);
    124 
    125 error:
    126 	SDL_SetError("Couldn't create XImage");
    127 	return 1;
    128 }
    129 
    130 void X11_DestroyImage(_THIS, SDL_Surface *screen)
    131 {
    132 	if ( SDL_Ximage ) {
    133 		XDestroyImage(SDL_Ximage);
    134 #ifndef NO_SHARED_MEMORY
    135 		if ( use_mitshm ) {
    136 			XShmDetach(SDL_Display, &shminfo);
    137 			XSync(SDL_Display, False);
    138 			shmdt(shminfo.shmaddr);
    139 		}
    140 #endif /* ! NO_SHARED_MEMORY */
    141 		SDL_Ximage = NULL;
    142 	}
    143 	if ( screen ) {
    144 		screen->pixels = NULL;
    145 	}
    146 }
    147 
    148 /* Determine the number of CPUs in the system */
    149 static int num_CPU(void)
    150 {
    151        static int num_cpus = 0;
    152 
    153        if(!num_cpus) {
    154 #if defined(__LINUX__)
    155            char line[BUFSIZ];
    156            FILE *pstat = fopen("/proc/stat", "r");
    157            if ( pstat ) {
    158                while ( fgets(line, sizeof(line), pstat) ) {
    159                    if (SDL_memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
    160                        ++num_cpus;
    161                    }
    162                }
    163                fclose(pstat);
    164            }
    165 #elif defined(__IRIX__)
    166 	   num_cpus = sysconf(_SC_NPROC_ONLN);
    167 #elif defined(_SC_NPROCESSORS_ONLN)
    168 	   /* number of processors online (SVR4.0MP compliant machines) */
    169            num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
    170 #elif defined(_SC_NPROCESSORS_CONF)
    171 	   /* number of processors configured (SVR4.0MP compliant machines) */
    172            num_cpus = sysconf(_SC_NPROCESSORS_CONF);
    173 #endif
    174            if ( num_cpus <= 0 ) {
    175                num_cpus = 1;
    176            }
    177        }
    178        return num_cpus;
    179 }
    180 
    181 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
    182 {
    183 	int retval;
    184 
    185 	X11_DestroyImage(this, screen);
    186         if ( flags & SDL_OPENGL ) {  /* No image when using GL */
    187         	retval = 0;
    188         } else {
    189 		retval = X11_SetupImage(this, screen);
    190 		/* We support asynchronous blitting on the display */
    191 		if ( flags & SDL_ASYNCBLIT ) {
    192 			/* This is actually slower on single-CPU systems,
    193 			   probably because of CPU contention between the
    194 			   X server and the application.
    195 			   Note: Is this still true with XFree86 4.0?
    196 			*/
    197 			if ( num_CPU() > 1 ) {
    198 				screen->flags |= SDL_ASYNCBLIT;
    199 			}
    200 		}
    201 	}
    202 	return(retval);
    203 }
    204 
    205 /* We don't actually allow hardware surfaces other than the main one */
    206 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
    207 {
    208 	return(-1);
    209 }
    210 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
    211 {
    212 	return;
    213 }
    214 
    215 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
    216 {
    217 	if ( (surface == SDL_VideoSurface) && blit_queued ) {
    218 		XSync(GFX_Display, False);
    219 		blit_queued = 0;
    220 	}
    221 	return(0);
    222 }
    223 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
    224 {
    225 	return;
    226 }
    227 
    228 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
    229 {
    230 	return(0);
    231 }
    232 
    233 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
    234 {
    235 	int i;
    236 
    237 	for (i = 0; i < numrects; ++i) {
    238 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
    239 			continue;
    240 		}
    241 		XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
    242 			  rects[i].x, rects[i].y,
    243 			  rects[i].x, rects[i].y, rects[i].w, rects[i].h);
    244 	}
    245 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
    246 		XFlush(GFX_Display);
    247 		blit_queued = 1;
    248 	} else {
    249 		XSync(GFX_Display, False);
    250 	}
    251 }
    252 
    253 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
    254 {
    255 #ifndef NO_SHARED_MEMORY
    256 	int i;
    257 
    258 	for ( i=0; i<numrects; ++i ) {
    259 		if ( rects[i].w == 0 || rects[i].h == 0 ) { /* Clipped? */
    260 			continue;
    261 		}
    262 		XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
    263 				rects[i].x, rects[i].y,
    264 				rects[i].x, rects[i].y, rects[i].w, rects[i].h,
    265 									False);
    266 	}
    267 	if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
    268 		XFlush(GFX_Display);
    269 		blit_queued = 1;
    270 	} else {
    271 		XSync(GFX_Display, False);
    272 	}
    273 #endif /* ! NO_SHARED_MEMORY */
    274 }
    275 
    276 /* There's a problem with the automatic refreshing of the display.
    277    Even though the XVideo code uses the GFX_Display to update the
    278    video memory, it appears that updating the window asynchronously
    279    from a different thread will cause "blackouts" of the window.
    280    This is a sort of a hacked workaround for the problem.
    281 */
    282 static int enable_autorefresh = 1;
    283 
    284 void X11_DisableAutoRefresh(_THIS)
    285 {
    286 	--enable_autorefresh;
    287 }
    288 
    289 void X11_EnableAutoRefresh(_THIS)
    290 {
    291 	++enable_autorefresh;
    292 }
    293 
    294 void X11_RefreshDisplay(_THIS)
    295 {
    296 	/* Don't refresh a display that doesn't have an image (like GL)
    297 	   Instead, post an expose event so the application can refresh.
    298 	 */
    299 	if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
    300 		SDL_PrivateExpose();
    301 		return;
    302 	}
    303 #ifndef NO_SHARED_MEMORY
    304 	if ( this->UpdateRects == X11_MITSHMUpdate ) {
    305 		XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
    306 				0, 0, 0, 0, this->screen->w, this->screen->h,
    307 				False);
    308 	} else
    309 #endif /* ! NO_SHARED_MEMORY */
    310 	{
    311 		XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
    312 			  0, 0, 0, 0, this->screen->w, this->screen->h);
    313 	}
    314 	XSync(SDL_Display, False);
    315 }
    316 
    317