Redrawing contents of windows

The module resizewindow.c works fine as long as the window is not covered or reduced to icon. When this happens, part of all the window becomes invisible, and when it is make visible again, its content is deleted. This page tells why part (or all) of the window is erased, and how this can be prevented.

Windows of applications users normally use are not deleted when covered or reduced to icon, while this happens to our modules. Is X unfair?

What happens in fact is that, whenever part of the window is made not visible to the screen (either because there is another window over it, or the window is reduced to icon), its content is deleted. This happens for all applications, including xterm and any other X application. This effect is not perceived for these applications simply because, when the window is made visible again, the part that has been erased is drawn again. Since this is done (hopefully) quickly, the user do not realize that the window content has been drawn again.

Any X application can do this, including the xscreensaver modules. In particular, we check whether the window has been made visible, and redraw the whole content if it has. This is somewhat an overkill, as only part of the window may require redrawing. However, it is a great simplification.

In order for redrawing the content of a window, a way for saving it is needed. There are two possible ways: first, remember which drawings have been made; second, store the state of the window in a pixmap. A pixmap is a memory area that can be used for drawing, using almost all the same functions. However, what is drawn in the pixmap is not displayed in any part of the screen. Another way to say this is that pixmaps are off-line resources. The only way to make the content of a pixmap visible is to copy it into a window.

We use the following strategy: we allocate a pixmap that is as large as the window, and draw into it the same things we draw into the window. This way, we are guaranteed that it is always identical to the window. Each time the window is made visible, we copy the pixmap into the window. This restore the original content of the window, that is, it draws everything that was in the window before it was covered.

Two variables are needed: one is the pixmap, the other one is an event.

  Pixmap double_buffer;

  XEvent e;

After the window is created, the pixmap can be created as well.

  /* create the double buffer */
  double_buffer = XCreatePixmap(dpy, w,
                  wa.width, wa.height, wa.depth);
  XSetForeground(dpy, g, BlackPixelOfScreen(DefaultScreenOfDisplay(dpy)));
  XFillRectangle(dpy, double_buffer, g, 0, 0, wa.width, wa.height);
  XCopyArea(dpy, double_buffer, w, g, 0, 0, wa.width, wa.height, 0, 0);

Note that pixmaps cannot be cleared with XClearArea. Using a pixmap that has not be cleared with XFillRectangle may give some interesting effects.

Since we want to redraw the window whenever it is made visible, and this corresponds to Expose events, we tell the X server to send us events of this type.

  /* we want expose events */
  XSelectInput(dpy, w, ExposureMask);

Since we want the pixmap and the window to have the same content, each time we do something to the window, we do the same on the pixmap. Note that XClearArea cannot be used on the pixmap, so we use XFillRectangle instead.

      /* draw in the window and in the double buffer */
      XSetForeground(dpy, g, xcolors[random()%NCOLORS].pixel);
      XFillRectangle (dpy, w, g, x, y, 50, 40);
      XFillRectangle (dpy, double_buffer, g, x, y, 50, 40);


      /* once in a while, clear all */
      if( random()%500<1 ) {
        XClearWindow(dpy, w);
        XSetForeground(dpy, g, BlackPixelOfScreen(DefaultScreenOfDisplay(dpy)));
        XFillRectangle(dpy, double_buffer, g, 0, 0, wa.width, wa.height);
      }

Finally, the window has to be redrawn whenever an Expose event (which may indicate that some part of the window that was previously covered is now visible) is received.

      /* check events */
      if( XCheckWindowEvent(dpy, w, ExposureMask, &e) ) {
        while( XCheckWindowEvent(dpy, w, ExposureMask, &e) );
        XCopyArea(dpy, double_buffer, w, g,
                   0, 0, wa.width, wa.height, 0, 0);
      }

Complete code: recopywindow.c.


Next: permanent changes on the background.