f3f32e05844bbd6d3661dbd9975115eb01fd92b9
[xscreensaver] / utils / xshm.c
1 /* xscreensaver, Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 2001
2  *  by Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* The MIT-SHM (Shared Memory) extension is pretty tricky to use.
14    This file contains the common boiler-plate for creating a shared
15    XImage structure, and for making sure that the shared memory segments
16    get allocated and shut down cleanly.
17
18    This code currently deals only with shared XImages, not with shared Pixmaps.
19    It also doesn't use "completion events", but so far that doesn't seem to
20    be a problem (and I'm not entirely clear on when they would actually be
21    needed, anyway.)
22
23    If you don't have man pages for this extension, see
24    http://www.physik.uni-regensburg.de/~scs22156/sofie-0.2/mit-shm.html
25    or in the R6 sources as "xc/doc/specs/Xext/mit-shm.ms", for example,
26    ftp://ftp.x.org/pub/R6.4/xc/doc/specs/Xext/mit-shm.ms
27
28    (This document seems not to ever remain available on the web in one place
29    for very long; you can search for it by the title, "MIT-SHM -- The MIT
30    Shared Memory Extension".)
31
32    To monitor the system's shared memory segments, run "ipcs -m".
33   */
34
35 #include "utils.h"
36
37 #ifdef HAVE_XSHM_EXTENSION      /* whole file */
38
39 /* #define DEBUG */
40
41 #include <errno.h>              /* for perror() */
42 #include <X11/Xutil.h>          /* for XDestroyImage() */
43
44 #include "xshm.h"
45 #include "resources.h"          /* for get_string_resource() */
46
47 #ifdef DEBUG
48 # include <X11/Xmu/Error.h>
49 #endif
50
51 extern char *progname;
52
53
54 /* The documentation for the XSHM extension implies that if the server
55    supports XSHM but is not the local machine, the XShm calls will return
56    False; but this turns out not to be the case.  Instead, the server
57    throws a BadAccess error.  So, we need to catch X errors around all
58    of our XSHM calls, sigh.
59  */
60
61 static Bool shm_got_x_error = False;
62 XErrorHandler old_handler = 0;
63 static int
64 shm_ehandler (Display *dpy, XErrorEvent *error)
65 {
66   shm_got_x_error = True;
67
68 #ifdef DEBUG
69   fprintf (stderr, "\n%s: ignoring X error from XSHM:\n", progname);
70   XmuPrintDefaultErrorMessage (dpy, error, stderr);
71   fprintf (stderr, "\n");
72 #endif
73
74   return 0;
75 }
76
77
78 #define CATCH_X_ERROR(DPY) do {                         \
79   XSync((DPY), False);                                  \
80   shm_got_x_error = False;                              \
81   if (old_handler != shm_ehandler)                      \
82     old_handler = XSetErrorHandler (shm_ehandler);      \
83 } while(0)
84
85 #define UNCATCH_X_ERROR(DPY) do {                       \
86   XSync((DPY), False);                                  \
87   if (old_handler)                                      \
88     XSetErrorHandler (old_handler);                     \
89     old_handler = 0;                                    \
90 } while(0)
91
92
93 XImage *
94 create_xshm_image (Display *dpy, Visual *visual,
95                    unsigned int depth,
96                    int format, char *data,
97                    XShmSegmentInfo *shm_info,
98                    unsigned int width, unsigned int height)
99 {
100   Status status;
101   XImage *image = 0;
102   if (!get_boolean_resource("useSHM", "Boolean"))
103     return 0;
104
105   if (!XShmQueryExtension (dpy))
106     return 0;
107
108   CATCH_X_ERROR(dpy);
109   image = XShmCreateImage(dpy, visual, depth,
110                           format, data, shm_info, width, height);
111   UNCATCH_X_ERROR(dpy);
112   if (shm_got_x_error)
113     return 0;
114
115 #ifdef DEBUG
116   fprintf(stderr, "\n%s: XShmCreateImage(... %d, %d)\n", progname,
117           width, height);
118 #endif
119
120   shm_info->shmid = shmget(IPC_PRIVATE,
121                            image->bytes_per_line * image->height,
122                            IPC_CREAT | 0777);
123 #ifdef DEBUG
124   fprintf(stderr, "%s: shmget(IPC_PRIVATE, %d, IPC_CREAT | 0777) ==> %d\n",
125           progname, image->bytes_per_line * image->height, shm_info->shmid);
126 #endif
127
128   if (shm_info->shmid == -1)
129     {
130       char buf[1024];
131       sprintf (buf, "%s: shmget failed", progname);
132       perror(buf);
133       XDestroyImage (image);
134       image = 0;
135       XSync(dpy, False);
136     }
137   else
138     {
139       shm_info->readOnly = False;
140       image->data = shm_info->shmaddr = shmat(shm_info->shmid, 0, 0);
141
142 #ifdef DEBUG
143       fprintf(stderr, "%s: shmat(%d, 0, 0) ==> %d\n", progname,
144               shm_info->shmid, (int) image->data);
145 #endif
146
147       CATCH_X_ERROR(dpy);
148       status = XShmAttach(dpy, shm_info);
149       UNCATCH_X_ERROR(dpy);
150       if (shm_got_x_error)
151         status = False;
152
153       if (!status)
154         {
155           fprintf (stderr, "%s: XShmAttach failed!\n", progname);
156           XDestroyImage (image);
157           XSync(dpy, False);
158           shmdt (shm_info->shmaddr);
159           image = 0;
160         }
161 #ifdef DEBUG
162       else
163         fprintf(stderr, "%s: XShmAttach(dpy, shm_info) ==> True\n", progname);
164 #endif
165
166       XSync(dpy, False);
167
168       /* Delete the shared segment right now; the segment won't actually
169          go away until both the client and server have deleted it.  The
170          server will delete it as soon as the client disconnects, so we
171          should delete our side early in case of abnormal termination.
172          (And note that, in the context of xscreensaver, abnormal
173          termination is the rule rather than the exception, so this would
174          leak like a sieve if we didn't do this...)
175
176          #### Are we leaking anyway?  Perhaps because of the window of
177          opportunity between here and the XShmAttach call above, during
178          which we might be killed?  Do we need to establish a signal
179          handler for this case?
180        */
181       shmctl (shm_info->shmid, IPC_RMID, 0);
182
183 #ifdef DEBUG
184       fprintf(stderr, "%s: shmctl(%d, IPC_RMID, 0)\n\n", progname,
185               shm_info->shmid);
186 #endif
187     }
188
189   return image;
190 }
191
192
193 void
194 destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info)
195 {
196   Status status;
197
198   CATCH_X_ERROR(dpy);
199   status = XShmDetach (dpy, shm_info);
200   UNCATCH_X_ERROR(dpy);
201   if (shm_got_x_error)
202     status = False;
203   if (!status)
204     fprintf (stderr, "%s: XShmDetach failed!\n", progname);
205 #ifdef DEBUG
206   else
207     fprintf (stderr, "%s: XShmDetach(dpy, shm_info) ==> True\n", progname);
208 #endif
209
210   XDestroyImage (image);
211   XSync(dpy, False);
212
213   status = shmdt (shm_info->shmaddr);
214
215   if (status != 0)
216     {
217       char buf[1024];
218       sprintf (buf, "%s: shmdt(0x%lx) failed", progname,
219                (unsigned long) shm_info->shmaddr);
220       perror(buf);
221     }
222 #ifdef DEBUG
223   else
224     fprintf (stderr, "%s: shmdt(shm_info->shmaddr) ==> 0\n", progname);
225 #endif
226
227   XSync(dpy, False);
228 }
229
230
231 #endif /* HAVE_XSHM_EXTENSION */