jim@dandelion.CI.COM (Jim Fulton) (11/04/87)
Recently, a terse discussion on the XTENSIONS mailing list described how the X10 notion of grabbing bits off the RootWindow (e.g. for hardcopy or popping temporary windows) no longer has any meaning under X11. To help myself sort things out I put together the following note describing how retrieving bits from the screen has changed from X10 to X11. I'm posting it in hopes that other people who are making the transition to X11 might find it useful. Under X10, two of the more common uses of grabbing arbitrary regions of pixels out of the frame buffer include generating hardcopy and saving the screen underneath pop-up menus (or other temporary graphics). Although the former operation pulls the bits back to the client program and the latter leaves them in the server, both operations are commonly implemented by calling the appropriate Xlib routine (XPixmapGet{XY,Z} or XPixmapSave) with the RootWindow as the window argument. However, as has been pointed out, this approach is no longer guaranteed to work under X11. This is due to the fact that X11 windows have "depth" as well width and height. This attribute, called a "Visual", is how X11 provides coherence among the various color models (such as PseudoColor in which pixels values are indirected through a color lookup table before being sent to the electron guns, and TrueColor in which pixels values directly control the guns). What makes this somewhat tricky is that a display may support more than one type of visual at a time. X11 allows EACH WINDOW on a display to select the visual to be used for the pixels that it contains. Unlike clipping, this choice is not constrained by the window hierarchy: a window may choose to inherit its parent's visual (by passing CopyFromParent as the visual argument to XCreateWindow), but it is not required. Most displays probably will only support windows of one depth and color model (which is different from the requirement that all servers support single plane pixmaps, i.e. bitmaps). However, consider a sophisticated, deep frame buffer (say, 32 bits per pixels) that supports 24 bit TrueColor windows for image rendering applications, as well as 16 bit PseudoColor windows for applications such as CAE or CAD that like to play colormap games. For the sake of discussion, let us assume that we have one of each of these windows arranged as follows: +----------------------------------------+ | RootWindow | | | | +---------------+ | | | 24 bits deep, | | | | TrueColor | | | | | +---------------+ | | | | | 16 bits deep, | | | | Image | | PseudoColor | | | +---------------+ | | | | | MCAE | | | +---------------+ | | | +----------------------------------------+ An interesting question is: what is the type of the RootWindow? It can't be both TrueColor and PseudoColor at the same time. In fact, it could even be of yet a third type. Furthermore, if we do graphics on the RootWindow that we choose not to have clipped by the top-level windows (its children), what should appear on the screen? Under X10 there isn't any confusion since all windows are of the same type; under X11, there is. For example, if we do an XGetImage to retrieve the bits from the whole screen (to send to a printer, for example), what values should we get for pixels in windows that are not of the same type as the RootWindow? Unfortunately, we can't just assume that the pixels on the screen can be converted to the appropriate type without losing information. In the above diagram, if the RootWindow were a 16 bit PseudoColor window, then pixels from the Image window would have to drop 8 bits of data per pixel. This is why the X11 protocol says that doing graphics "on a window of one depth with mapped inferiors of differing depth is not illegal, but the semantics is undefined by the core protocol" (p. 52 under ClipByChildren in the CreateGC section). This is not just to cut hardware designers some slack, but rather to acknowledge that in many situations there isn't a uniform "correct" behavior. One might think that if the RootWindow were of the maximum possible depth then we'd be able to grab the bits and put them back without disturbing anything. Unfortunately, this does not guarantee the right results since there may be different modes for reading and writing pixels in the different types of windows. In addition, there might be additional state information stored with each pixel that isn't part of the "visible" bit planes (in our hypothetical 32 bit display, we have at least 8 extra bits per pixel that can be used for other things). Just extracting the visible bit planes may not give you enough information to allow the screen to be fully restored. This potential loss of information is what prevents the X10 techniques for grabbing pixmaps off the RootWindow from working properly under X11. As a reminder, both the protocol spec (p. 61) and the Xlib manual (p. 108) have a caveat under the description of XGetImage that states that the routine is only for "rudimentary" hardcopy support. However, X11 does have a very nice solution for saving what is underneath pop-up menus. In X10, if you wanted to do temporary graphics you would generally write something along the lines of: saved_area_pixmap = do_clipped_pixmap_save (RootWindow, x, y, width, height); saved_area_dirty = (!saved_area_pixmap); XConfigureWindow (tmpw, x, y, width, height); XMapWindow (tmpw); do_temporary_graphics (); if (saved_area_dirty) { XUnmapWindow (tmpw); /* generate an expose event */ } else { XUnmapTransparent (tmpw); /* be sneaky */ XPixmapPut (RootWindow, 0, 0, x, y, width, height, saved_area_pixmap, GXcopy, AllPlanes); } if (saved_area_pixmap) XFreePixmap (saved_area_pixmap); where do_clipped_pixmap_save clips the requested region to the boundaries of the screen so that an Error event isn't generated. Even if you wrap XGrabServer/XUngrabServer calls around the whole mess to prevent other clients from writing to the screen, the do_temporary_graphics routine could be rather complicated and cause the application to draw in some of the windows that are obscured. Then, when the saved pixmap is restored, the screen becomes invalid. If this happens, we're forced to generate the very refresh we were trying to avoid. In English, the pseudocode above tries to do the following: Before making the temporary graphics window visible, save the pixels that it would obscure. Then, when the window is moved or unmapped, restore the exposed pixels, if possible, or else generate Expose events. One would expect that this function is something that would be best handled by the server since it already has all of the appropriate information. That is why each window in X11 has a "save-unders" attribute that indicates whether or not the server should try to stash away areas of the screen that the window obscures. There is an important distinction between save-unders regions and images that are retrieved using XGetImage: save-unders store the "whole" pixels, not just the "visible" portions. This ensures that there is enough information to allow the server to properly restore the screen. Furthermore, if the server decides that it can't do the save-under (perhaps because the server ran out of offscreen memory or some application drew into the regions), Expose events will be generated automatically. If we assume that the save-unders and override-redirect attributes are set on the temporary window (the latter is to let the server know that it should bypass any window manager that would normally intercept any window manipulation requests), the pseudocode shown above can be reduced in X11 to: XMoveResizeWindow (dpy, tmpw, x, y, width, height); /* or merge */ XMapRaised (dpy, tmpw); /* into one XConfigureWindow call */ do_temporary_graphics (); XUnmapWindow (dpy, tmpw); At this point, either the screen will have been repainted from the save-unders, or else Expose events will have been generated for all of the appropriate windows. Unfortunately, save-unders are not implemented in the X.V11R1 sample server. They require hooks into the ddX layer (for saving the pixels) as well as the rectangle management code (for invalidating saved regions on servers that don't understand drawing into obscured regions). Until someone implements save-unders (as the various vendors undoubtedly will), applications will just have to deal with extra expose events. Anyway, I hope that this note can be of use to other members of the X community who are switching over from X10 to X11. Jim Fulton Cognition Inc. uucp: ...!{mit-eddie,talcott,necntc}!dandelion!jim 900 Tech Park Drive domain: jim@dandelion.ci.com, jim@athena.mit.edu, Billerica, MA 01821 fulton@eddie.mit.edu (617) 667-4800