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.
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.
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.
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.
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.
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:
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.
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;
}
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.
| GtkType Constant | Corresponding C Type |
|---|---|
| GTK_TYPE_INVALID | None |
| GTK_TYPE_NONE | void |
| GTK_TYPE_CHAR | gchar |
| GTK_TYPE_UCHAR | guchar |
| GTK_TYPE_BOOL | gboolean |
| GTK_TYPE_INT | gint |
| GTK_TYPE_UINT | guint |
| GTK_TYPE_LONG | glong |
| GTK_TYPE_ULONG | gulong |
| GTK_TYPE_FLOAT | gfloat |
| GTK_TYPE_DOUBLE | gdouble |
| GTK_TYPE_STRING | gchar* |
| GTK_TYPE_ENUM | Any enumeration |
| GTK_TYPE_FLAGS | guint |
| GTK_TYPE_BOXED | gpointer |
| GTK_TYPE_POINTER | gpointer |
| GTK_TYPE_SIGNAL | GtkSignalFunc, gpointer |
| GTK_TYPE_ARGS | gint, GtkArg* |
| GTK_TYPE_CALLBACK | GtkCallbackMarshal, gpointer, GtkDestroyNotify |
| GTK_TYPE_C_CALLBACK | GtkFunction, gpointer |
| GTK_TYPE_FOREIGN | gpointer, GtkDestroyNotify |
| GTK_TYPE_OBJECT | GtkObject* |
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. >K_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 ... */
}
| #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.
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.
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.
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]));
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.
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.
| #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) |
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.
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).
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;
};
| #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) |
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.
| #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.
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:
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.
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.
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).
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.
| #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) |
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.
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().
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.
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."
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.
| #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.
| Field | Type | Flag | Default Value | Purpose |
|---|---|---|---|---|
| title | gchar* | GDK_WA_TITLE | Program Name | The window's title |
| event_mask | gint | none | none | Events to receive on this window |
| x | gint16 | GDK_WA_X | 0 | X position relative to parent window |
| y | gint16 | GDK_WA_Y | 0 | Y position relative to parent window |
| width | gint16 | none | none | Width of window |
| height | gint16 | none | none | Height of window |
| wclass | GdkWindowClass | none | none | GDK_INPUT_ONLY vs. GDK_INPUT_OUTPUT |
| visual | GdkVisual* | GDK_WA_VISUAL | X's "default visual" | Visual for this window |
| colormap | GdkColormap* | GDK_WA_COLORMAP | X's "default colormap" [PD]footnote! | Colormap for this window |
| window_type | GdkWindowType | none | none | Window type (see text) |
| cursor | GdkCursor* | GDK_WA_CURSOR | Parent window's cursor | Mouse pointer for this window |
| wmclass_name | gchar* | GDK_WA_WMCLASS | none (doesn't set hint) | Set the "name" part of the class hint (see text) |
| wmclass_class | gchar* | GDK_WA_WMCLASS | none (doesn't set hint) | Set the "class" part of the class hint (see text) |
| override_redirect | gboolean | GDK_WA_NOREDIR | FALSE [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.
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.
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.
| #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;
};
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:
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.
| #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) |
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).
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; you should wait until you receive an expose event before you draw. Expose events are covered in Section 10.5.8.
PRODUCTION: The 2x2 grid of "logical combinations" should be formatted somehow nice, not sure how :-) thanks -hp
Like GdkWindow, a GdkPixmap is merely a client-side handle for an object located on the X server. Because of this, some things are entirely infeasible from a performance point of view; notably, if you are doing anything which requires significant manipulation of individual pixels, drawables will be far too slow. On the other hand, copying a pixmap to a window is not as slow as you might think, because both objects are on the same machine.
Creating a pixmap is much easier than creating a window, because most of the window attributes are not relevant to pixmaps. The function is gdk_pixmap_new() (Function Listing 10.4). It accepts an initial size, and a bit depth. If a depth of -1 is given, the depth is copied from its GdkWindow argument. You can't choose an arbitrary number for the depth---the server will not support all depths, and the pixmap's depth must match the depth of any windows you plan to copy it to. To destroy a pixmap, call gdk_pixmap_unref().
The GdkWindow argument to gdk_pixmap_new() may not seem strictly necessary. However, the function wraps XCreatePixmap(), which takes an X window as an argument. It uses this argument to determine which screen to create the window on; some X servers have multiple displays. Screens are an Xlib concept totally concealed by GDK; GDK supports only one screen at a time. Thus the window argument to gdk_pixmap_new() seems mysterious from a GDK point of view.
| #include <gdk/gdk.h> |
GdkPixmap*
gdk_pixmap_new(GdkWindow* window,
gint width,
gint height,
gint depth) |
void gdk_pixmap_unref(GdkPixmap* pixmap) |
Events are sent to your application to indicate changes in a GdkWindow or user actions you might be interested in. All events are associated with a GdkWindow. They also come to be associated with a GtkWidget; the GTK+ main loop passes events from GDK to the GTK+ widget tree.
There are many kinds of events; the GdkEvent union can represent any of them. A special event type, GdkEventAny, contains the three fields common to all events; any event can be cast to GdkEventAny. The first field in GdkEventAny is a type marker, GdkEventType; GdkEventType is also included in the GdkEvent union. Confused yet? Seeing the code should help. Here is GdkEventAny:
struct _GdkEventAny
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
};
and GdkEvent:
union _GdkEvent
{
GdkEventType type;
GdkEventAny any;
GdkEventExpose expose;
GdkEventNoExpose no_expose;
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventKey key;
GdkEventCrossing crossing;
GdkEventFocus focus_change;
GdkEventConfigure configure;
GdkEventProperty property;
GdkEventSelection selection;
GdkEventProximity proximity;
GdkEventClient client;
GdkEventDND dnd;
};
Every event type has the three members of GdkEventAny as its first three members. Thus, the type of an event can be referred to in many ways (assume a GdkEvent* called event):
You'll probably see all these in GTK+ source code. Of course, each event subtype has its own unique members; the type field tells you which subtype is valid.
The window field of GdkEventAny is the GdkWindow the event was sent to. If the send_event flag is TRUE, then the event was synthesized by another (or your own) application; if FALSE, it originated with the X server. GDK does not export the X interface for sending events (XSendEvent()). However, GTK+ often "makes up" an event by declaring a static event struct, filling it in, then emitting the event's corresponding widget signal. These synthesized events will have send_event set to TRUE. Section 10.5.3 explains how GTK+ associates events with widget signals.
There are more possible values for GdkEventType than there are members in the GdkEvent union. Many event types share the same data; for example, GDK_BUTTON_PRESS and GDK_BUTTON_RELEASE both use the button member of GdkEvent, since the same information is conveyed when mouse buttons are pressed and released. Table 10.2 shows all possible values of the GdkEventType enumeration, and the corresponding GdkEvent member. The meaning of each event type is described later in this section.
| Value | GdkEvent Member |
|---|---|
| GDK_NOTHING | none [PD] footnote! |
| GDK_DELETE | GdkEventAny |
| GDK_DESTROY | GdkEventAny |
| GDK_EXPOSE | GdkEventExpose |
| GDK_MOTION_NOTIFY | GdkEventMotion |
| GDK_BUTTON_PRESS | GdkEventButton |
| GDK_2BUTTON_PRESS | GdkEventButton |
| GDK_3BUTTON_PRESS | GdkEventButton |
| GDK_BUTTON_RELEASE | GdkEventButton |
| GDK_KEY_PRESS | GdkEventKey |
| GDK_KEY_RELEASE | GdkEventKey |
| GDK_ENTER_NOTIFY | GdkEventCrossing |
| GDK_LEAVE_NOTIFY | GdkEventCrossing |
| GDK_FOCUS_CHANGE | GdkEventFocus |
| GDK_CONFIGURE | GdkEventConfigure |
| GDK_MAP | GdkEventAny |
| GDK_UNMAP | GdkEventAny |
| GDK_PROPERTY_NOTIFY | GdkEventProperty |
| GDK_SELECTION_CLEAR | GdkEventSelection |
| GDK_SELECTION_REQUEST | GdkEventSelection |
| GDK_SELECTION_NOTIFY | GdkEventSelection |
| GDK_PROXIMITY_IN | GdkEventProximity |
| GDK_PROXIMITY_OUT | GdkEventProximity |
| GDK_DRAG_ENTER | GdkEventDND |
| GDK_DRAG_LEAVE | GdkEventDND |
| GDK_DRAG_MOTION | GdkEventDND |
| GDK_DRAG_STATUS | GdkEventDND |
| GDK_DROP_START | GdkEventDND |
| GDK_DROP_FINISHED | GdkEventDND |
| GDK_CLIENT_EVENT | GdkEventClient |
| GDK_VISIBILITY_NOTIFY | GdkEventVisibility |
| GDK_NO_EXPOSE | GdkEventNoExpose |
PRODUCTION: Footnote after "none" in the GDK_NOTHING row: "GDK_NOTHING is an "invalid" value. GDK uses this internally."
PRODUCTION: Again, a dash might be nicer than "none," see what you think -hp
Each GdkWindow has an associated event mask which determines which events on that window the X server will forward to your application. You specify the event mask when a GdkWindow is created, as part of the GdkWindowAttr struct (Section 10.2.2). You can access and change the event mask later using gdk_window_set_events() and gdk_window_get_events(). If the GdkWindow in question belongs to a widget, you should not change the event mask directly; rather, call gtk_widget_set_events() or gtk_widget_add_events(). gtk_widget_set_events() should be used after a widget is realized; gtk_widget_add_events() can be used to add events to the existing mask at any time. Function Listing 10.5 and Function Listing 10.6 show these functions.
| #include <gdk/gdk.h> |
GdkEventMask gdk_window_get_events(GdkWindow* window) |
void
gdk_window_set_events(GdkWindow* window,
GdkEventMask event_mask) |
| #include <gtk/gtkwidget.h> |
gint gdk_widget_get_events(GtkWidget* widget) |
void
gtk_widget_add_events(GtkWidget* widget,
gint event_mask) |
void
gtk_widget_set_events(GtkWidget* widget,
gint event_mask) |
Table 10.3 shows which event masks request which events. Some events do not have to be selected to be received; in particular:
| Mask | Event Type |
|---|---|
| GDK_EXPOSURE_MASK | GDK_EXPOSE |
| GDK_POINTER_MOTION_MASK | GDK_MOTION_NOTIFY |
| GDK_POINTER_MOTION_HINT_MASK | N/A (see Section 10.5.6) |
| GDK_BUTTON_MOTION_MASK | GDK_MOTION_NOTIFY (while a button is pressed) |
| GDK_BUTTON1_MOTION_MASK | GDK_MOTION_NOTIFY (while button 1 is pressed) |
| GDK_BUTTON2_MOTION_MASK | GDK_MOTION_NOTIFY (while button 2 is pressed) |
| GDK_BUTTON3_MOTION_MASK | GDK_MOTION_NOTIFY (while button 3 is pressed) |
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS |
| GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_RELEASE |
| GDK_KEY_PRESS_MASK | GDK_KEY_PRESS |
| GDK_KEY_RELEASE_MASK | GDK_KEY_RELEASE |
| GDK_ENTER_NOTIFY_MASK | GDK_ENTER_NOTIFY |
| GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY |
| GDK_FOCUS_CHANGE_MASK | GDK_FOCUS_IN, GDK_FOCUS_OUT |
| GDK_STRUCTURE_MASK | GDK_CONFIGURE, GDK_DESTROY, GDK_MAP, GDK_UNMAP |
| GDK_PROPERTY_CHANGE_MASK | GDK_PROPERTY_NOTIFY |
| GDK_VISIBILITY_NOTIFY_MASK | GDK_VISIBILITY_NOTIFY |
| GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_IN |
| GDK_PROXIMITY_OUT_MASK | GDK_PROXIMITY_OUT |
| GDK_SUBSTRUCTURE_MASK | Receive GDK_STRUCTURE_MASK events for child windows |
| GDK_ALL_EVENTS_MASK | All events |
In a GTK+ program, you will never receive GDK events directly. Instead, all events are passed to a GtkWidget, which emits a corresponding signal. You handle events by connecting handlers to GtkWidget signals.
The X server sends each X client a stream of events. Events are sent and received in the order of their occurrence. GDK converts each XEvent it receives into a GdkEvent, then places events in a queue. GTK+ monitors GDK's event queue; for each event received, it decides which widget (if any) should receive the event. The GtkWidget base class defines signals for most event types (such as "button_press_event"); it also defines a generic "event" signal. The GTK+ main loop calls gtk_widget_event() to deliver an event to a widget; this function first emits the "event" signal, then emits a signal for the specific event type (if appropriate). Some events are handled in special ways; notably, drag-and-drop events do not directly correspond to drag-and-drop signals.
In general, events go to the widget owning the GdkWindow the event occurred on. However, there are certain special cases.
if a widget has the grab (i.e., if gtk_grab_add() was called, see Section 3.3.3), certain events will only be forwarded to the widget with the grab, or the children of that widget. Events that occur on other widgets are ignored. Only certain user-initiated events such as button events and key events are affected by a grab.
Widget sensitivity (see Section 3.3.3) also affects where events are sent. Events representing user interaction are not forwarded to insensitive widgets.
As you might expect, widgets with no associated GdkWindow do not originate events; X only sends events to windows. There is one exception: containers synthesize expose events for their windowless children.
The GTK+ main loop propagates certain events from child widgets to their parent containers. That is, for each event, a signal is emitted first from a child widget, then from its immediate parent, then from the parent's parent, and so on. For example, if you click a GtkMenuItem, it ignores the button press and lets the menu it's a part of handle it. Some events are not propagated; Table 10.4 gives details.
Event propagation ends once a widget "handles" the event. This ensures that only one user-visible change results from any user action. Handlers for GtkWidget's event signals must return a gint value. Recall that the last signal handler to run determines the return value of a signal emission---see Section 9.6.3. All event signals are GTK_RUN_LAST, so the return value will come from:
If the emission of an event signal returns TRUE, the GTK+ main loop will stop propagating the current event. If it returns FALSE, the main loop will propagate the event to the widget's parent. Recall that each event results in two signal emissions: a generic "event" signal and a specific signal (such as "button_press_event" or "key_press_event"). If either emission returns TRUE, event propagation ends. The return value from the generic "event" signal has one additional effect: if TRUE, the second, more specific signal will not be emitted.
Table 10.4 summarizes how GtkWidget signals correspond to event types, which events are affected by an active grab, and which events are propagated from parent to child. Signal handlers for all event signals should return a gint and take three arguments: the widget emitting the signal, the event which triggered the signal, and a user data pointer.
| Event Type | GtkWidget Signal | Propagated? | Grabbed? |
|---|---|---|---|
| GDK_DELETE | "delete_event" | No | No |
| GDK_DESTROY | "destroy_event" | No | No |
| GDK_EXPOSE | "expose_event" | No | No |
| GDK_MOTION_NOTIFY | "motion_notify_event" | Yes | Yes |
| GDK_BUTTON_PRESS | "button_press_event" | Yes | Yes |
| GDK_2BUTTON_PRESS | "button_press_event" | Yes | Yes |
| GDK_3BUTTON_PRESS | "button_press_event" | Yes | Yes |
| GDK_BUTTON_RELEASE | "button_release_event" | Yes | Yes |
| GDK_KEY_PRESS | "key_press_event" | Yes | Yes |
| GDK_KEY_RELEASE | "key_release_event" | Yes | Yes |
| GDK_ENTER_NOTIFY | "enter_notify_event" | No | Yes |
| GDK_LEAVE_NOTIFY | "leave_notify_event" | No | Yes [PD] footnote! |
| GDK_FOCUS_CHANGE | "focus_in_event", "focus_out_event" | No | No |
| GDK_CONFIGURE | "configure_event" | No | No |
| GDK_MAP | "map_event" | No | No |
| GDK_UNMAP | "unmap_event" | No | No |
| GDK_PROPERTY_NOTIFY | "property_notify_event" | No | No |
| GDK_SELECTION_CLEAR | "selection_clear_event" | No | No |
| GDK_SELECTION_REQUEST | "selection_request_event" | No | No |
| GDK_SELECTION_NOTIFY | "selection_notify_event" | No | No |
| GDK_PROXIMITY_IN | "proximity_in_event" | Yes | Yes |
| GDK_PROXIMITY_OUT | "proximity_out_event" | Yes | Yes |
| GDK_CLIENT_EVENT | "client_event" | No | No |
| GDK_VISIBILITY_NOTIFY | "visibility_notify_event" | No | No |
| GDK_NO_EXPOSE | "no_expose_event" | No | No |
PRODUCTION: Footnote for the "grabbed" column of GDK_LEAVE_NOTIFY: "GDK_LEAVE_NOTIFY goes to the grabbing widget, if the grabbing widget also received the corresponding GDK_ENTER_NOTIFY. Otherwise, the leave notification goes to the widget that received the enter notification. This ensures that enter and leave events are received in pairs."
Four different event types result in a GdkEventButton:
If you click three times quickly, on the same GdkWindow, the following events are received in order:
The X server automatically causes a pointer grab when a button is pressed, and releases it when it is released. This means that the button release event always goes to the same window that received the button press event. Xlib allows you to change this behavior, but GDK does not. (In the Xlib documentation, this automatic grab is referred to as a "passive" grab. It's distinct from an "active" grab initiated with gdk_pointer_grab(), described in Section 10.6.2.)
A button event is defined as follows:
typedef struct _GdkEventButton GdkEventButton;
struct _GdkEventButton
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble pressure;
gdouble xtilt;
gdouble ytilt;
guint state;
guint button;
GdkInputSource source;
guint32 deviceid;
gdouble x_root, y_root;
};
Button events are marked with a time stamp (time) by the X server. The time is given in milliseconds of "server time"; every few weeks the integer overflows and timestamps begin again at 0. Thus, you should not rely on the value as an absolute measure of time; it is intended only to determine relative time between events.
The mouse pointer's X and Y coordinates (relative to the window the event occurred in) are included in GdkEventButton. Keep in mind that the pointer may be outside the window (if a pointer grab is in effect; see Section 10.6). If the pointer is outside the window, its coordinates could be negative or larger than the window's size. Coordinates are given as doubles rather than integers, because some input devices such as graphics tablets have sub-pixel resolution. For most purposes, you will want to cast the doubles to integers. pressure, xtilt, and ytilt are also special features of some input devices; they can be ignored almost all the time.
The state member of GdkEventButton indicates which modifier keys or mouse buttons were held down an instant before the button was pressed. It is a bitfield, with one or more of the flags in Table 10.5 set. Since the modifiers are read just before the button press, it follows that button press events do not have the pressed button in state, but button release events do have it.
Be careful to check for the presence of certain bit masks, rather than the exact value of state. That is, prefer this:
if ( (state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK )
and avoid this:
if ( state == GDK_SHIFT_MASK )
If you check the exact value of state, your application will mysteriously stop working if the user has Num Lock or some other obscure modifier turned on.
| Modifier Mask | Meaning |
|---|---|
| GDK_SHIFT_MASK | Shift |
| GDK_LOCK_MASK | Caps Lock |
| GDK_CONTROL_MASK | Control |
| GDK_MOD1_MASK | Mod1 (often Meta or Alt) |
| GDK_MOD2_MASK | Mod2 |
| GDK_MOD3_MASK | Mod3 |
| GDK_MOD4_MASK | Mod4 |
| GDK_MOD5_MASK | Mod5 |
| GDK_BUTTON1_MASK | Button 1 |
| GDK_BUTTON2_MASK | Button 2 |
| GDK_BUTTON3_MASK | Button 3 |
| GDK_BUTTON4_MASK | Button 4 |
| GDK_BUTTON5_MASK | Button 5 |
| GDK_RELEASE_MASK | Key releases |
PRODUCTION: Please add the following footnote to the GDK_RELEASE_MASK cell: "GDK_RELEASE_MASK is not used by GDK; GTK+ uses it to allow keyboard accelerators to be bound to key releases. Normal events will never have GDK_RELEASE_MASK in their state field."
The button member of GdkEventButton indicates which button triggered the event (i.e., the button which was pressed or released). Buttons are numbered from one to five; most of the time, button one is the left button, button two is the middle button, and button three is the right button. Left-handed users might reverse these. Button four and five events are generated by some scroll wheel mice when you spin the scroll wheel; GTK+ attempts to send capture these events and move nearby scroll bars. You should probably ignore any events you receive for buttons four or five.
The three standard mouse buttons have conventional meanings in Gnome. Button one is used for selection, drag and drop, and operating widgets: the most common tasks. Button three typically activates a pop-up menu. Button two is traditionally used to move objects, such as the panel. Sometimes button one moves objects also; for example, desktop icons can be moved with either button one or two. It is a good idea to be consistent with other applications whenever possible.
The source and deviceid members are used to determine which device triggered the event; the user may have a graphics tablet and a mouse connected simultaneously, for example. You can ignore these fields unless you are writing an application that can take advantage of non-mouse devices.
The last two members of GdkEventButton, x_root and y_root, are the x and y coordinates translated to be relative to the root window rather than the window receiving the event. You can use these as "absolute" coordinates to compare events from two different windows.
There are only two types of key event: GDK_KEY_PRESS and GDK_KEY_RELEASE. Some hardware does not generate key release events; you should not write code that depends on GDK_KEY_RELEASE events, though your code should respond sanely if one is received.
Here are the contents of a key event:
typedef struct _GdkEventKey GdkEventKey;
struct _GdkEventKey
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
guint state;
guint keyval;
gint length;
gchar *string;
};
The first three members are the standard members from GdkEventAny; the time and state members are identical to those found in GdkEventButton.
keyval contains a keysym. The X server keeps a global translation table which converts combinations of physical keys and modifiers to keysyms. For example, the key marked "A" on the keyboard typically generates the keysym GDK_a with no modifiers, and GDK_A with shift held down. Users can change the physical-key-to-keysym mapping; for example, they may rearrange their keys to create a Dvorak keyboard (more commonly, they might swap Control and Caps Lock, or use the Alt key as a Meta key). Keysyms are defined in gdk/gdkkeysyms.h. You will need to include this file to use the keyval field.
Keysyms are matched with a string representation. For example, the GDK_a keysym typically maps to the string "a". (However, X allows the keysym-to-string mapping to be modified.) The string member of GdkEventKey contains a keysym's string representation, and the length member contains the string's length. Keep in mind that the length may be 0 (many non-alphanumeric keys have no string representation by default). (If you're familiar with Xlib, the string field is simply the result of XLookupString() or XmbLookupString(). Even if you aren't familiar with Xlib, the man pages for these functions may be helpful.)
In general, if you are reading key events in order to create a textual representation of what the user is typing, you should use the string field of GdkEventKey. GtkEntry and GtkText use the string field, for example. A word processor would also read this field. If you're reading key events for some other reason (such as keyboard shortcuts), or if you are interested in keys with no string representation by default (such as function keys or arrow keys), you will need to use the keyval field and the keysyms defined in gdk/gdkkeysyms.h.
Here is a sample key event callback demonstrating how to extract information from a key event. It would be suitable for connection to the "key_press_event" signal of any GtkWidget:
static gint
key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
{
if (event->length > 0)
printf("The key event's string is `%s'\n", event->string);
printf("The name of this keysym is `%s'\n",
gdk_keyval_name(event->keyval));
switch (event->keyval)
{
case GDK_Home:
printf("The Home key was pressed.\n");
break;
case GDK_Up:
printf("The Up arrow key was pressed.\n");
break;
default:
break;
}
if (gdk_keyval_is_lower(event->keyval))
{
printf("A non-uppercase key was pressed.\n");
}
else if (gdk_keyval_is_upper(event->keyval))
{
printf("An uppercase letter was pressed.\n");
}
}
gdk_keyval_name() is useful for debugging; it returns the name of the keysym without the GDK_ prefix. For example, it returns "Home" if passed the value GDK_Home. The string is statically allocated. gdk_keyval_is_lower() returns FALSE if the keysym has an uppercase equivalent. Thus it returns TRUE for lowercase letters, numbers, and all non-alphanumeric characters. It returns FALSE only for uppercase letters. gdk_keyval_is_upper() returns the opposite values.
Events are emitted to let you track the mouse as it moves around the screen. Motion events are emitted as the pointer moves inside a window; crossing events are emitted when the pointer enters or leaves a GdkWindow. The type field for motion events is GDK_MOTION_NOTIFY. There are two kinds of crossing events: GDK_ENTER_NOTIFY and GDK_LEAVE_NOTIFY.
There are two ways to track motion events. If you specify GDK_POINTER_MOTION_MASK in the event mask for a window, you will receive as many motion events as the X server can generate. If the user moves the pointer rapidly, you will be deluged in motion events; you must handle them quickly, or your application may become unresponsive while it processes the backlog. If you also specify GDK_POINTER_MOTION_HINT_MASK, motion events are sent one at a time. At most one event will be sent until you call gdk_window_get_pointer(), the pointer leaves and re-enters the window, or a button or key event occurs. Thus, each time you receive a motion event, you must call gdk_window_get_pointer() to get the current pointer position and signal the server that you are ready for another event. See Section 10.6 for details on gdk_window_get_pointer().
Which mode you choose depends on the application. If you need to trace the exact trajectory of the pointer, you will want to get all motion events. If you only care about the most recent pointer position, you will want to include GDK_POINTER_MOTION_HINT_MASK in your window's event mask to minimize network traffic and maximize responsiveness. One caveat: gdk_window_get_pointer() requires a server round-trip to obtain the pointer position; so it does place some maximum limit on your application's responsiveness. If you can handle motion events quickly enough to keep them from backlogging, your application will probably seem faster without GDK_POINTER_MOTION_HINT_MASK. Motion events are unlikely to come more often than a couple hundred per second --- so if you can handle them in less than 5 milliseconds, you should be OK.
You can ask to receive motion events only while one or more mouse buttons are held down. To receive motion events while any button is down, use GDK_BUTTON_MOTION_MASK in place of GDK_POINTER_MOTION_MASK. You can use GDK_POINTER_MOTION_HINT_MASK with GDK_BUTTON_MOTION_MASK to limit the number of events received, just as you can use it with GDK_POINTER_MOTION_MASK. If you are only interested in motion events while a certain button is pressed, you can use the more specific GDK_BUTTON1_MOTION_MASK, GDK_BUTTON2_MOTION_MASK, and GDK_BUTTON3_MOTION_MASK. Any combination of these three is allowed. They can also be combined with GDK_POINTER_MOTION_HINT_MASK to limit the number of events.
In sum, you can select which motion events to receive along the "button state" dimension using these five masks:
By default, you are deluged with events as quickly as the X server can generate them; adding GDK_POINTER_MOTION_HINT_MASK to the event mask enables one-at-a-time behavior.
Motion events are represented by GdkEventMotion:
typedef struct _GdkEventMotion GdkEventMotion;
struct _GdkEventMotion
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble pressure;
gdouble xtilt;
gdouble ytilt;
guint state;
gint16 is_hint;
GdkInputSource source;
guint32 deviceid;
gdouble x_root, y_root;
};
Most of these fields should be familiar to you from GdkEventButton; in fact, the only field unique to GdkEventMotion is the is_hint flag. If this field is TRUE, GDK_POINTER_MOTION_HINT_MASK was selected. You might use this flag if you are writing a widget for other people to use, and you want to let them choose how to receive motion events. In your motion event handler, you could do this:
double x, y;
x = event->motion.x;
y = event->motion.y;
if (event->motion.is_hint)
gdk_window_get_pointer(event->window, &x, &y, NULL);
That is, you call gdk_window_get_pointer() only if necessary. If you are using GDK_POINTER_MOTION_HINT_MASK, you should prefer the results from gdk_window_get_pointer() to the coordinates given in the event, because they are more recent. (If you are receiving every event, it makes no sense to call gdk_window_get_pointer() because it is relatively slow and will worsen the backlog---you're getting every event eventually anyway.)
Crossing events occur when the mouse pointer enters or leaves a window. If you move the mouse pointer rapidly across your application, GDK generates these events for every window you pass through. However, GTK+ will try to remove the events "in the middle" and forward only the first leave event and the last enter event to widgets. If you feel you should be getting enter/leave events when you aren't, this optimization is a likely cause.
Here is GdkEventCrossing:
typedef struct _GdkEventCrossing GdkEventCrossing;
struct _GdkEventCrossing
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkWindow *subwindow;
guint32 time;
gdouble x;
gdouble y;
gdouble x_root;
gdouble y_root;
GdkCrossingMode mode;
GdkNotifyType detail;
gboolean focus;
guint state;
};
Again, many of the fields should be familiar; coordinates relative to the event window and the root window, a time stamp, a state bitfield indicating which buttons and modifiers are active, and the standard three fields from GdkEventAny. However, there are several new fields.
The standard window field contains a pointer to the window the pointer is entering or leaving; x and y are relative to this window. However, the pointer may have been in a child of the window receiving the event before a leave event occurred; the pointer may end up in a child window when an enter event occurs. In these cases, subwindow is set to the child window. Otherwise subwindow is NULL. Note that the child window will receive its own enter and leave events, if it GDK_ENTER_NOTIFY_MASK or GDK_LEAVE_NOTIFY_MASK are in its event mask.
The mode field indicates whether the event occurred normally, or as part of a pointer grab. When the pointer is grabbed or ungrabbed (see Section 10.6), the pointer may be moved; crossing events caused by a grab have the GDK_CROSSING_GRAB mode, those caused by an ungrab have GDK_CROSSING_UNGRAB, and all others have GDK_CROSSING_NORMAL. This field appears to be completely useless; some quick greps through GTK+ and Gnome reveal no examples of its use.
The detail field is rarely used. It gives information about the relative tree positions of the window being left and the window being entered. It has two simple and useful values:
Several other values are also possible: GDK_NOTIFY_VIRTUAL, GDK_NOTIFY_INFERIOR, GDK_NOTIFY_NONLINEAR, GDK_NOTIFY_NONLINEAR_VIRTUAL, and GDK_NOTIFY_UNKNOWN. However, they are never used and are too complicated to explain here.
The focus field in GdkEventCrossing indicates whether the event window or one of its ancestors has the keyboard input focus. Keyboard focus is an X concept, used to determine which window should receive key events. The window manager decides which toplevel window has the focus (usually the focused window is highlighted and brought to the top; most window managers let you choose between "focus follows mouse" and "click to focus" modes). When an application has the focus, it is free to move it among its subwindows---perhaps different text entry fields. However, GTK+ does not use the X focus mechanism for subwindows. Toplevel GtkWindow widgets are the only ones which receive the X focus. Thus, they receive all raw key events from X (by way of GDK). GTK+ implements its own concept of widget focus, which is analagous to X's window focus, but in reality entirely distinct. When a toplevel GtkWindow widget receives key events, it forwards them to the widget with GTK+'s focus.
In short, this means the focus flag will be TRUE if the toplevel GtkWindow containing the event window currently has the X focus. The focus flag is unrelated to GTK+'s widget focus concept.
The previous section explained the difference between GTK+'s concept of keyboard focus and the X/GDK concept. This makes focus events a little bit confusing. There is only one type of focus event, GDK_FOCUS_CHANGE, which is received whenever a window gains or loses the keyboard focus. As we just said, only toplevel GtkWindow widgets ever gain or lose the focus from GDK's point of view; so this event may not seem useful. However, each GtkWindow maintains a current "focus widget" and forwards key events to that widget. It also synthesizes GDK-style focus events as the focus widget changes. Thus, even though GDK-style focus is not being used, widgets receive events in the same way they would if it were being used. There are subtle differences: for example, widgets receive focus events whether or not their GdkWindow's event mask includes GDK_FOCUS_CHANGE_MASK. Only toplevel widgets need to specify this mask.
Focus events themselves are very simple. When a widget gains the keyboard focus, it receives a focus event with its in member set to TRUE (a "focus in event") and when a widget loses the focus it receives a focus event with the in member set to FALSE (a "focus out event"). Otherwise, focus events contain only the three standard fields from GdkEventAny:
typedef struct _GdkEventFocus GdkEventFocus;
struct _GdkEventFocus
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
gint16 in;
};
Expose events are received when a previously-obscured region of a window becomes visible. GdkWindow contents are not recorded; that is, if you draw to a GdkWindow, then the X server places another window on top of it, the graphics in the bottom window will be lost. When the top window is moved away, the bottom window will receive an expose event indicating the region that needs to be re-drawn. Expose events are also sent when a window first appears on-screen. (Incidentally, you should not draw into a GdkWindow until you receive the first expose event. The first expose event is your signal that the window is actually on-screen.)
Expose events have a unique feature: GTK+ synthesizes them for windowless widgets. This is the only kind of event GTK_NO_WINDOW widgets will receive.
Expose events are very simple:
typedef struct _GdkEventExpose GdkEventExpose;
struct _GdkEventExpose
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkRectangle area;
gint count;
};
area is the area that has been exposed and should be redrawn. count is the number of expose events that follow this one; conceivably you might want to compress successive events into a single redraw. However, GDK already makes a reasonable effort to do this, so adding another pass is unlikely to gain much. GdkRectangle is defined as follows:
typedef struct _GdkRectangle GdkRectangle;
struct _GdkRectangle
{
gint16 x;
gint16 y;
guint16 width;
guint16 height;
};
There is one other time you will receive expose events. If you call gdk_window_copy_area() to copy part of one window into another, the source window region may be partially or completely obscured. If it is, X will be unable to copy something sensible from the obscured region. By default, expose events will be generated for the areas of the destination window X was unable to copy something to. Your program's standard redraw routine can then refresh these areas of the destination window by hand. You can turn this behavior off by calling gdk_gc_set_exposures() on the graphics context passed to gdk_window_copy_area(). (Graphics contexts are discussed in Section 10.8.)
If the source window region is not obscured, gdk_window_copy_area() can successfully draw the entire destination region. In this case, no expose events are generated; a single "no expose" event is generated instead. This event contains no information beyond the standard event fields:
typedef struct _GdkEventNoExpose GdkEventNoExpose;
struct _GdkEventNoExpose
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
};
As a GdkWindow is shown, hidden, resized, or destroyed, events are emitted.
Configure events indicate that the size or position of the event window has changed. They include the new size and positon of the window:
typedef struct _GdkEventConfigure GdkEventConfigure;
struct _GdkEventConfigure
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
gint16 x, y;
gint16 width;
gint16 height;
};
All widgets receive this event (since GDK_STRUCTURE_MASK is automatically in the event mask), but the widget size allocation system already conveys the necessary information. That is, most widgets resize their GdkWindow themselves in response to a size allocation; configure events just report back the resize---not very useful. There are two notable exceptions. First, the toplevel GtkWindow widget is in charge of initiating the size allocation process, and has no parent to get an allocation from, so it monitors configure events to determine its size allocation. When you resize a GtkWindow using its window manager decorations, it will receive configure events and act accordingly. The second exception is GtkDrawingArea. GtkDrawingArea sends itself a configure event when it receives its size allocation. This is convenient, since you will usually want to repaint the contents of the drawing area if it is resized. Like all "fake" events GTK+ creates, send_event will be TRUE for this configure event.
Other changes in a GdkWindow are signalled with GdkEventAny; these events contain no special information, they just tell you that something has occurred. They are distinguished by their type field:
There are a few other event types that aren't very useful, so this book does not cover them in detail. This section gives a brief description of each one, for completeness. You can safely skip this section.
Visibility events occur when a window is obscured or unobscured. They are not very useful, because other events already give you the information you need. In particular, when a window is unobscured you receive expose events indicating which newly-visible areas should be redrawn. Here is the event:
typedef struct _GdkEventVisibility GdkEventVisibility;
struct _GdkEventVisibility
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkVisibilityState state;
};
state is an enumeration value indicating whether the window was obscured or unobscured.
X associates properties with windows. These are basically key-value pairs used for interclient communication; most commonly, they relay some information about a toplevel window to the window manager. GTK+ provides a high-level interface for all the important properties, so you should not need to deal with them directly. Property events are sent when a property's value changes.
typedef struct _GdkEventProperty GdkEventProperty;
struct _GdkEventProperty
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkAtom atom;
guint32 time;
guint state;
};
X has a simple cut-and-paste mechanism (typically, you select some text with button 1 and paste with button 2). The current contents of the "clipboard" are called the selection. You only need to know about this if you are writing a widget like GtkText or GtkEntry that allows you to cut and paste text. Selection events indicate that the current selection has changed, and look like this:
typedef struct _GdkEventSelection GdkEventSelection;
struct _GdkEventSelection
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkAtom selection;
GdkAtom target;
GdkAtom property;
guint32 requestor;
guint32 time;
};
Most GTK+ widgets deal with the selection using a higher-level interface, found in gtk/gtkselection.h. If you're interested in writing selection-handling code, have a look at this header.
Client events are an arbitrary chunk of data sent from one application to another. However, there are some conventional "messages" that can be sent, mostly defined in the Inter-Client Communication Conventions Manual (ICCCM). The ICCCM is free with the X Window System distribution from the Open Group. Client events are mostly used for communication between applications and the window manager. (The Xlib event being wrapped is called ClientMessage, so look for ClientMessage in the ICCCM.) An important ClientMessage event is sent from the window manager to applications, asking for windows to be deleted. However, GDK converts these events to a GdkEventAny with type GDK_DELETE, so a GdkEventClient will not be received. Some events involved in drag-and-drop are also ClientMessage events, but GDK translates these to GdkEventDND, so a GdkEventClient will not be received for them either. GdkEventClient will only be received if some other client sends your application an unconventional message GDK and GTK+ are not prepared to understand. Most of the common messages are nicely wrapped in a high-level interface.
Just for reference, here is the event:
typedef struct _GdkEventClient GdkEventClient;
struct _GdkEventClient
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkAtom message_type;
gushort data_format;
union {
char b[20];
short s[10];
long l[5];
} data;
};
The union at the end is used to hold the contents of the message. send_event is always TRUE, since this event is always sent from one client to another and does not come from the X server.
Drag and drop events are actually pretty useful if you're implementing a drag and drop feature in your application. However, since few applications need to do this and the topic is fairly involved, this books glosses over the topic. GTK+ comes with some documentation on drag and drop and several examples.
typedef struct _GdkEventDND GdkEventDND;
struct _GdkEventDND {
GdkEventType type;
GdkWindow *window;
gint8 send_event;
GdkDragContext *context;
guint32 time;
gshort x_root, y_root;
};
Proximity events are generated when using GDK's wrapper for the XInput extension. The XInput extension is an add-on for standard X that allows you to use nonstandard devices such as graphics tablets. A proximity event indicates that the stylus has moved in or out of contact with the tablet, or perhaps that the user's finger has moved in or out of contact with a touch screen. The X distribution comes with some documentation for the XInput extension, and Owen Taylor's gsumi application contains example GDK code.
typedef struct _GdkEventProximity GdkEventProximity;
struct _GdkEventProximity
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
GdkInputSource source;
guint32 deviceid;
};
The mouse pointer is represented on the screen by a small bitmap called the cursor. The cursor is normally an arrow shape, but it can be changed on a window-by-window basis. As the pointer moves, it generates motion events and moves the cursor on the screen to give the user feedback.
You can query the pointer's location with gdk_window_get_pointer() (Function Listing 10.7). This function requests the X and Y coordinates of the pointer relative to the window passed as its first argument. It also requests the currently active modifiers (including modifier keys and buttons; this field is identical to the state field in several events, such as button events). If NULL is passed for the x, y, or state arguments, that argument will be ignored.
| #include <gdk/gdk.h> |
GdkWindow*
gdk_window_get_pointer(GdkWindow* window,
gint* x,
gint* y,
GdkModifierMask* state) |
It is possible to grab the pointer, which means that all pointer events will go to the grab window for the duration of the grab. Normally pointer events go to the window the pointer is inside. You should grab the pointer, for example, if the user is using click-and-drag selection to select a rectangular area. If they click and then inadvertently drag the pointer outside the window, you should continue to track the pointer's location and change the selection accordingly. The grab also ensures that pointer events won't be sent to other applications.
To grab the pointer, call gdk_pointer_grab(), shown in Function Listing 10.8. The first argument to this function is the grab window; this window will receive events during the grab. The next argument should be TRUE or FALSE; it specifies whether events will go only to the grab window, or to its child windows as well. The confine_to argument specifies a window to confine the pointer to. The user will not be able to move the pointer outside this window. You can specify a different cursor for the duration of the grab; see the next section for details on creating a cursor. If you don't want to change the cursor, give NULL as the cursor argument. (Side note: it is safe to destroy the cursor immediately after calling gdk_pointer_grab() because it is a server-side resource and X will not deallocate it until the grab is over.)
The final argument, time, specifies when the grab should take effect, in server time. This is intended to resolve conflicts if two clients try to grab the pointer simultaneously; the time must be after the last grab time, and it must not be in the future. Usually, you will want to use the time field from the event you're processing, or the GDK_CURRENT_TIME macro. GDK_CURRENT_TIME is a magic constant that tells the X server to substitute the current time.
gdk_pointer_grab() returns TRUE if it succeeds. It is possible for it to fail if the grab window or confine_to window is hidden, another client has the grab already, or any of the arguments are invalid. Regrettably few applications check this return value, which is a bug (granted, a difficult-to-trigger one).
To ungrab the pointer, call gdk_pointer_ungrab(); the time argument is identical to the one in gdk_pointer_grab(). You can find out if the pointer is grabbed using gdk_pointer_is_grabbed(). You must ungrab the pointer when you're finished with it, because the user will be unable to use other applications while the pointer is grabbed.
Note that the GDK-level concept of grabbing the pointer is distinct from the GTK+-level grab concept. A GTK+ grab redirects certain events to a grabbing widget, creating a "modal" widget such as a dialog (see Section 3.3.3). GTK+'s grab only affects the current application; only events that occur on one of the current application's widgets are redirected. The scope of a GDK grab is wider, encompassing the entire X server, not just your application.
| #include <gdk/gdk.h> |
gint
gdk_pointer_grab(GdkWindow* window,
gint owner_events,
GdkWindow* confine_to,
GdkCursor* cursor,
guint32 time) |
void gdk_pointer_ungrab(guint32 time) |
gint gdk_pointer_is_grabbed() |
You can change the cursor shape at any time; cursor shapes are set on a window-by-window basis with gdk_window_set_cursor() (Function Listing 10.9). By default, windows use their parent's cursor; you can restore the default cursor by setting a window's cursor to NULL.
Two ways are provided to create a cursor. The simplest way is to choose a cursor from the cursor font that comes with X. The cursor font contains cursors instead of characters; you can view it with the command xfd -fn cursor. You can also browse the available cursors using the testgtk program that comes with GTK+. Each cursor shape has a constant defined in gdk/gdkcursors.h. gdk_cursor_new() accepts one of these constants as its only argument:
GdkCursor* cursor; cursor = gdk_cursor_new(GDK_CLOCK); gdk_window_set_cursor(window, cursor); gdk_cursor_destroy(cursor);
Notice that you can destroy the cursor as soon as you attach it to a window; GdkCursor is a client-side handle for a server-side resource, and X will keep the server-side resource around as long as it's in use.
If none of the cursors in the cursor font are appropriate, you can create a custom cursor from a bitmap. Two bitmaps, actually: the source pixmap, and the mask. Since these are bitmaps, every pixel is either on or off (0 or 1). If a pixel is 0 in the mask, that pixel will be transparent. If a pixel is 1 in both pixmaps, it will be displayed in the fg (foreground) color passed to gdk_cursor_new_from_pixmap(). If a pixel is 1 in the mask but 0 in the source pixmap, it will be displayed in the bg (background) color. The source and mask pixmaps must be the same size, and they must have a depth of one.
The foreground and background colors should be contrasting, so the cursor will be visible against any background. Most cursors are drawn in the foreground color and outlined in the background color. (To see this, move an X cursor over a dark background; you will notice a white outline around its edges.) To achieve this, mask should be slightly larger than source, but the same shape.
The final two arguments to gdk_cursor_new_from_pixmap() are the coordinates of the cursor's hot spot. This is the point drawn at the mouse pointer's location---the tip of an arrow cursor, or the center of a crosshair cursor. gdk_cursor_new_from_pixmap() will fail if the hot spot is not within the bitmap.
| #include <gdk/gdk.h> |
GdkCursor* gdk_cursor_new(GdkCursorType cursor_type) |
GdkCursor*
gdk_cursor_new_from_pixmap(GdkPixmap* source,
GdkPixmap* mask,
GdkColor* fg,
GdkColor* bg,
gint x,
gint y) |
void gdk_cursor_destroy(GdkCursor* cursor) |
void
gdk_window_set_cursor(GdkWindow* window,
GdkCursor* cursor) |
An X font is (yet another) a server-side resource. In essence, a font is a collection of bitmaps representing characters. The bitmaps in a single font will have a similar size and style. GDK allows you to manipulate fonts with a client-side handle called GdkFont.
To obtain a GdkFont, call gdk_font_load() (or use the font from a preexisting GtkStyle; more on this in Section 10.11). Fonts are loaded by name; font names are a fairly involved topic. Names follow a convention called the "X Logical Font Description," or XLFD. The best way to get a feel for XLFD is to play with the xfontsel program that comes with X. You can also get a list of font names on your X server with the xlsfonts program. The standard X distribution includes a 30-page XLFD manual, but the basics aren't too complicated.
A font name is a string made up of hyphen-separated fields. Each field describes some aspect of the font. For example:
-misc-fixed-medium-r-normal--0-0-75-75-c-0-iso8859-1
or
-adobe-new century schoolbook-bold-i-normal--11-80-100-100-p-66-iso8859-1
The fourteen fields are:
It is not necessary to specify all 14 fields when requesting a font. "Wild cards" are allowed: * matches any number of characters, and ? matches a single character. For example, a 160 point Bold Roman Helvetica font can be requested like this:
-*-helvetica-bold-r-*-*-*-160-*-*-*-*-*-*
When you pass a font name to gdk_font_load(), you should consider it a default choice only. Users in other countries will certainly want to use a font appropriate to their language; US and European users might want to customize fonts too. Also, there are no fonts guaranteed to exist on all servers. Thus you should provide a way to customize any fonts you use. The easiest way is to use fonts from a widget's GtkStyle (again, see Section 10.11).
gdk_font_load() returns NULL if it fails to find a font matching the supplied name. When you are done with a font, you should release it by calling gdk_font_unref(). Function Listing 10.10 summarizes these functions.
When loading fonts, you will need to specify at least the font name, the weight, the slant, and a size---otherwise the * wildcards would probably load a random bold italic font, which is unlikely to be what you want. The Xlib Programming Manual suggests that you always specify the font size in points, so that users with different monitors will get the right effect; however, X servers do not reliably know the display resolution, so this is more theory than reality. Perhaps it's better to specify pixels, because you do know the pixel size of other elements of your display. Neither is perfect; be sure you make your application's fonts configurable.
| #include <gdk/gdk.h> |
GdkFont* gdk_font_load(const gchar* font_name) |
void gdk_font_unref(GdkFont* font) |
To use a font, you typically need detailed information about its metrics. Font metrics are used to position characters with respect to one another, and to determine the size of strings rendered in the font. The most fundamental metrics are the ascent and descent of the font. Text is placed on a baseline; the baseline is like a rule on a sheet of notebook paper. The bottom of each character touches the baseline. Some characters (such as lowercase "p" and "y") extend below the baseline. A font's descent is the maximum distance below the baseline reached by its characters. Its ascent is the maximum distance above the baseline. The height of a font is the sum of the ascent and the descent. When rendering multiple lines of text, you should leave at least the font's height between each baseline.
Ascent and descent are available as members of the GdkFont struct:
typedef struct _GdkFont GdkFont;
struct _GdkFont
{
GdkFontType type;
gint ascent;
gint descent;
};
The type field distinguishes fonts from fontsets; fontsets are used to display non-European languages.
PRODUCTION: Please add this footnote after the preceding sentence: "If you're wondering where the reference to the server-side resource is hidden: the GdkFont* returned from gdk_font_load() actually points to a GdkFontPrivate defined in gdk/gdkprivate.h; GdkFont contains only the public fields of GdkFontPrivate. Nearly all the GDK types work this way." Thanks -hp
Individual characters in a font have their own ascent and descent; a character's ascent and descent will always be less than or equal to the font's ascent and descent. GDK can calculate the sum of the maximum ascent and descent for a particular string, rather than an entire font; this height will be less than or equal to the font's height. The relevant functions are gdk_string_height(), gdk_text_height(), and gdk_char_height(). gdk_text_height() is different from gdk_string_height() because it accepts the length of the string as an argument; gdk_string_height() calls strlen() for you. Thus gdk_text_height() is preferred if you already know a string's length.
In addition to its vertical metrics, each character in a font has three metrics describing its horizontal dimensions. The width of a character is the distance from the left origin of the character to the start of the next character. Note: the width is not the distance to the furthest-right pixel in a character; in some fonts, especially italic fonts, characters may lean over past the start of the next character. The left-side-bearing or lbearing is the distance from the left origin to the leftmost pixel in the character; the right-side-bearing or rbearing is the distance from the origin to the rightmost pixel in the character. Thus the rbearing can be larger than the width, in the case of italic fonts that lean over past the start of the next character.
GDK functions that return a character or string width return the width of the character, or the sum of the character widths in the string. If the rbearing of the rightmost character is greater than its width, a string may require more space than gdk_string_width(), gdk_text_width(), or gdk_char_width() will return. Like the height-measurement functions, the _string_ variant computes the length of the string for you, and the _text_ variant accepts a precomputed length as an argument.
The functions ending in _measure are more often what you want. For a string with N characters, these return the sum of the widths of the first N-1 characters, plus the rbearing of the final character. That is, they take into account the fact that rbearing may be greater than width. If you're trying to decide how much space to leave for rendering a string, you probably want the gdk_string_measure(), gdk_text_measure(), or gdk_char_measure() functions. Sometimes you don't want to consider the rbearing, however; for example, if you're centering a string, it may look nicer to use the width (because a small italic flourish extending past the width won't "fill" the space and the string will look slightly left of center).
gdk_text_extents() and gdk_string_extents() return all the metrics for a string, including both bearings, the width, ascent, and descent. The returned left-side-bearing is the leftmost pixel in the string; the right-side-bearing is the rightmost pixel as returned by gdk_text_measure(). The width is the sum of character widths, as returned by gdk_text_width().
Figure 10.1 depicts the various font metrics. Function Listing 10.11 summarizes the API for querying font metrics. All font metrics are computed on the client side, so these functions are not expensive compared to most of the drawing API.
| #include <gdk/gdk.h> |
gint
gdk_string_width(GdkFont* font,
const gchar* string) |
gint
gdk_text_width(GdkFont* font,
const gchar* string,
gint string_length) |
gint
gdk_char_width(GdkFont* font,
gchar character) |
gint
gdk_string_measure(GdkFont* font,
const gchar* string) |
gint
gdk_text_measure(GdkFont* font,
const gchar* string,
gint string_length) |
gint
gdk_char_measure(GdkFont* font,
gchar character) |
gint
gdk_string_height(GdkFont* font,
const gchar* string) |
gint
gdk_text_height(GdkFont* font,
const gchar* string,
gint string_length) |
gint
gdk_char_height(GdkFont* font,
gchar character) |
void
gdk_string_extents(GdkFont* font,
const gchar* string,
gint* lbearing,
gint* rbearing,
gint* width,
gint* ascent,
gint* descent) |
void
gdk_text_extents(GdkFont* font,
const gchar* string,
gint string_length,
gint* lbearing,
gint* rbearing,
gint* width,
gint* ascent,
gint* descent) |
A graphics context, or GC, is simply a set of parameters to be used when drawing (such as color, clip mask, font, and so on). It is a server-side resource, just as pixmaps and windows are. GCs reduce the number of arguments to the GDK drawing functions, and also reduce the number of parameters passed from client to server with each drawing request.
A graphics context can be created with a GdkGCValues struct, analagous to GdkWindowAttr; the struct contains all the interesting features of a graphics context, and you pass gdk_gc_new_with_values() flags indicating which fields are valid. The other fields retain their default value. You can also create an all-default GC with gdk_gc_new() (this is usually easier). Functions are provided to change GC settings after the GC is created as well---but remember that each change requires a message to the X server. These functions are summarized in Function Listing 10.12. The attributes of a GC, and the flags used as the final argument to gdk_gc_new_with_values(), are summarized in Table 10.6.
All GCs are not interchangeable; they are tied to a particular depth and visual. The GC's depth and visual must match the depth and visual of the drawable you are drawing to. A GC's depth and visual are taken from the GdkWindow* argument to gdk_gc_new(), so the easiest way to handle this issue is to create the GC with the window you plan to draw on.
GdkGCValues is a nice summary of a GC's attributes:
typedef struct _GdkGCValues GdkGCValues;
struct _GdkGCValues
{
GdkColor foreground;
GdkColor background;
GdkFont *font;
GdkFunction function;
GdkFill fill;
GdkPixmap *tile;
GdkPixmap *stipple;
GdkPixmap *clip_mask;
GdkSubwindowMode subwindow_mode;
gint ts_x_origin;
gint ts_y_origin;
gint clip_x_origin;
gint clip_y_origin;
gint graphics_exposures;
gint line_width;
GdkLineStyle line_style;
GdkCapStyle cap_style;
GdkJoinStyle join_style;
};
The foreground color is the "pen color" used to draw lines, circles, and other shapes. The purpose of the background color depends on the particular drawing operation. These colors must be allocated in the current colormap with gdk_color_alloc().
The font field is unused: in Xlib, it specifies the font to use when drawing text. In GDK, it used to have the same purpose; but now the GDK routines for drawing text all require a GdkFont* argument instead. An Xlib graphics context can only store plain fonts, but a GdkFont can also represent a fontset (used to render some foreign languages). GDK should probably store a font field in its GdkGC instead of requiring a font argument to the text-drawing functions, but it doesn't.
The function field specifies how each pixel being drawn is combined with the pixel that already exists in the drawable. There are many possible values, but only two are ever used:
The fill field determines how the tile and stipple fields are used. A tile is a pixmap with the same depth as the destination drawable; it is copied over and over into the destination drawable---the origin of the first tile is (ts_x_origin, ts_y_origin). A stipple is a bitmap (pixmap with depth 1); stipples are also tiled starting at (ts_x_origin, ts_y_origin). Possible fill values are:
Some X servers do not implement the more obscure function and fill modes very efficiently. Don't be surprised if using them noticeably slows down drawing.
The optional clip_mask is a bitmap; only bits set in this bitmap will be drawn. The mapping from the clip mask to the drawable is determined by clip_x_origin and clip_y_origin; these define the drawable coordinates corresponding to (0,0) in the clip mask. It is also possible to set a clip rectangle (the most common and useful form of clipping) or a clip region (a region is an arbitrary area on the screen, typically a polygon or list of rectangles). To set a clip rectangle, use gdk_gc_set_clip_rectangle():
GdkRectangle clip_rect; clip_rect.x = 10; clip_rect.y = 20; clip_rect.width = 200; clip_rect.height = 100; gdk_gc_set_clip_rectangle(gc, &clip_rect);
To turn off clipping, set the clip rectangle, clip region, or clip mask to NULL.
The subwindow_mode of a GC only matters if the drawable is a window. The default setting is GDK_CLIP_BY_CHILDREN; this means that child windows are not affected by drawing on parent windows. This preserves the illusion that child windows are "on top" of parents, and child windows are opaque. GDK_INCLUDE_INFERIORS will draw right over the top of any child windows, overwriting any graphics the child windows may contain; normally this mode is not used. If you do use GDK_INCLUDE_INFERIORS, you will probably use GDK_XOR as your drawing function, since it allows you to restore the child windows' previous contents.
graphics_exposures is a boolean value which defaults to TRUE; it determines whether gdk_window_copy_area() sometimes generates expose events. Section 10.5.8 explained this in more detail.
The final four GC values determine how lines are drawn. These values are used for drawing lines, including the borders of unfilled polygons and arcs. The line_width field specifies the width of a line, in pixels. A line width of 0 specifies a "thin line"; thin lines are one-pixel lines that can be drawn very quickly (usually with hardware acceleration), but the exact pixels drawn depend on the X server in use. For consistent results, use a width of 1 instead.
The line_style field can have one of three values:
Dashes are specified with gdk_gc_set_dashes(); GdkGCValues does not include a field for this. gdk_gc_set_dashes() accepts three arguments:
You might set a whimsical dash pattern this way, for example:
gchar dash_list[] = { 5, 5, 3, 3, 1, 1, 3, 3 };
gdk_gc_set_dashes(gc, 0, dash_list, sizeof(dash_list));
The default dash list is {4, 4} with an offset of 0.
Figure 10.2 shows some dashed lines drawn with GDK_LINE_DOUBLE_DASH. The graphics context's foreground color is black, and its background color is a light gray. The first five lines are the default {4, 4} dash pattern with offsets of 0, 1, 2, 3, and 4. Remember that 0 is the default. Figure 10.3 shows a magnified view of these five lines. The last line is the whimsical dash pattern mentioned above; it's shown magnified in Figure 10.4.
PRODUCTION: For these dashed-line figures, feel free to alter the exact shade of gray used for the "off dashes" so it contrasts with the black "on dashes." Also feel free to crop the whitespace around the dashed lines and to resize the images, as long as the aspect ratio is retained. Thanks -hp


PRODUCTION: Please label the five lines with the following tags, respectively from the top: dash_offset = 0, dash_offset = 1, dash_offset = 2, dash_offset = 3, dash_offset = 4. Thanks -hp

PRODUCTION: Please label each dash in the image (this includes both the gray and the black dashes). They should be labelled with their length; the lengths are: 5, 5, 3, 3, 1, 1, 3, 3. The lengths restart when they're exhausted, so the eight lengths should repeat over and over, one per dash, until you run out of dashes. If the last dash is too short please put the word "truncated" in parentheses after its length. Thanks -hp
cap_style determines how X draws line endpoints (or dash endpoints, if a line is dashed). It has four possible values:
The join_style parameter affects how lines are connected to one another, when drawing a polygon or drawing multiple lines in one function call. If you think of lines as long, thin rectangles, it is clear that they do not connect smoothly; there is a "notch" where the two endpoints come together. The three join styles fill in this notch:
| #include <gdk/gdk.h> |
GdkGC* gdk_gc_new(GdkWindow* window) |
GdkGC*
gdk_gc_new_with_values(GdkWindow* window,
GdkGCValues* values,
GdkGCValuesMask values_mask) |
void
gdk_gc_set_dashes(GdkGC* gc,
gint dash_offset,
gchar dash_list,
gint n) |
void gdk_gc_unref(GdkGC* gc) |
| Attribute | GdkGCValuesMask | Modifying Function | Default Value |
|---|---|---|---|
| GdkColor foreground | GDK_GC_FOREGROUND | gdk_gc_set_foreground() | black |
| GdkColor background | GDK_GC_BACKGROUND | gdk_gc_set_background() | white |
| GdkFont *font | GDK_GC_FONT | gdk_gc_set_font | depends on X server |
| GdkFunction function | GDK_GC_FUNCTION | gdk_gc_set_function() | GDK_COPY |
| GdkFill fill | GDK_GC_FILL | gdk_gc_set_fill() | GDK_SOLID |
| GdkPixmap *tile | GDK_GC_TILE | gdk_gc_set_tile() | pixmap filled with foreground color (i.e. effectively none) |
| GdkPixmap *stipple | GDK_GC_STIPPLE | gdk_gc_set_stipple() | all-bits-on bitmap (i.e. effectively none) |
| GdkPixmap *clip_mask | GDK_GC_CLIP_MASK | gdk_gc_set_clip_mask() | none |
| GdkSubwindowMode subwindow_mode | GDK_GC_SUBWINDOW | gdk_gc_set_subwindow() | GDK_CLIP_BY_CHILDREN |
| gint ts_x_origin | GDK_GC_TS_X_ORIGIN | gdk_gc_set_ts_origin() | 0 |
| gint ts_y_origin | GDK_GC_TS_Y_ORIGIN | gdk_gc_set_ts_origin() | 0 |
| gint clip_x_origin | GDK_GC_CLIP_X_ORIGIN | gdk_gc_set_clip_origin() | 0 |
| gint clip_y_origin | GDK_GC_CLIP_Y_ORIGIN | gdk_gc_set_clip_origin() | 0 |
| gint graphics_exposures | GDK_GC_EXPOSURES | gdk_gc_set_exposures() | TRUE |
| gint line_width | GDK_GC_LINE_WIDTH | gdk_gc_set_line_attributes() | 0 |
| GdkLineStyle line_style | GDK_GC_LINE_STYLE | gdk_gc_set_line_attributes() | GDK_LINE_SOLID |
| GdkCapStyle cap_style | GDK_GC_CAP_STYLE | gdk_gc_set_line_attributes() | GDK_CAP_BUTT |
| GdkJoinStyle join_style | GDK_GC_JOIN_STYLE | gdk_gc_set_line_attributes() | GDK_JOIN_MITER |
| gchar dash_list[] | none | gdk_gc_set_dashes() | {4, 4} |
| gint dash_offset | none | gdk_gc_set_dashes() | 0 |
Once you understand drawables, colors, visuals, graphics contexts, and fonts, actually drawing is very simple. This section is a quick summary of the GDK drawing routines. Remember that drawing is a server-side operation; for example, if you ask to draw a line, Xlib will send the line's endpoints to the server, and the server will do the actual drawing using the specified GC (the GC is also a server-side resource). Often this is an important performance consideration.
PRODUCTION: Please add the following footnote to the previous sentence: "That is, it is much faster to pass the server two line endpoints, rather than all the points in the line. But if your image consists of arbitrary pixels and is not easy to represent as a series of GDK calls, using the GDK functions can be much slower than rendering on the client side and then passing the server a finished raster image with GdkRGB." Thanks -hp
You can draw a single point with gdk_draw_point(), or multiple points with gdk_draw_points() (Function Listing 10.13). The point is drawn in the current foreground color. Multiple points are given as an array. A GdkPoint looks like this:
typedef struct _GdkPoint GdkPoint;
struct _GdkPoint
{
gint16 x;
gint16 y;
};
Remember that X coordinates start in the top left corner, are relative to the drawable, and may not overflow a signed sixteen-bit integer.
| #include <gdk/gdk.h> |
void
gdk_draw_point(GdkDrawable* drawable,
GdkGC* gc,
gint x,
gint y) |
void
gdk_draw_points(GdkDrawable* drawable,
GdkGC* gc,
GdkPoint* points,
gint npoints) |
To draw a single line, pass its endpoints as arguments to gdk_draw_line() (Function Listing 10.14). To draw connected lines, you pass a list of points to gdk_draw_lines(); GDK will "connect the dots." To draw multiple lines that aren't necessarily connected, pass a list of segments to gdk_draw_segments(); a GdkSegment is:
typedef struct _GdkSegment GdkSegment;
struct _GdkSegment
{
gint16 x1;
gint16 y1;
gint16 x2;
gint16 y2;
};
If lines or segments drawn in the same request meet at their endpoints, they are joined with the join style from the GC.
| #include <gdk/gdk.h> |
void
gdk_draw_line(GdkDrawable* drawable,
GdkGC* gc,
gint x1,
gint y1,
gint x2,
gint y2) |
void
gdk_draw_lines(GdkDrawable* drawable,
GdkGC* gc,
GdkPoint* points,
gint npoints) |
void
gdk_draw_segments(GdkDrawable* drawable,
GdkGC* gc,
GdkSegment* segments,
gint nsegments) |
Rectangles are drawn with gdk_draw_rectangle() (Function Listing 10.15). The filled argument indicates whether to fill the rectangle; TRUE means to fill it.
| #include <gdk/gdk.h> |
void
gdk_draw_rectangle(GdkDrawable* drawable,
GdkGC* gc,
gint filled,
gint x,
gint y,
gint width,
gint height) |
gdk_draw_arc() draws an ellipse or a portion of one. (Function Listing 10.16). The arc can be filled or unfilled; the third argument to the function toggles fill. The fourth through seventh arguments describe a rectangle; the ellipse is inscribed in this rectangle. angle1 is the angle at which to start drawing; it is relative to the 3 o'clock position (that is, 0 radians). angle2 is the distance to travel around the arc; if positive, travel is counterclockwise, otherwise travel is clockwise. Both angle1 and angle2 are specified in sixty-fourths of a degree; so, 360 degrees is given as 360*64. This allows more precise specification of the arc's size and shape, without using floating point numbers. angle2 should not exceed 360 degrees, since it is nonsensical to move more than 360 degrees around the ellipse.
To draw a circle, draw from 0 to 360*64 inside a square:
gdk_draw_arc(drawable, gc, TRUE,
0, 0,
50, 50,
0, 360*64);
To draw half an ellipse, change the aspect ratio and halve the span of the arc:
gdk_draw_arc(drawable, gc, TRUE,
0, 0,
100, 50,
0, 180*64);
Many X servers draw the edges of filled arcs in an aesthetically unpleasing way; in particular, very small circles may not look very circular. You can work around this by also drawing the circle's outline.
| #include <gdk/gdk.h> |
void
gdk_draw_arc(GdkDrawable* drawable,
GdkGC* gc,
gint filled,
gint x,
gint y,
gint width,
gint height,
gint angle1,
gint angle2) |
gdk_draw_polygon() draws a filled or unfilled polygon (Function Listing 10.17). Notice that gdk_draw_lines() can also be used to draw an unfilled polygon (there is no reason to prefer one or the other). The arguments to gdk_draw_polygon() are the same as those to gdk_draw_lines(). The polygon does not have to be convex. It may also be self-intersecting. Self-intersecting polygons are filled with an "Even-Odd Rule," which means regions with an odd number of polygon areas overlapping them are not filled. That is, if the polygon does not overlap itself, it is entirely filled; if a region is overlapped once, it is not filled; if it's overlapped twice, it is filled; and so on.
| #include <gdk/gdk.h> |
void
gdk_draw_polygon(GdkDrawable* drawable,
GdkGC* gc,
gint filled,
GdkPoint* points,
gint npoints) |
There are two functions to draw strings; as an optimization, gdk_draw_text() takes the length of the string to draw as an argument. gdk_draw_string() uses strlen() to compute the string length for you. Otherwise the two are identical. The x and y coordinates specify the location of the left side of the text's baseline. See Section 10.7 for more information on fonts and font metrics. Text is drawn in the foreground color.
There is no way to draw scaled or rotated text with GDK. GnomeCanvasText offers a slow and low-quality way to render scaled and rotated text (see Section 12.4). If you need high-quality scaling and rotating, you will need to use additional libraries, such as t1lib for Type 1 fonts or FreeType for True Type fonts. Another possibility is the Display Postscript extension to X (XDPS); the GNU Project is working on a free implementation of XDPS. The Gnome project also has a text solution in development, as part of the gnome-print library.
| #include <gdk/gdk.h> |
void
gdk_draw_string(GdkDrawable* drawable,
GdkFont* font,
GdkGC* gc,
gint x,
gint y,
const gchar* text) |
void
gdk_draw_text(GdkDrawable* drawable,
GdkFont* font,
GdkGC* gc,
gint x,
gint y,
const gchar* text,
gint text_length) |
gdk_draw_pixmap() copies a region from a pixmap to another drawable (pixmap or window). The source and destination drawables must have the same depth and visual. If you pass -1 for the width or height, the full size of the source pixmap is substituted. The source can actually be any drawable, including a window, but gdk_window_copy_area() will make your code clearer if the source is a window. Function Listing 10.19 shows gdk_draw_pixmap().
| #include <gdk/gdk.h> |
void
gdk_draw_pixmap(GdkDrawable* drawable,
GdkGC* gc,
GdkDrawable* src,
gint xsrc,
gint ysrc,
gint xdest,
gint ydest,
gint width,
gint height) |
GDK's GdkRGB module allows you to copy a client-side buffer of image data to a drawable. If you need to manipulate images extensively, or copy image data to the server, this is the correct way to do it. You can't directly manipulate a GdkPixmap because a pixmap is a server-side object. Copying image data to the server with gdk_draw_point() would be unbelievably slow, since each point would require a server request (probably more than one, since you will need to change the GC for each point).
Internally, GdkRGB uses an object called GdkImage to rapidly copy image data to the server in a single request. This is still somewhat slow---sizeable data does have to be copied---but GdkRGB is highly tuned and uses shared memory if the client and server happen to be on the same machine. So it's the fastest way to perform this task, given the X architecture. It will also handle some tricky issues for you (such as adapting to the colormaps and visuals available on a given X server).
The GdkRGB functions are in a separate header, gdk/gdkrgb.h. Before using any GdkRGB functions, you must initialize the module with gdk_rgb_init() (Function Listing 10.20); this sets up the visual and colormap GdkRGB will use, and some internal data structures.
The drawable you intend to copy the RGB buffer to must use GdkRGB's visual and colormap. If the drawable is a part of a widget, the easiest way to ensure this is to push the GdkRGB visual and colormap when you create the widget:
GtkWidget* widget; gtk_widget_push_visual(gdk_rgb_get_visual()); gtk_widget_push_colormap(gdk_rgb_get_cmap()); widget = gtk_whatever_new(); gtk_widget_pop_visual(); gtk_widget_pop_colormap();
The current version of GTK+ will be better-behaved if you do this when creating the toplevel window containing the drawable, instead of when creating the drawable itself. However, in principle you can do it for only the drawable.
GdkRGB understands several kinds of image data, including 24- and 32-bit RGB data, 8-bit grayscale, and 8-bit indexes into an array of RGB values (a client-side GdkRgbCmap). This section describes only the simplest, 24-bit RGB data; this kind of buffer is rendered with gdk_draw_rgb_image(). There are separate functions to render the other buffer types, but all of them work in essentially the same way.
A 24-bit RGB buffer is a one-dimensional array of bytes; every byte triplet makes up a pixel (byte 0 is red, byte 1 is green, byte 2 is blue). Three numbers describe the size of the array and the location of bytes within it:
The x, y, width, and height arguments to gdk_rgb_draw_image() define a region of the target drawable to copy the RGB buffer to. The RGB buffer must have at least width columns and height rows. Row 0, column 0 of the RGB buffer will be copied to point (x, y) on the drawable.
Dithering simulates a larger number of colors on displays with a limited palette. Dithering only matters on 8- and 16-bit displays; 24-bit displays do not have a limited palette. The dither argument is an enumerated type; it has three possible values:
The gc argument to gdk_draw_rgb_image() is simply passed through to gdk_draw_image() (recall that GdkRGB uses GdkImage internally). The gc components that make sense are used (such as the clip mask, drawing function, and subwindow mode).
| #include <gdk/gdkrgb.h> |
void gdk_rgb_init() |
GdkColormap* gdk_rgb_get_cmap() |
GdkVisual* gdk_rgb_get_visual() |
void
gdk_draw_rgb_image(GdkDrawable* drawable,
GdkGC* gc,
gint x,
gint y,
gint width,
gint height,
GdkRGBDither dither,
guchar* rgb_buf,
gint rowstride) |
GDK objects have either reference counting or destruction, but not both. Pixmaps, fonts, graphics contexts, and colormaps are purely reference counted. (gdk_gc_destroy() exists but is deprecated --- it's just a wrapper for gdk_gc_unref().) In general, reference counting is analagous to GtkObject reference counting. That is, objects start with a reference count of one; when the reference count reaches 0 then the object is destroyed.
Cursors and images are not reference counted; they simply have a destroy function. Some types represent static objects that are never destroyed; GdkVisual is the main example.
GdkWindow is the strange case; it's reference counted, but gdk_window_destroy()must be called at some point. The reference counting applies to the client-side GdkWindow handle; gdk_window_destroy() applies to the actual server-side object. See Section 10.2 for an explanation of the distinction. gdk_window_destroy() unreferences the client-side handle after it destroys the server-side object. It's safe to call any of the GdkWindow functions on a destroyed window that still has a reference count greater than zero; they will all return immediately without taking any action.
In practice this means that one section of code should "own" the GdkWindow; it will create the window, and hold the initial reference (remember that objects are created with a reference count of one). It will also call gdk_window_destroy() eventually, destroying the server-side object and removing the initial reference count. If no other code increases the count, the client-side handle will be freed. If some other code has increased the reference count with gdk_window_ref(), the client-side handle will remain safe to use but attempts to use it will have no effect. When the reference count is eventually decremented to zero, the client-side handle will be freed.
In GTK+, windows are generally created and destroyed by the same widget; if other widgets want to draw on the window, they increase the window's reference count.
GtkStyle is not part of GDK, but it is an important abstraction layer between GTK+ and GDK that allows users to customize how widgets are rendered. Instead of drawing with GDK directly, widgets should prefer GDK resources from a GtkStyle, and special drawing functions provided in gtk/gtkstyle.h. Often there is no appropriate function, but when there is it should be used.
A GtkStyle stores GDK resources to be used when drawing widgets. Styles allow widgets to share these resources, reducing overhead; they also permit users to customize GTK+'s appearance. Here is the GtkStyle struct:
typedef struct _GtkStyle GtkStyle;
struct _GtkStyle
{
GtkStyleClass *klass;
GdkColor fg[5];
GdkColor bg[5];
GdkColor light[5];
GdkColor dark[5];
GdkColor mid[5];
GdkColor text[5];
GdkColor base[5];
GdkColor black;
GdkColor white;
GdkFont *font;
GdkGC *fg_gc[5];
GdkGC *bg_gc[5];
GdkGC *light_gc[5];
GdkGC *dark_gc[5];
GdkGC *mid_gc[5];
GdkGC *text_gc[5];
GdkGC *base_gc[5];
GdkGC *black_gc;
GdkGC *white_gc;
GdkPixmap *bg_pixmap[5];
/* private */
gint ref_count;
gint attach_count;
gint depth;
GdkColormap *colormap;
GtkThemeEngine *engine;
gpointer engine_data;
GtkRcStyle *rc_style;
GSList *styles;
};
The private fields should be ignored. The public fields contain GDK resources for widget rendering. The first group of fields contains arrays of colors; these arrays are indexed by the widget state enumeration (GTK_STATE_ACTIVE, etc.). A widget might use widget->style->fg[GTK_STATE_NORMAL] to render text, for example. Each widget has an associated style, stored in the style field of GtkWidget.
Widgets should use the font stored in their associated GtkStyle; they should use the style's graphics contexts when drawing in the style's colors.
GtkStyle also contains a virtual table, GtkStyleClass, which can be implemented by a dynamically-loaded theme engine. The virtual table is quite large, so it isn't reproduced here. Have a look at gtk/gtkstyle.h.
gtk/gtkstyle.h contains drawing functions that use a style's virtual table to draw various GUI elements. There are two variants of each drawing function. One variant, prefixed with gtk_draw_, renders to any drawable; the other variant, prefixed with gtk_paint_, renders part of a widget. For example, gtk_draw_shadow() looks like this:
void gtk_draw_shadow (GtkStyle *style,
GdkWindow *window,
GtkStateType state_type,
GtkShadowType shadow_type,
gint x,
gint y,
gint width,
gint height);
While gtk_paint_shadow() adds area, widget, and detail arguments:
void gtk_paint_shadow (GtkStyle *style,
GdkWindow *window,
GtkStateType state_type,
GtkShadowType shadow_type,
GdkRectangle *area,
GtkWidget *widget,
gchar *detail,
gint x,
gint y,
gint width,
gint height);
Each of these corresponds to the draw_shadow member in GtkStyleClass.
All gtk_paint_ functions add the same three arguments to their gtk_draw_ counterparts; the area argument is a clipping rectangle, the widget argument is the widget being drawn to, and the detail argument is a hint used by theme engines. Here's a call to gtk_paint_shadow() from the GtkEntry source code, for example:
gtk_paint_shadow (widget->style, widget->window,
GTK_STATE_NORMAL, GTK_SHADOW_IN,
NULL, widget, "entry",
x, y, width, height);
Here the area argument is NULL, specifying that no clipping should be used.
Because there are a couple dozen functions in GtkStyleClass, and there are numerous examples in the GTK+ source code, this book won't describe them in detail. When writing your own widgets, simply locate a GTK+ widget that draws a similar graphical element, and use the same gtk_paint_ function it uses.
HTML output machine generated on Sunday, June 27 1999, 12:56:12