# Using Xlib shared memory extension What is M.I.T shared memory extension ? Basically, this is some ximages functions that use the SYS V shared memory primitives to provide an interesting facility. It permits to store image data in a shared memory segment, and then datas need not to be move through the Xlib interprocess communication channel as it works with the classics Xlib calls. Sadly, the shared memory extension isn't provided by all X servers. You can use xdpyinfo(1) to know if your server supports the extension. As your system need the SYS V shared memory to be available and enabled, it's good to use a series of #define, #ifdef and #endif to prevent compilation errors. Well, if you have decided to use it you need some header files: /* comment this line to compile without MIT Shared Memory extension. */ #define __USE_X_SHAREDMEMORY__ #ifdef __USE_X_SHAREDMEMORY__ #include #include #include #endif The use of the __USE_X_SHAREDMEMORY__ define, in the XWindow structure described in the beginning, should be now enough clear for you. If you use the shared memory extension, it's safe to test if the server support it (it's important if your binaries have to work on other environments), that's why I put the videoaccesstype field in my XWindow structure: #define VIDEO_XI_STANDARD 0x00 /* Use standard Xlib calls */ #define VIDEO_XI_SHMSTD 0X01 /* Use Xlib shared memory extension */ #ifdef _USE_X_SHAREDMEMORY__ int ShmMajor,ShmMinor; Bool ShmPixmaps; #endif xw->accesstype=VIDEO_XI_STANDARD; #ifdef _USE_X_SHAREDMEMORY__ if(XShmQueryVersion(XWnd->display,&ShmMajor,&ShmMinor,&ShmPixmaps)) XWnd->videoaccesstype=VIDEO_XI_SHMSTD; #endif $ man XShmQueryVersion Status XShmQueryVersion(display, major, minor, pixmaps) Display *display; /* Specifies the connection to the X server */ int *major, *minor; /* major.minor version of the implementation */ Bool *pixmaps; /* return True if shared memory pixmaps are supported. See section 5 - shared memory pixmaps for more informations. */ This function return 1 or 0, if shared memory ximages are available or not. Okay, we suppose the server supports shared memory, we can now go through the shared memory ximage intialisation proccess. For that purpose we need to allocate memory for the shared memory segment structure, and then create an ximage using the XShmCreateImage function: if(xw->videoaccesstype==VIDEO_XI_SHMSTD) { xw->shmseginfo=(XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo)); if(!xw->shmseginfo) return ERROR; memset(xw->shmseginfo,0, sizeof(XShmSegmentInfo)); xw->ximage=XShmCreateImage(xw->display, xw->visual, xw->depth, ZPixmap, NULL,xw->shmseginfo, xw->width, xw->height); if(!xw->ximage) return ERROR; } else { ... /* Put here the classic ximage initialisation we've previously seen */ ... } $ man XShmCreateImage XImage *XShmCreateImage (display, visual, depth, format, data, shminfo, width, height) Display *display; Visual *visual; unsigned int depth, width, height; int format; char *data; XShmSegmentInfo *shminfo; /* shared memory segment */ The XShmCreateImage is slightly different from the original Xlib function. Here we don't have bitmap_pad and bytes_per_line parameters, but XShmSegmentInfo *shminfo appear. That's the shared memory segment which will contain our direct buffer access, that's why we need not to give an entry for the char *data parameter (and because the shared memory buffer doesn't exist for the moment). Now, we can setup our shared memory segment (of course the following part of code must be put in the right place in the if condition, after the XShmCreateImage function): /* we firstly create our shared memory segment with the size we need, and correct permissions for the owner, the group and the world --> 0777 */ xw->shmseginfo->shmid=shmget(IPC_PRIVATE, xw->ximage->bytes_per_line* xw->ximage->height,IPC_CREAT|0777); if(xw->shmseginfo->shmid<0) return ERROR; /* Then, we have to attach the segment to our process, and we let the function search the correct memory place --> NULL. It's safest ! */ xw->shmseginfo->shmaddr=shmat(xw->shmseginfo->shmid,NULL,0)); if(!xw->shmseginfo->shmaddr) return ERROR; /* We set the buffer in Read and Write mode */ xw->shmseginfo->readOnly=False; You should read books and use man to learn more about SYS V IPC functions such as shmget and shmat (It's not the goal of this document). [AT&T 89] After having correctly create and set the shared memory segment, the next step is to inform the XImage structure and then to attach the segment to the window's display: xw->virtualscreen=(unsigned char *)(xw->ximage->data = xw->shmseginfo->shmaddr); if(!XShmAttach(xw->display,xw->shmseginfo)) return ERROR; $ man XShmAttach Status XShmAttach(display, shminfo) Display *display; XShmSegmentInfo *shminfo; Our shared memory ximage is now available, and we should use these few lines to draw it in the window when we need it: XShmPutImage(xw->display, xw->window, xw->gc, xw->ximage, 0, 0, 0, 0, xw->width, xw->height, False); XFlush(xw->display); & man XShmPutImage Status XShmPutImage (display, d, gc, image, src_x, src_y, dest_x, dest_y, width, height, send_event) Display *display; Drawable d; GC gc; XImage *image; int src_x, src_y, dest_x, dest_y; unsigned int width, height; Bool send_event; /* send completion event if True */ The parameters for XShmPutImage are basically the same as the classic XPutImage function. There's only one extra parameter, which is send_event. If you put True in this field, the server will generate a completion event [CORB R5] which informs your program that it is now safe to manipulate the memory again. We need not this event because we use the XFlush(display) function that flushes the output buffer. Then, we are sure the memory can be manipulate safely again. Now, to destroy the window and the shared memory ximage, we have to detach the shared memory segment from the display, destroy the ximage, and destroy the memory segment. After that process, we just need to use common terminate graphic functions: XShmDetach(xw->display,xw->shmseginfo); if(xw->ximage) XDestroyImage(xw->ximage); shmdt(xw->shmseginfo->shmaddr); shmctl(xw->shmseginfo->shmid,IPC_RMID,NULL); free(xw->shmseginfo); XFreeGC(xw->display,xw->gc); XDestroyWindow(xw->display,xw->window); XCloseDisplay(xw->display); free(xw); # Another method: Shared memory pixmaps It's another side of the M.I.T shared memory extension. I think it is interesting to know it is available. Shared memory pixmaps are privileged in my code, but I'm not sure that it is better than shared memory ximages.. Let's use it. We must add a new video access type: #define VIDEO_XI_SHMPIXMAP 0x02 /* Use shared memory pixmaps */ We have seen the XShmQueryVersion function in the previous section that informs us if the server supports both shared memory extension, and shared memory pixmaps. But we need one more important information. Shared memory pixmaps support only one format (check this paragraph in section 3 for more infos) for data storage depending on your server. I suppose you use ZPixmap format in your program, we can now make a complete detection using XShmPixmapFormat: xw->videoaccesstype=VIDEO_XI_STANDARD; #ifdef __USE_X_SHAREDMEMORY__ if(XShmQueryVersion(xw->display,&XShmMajor,&XShmMinor,&XShmPixmaps)) if(XShmPixmaps==True && XShmPixmapFormat(xw->display)==ZPixmap) xw->videoaccesstype=VIDEO_XI_SHMPIXMAP; else xw->videoaccesstype=VIDEO_XI_SHMSTD; #endif After that, we just need to initialize our pixmap structure, that is a bit same as shared memory ximages: switch(xw->videoaccesstype) { #ifdef __USE_X_SHAREDMEMORY__ case VIDEO_XI_SHMPIXMAP: xw->shmseginfo=(XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo)); if(!xw->shmseginfo) return ERROR; memset(xw->shmseginfo,0,sizeof(XShmSegmentInfo)); xw->shmseginfo->shmid=shmget(IPC_PRIVATE, xw->screensize,IPC_CREAT|0777); if(xw->shmseginfo->shmid<0) return ERROR; xw->videomemory=(unsigned char *)(xw->shmseginfo->shmaddr= shmat(xw->shmseginfo->shmid,0,0)); xw->virtualscreen=xw->videomemory; xw->shmseginfo->readOnly=False; if(!XShmAttach(xw->display,xw->shmseginfo)) return ERROR; xw->pixmap=XShmCreatePixmap(xw->display,xw->window, xw->videomemory,xw->shmseginfo, xw->width,xw->height,xw->depth); XSetWindowBackgroundPixmap(xw->display,xw->window,xw->pixmap); break; case VIDEO_XI_SHMSTD: ... /* Put here the shared memory ximage initialization (section 4) */ ... break; #endif /* __USE_X_SHAREDMEMORY__ */ case VIDEO_XI_STANDARD: ... /* Put here the classic ximage initialisation (section 3) */ ... break; } $ man XShmCreatePixmap Pixmap XShmCreatePixmap(display, d, data, shminfo, width, height, depth); Display *display; Drawable d; char *data; /* Pointer to the video buffer */ XShmSegmentInfo *shminfo; /* ptr to shared memory segment */ unsigned int width, height, depth; Using, the pixmap is very simple. You still have access to the video buffer throught xw->virtualscreen for your effects, then you can make it appear in the window using: XClearWindow(xw->display,xw->window); XSync(xw->display,FALSE); /* act as XFlush and wait for all requests to be received and processed by the X server */ That's it! Now, if you need to destroy a shared memory pixmap buffer, it is same as shared memory ximage, plus the pixmap deallocation proccess using XFreePixmap XShmDetach(XWnd->display,XWnd->shmseginfo); XFreePixmap(xw->display,xw->pixmap); if(xw->shmseginfo->shmaddr) shmdt(xw->shmseginfo->shmaddr); if(xw->shmseginfo->shmid>=0) shmctl(xw->shmseginfo->shmid,IPC_RMID,NULL); free(xw->shmseginfo); XFreeGC(xw->display,xw->gc); XDestroyWindow(xw->display,xw->window); XCloseDisplay(xw->display); # Events management Events management is not so difficult. Just put an infinite loop in your main() function such as: int InfiniteLoop=1; while(InfiniteLoop) { ... /* cool stuffs here :) */ } or for(;InfiniteLoop;) { ... } Then you must put series of little functions in the infinite loop, but it depends on what your program have to do. For a classic X program this should be good to use (as an example): while(InfiniteLoop) { XEvent ev; long KEY; XNextEvent(xw->display,&ev); switch(ev.type) { case KeyPress: KEY=XLookupKeysym((XKeyEvent *)&ev,0); switch(KEY) { case XK_Escape: InfiniteLoop=FALSE; break; case XK_KP_Enter: case XK_Return: XDrawString(xw->display,xw->window,xw->gc,50,50,"Hello",5); break; } break; case ButtonPress: InfiniteLoop=FALSE; break; } } for demos you should use something like: while(InfiniteLoop) { XEvent ev; long KEY; while(XPending(xw->display)) { XNextEvent(xw->display,&ev); switch(ev.type) { case KeyPress: KEY=XLookupKeysym((XKeyEvent *)&ev,0); switch(KEY) { case XK_Escape: InfiniteLoop=FALSE; break; } break; case ButtonPress: InfiniteLoop=FALSE; break; } } KewlHeavyEffect(xw); PutXImage(xw); } Okay, I must inform you that you can found some key definitions (XK_Escape...) in /usr/X11/include/X11/keysymdef.h. And, notice that XPending and XNextEvent flushes the output buffer, then we don't need to use XFlush or XSync in our PutXImage function. # Download examples * A little classic X Window example, with standard events management. [ DOWNLOAD ] * A bump effect, that uses the best direct video access, depending on your system. [ DOWNLOAD ] Each archive contains an example of timer programming. # Bibliography http://nobug.ifrance.com/nobug2/article1/babyloon/tut_xwin.htm XSelectInput(display, wnd, ExposureMask); GC gc = ::XCreateGC(display, wnd, 0, 0); XCopyPlane