Signals

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

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

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

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

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

Adding a New Signal

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


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

  gtk_object_class_add_signals (object_class, button_signals, LAST_SIGNAL);



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


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


static guint button_signals[LAST_SIGNAL] = { 0 };


gtk_signal_new() has the following effects:

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

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

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

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


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

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

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


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

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

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

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


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

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

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


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

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

Using Existing Signals

Figure 4 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; the section called Adding a New Signal 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 the section called Adding a New Signal; 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. the section called What Happens When A Signal Is Emitted 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 the section called Adding a New Signal). 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);

Figure 4. Using Signals

Emitting A Signal

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

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


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

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


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


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

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


  gint return_val;

  return_val = FALSE;

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

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

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

All four signal emission functions are summarized in Figure 5.

#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);

Figure 5. Signal Emission

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.

What Happens When A Signal Is Emitted

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

  1. If you are emitting the signal by name, the signal ID is looked up.

  2. If another emission of the same signal is in progress, and the signal has the GTK_RUN_NO_RECURSE flag set, GTK+ signals the previous emission to restart and this emission ends.

  3. If the signal is GTK_RUN_FIRST, the default signal handler is called using the signal's marshaller. If the emission is stopped from within the handler, (using gtk_emit_stop_by_name() or one of its cousins), this emission ends. If the signal is re-emitted from within the handler and is GTK_RUN_NO_RECURSE, this emission restarts.

  4. If there are any emission hooks installed for this signal, they are invoked. GTK+ does not check whether the emission has been stopped or re-emitted at this point; it will not check until the next step. Emission hooks should not re-emit the signal they are watching, or try to stop the emission.

  5. Any normally-connected callbacks are invoked using the signal's marshaller. Callbacks connected with gtk_signal_connect_after() are not invoked at this point. After invoking each callback, GTK+ checks whether it stopped the signal and the emission ends if so. GTK+ also checks whether the signal was re-emitted, and if so restarts the emission process for GTK_RUN_NO_RECURSE signals.

  6. If the signal is GTK_RUN_LAST, the default handler is invoked. Afterward GTK+ again checks whether the emission has been stopped or should be restarted.

  7. Any callbacks connected with gtk_signal_connect_after() are invoked. After invoking each one, GTK+ checks whether the emission should be stopped or restarted.

Within each step the handlers are invoked in the order they were connected. The order of the steps is fixed: GTK_RUN_FIRST default handler, emission hooks, normal connections, GTK_RUN_LAST default handler, "after" connections.