Introduction

This document is an attempt to give the reader a starting place in the nontrivial task of learning GDK. It's not (yet) meant to be a complete treatise on good programming practices with regard to GDK or otherwise. It is intentionally incomplete - the responsability of maintaining a full description of programming with GDK is not one I can expect to fulfill right now.

There are a multitude of documents out there that describe the high level interface that GDK was built for - GTK+. These documents are exactly no help when you need to get at the low level "blit this pixmap to the screen please" stuff. Most of the time you won't need to do this, and it's quite justified that GTK+ is the main focus of documenters thus far. What GDK does provide is a layer that is much more portable than say the X protocol, without sacrificing any of the low-level accessibility that systems such as X provide. The true power of this abstraction is that if you choose to use it rather than say, X, your software will automatically render on the Linux Framebuffer and Windows. Of course it isn't every app that needs such low-level access.

Before you begin...

I had hoped, when I came to Gdk that I wouldn't need to know much about X internals. I assumed that since Gdk ran on Windows, linuxfb and X that there couldn't be that much that was X specific exposed to the developer. I now know that this is exactly wrong. Gdk is in fact a fairly thin wrapper around X that happens to have ports to other drivers. You really will need to know a little about X internals and Gdk data structures to be able to make any serious headway. For the problem of learning xlib and X, I refer you to this link - the tutorials there are most informative. Skim through the X programming tutorials, make sure you understand graphics contexts, events and so on. The specifics aren't so important, but have some vague idea of how X talks to your program. The other document I would refer you to is unfortunately only for Gdk 1.2. The concepts are valid but some of it outdated. That said, it's very important to have some idea of the "class hierarchy". Being able to navigate this and understanding precisely what (say) a GdkWindow is will help immensely -- particularly when debugging. Again, a thorough reading isn't necessary.

Why GDK?

In writing this document I have faced an immense amount of inertia. There was a general feeling amongst people knowldedgable in the field that GDK shouldn't be used for general purpose programming. In some senses they are correct - there generally isn't much need for the low-level access that GDK provides. It is indeed because of this that libraries such as GTK+ and Qt have thrived. At one point I was being told that no documentation for GDK existed precisely because its developers did not want people using it.

The question remains: why would anyone want to use GDK? There isn't really an easy answer to that -- I suppose in some sense if you are reading this document you already know the answer. Remember that GDK is only a thin wrapper around Xlib -- in short anywhere X is applicable, GDK is too. But GDK gives you more than X -- for example the method for allocating colours is positively straightforward. This is often highlighted as one of Xlib's greatest shortcomings.

Mozilla uses GDK. If you switch to the classic theme in recent versions you'll notice that all your widgets look the same as those in your GTK applications. The way this is achieved offers an excellent example of why you'd want to use GDK. Mozilla, as many of you would know, uses GDK to do all its low-level drawing. That is, the XUL theming engine asks GDK to draw Mozilla onto your screen; XUL itself is not a rendering engine. When you switch to the classic theme, Mozilla asks GTK to create a handful of widgets without windows (one day, that will be the name of a band :)). When Mozilla needs to draw a "classic" button, it simply tells that widget to render to the GdkDrawables that Mozilla is already using in its low-level drawing routines. To summarise: using GDK for low-level drawing gave the Mozilla developers a straightforward way to offer users a look for Mozilla that is consistent with their desktop.

My interest in GDK is in fact quite similar. It has long been a dream of mine that I would one day be able to run all the neat applications available in Windows under WINE and have it all integrate perfectly -- these applications would look and feel like "Linux programs". If I was running a GNOME desktop, my applications should look and feel like GNOME applications, irrespective of whether WINE is running them, or they are native applications.

I have a number of strategies in mind for achieving this, but first on my list was to make my programs look like GTK2 applications. Currently, WINE uses Xlib directly to render everything. This in and of itself isn't a problem, except that GTK+ widgets render to GdkDrawables, not to X windows -- as well they should in order to maintain GTKs platform independence. But this particular set of circumstances offers a compelling avenue to follow: If GDK is a thin wrapper around X, why not replace the Xlib calls with GDK calls and then ask GTK to render its widgets directly, in the same manner as Mozilla? - and this is precisely what I intend to do. There are other approaches -- for example making a GdkDrawable that just renders into a pixmap and then blitting the widgets directly to the screen. To me that seems troublesome; for starters I would need to come to terms with Xlib's colour handling. It also seems limiting considering the tools we have. Using GDK as a low-level drawing system imediately opens up other platforms where work on WINE could continue. Windows for one :). In addition it means that just porting GDK to another platform gives you anohter platform imediately. It opens a lot of possibilities that straight X just can't give you.

Writing a window manager? Just about everyone is these days. Coding in GDK is a much less onerous task than programming straight X. Writing a game? GDK gives you access to the linux framebuffer, and GdkGL gives you access to 3D rendering hardware.

To me the answer to my question is "If you can use X, why not use GDK?"

Your first GDK app

So, without further ado I'd like to present for you the very first GDK app you should know how to write. This is a fundamentally boring app, but an important one. It will just display a window on the screen. Here is the code:

/* The GDK headers. Very important. */ #include <gdk/gdk.h> /* Convenient way to suck in all the glib stuff. */ #include <glib.h> int main ( int argc, char *argv[]) { /* a pointer to a window object - this is what we will draw on. */ GdkWindow *window; /* a set of "attributes" for our windows. This struct holds things * like width, height etc. */ GdkWindowAttr attributes; /* a bitmask of the attributes you actually want to read out of the * attributes thing above */ gint attributes_mask; /* GDK apps need a main loop to wait for events and so forth. This main * loop has a struct which tells you about it called "GMainLoop". We need * one of them. */ GMainLoop *mainloop; /* initialise gdk. We pass in argc and argv - it uses them for info like * display on X and so forth. Die if GDK can't initialize. * * Always initialise gdk before working with it. Stuff breaks otherwise. */ if (!gdk_init_check (&argc, &argv)) { return FALSE; } /* gdk_rgb_init() is a function which I can only guess sets up the * true colour colour map. It returns void so we can't check its * return value. */ gdk_rgb_init(); /* set the attributes for the window. Lets create a 400x400 top-level * window. */ attributes.window_type = GDK_WINDOW_TOPLEVEL; attributes.width = 400 ; attributes.height = 400 ; attributes.wclass = GDK_INPUT_OUTPUT; /* Get a true-colour colour map. GDK also supports indexed colour maps * but they aren't so interesting for us. */ attributes.colormap = gdk_rgb_get_cmap (); /* set our mask so that the "colormap" element is used. */ attributes_mask = GDK_WA_COLORMAP; /* create the window. */ window = gdk_window_new ( NULL , &attributes, attributes_mask); /* show the window. */ gdk_window_show (window); /* Create a new main loop object. The True is to say that the structure * should be initialised to say that it is running. */ mainloop = g_main_new (TRUE); /* Run that main loop */ g_main_run (mainloop); }

Ok, so let's go through what that's doing. First of all there's the declaration of a bunch of variables. The first one is pointer to a window. Why a pointer? Well, mostly because Gdk deals in pointers. Gdk is built on top of glib, which amongst other things provides an approximation of classes and other OO principles to C. The reasons why aren't very interesting to us, but glib will tend to deal in pointers, and so Gdk will follow suit. Let's look at those declarations:

GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; GMainLoop *mainloop;

As you may have guessed, the GdkWindow structure is a structure that contains information about a window - most importantly it gives us a way to refer to the window when we want to call functions on it. Next we allocate a GdkWindowAttr structure on the stack. This structure merely serves as a temporary holding bay for settings that define the specific properties we want our window to have (width, height and so forth). The glib type gint is glib's platform independent integer type. As the name suggests it will be used as a bitmask to filter which attributes we will be setting on our window. The GMainLoop structure stores information about a thread of execution. GDK operates using an event loop - a routine which fetches any pending events for your application, calls appropriate callbacks and updates the screen. Without the main loop your program would either do nothing at all or it might pop up a window and then exit imediately.

Before we proceed I would like to mention something about the naming policies those types demonstrate. The GdkWindow and GdkWindowAttr structures are prefixed with "Gdk" - it is no coincidence that this is a Gdk "class". Likewise, the glib "classes" and "primitives" are prefixed with a "g". Lower case for primitives, upper case for classes. It isn't of importance for us, but Gtk+ classes are prefixed with "Gtk". Keep this in mind because it will give you some idea of which source code to dig through if you end up in trouble that no-one can help with.

After the standard variable declarations there are some calls to Gdk code to initialize various internals. What this code does precisely isn't important for us, but I will say this: Never forget to call the initialization functions. There will come a time when everyone will fail to do this. Unfortunately Gdk doesn't imediately die with a segfault, it will start executing code, and shortly after the start of the program it will die horribly with a colourful combination of Gdk errors and odd signals. They are all safe to call multiple times, so if you are in doubt, another call won't hurt at all. The initialization calls are as follows:

if (!gdk_init_check (&argc, &argv)) { return FALSE; } gdk_rgb_init();

The call to gdk_init_check does very much as the name suggests. It is the function you must always call. It initialises all the internal Gdk "stuff" and checks that this has all ocurred successfully. If it fails, then the if condition fails, and the program exits there. Our program cannot proceed if Gdk cannot initalise. gdk_init_check takes as parameters pointers to the program's argument array and size. This is because Gdk has a number of standard parameters it can accept - debugging options and so forth. Gdk will process the argument array, perform appropriate tasks for options it recognises and then remove these options from the array and update the argument count appropriately. The corollary to this is that it must be called before you do any processing of arguments to your program.

The second initialiser we call is gdk_rgb_init . This one does not always need to be called; it is only used when we intend to work in so-called "true-colour". It essentially instructs X (or Windows or whatever) to allocate the appropriate structures to allow true-colour operation. Likewise, you will get indecipherable errors if you neglect to use this parameter. This tutorial is intended only to be a starting point, so for now I'm neglecting explanations of indexed colour with Gdk. Needless to say however, it has its own initialiser.

The next section is concerned with telling Gdk about the window we want to make. It is logically divided into two sections. Lets examine the first one:

attributes.window_type = GDK_WINDOW_TOPLEVEL; attributes.width = 400 ; attributes.height = 400 ; attributes.wclass = GDK_INPUT_OUTPUT;

The first section sets all the parameters that you absolutely must have set to create a Gdk window. This is the first use of the GdkWindowAttr structure we allocated in our variable declarations. The GdkWindowAttr structure can contain all manner of instructions for the window (details here for those interested), but for now we are just saying that it will be a 400x400 window and that it will be a toplevel window. A "toplevel window" is more or less one that belongs to the window manager rather than your application. Every application needs at least one of them. Finally we set the "class" of the window. There are two types. GDK_INPUT_OUTPUT refers to what you normally expect a window to be - a box on the screen that can accept user input and respond to it visually. The other class of window, GDK_INPUT_ONLY is an invisible window that is just used for capturing user input. These are the minimum specifications that Gdk needs to construct a window - again, never forget to do this because odd errors ensue.

Next we move on to the "optional" parts of the window specification:

attributes.colormap = gdk_rgb_get_cmap ();

In this case we just set the colour map that this particular window should use. As mentioned previously, we're only really concerned with true-colour here, and, true to this goal we are setting the colour map of our window to be true-colour. You might think that this would be standard, and it probably could be. The reason we use gdk_rgb_get_cmap to retrieve the colour map is that we will then be using the same colour map that gdk_rgb_init setup for us during the initialisation section. This saves memory, gives Gdk less things to worry about, and is generally a good idea.

Having set some parameters in our GdkWindowAttr structure, we now need a way of signalling exactly which settings from this structure should be used when building our window. You may well ask why we need to do this; surely the computer could merely observe which settings we "set" and ignore all the others. This is more a limitation of the C programming language than an aesthetic pleasantry. There are other ways to do it, but a bitmask is one of the less error prone. Examine this line:

attributes_mask = GDK_WA_COLORMAP;

Now, GDK_WA_COLORMAP is defined as an integer in one of the many header files Gdk included for us. The list of all related values is here. That number represents an instruction to use the specified information about the colour map when building our window. If we wanted an instruction to use more information, we would combine the numbers from the previous link with a bitwise OR. If you aren't familiar with bitwise operations that will make little sense, but rest assured that you will quickly learn if you do any Gdk programming.

After that preamble, the actual construction of the window is disappointingly anti-climactic. Here it is:

window = gdk_window_new ( NULL , &attributes, attributes_mask);

The first parameter is the parent window - for now this has little significance. Indeed, in our single window program we specify that the parent window is NULL , since this is the toplevel window. That is to say that the window manager "owns" this window, and it will manage this window accordingly; our program does not need to know about the management of this window. The second parameter is a pointer to our GdkWindowAttr structure, which holds all the instructions about window size and so forth. The third paramter is the bitmask I just described.

So, to summarise our position so far, we have made a structure to refer to the window with - a window "handle" if you will. We have made a structure containing information describing features of the window - width, height, colour map etc. We have formed an instruction about precisely which of these instructions to follow, and finally we have passed all this information to Gdk and it has set the appropriate data in the GdkWindow object and initialised the various data structures it needs for our new window. It has constructed the window object

The next line is similarly anti-climactic. It is merely an instruction that once the "main loop" starts, this window should be visible:

gdk_window_show (window);

The final two instructions actually start the main loop iterating, and display our window. The first instruction:

mainloop = g_main_new (TRUE);

could have been called at any point in the program. This merely constructs a new handle for a "main loop" (yes, there can be more than one. This will become clear later). It does not actually start the loop running (it is similar to calling malloc , except that it actually sets some of the members in the object to specific values). The only parameter, which is set to TRUE indicates that this particular main loop should have the flag indicating that it is running set to true. This equally could have been set to false since it will be set once the loop is actually started, but a parameter is required and TRUE is as good as FALSE . Note that this is a glib function, not a Gdk one.

The final part to our program is to start the main loop executing. The following line performs this task:

g_main_run (mainloop);

And that completes our program. The only thing left is build instructions. You compile it (and all other Gdk programs) like so:

$ gcc -o program program.c `glib-config --cflags --libs` `gtk-config --cflags --libs`

Copyright (c) 2002 James Gregory