| 1 | /* |
| 2 | SDL - Simple DirectMedia Layer |
| 3 | Copyright (C) 1997-2009 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 */ |
| 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 | |