THE
MIT
SHARED
MEMORY
EXTENSION
©1998 Jeff Weeks and Codex software
Remember back to the ol' days of DOS when you wrote all your graphics data
to memory buffers and then copied them to video memory in one pass? Ever wonder
if you can do that in X Windows? Well, yes you can! The MIT Shared Memory
Extension (commonly refered to as XSHM) allows just that, and hopefully this
document can help you understand just how to do this.
The basic API to the XSHM extension is very must similar to the standard XImage
API provided by all X11 implementations. The difference here, however, is
how you obtain your XImage. First of all, though, you must query the X server
to see if it has the XSHM extension. You do so as follows:
Display *display;
int ignore, major, minor;
Bool pixmaps;
/* Check for the XShm extension */
if( XQueryExtension(display, "MIT-SHM", &ignore, &ignore, &ignore) ) {
if(XShmQueryVersion(display, &major, &minor, &pixmaps) == True) {
printf("XShm extention version %d.%d %s shared pixmaps\n",
major, minor, (pixmaps==True) ? "with" : "without");
}
else return 1;
}
Remember to #include <X11/extensions/XShm.h> for the above function
prototypes, as well as the standard X11 includes.
Okay, so now you know the XShm extension exists, how do we use it? Well,
first you must allocate an XImage. Typically you would use XCreateImage, but
sence we're using the extension, we use XShmCreateImage. The two functions
are almost exactly the same, but for one parameter.
Display *display;
Screen *screen;
XImage *image;
XShmSegmentInfo shminfo;
int x, y;
image = XShmCreateImage(display, DefaultVisualOfScreen(screen),
DefaultDepthOfScreen(screen),
ZPixmap, NULL, &shminfo,
x, y );
if(image == NULL) return 1;
That'll create a shared image x pixels wide, and y pixels high. It uses the
ZPixmap format, which is all you'll ever need. The only weird thing here
is that shminfo part. What's it all about? Well, to use shared memory you
must pass this shared memory info variable. It's definition is in <sys/shm.h> so be
sure to #include it.
Now, we haven't done anything to shminfo so it's about time we filled it in with the
vital info, and allocated some shared memory. This is done as follows:
/* Get the shared memory and check for errors */
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line*image->height,
IPC_CREAT | 0777 );
if(shminfo.shmid < 0) return 1;
That should be fairly easy to understand. Our XImage structure has already
been filled in with bytes_per_line and height info and so we can multiply
them together to figure out how much memory to get. The IPC variables are
just to tell shmget how to get our memory, and what permissions to give it.
The above should work fine in all cases. However, we need yet another
include file here; <sys/ipc.h> Remember to #include that one too.
Okay, we're still not done though. We've allocated our memory, but the system
still requires us to attach it. We actually end up attaching the memory
twice; Once to the system, once to X11. Here's what it looks like:
/* attach, and check for errrors */
shminfo.shmaddr = image->data = (char *)shmat(shminfo.shmid, 0, 0);
if(shminfo.shmaddr == (char *) -1) return 1;
/* set as read/write, and attach to the display */
shminfo.readOnly = False;
XShmAttach(display, &shminfo);
Now we're finally done! We have our very own shared memory XImage. So how
do we use it? That's simple enough. You just assigned an address to image->data.
That's your memory buffer. You can compose your graphics in that memory space
and then copy it to the display as follows:
XShmPutImage(display, window, gc, image, 0, 0, 0, 0, img->width, img->height, False);
Again, very similar to XPutImage, but with one difference; The last variable
specifies wether you want an event to be generated when XShmPutImage is
finished. You see, XShmPutImage works in the background, so you can continue
doing whatever, while it copies your graphics to the display. If you want to
know exactly when XShmPutImage is finished, specify True. If you don't care,
just specify False. One complication of specifying True, however, is calculating
the event type of the completion event; It's not constant. You can calculate
it as follows:
/* The event type used to tell if X11 is finished drawing... */
CompletionType = XShmGetEventBase(display) + ShmCompletion;
Now, instead of copying the image to the display, you can also copy the display
to the image. You do so as follows:
XShmGetImage(display, window, image, 0, 0, plane_mask);
The plane_mask variable defines which planes to read in. Usually you'd just
read every plane, and therefore pass 0xFFFFFFFF.
Finally, how do you clean up after all this? Well, you must detach your
memory (twice again), remove your XImage from memory, and do whatever other
typical clean up functions you must do. Here's the XShm specific clean up:
XShmDetach(display, &shminfo);
XDestroyImage(image);
shmdt(shminfo.shmaddr);
Well, that's it folks. I hope you use this knowledge to create more excellent
X11 programs instead of following the microsoft trend. I would recommend
looking through the man pages of some of the functions I've introduced to get a
greater understanding of what each parameter does. It seems, however, that
the XShm style functions don't have man pages, and so you must look up the
regular functions such as XPutImage and XGetImage. For the most part they
are exactly the same as their XShm counter parts.