The GDK Wrapper

Most of the X concepts we've covered so far apply very closely to GDK, which, you may recall, provides a rather thin wrapper around the Xlib drawing API. We'll touch lightly on the GDK wrapper in this section, before moving on to some actual graphics code in the sections that follow.

Simpler API

The GDK wrapper does a lot to make Xlib more palatable. Possibly the most important thing it does is provide an abstraction layer around Xlib, which makes it easier to port GTK+ and GNOME applications to new graphical systems, like BeOS or Microsoft Windows. If you make sure that your applications don't access anything lower than GDK, you'll increase your chances of an easier port. When GDK is ported to a new system, porting your own application will often be as simple as a recompile and a handful of minor tweaks (depending, of course, on what goes on in your particular application). GDK also remaps the Xlib API into a form more appropriate for and consistent with GTK+ and GNOME. Where possible, it simplifies the parameter lists of each function, making it easier to use.

Let's look at how the Xlib API for creating a new pixmap compares to the GDK API. The relevant Xlib function is XCreatePixmap( ):

Pixmap XCreatePixmap(Display *display, Drawable d,
    unsigned int width, unsigned int height,
    unsigned int depth)

The GDK wrapper function combines the first two parameters of XCreatePixmap( ) into the single GdkWindow parameter and changes the function name into the GDK style: gdk_pixmap_new( ). It also converts the unsigned int parameters into the more abstracted gint types:

GdkPixmap* gdk_pixmap_new(GdkWindow *window,
    gint width, gint height,
    gint depth)

As we'll see in Section 10.3, GDK also adds quite a bit of new functionality that doesn't exist in Xlib's API. In addition, it provides wrappers for all of the important X11 concepts, such as drawables (GdkDrawable, GdkPixmap, and GdkWindow), colors (GdkColor and GdkColormap), visuals (GdkVisual), and even fonts and cursors (GdkFont and GdkCursor).

Using Color in GDK

Color management can be a convoluted nightmare with raw Xlib applications. At that level you have to keep track of which visual you're in, how the color maps are laid out, whether or not you can create new colors in the color maps, and many other tedious, repetitive details. One of the blessings of GDK is the way in which it abstracts most of these tasks away from the applications programmer. You need to know only a few simple functions, and GDK takes care of the rest.

The first thing you need is the color map. You can get the default system color map with gdk_colormap_get_system( ). If you need to know which color map a specific widget is using, you can call gtk_widget_get_colormap( ) on it. GdkRGB and Imlib (a graphics loading library used by GNOME 1.0) also offer access to their own tuned color maps, with gdk_rgb_get_cmap( ) and gdk_imlib_get_colormap( ), respectively. And finally, if none of these other preexisting color maps are good enough, you can create your own color map with gdk_colormap_new( ), although you will rarely have to go that far. The color map functions look like this:

GdkColormap* gdk_colormap_get_system(  );
GdkColormap* gtk_widget_get_colormap(GtkWidget *widget);
GdkColormap* gdk_rgb_get_cmap(  );
GdkColormap *gdk_imlib_get_colormap(  );
GdkColormap* gdk_colormap_new (GdkVisual *visual,
    gint allocate);

It is generally a good idea to use the same color map with all widgets in an application; when you start mixing color maps, the color palette may flash whenever the focus switches between widgets with different color maps, espe- cially in lower color depths like 8-bit.

To obtain a color from a color map in GDK, you must allocate it with a call to gdk_colormap_alloc_color( ):

gboolean gdk_colormap_alloc_color(GdkColormap *colormap,
    GdkColor *color, gboolean writable, gboolean best_match);

The writable parameter tells GDK to add the color to the color map if it's not already there; writable is ignored in read-only color maps. The best_match parameter instructs GDK to find a nearby color if it can't find an exact match, in which case it modifies the color value you passed in to match the color it finds. These two parameters are more or less mutually exclusive: If the color map is writable (such as with GrayScale or PseudoColor visuals), GDK should allocate a new color that exactly matches your request rather than trying to find an approximate match. The most common usage is to call the function with a writable of FALSE and a best_match of TRUE.

To specify the color you want, you must populate a GdkColor structure before passing it to gdk_colormap_alloc_color( ). The GdkColor structure is very simple:

struct GdkColor
  gulong pixel;
  gushort red;
  gushort green;
  gushort blue;

It's your job to fill in the red, green, and blue fields, and GDK's job to fill in the pixel field with the color map index it finds for that color. You can fill in the RGB values by hand, by choosing 16-bit values for each field, like this:

GdkColor red =    { 0, 0xFFFF, 0x0000, 0x0000 };
GdkColor yellow = { 0, 0xFFFF, 0xFFFF, 0x0000 };
GdkColor gray =   { 0, 0x8888, 0x8888, 0x8888 };

An easier, more intuitive way to initialize a GdkColor structure is to use the gdk_color_parse( ) function. It accepts a text string with a color name, like "red," "slate gray," or "MediumSpringGreen"; you can also pass in an RGB string-for example, "RGB:FF/FF/00"-to get results similar to those obtained in the yellow example above. gdk_color_parse( ) invokes the Xlib function XParseColor( ) to look up an RGB value from a table of common colors

gint gdk_color_parse(const gchar *spec, GdkColor *color);

The master list of these colors is in the rgb.txt file distributed with the X Window System, in the directory /usr/X11R6/lib/X11 or someplace similar.

You then pass the parsed GdkColor to gdk_colormap_alloc_color( ), and if all goes right, you can use that new color. You should always check the return values on both of these functions to make sure that GDK found a valid color for you. Here's a quick example of how to retrieve a medium spring green from the system palette:

GdkColor color;
GdkColormap *cmap = gdk_colormap_get_system(  );

if (gdk_parse_color("MediumSpringGreen", &color) &&
    gdk_colormap_alloc_color(cmap, &color, FALSE, TRUE))
  /* Use our MediumSpringGreen for a nice pastoral scene */
  /* Go find some other nice green */

Because of the GDK abstraction layer, this simple block of code will work on any visual. You don't have to worry about what the color depth is, or whether or not the color map is writable. GDK will handle these details and return a sensible result or bail out with a FALSE if it can't.