28439c030b92c7041bd2e21b8cf37af5ab7e1ee4
[xscreensaver] / utils / xshm.c
1 /* xscreensaver, Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 2001, 2006
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.x.org/X11R6.8.1/docs/Xext/
25
26    (This document seems not to ever remain available on the web in one place
27    for very long; you can search for it by the title, "MIT-SHM -- The MIT
28    Shared Memory Extension".)
29
30    To monitor the system's shared memory segments, run "ipcs -m".
31   */
32
33 #include "utils.h"
34
35 #ifdef HAVE_XSHM_EXTENSION      /* whole file */
36
37 /* #define DEBUG */
38
39 #include <errno.h>              /* for perror() */
40 #include <X11/Xutil.h>          /* for XDestroyImage() */
41
42 #include "xshm.h"
43 #include "resources.h"          /* for get_string_resource() */
44
45 #ifdef DEBUG
46 # include <X11/Xmu/Error.h>
47 #endif
48
49 extern char *progname;
50
51
52 /* The documentation for the XSHM extension implies that if the server
53    supports XSHM but is not the local machine, the XShm calls will return
54    False; but this turns out not to be the case.  Instead, the server
55    throws a BadAccess error.  So, we need to catch X errors around all
56    of our XSHM calls, sigh.
57  */
58
59 static Bool shm_got_x_error = False;
60 XErrorHandler old_handler = 0;
61 static int
62 shm_ehandler (Display *dpy, XErrorEvent *error)
63 {
64   shm_got_x_error = True;
65
66 #ifdef DEBUG
67   fprintf (stderr, "\n%s: ignoring X error from XSHM:\n", progname);
68   XmuPrintDefaultErrorMessage (dpy, error, stderr);
69   fprintf (stderr, "\n");
70 #endif
71
72   return 0;
73 }
74
75
76 #define CATCH_X_ERROR(DPY) do {                         \
77   XSync((DPY), False);                                  \
78   shm_got_x_error = False;                              \
79   if (old_handler != shm_ehandler)                      \
80     old_handler = XSetErrorHandler (shm_ehandler);      \
81 } while(0)
82
83 #define UNCATCH_X_ERROR(DPY) do {                       \
84   XSync((DPY), False);                                  \
85   if (old_handler)                                      \
86     XSetErrorHandler (old_handler);                     \
87     old_handler = 0;                                    \
88 } while(0)
89
90
91 XImage *
92 create_xshm_image (Display *dpy, Visual *visual,
93                    unsigned int depth,
94                    int format, char *data,
95                    XShmSegmentInfo *shm_info,
96                    unsigned int width, unsigned int height)
97 {
98   Status status;
99   XImage *image = 0;
100   if (!get_boolean_resource(dpy, "useSHM", "Boolean"))
101     return 0;
102
103   if (!XShmQueryExtension (dpy))
104     return 0;
105
106   CATCH_X_ERROR(dpy);
107   image = XShmCreateImage(dpy, visual, depth,
108                           format, data, shm_info, width, height);
109   UNCATCH_X_ERROR(dpy);
110   if (shm_got_x_error)
111     return 0;
112
113 #ifdef DEBUG
114   fprintf(stderr, "\n%s: XShmCreateImage(... %d, %d)\n", progname,
115           width, height);
116 #endif
117
118   shm_info->shmid = shmget(IPC_PRIVATE,
119                            image->bytes_per_line * image->height,
120                            IPC_CREAT | 0777);
121 #ifdef DEBUG
122   fprintf(stderr, "%s: shmget(IPC_PRIVATE, %d, IPC_CREAT | 0777) ==> %d\n",
123           progname, image->bytes_per_line * image->height, shm_info->shmid);
124 #endif
125
126   if (shm_info->shmid == -1)
127     {
128       char buf[1024];
129       sprintf (buf, "%s: shmget failed", progname);
130       perror(buf);
131       XDestroyImage (image);
132       image = 0;
133       XSync(dpy, False);
134     }
135   else
136     {
137       shm_info->readOnly = False;
138       image->data = shm_info->shmaddr = shmat(shm_info->shmid, 0, 0);
139
140 #ifdef DEBUG
141       fprintf(stderr, "%s: shmat(%d, 0, 0) ==> %d\n", progname,
142               shm_info->shmid, (int) image->data);
143 #endif
144
145       CATCH_X_ERROR(dpy);
146       status = XShmAttach(dpy, shm_info);
147       UNCATCH_X_ERROR(dpy);
148       if (shm_got_x_error)
149         status = False;
150
151       if (!status)
152         {
153           fprintf (stderr, "%s: XShmAttach failed!\n", progname);
154           XDestroyImage (image);
155           XSync(dpy, False);
156           shmdt (shm_info->shmaddr);
157           image = 0;
158         }
159 #ifdef DEBUG
160       else
161         fprintf(stderr, "%s: XShmAttach(dpy, shm_info) ==> True\n", progname);
162 #endif
163
164       XSync(dpy, False);
165
166       /* Delete the shared segment right now; the segment won't actually
167          go away until both the client and server have deleted it.  The
168          server will delete it as soon as the client disconnects, so we
169          should delete our side early in case of abnormal termination.
170          (And note that, in the context of xscreensaver, abnormal
171          termination is the rule rather than the exception, so this would
172          leak like a sieve if we didn't do this...)
173
174          #### Are we leaking anyway?  Perhaps because of the window of
175          opportunity between here and the XShmAttach call above, during
176          which we might be killed?  Do we need to establish a signal
177          handler for this case?
178        */
179       shmctl (shm_info->shmid, IPC_RMID, 0);
180
181 #ifdef DEBUG
182       fprintf(stderr, "%s: shmctl(%d, IPC_RMID, 0)\n\n", progname,
183               shm_info->shmid);
184 #endif
185     }
186
187   return image;
188 }
189
190
191 void
192 destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info)
193 {
194   Status status;
195
196   CATCH_X_ERROR(dpy);
197   status = XShmDetach (dpy, shm_info);
198   UNCATCH_X_ERROR(dpy);
199   if (shm_got_x_error)
200     status = False;
201   if (!status)
202     fprintf (stderr, "%s: XShmDetach failed!\n", progname);
203 #ifdef DEBUG
204   else
205     fprintf (stderr, "%s: XShmDetach(dpy, shm_info) ==> True\n", progname);
206 #endif
207
208   XDestroyImage (image);
209   XSync(dpy, False);
210
211   status = shmdt (shm_info->shmaddr);
212
213   if (status != 0)
214     {
215       char buf[1024];
216       sprintf (buf, "%s: shmdt(0x%lx) failed", progname,
217                (unsigned long) shm_info->shmaddr);
218       perror(buf);
219     }
220 #ifdef DEBUG
221   else
222     fprintf (stderr, "%s: shmdt(shm_info->shmaddr) ==> 0\n", progname);
223 #endif
224
225   XSync(dpy, False);
226 }
227
228
229 #endif /* HAVE_XSHM_EXTENSION */