comparison src/video/x11/SDL_x11image.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children eb6b76a95f2d
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 #ifdef SAVE_RCSID
24 static char rcsid =
25 "@(#) $Id$";
26 #endif
27
28 #include <stdlib.h>
29
30 #include "SDL_error.h"
31 #include "SDL_endian.h"
32 #include "SDL_x11image_c.h"
33
34 #if defined(__USLC__)
35 #ifdef HAVE_KSTAT
36 #undef HAVE_KSTAT
37 #endif
38 #include <unistd.h>
39 #endif
40
41 #ifdef HAVE_KSTAT
42 #include <kstat.h>
43 #endif
44
45 #ifndef NO_SHARED_MEMORY
46
47 /* Shared memory information */
48 extern int XShmQueryExtension(Display *dpy); /* Not in X11 headers */
49
50 /* Shared memory error handler routine */
51 static int shm_error;
52 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
53 static int shm_errhandler(Display *d, XErrorEvent *e)
54 {
55 if ( e->error_code == BadAccess ) {
56 ++shm_error;
57 return(0);
58 } else
59 return(X_handler(d,e));
60 }
61 #endif /* ! NO_SHARED_MEMORY */
62
63 /* Various screen update functions available */
64 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
65 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects);
66
67 int X11_SetupImage(_THIS, SDL_Surface *screen)
68 {
69 #ifdef NO_SHARED_MEMORY
70 screen->pixels = malloc(screen->h*screen->pitch);
71 #else
72 /* Allocate shared memory if possible */
73 if ( use_mitshm ) {
74 shminfo.shmid = shmget(IPC_PRIVATE, screen->h*screen->pitch,
75 IPC_CREAT|0777);
76 if ( shminfo.shmid >= 0 ) {
77 shminfo.shmaddr = (char *)shmat(shminfo.shmid, 0, 0);
78 shminfo.readOnly = False;
79 if ( shminfo.shmaddr != (char *)-1 ) {
80 shm_error = False;
81 X_handler = XSetErrorHandler(shm_errhandler);
82 XShmAttach(SDL_Display, &shminfo);
83 XSync(SDL_Display, True);
84 XSetErrorHandler(X_handler);
85 if ( shm_error == True )
86 shmdt(shminfo.shmaddr);
87 } else {
88 shm_error = True;
89 }
90 shmctl(shminfo.shmid, IPC_RMID, NULL);
91 } else {
92 shm_error = True;
93 }
94 if ( shm_error == True )
95 use_mitshm = 0;
96 }
97 if ( use_mitshm ) {
98 screen->pixels = shminfo.shmaddr;
99 } else {
100 screen->pixels = malloc(screen->h*screen->pitch);
101 }
102 #endif /* NO_SHARED_MEMORY */
103 if ( screen->pixels == NULL ) {
104 SDL_OutOfMemory();
105 return(-1);
106 }
107
108 #ifdef NO_SHARED_MEMORY
109 {
110 int bpp = screen->format->BytesPerPixel;
111 SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
112 this->hidden->depth, ZPixmap, 0,
113 (char *)screen->pixels,
114 screen->w, screen->h,
115 (bpp == 3) ? 32 : bpp * 8,
116 0);
117 }
118 #else
119 if ( use_mitshm ) {
120 SDL_Ximage = XShmCreateImage(SDL_Display, SDL_Visual,
121 this->hidden->depth, ZPixmap,
122 shminfo.shmaddr, &shminfo,
123 screen->w, screen->h);
124 } else {
125 int bpp = screen->format->BytesPerPixel;
126 SDL_Ximage = XCreateImage(SDL_Display, SDL_Visual,
127 this->hidden->depth, ZPixmap, 0,
128 (char *)screen->pixels,
129 screen->w, screen->h,
130 (bpp == 3) ? 32 : bpp * 8,
131 0);
132 }
133 #endif /* NO_SHARED_MEMORY */
134 if ( SDL_Ximage == NULL ) {
135 SDL_SetError("Couldn't create 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 screen->pixels = NULL;
142 }
143 #endif /* ! NO_SHARED_MEMORY */
144 return(-1);
145 }
146 screen->pitch = SDL_Ximage->bytes_per_line;
147
148 /* Determine what blit function to use */
149 #ifdef NO_SHARED_MEMORY
150 this->UpdateRects = X11_NormalUpdate;
151 #else
152 if ( use_mitshm ) {
153 this->UpdateRects = X11_MITSHMUpdate;
154 } else {
155 this->UpdateRects = X11_NormalUpdate;
156 }
157 #endif
158 return(0);
159 }
160
161 void X11_DestroyImage(_THIS, SDL_Surface *screen)
162 {
163 if ( SDL_Ximage ) {
164 XDestroyImage(SDL_Ximage);
165 #ifndef NO_SHARED_MEMORY
166 if ( use_mitshm ) {
167 XShmDetach(SDL_Display, &shminfo);
168 XSync(SDL_Display, False);
169 shmdt(shminfo.shmaddr);
170 }
171 #endif /* ! NO_SHARED_MEMORY */
172 SDL_Ximage = NULL;
173 }
174 if ( screen ) {
175 screen->pixels = NULL;
176 }
177 }
178
179 /* This is a hack to see whether this system has more than 1 CPU */
180 static int num_CPU(void)
181 {
182 static int num_cpus = 0;
183
184 if(!num_cpus) {
185 #ifdef linux
186 char line[BUFSIZ];
187 FILE *pstat = fopen("/proc/stat", "r");
188 if ( pstat ) {
189 while ( fgets(line, sizeof(line), pstat) ) {
190 if (memcmp(line, "cpu", 3) == 0 && line[3] != ' ') {
191 ++num_cpus;
192 }
193 }
194 fclose(pstat);
195 }
196 #elif defined(HAVE_KSTAT)
197 kstat_ctl_t *kc = kstat_open();
198 kstat_t *ks;
199 kstat_named_t *kn;
200 if(kc) {
201 if((ks = kstat_lookup(kc, "unix", -1, "system_misc"))
202 && kstat_read(kc, ks, NULL) != -1
203 && (kn = kstat_data_lookup(ks, "ncpus")))
204 #ifdef KSTAT_DATA_UINT32
205 num_cpus = kn->value.ui32;
206 #else
207 num_cpus = kn->value.ul; /* needed in solaris <2.6 */
208 #endif
209 kstat_close(kc);
210 }
211 #elif defined(__USLC__)
212 num_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
213 #endif
214 if ( num_cpus <= 0 ) {
215 num_cpus = 1;
216 }
217 }
218 return num_cpus;
219 }
220
221 int X11_ResizeImage(_THIS, SDL_Surface *screen, Uint32 flags)
222 {
223 int retval;
224
225 X11_DestroyImage(this, screen);
226 if ( flags & SDL_OPENGL ) { /* No image when using GL */
227 retval = 0;
228 } else {
229 retval = X11_SetupImage(this, screen);
230 /* We support asynchronous blitting on the display */
231 if ( flags & SDL_ASYNCBLIT ) {
232 /* This is actually slower on single-CPU systems,
233 probably because of CPU contention between the
234 X server and the application.
235 Note: Is this still true with XFree86 4.0?
236 */
237 if ( num_CPU() > 1 ) {
238 screen->flags |= SDL_ASYNCBLIT;
239 }
240 }
241 }
242 return(retval);
243 }
244
245 /* We don't actually allow hardware surfaces other than the main one */
246 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
247 {
248 return(-1);
249 }
250 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
251 {
252 return;
253 }
254
255 int X11_LockHWSurface(_THIS, SDL_Surface *surface)
256 {
257 if ( (surface == SDL_VideoSurface) && blit_queued ) {
258 XSync(GFX_Display, False);
259 blit_queued = 0;
260 }
261 return(0);
262 }
263 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
264 {
265 return;
266 }
267
268 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
269 {
270 return(0);
271 }
272
273 /* Byte-swap the pixels in the display image */
274 static void X11_SwapAllPixels(SDL_Surface *screen)
275 {
276 int x, y;
277
278 switch (screen->format->BytesPerPixel) {
279 case 2: {
280 Uint16 *spot;
281 for ( y=0; y<screen->h; ++y ) {
282 spot = (Uint16 *) ((Uint8 *)screen->pixels +
283 y * screen->pitch);
284 for ( x=0; x<screen->w; ++x, ++spot ) {
285 *spot = SDL_Swap16(*spot);
286 }
287 }
288 }
289 break;
290
291 case 4: {
292 Uint32 *spot;
293 for ( y=0; y<screen->h; ++y ) {
294 spot = (Uint32 *) ((Uint8 *)screen->pixels +
295 y * screen->pitch);
296 for ( x=0; x<screen->w; ++x, ++spot ) {
297 *spot = SDL_Swap32(*spot);
298 }
299 }
300 }
301 break;
302
303 default:
304 /* should never get here */
305 break;
306 }
307 }
308 static void X11_SwapPixels(SDL_Surface *screen, SDL_Rect *rect)
309 {
310 int x, minx, maxx;
311 int y, miny, maxy;
312
313 switch (screen->format->BytesPerPixel) {
314 case 2: {
315 Uint16 *spot;
316 minx = rect->x;
317 maxx = rect->x + rect->w;
318 miny = rect->y;
319 maxy = rect->y + rect->h;
320 for ( y=miny; y<maxy; ++y ) {
321 spot = (Uint16 *) ((Uint8 *)screen->pixels +
322 y * screen->pitch + minx * 2);
323 for ( x=minx; x<maxx; ++x, ++spot ) {
324 *spot = SDL_Swap16(*spot);
325 }
326 }
327 }
328 break;
329
330 case 4: {
331 Uint32 *spot;
332 minx = rect->x;
333 maxx = rect->x + rect->w;
334 miny = rect->y;
335 maxy = rect->y + rect->h;
336 for ( y=miny; y<maxy; ++y ) {
337 spot = (Uint32 *) ((Uint8 *)screen->pixels +
338 y * screen->pitch + minx * 4);
339 for ( x=minx; x<maxx; ++x, ++spot ) {
340 *spot = SDL_Swap32(*spot);
341 }
342 }
343 }
344 break;
345
346 default:
347 /* should never get here */
348 break;
349 }
350 }
351
352 static void X11_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
353 {
354 int i;
355
356 /* Check for endian-swapped X server, swap if necessary (VERY slow!) */
357 if ( swap_pixels &&
358 ((this->screen->format->BytesPerPixel%2) == 0) ) {
359 for ( i=0; i<numrects; ++i ) {
360 if ( ! rects[i].w ) { /* Clipped? */
361 continue;
362 }
363 X11_SwapPixels(this->screen, rects + i);
364 XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
365 rects[i].x, rects[i].y,
366 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
367 X11_SwapPixels(this->screen, rects + i);
368 }
369 } else {
370 for ( i=0; i<numrects; ++i ) {
371 if ( ! rects[i].w ) { /* Clipped? */
372 continue;
373 }
374 XPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
375 rects[i].x, rects[i].y,
376 rects[i].x, rects[i].y, rects[i].w, rects[i].h);
377 }
378 }
379 if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
380 XFlush(GFX_Display);
381 ++blit_queued;
382 } else {
383 XSync(GFX_Display, False);
384 }
385 }
386
387 static void X11_MITSHMUpdate(_THIS, int numrects, SDL_Rect *rects)
388 {
389 #ifndef NO_SHARED_MEMORY
390 int i;
391
392 for ( i=0; i<numrects; ++i ) {
393 if ( ! rects[i].w ) { /* Clipped? */
394 continue;
395 }
396 XShmPutImage(GFX_Display, SDL_Window, SDL_GC, SDL_Ximage,
397 rects[i].x, rects[i].y,
398 rects[i].x, rects[i].y, rects[i].w, rects[i].h,
399 False);
400 }
401 if ( SDL_VideoSurface->flags & SDL_ASYNCBLIT ) {
402 XFlush(GFX_Display);
403 ++blit_queued;
404 } else {
405 XSync(GFX_Display, False);
406 }
407 #endif /* ! NO_SHARED_MEMORY */
408 }
409
410 /* There's a problem with the automatic refreshing of the display.
411 Even though the XVideo code uses the GFX_Display to update the
412 video memory, it appears that updating the window asynchronously
413 from a different thread will cause "blackouts" of the window.
414 This is a sort of a hacked workaround for the problem.
415 */
416 static int enable_autorefresh = 1;
417
418 void X11_DisableAutoRefresh(_THIS)
419 {
420 --enable_autorefresh;
421 }
422
423 void X11_EnableAutoRefresh(_THIS)
424 {
425 ++enable_autorefresh;
426 }
427
428 void X11_RefreshDisplay(_THIS)
429 {
430 /* Don't refresh a display that doesn't have an image (like GL) */
431 if ( ! SDL_Ximage || (enable_autorefresh <= 0) ) {
432 return;
433 }
434 #ifndef NO_SHARED_MEMORY
435 if ( this->UpdateRects == X11_MITSHMUpdate ) {
436 XShmPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
437 0, 0, 0, 0, this->screen->w, this->screen->h,
438 False);
439 } else {
440 #else
441 {
442 #endif /* ! NO_SHARED_MEMORY */
443 /* Check for endian-swapped X server, swap if necessary */
444 if ( swap_pixels &&
445 ((this->screen->format->BytesPerPixel%2) == 0) ) {
446 X11_SwapAllPixels(this->screen);
447 XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
448 0, 0, 0, 0, this->screen->w, this->screen->h);
449 X11_SwapAllPixels(this->screen);
450 } else {
451 XPutImage(SDL_Display, SDL_Window, SDL_GC, SDL_Ximage,
452 0, 0, 0, 0, this->screen->w, this->screen->h);
453 }
454 }
455 XSync(SDL_Display, False);
456 }