Next Previous Contents

2. Getting Started

The first thing to do, of course, is to obtain GTK--. You can get the latest version from http://gtkmm.sourceforge.net/. If you don't already have it, you will also need to install the companion library libsigc++, available from http://libsigc.sourceforge.net/.

The GTK-- source distribution also contains the complete source to the examples used in this tutorial, along with Makefiles to aid compilation. The binary package distributions include it as well; the example sources are included with the gtkmm-devel RPM, which installs them to /usr/doc. You'll need to copy the examples subdirectory to your home directory, or some other place where you have write access, to compile the examples, if you're using the RPM distribution.

To begin our introduction to GTK--, we'll start with the simplest program possible. This program will create an empty 200x200 pixel window. It has no way of exiting except to be killed using the shell.

Source location: examples/base/base.cc

#include <gtk--/main.h>
#include <gtk--/window.h>

int main(int argc, char *argv[])
{
    Gtk::Main kit(argc, argv);

    Gtk::Window window (GTK_WINDOW_TOPLEVEL);
    window.show();

    kit.run();  // you will need to ^C to exit.
    
    return(0);
}

You can compile the above program with gcc using:

g++ base.cc -o base `gtkmm-config --cflags --libs`

The meaning of the unusual compilation options is explained below.

All GTK-- programs must include certain GTK-- headers; gtk--.h includes the entire GTK-- kit, which is usually not a good idea, since it includes a meg or so of headers, but for simple programs, it suffices.

The next line:

Gtk::Main kit(argc, argv);

creates a Gtk::Main object. This is needed in all GTK-- applications. The constructor for this object initializes the GTK-- library for use, sets up default signal handlers, and checks the arguments passed to your application on the command line, looking for the following options:

It removes these from the argument list, leaving anything it does not recognize for your application to parse or ignore. This ensures that all GTK-- applications accept the same set of standard arguments.

The next two lines of code create and display a window:

  Gtk::Window window (GTK_WINDOW_TOPLEVEL);
  window.show();

The GTK_WINDOW_TOPLEVEL argument specifies that we want the window to undergo window manager decoration and placement. Rather than create a window of 0x0 size, a window without children is set to 200x200 by default so you can still manipulate it.

The show() method lets GTK-- know that we are done setting the attributes of this widget, and that it can display it.

The last line enters the GTK-- main processing loop.

  kit.run();

This is another call you will see in every (working) GTK-- application (the run() method of the Gtk::Main class). When control reaches this point, GTK-- will sleep waiting for X events (such as button or key presses), timeouts, or file I/O notifications to occur. In this simple example, however, events are ignored.

To compile this, use this command (assuming you've put the source code in simple.cc):

g++ -Wall -g simple.cc -o simple `gtkmm-config --cflags --libs`

To simplify compilation, we use the program gtkmm-config, which is present in all (properly installed) Gtk-- installations. This program 'knows' what compiler switches are needed to compile programs that use GTK--. The --cflags option causes gtkmm-config to output a list of include directories for the compiler to look in; the --libs option causes it to output the list of libraries for the compiler to link with and the directories to find them in (try running it from your shell-prompt to see what it's printing on your system).

For the above compilation command to work, note that you must surround the gtkmm-config invocation with backquotes. Backquotes cause the shell to execute the command inside them, and to use the command's output as part of the command line.

Some of the libraries that are usually linked in are:

This list will vary from installation to installation; run gtkmm-config --libs to see it for your system.

2.1 Theory of Signals and Callbacks

GTK is an event driven toolkit, which means it will sleep until an event occurs and control is passed to the appropriate function. This is done using a callback mechanism called a signal. When an event occurs, such as the press of a mouse button, the appropriate signal will be emitted by the widget that was pressed. This is how GTK does most of its useful work. There are a set of signals that all widgets inherit, such as "destroy", and there are also signals that are widget specific, such as "toggled" for toggle buttons. You can also create your own signals. To make a widget - a button for example - perform an action, we set up a signal handler to catch the signal(s) the button emits; the signal handler will perform whatever actions we want to be triggered by the button-press.

GTK-- objects emit signals using special signal-emitting objects which we will call signallers (they're also known as "signal objects"). A good way to understand this is to think of a traffic light. In most countries, a traffic light is a box with three lights on it, one red, one green, and one yellow. Most people know what these lights mean: red means "stop", yellow means "slow down", and green means "go". If a traffic light were a GTK-- object, it would have three "lamps" (signallers), which we might call Red, Green, and Yellow. In the same way, a (simple) GTK-- button has only one "lamp" (signaller) on it: it's called clicked, and it "blinks" (emits a signal) whenever somebody presses it.

Naturally, signals aren't much good unless somebody's watching them; a traffic light could blink red all day, but it would just be showing pretty colours if nobody stopped for it. Fortunately, most people know about traffic lights, but computers are a bit duller; we have to tell them explicitly what signals to watch out for, and what to do when they see them. We do this by connecting a signaller to a callback function (or simply callback); the callback function is what does something in response to the signal.

Thanks to the flexibility of libsigc++, the callback library used by GTK--, the callback can be almost any kind of function: it can be a method of some object, or a standalone static function - it can even be a signaller.

There is a point of potential confusion here which you should watch out for. We've given the name "signaller" to the libsigc++ objects which emit signals, but that's not what these objects are called by the library; libsigc++ signal objects are all of type SigC::Signal, as we'll see later. Remember that these objects, even though they're called "signals", are simply the "light bulbs" that do the signalling. In the traffic light example, the traffic light's light bulbs would be called SigC::Signals in libsigc++ parlance; keep that in mind, and you should be alright.

Here's an example of a callback being connected to a signal:

#include <libsigc++>
#include <gtk--/button.h>

void hello()
{
    cout << "Hello World" << endl;
}

main()
{
    Gtk::Button button("Hello World");
    button.clicked.connect(slot(&hello));
}

There's rather a lot to think about in this (non-functional) code. First let's identify the parties involved:

Now that we've got that straight, how exactly does hello() get called? The answer is that we connected hello() to one of the Gtk::Button's signallers, in this case the one called clicked. A signaller is an object of one of the template classes SigC::Signal#, where # is a number from 0 to 7 (as we'll show later, this number determines how many arguments the callback should take). A signaller's sole purpose in life is to call callback functions.

(An aside: GTK calls this scheme "signalling"; the sharp-eyed reader with GUI toolkit experience will note that this same design is oft seen under the name of "broadcaster-listener" (e.g., in Metrowerks' PowerPlant framework for the Macintosh). It works in much the same way: one sets up broadcasters, and then connects listeners to them; the broadcaster keeps a list of the objects listening to it, and when someone gives the broadcaster a message, it calls all of its objects in its list with the message. In GTK--, signal objects play the role of broadcasters, and slots play the role of listeners - sort of. More on this later.)

On first hearing of the concept of connecting callback functions to signal objects, one might well suppose that the signal object's connect method takes as argument a function pointer. But C++ is a strongly typed language, so this won't work, unless we're certain that we will only ever need one type of callback function. But we won't; we need lots of different types of callback functions, with varying numbers of arguments; and those arguments need to be of any type we want, and oh yes, we also need to be able to have any return value we want, and also we want complete compile-time type-checking, and for our beer to be free.

Believe it or not, C++ can handle all of these requirements but the last one. (Sorry.) Not only that, we get added flexibility. The particular technique by which we achieve all these things does work, but like so many things in life, it involves a tradeoff; it can be a bit difficult to get your head 'round at first, because it involves what looks at first like an odd bit of syntactical gymnastics, and it makes very heavy use of templates (the magic behind most of the weirdness and wonderfulness of C++). But it works, and it works beautifully; so well, in fact, that although you may have come to GTK-- for other reasons, you might well stick around because of the ingenious signalling system.

Now let's get back to the code. Here again is the line where we hooked up the callback:

    ...
    button.clicked.connect(slot(&hello));
    ...

Keep in mind that we're trying to set things up so that hello() gets called when we click the button. Now, note again that we don't pass a pointer to hello() directly to the button's signal's connect() function (remember, we can't). Instead, we pass it to something called slot(), and pass the result of that to connect(). What's that slot() function for?

slot() is a factory function which generates, unsurprisingly, slots. A slot is an object which looks and feels like a function, but is actually an object. Such beasts are known as function objects, or functors, and they're the key to the GTK-- operation. They're used instead of pointers because a pointer can't really know what kind of thing it's pointing at. Compilers can give you the illusion of this, using typed pointers, but the illusion breaks down in this situation.

Slots don't have that problem. Unlike function pointers, slots "know" what kind of function they're pointing at. You can make slots that point to member functions, static functions, normal functions - indeed, any kind of function at all - by invoking the proper slot-building factory function. Slots not only know what flavour of function they are pointing to, but they know where it is; if a slot is pointing to a method of some dynamically allocated object - which it can - the user of the slot doesn't have to know what the type of that object is. This is what makes slots so much better than pointers-to-methods: if we used those, signal objects would have to know what the type of those objects was, and since signal objects often come as part of a library (e.g., GTK--), they simply can't (unless you insist that the GTK-- authors #include your headers in the GTK-- source, which they won't).

Why is a slot called a "slot"? We don't know exactly; but we've got a good guess. You've probably seen a postal box with a narrow rectangular opening in it just big enough for letters to be put through. English speakers tend to refer to narrow rectangular openings as "slots", and slots are often used for passing messages (bits of mail) about, just as they are in GTK--. If the Button in the previous example were telling you about its having been pressed by sending you a letter ("Dear developer: At eight o'clock, I was clicked .."), it would very likely post that letter by pushing it through a slot. In the world of GTK--, the button does indeed tell you of its clickedness by passing a "message" through a slot (object).

Incidentally, the slot() functions are all declared as part of the namespace SigC. The fully qualified name of slot() is SigC::slot(); we've omitted it in these examples for brevity, but you need to know about this, in case you find your compiler telling you that it doesn't know where slot() is. All libsigc++ types and static functions are declared in the SigC namespace.

Here's a slightly larger example of slots in action:

void callback();

class callback_class : public SigC::Object
{
    void method();
};

callback_class callback_object;

main()
{
    Gtk::Button button;
    button.clicked.connect( slot(&callback) );
    button.clicked.connect( slot(callback_object, &callback_class::method) );
}

The first call to connect() is just like the one we saw last time; nothing new here. The next is more interesting. slot() is now called with two arguments (it's overloaded). The first argument is "f", which is the object that our new slot will be pointing at; the second argument is a pointer to one of its methods. This particular version of slot() creates a slot which will, when "called", call the pointed-to method of the specified object, in this case f.mymethod().

Another thing to note about this example is that we placed the call to connect() twice for the same signal object. This is perfectly fine; the result will be that when the button is clicked, both slots will be called. You can make buttons do double, triple, or n-duty this way, without modifying much code.

We just told you that the button's clicked signaller is expecting to call a function with no arguments. All signallers have requirements like this; you can't hook a function with two arguments to a signaller expecting to not have to provide any (unless you use an adapter, of course). Therefore, it's important to know what type of slot you'll be expected to provide to a given signaller.

To find out what type of slot you can connect to a signaller, you can look it up in the documentation, or you can look at the signaller's declaration - which you might have to do, since there might not be any documentation. Here's an example of a signaller declaration you might see in the GTK-- headers:

Gtk::EmitProxySignal1<gint,GtkDirectionType,
                 CppObjectType,BaseObjectType,3,&gtk_container_focus> focus;

This looks rather a mess; but fortunately, you can ignore most all of it. Other than the signaller's name (focus), two things are important to note here: the number following the word Signal at the beginning (1, in this case), and the first two types in the list (gint and GtkDirectionType). The number indicates how many arguments the signaller will be giving out; the first type, gint, is the type that the callback ought to return; and the next type, GtkDirectionType, is the type of this signaller's single argument. Don't let the remaining types (CppObjectType, BaseObjectType, etc.) worry you; they're used internally by GTK--.

The same principles apply for signallers which send out more arguments. Here's one that sends three (taken from <gtk--editable.h>/):

Gtk::EmitProxySignal3<void,const gchar*,gint,gint*,
       CppObjectType,BaseObjectType,2,&gtk_editable_insert_text> insert_text;

Granted, it looks even scarier, but it follows the same form. The number 3 at the end of the type's name indicates that our callback will need three arguments. The first type in the type list is void, so that should be our callback's return type. The following three types should be the argument types, in order, of our callback. Our callback function's prototype could look like this:

void insert_text_cb(const gchar* foo, gint bar, gint* foobar);

The names of the formal parameters aren't important at all, because the signaller will never see them. Of course, it is important to know what insert_text will be sending you in those argument, but for that you'll have to consult the documentation.

2.2 Overriding Member Functions

(Note: In the following section, we often use the terms "function", "member function" and "method" interchangeably. If we refer to a "function" which belongs to a class, we really mean "member function" or "method". We apologise for any confusion.)

So far, we've been telling you that the way to perform actions in response to button-presses and the like is to watch for signals. That's certainly a good way to do things, and the only practical way to do it in straight GTK+. In GTK--, though, it's not the only way.

With GTK--, instead of laboriously connecting callbacks to signals, you can simply make a new class which inherits from a widget - say, a button - and then override one of its member functions, such as the one that gets called when somebody presses the button. This can be a lot simpler than hooking up signals for each thing you want to do. You can do this in the straight-C world of GTK+ too; that's what GTK's object system is for. But in GTK+, you have to go through some complicated procedures to get object-oriented features like inheritance and overloading. In C++, it's simple, since those features are supported in the language itself; you can let the compiler do the dirty work.

This is one of the places where the beauty of C++ really comes out. One wouldn't think of subclassing a GTK+ widget simply to override its action method; it's just too much trouble. In GTK+, you almost always use signals to get things done, unless you're writing a new widget. But because overriding methods is so easy in C++, it's entirely practical - and sensible - to subclass a button for that purpose.

Overriding functions this way has a number of advantages. The first is that you can get a lot done with only a few lines of code; in some situations, you might find that you never need to use signals at all. It also means that you'll rarely have to write an entirely new widget - you can almost always do what you want by subclassing. And because overriding is so simple, you're a lot less likely to make the sort of silly mistakes you'll probably make if you're writing a new GTK+ widget in C. Also, overriding will usually result in reduced execution time; emitting and distributing signals takes longer than simply calling a virtual function.

So why did we spend all that time explaining signals? There are two main reasons why you need to be familiar with them. Firstly, the GTK-- library relies on them. Even if you don't use them much yourself, you need to understand what's going on when you see signals used. Secondly, subclassing isn't always the best way to accomplish things. Making a new class adds a symbol to your program, and that requires additional resources; and when you subclass a widget, its actions are fixed at compile time - signals allow you to change the behaviour of objects as the program is running. You can also make a signal easily affect multiple unrelated objects; overriding is not meant for that purpose. The power of signals shouldn't be ignored; you need them in your toolbox to be truly effective in GTK--.

To summarise: both techniques are valid tools for different situations. Which one you tend to use will help define your personal GTK-- programming style, but you need to know about them both.

GTK-- classes are designed with overriding in mind; they contain virtual member functions specifically intended to be overridden. These functions have _impl at the end of their names. All of the signals in GTK-- classes have corresponding overridable methods. _impl methods are declared protected, so they can only be used by derived classes.

Let's look at an example of overriding. Here is the first example from the section on signals, rewritten to use overriding:

#include <gtk--/button.h>

class OverriddenButton : public Gtk::Button
{
protected:
    virtual void clicked_impl();
}

void OverriddenButton::clicked_impl()
{
    cout << "Hello World" << endl;
// call the parent's version of the function
    Gtk::Button::clicked_impl();
}

main()
{
    OverriddenButton button("Hello World");
}

Here, instead of making a Gtk::Button object, which we then connect a signal to, we define a new class called OverriddenButton, which inherits from Gtk::Button. The only thing we change is the clicked_impl function, which is called whenever Gtk::Button emits the clicked signal. We define this function to print "Hello World" to stdout, and then we call the original, overridden function, to let Gtk::Button do what it would have done had we not overridden the clicked function. Note that we declared our personal clicked_impl function to be protected; you should always do this when you override a protected function.

You don't always have to call the parent's function; there are times when you might not want to. Note that we called the parent function after writing "Hello World", but we could have called it before. In this simple example, it hardly matters much, but there are times when it will. With signals, it's not quite so easy to change details like this, and you can do something here which you can't do at all with signals: you can call the parent function in the middle of your custom code.

On the other hand, to do this we had to make a new class, and this particular example came out a little longer, line-by-line, than the original. There are situations, though, where this extra work of making a new class can be extremely helpful. You might, for example, make a button which you want, say, 16 of, and you might want all of them to do roughly the same thing when pressed. Using signals, you'd have to manually create each button and then connect each one to a callback of some sort. If you make a new class, though, you don't have to connect anything - you can make each button behave correctly as soon as it's created. Not only that, you can make a new constructor for your class which takes extra parameters; you could then use these values to affect the behaviour of each object. The possibilities are virtually limitless.

We noted that the clicked_impl function is provided by the Gtk::Button widget for the clicked signal; this same principle holds for all signals that a widget supports. Gtk::Button gives you _impl functions called pressed_impl, released_impl, clicked_impl, enter_impl, and leave_impl (these all correspond to Gtk::Button signals; see the chapter on buttons for details). It also inherits some _impl functions from Gtk::Widget, which are, of course, available to subclasses of Gtk::Button.

2.3 Hello World in GTK--

We've now learned enough to look at a real example, instead of the silly stuff we've been analysing. In accordance with an ancient tradition of computer science, we now introduce Hello World, a la GTK--:

Source location: examples/helloworld/helloworld.cc

#include <iostream>
#include <gtk--/button.h>
#include <gtk--/main.h>
#include <gtk--/window.h>

using std::cout;

using SigC::slot;

class HelloWorld : public Gtk::Window
{
  Gtk::Button m_button;

public:
  HelloWorld();
  
  // this is a callback function. the data arguments are ignored in this example..
  // More on callbacks below.
  void hello();
  
  // When the window is given the "delete_event" signal (this is given
  // by the window manager, usually by the 'close' option, or on the
  // titlebar), this in turn calls the delete_event signal and the
  // delete_event_impl virtual function.  We will override the 
  // virtual function.
  virtual int delete_event_impl(GdkEventAny *event);
  
};


// This is a callback that will hand a widget being destroyed.
void destroy_handler()
{
  Gtk::Main::quit();
}


HelloWorld::HelloWorld()
  : Gtk::Window(GTK_WINDOW_TOPLEVEL), // create a new window
    m_button("Hello World")   // creates a new button with the label "Hello World".
{
  // Here we connect the "destroy" event to a signal handler.  
  // This event occurs when we call gtk_widget_destroy() on the window,
  // or if we return 'false' in the "delete_event" callback.
  destroy.connect(slot(&destroy_handler));

  // Sets the border width of the window.
  set_border_width(10);
          
  // When the button receives the "clicked" signal, it will call the
  // hello() method. The hello() method is defined below.
  m_button.clicked.connect(slot(this, &HelloWorld::hello));
  
  // This will cause the window to be destroyed by calling
  // gtk_widget_destroy(window) when "clicked".  Again, the destroy
  // signal could come from here, or the window manager.
  m_button.clicked.connect(destroy.slot());

  // This packs the button into the window (a gtk container).
  add(m_button);

  // The final step is to display this newly created widget...
  m_button.show();
  
  // and the window
  show();

  // NOTE : These last two lines can be replaced by
  //show_all();
}


void HelloWorld::hello()
{
  cout << "Hello World" << endl;
}


int HelloWorld::delete_event_impl(GdkEventAny *event)
{
  cout << "delete event occured" << endl;

  // if you return false in the "delete_event" signal handler,
  // GTK will emit the "destroy" signal.  Returning true means
  // you don't want the window to be destroyed.
  // This is useful for popping up 'are you sure you want to quit ?'
  // type dialogs.

  // Change true to false and the main window will be destroyed with
  // a "delete_event".
  return true;
}

  
int main (int argc, char *argv[])
{
  // all GTK applications must have a gtk_main(). Control ends here
  // and waits for an event to occur (like a key press or mouse event).
  Gtk::Main kit(argc, argv);

  HelloWorld helloworld;

  kit.run();
  return 0;
}

Try to compile and run it before going on.

Pretty thrilling, eh? Let's examine the code. First, the HelloWorld class:

class HelloWorld : public Gtk::Window
{
  Gtk::Button m_button;

public:
  HelloWorld();
  
  void hello();
  virtual int delete_event_impl(GdkEventAny *event);
  
};

This class implements the "Hello World" window. It's derived from Gtk::Window, and has a single Gtk::Button as a member. We'll be using two signals, and we also override the _impl method for the widget's delete_event signal. We've chosen to use the constructor to do all of the initialisation work for the window, including setting up the signals. Here it is, with the comments omitted:

HelloWorld::HelloWorld()
  : Gtk::Window(GTK_WINDOW_TOPLEVEL),
    m_button("Hello World")
{
  set_border_width(10);

  destroy.connect(slot(&destroy_handler));
  m_button.clicked.connect(slot(this, &HelloWorld::hello));
  m_button.clicked.connect(destroy.slot());

  add(m_button);
  m_button.show();
  show();
}

We've placed two initialiser statements in the declaration. The first one provides an argument to our parent's constructor; the argument we've provided here simply tells GTK-- to make us a full-fledged application window, instead of a transient dialog window or something of that sort. The next initialiser initialises our m_button object; we give it the label "Hello World".

Next we run the window's set_border_width() method. This sets the amount of space between the sides of the window and the widget it contains (windows can only contain a single widget, but this isn't really a limitation, as you'll see later on).

Then we hook up some signals. We need to handle three signals: delete_event and destroy for the window, and clicked for the button. delete_event and destroy are defined in Gtk::Widget, and are therefore common to all GTK-- widgets. (delete_event is one of a special class of signals which correspond to X events. We talk about those in Chapter 2.) A window receives a delete_event when someone clicks its close box; when it receives a destroy, it disappears (the way this mechanism works is explained below).

First we hook up the destroy signal to a callback, destroy_handler(), which looks like this:

void destroy_handler()
{
  Gtk::Main::quit();
}

When destroy_handler() is called, it invokes Gtk::Main::quit(), which, surprisingly enough, quits the program. This is what we want; when our only window disappears, we shouldn't keep running.

We next hook up two callbacks to m_button's clicked signal. The first of these runs one of our member functions, hello(), which prints our friendly greeting to stdout. The other one may be a bit odd-looking at first, because we didn't use slot() here; instead, we used a member function of destroy called slot(). This function, which is part of every libsigc++ Signal object, returns a slot which, when called, will emit the signal it came from. So, when the clicked signal occurs, and it calls this slot, that slot will emit the destroy signal, which will call the destroy_handler() function. (You can chain signals up this way as much as you please.) Therefore, clicking the button causes the program to quit.

Next, we use the window's add() method to put m_button in the window. (add() comes from Gtk::Container, which is described in the chapter on container widgets.) The add() method places the widget you give it in the window, but it doesn't display the widget. GTK-- widgets are always invisible when you create them; to get them to display, you must call their show() method, which is what we do in the next line. The same principle applies to the window itself (remember, it's a widget too), so we show() it next. We could also have used the window's show_all() method, instead of the two calls to show(). show_all() shows not only the widget, but all of the widgets it contains.

That's it for the constructor. Now what about the _impl function we overrode? We did this to handle the window's delete_event signal, as we mentioned earlier. X doesn't destroy windows when it sends them a delete event; in fact, it doesn't do anything to them except send them the event. This is useful when you have, for example, a window that contains a document; catching the delete event allows you to ask the user whether he or she wants to save the document, if it hasn't been.

GTK-- handles this event specially. A signal handler for delete_event is expected to return false if the window should really be destroyed, and true if it should stick around. To demonstrate this, we return true from our delete_event_impl():

int HelloWorld::delete_event_impl(GdkEventAny *event)
{
  cout << "delete event occured" << endl;
  return true;
}

Therefore, when you click the window's close box, it doesn't go away; it merely prints "delete event occured" on stdout. Had we returned false instead, GTK-- would cause our window to emit the destroy signal, and the program would quit.

Incidentally, this is one situation where it's perhaps a little bit silly to use signals. This is a case of a widget catching one of its own signals; instead of bothering to hook up a signal we'd be sending to ourselves, we override the _impl method. This makes sense, because we're subclassing Gtk::Window anyway.

Now let's look at our program's main() function. Here it is, sans comments:

int main (int argc, char *argv[])
{
  Gtk::Main kit(argc, argv);

  HelloWorld helloworld;

  kit.run();
  return 0;
}

First we instantiate an object called kit; this is of type Gtk::Main. Every GTK-- program must have one of these. We pass our command-line arguments to its constructor; it takes the arguments it wants, and leaves you the rest, as we described earlier.

Next we make an object of our HelloWorld class, whose constructor takes no arguments. At this point our window is on the screen; it only remains to invoke the run() method of our Gtk::Main object. This starts up the event loop; every GTK-- program has one of these too. During the event loop, GTK-- idles, waiting for events to come in from the X server. When it gets them, it does with them whatever is appropriate.

Note that if you interrupt the event loop, or don't run it, your program will effectively be "hung", because it won't be able to respond to events. The same can happen if, when you receive a signal, you execute an operation that takes a really long time. You've probably seen X programs hang like this. GTK-- does provide a way for you to give time to the event loop while you're busy doing something; you can also use threads (but that's way beyond the scope of this chapter).

The kit.run() call will return when we invoke the Gtk::Main::quit() function. At that point we can consider ourselves finished, so we return from main() with a successful result code, having successfully spread a little cheer to the world.

If you understood all that, then congratulations: you are now advanced to Journeyman Class, and may proceed to the next chapter. Add four to your maximum hit-points. Go forth and code!


Next Previous Contents