Graphics Effects by Manipulating X Colormaps
Published in The X Journal, May, 1992.
Copyright © 1992 Kenton Lee. All rights reserved.
Key words: X Window System, X11, Motif widgets, graphics, colormap, programming.
Abstract
Most X Window System programs use some color graphics. Most of these
graphics are generated by traditional raster manipulation techniques.
Raster manipulation is easily understood but it, unfortunately, doesn't
always give satisfactory results. This tutorial presents some
alternatives based on colormap manipulation. Colormap
manipulation techniques can be used to supplement, and sometimes
replace, raster manipulation techniques. Depending on the application,
colormap manipulation may significantly improve performance and/or
prevent the undesirable graphical artifacts.
Contents
Most X Window System programs use some color graphics. Most of these
graphics are generated using traditional raster manipulation techniques
(i.e., Xlib drawing functions). With these techniques, if the
drawing needs to be changed, all or part of the old drawing
is erased and new pieces are drawn.
Raster manipulation is easily understood but it, unfortunately, doesn't
always give satisfactory results. This tutorial presents some
alternatives based on colormap manipulation. Colormap
manipulation techniques can be used to supplement, and sometimes
replace, raster manipulation techniques. Depending on the application,
colormap manipulation may significantly improve performance and/or
prevent the undesirable graphical artifacts. To fully understand this
material, you should already understand the raster manipulation
techniques.
The two most useful colormap manipulation techniques are dynamic
overlay planes and multi-buffered animation. These
will be discussed in detail, with example Xlib code. Other techniques
are possible, again using standard Xlib functions, and we mention some
of them. None of these techniques are new to the computer graphics
community, but they may not be well known to X programmers. Most of
the techniques were first developed for the interactive CAD systems of
the 1970s.[Blinn, Shoup]
Before we get into the code, however, let's review two key X Window
System concepts: visual types and colormaps.
X supports many display hardware capabilities. Graphics applications
should use the X visual type mechanism to query and select
the graphics model most appropriate for your application and hardware.
The PseudoColor visual type with a depth of 8 bits per pixel
is probably the most popular visual type and the examples in this paper
will assume that are using this visual type and that you have specified
it correctly. The examples may be easily extended to other visual
types with writable colormaps: PseudoColor with other
depths, GrayScale, or DirectColor.
If you need more information, [Lemke] is an
excellent tutorial on using visual types.
Most popular texts on computer graphics (e.g.,
[Foley]) or the X Window System (e.g.,
[Scheifler]) discuss colormaps in detail,
so here we present only a brief summary of PseudoColor
colormaps.
The PseudoColor model of the graphics display contains
writable raster memory and a writable colormap. Raster memory,
including the frame buffer, contains a certain number of bits (usually
8) for each pixel displayed on the screen. In X, the values of these
bits are called pixel values.
The colormap is a small table with entries specifying the
RGB values of the currently available colors. In X, the colormap
entries are called color cells. The pixel values are
interpreted as indices to the color cells. Thus, to refresh the
screen, the display hardware uses the RGB values in the color cells
indexed by the pixel values. The size of the colormap is less than or
equal to 2^N color cells, where N is the
number of bits in a pixel value. A device with 8 bits per pixel value,
for example, will have a colormap with up to 256 color cells, indexed
though pixel values ranging from 0 through 255.
X provides three ways to access colormaps: shared color cells,
standard colormaps, and private color cells. The shared color cell
approach is the easiest to use: cells are allocated once with
read-only RGB values. The standard colormap approach is a special case
of the shared color cells approach, allocating a large number of
pre-defined sharable color cells for clients with similar needs. Both
the shared color cells and standard colormap approaches conserve color
cell resources and should be used whenever
possible.[Scheifler, Jones]
There are some powerful graphics techniques, however, that require
writable, private color cells. The techniques and examples discussed
in this tutorial fall in the third category.
To draw color graphics using the private color cells approach, you
first allocate the needed color cells and store the desired RGB values
in the color cells. The XAllocColorCells() Xlib function
allocates the color cells. Any of XStoreColor(),
XStoreColors(), and XStoreNamedColor() can
store RGB values in the color cells. Then you can create graphics
contexts which use the created colors as foreground or background pixel
values, possibly modified by GC functions or plane masks. Finally, you
use the graphics contexts to draw into the windows, using rendering
functions such as XDrawLine() and
XPutImage(). When your display refreshes the screen, it
uses the RGB value in the color cell associated with each visible pixel
value.
In many cases, you can generate similar graphics effects by either
changing the pixel values (raster manipulation) or by changing the RGB
values stored in the color cells (colormap manipulation). The standard
X drawing functions change the pixel values. Why should you change the
color cells instead instead of the pixel values?
The main differences are memory usage, speed, and graphical artifacts.
Many X clients use off-screen pixmaps for generating some graphics.
These pixmaps use alot of memory and may not be needed with colormap
manipulation techniques. Also, since you usually only have to change a
few (or a few dozen) color cells rather than hundreds or thousands of
pixel values, you usually get much better performance by changing the
color cells.[Blinn] Finally, colormap
manipulation can avoid some of the flickering effects usually seen in
graphics, especially animations, based raster manipulation.
The disadvantage of the colormap manipulation techniques is that they
use at least twice as many color cells compared to raster manipulation
techniques. As color cells tend to be valuable resources, this is
undesirable. In the examples below, we give more details on the
alternatives and their advantages and disadvantages. When designing
your application, you have to decide which techniques are most
appropriate.
The most important techniques we will be discussing combine colormap
manipulation techniques with raster manipulation techniques. The major
idea behind these techniques is that the bits in each pixel value can
be considered either as a unit or as smaller groups. If considered as
a group of values, you can create several independent pictures within
your your raster memory, and manipulate the color cells to display
combinations of the pictures.
For example, we might draw two independent pictures, one using 7 bits
of the pixel value and the other using the remaining 1 bit. Thus, the
8 bit pixel value is divided into:
picture one | picture two
? ? ? ? ? ? ? | ?
Let's use XAllocColorCells()
to allocate two color cells for picture one:
0 0 0 0 0 0 0 | ? white
1 0 0 0 0 0 0 | ? red
And two bits for specifying colors in picture two:
? ? ? ? ? ? ? | 0 white
? ? ? ? ? ? ? | 1 green
All graphics drawing in X is controlled through graphics contexts. You
use the colormap techniques by specifying the appropriate pixel values,
functions, and plane masks in the graphics contexts used in your
drawing. First, we create two graphics contexts for drawing. The
first draws picture one:
foreground: 10000000
background: 00000000
function: GXcopy
plane mask: 11111110
The second draws picture two:
foreground: 00000001
background: 00000000
function: GXcopy
plane mask: 00000001
After drawing, the resultant pixel values will be some of the
following:
0 0 0 0 0 0 0 | 0
0 0 0 0 0 0 0 | 1
1 0 0 0 0 0 0 | 0
1 0 0 0 0 0 0 | 1
What picture do we get in our window? That depends on the colors
stored in the color cells in the current colormap. We can change the
colors at will with XStoreColors(). This colormap
displays picture one, no matter what is in picture two with this
colormap:
0 0 0 0 0 0 0 | 0 white (background)
0 0 0 0 0 0 0 | 1 white (background)
1 0 0 0 0 0 0 | 0 red (foreground)
1 0 0 0 0 0 0 | 1 red (foreground)
We can display only picture two with this colormap:
0 0 0 0 0 0 0 | 0 white (background)
0 0 0 0 0 0 0 | 1 green (foreground)
1 0 0 0 0 0 0 | 0 white (background)
1 0 0 0 0 0 0 | 1 green (foreground)
With this colormap, we get both pictures, with picture two overlapping
picture one.
0 0 0 0 0 0 0 | 0 white (background)
0 0 0 0 0 0 0 | 1 green (picture 2 foreground)
1 0 0 0 0 0 0 | 0 red (picture 1 foreground)
1 0 0 0 0 0 0 | 1 green (picture 2 foreground, overlaps picture 1)
More complex effects are also possible. Below, we discuss techniques
for managing multi-color drawings and for erasing without disturbing
overlapping drawing.
Note that if other applications have already allocated color cells from
the default colormap, your application may not be able to allocate
enough for these techniques. In these cases, you will get an error
from the allocation function and you may want to create a separate
colormap. The ICCCM discusses techniques for installing your own
colormap.[Rosenthal]
One very useful colormap manipulation technique is the overlay
plane.[Blinn] You may have generated a
complex drawing, then want to draw some other (usually much simpler)
graphics above it. Using a simple raster manipulation drawing model,
the overlay graphics corrupt the base drawing, so when you erase the
overlay, you have to redraw the base drawing, which can be very compute
expensive, as well as ugly.
The typical overlay plane example is an air traffic control system,
with a static map display and moving air craft icons drawn over the
map. Another example, from the CAD world, is an interactive base
drawing with active areas that are highlighted only when
the mouse is within them. You can probably think of many other
examples for your application area.
Overlay plane applications usually divide the 8 bit pixel values into 7
bits for the base drawing and 1 bit for the overlay plane. The key to
this technique is giving all pixels in the overlay plane (i.e., pixels
with the overlay plane bit set to one) the same color. Thus, drawing
or erasing in the overlay plane is simply a matter of setting or
clearing this single bit.
Assuming the visual type and colormap are set correctly, the
XAllocColorCells() function is used to specify up to 128
writable base color cells for the base drawing and specify one plane
mask:
#define PLANES 1 /* number of overlay planes */
#define PIXELS 3 /* number of base colors */
unsigned long planeList[PLANES], pixelList[PIXELS];
status = XAllocColorCells(dpy, cmap, False, planeList, PLANES,
pixelList, PIXELS);
Please remember that the number of color cells actually allocated is
not C, where C is the number of base colors,
but (C * 2^P), where P is the number of
planes. The status will tell us if we could allocate the correct color
cells. If not, a new colormap is probably
necessary.[Rosenthal]
Assuming it was successful, we store colors in the allocated cells. We
could use XStoreColor(), but
XStoreNamedColor() makes the example a little clearer.
Again, the key is setting all pixels in the overlay plane are given the
same color.
int flags = DoRed|DoGreen|DoBlue;
XStoreNamedColor(dpy, cmap, "white", pixelList[0], flags);
XStoreNamedColor(dpy, cmap, "green", pixelList[1], flags);
XStoreNamedColor(dpy, cmap, "yellow", pixelList[2], flags);
XStoreNamedColor(dpy, cmap, "blue", pixelList[0] | planeList[0], flags);
XStoreNamedColor(dpy, cmap, "blue", pixelList[1] | planeList[0], flags);
XStoreNamedColor(dpy, cmap, "blue", pixelList[2] | planeList[0], flags);
One disadvantage of XStoreNamedColor() is that it will
generate an X protocol error if the color name is not known to your X
server. To avoid these errors, you could use
XLookupColor() to validate the names, choosing others if
the name is not known.
The graphics contexts for drawing the base graphics are created normally:
XGCValues xgcv;
unsigned long mask = GCForeground | GCBackground | GCFunction;
xgcv.function = GXcopy;
xgcv.background = pixelList[0];
xgcv.foreground = pixelList[1];
baseGC = XCreateGC(dpy, win, mask, &xgcv);
The graphics contexts for the overlay graphics use the plane mask
attribute to modify (set or clear) the overlay plane bit only. To
draw, we use the GXset function. Again, this works since
all pixel values with the overlay plane set generate the overlay
color:
mask = GCFunction | GCPlaneMask;
xgcv.function = GXset;
xgcv.plane_mask = planeList[0];
overlayDraw0GC = XCreateGC(dpy, win, mask, &xgcv);
To erase, we use the GXclear function:
mask = GCFunction | GCPlaneMask;
xgcv.function = GXclear;
xgcv.plane_mask = planeList[0];
overlayClear0GC = XCreateGC(dpy, win, mask, &xgcv);
This technique is easily extended to handle more than one overlay
plane. Note, however, that each overlay plane doubles the number of
color cells required. To specify more than one overlay plane, first
request that number of planes in the XAllocColorCells()
request. If that succeeds, set color values for all combinations of
pixels and overlay plane masks. For example, if two overlay planes are
used, you would have to specify these colors, in addition to those
specified above:
XStoreNamedColor(dpy, cmap, "red", pixelList[0] | planeList[1], flags);
XStoreNamedColor(dpy, cmap, "red", pixelList[1] | planeList[1], flags);
XStoreNamedColor(dpy, cmap, "red", pixelList[2] | planeList[1], flags);
XStoreNamedColor(dpy, cmap, "red",
pixelList[0] | planeList[0] | planeList[1], flags);
XStoreNamedColor(dpy, cmap, "red",
pixelList[1] | planeList[0] | planeList[1], flags);
XStoreNamedColor(dpy, cmap, "red",
pixelList[2] | planeList[0] | planeList[1], flags);
The last three colors, where both planes are specified, are used when
the two overlay drawings overlap (i.e., both overlay plane bits are
set). Any color could be used, depending on the effect desired for the
overlap, though most applications will want the same color as one of
the overlay drawings.
The graphics contexts for drawing and erasing the second overlay plane are
created the same way as for the first, using the second plane in the
plane_mask:
mask = GCFunction | GCPlaneMask;
xgcv.function = GXset;
xgcv.plane_mask = planeList[1];
overlayDraw1GC = XCreateGC(dpy, win, mask, &xgcv);
mask = GCFunction | GCPlaneMask;
xgcv.function = GXclear;
xgcv.plane_mask = planeList[1];
overlayClear1GC = XCreateGC(dpy, win, mask, &xgcv);
Note that both clearing graphics contexts clear the overlay planes
independently, even where the overlay drawings overlap. If necessary, you
could also create a graphics context that cleared both planes at once by
specifying both planes in the plane_mask attribute:
mask = GCFunction | GCPlaneMask;
xgcv.function = GXclear;
xgcv.plane_mask = planeList[0] | planeList[1];
overlayClearBothGC = XCreateGC(dpy, win, mask, &xgcv);
We leave at it as an exercise for the reader to find create uses for
the graphics contexts we have created.
Double (or multi-) buffering is used by most high quality animation
applications. The technique is similar to the overlay plane technique
described above. In double buffering, you draw two pictures in the
window, but manipulate the colormap to make only one of them visible.
Each picture can have several colors, usually the same set of colors in
each picture. To get the animated effect, you modify the second,
hidden picture, then use XStoreColors() to quickly change
the colormap, displaying the second picture and hiding the first, and
repeat. Because XStoreColors() works very quickly and the
drawing process is hidden from the user, the animation is very fast and
smooth. The alternative raster manipulation technique is sometimes
slower and usually generates undesirable flickering or other graphical
side effects as the drawing is created.
Some X servers support a protocol extension that provides multi-buffering
functionality. Your server may not have this extension or you may choose not
to use it for portability reasons. Below, we describe the two buffer (double
buffering) case, though this example is easily extended to any number of
buffers, up to the number of bits per pixel.
We could use the overlay plane approach, described above, to create the
hidden image, but this requires one overlay plane per color, which is
very expensive. The technique described here allows 2^P
colors, where P is the number of color planes used, e.g.,
three planes allows the animation of up to eight colors.
In this example, we assume that there are two drawings, each with three
colors. Three colors requires 2 planes, with one plane combination unused.
Assume that the allocated base pixel values are (binary)
01, 10, and 11 and
the allocated planes are the last two. To display the first picture and hide
the second, we must assign these colors to the pixel values:
0 1 0 0 0 0 | any white
1 0 0 0 0 0 | any yellow
1 1 0 0 0 0 | any red
We have allocated a total 12 pixel values (C * 2^P).
Since any could be (binary) 00,
01, 10, or 11, we store these 12
colors in the color cells:
0 1 0 0 0 0 | 0 0 white
0 1 0 0 0 0 | 0 1 white
0 1 0 0 0 0 | 1 0 white
0 1 0 0 0 0 | 1 1 white
1 0 0 0 0 0 | 0 0 yellow
1 0 0 0 0 0 | 0 1 yellow
1 0 0 0 0 0 | 1 0 yellow
1 0 0 0 0 0 | 1 1 yellow
1 1 0 0 0 0 | 0 0 red
1 1 0 0 0 0 | 0 1 red
1 1 0 0 0 0 | 1 0 red
1 1 0 0 0 0 | 1 1 red
To hide the first picture and display the second, we use the opposite colormap:
any | 0 0 white
any | 0 1 yellow
any | 1 0 red
any | 1 1 unused
or:
0 1 0 0 0 0 | 0 0 white
0 1 0 0 0 0 | 0 1 yellow
0 1 0 0 0 0 | 1 0 red
0 1 0 0 0 0 | 1 1 unused
1 0 0 0 0 0 | 0 0 white
1 0 0 0 0 0 | 0 1 yellow
1 0 0 0 0 0 | 1 0 red
1 0 0 0 0 0 | 1 1 unused
1 1 0 0 0 0 | 0 0 white
1 1 0 0 0 0 | 0 1 yellow
1 1 0 0 0 0 | 1 0 red
1 1 0 0 0 0 | 1 1 unused
The code for the double buffered animation is similar to the overlay plane
code. First, allocate the twelve color cells:
#define PLANES 2 /* planes for combinations */
#define PIXELS 3 /* base pixel values */
status = XAllocColorCells(dpy, cmap, False, planeList, PLANES,
pixelList, PIXELS);
Again, if this fails, a new colormap is needed.
You could use XStoreNamedColor(), as in the overlay plane
example, to store the colors, but since the colors will be changed
repeatedly during the animation process, a more efficient method is to
create two vectors of color values (one for the first picture and one
for the second) and install one of the vector all at once with
XStoreColors(). Since XStoreColors()
requires RGB values instead of color names, we first convert a list of
color names to a list of RGB values. The RGB values are stored in
XColor structures:
XColor exact; /* not used */
XColor colorDefs[PIXELS]; /* list of RGB values */
static char names[] = { "white", "yellow", "red"};
for (i = 0; i < PIXELS; i++) {
status = XLookupColor(dpy, cmap, names[i], &exact, colorDefs + i);
if (status == 0) {
printf("could not find color %s\n", names[i]);
exit(1);
}
}
Here, we print an error message and exit if the color name is not found. An
alternative is to try to use a different color.
Next, we create the two XColor vectors, representing the
two sets of twelve color cells, as shown above. One helpful hint is to
put all the data for each picture, including the vectors and the
graphics contexts, into abstract data types. Switching pictures is
then simply a matter of switching a pointer to the data.
#define TOTAL 12 /* PIXELS * 2^PLANES */
typedef struct picture { /* abstract data type */
XColor map[TOTAL]; /* color map vectors */
GC draw0GC, draw1GC, clearGC; /* graphics contexts */
struct picture *other;
} PICTURE;
PICTURE p1, p2, *current = &p1;
/* vector 1 */
for (cell = i = 0; i < PIXELS; i++) {
XColor *c = colorDefs + i;
copyColor(c, p1.map + (cell++), pixelList[i]);
copyColor(c, p1.map + (cell++), pixelList[i] | planeList[0]);
copyColor(c, p1.map + (cell++), pixelList[i] | planeList[1]);
copyColor(c, p1.map + (cell++),
pixelList[i] | planeList[0] | planeList[1]);
}
/* vector 2 */
for (cell = i = 0; i < PIXELS; i++) {
copyColor(colorDefs + 0, p2.map + (cell++), pixelList[i]);
copyColor(colorDefs + 1, p2.map + (cell++),
pixelList[i] | planeList[0]);
copyColor(colorDefs + 2, p2.map + (cell++),
pixelList[i] | planeList[1]);
copyColor(colorDefs + 0, p2.map + (cell++),
pixelList[i] | planeList[0] | planeList[1]);
}
We store the first color in the unused plane combination. If we later
find that we need to use that combination, the code is easy to modify.
This utility function was useful for copying XColor
definitions and assigning pixel values:
void copyColor(def, color, pixel)
XColor *def, *color;
unsigned long pixel;
{
color->red = def->red;
color->green = def->green;
color->blue = def->blue;
color->flags = DoRed | DoGreen | DoBlue;
color->pixel = pixel;
}
Because we want to be able to draw to either picture without disturbing
the other, our graphics contexts are slightly more complex than in the
overlay plane example. All of the graphics contexts now have plane
masks to prevent corruption from one picture to the other. Also, the
GXcopy function is used in all of the graphics contexts,
giving more control over the resultant drawing. Again, we store the
graphics contexts in the abstract data type, allowing easy access from
the drawing routines.
/* create plane masks */
unsigned long used;
for(j = 0, used = 0L; j < PLANES; j++)
used |= planeList[j];
/* map 1 */
xgcv.plane_mask = ~ used;
mask = GCFunction | GCPlaneMask | GCForeground;
xgcv.function = GXcopy;
xgcv.foreground = pixelList[1];
p1.draw0GC = XCreateGC(dpy, win, mask, &xgcv);
xgcv.foreground = pixelList[2];
p1.draw1GC = XCreateGC(dpy, win, mask, &xgcv);
xgcv.foreground = pixelList[0];
p1.clearGC = XCreateGC(dpy, win, mask, &xgcv);
/* map 2 */
mask = GCFunction | GCPlaneMask | GCForeground;
xgcv.plane_mask = used;
xgcv.function = GXcopy;
xgcv.foreground = planeList[0];
p2.draw0GC = XCreateGC(dpy, win, mask, &xgcv);
xgcv.foreground = planeList[1];
p2.draw1GC = XCreateGC(dpy, win, mask, &xgcv);
xgcv.foreground = 0L;
p2.clearGC = XCreateGC(dpy, win, mask, &xgcv);
The first defined color, pixelList[0], is used both for
the window background and for clearing the graphics. The other two
colors, pixelList[1] and pixelList[2], are
used for foreground graphics. Further graphics context attributes,
such as background colors, line widths, stipples, etc. could be
specified in the usual way for more complex
drawings.[Scheifler]
Finally, to switch pictures, we switch color cell definitions:
XStoreColors(dpy, cmap, hidden->map, TOTAL);
Where hidden is a pointer to the currently hidden
PICTURE abstract data type.
Again, our reader exercise is to use these graphics contexts to create
interesting applications.
While overlay planes and double buffering are the two most important
colormap manipulation graphics techniques, others may also be useful to
some applications. We will briefly discuss two others, color
cycle animation and alternate color animation,
which are useful in some applications. Their implementations are much
simpler than the above examples, so we will not present example code.
Both are pure colormap manipulation techniques; the raster graphics are
drawn once and all other graphical effects come solely from
manipulating the colormap. They have the advantage of being very fast,
but the disadvantage of being very inflexible.[Shoup]
Color cycle animation is one of the simplest colormap manipulation
techniques. It is most frequently used in video games and simulations
where a flashy effect is needed. One application of color cycling is a
"sparkling" or "fluid" effect where pixels in a
window are drawn with random pixel values, then the colors associated
with the color cells are rapidly cycled. The colors are typically
cycled simply by shifting them by one pixel value per iteration and
storing with XStoreColors(). Some simple motion object
effects can also be generated by assigning the same pixel values to
larger areas, such lines or rectangles.
Similar to color cycling is alternate color animation. With this
technique, only two unique colors are used, foreground and background.
The background color is set to be the same as the window background
color. Several instances of a single object are drawn in several
different locations, with the different location representing different
positions along the path of the object's animation. Each instance is
drawn with a different pixel value. To achieve the animation, use
XStoreColors() or XStoreNamedColor() to
assign the foreground color to exactly one of the pixel values. As the
foreground color is remapped to different instances' pixel values, the
object will appear to move.
Both color cycling and alternate color animation can be extended to
handle many objects, overlapping objects, etc. with some restrictions.
The major restriction is that you cannot change the path of the
animation once the object instances have been drawn. For more details,
see [Shoup]. If you need a dynamic path, you
must use the overlay plane or multi-buffer techniques described above.
While few, if any, popular X Toolkit widgets use these colormap
manipulation techniques for their graphics, you can easily use them in
your own graphics widgets. The color allocation and mapping
functionality should be implemented in these widgets' initialization
and set-values class methods.
In conclusion, colormap manipulation techniques are very powerful and
easily implemented using standard Xlib functions. They can greatly
improve the performance of some applications and remove unwanted
graphical artifacts from others. They do not always replace the more
popular raster manipulation techniques, but do supplement them well.
Colormap manipulation techniques do require, however, many more color
cells than do raster manipulation techniques that use shared color
cells or standard colormaps, so those techniques should also be
considered.
The examples presented in this tutorial assume an 8 bit per pixel
PseudoColor visual type. You should be able to easily
extend them to use any visual type with a writable colormap.
- [Blinn]
- James Blinn, "Raster Graphics", in
Tutorial: Computer Graphics,
edited by Kellogg Booth, IEEE Computer Society Press, 1979.
- [Foley]
- Foley, van Dam, Feiner, and Hughes, Computer Graphics: Principles
and Practice, second edition, Addison-Wesley, 1990.
- [Jones]
- Oliver Jones, Introduction to the X Window System,
Prentice-Hall, 1989.
- [Lemke]
- David Lemke and Davis Rosenthal, "Visualizing X11 Clients,"
Proceedings of the Summer, 1988 USENIX Conference.
- [Rosenthal]
- David Rosenthal, "Inter-Client Communication Conventions
Manual," in [Scheifler].
- [Scheifler]
- Robert Scheifler and James Gettys, X Window System,
second edition, Digital Press, 1990.
- [Shoup]
- Richard Shoup, "Color Table Animation",
SIGGRAPH'79 Conference Proceedings.
Kenton Lee is an
independent software consultant specializing in X Window System and
OSF/Motif software development.
He has been developing UNIX graphical user interface software since 1981.
Ken has published over two dozen technical papers on the X Window System.
Most are available over the World Wide Web at
http://www.rahul.net/kenton/bib.html.
Ken may be reached by Internet electronic mail to
kenton @ rahul.net
or the World Wide Web at
http://www.rahul.net/kenton/.
For more information on the X Window System, please visit my home page..