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@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 */ 34static int shm_error; 35static int (*X_handler)(Display *, XErrorEvent *) = NULL; 36static 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 45static 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 */ 81static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects); 82static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects); 83 84int 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 127error: 128 SDL_SetError("Couldn't create XImage"); 129 return 1; 130} 131 132void 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 */ 151static 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 183int 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 */ 208int X11_AllocHWSurface(_THIS, SDL_Surface *surface) 209{ 210 return(-1); 211} 212void X11_FreeHWSurface(_THIS, SDL_Surface *surface) 213{ 214 return; 215} 216 217int 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} 225void X11_UnlockHWSurface(_THIS, SDL_Surface *surface) 226{ 227 return; 228} 229 230int X11_FlipHWSurface(_THIS, SDL_Surface *surface) 231{ 232 return(0); 233} 234 235static 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 255static 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*/ 284static int enable_autorefresh = 1; 285 286void X11_DisableAutoRefresh(_THIS) 287{ 288 --enable_autorefresh; 289} 290 291void X11_EnableAutoRefresh(_THIS) 292{ 293 ++enable_autorefresh; 294} 295 296void 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