GTK Programming in C++
GTK Programming in C++
c h a p t e r
3
Signals, Events,
Objects, and Types
This chapter begins with a discussion of signals and signal handling. The topic of signals is
an important one. A typical Gtk+ application will perform all of its useful work within the
context of a signal handler, as we will see time and again throughout the course of this book.
In addition to signals, we’ll also cover Gtk+ events and objects, defining what they are and
how they can be used and manipulated by an application. The chapter will close with a short
discussion on Gtk+ types.
Signals
Signals provide the mechanism by which a widget communicates useful information to a
client about some change in its state.
In Chapter 2, “Hello Gtk+!,” we developed and discussed three “Hello World!” applications.
Two of these were console-based, using standard I/O to display output to the screen and retrieve
input from the user. We saw that flow of control in these programs was synchronous, meaning
that statements were executed one after another, and when I/O was needed, the program would
block in a routine such as fgets() until the input data needed by the application was entered by
the user. The third of our “Hello World!” applications was also our first Gtk+ application. Two
signal functions or callbacks were implemented in hellogtk+. Neither of these functions was
called directly by hellogtk+. Instead, one of these functions was invoked by Gtk+ in response to
the user pressing the “Print” button. The other was invoked in response to the application being
closed (via a window manager control, for example).
47
4276 | CH03.fm Page 48 Sunday, July 15, 2001 5:25 PM
• The pointer is positioned over the button, and a mouse button is released.
• The user clicks the button (a combination of pressing and releasing a mouse button
while the pointer is positioned over the button).
Each widget class implements signals needed to make that widget class useful to applica-
tion designers. In addition, widget classes inherit signals from classes higher in the Gtk+ class
hierarchy. For example, a signal is emitted when a push button is destroyed. This signal is
actually generated by a superclass of GtkButton. The signals implemented by a superclass
represent functionality needed by many classes of widget. It is better to implement this func-
tionality once in a superclass, allowing child classes to inherit the behavior, than it is to repli-
cate the same functionality in each of the widget classes that need it.
Gtk+ does not force clients to use any of the signals that a class implements. However,
in order to be useful, most applications will need to make use of at least one of the signals
provided so that the widget can communicate useful information back to the client.
Handling Signals
Handling a signal in a Gtk+ application involves two steps. First, the application must imple-
ment a signal handler; this is the function that will be invoked by the widget when the signal
triggers. Second, the client must register the signal handler with the widget. Registering a sig-
nal handler with a widget occurs after the application has created or instantiated the widget,
by calling the Gtk+ routine gtk_signal_connect(). The prototype for this function is:
gint
gtk_signal_connect(
GtkObject *object, /* the widget */
gchar *name, /* the signal */
GtkSignalFunc func, /* the signal handler */
gpointer func_data ); /* application-private data */
The first argument, object, tells Gtk+ from which widget instance we would like the sig-
nal to be generated. This widget pointer is returned by a call to one of the gtk_*_new func-
tions. For example, if the widget we are registering the signal handler with is a GtkButton,
then the object argument is the return value from the function gtk_button_new() or gtk_
button_new_with_label(). Because both of these functions return a variable of type Gtk-
Widget *, we must use one of the casting macros provided by Gtk+ to coerce the GtkWidget
* variable holding the widget instance pointer to the type GtkObject *. For example:
GtkWidget *button;
...
Signals 49
The second argument to gtk_signal_connect() is the name of the signal we would like to
associate with the signal handler. For those signals implemented by GtkButton, this will be
one of the following strings:
• enter The pointer entered the rectangular region occupied by the button.
• leave The pointer left the rectangular region occupied by the button.
• pressed The pointer was positioned over the button, and a mouse button was pressed.
• released The pointer was positioned over the button, and a mouse button was released.
• clicked The user clicked the button (a combination of pressing and releasing the
mouse button while the pointer was positioned over the button).
The third argument to gtk_signal_connect() is a pointer to the function that should be
invoked by the widget when the signal specified by argument two, name, is triggered. The
final argument to gtk_signal_connect() is a pointer to private data that will be passed to the
signal handler by the widget when the signal handler is invoked.
Unfortunately, signal functions do not adhere to a single function prototype. The arguments
passed to a signal handler will vary based on the widget generating the signal. The general form
of a Gtk+ signal handler is as follows:
void
callback_func( GtkWidget *widget, gpointer callback_data );
I will describe the function prototypes for signal handlers in later chapters, along with
the widgets that generate them. However, at this point, I can say a couple of things about
callback function arguments that hold true regardless of the widget class involved:
• The first argument of the signal handler will always be a pointer to the widget that
generated the signal.
• The callback_data argument will always be the last argument passed to the signal handler.
• Any arguments that are specific to the widget or to the signal will occur between the
first and last arguments of the signal handler.
The final argument passed to the callback function, callback_data, contains a pointer to
data that is private to the application and has no meaning whatsoever to the widget. The use
of private callback data is a practice that Gtk+ borrowed from Xt/Motif, and it has powerful
implications for application design.
007 {
008 time_t timeval;
009
010 timeval = time( NULL );
011 strcpy( timestr, ctime( &timeval ) );
012 }
013
014 void
015 PrintAndExit (GtkWidget *widget, char timestr[][26])
016 {
017 int i;
018
019 for ( i = 0; i < 4; i++ )
020 printf( "timestr[ %d ] is %s", i, timestr[ i ] );
021 gtk_main_quit ();
022 }
023
024 int
025 main( int argc, char *argv[] )
026 {
027 GtkWidget *window, *box, *button;
028
029 static char times[ 4 ][ 26 ] =
030 { "Unset\n", "Unset\n", "Unset\n", "Unset\n" };
031
032 gtk_set_locale ();
033
034 gtk_init (&argc, &argv);
035
036 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
037
038 gtk_signal_connect (GTK_OBJECT(window), "destroy",
039 GTK_SIGNAL_FUNC(PrintAndExit), times);
040
041 gtk_window_set_title (GTK_WINDOW (window), "Signals 1");
042 gtk_container_border_width (GTK_CONTAINER (window), 0);
043
044 box = gtk_vbox_new (FALSE, 0);
045 gtk_container_add (GTK_CONTAINER (window), box);
046
047 button = gtk_button_new_with_label ("Update 0");
048 gtk_signal_connect (GTK_OBJECT (button), "clicked",
049 GTK_SIGNAL_FUNC(Update), ×[0]);
050 gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
051
052 button = gtk_button_new_with_label ("Update 1");
053 gtk_signal_connect (GTK_OBJECT (button), "clicked",
054 GTK_SIGNAL_FUNC(Update), ×[1]);
055 gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);
056
057 button = gtk_button_new_with_label ("Update 2");
058 gtk_signal_connect (GTK_OBJECT (button), "clicked",
4276 | CH03.fm Page 51 Sunday, July 15, 2001 5:25 PM
Events 51
The purpose of this example is to illustrate how private data can be passed to a callback rou-
tine. On lines 029 and 030, we declare an array of four 26-character strings, 26 characters being
what is needed to hold the value returned by the ctime(3) function. These strings are initialized
to the value “Unset\n” so that the callback routine that will be invoked when we exit, PrintAn-
dExit(), has something sensible to print should the user not change one or more of the string’s
values. On lines 048, 053, 058, and 083, we register the signal function Update() with the Gtk-
Button that was created a line or two earlier, using gtk_signal_connect(). Each of these calls to
gtk_signal_connect() is passed a different func_data argument; the first call is passed the
address of the first cell in the array of times, the second call is passed the address of the second
cell of times, and so forth.
Whenever the user clicks one of the buttons labeled “Update 0”, “Update 1”, “Update 2”,
or “Update 3”, Update() will be invoked. The timestr argument will be set by Gtk+ to the pri-
vate data assigned when the callback or signal function was registered.
This may be a silly example, but it illustrates a very important technique. Note that we
have no logic inside of Update() that concerns itself with the button pressed by the user; we
simply don’t need to know this. All we need to know is that the callback function is being
passed a pointer to a string presumed to be big enough to hold the ctime(3) result that is
going to be stuffed into it.
It is easy to extend this example to a real-life application such as a word processor or to
any application that allows a user to manipulate more than one document at a time, such as
a spreadsheet or a photo manipulation program like xv or GIMP. Whenever a callback is
designed to manipulate data of some kind, try to make that data available to the callback
function via the func_data argument. This will enable reuse of callbacks and minimize the
need for maintaining global data.
Events
Events are similar to signals in that they are a method by which Gtk+ can tell an application
that something has happened. Events and signals differ mainly in what it is they provide
notification of. Signals make applications aware of somewhat abstract, high-level changes,
such as GUI (not mouse) button presses, toggle button state changes, or the selection of a
4276 | CH03.fm Page 52 Sunday, July 15, 2001 5:25 PM
row in a list widget. Events mainly provide a way for Gtk+ to pass along to the client any
X11 events that have been received over the X server connection in which the client has
expressed an interest.
Events and signals share the same Gtk+ APIs. To register a callback function for an
event, use gtk_signal_connect(). The APIs involved will be discussed later in this chapter.
gint
callback_func( GtkWidget *widget, GdkEvent *event,
gpointer callback_data );
widget is the Gtk+ widget to which the event pertains, event is a GDK data structure that
contains information about the event, and callback_data is the application-specific data that
was registered with the handler by the client at the time that gtk_signal_connect() was called.
Most event callbacks adhere to the preceding prototype, but there are variations. In the
following section where individual events are described, I will provide the callback func-
tion prototype that is most appropriate for each event.
Table 3.1 defines each of the events supported by Gtk+ 1.2. Note that the names all start
with GDK_ because the events all originate from within GDK code.
Table 3.1 GDK Events
Events 53
Note that there are X11 events that are not passed on to your Gtk+ application. For example,
MappingNotify events are responded to by GDK by calling XRefreshKeyboardMapping(),
which is the standard way for Xlib clients to handle the reception of this event. Unless you take
extraordinary means to look for it, your application will never see a MappingNotify event.
In X11, clients must tell the server which events the client is interested in receiving by
soliciting the events. If an event is not solicited by a client, it will not be sent. There are a
few exceptions, however: MappingNotify, ClientMessage, and the Selection* events are all
nonmaskable and will always be sent to the client.
In Gtk+/GDK, clients must also solicit the events in which they have interest. This is done
on a per-widget basis, using a technique that is very similar to calling XSelectInput() from an
Xlib program. In Gtk+, the routine to call is gtk_widget_set_events(). Here is its prototype:
void
gtk_widget_set_events (GtkWidget *widget, gint events)
The argument events is a bitmask used to indicate the types of events the client would
like to receive notification of from Gtk+, and widget is the handle of the Gtk+ widget to
which the event notification pertains. The X server will only send events specified in the
events mask that belong to the window defined by the widget. This implies that widgets that
4276 | CH03.fm Page 55 Sunday, July 15, 2001 5:25 PM
Events 55
do not create a window cannot receive events (we’ll return to this issue later in this book).
Events that are not solicited for a window are not transmitted to the client by the X server.
Unless you plan to handle a specific event in your application, there is really no need for
you to call this routine. This does not mean that events will not be solicited for the widget;
it is very likely that one or more events will be solicited by the widget implementation.
The events bitmask can be constructed by OR’ing together one or more of the constants
defined by GDK (see Table 3.2).
Table 3.2 GDK Event Masks
The masks in the preceding table are not one-to-one with the events listed in Table 3.1.
Some of the masks will lead to the reception of more than one event, and your callback
function may have to check to see which event was received, depending on the application.
Table 3.3 should clarify the mapping that exists between masks and events.
Table 3.3 Event Mask-to-Event Mappings
GDK_EXPOSURE_MASK GDK_EXPOSE
GDK_POINTER_MOTION_MASK GDK_MOTION_NOTIFY
GDK_POINTER_MOTION_HINT_MASK GDK_MOTION_NOTIFY
GDK_BUTTON_MOTION_MASK GDK_MOTION_NOTIFY
GDK_BUTTON1_MOTION_MASK GDK_MOTION_NOTIFY
GDK_BUTTON2_MOTION_MASK GDK_MOTION_NOTIFY
GDK_BUTTON3_MOTION_MASK GDK_MOTION_NOTIFY
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_BUTTON_RELEASE_MASK GDK_BUTTON_RELEASE
GDK_LEAVE_NOTIFY_MASK GDK_LEAVE_NOTIFY
GDK_FOCUS_CHANGE_MASK GDK_FOCUS_CHANGE
GDK_STRUCTURE_MASK GDK_DESTROY
GDK_CONFIGURE
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
4276 | CH03.fm Page 57 Sunday, July 15, 2001 5:25 PM
Events 57
GDK_SUBSTRUCTURE_MASK GDK_DESTROY
GDK_CONFIGURE
GDK_MAP
GDK_UNMAP
What happens if you specify a mask that does not contain bits set by the widget? For
example, the GtkButton widget selects GDK_BUTTON_PRESS_MASK for its window
when the buttons’ window is created. Let’s say your client calls gtk_set_widget_events(),
and the mask you supply does not have the GDK_BUTTON_PRESS_MASK bit set, as in
the following code:
In this case, button press events will be sent to the client and processed by the GtkButton
widget, in addition to MotionNotify events that will be handled by the client in MotionNo-
tifyCallback().
What about selecting an event that has already been selected by a widget? For example:
button = gtk_button_new_with_label ("Print");
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC(PrintString), GTK_OBJECT (window));
This too will not affect the widget. When a button press occurs, Gtk+ will first call But-
tonPressCallback() and then call PrintString(). Note that we really did not need to call
gtk_widget_set_events() to select GDK_BUTTON_PRESS_MASK for the GtkButton
widget because that event was already selected by the widget itself, but it didn’t hurt.
Event Types
Earlier we introduced the function prototype for the callback function invoked by Gtk+
upon reception of a signal that the client has solicited and for which a signal function has
been registered. The prototype, once again, is as follows:
4276 | CH03.fm Page 58 Sunday, July 15, 2001 5:25 PM
gint
callback_func( GtkWidget *widget, GdkEvent *event,
gpointer callback_data );
GdkEvent is actually a C union of structures, one structure for each signal type listed in
Table 3.1:
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;
};
The following describes each of the structures encapsulated within the GdkEvent union (with
the only exceptions being GdkEventProximity, which is not covered, and GdkEventDND,
which is an internal event type used in the implementation of Drag and Drop, also not discussed
in this book). Each of the preceding names is a typedef for a struct that has the same name but
is prefixed with ‘_’. For example:
typedef struct _GdkEventExpose GdkEventExpose;
In each of the following structures, as well as in the preceding GdkEvent, GdkEventType is
an enum that defines the events in Table 3.1. Thus, in a callback function that is supposed to
process LeaveNotify events, the event type can be verified using code similar to the following:
void
LeaveFunc( GtkWidget *widget, GdkEvent *event, gpointer callback_data )
{
if (event==(GdkEvent *)NULL || event->type!=GDK_LEAVE_NOTIFY) {
ErrorFunction( "LeaveFunc: NULL event or wrong type\n" );
return; /* bogus event */
}
/* event is good */
...
}
4276 | CH03.fm Page 59 Sunday, July 15, 2001 5:25 PM
Events 59
In the preceding routine, we leave the signal function if the event pointer is NULL or if
the type of the event is not GDK_LEAVE_NOTIFY.
GdkEventExpose
struct _GdkEventExpose
{
GdkEventType type; /* GDK_EXPOSE */
GdkWindow *window;
gint8 send_event;
GdkRectangle area;
gint count; /* If non-zero, how many more events follow */
};
gint
func(GtkWidget *widget, GdkEventExpose *event, gpointer arg);
Description
Expose events are identified by a type field set to GDK_EXPOSE. Window identifies the
window that needs repainting, and area defines the region that this expose event describes.
If more than one region in a window becomes exposed, multiple expose events will be sent
by the X server. The number of events pending for the window is identified by count. If your
code ignores the area field and redraws the entire window in the expose signal function,
then your code should wait until it receives an expose event with a count field equal to zero.
GdkEventNoExpose
struct _GdkEventNoExpose
{
GdkEventType type; /* GDK_NO_EXPOSE */
GdkWindow *window;
gint8 send_event;
};
gint
func(GtkWidget *widget, GdkEventAny *event, gpointer arg);
Description
NoExpose events are received if CopyArea or CopyPlane X protocol is performed success-
fully. This will happen only if all values in the source image were able to be copied by the
4276 | CH03.fm Page 60 Sunday, July 15, 2001 5:25 PM
X server, with no portions of the source window obscured, and if the graphics_exposures
flag in the X GC used in the CopyArea or CopyPlane request was set to True.
XCopyArea is invoked by both gdk_draw_pixmap() and gdk_window_copy_area().
GdkEventVisibility
struct _GdkEventVisibility
{
GdkEventType type; /* GDK_VISIBILITY_NOTIFY */
GdkWindow *window;
gint8 send_event;
GdkVisibilityState state;
};
gint
func(GtkWidget *widget, GdkEventVisibility *event, gpointer arg);
Description
Visibility events are sent when the visibility of a window has changed. The state field of the
event describes the nature of the change and can be one of the following values in Table 3.4.
Table 3.4 Visibility Event States
Value Meaning
GdkEventMotion
struct _GdkEventMotion
{
GdkEventType type; /* GDK_MOTION_NOTIFY */
GdkWindow *window;
gint8 send_event;
4276 | CH03.fm Page 61 Sunday, July 15, 2001 5:25 PM
Events 61
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;
};
gint
func(GtkWidget *widget, GdkEventMotion *event, gpointer arg);
Description
Motion notify events indicate that the pointer has moved from one location of the screen to
another. The time field indicates the time of the event in server-relative time (milliseconds
since the last server reset). If the window is on the same screen as the root (which is usually
the case), then x and y are the pointer coordinates relative to the origin of the window; oth-
erwise, they are both set to 0. x_root and y_root are the coordinates relative to the root win-
dow. Pressure is always set to the value 0.5, and xtilt and ytilt are always set to the value 0.
Source is always set to GDK_SOURCE_MOUSE, and deviceid is always set to the value
GDK_CORE_POINTER. Is_hint is set to 1 if the mask used to select the event was
GDK_POINTER_MOTION_HINT_MASK; otherwise, it will be 0. If is_hint is 1e, then
the current position information needs to be obtained by calling gdk_window_
get_pointer(). State is used to specify the state of the mouse buttons and modifier keys just
before the event. The values possible for state are constructed by OR’ing any of the follow-
ing bits in Table 3.5.
Table 3.5 Motion Event States
Value Meaning
Value Meaning
shift, lock, control, mod1 through mod5, and Button1 through Button5 are logical names
in X11 and are subject to remapping by the user. The X11 user command for performing
this remapping is xmodmap(1). The xmodmap(1) command can also be used to view the
current logical name to keysym mapping, for example:
GdkEventButton
struct _GdkEventButton
{
GdkEventType type; /* GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS,
GDK_3BUTTON_PRESS, GDK_BUTTON_RELEASE */
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
gdouble pressure;
gdouble xtilt;
gdouble ytilt;
guint state;
guint button;
4276 | CH03.fm Page 63 Sunday, July 15, 2001 5:25 PM
Events 63
GdkInputSource source;
guint32 deviceid;
gdouble x_root, y_root;
};
gint
func(GtkWidget *widget, GdkEventButton *event, gpointer arg);
Description
Button events indicate that a mouse button press or release has occurred. The time field
indicates the time of the event in server-relative time (milliseconds since server reset). If the
window receiving the button press or release is on the same screen as the root (which is usu-
ally the case), then x and y are the pointer coordinates relative to the origin of window; oth-
erwise, they are both set to zero. X_root and y_root are the coordinates of the press or
release relative to the root window. Pressure is always set to the value 0.5, and xtilt and ytilt
are always set to the value zero. Source is always GDK_SOURCE_MOUSE, and deviceid
is always set to the value GDK_CORE_POINTER. State is used to specify the state of the
mouse buttons and modifier keys just before the event. The values possible for state are the
same values as those previously described for GdkEventMotion. Button indicates which
button the event is for, with 1 indicating button 1, 2 indicating button 2, and so on.
GdkEventKey
struct _GdkEventKey
{
GdkEventType type; /* GDK_KEY_PRESS GDK_KEY_RELEASE */
GdkWindow *window;
gint8 send_event;
guint32 time;
guint state;
guint keyval;
gint length;
gchar *string;
};
gint
func(GtkWidget *widget, GdkEventKey *event, gpointer arg);
4276 | CH03.fm Page 64 Sunday, July 15, 2001 5:25 PM
Description
Key events indicate that a keyboard key press or key release has occurred. The time field indi-
cates the time of the event in server-relative time (milliseconds since server reset). State is used
to specify the state of the mouse buttons and modifier keys just before the event. The values
possible for state are the same as those previously described for GdkEventMotion. Keyval indi-
cates which key was pressed or released. Keyval is the keysym value that corresponds to the
key pressed or released. Keysyms values are symbolic values that represent the keys on the
keyboard. Keyboards generate hardware-dependent values that are mapped by Xlib to keysyms
using a table provided by the X server. For example, the hardware code generated when the
user presses the key labeled “A” is converted to the keysym value XK_A. It is this value (e.g.,
XK_A) that is stored inside the keyval field. String contains a string of ASCII characters that
were obtained by GDK by calling the Xlib function XLookupString(). Usually, the string will
be of length 1 and will correspond directly to the glyph or symbol displayed on the key that
was pressed or released (e.g., for XK_A, the string will be “A”). However, clients can associate
an arbitrarily long string with a key using XRebindKeysym(). The length of this string, which
is limited to 16 characters by GDK, is stored in length, and string contains the value of the
string (truncated if necessary to 16 characters) returned by XLookupString().
GdkEventCrossing
struct _GdkEventCrossing
{
GdkEventType type; /* GDK_ENTER_NOTIFY GDK_LEAVE_NOTIFY */
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;
};
gint
func(GtkWidget *widget, GdkEventCrossing *event, gpointer arg);
4276 | CH03.fm Page 65 Sunday, July 15, 2001 5:25 PM
Events 65
Description
Crossing events indicate that the mouse pointer has entered or left a window. The window into
which the pointer has entered, or from which it has left, is indicated by window. If the event
type is GDK_LEAVE_NOTIFY and the pointer began in a child window of window, then
subwindow will be set to the GDK ID of the child window, or else it will be set to the value
NULL. If the event type is GDK_ENTER_NOTIFY and the pointer ends up in a child win-
dow of window, then subwindow will be set to the GDK ID of the child window, or else it will
be set to the value NULL. The time field indicates the time of the event in server-relative time
(milliseconds since server reset). If window is on the same screen as the root (which is usually
the case), then x and y specify the pointer coordinates relative to the origin of window; other-
wise, they are both set to zero. X_root and y_root are the coordinates of the pointer relative to
the root window. If the enter or leave event was caused by normal mouse movement or if it
was caused by a pointer warp (that is, the client has explicitly moved the mouse), then mode
will be set to GDK_CROSSING_NORMAL. Or, if the crossing event was caused by a pointer
grab, mode will be set to GDK_CROSSING_GRAB. Finally, if the crossing event was caused
by a pointer ungrab, then mode will be set to the value GDK_CROSSING_UNGRAB. If the
receiving window is the focus window or is a descendant of the focus window (subwindow is
not NULL), then focus will be set to TRUE; otherwise, it will be set to FALSE. State specifies
the state of the mouse buttons and modifier keys just before the event. The values possible for
state are the same as those previously described for GdkEventMotion.
The final field, detail, is a bit complicated to describe. Here we’ll simply list the GDK
values that can be stored in this field and the X11 values to which they map. In practice,
X11 client applications (and, by extension, Gtk+ applications) rarely, if ever, will make use
of the data in this field (see Table 3.6).
Table 3.6 Event Crossing Event Detail Field
NotifyInferior GDK_NOTIFY_INFERIOR
NotifyAncestor GDK_NOTIFY_ANCESTOR
NotifyVirtual GDK_NOTIFY_VIRTUAL
NotifyNonlinear GDK_NOTIFY_NONLINEAR
NotifyNonlinearVirtual GDK_NOTIFY_NONLINEAR_VIRTUAL
GdkEventFocus
struct _GdkEventFocus
{
GdkEventType type; /* GDK_FOCUS_CHANGE */
GdkWindow *window;
gint8 send_event;
gint16 in;
};
4276 | CH03.fm Page 66 Sunday, July 15, 2001 5:25 PM
gint
func(GtkWidget *widget, GdkEventFocus *event, gpointer arg);
Description
Focus events indicate a change in keyboard focus from one window to another. When keyboard
focus changes, two events are sent. One event is sent for the window that had the keyboard
focus just prior to the focus change. The other is sent for the window that just obtained the key-
board focus. The in field is used to define the type of focus change. If the X11 event type is
FocusIn, then the window identified by window received focus, and in will be set to TRUE.
Otherwise, the X11 event type was FocusOut, the window identified by window lost input
focus, and in will be set to FALSE.
GdkEventConfigure
struct _GdkEventConfigure
{
GdkEventType type; /* GDK_CONFIGURE */
GdkWindow *window;
gint8 send_event;
gint16 x, y;
gint16 width;
gint16 height;
};
Description
Configure events indicate a change in the size and/or location of a window. The window
field identifies the window that was moved or resized. The x and y fields identify the new
x and y locations of the window in the root window coordinate space. Width and height
identify the width and the height of the window.
GdkEventProperty
struct _GdkEventProperty
{
GdkEventType type; /* GDK_PROPERTY_NOTIFY */
4276 | CH03.fm Page 67 Sunday, July 15, 2001 5:25 PM
Events 67
GdkWindow *window;
gint8 send_event;
GdkAtom atom;
guint32 time;
guint state;
};
gint
func(GtkWidget *widget, GdkEventProperty *event, gpointer arg);
Description
Property events indicate a change of a property. Properties are named data associated with a
window. This data is stored on the X server to which your Gtk+ client is connected. Properties
have a unique ID that either is predefined or is assigned by the X server at the time the prop-
erty is installed. This ID is identified by the atom field in the preceding event struct. Several
standard properties are used to help the window manager do its job. For example, when you
call gtk_set_window_title(), GDK will set the XA_WM_NAME atom of the window to the
character string that was passed to gtk_set_window_title(). The window manager will be noti-
fied of this property change by the X server via a PropertyNotify event. Upon receiving the
event, the window manager will redraw the title displayed in the window title bar decoration
that the window manager has placed around your application’s top-level window.
While the major use of properties is in satisfying window manager protocols (such as
specifying window titles and icon pixmaps) or client notification of window deletion, prop-
erties can also be used as a form of interprocess communication among cooperating clients.
GDK provides routines that allow you to create, modify, and destroy properties.
The time field stores the time that the event occurred in server-relative time (milliseconds
since server reset). State identifies the type of change that has occurred. PropertyNewValue indi-
cates that the value of the property identified by atom has changed. PropertyDelete indicates that
the property identified by atom no longer exists on the server.
GdkEventSelection
struct _GdkEventSelection
{
GdkEventType type; /* GDK_SELECTION_CLEAR GDK_SELECTION_REQUEST
GDK_SELECTION_NOTIFY */
GdkWindow *window;
gint8 send_event;
GdkAtom selection;
GdkAtom target;
GdkAtom property;
guint32 requestor;
guint32 time;
};
4276 | CH03.fm Page 68 Sunday, July 15, 2001 5:25 PM
gint
func(GtkWidget *widget, GdkEventSelection *event, gpointer arg);
Selection Protocol. Basically, the protocol between the owner and requestor is as follows.
In this example, we’ll assume we are performing copy and paste between text edit (entry) wid-
gets in two different clients. We’ll refer to the text edit widget in client 1 as entry-1 and the
text edit widget in client 2 as entry-2.
When the user selects text in entry-1 (we ignore here how that is done), client 1 will call
gtk_selection_owner_set() to obtain ownership of the selection atom named GDK_
SELECTION_PRIMARY (XA_PRIMARY). If successful and client 1 did not already own
the selection, a SelectionClear (GDK_SELECTION_CLEAR) event will be sent to the pre-
vious owner (perhaps client 2, but this could be any X11 client connected to the X server,
written using Xlib, Motif, or any other X11 toolkit). The client receiving the SelectionClear
event will respond by unhighlighting the previously selected text. Notice that all we have
done so far is switch the ownership of the primary selection atom from one client to another.
No data has been transferred at this point.
Assume now that the text edit widget in client 2 (entry-2) obtains focus, and the user ini-
tiates a paste operation in some application-specific way. Client 2 now takes on the role of
requestor and calls gtk_selection_convert() to obtain the data. Gtk_selection_convert() will call
gdk_selection_convert(), which in turn will call XConvertSelection(). XConvertSelection() is
passed a window ID, the GDK_SELECTION_PRIMARY atom, and a target atom. The target
atom is used to indicate the data type to which the requestor would like the selected data to be
converted, if necessary or even possible, by the owner prior to transferring the selected data to
the X server. A base set of targets is predefined by X11’s Inter-Client Communication Conven-
tions Manual (ICCCM). Table 3.7 illustrates some predefined target atoms in X11.
4276 | CH03.fm Page 69 Sunday, July 15, 2001 5:25 PM
Events 69
XA_ARC XArc
XA_POINT XPoint
XA_ATOM Atom
XA_RECTANGLE XRectangle
XA_CARDINAL int
XA_STRING char *
XA_COLORMAP Colormap
XA_VISUALID VisualID
XA_CURSOR Cursor
XA_WINDOW Window
XA_DRAWABLE Drawable
XA_WM_HINTS XWMHints
XA_FONT Font
XA_INTEGER int
XA_WM_SIZE_HINTS XSizeHints
After the requestor has successfully called gtk_selection_convert(), the owner receives a
SelectionRequest (GDK_SELECTION_REQUEST) event. Selection identifies the selection
to which the request pertains. Usually, selection will be GDK_SELECTION_PRIMARY
unless the owner is supporting multiple selections. Target is the target atom (for example, one
of the atoms listed in Table 3.7). Property identifies the atom or property where the selected
data should be placed. Requestor identifies the window of the client that is making the request.
Now that the owner has received the GDK_SELECTION_REQUEST event, it attempts to
convert the selection it owns to the requested type. If the owner is unable to perform the conversion
(for example, the data associated with the selection is text and the requestor wants it converted to
a colormap), then the owner creates and sends to the requestor a GDK_SELECTION_NOTIFY
event with the property field set to GDK_NONE. If the conversion was successful, the owner also
sends a GDK_SELECTION_NOTIFY event but with the property field set to the same value
4276 | CH03.fm Page 70 Sunday, July 15, 2001 5:25 PM
received by the owner in the GDK_SELECTION_REQUEST event. The selection and target
fields in any GDK_SELECTION_NOTIFY event should be the same values as those received in
the GDK_SELECTION_REQUEST event.
The final major portion of the selection protocol happens back on the requestor. The requestor
will receive a GDK_SELECTION_NOTIFY event. If the property field is GDK_NONE, then
the requestor knows that the selection failed. Otherwise, the selection was successful, and the
requestor then reads the property specified in the property field for the converted data.
GdkEventClient
struct _GdkEventClient
{
GdkEventType type; /* GDK_CLIENT_EVENT */
GdkWindow *window;
gint8 send_event;
GdkAtom message_type;
gushort data_format;
union {
char b[20];
short s[10];
long l[5];
} data;
};
gint
func(GtkWidget *widget, GdkEventClient *event, gpointer arg);
Client events provide a mechanism by which one client can send an event to some other
client executing on the same X server. An example of this was illustrated when we discussed
selections. The owner of a selection, in response to a GDK_SELECTION_REQUEST event,
will send a GDK_SELECTION_NOTIFY event to the requestor client to indicate the result
of the request.
Any event type can be sent by a client to another client using this mechanism. In practice,
however, use of client events is generally restricted to selections, where it is needed to satisfy
the selection protocol, or to window managers which use them to notify clients of some pend-
ing event, such as the destruction of a window.
Client events are never selected by the receiving client; they will always be sent to the
receiving client regardless of the event mask associated with the receiving clients’ window.
Message_type is an atom that is used to identify the type of the message sent. It is up to
the clients that send and receive messages of this type to agree on the value of this field.
Data_format specifies the format of the message sent in the event and must have one of these
values: 8, 16, or 32. This is necessary so that the X server can do the necessary swapping of
4276 | CH03.fm Page 71 Sunday, July 15, 2001 5:25 PM
Events 71
bytes. Data contains the actual data sent in the message, either 20 8-bit chars, 10 16-bit shorts,
or 5 32-bit longs.
GdkEventAny
struct _GdkEventAny
{
GdkEventType type; /* any event type is possible here */
GdkWindow *window;
gint8 send_event;
};
default is for a given widget; the choices include calling the class function before, after, or
both before and after your callbacks for the widget have been called.
Let’s look at the functions that are available to application programmers for use in creating,
controlling, and destroying signals. In doing so, we will discuss a few interesting tidbits about
signals not covered so far.
Signal Lookup
The first function is gtk_signal_lookup(). The prototype for this function is as follows:
gint
gtk_signal_lookup (gchar *name, gint object_type)
What gtk_signal_lookup() does is search the widget hierarchy for the signal identified
by name, starting with the object type specified by object_type and searching recursively
higher to include the object type’s parents if needed. If the search is successful, then the
signal identifier, a unique number that identifies the signal, will be returned. If the search is
not successful, then gtk_signal_lookup() returns 0.
To use this function, you need to know what object types and signal names are. Let’s start with
object types. Each widget class in Gtk+ has an object type, defined by the widget programmer.
The naming convention for object types seems to be GTK_OBJECT_*, where * is replaced with
the name of the widget class. For example, the object type that corresponds to the GtkButton wid-
get class is GTK_OBJECT_BUTTON. The object type is defined in the header file for the widget
class, usually gtk.h, where type is the name of the widget class. Again, using GtkButton as our
example, the header file in which the GTK_OBJECT_BUTTON macro is defined is named gtk-
button.h. The object macro is defined to be a call to a function also defined by the widget writer.
There is a convention for the naming of this function, too; in this case it is gtk_*_get_type(),
which for the GtkButton class would be gtk_button_get_type().
Now let’s turn to signal names. In the example code presented earlier in this chapter, we
connected a callback routine to the “destroy” signal of the window object that represented
our application’s main window with the following code:
Here, destroy is an example of a signal name. Another signal that we connected to our
application was the “clicked” signal, defined by the GtkButton widget class. Each widget
class defines some number of signals that are specific to the widget class. Signals that are
common to more than one class will be defined in a parent class, from which the widget
classes that share that signal can inherit.
When I introduce a widget class in the chapters that follow, I will specify the name of
the object type macro that corresponds to the widget class as well as the name and behavior
of each signal supported by the widget class.
Let’s now take a quick look at how gtk_signal_lookup() might be called. To make the
example familiar, we’ll simply modify the earlier example to use gtk_signal_lookup() to val-
4276 | CH03.fm Page 73 Sunday, July 15, 2001 5:25 PM
Signal Lookup 73
idate the signal name we pass to gtk_signal_connect(). Note that this is sort of a contrived
example, but it does illustrate how to call gtk_signal_lookup().
object = GTK_OBJECT(window);
if ( gtk_signal_lookup( "destroy", GTK_OBJECT_TYPE(object) ) )
gint
HasSignal( GtkWidget *widget, char *name )
{
GtkObject *object;
int retval = 0;
object = GTK_OBJECT(widget);
if ( object != (GtkObject *) NULL )
retval = gtk_signal_lookup( name,
GTK_OBJECT_TYPE( object ) );
return( retval );
}
Gtk+ also defines a function that takes a signal number and returns that signal’s character
string name. Here is its prototype:
gchar*
gtk_signal_name (gint signal_num)
Gtk+ maintains a global table of signal names. A signal number in Gtk+ is merely an
index into this table, so what this function really does is return the string that is stored in
the table indexed by signal_num (or 0 if signal_num is not a valid index into the table).
4276 | CH03.fm Page 74 Sunday, July 15, 2001 5:25 PM
Emitting Signals
Although in practice this may not be a very common thing to do, Gtk+ does give a client
the ability to cause events and signals to trigger. This can be done by calling one of the
gtk_signal_emit* functions:
void
gtk_signal_emit (GtkObject *object, gint signal_type, ...)
void
gtk_signal_emit_by_name (GtkObject *object, gchar *name, ...)
The first argument to either function is the object from which the signal or the event will
be generated. The second argument to gtk_signal_emit() is the type of the signal. This can be
found by calling gtk_signal_lookup(), as previously described (or the function HasSignal(),
as previously developed). The second argument to gtk_signal_emit_by_name() is the event
name; gtk_signal_emit_by_name() will do the lookup operation itself. If you already have the
signal type value, it is more efficient to call gtk_signal_emit() to avoid the overhead incurred
by gtk_signal_emit_by_name() to look up the event name string and convert to the signal type
value accepted by gtk_signal_emit().
The remaining arguments to the gtk_signal_emit* functions will vary in type and num-
ber based on the signal being emitted. For example, the prototype for the map_event
(GDK_MAP) callback function is as follows:
gint
func(GtkWidget *widget, GdkEventAny *event, gpointer arg);
GdkEventAny event;
gint retval;
...
Signal Lookup 75
void
func(GtkWidget *button, gpointer data);
GtkWidget *clist;
int row, column;
...
014
015 gtk_signal_emit( GTK_OBJECT(drawing),
016 gtk_signal_lookup( "motion_notify_event",
017 GTK_OBJECT_TYPE(drawing) ), &event, &retval );
018
019 alarm(1L);
020 }
021
022 static void
023 motion_notify_callback( GtkWidget *w, GdkEventMotion *event, char *arg )
024 {
025 static int count = 1;
026
027 fprintf( stderr, "In motion_notify_callback %s %03d\n", arg, count++ );
028 fflush( stderr );
029 }
030
031 void
032 Exit (GtkWidget *widget, gpointer arg)
033 {
034 gtk_main_quit ();
035 }
036
037 int
038 main( int argc, char *argv[] )
039 {
040 GtkWidget *window, *box;
041 struct sigaction old, act;
042
043 gtk_set_locale ();
044
045 gtk_init (&argc, &argv);
046
047 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
048
049 gtk_signal_connect (GTK_OBJECT(window), "destroy",
050 GTK_SIGNAL_FUNC(Exit), NULL);
051
052 gtk_window_set_title (GTK_WINDOW (window), "Events 3");
053 gtk_container_border_width (GTK_CONTAINER (window), 0);
054
055 box = gtk_vbox_new (FALSE, 0);
056 gtk_container_add (GTK_CONTAINER (window), box);
057
058 drawing = gtk_drawing_area_new ();
059 gtk_widget_set_events (drawing,
060 GDK_POINTER_MOTION_MASK);
061 gtk_signal_connect( GTK_OBJECT(drawing), "motion_notify_event",
062 GTK_SIGNAL_FUNC(motion_notify_callback), "Hello World" );
063 gtk_box_pack_start (GTK_BOX (box), drawing, TRUE, TRUE, 0);
064
065 gtk_widget_show_all (window);
4276 | CH03.fm Page 77 Sunday, July 15, 2001 5:25 PM
Signal Lookup 77
066
067 act.sa_handler = AlarmFunc;
068 act.sa_flags = 0;
069 sigaction( SIGALRM, &act, &old );
070 alarm( 1L );
071
072 gtk_main ();
073
074 sigaction( SIGALRM, &old, NULL );
075 return( 0 );
076 }
On lines 067 through 070, we use POSIX signal function sigaction(2) to register a
SIGALRM signal handler named AlarmFunc(), which is implemented on lines 009 through
020. Then, on line 070, we call alarm(2) to cause the SIGALRM signal to fire one second
later. When SIGALRM is triggered, AlarmFunc() is entered.
009 void
010 AlarmFunc( int foo )
011 {
012 GdkEvent event;
013 gint retval;
014
015 gtk_signal_emit( GTK_OBJECT(drawing),
016 gtk_signal_lookup( "motion_notify_event",
017 GTK_OBJECT_TYPE(drawing) ), &event, &retval );
018
019 alarm(1L);
020 }
Controlling Signals
Gtk+ provides a few functions that allow applications to control signals in a variety of ways.
The first of these functions is gtk_signal_emit_stop():
void
gtk_signal_emit_stop (GtkObject *object, gint signal_type)
void
gtk_signal_emit_stop_by_name (GtkObject *object, char *name)
An example should make this clear. I modified the preceding example slightly so that
five different signal callback functions are registered with the GtkDrawingAreaWidget:
Signal Lookup 79
...
The effect of this change is that, should one of the signal callback functions gen-
erate a random number below 50, the remaining signal callback functions will not be
invoked for the signal emission because signal emission will be stopped. In testing
this function, motion_notify_callback2() was called approximately half as often as
motion_notify_callback1(), motion_notify_callback3() was called approximately
half as often as motion_notify_callback2(), and so on. At least this demonstrates that
my random number macro was performing approximately as it should have been.
Note that we do not need to reconnect the signal callback functions after an emission is
stopped. The next time the signal is generated, all functions are once again eligible for invo-
cation. Also, calling gtk_signal_emit_stop*() for a signal that is not being emitted is a no-op.
I mentioned that connecting a signal with gtk_signal_connect() will cause the registered
signal function to be invoked after all signal functions previously registered with the widget
for that signal and prior to the default class signal function implemented for the widget.
However, applications can arrange to have signal callback functions invoked after the class
signal callback function by registering the callback with gtk_signal_connect_after():
gint
gtk_signal_connect_after (GtkObject *object, gchar *name,
GtkSignalFunc func, gpointer func_data)
gint
gtk_signal_connect_object (GtkObject *object, gchar *name,
GtkSignalFunc func, GtkObject *slot_object)
The major difference between gtk_signal_connect_object() and the other signal connection
functions—gtk_signal_connect() and gtk_signal_connect_after()—is reflected in the function
prototypes of the gtk_signal_connect* functions and in the function prototypes of the signal
callback functions that are invoked.
4276 | CH03.fm Page 80 Sunday, July 15, 2001 5:25 PM
...
button = gtk_button_new_with_label("Close");
gtk_box_pack_start(GTK_BOX(box2), button, TRUE, TRUE, 0);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window));
Here, the clicked signal supported by the GtkButton class is registered with the GtkBut-
ton instance defined by button. When the “clicked” signal is triggered, the function
gtk_widget_destroy() will be invoked. The function prototype for gtk_widget_destroy() is
as follows:
void
gtk_widget_destroy (GtkWidget *widget);
Note that gtk_widget_destroy() takes only one argument, which in this case is a widget
to destroy. The widget argument passed to gtk_widget_destroy() is the same object that was
passed as the last argument to gtk_signal_connect_object().
It is likely that the only time you will ever use this technique is when handling the destruction
of simple dialogs such as those used to display an error or warning message to the user. There
is little need for an application signal callback function to deal with the cancellation or dismissal
of such a dialog, and so the preceding technique works well. However, if you have a dialog that
allows users to make changes to data, you’ll want to register an application-specific signal call-
back function with the “clicked” signal of “Cancel” or “Dismiss” button so that your application
will have the opportunity to verify the cancellation operation.
4276 | CH03.fm Page 81 Sunday, July 15, 2001 5:25 PM
Signal Lookup 81
Gtk+ supplies two functions that can be used by an application to disconnect a previ-
ously registered signal callback function from a signal or event. The first of these is
gtk_signal_disconnect():
void
gtk_signal_disconnect (GtkObject *object, gint id);
The argument object is the object with which the signal was registered, corresponding
to the first argument that was passed to the gtk_signal_connect* family of functions. As I
pointed out earlier, more than one signal callback function can be registered with a given
signal, so the id argument to gtk_signal_disconnect() is needed to identify which of the reg-
istered signal callback functions is to be disconnected. The argument id is the value
returned by the gtk_signal_connect* function used to connect the signal callback function
to the signal. In the following example, a signal callback function is connected to a clicked
signal, and then is immediately disconnected, to illustrate the techniques involved:
GtkWidget *button;
gint id;
...
button = gtk_button_new_with_label("Close");
id = gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window));
gtk_signal_disconnect (GTK_OBJECT (button), id);
void
gtk_signal_disconnect_by_data (GtkObject *object, gpointer data);
Note that multiple signal callback functions can be disconnected with a call to
gtk_signal_disconnect_by_data(), as every signal callback function registered with the
object or application data to which data pertains will be disconnected by this function.
Gtk+ also allows an application to temporarily block the invocation of a signal callback
function. This can be done by calling gtk_signal_handler_block():
void
gtk_signal_handler_block (GtkObject *object, gint id);
void
gtk_signal_handler_block_by_data (GtkObject *object, gint data);
The argument blocking a signal handler function is not the same as stopping signal emis-
sion by calling gtk_signal_emit_stop(). When signal emission is stopped, it is only for the
emissions corresponding to the triggering of a single event. The next time the event or signal
is triggered, each and every signal callback function is once again eligible for invocation.
When a signal callback function is blocked, it will not be invoked until it has been unblocked,
no matter how many times the signal or event is triggered.
Each signal callback function registered with an object maintains a “blocked” count
that starts at 0 and is incremented each time the signal is blocked by a call to a gtk_signal_
handler_block* function.
Signal callback functions that are blocked can be unblocked at any time by a call to gtk_
signal_handler_unblock():
void
gtk_signal_handler_unblock (GtkObject *object, gint id);
This decrements an object’s blocked count by one. When the blocked count goes to zero,
the signal callback function for the specified object, identified by id, will become eligible
for invocation the next time the signal or event is triggered. The function
void
gtk_signal_handler_unblock_by_data (GtkObject *object, gint data);
gtk_signal_handlers_destroy() destroys all signal callback functions that have been reg-
istered with the specified object. This will not, however, destroy the class signal and event
functions that are implemented by the object or widget.
Objects
The very first argument passed to gtk_signal_connect() is a pointer to variables of
type GtkObject. Example code in the preceding section made use of a macro named
GTK_OBJECT to coerce variables that were declared as GtkWidget * to GtkObject *
4276 | CH03.fm Page 83 Sunday, July 15, 2001 5:25 PM
Objects 83
so that the code would conform to the function prototype of the routine being called.
Objects have played a part in nearly every function that has been discussed so far in
this chapter. However, until now, I’ve not really defined yet what an object is. Obtaining
a basic understanding of objects is the main idea behind this section.
Many of you will no doubt have some previous experience with C++, Smalltalk, or some
other object-oriented language or programming paradigm. Perhaps you have heard about object-
oriented programming but have no actual experience in its use. Or perhaps you have no idea at
all what I am talking about when I use the terms “object” and “object-oriented programming.”
It is not within the scope of this book to present an in-depth look at object-oriented systems
or design. Gtk+ is a C-based toolkit, and C is not considered to be an object-oriented lan-
guage, although object-oriented designs can in fact be implemented in C.
For us, a widget is the practical manifestation of what it is that we talk about when we
refer to objects in Gtk+. Widgets, as we will come to see in the chapters that follow, are
characterized by both visual representation and functionality. Visual representation defines
how the widget appears in the user interface of the application. A widget’s functionality
defines how that widget will respond to input events directed towards it by Gtk+.
• The user clicks the button (a combination of pressing and releasing a mouse button
while the pointer is positioned over the button).
The behavior of a widget often corresponds to visual change, as is the case with the Gtk-
Button widget, which will change its appearance after one of the preceding events has
occurred. For example, as the pointer enters the rectangular region occupied by the button,
the widget will redraw the button in a different color (a lighter shade of gray) to provide
visual feedback to the user that the pointer is in a region owned by the button (see Figure
3.2). Should the user press mouse button 1 while the pointer is positioned over a GtkButton
widget, the widget will redraw itself as shown in Figure 3.3.
Let’s now take a look at another Gtk+ widget, the GtkToggleButton, and see how it compares
to the GtkButton widget.
Toggle buttons are used by an application to represent a value that can have one of two
states. Examples include On or Off, Up or Down, and Left or Right.
In the nontoggled state, a GtkToggleButton widget has an appearance much like that of
a GtkButton widget (see Figure 3.4). GtkToggleButton widgets are rectangular in shape and
have a label. Like GtkButton, a GtkToggleButton widget’s label can be either a text string
or a pixmap. Visually, a user would be hard-pressed to tell a button from a toggle button in
a user interface at first glance.
4276 | CH03.fm Page 85 Sunday, July 15, 2001 5:25 PM
Objects 85
Functionally, GtkToggleButton and GtkButton are closely related. Both respond basically
the same way to the pointer entering or leaving the region occupied by a widget instance. The
GtkToggleButton widget supports the same signals as GtkButton, plus a new signal, “toggled.”
The GtkToggleButton widget will emit this signal after the user positions the pointer over a
toggle button and presses mouse button 1, the same condition that leads GtkButton to emit a
“clicked” signal. In fact, a GtkToggleButton widget can also emit a “clicked” signal if the
application so desires.
As the pointer enters the area occupied by a GtkToggleButton widget, the widget will
redraw itself in a lighter shade of gray, just as a GtkButton widget does. However, GtkToggle-
Button’s visual response to presses and releases is different. In the toggled state, a toggle button
will appear as in Figure 3.5, which corresponds to the pressed state of GtkButton. In the untog-
gled state, a toggle button will appear as in Figure 3.4, corresponding to the unpressed state of
GtkButton. The transition between the toggled and untoggled state occurs at the time of the
button release (assuming the pointer is still within the area of the button at the time of release;
otherwise, the toggle button widget will revert to its prior state).
We’ve now established that GtkButton and GtkToggleButton share much in terms of
look, feel, and functionality. So, how does this relate to objects?
4276 | CH03.fm Page 86 Sunday, July 15, 2001 5:25 PM
Widgets in Gtk+ are organized as a hierarchy of classes. Each class in the Gtk+ widget class
hierarchy is ultimately a descendant of the class named GtkObject. Refer to the appendix for a
listing of the Gtk+ class hierarchy as of Gtk+ 1.2.
The GtkObject class represents a parent class from which all classes in the widget hierarchy
inherit basic behavior. GtkObjects’ contribution is minimal but important. Among the func-
tionality provided by GtkObject is the signal mechanism. As one descends the hierarchy, visual
representation (if any) and functionality become increasingly specialized. Each node in the
class hierarchy diagram that has descendants provides a base class from which those descen-
dants can, if they choose, inherit their look and feel or functionality. A child class will always
replace some (perhaps all) of the look and feel or functionality of its parent class or introduce
new look and feel or functionality that was not present in the parent class.
Such is the case with GtkButton (the parent) and GtkToggleButton (the child). Much of
the implementation of GtkToggleButton is inherited from GtkButton. GtkToggleButton
overrides the semantics of button presses and button releases and introduces the “toggled”
signal, but essentially, a toggle button is really a button for the most part.
Object API
GtkObject implements an API that can be used by widget implementations and client devel-
opers. Here I just focus on a few of the application-level functions in this API so we can
obtain a better understanding of what objects are from the perspective of an application.
The first routine is gtk_object_destroy():
void
gtk_object_destroy( GtkObject *object )
gtk_object_destroy() takes an object as a parameter and destroys it. This routine can be
called from any place that gtk_widget_destroy() is called. We saw one example of the use of
gtk_widget_destroy() earlier in this chapter when I discussed gtk_signal_connect_object().
For example, let’s say you have a GtkButton widget that you need to destroy. You can
perform the destruction using either of the following techniques:
GtkButton *button;
...
or
GtkButton *button;
...
Objects 87
GtkObject *button;
...
or
GtkObject *button;
...
gtk_object_destroy( button );
Finally, we could declare the button as GtkWidget, and then it would be:
GtkWidget *button;
...
gtk_widget_destroy( button );
or
GtkWidget *button;
...
Regardless of how it’s done, in the end, the button will be destroyed. Notice the use of the
casting macros. If a routine expects an object and you have a widget, use GTK_OBJECT to
convert the widget to an object. And, going the other way, use GTK_WIDGET to cast an
object to a widget when a widget is needed.
These casting macros are not restricted to just GtkWidget and GtkObject. All widget
classes in the widget hierarchy implement a macro that can be used to convert from one
widget class to another. In later chapters, I will point out the macro name when I discuss
the corresponding widget class, but as a general rule of thumb, the name of the macro can
be formed by taking the widget class name, converting it to all uppercase, and inserting
an underscore (_) after the initial GTK. For example, the casting macro for GtkButton is
GTK_BUTTON. It is not actually this easy; additional underscores are added in some
cases in which the class name is formed by a concatenation of words. For example, the
casting macro for the class GtkDrawingArea is GTK_DRAWING_AREA.
These “casting” macros do not perform just a simple C-style cast. They also make sure that
the item being cast is non-NULL and that the class to which the object is being cast either is
4276 | CH03.fm Page 88 Sunday, July 15, 2001 5:25 PM
of the same class (making the cast a no-op) or is a super-class in the widget instance hierarchy.
Thus, a cast from any widget class (e.g., GtkButton) to GtkWidget will be successful because
all buttons inherit from GtkWidget. A cast from GtkList to GtkText will fail because GtkList
does not inherit from GtkText. The casting macros generate warning output if, for whatever
reason, the cast being performed is illegal.
As you become moderately experienced as a Gtk+ programmer, deciding when and
when not to use the casting macros will become somewhat second nature.
Here is a source code snippet that illustrates casting at a few different levels:
037 /* button */
038
039 button = (GtkButton *) gtk_button_new_with_label ("foo");
040
041 gtk_signal_connect (GTK_OBJECT(button), "destroy",
042 GTK_SIGNAL_FUNC(PrintAndExit), "button, object destroy");
043
044 gtk_object_destroy( GTK_OBJECT( button ) );
045
046 button = (GtkButton *) gtk_button_new_with_label ("foo");
047
048 gtk_signal_connect (GTK_OBJECT(button), "destroy",
049 GTK_SIGNAL_FUNC(PrintAndExit), "button, widget destroy");
050
051 gtk_widget_destroy( GTK_WIDGET( button ) );
052
...
4276 | CH03.fm Page 89 Sunday, July 15, 2001 5:25 PM
Objects 89
069 return( 0 );
070 }
The application basically creates and destroys six buttons. Here, I only show the lines
pertaining to creating the button and storing its handle in a variable of GtkButton *. The full
application contains code that creates and destroys instances of GtkButton, storing them as
GtkObject * and as GtkWidget *. Our first need for a cast occurs on line 039. Here, the
return value from gtk_button_new_with_label() is GtkWidget *, and I am required to cast
this result to (GtkButton *) to eliminate a compile-time warning from gcc(1). Note that all
gtk_*_new() functions return a handle of type GtkWidget * because buttons, scrollbars,
labels, toggle buttons, and so on, are all widgets. I personally feel that using “GtkButton
*button;” to declare a variable that is going to hold a widget handle to a GtkButton to be
better style, but adding the casts is annoying, so I suggest all widgets be declared as Gtk-
Widget *. There are other good reasons for doing this, but avoiding the need for adding
casts all over the place is reason enough.
On lines 041 and 042, we register a signal callback function with Gtk+ for the “destroy” sig-
nal. The user data argument is a string that identifies the operation; in this case, “button, object
destroy” means we have stored the widget in a “button” variable (i.e., a variable of type GtkBut-
ton *) and we are going to call gtk_object_destroy() as opposed to gtk_widget_destroy() to
destroy the widget. On line 044, we destroy the widget by making a call to gtk_object_destroy().
Note that the argument is cast by the GTK_OBJECT macro because the widget instance was
stored in a GtkButton * variable, but gtk_object_destroy() requires a variable of GtkObject *.
The same basic logic prevails on lines 046 through 051, except this time, the button is
destroyed with a call to gtk_widget_destroy(), requiring us to cast the button variable from
a GtkButton to a GtkWidget using GTK_WIDGET.
Object Attributes
In Gtk+, objects have attributes. When you instantiate a GtkButton widget with a call to
gtk_button_new_with_label(), for example, you are setting the button widget’s label to the
string that was passed in as an argument. Actually, there is more going on than just this, but
from an application’s perspective, this is effectively what happens.
Usually, perhaps ideally, applications will not make use of the following functions.
However, it is worthwhile to look at them because it will strengthen your concept of what
an object is in Gtk+.
The first function we’ll look at is gtk_object_query_args():
GtkArg *
gtk_object_query_args (GtkType type, guint32 **flags, guint *nargs);
The function gtk_object_query_args() can be used to obtain the list of attributes supported
by a widget class. This can only be done after the application has instantiated at least one
instance of the class being queried. The argument type defines the class to be queried. The
best way to obtain the value of type is to call a routine provided by the class implementation.
For the GtkButton class, this is gtk_button_get_type(). For other classes, it will be named
4276 | CH03.fm Page 90 Sunday, July 15, 2001 5:25 PM
gtk_*_get_type() by convention (the actual names are documented along with the widget
classes as they are discussed later in this book).
The second argument, flags, is a pointer to an array of unallocated guint32 (or guint) values.
You can pass (gunit32 **) NULL here or the address of a variable of type guint32 *:
type = gtk_button_get_type();
or
guint32 *flags;
Flag Meaning
Objects 91
If you are curious about the contents of GtkArg, the structure is defined in gtktypeutils.h.
However, you can, and should, access the fields in this structure using accessor macros
defined by Gtk+. Table 3.9 lists the possible simple data types that an attribute can have,
along with the accessor macros that can be used to obtain the data for each type.
Table 3.9 Nonaggregate Accessor Macros
gchar GTK_VALUE_CHAR(a)
guchar GTK_VALUE_UCHAR(a)
gboolean GTK_VALUE_BOOL(a)
gint GTK_VALUE_INT(a)
guint GTK_VALUE_UINT(a)
glong GTK_VALUE_LONG(a)
gulong GTK_VALUE_ULONG(a)
gfloat GTK_VALUE_FLOAT(a)
gdouble GTK_VALUE_DOUBLE(a)
gchar * GTK_VALUE_STRING(a)
gint GTK_VALUE_ENUM(a)
guint GTK_VALUE_FLAGS(a)
gpointer GTK_VALUE_BOXED(a)
gpointer GTK_VALUE_POINTER(a)
GtkObject * GTK_VALUE_OBJECT(a)
Accessor macros are also defined for the following aggregate types in Table 3.10.
Table 3.10 Aggregate Accessor Macros
GTK_VALUE_SIGNAL(a) struct {
GtkSignalFunc f;
gpointer d;
} signal_data;
4276 | CH03.fm Page 92 Sunday, July 15, 2001 5:25 PM
GTK_VALUE_ARGS(a) struct {
gint n_args;
GtkArg *args;
} args_data;
GTK_VALUE_CALLBACK(a) struct {
GtkCallbackMarshal marshal;
gpointer data;
GtkDestroyNotify notify;
} callback_data;
GTK_VALUE_C_CALLBACK(a) struct {
GtkFunction func;
gpointer func_data;
} c_callback_data;
GTK_VALUE_FOREIGN(a) struct {
gpointer data;
GtkDestroyNotify notify;
} foreign_data;
...
On line 020, we call gtk_object_query_args(). Then, on lines 029 through 080, we iterate
through the array of GtkArg structs returned. For each arg, we determine its type using
GTK_FUNDAMENTAL_TYPE (line 032). Then, in the switch statement, we print that
type as a string to stdout:
Objects 93
031
032 switch( GTK_FUNDAMENTAL_TYPE (args[i].type) ) {
033 case GTK_TYPE_CHAR :
034 printf( "GTK_TYPE_CHAR, " );
035 break;
036 case GTK_TYPE_UCHAR :
037 printf( "GTK_TYPE_UCHAR, " );
038 break;
039 case GTK_TYPE_BOOL :
040 printf( "GTK_TYPE_BOOL, " );
041 break;
...
080 }
Following the switch, on lines 081 through 101, we interpret the corresponding entry in
the flags array that was returned. Remember, if NULL is sent as the second argument to
gtk_object_query_args(), then no flags will be returned.
Finally, on lines 106 through 109, the flags and args pointers are freed by a call to g_free().
111 return( 0 );
112 }
void
gtk_object_arg_get (GtkObject *object, GtkArg *arg, GtkArgInfo *info)
The first argument, object, is the widget from which object data is to be retrieved. The sec-
ond argument, arg, is effectively the element in the vector returned by gtk_object_query_args()
that corresponds to the attribute being queried. You can use gtk_object_query_args() to obtain
this value, or you can allocate a GtkArg variable on the stack and set the name field to the
attribute you want to query, for example:
GtkArg myArg;
GtkWidget *myButton;
...
myArg.name = "GtkButton::label";
gtk_object_arg_get( GTK_OBJECT( myButton ), &myArg, NULL );
...
The final argument, info, should always be passed as NULL. In fact, there are no examples
of gtk_object_arg_get() usage in the Gtk+ source code where this argument is set to anything
but NULL. gtk_object_arg_get() will retrieve the value internally if you pass NULL, so per-
haps this argument will be deprecated in a future version of Gtk+.
On return, myArg will contain the data that was requested. If the data could not be obtained
for whatever reason (for example, the attribute does not exist), gtk_object_arg_get() will gen-
erate output to the console, for example:
The type field in the GtkArg struct will be set to GTK_TYPE_INVALID. This can be
checked using code similar to the following:
The second routine that can be used to retrieve attribute values is as follows:
4276 | CH03.fm Page 95 Sunday, July 15, 2001 5:25 PM
Objects 95
void
gtk_object_getv (GtkObject *object, guint n_args, GtkArg *args)
...
...
...
115 }
On line 017, we create an instance of the GtkButton class. We need to pass an object to
gtk_object_arg_get() to identify the object we are querying, and we also need, in this example,
to create an instance of GtkButton so that gtk_object_query_args() can do its job.
On line 019, we call gtk_object_query_args() to obtain a list of the attributes supported by
the GtkButton widget class. Then, on lines 026 through 105, we iterate through the array
returned by gtk_object_query_args(). For each element, we make a call to gtk_object_arg_get();
this occurs on line 033. We then switch on the type field set by gtk_object_arg_get(), accessing
this value using the GTK_FUNDAMENTAL_TYPE macro as previously described. In the
switch statement, we simply use the type to determine the format string passed to printf and use
the accessor macro needed to retrieve the value from the GtkArg element. Note the use of
g_free(), which is needed to release storage allocated by Gtk+ for GTK_TYPE_STRING
attributes, as shown on lines 074 through 078.
Gtk+ provides two routines for setting attribute values in a widget. They are:
void
gtk_object_set (GtkObject *object, const gchar *first_arg_name, ...)
and
void
gtk_object_setv (GtkObject *object, guint n_args, GtkArg *args)
Both can be used to set multiple attributes. gtk_object_setv() would be the more conve-
nient routine to call after obtaining a GtkArg vector from gtk_object_query_args(), although
this is not required, of course. In all other cases, gtk_object_set() is probably the easiest of the
two to use.
gtk_object_setv() takes the very same arguments as gtk_object_getv(). The only difference
is that the elements in the args vector need to contain that data to which the attribute is being
set and the type. To do this, use the accessor macros used to read data from a GtkArg struct.
For example, to change the label of a button, we might code the following:
4276 | CH03.fm Page 97 Sunday, July 15, 2001 5:25 PM
Objects 97
GtkArg arg;
GtkWidget *widget;
...
arg.type = GTK_TYPE_STRING;
arg.name = "GtkButton::label";
GTK_VALUE_STRING(arg) = "Yabba Dabba Doo";
gtk_object_setv( GTK_OBJECT( widget ), 1, &arg );
The function gtk_object_set() accepts a variable argument list. Each attribute to be set is
specified in the argument list by its name, such as GtkButton::label, followed by a variable
number of arguments that specify the value of that attribute. In some cases, a single argu-
ment can be used to specify a value, for example, a button label value is a string. In some
cases, the attribute being set is an aggregate, and in this case, the value arguments will cor-
respond to the fields of a structure or the elements of a table.
The final argument to gtk_object_set() must be NULL to indicate the end of the argu-
ment list (if you forget the NULL, gtk_object_set() will read beyond the stack, leading to
unpredictable behavior).
The preceding example, using gtk_object_set(), is reduced to the following:
GtkWidget *widget;
void
gtk_object_set_data (GtkObject *object, const gchar *key,
gpointer data)
An item on the object’s data list will be added by Gtk+ as a result of making this call.
The second function is gtk_object_set_data_full():
4276 | CH03.fm Page 98 Sunday, July 15, 2001 5:25 PM
void
gtk_object_set_data_full (GtkObject *object, const gchar *key,
gpointer data, GtkDestroyNotify destroy)
void
DestroyFunc ( gpointer data )
You may pass NULL as the last argument to gtk_object_set_data_full(), but then the call
effectively becomes equivalent to calling gtk_object_set_data().
If an entry indexed by key already exists on the object’s list prior to calling either
gtk_object_set_data() or gtk_object_set_data_full(), then the gpointer stored by that entry
will be replaced by data. A new entry on the list will not be created because indexes on the
list must be unique.
To retrieve data from an object’s list, call gtk_object_get_data():
gpointer
gtk_object_get_data (GtkObject *object, const gchar *key)
The function gtk_object_get_data() takes an object and a key. If there is no entry on the
object’s list indexed by key, then NULL is returned. Otherwise, the data that is stored on
the list indexed by key will be returned.
To remove an entry from an object’s list, call gtk_object_remove_data() or
gtk_object_remove_no_notify():
void
gtk_object_remove_data (GtkObject *object, const gchar *key)
void
gtk_object_remove_no_notify (GtkObject *object, const gchar *key)
Either function will remove the entry indexed by key from the list maintained by object,
if such an entry exists. If gtk_object_remove_data() was called, the destroy function regis-
tered with the entry, if any, will be invoked, and a copy of the data stored by that entry will
be passed as an argument as previously discussed. If gtk_object_remove_no_notify() is
used, then the destroy function will not be invoked.
Gtk+ supports the following two convenience functions:
void
gtk_object_set_user_data (GtkObject *object, gpointer data)
gpointer
gtk_object_get_user_data (GtkObject *object)
4276 | CH03.fm Page 99 Sunday, July 15, 2001 5:25 PM
Objects 99
So how might The GIMP maintain information about images currently being edited? A
convenient method for organizing this data would be to maintain a data structure for each
image being edited. A possible candidate data structure is the following:
4276 | CH03.fm Page 100 Sunday, July 15, 2001 5:25 PM
Now that we have a way to represent this data, where should we store this data structure?
Whatever method we choose, we must be able to easily associate the image currently being
edited or displayed by the user with the data about that image.
One possibility would be to store it in a global linked list. Whenever the user selects a
window and it is brought to the front, we search the linked list for the entry with a “win”
field that contains the window handle of the window that was raised; this record will contain
the information about the image being edited in the window. This is a perfectly fine solu-
tion. The only problem is that the application will need to maintain code needed to support
the linked list.
An alternate solution would be to use indexed data. To associate image data with a window,
we simply use gtk_object_set_data() at the time the image is created or opened. For example,
the routine that creates a new image and its window might perform the following:
...
ImageData *imageData;
GtkWidget *dialog;
/* set defaults */
/* create a window */
...
4276 | CH03.fm Page 101 Sunday, July 15, 2001 5:25 PM
Types 101
HandleDestroy() can then retrieve the image_data entry from the dialog and, if the image
data is “dirty”, give the user the opportunity to save changes to a file:
void
HandleDestroy (GtkWidget *widget, gpointer data)
{
ImageData *ptr;
Well, that ends my coverage of objects in this chapter. You should now have a good idea of
what an object is and be aware of some of the ways that objects can be used in a Gtk+ application.
Types
You may have noticed that the code snippets and function prototypes presented in this chap-
ter make use of nonstandard C types such as gpointer, gint, and gchar *. These types, which
are defined by Glib in glib.h, are intended to aid in the portability of Gtk+, GDK, and Glib
and the applications that make use of these toolkits.
4276 | CH03.fm Page 102 Sunday, July 15, 2001 5:25 PM
You should get in the habit of using these types, particularly when declaring variables
that will be passed as arguments to the Glib, GDK, or Gtk+ APIs. Using C language types
such as void *, int, or char * is acceptable in other cases. Declaring a loop index variable as
int as opposed to gint will not lead to any problems, unless perhaps the index variable is
used as an argument to a Glib function that requires gint, for example. While perhaps
unlikely, it is not guaranteed that a gint will map to an int in all implementations.
Table 3.11 lists the basic types defined by Glib for UNIX and Linux.
Table 3.11 Glib Types
char gchar
short gshort
int gint
int gboolean
long glong
float gfloat
double gdouble
void * gpointer
Where more than one Glib type maps to the same C type (for example, gboolean and gint
both map to int in the preceding table), avoid interchanging Glib types. In other words, if a func-
tion prototype mandates the use of a gboolean, do not use a gint in its place; use a gboolean.
4276 | CH03.fm Page 103 Sunday, July 15, 2001 5:25 PM
Summary 103
Summary
In this chapter, we discussed signals and signal handling. Signals are the way in which wid-
gets communicate changes back to your application and are a required part of any meaningful
Gtk+ application. You will, as a Gtk+ programmer, do much of your programming within the
context of signal functions. We also covered Gtk+ events and objects and described the asso-
ciated functions for each. Events are low-level when compared to signals, corresponding to
events that exist at the X protocol level. Many (most) of the events we discussed are inter-
cepted by widgets on behalf of your application and are translated into their higher level signal
counterparts. Some applications, however, can make good use of events (this is especially true
for applications that involve interactive graphics of some kind). We also discussed objects.
Objects are fundamental to the architecture of the Gtk+ toolkit. In a practical sense, you will
find yourself using the terms “object” and “widget” interchangeably. All Gtk+ widgets are
descendants of GtkObject in the object/widget hierarchy. This chapter described what objects
are as well as the API that exists for manipulating them. The chapter ended with a short dis-
cussion of Gtk+ data types. For the sake of portability, you should strive to use the Gtk+ types
(e.g., use “guint” instead of “unsigned int”), although, as I will illustrate time and again in this
book, use of the Gtk+ types is by no means a requirement.
4276 | CH03.fm Page 104 Sunday, July 15, 2001 5:25 PM