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@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 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 125error: 126 SDL_SetError("Couldn't create XImage"); 127 return 1; 128} 129 130void 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 */ 149static 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 181int 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 */ 206int X11_AllocHWSurface(_THIS, SDL_Surface *surface) 207{ 208 return(-1); 209} 210void X11_FreeHWSurface(_THIS, SDL_Surface *surface) 211{ 212 return; 213} 214 215int 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} 223void X11_UnlockHWSurface(_THIS, SDL_Surface *surface) 224{ 225 return; 226} 227 228int X11_FlipHWSurface(_THIS, SDL_Surface *surface) 229{ 230 return(0); 231} 232 233static 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 253static 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*/ 282static int enable_autorefresh = 1; 283 284void X11_DisableAutoRefresh(_THIS) 285{ 286 --enable_autorefresh; 287} 288 289void X11_EnableAutoRefresh(_THIS) 290{ 291 ++enable_autorefresh; 292} 293 294void 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