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 */
34 static int shm_error;
35 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
shm_errhandler(Display * d,XErrorEvent * e)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
try_mitshm(_THIS,SDL_Surface * screen)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
X11_SetupImage(_THIS,SDL_Surface * screen)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
X11_DestroyImage(_THIS,SDL_Surface * screen)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 */
num_CPU(void)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
X11_ResizeImage(_THIS,SDL_Surface * screen,Uint32 flags)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 */
X11_AllocHWSurface(_THIS,SDL_Surface * surface)206 int X11_AllocHWSurface(_THIS, SDL_Surface *surface)
207 {
208 return(-1);
209 }
X11_FreeHWSurface(_THIS,SDL_Surface * surface)210 void X11_FreeHWSurface(_THIS, SDL_Surface *surface)
211 {
212 return;
213 }
214
X11_LockHWSurface(_THIS,SDL_Surface * surface)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 }
X11_UnlockHWSurface(_THIS,SDL_Surface * surface)223 void X11_UnlockHWSurface(_THIS, SDL_Surface *surface)
224 {
225 return;
226 }
227
X11_FlipHWSurface(_THIS,SDL_Surface * surface)228 int X11_FlipHWSurface(_THIS, SDL_Surface *surface)
229 {
230 return(0);
231 }
232
X11_NormalUpdate(_THIS,int numrects,SDL_Rect * rects)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
X11_MITSHMUpdate(_THIS,int numrects,SDL_Rect * rects)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
X11_DisableAutoRefresh(_THIS)284 void X11_DisableAutoRefresh(_THIS)
285 {
286 --enable_autorefresh;
287 }
288
X11_EnableAutoRefresh(_THIS)289 void X11_EnableAutoRefresh(_THIS)
290 {
291 ++enable_autorefresh;
292 }
293
X11_RefreshDisplay(_THIS)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