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