[ / main / writing / xshm ]

    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 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; 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.