e14743d1 |
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 | |