NOTE here is a more readable version of the entire book, generated from DocBook. This page contains the old HTML I was generating myself with a custom program.

GTK+/Gnome Application Development

Havoc Pennington

Part 3: Advanced GTK+/Gnome Techniques

The previous part got you started with a skeletal application. This part of the book describes features available to the Gnome developer as she begins to implement the main functionality of the program. The first few chapters in this part cover some useful GTK+ details. The later chapters branch out to describe facilities unique to Gnome. You may not use all the tools described here for a given application, but you will probably use many of them.

9

The GTK+ Object and Type System

People often ask why GTK+ was written in C rather than an object-oriented language. The answer is that C is more portable and standardly available than any other language. However, although C lacks syntactic sugar for object-oriented programming, it in no way prohibits an object-oriented approach.

GTK+ implements its own custom object system, which offers standard object-oriented features such as inheritance and virtual functions. In the tradition of languages such as Lisp, Smalltalk, and Java, the GTK+ object system is more runtime-centric than that of C++, allowing interpreted language bindings and GUI builders to interact with it in powerful ways.

You may recall from Chapter 3 that widgets are a special type of GtkObject; any object with GtkWidget in its ancestry is a widget. Widgets represent a region on the screen---most of them are user interface elements, such as buttons or menus. There's nothing GUI-specific about GtkObject; the object system can be used in non-graphical programs.

This chapter dives right into the details of GTK+'s object system, giving you an idea what's happening "behind the scenes" in any GTK+ program. Sooner or later you'll need this information: to write your own objects, debug existing objects, or just understand GTK+ code on a conceptual level.

9.1: Object and Class Structures

Each GtkObject has two essential components: a struct representing an instance of the object, and a struct representing the class. In general, the instance struct contains the data members for each instance, and the class struct contains class function pointers (which can be overridden by subclasses). The class struct can also contain class data members---however, it's more typical to use static variables in the .c file implementing the object. If you're familiar with C++, the class struct is equivalent to a vtable, only the class struct is written by hand. It stores virtual functions for an object type.

Here are the structs used in GtkButton:


typedef struct _GtkButton       GtkButton;
typedef struct _GtkButtonClass  GtkButtonClass;

struct _GtkButton
{
  GtkBin bin;

  GtkWidget *child;

  guint in_button : 1;
  guint button_down : 1;
  guint relief : 2;
};

struct _GtkButtonClass
{
  GtkBinClass        parent_class;
  
  void (* pressed)  (GtkButton *button);
  void (* released) (GtkButton *button);
  void (* clicked)  (GtkButton *button);
  void (* enter)    (GtkButton *button);
  void (* leave)    (GtkButton *button);
};


Notice that the first member of struct _GtkButton is GtkBin---that's because GtkButton is a subclass of GtkBin. (GtkBin is a GtkContainer that can hold one child.) Since GtkBin is the first member, we can safely cast a GtkButton to GtkBin. In struct _GtkButtonClass, the same principle applies, and GtkBinClass is the first member.

9.2: Type Checking and New Types

GTK+ has an extensive type system, which is to some extent independent of its object system. However, the object system makes use of the larger type system. Every object has a type, and every type has a unique integer identifier. When writing a GtkObject, it's customary to provide a function which returns the type's identifier.

In the case of GtkButton, the relevant function is:


GtkType gtk_button_get_type();

The first time this function is invoked, it will register a GtkButton type with the object system, and in the process obtain a type identifier. On subsequent calls, the type identifier is simply returned. GtkType is a typedef (unsigned int is the actual type of GTK+'s type identifiers).

PRODUCTION: Please add this footnote to the previous sentence: "You should not make assumptions about GtkType values. However, each GtkType has a unique sequence number associated with it; sequence numbers are optimized to be array indices (that is, they are densely-packed and as small as possible). You can extract sequence numbers with the GTK_TYPE_SEQNO() macro and use them to index a table containing information about types."

The type system allows GTK+ to check the validity of casts. To facilitate this, objects customarily provide macros like these in their header file:


#define GTK_TYPE_BUTTON            (gtk_button_get_type ())
#define GTK_BUTTON(obj)            (GTK_CHECK_CAST ((obj), \
                                    GTK_TYPE_BUTTON, GtkButton))
#define GTK_BUTTON_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), \
                                    GTK_TYPE_BUTTON, GtkButtonClass))
#define GTK_IS_BUTTON(obj)         (GTK_CHECK_TYPE ((obj),  \
                                    GTK_TYPE_BUTTON))
#define GTK_IS_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass),  \
                                    GTK_TYPE_BUTTON))


Instead of simply casting an object, you can use the GTK_BUTTON() macro. If GTK_NO_CHECK_CASTS is defined, these macros are equivalent to simple casts. Otherwise, they retrieve the type of the object and compare it to the type you're attempting to cast to.

PRODUCTION: Please add this footnote to the previous sentence: "When you compile GTK+, use the --enable-debug option to configure to enable type checking." Thanks -hp

GTK+ also provides convenient runtime type checking, with the GTK_IS_BUTTON() macro. This is often used in preconditions; for example, a function expecting a button as an argument might have this check at the beginning:


  g_return_if_fail(GTK_IS_BUTTON(widget));

The GTK+ and Gnome library functions have many such checks. You can also use the macro to make certain code conditional on an object's type, though this is most likely a poor idea from a design standpoint.

To give you an idea what sort of information GTK+ stores about each object type, here's the implementation of gtk_button_get_type():


GtkType
gtk_button_get_type (void)
{
  static GtkType button_type = 0;

  if (!button_type)
    {
      static const GtkTypeInfo button_info =
      {
        "GtkButton",
        sizeof (GtkButton),
        sizeof (GtkButtonClass),
        (GtkClassInitFunc) gtk_button_class_init,
        (GtkObjectInitFunc) gtk_button_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      button_type = gtk_type_unique (GTK_TYPE_BIN, &button_info);
      gtk_type_set_chunk_alloc (button_type, 16);
    }

  return button_type;
}


The code fills in a struct with information about the class, then hands that struct to GTK+ to get a type identifier (GtkType). Only six components of the GtkTypeInfo struct are important. GtkButton gives GTK+ a human-readable name for the class, used in error messages and the like; the size of the instance and class structs; then a function to initialize the class struct and another to initialize each new instance. The sixth and seventh members of the struct (reserved_1 and reserved_2) are obsolete and preserved only for compatibility. The final member is a pointer to a base class initialization function, used to initialize the class struct of any subclasses.

gtk_type_unique() registers the new type and obtains a type identifier. The GTK_TYPE_BIN argument is a macro containing the type of GtkButton's parent class, GtkBin. The call to gtk_type_set_chunk_alloc() optimizes memory allocation for this type; it is never required, and should only be used for frequently-allocated types like GtkButton.

Given a registered GtkButton type, the following code creates a type instance:


GtkWidget*
gtk_button_new (void)
{
  return GTK_WIDGET (gtk_type_new (gtk_button_get_type ()));
}


The newborn GtkButton will be initialized by its instance initializer. The instance initialization function is called each time an instance of the type is created; it gives the object's data members reasonable default values:


static void
gtk_button_init (GtkButton *button)
{
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS);
  GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);

  button->child = NULL;
  button->in_button = FALSE;
  button->button_down = FALSE;
  button->relief = GTK_RELIEF_NORMAL;
}


Remember that gtk_button_init() was passed to gtk_type_unique() when the GtkButton type was created. GTK+ stores the function pointer and uses it to create GtkButton instances.

Instance structs are created with all bits set to 0; so settings members to 0 or NULL is not strictly necessary. Still, most GTK+ code does initialize the members, for clarity.

The class initialization and base class initialization functions require some background information to understand fully; you will know how to write them after you read this chapter.

9.3: Initializing a New Class

When a type is first used, GTK+ creates an instance of its class struct (using the information supplied to gtk_type_unique()). To initialize the class struct for a type, GTK+ first checks that all parent classes are initialized and initializes them if not. Then it fills the top portion of the class struct with a byte-for-byte copy of the parent's class struct. This means the subclass inherits any function pointers found in the parent class.

Next, the base class initialization functions of each parent class and that of the class itself are called in order, starting with GtkObject. (The base class init function is the last argument to gtk_type_unique()). A base class initializer is optional; in the GtkButton case, there is none. If present, the base class initializer supplements the byte-for-byte copy of the class struct; for example, some functions should not be inherited. To prevent class function inheritance, the base class initializer can zero certain function pointers. Normally you do not need a base class initializer.

Finally, GTK+ calls the type's own class init function. The class init function can override functions from the parent class by replacing them in the class struct. It should also fill in any functions unique to the subclass, and register signals and object arguments (discussed later in the chapter).

A concrete example should make the class creation process clear. The class hierarchy for GtkButton is shown in Figure 9.1. When the GtkButton type is registered, an empty GtkButtonClass is created. This class struct is initialized as follows:

  1. The class struct for GtkBin, GtkButton's immediate parent, is copied into it. This means GtkButton inherits class functions from GtkBin.
  2. The base class initialization function for GtkObject is called on it. This zeroes some GtkObject class functions that should not be inherited.
  3. There is no base class initializer for GtkWidget, or it would be called.
  4. The base class initializer for GtkContainer is called. This zeroes some GtkContainer class functions that should not be inherited, and initializes a GtkContainerClass data member.
  5. There is no base class initializer for GtkBin, or it would be called.
  6. There is no base class initializer for GtkButton, or it would be called.
  7. The class initializer is called for GtkButton. This fills in the GtkButtonClass structure, registers signals, and registers object arguments.

When writing a new class, you only need to concern yourself with the final two steps---you should consider whether a base class initializer is needed, and supply it if so; you must supply a class initializer in all cases.

Figure 9.1: GtkButton Ancestry

PRODUCTION: The "GtkButton Ancestry" figure should show the following information, drawn nicely:


GtkObject
   |
GtkWidget
   | 
GtkContainer
   | 
GtkBin
   | 
GtkButton

Here is the GtkButton class initialization function, just to give you an initial sense of things; read on to learn what this code does.


static void
gtk_button_class_init (GtkButtonClass *klass)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;

  parent_class = gtk_type_class (GTK_TYPE_BIN);

  gtk_object_add_arg_type ("GtkButton::label", 
                           GTK_TYPE_STRING, 
                           GTK_ARG_READWRITE, 
                           ARG_LABEL);
  gtk_object_add_arg_type ("GtkButton::relief", 
                           GTK_TYPE_RELIEF_STYLE, 
                           GTK_ARG_READWRITE, 
                           ARG_RELIEF);


  button_signals[PRESSED] =
    gtk_signal_new ("pressed",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, pressed),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[RELEASED] =
    gtk_signal_new ("released",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, released),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[CLICKED] =
    gtk_signal_new ("clicked",
                    GTK_RUN_FIRST | GTK_RUN_ACTION,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, clicked),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[ENTER] =
    gtk_signal_new ("enter",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, enter),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[LEAVE] =
    gtk_signal_new ("leave",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, leave),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);


  object_class->set_arg = gtk_button_set_arg;
  object_class->get_arg = gtk_button_get_arg;

  widget_class->activate_signal = button_signals[CLICKED];
  widget_class->realize = gtk_button_realize;
  widget_class->draw = gtk_button_draw;
  widget_class->draw_focus = gtk_button_draw_focus;
  widget_class->draw_default = gtk_button_draw_default;
  widget_class->size_request = gtk_button_size_request;
  widget_class->size_allocate = gtk_button_size_allocate;
  widget_class->expose_event = gtk_button_expose;
  widget_class->button_press_event = gtk_button_button_press;
  widget_class->button_release_event = gtk_button_button_release;
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
  widget_class->focus_in_event = gtk_button_focus_in;
  widget_class->focus_out_event = gtk_button_focus_out;

  container_class->add = gtk_button_add;
  container_class->remove = gtk_button_remove;
  container_class->child_type = gtk_button_child_type;

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
  klass->enter = gtk_real_button_enter;
  klass->leave = gtk_real_button_leave;
}


9.4: GtkArg and the Type System

Before delving further into GtkObject, you will need more details on GTK+'s type system. The type system is used in many contexts:

Because of its type system, GTK+ is particularly easy to manipulate from dynamically-typed, interactive languages. There are bindings available for nearly all popular languages, and the bindings can be lightweight (since GTK+ already includes much of the needed functionality, and types can be handled generically so the amount of glue code is reduced). You can find a complete list of functions for querying and using GTK+'s type system in gtk/gtktypeutils.h; most of these are not useful in applications. Only the functions of general interest are described in this book.

GTK+ has a number of so-called fundamental types which are automatically registered during gtk_init() (or gnome_init()). The fundamental types include all the primitive C types, some GTK+ types such as GTK_TYPE_SIGNAL, and GTK_TYPE_OBJECT. Fundamental types are essentially the "base classes" understood by the GTK+ type system; for example, the fundamental type of any enumeration is GTK_TYPE_ENUM, and the fundamental type of any GtkObject subclass is GTK_TYPE_OBJECT. Fundamental types are supposed to cover all the "special cases" in the GTK+ type system; all types ultimately derive from some fundamental type. A type's fundamental type is extracted from a GtkType with the GTK_FUNDAMENTAL_TYPE() macro. The fundamental types are shown in Table 9.1.

There is a second category of GtkType values: builtin types are registered by GTK+ and libgnomeui during library initialization and are thus always available. Builtin types include enumerations, flags, and some structs (GdkWindow, or GdkImlibImage, for example). Builtin types are distinct from fundamental types because the GTK+ object system does not have to understand them; for the purposes of getting and setting argument values, they can be treated as fundamental types. They are somewhat arbitrarily distinguished from user-registered enumeration or flag types. (The difference between builtin types and user types is the time of registration.)

Builtin types are all accessible via macros that come with GTK+ and Gnome. These begin with GTK_TYPE_, as in: GTK_TYPE_WINDOW, GTK_TYPE_GDK_WINDOW, GTK_TYPE_RELIEF_STYLE, GTK_TYPE_GNOME_DIALOG. As you can see, the name of the type macro is derived from the name of the GtkObject, struct, or enumeration; if the object name begins with "Gtk," the "Gtk" is dropped. The above examples map to the GtkWindow widget, GdkWindow struct, GtkReliefStyle enumeration, and GnomeDialog widget, respectively.

The final major category of GtkType values consists of the registered GtkObject types. These are registered the first time the _get_type() routine for each object is called.

Table 9.1: The GTK+ Fundamental Types
GtkType Constant Corresponding C Type
GTK_TYPE_INVALIDNone
GTK_TYPE_NONEvoid
GTK_TYPE_CHARgchar
GTK_TYPE_UCHARguchar
GTK_TYPE_BOOLgboolean
GTK_TYPE_INTgint
GTK_TYPE_UINTguint
GTK_TYPE_LONGglong
GTK_TYPE_ULONGgulong
GTK_TYPE_FLOATgfloat
GTK_TYPE_DOUBLEgdouble
GTK_TYPE_STRINGgchar*
GTK_TYPE_ENUMAny enumeration
GTK_TYPE_FLAGSguint
GTK_TYPE_BOXEDgpointer
GTK_TYPE_POINTERgpointer
GTK_TYPE_SIGNALGtkSignalFunc, gpointer
GTK_TYPE_ARGSgint, GtkArg*
GTK_TYPE_CALLBACKGtkCallbackMarshal, gpointer, GtkDestroyNotify
GTK_TYPE_C_CALLBACKGtkFunction, gpointer
GTK_TYPE_FOREIGNgpointer, GtkDestroyNotify
GTK_TYPE_OBJECTGtkObject*

Some of the fundamental types require further explanation. In brief:

A fundamental type describes not only describe the data layout but also how memory is managed. For values passed in as arguments, the called function is not allowed to retain the pointer beyond the duration of the call. For returned values, the caller assumes ownership of the memory. GTK_TYPE_BOXED, GTK_TYPE_ARGS, and GTK_TYPE_STRING obey this rule.

Note that you should almost always use the most informative type available. Notably, GTK_TYPE_POINTER should only be used for generic pointers (gpointer); whenever possible, prefer a "subclass" of GTK_TYPE_BOXED such as GTK_TYPE_GDK_WINDOW or GTK_TYPE_GDK_EVENT. Similarly, it is better to use a specific enumeration type, rather than GTK_TYPE_ENUM. GTK_TYPE_CALLBACK is normally preferred to GTK_TYPE_C_CALLBACK or GTK_TYPE_SIGNAL, because GTK_TYPE_CALLBACK includes information about how to marshal the function and destroy the callback data.

GTK+ has a consistent interface for passing typed values around; to do this, it needs a data structure which stores a type tag and a value. GtkArg fills the bill. Here is its definition, from gtk/gtktypeutils.h:


typedef struct _GtkArg GtkArg;

struct _GtkArg
{
  GtkType type;
  gchar *name;

  union {
    gchar char_data;
    guchar uchar_data;
    gboolean bool_data;
    gint int_data;
    guint uint_data;
    glong long_data;
    gulong ulong_data;
    gfloat float_data;
    gdouble double_data;
    gchar *string_data;
    gpointer pointer_data;
    GtkObject *object_data;
    
    struct {
      GtkSignalFunc f;
      gpointer d;
    } signal_data;
    struct {
      gint n_args;
      GtkArg *args;
    } args_data;
    struct {
      GtkCallbackMarshal marshal;
      gpointer data;
      GtkDestroyNotify notify;
    } callback_data;
    struct {
      GtkFunction func;
      gpointer func_data;
    } c_callback_data;
    struct {
      gpointer data;
      GtkDestroyNotify notify;
    } foreign_data;
  } d;
};

The type field contains the value's GtkType, as you might expect. The name field is an object argument name---more on arguments in a moment. The final union stores a value of the appropriate type; there is one union member for each fundamental type. This value field should be accessed using a special set of macros provided for the purpose, listed in Macro Listing 9.1; each macro corresponds to a fundamental type. These macros are defined so that you can use the & operator on them; e.g. &GTK_VALUE_CHAR(arg).

To print a GtkArg's value, you might write code like this:


   GtkArg arg;

   /* ... */

   switch (GTK_FUNDAMENTAL_TYPE (arg.type))
     {
     case GTK_TYPE_INT:
       printf("arg: %d\n", GTK_VALUE_INT(arg));
       break;
     /* ... case for each type ... */
     }

Macro Listing 9.1: Macros for Accessing GtkArg Values
 #include <gtk/gtktypeutils.h>
GTK_VALUE_CHAR(arg)
GTK_VALUE_UCHAR(arg)
GTK_VALUE_BOOL(arg)
GTK_VALUE_INT(arg)
GTK_VALUE_UINT(arg)
GTK_VALUE_LONG(arg)
GTK_VALUE_ULONG(arg)
GTK_VALUE_FLOAT(arg)
GTK_VALUE_DOUBLE(arg)
GTK_VALUE_STRING(arg)
GTK_VALUE_ENUM(arg)
GTK_VALUE_FLAGS(arg)
GTK_VALUE_BOXED(arg)
GTK_VALUE_POINTER(arg)
GTK_VALUE_OBJECT(arg)
GTK_VALUE_SIGNAL(arg)
GTK_VALUE_ARGS(arg)
GTK_VALUE_CALLBACK(arg)
GTK_VALUE_C_CALLBACK(arg)
GTK_VALUE_FOREIGN(arg)

Some uses of GtkArg require you to assign a value to it. The GTK_VALUE_ macros are not appropriate here; instead, a parallel set of macros exist which return a pointer to an assignable location. These are called GTK_RETLOC_CHAR(), GTK_RETLOC_UCHAR(), and so on.

9.5: Object Arguments

Arguments are one of the most interesting features of GtkObject. Arguments are a mechanism for handling what CORBA's Interface Definition Language (IDL) calls an attribute: a value with a "getter" and a "setter." In concrete terms, object arguments pair a key (which is a string) with a value (represented as a GtkArg). Each GtkObject subclass can register permissible keys and the GtkTypes of their associated values.

Using object arguments, one can discover at runtime what attributes an object has, and then get or set their values. This is very useful for people implementing GUI builders, since some of the widget configuration dialogs can be automated. Similarly, it makes it much easier to write GTK+ bindings for scripting languages. It can also be convenient for programmers, since they can avoid writing all the get/set functions---the GnomeCanvas, for example, uses object arguments for almost all of its API. Finally, object arguments may be configurable via the gtkrc configuration mechanism in a future version of GTK+, making it possible for users to extensively customize GTK+ software.

9.5.1: Setting Object Arguments

Most commonly, arguments are used as an API to set attributes of widgets. However, not all of the GTK+ API has been exported via arguments, so it is not always possible.

PRODUCTION: Please add a footnote to the previous sentence: "This is due to lack of time rather than lack of will; patches to add argument support to objects that don't have it are welcome."

To set widget attributes, the most convenient interface is gtk_object_set(). Here's an example:


   gtk_object_set(GTK_OBJECT(vbox), 
                  "GtkContainer::border_width", (gulong) 10,
                  NULL);

The above code is identical in effect to:


   gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);

It's up to you which to use; it depends on the context. Typically, you would use the argument mechanism if you have a reason to, i.e. if you are using its dynamic, runtime-oriented features. However, if you are setting several attributes, it may be easier to type and read.

gtk_object_set() takes a GtkObject as the first argument, followed by any number of key-value pairs. If a key is not defined for the object you pass in, a runtime error will be triggered. The list of key-value pairs must be terminated with a NULL key. When a GtkObject registers itself with GTK+, it tells GTK+ what type of value to expect after each key. For the aggregate fundamental types gtk_object_set() will expect more than one C function argument after the key. For example, first a signal function and then a user data pointer will be expected after GTK_TYPE_SIGNAL arguments. (Table 9.1 gives the types of the expected arguments.)

It is permissible to leave off the object class portion of an argument name---"GtkContainer::border_width" can be simply "border_width":


   gtk_object_set(GTK_OBJECT(vbox), 
                  "border_width", (gulong) 10,
                  NULL);

If you do not specify the class name as part of the argument name, GTK+ will start with the real type of the object and look up the argument name in the argument table for each superclass until it finds the right one (GtkContainer in this case). If you do specify the class name, GTK+ will only look for the argument in the specified class's argument table.

Since gtk_object_set() uses C variable argument lists, it has limited type safety. This can be a real problem in your code. You may have noticed the cast to gulong in the sample call to gtk_object_set(). The argument GtkContainer::border_width has type GTK_TYPE_ULONG. GTK+ will extract sizeof(gulong) bytes from the argument list when it encounters this argument. If you leave out the cast, C will probably pass only sizeof(gint) bytes to the function. As you might imagine, this causes memory corruption on many platforms. A similar problem arises with arguments of type GTK_TYPE_DOUBLE; if you type 5 instead of 5.0, C will pass an integer to gtk_object_set(). These bugs are very hard to find, once you introduce them.

gtk_object_set() is syntactic sugar for a more fundamental function call, gtk_object_setv(). gtk_object_setv() takes a vector of GtkArg (gtk_object_set() converts each key-value pair in its argument list to GtkArg internally).


   GtkArg args[1];
   args[0].name = "GtkContainer::border_width";
   args[0].type = GTK_TYPE_ULONG;
   GTK_VALUE_ULONG(args[0]) = 10;
   gtk_object_setv(GTK_OBJECT(button), 
                   1,
                   args);

The second argument to gtk_object_setv() is the length of the array of GtkArg. gtk_object_set() is plainly easier to use when you are typing the code in manually, but gtk_object_setv() can be passed a dynamically-constructed argument array---which is convenient if you're exporting GTK+ functionality to an interpreted environment.

It is also possible to set object arguments when objects are created. You can create most objects using the gtk_object_new() function, and most widgets with the gtk_widget_new() function. The routines take a GtkType as their first argument, and create an object or widget of that type. They then take a list of argument-value pairs, just as gtk_object_set() does. There are also gtk_object_newv() and gtk_widget_newv() variants.

9.5.2: Reading Object Arguments

To get the value of one or more arguments, you simply create an array of GtkArg, filling in the name field of each GtkArg. gtk_object_getv() fills in the type fields and the argument values. If an error occurs, the type field is set to GTK_TYPE_INVALID. If the fundamental type of the returned value is GTK_TYPE_STRING, GTK_TYPE_BOXED, or GTK_TYPE_ARGS, you are responsible for freeing it.

Here's a simple example:


  GtkArg args[2];
    
  args[0].name = "GtkContainer::border_width";
  args[1].name = "GtkContainer::resize_mode";
  gtk_object_getv(GTK_OBJECT(button), 
                  2, 
                  args);

  g_assert(args[0].type == GTK_TYPE_ULONG);
  g_assert(args[1].type == GTK_TYPE_RESIZE_MODE);
  g_assert(GTK_FUNDAMENTAL_TYPE(args[1].type) == GTK_TYPE_ENUM);

  printf("Border width: %lu Resize mode: %d\n", 
         GTK_VALUE_ULONG(args[0]), GTK_VALUE_ENUM(args[1]));

9.5.3: Using Object Arguments in Your Own GtkObject Subclass

If you're writing a custom GtkObject or a custom subclass of some existing object, you can register your own object arguments in the class initialization function, at the same time you register your object's signals. To do this, call gtk_object_add_arg_type()---here's an example from GtkContainer:


    gtk_object_add_arg_type("GtkContainer::border_width", 
                            GTK_TYPE_ULONG, 
                            GTK_ARG_READWRITE, 
                            ARG_BORDER_WIDTH);
  

The first argument must be a static string constant, because GTK+ does not copy it. It must also begin with the name of your new class, separated from the name of the argument by two colons (reminiscent of the C++ scope operator). The second argument should be the type of the argument; this can be any GtkTypeGTK+ knows about.

The third argument contains one or more flags, defined in gtk/gtkobject.h. The available flags are:

There are some limitations on which flags can be used.

The fourth and final argument to gtk_object_add_arg_type() is an argument ID to be used by the object subclass to identify this argument. This can be any integer except 0, but it is customary to use a private enumeration in the object implementation's .c file. GtkObject has two class functions any subclass with arguments must implement: one to get arguments specific to the subclass, and one to set them. These functions are passed the argument ID, so they know which argument to get or set. Argument IDs reduce the need for string comparisons, increasing the efficiency of argument manipulation.

For example, GtkContainer defines these functions:

 
static void gtk_container_get_arg(GtkObject* object,
                                  GtkArg* arg,
                                  guint arg_id);
static void gtk_container_set_arg(GtkObject* object,
                                  GtkArg* arg,
                                  guint arg_id);

It uses this enumeration to create its argument IDs:


enum {
  ARG_0,              /* Skip 0, an invalid argument ID */
  ARG_BORDER_WIDTH,
  ARG_RESIZE_MODE,
  ARG_CHILD
};

It registers its arguments in gtk_container_class_init() as follows:


  gtk_object_add_arg_type("GtkContainer::border_width",
                          GTK_TYPE_ULONG, 
                          GTK_ARG_READWRITE, 
                          ARG_BORDER_WIDTH);
  gtk_object_add_arg_type("GtkContainer::resize_mode", 
                          GTK_TYPE_RESIZE_MODE, 
                          GTK_ARG_READWRITE, 
                          ARG_RESIZE_MODE);
  gtk_object_add_arg_type("GtkContainer::child", 
                          GTK_TYPE_WIDGET, 
                          GTK_ARG_WRITABLE, 
                          ARG_CHILD);

gtk_container_set_arg() and gtk_container_get_arg() are installed in the class struct:


  object_class->get_arg = gtk_container_get_arg;
  object_class->set_arg = gtk_container_set_arg;

gtk_container_set_arg() and gtk_container_get_arg() are then implemented like this:


static void
gtk_container_set_arg (GtkObject    *object,
                       GtkArg       *arg,
                       guint         arg_id)
{
  GtkContainer *container;

  container = GTK_CONTAINER (object);

  switch (arg_id)
    {
    case ARG_BORDER_WIDTH:
      gtk_container_set_border_width (container, GTK_VALUE_ULONG (*arg));
      break;
    case ARG_RESIZE_MODE:
      gtk_container_set_resize_mode (container, GTK_VALUE_ENUM (*arg));
      break;
    case ARG_CHILD:
      gtk_container_add (container, GTK_WIDGET (GTK_VALUE_OBJECT (*arg)));
      break;
    default:
      break;
    }
}

static void
gtk_container_get_arg (GtkObject    *object,
                       GtkArg       *arg,
                       guint         arg_id)
{
  GtkContainer *container;

  container = GTK_CONTAINER (object);
  
  switch (arg_id)
    {
    case ARG_BORDER_WIDTH:
      GTK_VALUE_ULONG (*arg) = container->border_width;
      break;
    case ARG_RESIZE_MODE:
      GTK_VALUE_ENUM (*arg) = container->resize_mode;
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
}    

Notice that the type must be set to GTK_TYPE_INVALID if your subclass doesn't understand the argument ID. This is used as an error indicator; users who call gtk_object_getv() will check for it.

If you flip back to page XXXX and have another look at the GtkButton class initialization function, you should now understand what is going on with respect to object arguments.

PRODUCTION: Please replace XXXX with the page number of the button class initialization function. This is at the end of Section 9.3 just before Section 9.4.

9.5.4: Discovering the Available Object Arguments

You can easily find out at runtime what arguments a given GtkObject understands, using the gtk_object_query_args(). Here is a nifty piece of code which prints out the arguments for the entire class hierarchy of a given GtkObject:


void
print_arguments(GtkObject* object)
{
  GtkType type;

  type = GTK_OBJECT_TYPE(object);

  do {
    GtkArg* args;
    guint32* flags;
    guint n_args;
    guint i;

    args = gtk_object_query_args(type,
                                 &flags, 
                                 &n_args);
  
    printf("Displaying arguments for object type `%s'\n",
           gtk_type_name(type));

    i = 0;
    while (i < n_args)
      {
        printf(" - Argument %u is called `%s' and has type `%s'\n",
               i, 
               args[i].name, 
               gtk_type_name(args[i].type));
      
        ++i;
      }

    g_free(args);
    g_free(flags);

    type = gtk_type_parent(type);
  } 
  while (type != GTK_TYPE_INVALID);
}

Notice that a type's parent type can be obtained using the gtk_type_parent() function, and that you can extract the GtkType tag from a GtkObject using the GTK_OBJECT_TYPE() macro. GTK_OBJECT_TYPE() is defined as follows:


#define GTK_OBJECT_TYPE(obj) (GTK_OBJECT (obj)->klass->type)

An object's type is stored in its class structure, and a pointer to an object's class structure is stored in each instance of the object. (The class structure pointer is called klass rather than class to avoid confusing C++ compilers.)

Function Listing 9.1 summarizes the functions for reading, writing, and querying object arguments.

Function Listing 9.1: Manipulating Object Arguments
 #include <gtk/gtkobject.h>
void
gtk_object_getv(GtkObject* object,
                guint n_args,
                GtkArg* args)
void
gtk_object_set(GtkObject* object,
               const gchar* first_arg_name,
               ...)
void
gtk_object_setv(GtkObjec* object,
                guint n_args,
                GtkArg* args)
void
gtk_object_add_arg_type(const gchar* arg_name,
                        GtkType arg_type,
                        guint arg_flags,
                        guint arg_id)
GtkArg*
gtk_object_query_args(GtkType class_type,
                      guint32** arg_flags,
                      guint* n_args)

9.6: Signals

A GtkObject can emit a signal. Signals are stored in a global table by GTK+. Handlers or callbacks can be connected to signals; when a signal is emitted, its callbacks are invoked. The process of invoking all handlers for a signal is called emission.

Abstractly, a signal is a kind of message that an object wants to broadcast; the kind of message is associated with certain conditions (such as the user selecting a list item) and with message-specific parameter types which are passed to connected callbacks (such as the index of the row the user selected). User callbacks are connected to a particular signal and to a particular object instance. That is, you do not connect callbacks to the "clicked" signal of all buttons; rather, you connect to the "clicked" signal of a particular one. (However, there is a way to monitor all emissions of a signal---these callbacks are called "emission hooks.")

Signals are typically associated with a class function pointer which is invoked every time the signal is emitted; if non-NULL, the pointed-to class function serves as a default handler for the signal. It is up to the author of each GtkObject subclass whether to provide a space in the class struct for a default handler, and whether to implement the default handler in the base class. Conventionally, signals have the same name as the class function they are associated with.

For example, the GtkButtonClass struct has a member called clicked; this member is registered as the default handler for the "clicked" signal. However, the GtkButton base class does not implement a default handler, and leaves the clicked member set to NULL. Subclasses of GtkButton could optionally fill it in with an appropriate function. If GtkButton did implement a default clicked handler, subclasses could still override it with a different one.

Note that GTK+ signals have nothing to do with UNIX signals. Sometimes new GTK+ users confuse the two.

9.6.1: Adding a New Signal

Once you understand the GTK+ type system and GtkArg, signal registration is fairly transparent. Here is the signal registration code from GtkButton again:


  button_signals[PRESSED] =
    gtk_signal_new ("pressed",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, pressed),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[RELEASED] =
    gtk_signal_new ("released",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, released),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[CLICKED] =
    gtk_signal_new ("clicked",
                    GTK_RUN_FIRST | GTK_RUN_ACTION,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, clicked),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[ENTER] =
    gtk_signal_new ("enter",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, enter),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
  button_signals[LEAVE] =
    gtk_signal_new ("leave",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkButtonClass, leave),
                    gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);

  gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);



Earlier in gtkbutton.c, an enumeration and an array were declared as follows:


enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
  LAST_SIGNAL
};


static guint button_signals[LAST_SIGNAL] = { 0 };


gtk_signal_new() has the following effects:

gtk_object_class_add_signals() attaches signal identifiers to the object's class struct, so the signals for a given class can be rapidly located. Conventionally, the argument to this function is an enumeration-indexed static array, as shown for GtkButton. The static array is also useful when implementing the functionality of the class (the signal identifiers are used to emit the signals).

The first argument to gtk_signal_new() is a name for the signal; you refer to the signal by name when you call gtk_signal_connect(), for example. The third argument is the GtkType of the object type emitting the signal, and the fourth is the location of the associated class function in the type's class struct. A macro is provided to compute the offset. If you specify an offset of 0, no class function will be associated with the signal. Note that giving a zero offset is distinct from giving a valid offset but setting the function member in the struct to NULL; in the latter case, subclasses of the object can fill in a value for the default handler.

The second argument is a bitfield. Here are the associated flags:

The last few arguments to gtk_signal_new() provide a marshaller, and tell GTK+ the marshaller's type. A marshaller invokes a callback function, based on an array of GtkArg it receives from GTK+. Marshallers are needed because C function argument lists cannot be constructed at runtime. GTK+ comes with a number of prewritten marshallers; here is the one used for all GtkButton signals:


typedef void (*GtkSignal_NONE__NONE) (GtkObject* object,
                                      gpointer user_data);
void 
gtk_marshal_NONE__NONE (GtkObject * object,
                        GtkSignalFunc func,
                        gpointer func_data,
                        GtkArg * args)
{
  GtkSignal_NONE__NONE rfunc;
  rfunc = (GtkSignal_NONE__NONE) func;
  (*rfunc) (object,
            func_data);
}

As you can see, the NONE__NONE refers to the fact that the expected callback type returns no value and has no "special" arguments. GTK+ automatically passes the object emitting the signal and a user_data field to all callbacks; special signal arguments are inserted in between these two. Since there are no signal-specific arguments in this case, the array of GtkArg is ignored.

The naming convention for marshallers places a double underscore between the return value and the special arguments, if any. Here's a more complex example:


typedef gint (*GtkSignal_INT__POINTER) (GtkObject * object,
                                        gpointer arg1,
                                        gpointer user_data);
void 
gtk_marshal_INT__POINTER (GtkObject * object,
                          GtkSignalFunc func,
                          gpointer func_data,
                          GtkArg * args)
{
  GtkSignal_INT__POINTER rfunc;
  gint *return_val;
  return_val = GTK_RETLOC_INT (args[1]);
  rfunc = (GtkSignal_INT__POINTER) func;
  *return_val = (*rfunc) (object,
                          GTK_VALUE_POINTER (args[0]),
                          func_data);
}

Notice that the last element of the array of GtkArg is a space for the return value; if there is no return value, this element will have type GTK_TYPE_NONE and can be ignored. GTK+ provides macros such as GTK_RETLOC_INT() to extract a "return location" from a GtkArg. Similar GTK_RETLOC_ macros exist for all the fundamental types.

The function pointer signatures in the class structure for an object will correspond to the type of the signal. This is a convenient way to find out what signature the callbacks connected to a signal should have, if the GTK+ header files are readily available on your system.

The last arguments to gtk_signal_new() give the type of the signal's marshaller. First a return value type is given, then the number of special arguments, then a variable argument list containing that many GtkType values in the appropriate order. Since GtkButton has no examples of signals with arguments, here is one from GtkWidget:


  widget_signals[BUTTON_PRESS_EVENT] =
    gtk_signal_new("button_press_event",
                   GTK_RUN_LAST,
                   object_class->type,
                   GTK_SIGNAL_OFFSET (GtkWidgetClass, button_press_event),
                   gtk_marshal_BOOL__POINTER,
                   GTK_TYPE_BOOL, 1,
                   GTK_TYPE_GDK_EVENT);

"button_press_event" returns a boolean value, and has a GdkEvent* argument. Notice that the marshaller works with any GTK_TYPE_POINTER, but the signal requires the more-specific boxed type GTK_TYPE_GDK_EVENT, allowing language bindings to query the correct kind of pointer.

Signals can have many arguments; here is one from GtkCList:


  clist_signals[SELECT_ROW] =
    gtk_signal_new ("select_row",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GtkCListClass, select_row),
                    gtk_marshal_NONE__INT_INT_POINTER,
                    GTK_TYPE_NONE, 3,
                    GTK_TYPE_INT,
                    GTK_TYPE_INT,
                    GTK_TYPE_GDK_EVENT);

The "select_row" signal returns no value, but has three arguments (the selected row and column number, and the event that caused the selection).

9.6.2: Using Existing Signals

Function Listing 9.2 shows the wide array of functions available for manipulating signals. You should already be familiar with the most fundamental signal operation: connecting a signal handler to be invoked when the signal is emitted, like this:


  gtk_signal_connect(GTK_OBJECT(window),
                     "delete_event",
                     GTK_SIGNAL_FUNC(delete_event_cb),
                     NULL);

  gtk_signal_connect(GTK_OBJECT(button),
                     "clicked",
                     GTK_SIGNAL_FUNC(button_click_cb),
                     label);


You may not be aware that gtk_signal_connect() returns a "handler ID" which can be used to refer to the connection it creates. Using the handler ID, you can unregister the callback with gtk_signal_disconnect(). You can also temporarily "block" the callback by calling gtk_signal_handler_block(). This increments a "block count"; the callback will not be invoked until the block count returns to 0. gtk_signal_handler_unblock() decrements the block count. Both gtk_signal_disconnect() and gtk_signal_handler_unblock() have variants that search for the handler ID given a callback function or user data pointer; these are possibly more convenient, with some loss of efficiency.

It can be useful to block signal handlers if you'll be changing some aspect of an object yourself, and thus don't need to run the callbacks you use to respond to user actions. For example, you normally change some boolean variable if the user clicks a toggle button, in a callback to the "toggled" signal. If you update the toggle button programmatically because the flag was changed via some mechanism other than the button, "toggled" will still be emitted; but you want to block your callback, since the flag is already correct.

gtk_signal_connect() is not the only way to connect to a signal. You can also use gtk_signal_connect_object(); this simply swaps the signal-emitting object pointer and the user data pointer in the arguments passed to the callback. Normally, the object comes first, then any arguments unique to the signal, and finally the user data pointer; with gtk_signal_connect_object(), the object is last and user data is first. This function is useful when you want to use a pre-existing function as a callback without writing a wrapper to move its arguments. For example:


  gtk_signal_connect_object(GTK_OBJECT(button),
                            "clicked",
                            GTK_SIGNAL_FUNC(gtk_widget_destroy),
                            GTK_OBJECT(dialog));

Because the user data and the button are swapped, the first argument to gtk_widget_destroy() will be the dialog rather than the button, closing the dialog. When using gtk_signal_connect_object(), your callback data must be a GtkObject to avoid confusing marshallers that expect an object as their first argument.

gtk_signal_connect_after() asks GTK+ to run the callback after the object's default signal handler, rather than before it. This only works with certain signals, those with the GTK_RUN_LAST flag set; Section 9.6.1 explains this flag.

gtk_signal_connect_object_after() combines the effects of gtk_signal_connect_object() and gtk_signal_connect_after().

gtk_signal_connect_full() gives you complete control over the connection and is mostly useful in language bindings. The object_signal and after arguments can be TRUE or FALSE, toggling argument order and time of callback invocation. The functions we just mentioned also let you change this, so gtk_signal_connect_full() adds little. Its unique features are the ability to specify a callback marshaller, and the ability to specify a GtkDestroyNotify function. Notice that gtk_signal_connect_full() does not expect the same kind of marshaller described in Section 9.6.1; it expects a more general marshaller appropriate for marshalling functions written in languages other than C. If you give a non-NULLGtkDestroyNotify function, it will be invoked on the user data pointer when this handler is disconnected or the GtkObject is destroyed. Here is the proper signature for the function:


  typedef void (*GtkDestroyNotify)   (gpointer   data);

Conveniently, you can use g_free() or gtk_object_destroy() as a GtkDestroyNotify. Of course, if these aren't appropriate you can write a custom function.

gtk_signal_connect_while_alive() is a variant on gtk_signal_connect(); its additional argument is an object to monitor. When the monitored object is destroyed (emits the "destroy" signal), the handler will be disconnected. That is, handlers connected with this function are automatically disconnected when a specified object no longer exists.

There's rarely a need to do so, but you can look up a signal's ID number given the signal name and the object type that emits it. This function is gtk_signal_lookup(). Note that names are not globally unique, but they are unique with respect to a particular object type. On the other hand, signal IDs are globally unique.

During the emission of a signal (that is, during the process of invoking its handlers), you can call gtk_signal_emit_stop() (or its _by_name() variant) to halt the emission. These functions are only useful from within signal handlers, because they must be called during the emission process or they won't have anything to stop. They do not take effect immediately; instead, they set a variable that GTK+ checks at key points during emission. Section 9.6.4 describes this in detail.

Emission hooks can be used to monitor all emissions of a given signal (regardless of the object instance doing the emitting). Emission hooks have the following signature:


typedef gboolean (*GtkEmissionHook) (GtkObject      *object,
                                     guint           signal_id,
                                     guint           n_params,
                                     GtkArg         *params,
                                     gpointer        data);

They are passed the same parameters GTK+ would normally pass to callback marshallers (see Section 9.6.1). You can connect an emission hook with a destroy notify function to be invoked on the user data pointer when the hook is removed. When you add an emission hook, an integer identify is returned. You can remove emission hooks with this ID number.

Emission hooks are rarely useful, but sometimes they are the only way to do something. For example, Gnome optionally plays sound effects when certain signals are emitted (such as button clicks).

Finally, you can ask everything you ever wanted to know about a signal using gtk_signal_query(). This function is intended for GUI builders and language bindings to use; it is probably not useful in application code. It returns a GtkSignalQuery structure filled with information about the signal. The return value should be freed with g_free() but not modified in any way (it contains pointers to internal data which isn't copied). Here is the definition of GtkSignalQuery:


typedef struct _GtkSignalQuery GtkSignalQuery;

struct  _GtkSignalQuery
{
  GtkType          object_type;
  guint            signal_id;
  const gchar     *signal_name;
  guint            is_user_signal : 1;
  GtkSignalRunType signal_flags;
  GtkType          return_val;
  guint            nparams;
  const GtkType   *params;
};

Function Listing 9.2: Using Signals
 #include <gtk/gtksignal.h>
guint
gtk_signal_lookup(const gchar* name,
                  GtkType object_type)
gchar*
gtk_signal_name(guint signal_id)
void
gtk_signal_emit_stop(GtkObject* object,
                     guint signal_id)
void
gtk_signal_emit_stop_by_name(GtkObject* object,
                             const gchar* name)
guint
gtk_signal_connect(GtkObject* object,
                   const gchar* name,
                   GtkSignalFunc func,
                   gpointer func_data)
guint
gtk_signal_connect_after(GtkObject* object,
                         const gchar* name,
                         GtkSignalFunc func,
                         gpointer func_data)
guint
gtk_signal_connect_object(GtkObject* object,
                          const gchar* name,
                          GtkSignalFunc func,
                          GtkObject* slot_object)
guint
gtk_signal_connect_object_after(GtkObject* object,
                                const gchar* name,
                                GtkSignalFunc func,
                                GtkObject* slot_object)
guint
gtk_signal_connect_full(GtkObject* object,
                        const gchar* name,
                        GtkSignalFunc func,
                        GtkCallbackMarshal marshal,
                        gpointer data,
                        GtkDestroyNotify destroy_func,
                        gint object_signal,
                        gint after)
void
gtk_signal_connect_object_while_alive(GtkObject* object,
                                      const gchar* signal,
                                      GtkSignalFunc func,
                                      GtkObject* alive_object)
void
gtk_signal_connect_while_alive(GtkObject* object,
                               const gchar* signal,
                               GtkSignalFunc func,
                               gpointer func_data,
                               GtkObject  * alive_object)
void
gtk_signal_disconnect(GtkObject* object,
                      guint handler_id)
void
gtk_signal_disconnect_by_func(GtkObject* object,
                              GtkSignalFunc func,
                              gpointer func_data)
void
gtk_signal_disconnect_by_data(GtkObject  * object,
                              gpointer func_data)
void
gtk_signal_handler_block(GtkObject* object,
                         guint handler_id)
void
gtk_signal_handler_block_by_func(GtkObject* object,
                                 GtkSignalFunc func,
                                 gpointer func_data)
void
gtk_signal_handler_block_by_data(GtkObject* object,
                                 gpointer func_data)
void
gtk_signal_handler_unblock(GtkObject* object,
                           guint handler_id)
void
gtk_signal_handler_unblock_by_func(GtkObject* object,
                                   GtkSignalFunc func,
                                   gpointer func_data)
void
gtk_signal_handler_unblock_by_data(GtkObject* object,
                                   gpointer func_data)
guint
gtk_signal_add_emission_hook(guint signal_id,
                             GtkEmissionHook hook_func,
                             gpointer data)
guint
gtk_signal_add_emission_hook_full(guint signal_id,
                                  GtkEmissionHook hook_func,
                                  gpointer data,
                                  GDestroyNotify destroy)
void
gtk_signal_remove_emission_hook(guint signal_id,
                                guint hook_id)
GtkSignalQuery*
gtk_signal_query(guint signal_id)

9.6.3: Emitting A Signal

It's your object's responsibility to emit its signals at appropriate times. This is very simple; if you've saved the return value from gtk_signal_new(), that identifier can be used to emit the signal. Otherwise, you can emit the signal by name (with some cost in execution speed, since GTK+ will have to look up the identifier in a hash table).

Here is code from gtk/gtkbutton.c which is used to emit the "button_pressed" signal:


void
gtk_button_pressed (GtkButton *button)
{
  g_return_if_fail (button != NULL);
  g_return_if_fail (GTK_IS_BUTTON (button));

  gtk_signal_emit (GTK_OBJECT (button), button_signals[PRESSED]);
}


If a signal has arguments (other than the standard two), you must specify those as a variable argument list:


  gtk_signal_emit (GTK_OBJECT (widget), widget_signals[SIZE_REQUEST],
                   &widget->requisition);

If a signal returns a value, you must pass a location for the returned value as the final argument:


  gint return_val;

  return_val = FALSE;

  gtk_signal_emit (GTK_OBJECT (widget), widget_signals[EVENT], event,
                   &return_val);

Notice that return_val is initialized to something sane; if there are no signal handlers, none of them will assign a value to return_val. So you must initialize the variable. Each signal handler's return value will be assigned to the same location, so the final value of return_val is determined by the last signal handler to run. Note that certain return values (such as strings) must be freed by the signal emitter.

gtk_signal_emit_by_name() is the same as gtk_signal_emit(), except that the second argument is a signal name rather than a signal ID number. There are also variants of both emission functions that take a vector of GtkArg instead of a variable argument list. These variants expect arrays of n+1GtkArg structs, where n is the number of signal arguments and there is an additional GtkArg for the return value. The GtkArg structs should be initialized with sane values. If the function returns no value, the return value GtkArg will have GTK_TYPE_NONE.

All four signal emission functions are summarized in Function Listing 9.3.

Function Listing 9.3: Signal Emission
 #include <gtk/gtksignal.h>
void
gtk_signal_emit(GtkObject* object,
                guint signal_id,
                ...)
void
gtk_signal_emit_by_name(GtkObject* object,
                        const gchar* name,
                        ...)
void
gtk_signal_emitv(GtkObject* object,
                 guint signal_id,
                 GtkArg* params)
void
gtk_signal_emitv_by_name(GtkObject* object,
                         const gchar* name,
                         GtkArg* params)

Keep in mind that it is usually inappropriate to simply emit a signal outside of an object's implementation. Only GTK_RUN_ACTION signals are guaranteed to work properly without special setup or shutdown. Objects often export functions you can use to emit signals properly; for example, to emit the "size_request" signal, GtkWidget provides this function:


void
gtk_widget_size_request (GtkWidget      *widget,
                         GtkRequisition *requisition)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));

  gtk_widget_ref (widget);
  gtk_widget_ensure_style (widget);
  gtk_signal_emit (GTK_OBJECT (widget), widget_signals[SIZE_REQUEST],
                   &widget->requisition);

  if (requisition)
    gtk_widget_get_child_requisition (widget, requisition);

  gtk_widget_unref (widget);
}

As you can see, particular actions are required before and after emitting the signal; thus it should only be emitted via the gtk_widget_size_request() function.

9.6.4: What Happens When A Signal Is Emitted

Given the many different options when creating signals and connecting callbacks, you may be thoroughly confused about what happens when a signal is emitted. Here's a summary of the sequence of events:

  1. If you are emitting the signal by name, the signal ID is looked up.
  2. If another emission of the same signal is in progress, and the signal has the GTK_RUN_NO_RECURSE flag set, GTK+ signals the previous emission to restart and this emission ends.
  3. If the signal is GTK_RUN_FIRST, the default signal handler is called using the signal's marshaller. If the emission is stopped from within the handler, (using gtk_emit_stop_by_name() or one of its cousins), this emission ends. If the signal is re-emitted from within the handler and is GTK_RUN_NO_RECURSE, this emission restarts.
  4. If there are any emission hooks installed for this signal, they are invoked. GTK+ does not check whether the emission has been stopped or re-emitted at this point; it will not check until the next step. Emission hooks should not re-emit the signal they are watching, or try to stop the emission.
  5. Any normally-connected callbacks are invoked using the signal's marshaller. Callbacks connected with gtk_signal_connect_after() are not invoked at this point. After invoking each callback, GTK+ checks whether it stopped the signal and the emission ends if so. GTK+ also checks whether the signal was re-emitted, and if so restarts the emission process for GTK_RUN_NO_RECURSE signals.
  6. If the signal is GTK_RUN_LAST, the default handler is invoked. Afterward GTK+ again checks whether the emission has been stopped or should be restarted.
  7. Any callbacks connected with gtk_signal_connect_after() are invoked. After invoking each one, GTK+ checks whether the emission should be stopped or restarted.

Note that within each step the order of handler invocation is unspecified. For example, the handlers connected with gtk_signal_connect() are not guaranteed to be called in any particular order. However, the order of the steps themselves is guaranteed: GTK_RUN_FIRST default handler, emission hooks, normal connections, GTK_RUN_LAST default handler, "after" connections.

9.7: Object Finalization

To write a GtkObject, you must implement the methods provided by the GtkObject interface, or at least be sure you are happy with the default implementations. There are only five GtkObject methods; two of them are get_arg and set_arg, described in Section 9.5.3. The other three implement object destruction; here are the fields in GtkObjectClass:


  void (* shutdown) (GtkObject *object);
  void (* destroy)  (GtkObject *object);
  
  void (* finalize) (GtkObject *object);

As you might guess from this, objects are destroyed in a three-stage process. Each method represents one stage in the process; if your object subclass overrides any of them, it must "chain up" to the corresponding method in the parent class (see Section 9.7.1). The three methods do the following:

Note: Objects can be destroyed regardless of their reference count. This means that the shutdown method is invoked and the destroy signal is emitted. However, as long as the reference count is greater than 0, the object will not be finalized.

The shutdown method has no defined role; its purpose depends on the particular object. For example, the GtkWidget shutdown implementation removes the widget from its parent container, and unrealizes the widget. This is especially important for containers: their destroy method destroys all children of the container. If the container was not unrealized before destruction, it would still be visible and the user would see each child disappear, followed by the container. With the shutdown method, everything disappears at once.

The destroy method frees as many resources as possible without rendering the object "unsafe." If your object has invariants describing its integrity, a destroy method will not violate them. All public functions exported by an object implementation should gracefully handle destroyed objects (they should not crash---remember that an object can be destroyed while references to it persist). The finalize method actually frees the object, meaning that attempts to use the object become dangerous bugs.

The statement that "public functions exported by an object implementation should gracefully handle destroyed objects" requires some qualification. This is the intended behavior; otherwise, code could not ensure the sanity of an object by increasing its reference count. However, the implementation does not yet live up to the guarantee in all cases. Some public functions in GTK+ and Gnome still assume data structures freed in the destroy method exist, or re-allocate data structures the destroy method already freed. Unless the finalize method re-frees those data structures, they will be leaked. To avoid these bugs, it is best to avoid calling functions on destroyed objects (in practice, it would be uncommon to do so).

You can count on being able to check the type and object flags of a destroyed object, however; and it is certainly safe to call gtk_object_unref() on a destroyed object. In your own object implementations, be sure you implement each public function correctly; check whether the object is destroyed with GTK_OBJECT_DESTROYED(), and keep in mind that user code can run between the destroy method and the finalize method.

Notice that the destroy method is the default handler for a "destroy" signal, but the shutdown and finalize methods are class functions only. This reduces the complexity and increases the speed of the finalization process. Also, because finalize destroys the integrity of an object, it would be unsafe to emit as a signal (GTK+ does have a facility called "weak references" that allows you to invoke a callback when an object is finalized---weak references do not assume that the GtkObject is in a sane state).

To make things more concrete, let's look at the functions you would use to destroy an object. First, gtk_object_destroy():


void
gtk_object_destroy (GtkObject *object)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (object));
  g_return_if_fail (GTK_OBJECT_CONSTRUCTED (object));
  
  if (!GTK_OBJECT_DESTROYED (object))
    {
      gtk_object_ref (object);
      object->klass->shutdown (object);
      gtk_object_unref (object);
    }
}

Notice that destroyed-but-not-finalized objects are flagged, and this flag can be checked with the GTK_OBJECT_DESTROYED() macro. gtk_object_destroy() ensures that objects are not destroyed twice by ignoring any already-destroyed objects. If an object has not been destroyed, gtk_object_destroy() references it to prevent finalization during the destroy process and invokes the shutdown method; by default, that method looks like this:


static void
gtk_object_shutdown (GtkObject *object)
{
  GTK_OBJECT_SET_FLAGS (object, GTK_DESTROYED);
  gtk_signal_emit (object, object_signals[DESTROY]);
}

This method sets the destroyed flag, to ensure that any recursive gtk_object_destroy() calls have no effect; then it emits the "destroy" signal. gtk_object_shutdown() seems pointless by itself; however, subclasses may override this method with something more substantial, chaining up to the GtkObject default method (see Section 9.7.1).

It may be unclear that gtk_object_shutdown() is a method implementation, while gtk_object_destroy() is a public function. Note that gtk_object_shutdown() is the internal function that implements the shutdown method for the GtkObject class, while gtk_object_destroy() is part of the public API. The GtkObject implementation of the destroy method is called gtk_object_real_destroy():


static void
gtk_object_real_destroy (GtkObject *object)
{
  if (GTK_OBJECT_CONNECTED (object))
    gtk_signal_handlers_destroy (object);
}

This code simply cleans up any signal handlers associated with the object. gtk_object_real_destroy() is the default handler invoked when the "destroy" signal is emitted. gtk_object_destroy() invokes the (possibly overridden) class function shutdown; the default shutdown method emits the "destroy" signal.

Finalization is initiated by gtk_object_unref(), if and only if the reference count has reached 0. gtk_object_unref() can be invoked directly by a user, but often gtk_object_destroy() invokes it. Here it is:


void
gtk_object_unref (GtkObject *object)
{
  g_return_if_fail (object != NULL);
  g_return_if_fail (GTK_IS_OBJECT (object));
  g_return_if_fail (object->ref_count > 0);
  
  if (object->ref_count == 1)
    {
      gtk_object_destroy (object);
  
      g_return_if_fail (object->ref_count > 0);
    }

  object->ref_count -= 1;

  if (object->ref_count == 0)
    {
      object->klass->finalize (object);
    }
}

If an object has a reference count of 1, calling gtk_object_unref() invokes the shutdown and destroy methods (via gtk_object_destroy()) and then finalizes the object (unless the reference count was incremented sometime during the shutdown/destroy process; this is allowed and will prevent finalization). If an object's reference count is greater than 1 at the start of gtk_object_unref(), the reference count is simply decremented.

Again, notice that an object can be destroyed while the reference count is greater than 1 if the user calls gtk_object_destroy(); if this happens, finalization does not take place until the holders of the remaining references call gtk_object_unref(). In the most common case, the gtk_object_destroy() implementation holds the last reference count --- have another look at the gtk_object_destroy() code with this in mind.

For completeness, here is GtkObject's default finalize method:


static void
gtk_object_finalize (GtkObject *object)
{
  gtk_object_notify_weaks (object);

  g_datalist_clear (&object->object_data);
  
  gtk_type_free (GTK_OBJECT_TYPE (object), object);
}

The three function calls in this method do the following:

Section 3.3.1 has more to say about reference counting and destruction with respect to widgets.

9.7.1: Chaining Up

If an object overrides the shutdown, destroy, or finalize methods, it should chain up to the default implementation, to ensure that each parent class has a chance to clean up. Here is an example of chaining up:


static void
gtk_widget_real_destroy (GtkObject *object)
{

  /* ... */

  if (parent_class->destroy)
    parent_class->destroy (object);

};

gtk_widget_real_destroy() is installed in the widget's class struct in the class initialization function, overwriting the GtkObject default. parent_class is a pointer to the parent's class struct; usually you will want to store this pointer in your class initialization function, as GtkWidget does:


static GtkObjectClass *parent_class = NULL;

/* ... code omitted ... */

static void
gtk_widget_class_init (GtkWidgetClass *klass)
{
  GtkObjectClass *object_class;
  
  object_class = (GtkObjectClass*) klass;
  
  parent_class = gtk_type_class (gtk_object_get_type ());

  /* ... code omitted ... */
  
  object_class->set_arg = gtk_widget_set_arg;
  object_class->get_arg = gtk_widget_get_arg;
  object_class->shutdown = gtk_widget_shutdown;
  object_class->destroy = gtk_widget_real_destroy;
  object_class->finalize = gtk_widget_finalize;
} 

Of course, if parent_class is not a GtkObjectClass*, you will need to cast it with the GTK_OBJECT_CLASS() macro.

An aside: notice that you should not chain up when implementing get_arg and set_arg --- GTK+ special-cases these methods in gtk_object_set() and gtk_object_get(). Recall that the GtkObject base class initializer zeroes these two methods, rather than leaving the default implementation. When setting or getting an argument value, GTK+ uses the information provided on argument registration to jump directly to the correct class struct and invoke only the correct get_arg or set_arg method. Chaining up would be a much slower way to implement the same thing (and would require unique argument IDs within the same class ancestry).

9.8: Attaching Data to Objects

You can "attach" arbitrary string-pointer pairs to a GtkObject instance, in effect adding a new data member. GTK+ uses this some internally, but it can also be a convenient way to pass data around in your application. In particular, it's a nice way to pass information to callbacks.

PRODUCTION: Please add this footnote to the previous sentence: "Be careful though; object data can be used to create incredibly difficult-to-read code. If you have a lot of data, creating a sensible struct or object is likely to be nicer. In many cases, a nice design involves subclassing an object and adding additional members to its instance struct." Thanks -hp

Here's a simple example:


    GtkWidget* button = gtk_button_new();
    GtkWidget* label  = gtk_label_new(_("Foo"));
  
    gtk_object_set_data(GTK_OBJECT(button), "my_label_key", label); 
  

Later, when you have a pointer to the button but not the label (perhaps in a callback connected to the button's "clicked" signal), you can do this:


    GtkWidget* label = gtk_object_get_data(GTK_OBJECT(button),
                                           "my_label_key");

    /* If no data is found for the key, NULL is returned. */

    if (label == NULL)
      {
        g_warning("No data was associated with 'my_label_key'!");
      }
  

A pair of convenience functions use a predetermined key and thus save typing (and remembering) the object data key. These are gtk_object_set_user_data() and gtk_object_get_user_data(). You can also register a function to free the data when the data is removed or replaced, or the GtkObject is destroyed; This function should be of type GtkDestroyNotify:


  typedef void (*GtkDestroyNotify) (gpointer data);

Conveniently, g_free() and gtk_object_unref() will work here. You register a "destroy notification" function when you set the data, using gtk_object_set_data_full(). You can remove data before the object is destroyed with gtk_object_remove_data(), or remove it without calling the destroy function with gtk_object_remove_no_notify(). Setting the data to NULL is equivalent to removing it with gtk_object_remove_data(), and will also call the destroy function if you registered one. Function Listing 9.4 summarizes the object data functions.

It's worth pointing out that the object data system is a thin wrapper around the GData facility in glib, which can be used standalone.

Function Listing 9.4: Attaching key-value pairs to a GtkObject
 #include <gtk/gtkobject.h>
void
gtk_object_set_data(GtkObject* object,
                    const gchar* key,
                    gpointer data)
void
gtk_object_set_data_full(GtkObject* object,
                         const gchar* key,
                         gpointer data,
                         GtkDestroyNotify destroy)
void
gtk_object_remove_data(GtkObject* object,
                       const gchar* key)
gpointer
gtk_object_get_data(GtkObject* object,
                    const gchar* key)
void
gtk_object_remove_no_notify(GtkObject* object,
                            const gchar* key)
void
gtk_object_set_user_data(GtkObject* object,
                         gpointer data)
gpointer
gtk_object_get_user_data(GtkObject* object)

10

GDK Basics

This chapter will discuss GDK, the underpinning of GTK+, and some of the occasions you might have to use it. To write custom widgets and canvas items, you will need to understand a few of these low-level details. Like chapters two and three, this chapter is a quick summary that doesn't hold your hand; there is no way to cover all of GDK in a single chapter. However, the chapter will try to cover the important concepts and data types of GDK, and should be a useful reference on certain topics. As details come up in later chapters, you can use this background to understand them. This chapter does not attempt to exhaustively catalog GDK's API.

10.1: GDK and Xlib

The X Window System comes with a low-level and thoroughly unpleasant library called Xlib. Almost every function in GDK is a very thin wrapper around a corresponding Xlib function; but some of the complexity (and functionality) of Xlib is hidden, to simplify programming and to make GDK easier to port to other windowing systems. (There is a port of GDK to Windows available.) The concealed Xlib functionality will rarely be of interest to application programmers; for example, many features used only by window managers are not exposed in GDK. If necessary, you can use Xlib directly in your application by including the special gdk/gdkx.h header file. (Check out the GDK source code to see how to extract the low-level Xlib data structures from their GDK wrappers.)

If you need excruciating details on a GDK function, you can typically glance at the source to determine the Xlib function it wraps, and then read the man page for the Xlib function. For example, here is the implementation of gdk_draw_point():


void
gdk_draw_point (GdkDrawable *drawable,
                GdkGC       *gc,
                gint         x,
                gint         y)
{
  GdkWindowPrivate *drawable_private;
  GdkGCPrivate *gc_private;

  g_return_if_fail (drawable != NULL);
  g_return_if_fail (gc != NULL);

  drawable_private = (GdkWindowPrivate*) drawable;
  if (drawable_private->destroyed)
    return;
  gc_private = (GdkGCPrivate*) gc;

  XDrawPoint (drawable_private->xdisplay, drawable_private->xwindow,
              gc_private->xgc, x, y);
}

Each data structure is cast to its "private" version, which contains information relating to the particular window system GDK is being used on; this is to keep window-system-specific declarations out of the gdk/gdk.h header file. The private version of each data structure contains a wrapped Xlib data structure, which is passed to XDrawPoint(). So the XDrawPoint() documentation will also apply to gdk_draw_point().

10.2: GdkWindow

GdkWindow is a wrapper around Xlib's Window object. (It was discussed briefly in Section 3.3.2.) A GdkWindow represents a region on the screen. It can be shown or hidden (called mapping and unmapping the window in Xlib). You can capture events received by a GdkWindow, draw graphics inside it, and move or resize it. GdkWindows are arranged in a tree structure; that is, each window can have child windows. Child windows are positioned relative to their parent window, and move when the parent moves. Child windows don't draw outside of their parent's bounds (i.e. they are clipped by the parent window).

The tree of GdkWindows is not specific to each application; there is a global tree of windows controlled by the X server and the window manager. The root window has no parent; all windows derive from it. All or part of it is visible as your desktop background. Each window can be owned by a different UNIX process; some windows will be created by the window manager, some will come from user applications.

GdkWindow and GtkWindow are very different things; GtkWindow is a GTK+ widget used to represent toplevel windows (toplevel windows are the highest application-controlled windows in the window hierarchy). Window managers typically create decorations for toplevel windows; decorations include title bars, close buttons, and the like.

It's important to understand that an X window is primarily an object on the X server. X clients receive a unique integer ID for each window, and refer to windows by ID. Thus, all window operations take place on the server; all functions that deal with X windows go across the network.

GdkWindow is a wrapper around the integer ID returned by X. It does keep local copies of some information (such as the window's dimensions), so some GDK operations are more efficient than the corresponding Xlib operations. Still, GdkWindow is essentially a handle for a server-side object. Many GDK objects are similar; fonts, pixmaps, cursors, and so on are also handles for server-side objects.

10.2.1: GdkWindow and GtkWidget

Many GtkWidget subclasses have an associated GdkWindow. In theory, GTK+ applications could create only toplevel windows, and have all widgets draw into them. However, it would make little sense to do so; GdkWindow allows the X Window System to automatically handle many details. For example, events received from GDK are marked with the window they occurred in; GTK+ can rapidly determine which widget each event corresponds to.

There are some widgets with no associated GdkWindow; these are called "no window" widgets, an allusion to the GTK_NO_WINDOW flag that marks them. (You can test this flag with the macro GTK_WIDGET_NO_WINDOW().) Widgets without a window render themselves into their parent container's GdkWindow. Windowless widgets are relatively small and lightweight; GtkLabel is the most common example. Because events are always received on a GdkWindow, windowless widgets do not receive events. (The GtkEventBox container can be used if you need to capture events on a windowless widget.)

PRODUCTION: Please add a footnote after the sentenced ending "do not receive events." The footnote should read: "This is not quite true. GTK+ will synthesize expose events for windowless widgets. Also, they may receive events that occur on a child widget and are propagated upward. However, they do not receive events on the GDK level."

10.2.2: GdkWindow Attributes

gdk_window_new() (shown in Function Listing 10.1) allows you to specify all of a window's attributes when you create it; many of them can be changed later as well. To specify a block of attributes, you pass in a GdkWindowAttr object; its contents will give you an idea what attributes a GdkWindow can have:


typedef struct _GdkWindowAttr GdkWindowAttr;

struct _GdkWindowAttr
{
  gchar *title;
  gint event_mask;
  gint16 x, y;
  gint16 width;
  gint16 height;
  GdkWindowClass wclass;
  GdkVisual *visual;
  GdkColormap *colormap;
  GdkWindowType window_type;
  GdkCursor *cursor;
  gchar *wmclass_name;
  gchar *wmclass_class;
  gboolean override_redirect;
};

Because some of the fields in GdkWindowAttr are optional, gdk_window_new() is used with an attributes_mask to specify which optional fields contain valid information (bit flags are available representing each optional field). GDK will only examine the optional fields given in the mask, so you can let the default values remain for fields you aren't interested in. Table 10.1 summarizes them briefly; fields with no attributes_mask flag are required and have no default value.

gdk_window_new() is typically used in widget implementations to create the widget's GdkWindow; you will rarely use it in any other context. gdk_window_destroy() destroys a window when you are done with it. Windows are also reference counted; be sure to read Section 10.10 for more details on this.

Function Listing 10.1: GdkWindow
 #include <gdk/gdk.h>
GdkWindow*
gdk_window_new(GdkWindow* parent,
               GdkWindowAttr* attributes,
               gint attributes_mask)
void
gdk_window_destroy(GdkWindow* window)

A GdkWindow's title is only really important for toplevel windows; most window managers will place it in the titlebar. Usually you should not specify it when creating a GdkWindow, however; instead, let your widget's users call gtk_window_set_title().

The window's event mask determines which events will be received on this window; Section 10.5 goes into more detail about events.

The X and Y coordinates for a window are specified in pixels, relative to the parent window's origin. The origin of each window is its top left ("northwest") corner. Notice that a 16-bit signed integer is used; X windows have a maximum size of 32768 pixels. Negative values are allowed, but the window will be clipped by its parent window (only the portion inside the parent window will be visible).

The width and height of a window are given in pixels, and are also 16-bit signed integers.

A window's GdkWindowClass can have one of two values:

A visual describes the color-handling characteristics of a display; a colormap contains the colors you can use to draw. Section 10.3 gives details on visuals and colormaps.

Windows can be one of several different types, specified by the GdkWindowType enumeration:

Only GDK_WINDOW_TOPLEVEL, GDK_WINDOW_CHILD, GDK_WINDOW_TEMP, and GDK_WINDOW_DIALOG are valid for gdk_window_new(). Library users may not create a GDK_WINDOW_ROOT. Pixmaps (GDK_WINDOW_PIXMAP) are created with gdk_pixmap_new(). Foreign windows (GDK_WINDOW_FOREIGN) are X windows created outside of GDK and wrapped using gdk_window_foreign_new() (declared in gdk/gdkx.h, since you will only need this function if you are using Xlib directly).

The cursor field specifies the mouse pointer (cursor) to use in this window; see Section 10.6 for information about cursors.

The "class hint" is described in Section 6.5.2. When writing widgets, you will not usually set the class hint. It is only relevant for toplevel windows; GTK+ provides gtk_window_set_wmclass() so application authors can set it to something sensible.

The last field in GdkWindowAttr determines whether the window is "override redirect." Normally, window managers intercept all requests to show, hide, move, or resize a toplevel window. They can then redirect or cancel these requests, to force windows to behave according to the window manager's layout policy. You can override this behavior by setting override_redirect to TRUE. Since window managers can not move windows with this flag set, they will normally not put a title bar or other decorations on them. Note that all GDK_WINDOW_TEMP windows use TRUE for this field; recall that GDK_WINDOW_TEMP is often used for popup menus, which are not controlled by the window manager.

Typically you should not change the override_redirect field; the default is almost always right, if you specify the correct GdkWindowType. However, there are some exceptions; the Gnome panel application sets this field, for example.

Table 10.1: GdkWindowAttr Fields
FieldTypeFlagDefault ValuePurpose
titlegchar*GDK_WA_TITLEProgram NameThe window's title
event_maskgintnonenoneEvents to receive on this window
xgint16GDK_WA_X0X position relative to parent window
ygint16GDK_WA_Y0Y position relative to parent window
widthgint16nonenoneWidth of window
heightgint16nonenoneHeight of window
wclassGdkWindowClassnonenoneGDK_INPUT_ONLY vs. GDK_INPUT_OUTPUT
visualGdkVisual*GDK_WA_VISUALX's "default visual"Visual for this window
colormapGdkColormap*GDK_WA_COLORMAPX's "default colormap" [PD]footnote!Colormap for this window
window_typeGdkWindowTypenonenoneWindow type (see text)
cursorGdkCursor*GDK_WA_CURSORParent window's cursorMouse pointer for this window
wmclass_namegchar*GDK_WA_WMCLASSnone (doesn't set hint)Set the "name" part of the class hint (see text)
wmclass_classgchar*GDK_WA_WMCLASSnone (doesn't set hint)Set the "class" part of the class hint (see text)
override_redirectgbooleanGDK_WA_NOREDIRFALSE [PD] footnote! Make the window "override redirect" (see text)

PRODUCTION: In the above table, the cell containing "X's default colormap" (on the "colormap" row) should have this footnote: "Colormaps are specific to visuals, so the default colormap is only used with the default visual. A new colormap will be created if you are using a non-default visual and do not specify a colormap."

PRODUCTION: In the table, the "Default" column for "override_redirect" should have a footnote which reads: "The default is TRUE and cannot be changed if the window's type is GDK_WINDOW_TEMP."

PRODUCTION: Also, it might look nicer to substitute a dash for "none" throughout - I don't know.

10.3: Visuals and Colormaps

Unfortunately, not all hardware is created equal. The most primitive X servers support only two colors; each pixel is either on or off. This is referred to as a "one bit per pixel (bpp)" display. A display with one bit per pixel is said to have a depth of one. More advanced X servers support 24 or 32 bits per pixel, and allow you to specify a different depth on a window-by-window basis. 24 bits per pixel allows 2^24 different pixels, which includes more colors than the human eye can differentiate.

PRODUCTION: 2^24 is two to the power of 24, i.e. instead of the "^" character, 24 should be superscript. thanks -hp

Conceptually, a bitmap display consists of a rectangular grid of pixels. Each pixel consists of some fixed number of bits; pixels are mapped to visible colors in a hardware-dependent way. One way to think about this is to imagine a two-dimensional array of integers, where the integer size is chosen to hold the required number of bits. Alternatively, you can think of a display like this as a stack of bit planes, or two-dimensional arrays of bits. If all the planes are parallel to one another, a pixel is a perpendicular line passing through the same coordinates on each plane, taking a single bit from each one. This is the origin of the term depth, since the number of bits per pixel is equal to the depth of the stack of bit planes.

In the X Window System, pixels represent entries in a color lookup table. A color is a red, green, blue (RGB) value---monitors mix red, green, and blue light in some ratio to display each pixel. Take an eight bit display, for example: eight bits are not enough to encode a color in-place; only a few arbitrary RGB values would be possible. Instead, the bits are interpreted as an integer and used to index an array of RGB color values. This table of colors is called the colormap; it can sometimes be modified to contain the colors you plan to use, though this is hardware-dependent---some colormaps are read-only.

A visual is required to determine how a pixel's bit pattern is converted into a visible color. Thus, a visual also defines how colormaps work. On an 8-bit display, the X server might interpret each pixel as an index into a single colormap containing the 256 possible colors. 24-bit visuals typically have three colormaps: one for shades of red, one for shades of green, and one for shades of blue. Each colormap is indexed with an eight-bit value; the three eight-bit values are packed into a 24-bit pixel. The visual defines the meaning of the pixel contents. Visuals also define whether the colormap is read-only or modifiable.

In short, a visual is a description of the color capabilities of a particular X server. In Xlib, you have to do a lot of fooling around with visuals; GDK and GTK+ shield you from most of the mess.

10.3.1: GdkVisual

Xlib can report a list of all available visuals and information about each; GDK keeps a client-side copy of this information in a struct called GdkVisual. GDK can report the available visuals, and rank them in different ways. Most of the time you will only use gdk_visual_get_system(), which returns a pointer to the default visual (Function Listing 10.2). (If you're writing a GtkWidget, gtk_widget_get_visual() returns the visual you should use; more on this in Chapter 11.) The returned visual is not a copy, so there is no need to free it; GDK keeps visuals around permanently.

Function Listing 10.2: Default Visual
 #include <gdk/gdk.h>
GdkVisual*
gdk_visual_get_system()

For reference, here are the contents of GdkVisual; most of the members are used to calculate pixel values from colors. Since this is fairly involved and rarely used, this book glosses over the topic. The depth member is convenient sometimes. Section 10.3.1 has more to say about the type member.


typedef struct _GdkVisual GdkVisual;

struct _GdkVisual
{
  GdkVisualType type;
  gint depth;
  GdkByteOrder byte_order;
  gint colormap_size;
  gint bits_per_rgb;

  guint32 red_mask;
  gint red_shift;
  gint red_prec;

  guint32 green_mask;
  gint green_shift;
  gint green_prec;

  guint32 blue_mask;
  gint blue_shift;
  gint blue_prec;
};

Types of Visual

Visuals differ along several dimensions. They can be grayscale or RGB, colormaps can be modifiable or fixed, and the pixel value can either index a single colormap or contain packed red, green, and blue indexes. Here are the possible values for GdkVisualType:

10.3.2: Color and GdkColormap

A GdkColor stores an RGB value and a pixel. Red, green, and blue are given as 16-bit unsigned integers; so they are in the range [0, 65535]. The contents of the pixel depend on the visual. Here is GdkColor:


typedef struct _GdkColor GdkColor;

struct _GdkColor
{
  gulong  pixel;
  gushort red;
  gushort green;
  gushort blue;
};

Before you can use a color to draw, you must:

In Xlib, this is an enormously complicated process, because it has to be done differently for every kind of visual. GDK conceals things fairly well. You simply call gdk_colormap_alloc_color() to fill in the pixel value and add the color to the colormap (Function Listing 10.3). Here is an example; it assumes a preexisting GdkColormap* colormap, which should be the colormap of the drawable you are targetting:


  GdkColor color;
  
  /* Describe a pure red */
  color.red   = 65535;
  color.green = 0;
  color.blue  = 0;

  if (gdk_colormap_alloc_color(colormap, &color, FALSE, TRUE))
    {
      /* Success! */
    }

If gdk_colormap_alloc_color() returns TRUE, then the color was allocated and color.pixel contains a valid value. The color can then be used to draw. The two boolean arguments to gdk_colormap_alloc_color() specify whether the color should be writeable, and whether to try to find a "best match" if the color can't be allocated. If a best match is used instead of allocating a new color, the color's RGB values will be changed to the best match. If you request a best match for a non-writeable entry, allocation really should not fail, since even on a black and white display either black or white will be the best match; only an empty colormap could cause failure. The only way to get an empty colormap is to create a custom colormap yourself. If you don't ask for the best match, failure is quite possible on displays with a limited number of colors. Failure is always possible with writeable colormap entries (where best match makes no sense, because the entry can be modified).

A writeable colormap entry is one that you can change at any time; some visuals support this, and some don't. The purpose of a writeable colormap entry is to change an on-screen color without redrawing the graphics. Some hardware stores pixels as indices into a color lookup table, so changing the lookup table changes how the pixels are displayed. The disadvantages of writeable colormap entries are numerous. Most notably: not all visuals support them, and writeable colormap entries can't be used by other applications (read-only entries can be shared, since other applications know the color will remain constant). Thus, it is a good idea to avoid allocating writeable colors. On modern hardware, they are more trouble than they're worth; the speed gain compared to simply redrawing your graphics will not be noticeable.

When you're finished with a color, you can remove it from the colormap with gdk_colormap_free_colors(). This is only really important for pseudo color and grayscale visuals, where colors are in short supply and the colormap can be modified by clients. GDK will automatically do the right thing for each visual type, so always call this function.

A convenient way to obtain RGB values is the gdk_color_parse() function. This takes an X color specification, and fills in the red, green, and blue fields of a GdkColor. An X color specification can have many forms; one possibility is an RGB string:


 RGB:FF/FF/FF

This specifies white (red, green, and blue are all at full intensity). The RGB: specifies a "color space," and determines the meaning of the numbers after it. X also understands several more obscure color spaces. If the color specification string doesn't begin with a recognized color space, X assumes it's a color name and looks it up in a database of names. So you can write code like this:


  GdkColor color;

  if (gdk_color_parse("orange", &color))
    {
      if (gdk_colormap_alloc_color(colormap, &color, FALSE, TRUE))
        {
          /* We have orange! */
        }
    } 

As you can see, gdk_color_parse() returns TRUE if it figures out the string you pass it. There is no way to know exactly what will be in the color database, so always check this return value.

Function Listing 10.3: Color Allocation
 #include <gdk/gdk.h>
gboolean
gdk_colormap_alloc_color(GdkColormap* colormap,
                         GdkColor* color,
                         gboolean writeable,
                         gboolean best_match)
void
gdk_colormap_free_colors(GdkColormap* colormap,
                         GdkColor* colors,
                         gint ncolors)
gint
gdk_color_parse(gchar* spec,
                GdkColor* color)

Obtaining a Colormap

If you're writing a GtkWidget subclass, the correct way to obtain a colormap is with gtk_widget_get_colormap() (see Chapter 11). Otherwise, the system (default) colormap is usually what you want; call gdk_colormap_get_system(), which takes no arguments and returns the default colormap.

The GdkRGB module (see Section 10.9.8) is another way to deal with colors; among other capabilities, it can set the foreground and background colors of a graphics context from an RGB value. The relevant functions are gdk_rgb_gc_set_foreground() and gdk_rgb_gc_set_background(). GdkRGB has a pre-allocated colormap that it uses to pick a best-match color; using it means that your application can share limited colormap resources with other applications using GdkRGB (such as the Gimp). You can also obtain GdkRGB's colormap and use it directly (see Section 10.9.8).

10.4: Drawables and Pixmaps

A pixmap is an off-screen buffer you can draw graphics into. After drawing into a pixmap, you can copy it to a window, causing it to appear on the screen (when the window is visible). (You can also draw into a window directly, of course. Using a pixmap as a buffer allows you to rapidly update the screen without repeating a series of primitive drawing operations.) Pixmaps are also good to store image data loaded from disk, such as icons and logos. You can then copy the image to a window. In GDK, the pixmap type is called GdkPixmap. A pixmap with a single bit representing each pixel is called a bitmap; GDK's bitmap type is GdkBitmap. "Bitmap" is not really a separate type; from X's point of view, it is simply a pixmap with a depth of 1. Like windows, pixmaps are server-side resources.

In X terminology, a drawable is anything you can draw graphics on. GDK has a corresponding type, called GdkDrawable. Drawables include windows, pixmaps, and bitmaps. Here is how the types are defined in GDK:


typedef struct _GdkWindow GdkWindow;
typedef struct _GdkWindow GdkPixmap;
typedef struct _GdkWindow GdkBitmap;
typedef struct _GdkWindow GdkDrawable;

On the client side, pixmaps and bitmaps are just GdkWindows with type GDK_WINDOW_PIXMAP. GdkDrawable is used in function declarations when either a window or a pixmap is an acceptable argument. Functions that draw graphics take either type; functions that move windows around or set window manager hints accept only windows. Only windows can receive events. GDK_INPUT_ONLY windows are a special case; they are not drawables and you can't draw on them.

Three of the four logical combinations of "window features" and drawability actually exist:


                       Drawable             Not Drawable
Window Features      Normal Window         Input Only Window
No Window Features   Pixmap/Bitmap            ---

Unfortunately, all three of these logically distinct cases appear the same from a type-checking point of view. So be careful not to use the wrong one. Also keep in mind that a normal window is not drawable until it actually appears on the screen; yo