The double buffer: double.c

The program double.c implements the double buffering tecnique to eliminate flickering of animation. Flickering is due to the fact that screen refresh may take place when we are changing the content of a window, but we have not finished yet. In the case of the bouncing ball, it may happen that the screen is refreshed while we have removed the ball from the old position, but have not yet finished redrawing it in the new position.

The double buffer is a memory area which is not directly mapped to the screen. When we want to make some change to the scene, we first modify this area, and then copy the whole area to the window. This way, a screen refresh may not take place while we are modifying the area.

In X Window, the double buffer can be implemented using pixmaps. A pixmap is simply a memory area that can be used to draw (using the very same functions used for windows). They can also be copied to window areas, and this is how we make them visible. First of all, we declare a variable double_buffer of type Pixmap.

  Pixmap double_buffer;
This simply creates a variable for representing a pixmap. In order to use the pixmap, it must be created. When creating a pixmap, we have to specify its size, as well as its depth. Since the double buffer is used as a draft canvas for the root window, size and depth are the same.
  /* create the double buffer */
  double_buffer = XCreatePixmap(dpy, root,
                  wa.width, wa.height, wa.depth);
Once a pixmap is created, its content is undefined (some interesting effects can be obtained by copying a newly created pixmap to some window). Since we want to display a red ball over a black background, we have to fill the pixmap with black. While windows have a definite default background color, and thus they can be cleared with XClearWindow, the same is not true for pixmaps. As a result, in order for the pixmap to be filled with black, we need to draw a filled rectangle as large as the pixmap.
  XSetBackground(dpy, g, BlackPixelOfScreen(DefaultScreenOfDisplay(dpy)));
  XFillRectangle(dpy, double_buffer, g, 0, 0, wa.width, wa.height);
The idea of double buffering is: when we want to modify the window, we do all needed changes to the double buffer, and then copy the buffer to the window only when the next shot is complete. In our case, to move to the next shot, we have to remove the ball, change the position, and draw the ball in the new position. Only at the end of these three operations we can copy the pixmap to the window.

Technically, to remove the ball from the old position, we cannot use XClearArea. We simply fill the area with black using the function XFillRectangle. This is how the double buffer is updated:

      /* remove the ball from the screen */
      XSetForeground(dpy, g, BlackPixelOfScreen(DefaultScreenOfDisplay(dpy)));
      XFillRectangle(dpy, double_buffer, g, x, y, 40, 40);

      /* change position */
      x+=dx;
      y+=dy;

      /* draw in the new position */
      XSetForeground(dpy, g, reds.pixel);
      XFillArc(dpy, double_buffer, g, x, y, 40, 40, 0, 360*64);
Note that so far the window root has not be changed at all: all changes have been made to the double_buffer pixmap. After this, the buffer is in a consistent state, that is, the ball is completely drawn in the new position. As a result, we can copy the buffer to the window. This is done using the function XCopyArea. This function can be used to copy either windows and pixmaps (see the man page for more details).
      /* copy the buffer to the window */
      XCopyArea(dpy, double_buffer, root, g, 0, 0, wa.width, wa.height, 0, 0);
This is the complete code: double.c. It is very similar to ball.c. Indeed, what changes is only that all drawing operations are made to the pixmap instead of the window, and the pixmap is copied to the window when it is completed.
Next: clipping