Added missing newline in NEDsim error message.
[screensavers] / screenhack / xshm.c
CommitLineData
3144ee8a
AT
1/* xscreensaver, Copyright (c) 1993-2017 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
9 * implied warranty.
10 */
11
12/* The MIT-SHM (Shared Memory) extension is pretty tricky to use.
13 This file contains the common boiler-plate for creating a shared
14 XImage structure, and for making sure that the shared memory segments
15 get allocated and shut down cleanly.
16
17 This code currently deals only with shared XImages, not with shared Pixmaps.
18 It also doesn't use "completion events", but so far that doesn't seem to
19 be a problem (and I'm not entirely clear on when they would actually be
20 needed, anyway.)
21
22 If you don't have man pages for this extension, see
23 https://www.x.org/releases/current/doc/xextproto/shm.html
24
25 (This document seems not to ever remain available on the web in one place
26 for very long; you can search for it by the title, "MIT-SHM -- The MIT
27 Shared Memory Extension".)
28
29 To monitor the system's shared memory segments, run "ipcs -m".
30 */
31
32#include "utils.h"
33
34/* #define DEBUG */
35
36#include <errno.h> /* for perror() */
37
38#ifdef HAVE_JWXYZ
39# include "jwxyz.h"
40#else
41# include <X11/Xutil.h> /* for XDestroyImage() */
42#endif
43
44#include "xshm.h"
45#include "resources.h" /* for get_string_resource() */
46#include "thread_util.h" /* for thread_malloc() */
47
48#ifdef DEBUG
49# include <X11/Xmu/Error.h>
50#endif
51
52extern char *progname;
53
54
55/* The documentation for the XSHM extension implies that if the server
56 supports XSHM but is not the local machine, the XShm calls will return
57 False; but this turns out not to be the case. Instead, the server
58 throws a BadAccess error. So, we need to catch X errors around all
59 of our XSHM calls, sigh.
60 */
61
62#ifdef HAVE_XSHM_EXTENSION
63
64static Bool shm_got_x_error = False;
65XErrorHandler old_handler = 0;
66static int
67shm_ehandler (Display *dpy, XErrorEvent *error)
68{
69 shm_got_x_error = True;
70
71#ifdef DEBUG
72 fprintf (stderr, "\n%s: ignoring X error from XSHM:\n", progname);
73 XmuPrintDefaultErrorMessage (dpy, error, stderr);
74 fprintf (stderr, "\n");
75#endif
76
77 return 0;
78}
79
80
81#define CATCH_X_ERROR(DPY) do { \
82 XSync((DPY), False); \
83 shm_got_x_error = False; \
84 if (old_handler != shm_ehandler) \
85 old_handler = XSetErrorHandler (shm_ehandler); \
86} while(0)
87
88#define UNCATCH_X_ERROR(DPY) do { \
89 XSync((DPY), False); \
90 if (old_handler) \
91 XSetErrorHandler (old_handler); \
92 old_handler = 0; \
93} while(0)
94
95#endif /* HAVE_XSHM_EXTENSION */
96
97
98static void
99print_error (int err)
100{
101 fprintf(stderr, "%s: %s\n", progname, strerror(err));
102}
103
104static XImage *
105create_fallback (Display *dpy, Visual *visual,
106 unsigned int depth,
107 int format, XShmSegmentInfo *shm_info,
108 unsigned int width, unsigned int height)
109{
110 XImage *image = XCreateImage (dpy, visual, depth, format, 0, NULL,
111 width, height, BitmapPad(dpy), 0);
112 shm_info->shmid = -1;
113
114 if (!image) {
115 print_error (ENOMEM);
116 } else {
117 /* Sometimes the XImage data needs to be aligned, such as for SIMD (SSE2
118 in Fireworkx), or multithreading (AnalogTV).
119 */
120 int error = thread_malloc ((void **)&image->data, dpy,
121 image->height * image->bytes_per_line);
122 if (error) {
123 print_error (error);
124 XDestroyImage (image);
125 image = NULL;
126 } else {
127 memset (image->data, 0, image->height * image->bytes_per_line);
128 }
129 }
130
131 return image;
132}
133
134
135XImage *
136create_xshm_image (Display *dpy, Visual *visual,
137 unsigned int depth,
138 int format, XShmSegmentInfo *shm_info,
139 unsigned int width, unsigned int height)
140{
141#ifndef HAVE_XSHM_EXTENSION
142
143 return create_fallback (dpy, visual, depth, format, shm_info, width, height);
144
145#else /* HAVE_XSHM_EXTENSION */
146
147 Status status;
148 XImage *image = 0;
149 if (!get_boolean_resource(dpy, "useSHM", "Boolean") ||
150 !XShmQueryExtension (dpy)) {
151 return create_fallback (dpy, visual, depth, format, shm_info,
152 width, height);
153 }
154
155 CATCH_X_ERROR(dpy);
156 image = XShmCreateImage(dpy, visual, depth,
157 format, NULL, shm_info, width, height);
158 UNCATCH_X_ERROR(dpy);
159 if (shm_got_x_error)
160 return create_fallback (dpy, visual, depth, format, shm_info,
161 width, height);
162
163#ifdef DEBUG
164 fprintf(stderr, "\n%s: XShmCreateImage(... %d, %d)\n", progname,
165 width, height);
166#endif
167
168 shm_info->shmid = shmget(IPC_PRIVATE,
169 image->bytes_per_line * image->height,
170 IPC_CREAT | 0777);
171#ifdef DEBUG
172 fprintf(stderr, "%s: shmget(IPC_PRIVATE, %d, IPC_CREAT | 0777) ==> %d\n",
173 progname, image->bytes_per_line * image->height, shm_info->shmid);
174#endif
175
176 if (shm_info->shmid == -1)
177 {
178 char buf[1024];
179 sprintf (buf, "%s: shmget failed", progname);
180 perror(buf);
181 XDestroyImage (image);
182 image = 0;
183 XSync(dpy, False);
184 }
185 else
186 {
187 shm_info->readOnly = False;
188 image->data = shm_info->shmaddr = shmat(shm_info->shmid, 0, 0);
189
190#ifdef DEBUG
191 fprintf(stderr, "%s: shmat(%d, 0, 0) ==> %d\n", progname,
192 shm_info->shmid, (int) image->data);
193#endif
194
195 CATCH_X_ERROR(dpy);
196 status = XShmAttach(dpy, shm_info);
197 UNCATCH_X_ERROR(dpy);
198 if (shm_got_x_error)
199 status = False;
200
201 if (!status)
202 {
203 fprintf (stderr, "%s: XShmAttach failed!\n", progname);
204 XDestroyImage (image);
205 XSync(dpy, False);
206 shmdt (shm_info->shmaddr);
207 image = 0;
208 }
209#ifdef DEBUG
210 else
211 fprintf(stderr, "%s: XShmAttach(dpy, shm_info) ==> True\n", progname);
212#endif
213
214 XSync(dpy, False);
215
216 /* Delete the shared segment right now; the segment won't actually
217 go away until both the client and server have deleted it. The
218 server will delete it as soon as the client disconnects, so we
219 should delete our side early in case of abnormal termination.
220 (And note that, in the context of xscreensaver, abnormal
221 termination is the rule rather than the exception, so this would
222 leak like a sieve if we didn't do this...)
223
224 #### Are we leaking anyway? Perhaps because of the window of
225 opportunity between here and the XShmAttach call above, during
226 which we might be killed? Do we need to establish a signal
227 handler for this case?
228 */
229 shmctl (shm_info->shmid, IPC_RMID, 0);
230
231#ifdef DEBUG
232 fprintf(stderr, "%s: shmctl(%d, IPC_RMID, 0)\n\n", progname,
233 shm_info->shmid);
234#endif
235 }
236
237 if (!image) {
238 return create_fallback (dpy, visual, depth, format, shm_info,
239 width, height);
240 }
241
242 return image;
243
244#endif /* HAVE_XSHM_EXTENSION */
245}
246
247
248Bool
249put_xshm_image (Display *dpy, Drawable d, GC gc, XImage *image,
250 int src_x, int src_y, int dest_x, int dest_y,
251 unsigned int width, unsigned int height,
252 XShmSegmentInfo *shm_info)
253{
254#ifdef HAVE_XSHM_EXTENSION
255 assert (shm_info); /* Don't just s/XShmPutImage/put_xshm_image/. */
256 if (shm_info->shmid != -1) {
257 /* XShmPutImage is asynchronous; the contents of the XImage must not be
258 modified until the server has placed the pixels on the screen and the
259 client has received an XShmCompletionEvent. Breaking this rule can cause
260 tearing. That said, put_xshm_image doesn't provide a send_event
261 parameter, so we're always breaking this rule. Not that it seems to
262 matter; everything (so far) looks fine without it.
263
264 ####: Add a send_event parameter. And fake it for XPutImage.
265 */
266 return XShmPutImage (dpy, d, gc, image, src_x, src_y, dest_x, dest_y,
267 width, height, False);
268 }
269#endif /* HAVE_XSHM_EXTENSION */
270
271 return XPutImage (dpy, d, gc, image, src_x, src_y, dest_x, dest_y,
272 width, height);
273}
274
275
276Bool
277get_xshm_image (Display *dpy, Drawable d, XImage *image, int x, int y,
278 unsigned long plane_mask, XShmSegmentInfo *shm_info)
279{
280#ifdef HAVE_XSHM_EXTENSION
281 if (shm_info->shmid != -1) {
282 return XShmGetImage (dpy, d, image, x, y, plane_mask);
283 }
284#endif /* HAVE_XSHM_EXTENSION */
285 return XGetSubImage (dpy, d, x, y, image->width, image->height, plane_mask,
286 image->format, image, 0, 0) != NULL;
287}
288
289
290void
291destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info)
292{
293#ifdef HAVE_XSHM_EXTENSION
294 Status status;
295
296 if (shm_info->shmid == -1) {
297#endif /* HAVE_XSHM_EXTENSION */
298
299 /* Don't let XDestroyImage free image->data. */
300 thread_free (image->data);
301 image->data = NULL;
302 XDestroyImage (image);
303 return;
304
305#ifdef HAVE_XSHM_EXTENSION
306 }
307
308 CATCH_X_ERROR(dpy);
309 status = XShmDetach (dpy, shm_info);
310 UNCATCH_X_ERROR(dpy);
311 if (shm_got_x_error)
312 status = False;
313 if (!status)
314 fprintf (stderr, "%s: XShmDetach failed!\n", progname);
315# ifdef DEBUG
316 else
317 fprintf (stderr, "%s: XShmDetach(dpy, shm_info) ==> True\n", progname);
318# endif
319
320 XDestroyImage (image);
321 XSync(dpy, False);
322
323 status = shmdt (shm_info->shmaddr);
324
325 if (status != 0)
326 {
327 char buf[1024];
328 sprintf (buf, "%s: shmdt(0x%lx) failed", progname,
329 (unsigned long) shm_info->shmaddr);
330 perror(buf);
331 }
332# ifdef DEBUG
333 else
334 fprintf (stderr, "%s: shmdt(shm_info->shmaddr) ==> 0\n", progname);
335# endif
336
337 XSync(dpy, False);
338
339#endif /* HAVE_XSHM_EXTENSION */
340}