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