|
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:
-
It registers the name of the signal.
-
It associates the signal with a particular GtkType.
-
It tells GTK+ where to find the default handler in
the class struct, if any.
-
It tells GTK+ what signature the signal's callbacks
will have.
-
It registers a marshaller, a
function which invokes the signal's callbacks in an
appropriate way.
-
It generates an integer identifier which can be used
to refer to the signal. (If you refer to the symbol
by name, GTK+ will find the ID associated with the
name and then use the ID.)
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:
-
GTK_RUN_FIRST means
that the default handler in the class struct, if any,
will run before user-connected callbacks. If this
flag is set, signal handlers should not return a
value.
-
GTK_RUN_LAST means
the opposite, the default handler will run last.
(Caveat: user callbacks connected with gtk_signal_connect_after() run after
a GTK_RUN_LAST
default handler. There is no way to ensure a default
handler is always run last.
GTK_RUN_FIRST
handlers are always first, however.)
-
GTK_RUN_BOTH is an
alias for (GTK_RUN_FIRST |
GTK_RUN_LAST), so the default handler will run
twice (on either side of user-connected callbacks).
-
GTK_RUN_NO_RECURSE
means that the signal should not be called
recursively. If a handler for a signal emits the same
signal again, normally the second emission is
performed as usual (calling all handlers), and then
the first emission continues, invoking its remaining
handlers. With
GTK_RUN_NO_RECURSE in effect, a second
emission aborts the first emission (ignoring any
handlers that remain), and restarts the emission
process. So only one emission is in progress at a
time. (Right now this is used only for GtkAdjustment's "changed" and
"value_changed" signals. Usually you don't
care about how many times a value changed, only
whether it changed and its most recent value. GTK_RUN_NO_RECURSE
"compresses" multiple emissions into a single
emission.)
-
GTK_RUN_ACTION means
the signal can be "bound" and invoked by the user. In
other words, no special setup or shutdown is required
in order to emit it. Among other things, GTK+ will
allow users to bind keyboard accelerators to these
signals using statements in the
.gtkrc configuration file.
-
GTK_RUN_NO_HOOKS
means that emission hooks are not allowed (you can't
monitor this signal for an entire object type, only
for particular object instances). It is used for
GtkObject's "destroy" signal because hooks
are not invoked on objects with the GTK_DESTROYED flag set and that
flag is set before emitting
"destroy". It's probably not good for anything
else.
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).
|
|