GSignal

The replacement for GtkSignals in libgobject are GSignals. As with most things in this new library, GSignals are a lot more versatile than GtkSignals. Signals are a general purpose notification framework. A type may have one or more signals, each of which may have an argument list and return value. Handlers can then be connected to instances of the type. When the signal is emitted on an instance, each of the connected handlers will be called.

Inside libgobject, signals are used for property change notification. In GTK+, signals are used for event notification and state change notification among other things.

Unlike GTK+ 1.2, instead of directly connecting C functions as signal handlers, GClosures are used in glib 2.0 (there are convenience wrappers included for the benefit of C programmers).

Connecting Handlers to GSignals

For a language binding, you will probably want to use either g_signal_connect_closure or g_signal_connect_closure_by_id:

guint g_signal_connect_closure(gpointer instance, const gchar *detailed_signal, GClosure *closure, gboolean after);

guint g_signal_connect_closure_by_id(gpointer instance, guint signal_id, GQuark detail, GClosure *closure, gboolean after);

Note that the only difference between the two functions is that g_signal_connect_closure converts its detailed_signal argument to signal_id and detail arguments using:

gboolean g_signal_parse_name(const gchar *detailed_signal, GType itype, guint *signal_id_p, GQuark *detail_p, gboolean force_detail_quark);

The detailed_signal or signal_id and detail arguments identify which signal to call on the instance. The closure argument says what function to call as a handler for the signal. The after argument says whether the handler should be called before or after the class closure of the signal (described in the next section).

The closure will be passed all the arguments that should be passed to the function it wraps (unlike the GtkCallbackMarshal used in GTK+ 1.2, which didn't include the actual instance in the arg list). The invocation_hint argument passed to the closure's marshal function will be a pointer to a GSignalInvocationHint structure:

typedef struct _GSignalInvocationHint GSignalInvocationHint;
struct _GSignalInvocationHint
{
  guint         signal_id;
  GQuark        detail;
  GSignalFlags  run_type;
};

For those who have used the GTK+ 1.2 signal system, the above functions would only seem to replace gtk_signal_connect and gtk_signal_connect_after -- not gtk_signal_connect_object and gtk_signal_connect_object_after. The behaviour of the second two functions is handled by the closure in the glib 2.0 model.

To get the *_object behaviour, you will need to pass in a closure that swaps the first argument passed to the closure and the data argument. For example, there are two constructors for "C Closures" -- g_cclosure_new and g_cclosure_new_swap, which performs the argument swapping. If you want to provide the argument swapping behaviour in your language binding, your custom closure type must support it.

To put it all together, the code to connect a function from the language you are binding to a signal of a particular object may look something like this pseudo C code:

static guint
connect_signal(ObjectWrapper *wrapper,
               const gchar *detailed_signal,
               Function *function,
               gboolean swap_data,
               gboolean after)
{
    GObject *instance = get_object_from_wrapper(Wrapper);
    GClosure *closure = create_closure_for_function(function, swap_data);

    return g_signal_connect_closure(instance, detailed_signal, closure, after);
}

Emitting Signals

Once you have handlers connected to a signal, you need to wait for the signal to be emitted. For most of the signals in GTK+, the emission is handled by the library. In some cases though, you may want to emit signals from the language you are writing the binding for. In these cases, you will need a wrapper for g_signal_emitv:

void g_signal_emitv(const GValue *instance_and_params, guint signal_id, GQuark detail, GValue *return_value);

The first argument to g_signal_emitv is the argument list for the signal emission. The first element in the array is a GValue for the instance the signal is being emitted on. The rest are any arguments to be passed to the signal. The types of the arguments can be found with the g_signal_query function:

void g_signal_query(guint signal_id, GSignalQuery *query);

This function will fill in a GSignalQuery structure passed in as the second argument. The structure has the following members:

typedef struct _GSignalQuery GSignalQuery;
struct _GSignalQuery
{
  guint         signal_id;
  const gchar  *signal_name;
  GType         itype;       /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
  GSignalFlags  signal_flags;
  GType         return_type; /* mangled with G_SIGNAL_TYPE_STATIC_SCOPE flag */
  guint         n_params;
  const GType  *param_types;
};

Using the param types, you should be able to convert an argument vector in the native format of the language binding to a GValue array (using the functions for converting from native formats to GValues written earlier).

The signal_id and detail arguments can be deduced with the g_signal_parse_name function.

The return_value is a GValue to store the return value of the signal emission. It should be initialised before the g_signal_emitv with code something like this:

GValue return_type = { 0, };
GSignalQuery query;

g_signal_query(signal_id, &query);
if (query.return_type != G_TYPE_NONE)
    g_value_init(&return_type, query.return_type);

Signal emission is wrapped in the pygobject_emit function in pygtk. The interface provided to python is a GObject.emit method that takes a detailed signal argument as the first argument, followed by any arguments for the signal. The method returns the return value of the signal (using the function to convert from a GValue to native types).

Creating New Signals

It is easy to add new signals to any instantiable type such as a GObject. This is done with the g_signal_newv function:

guint g_signal_newv(const gchar *signal_name, GType itype, GSignalFlags signal_flags, GClosure *class_closure, GSignalAccumulator accumulator, gpointer accu_data, GSignalCMarsahller c_marshaller, GType return_type, guint n_params, GType *param_types);

The signal_name argument is the name of the signal being created. The itype argument is the GType of instances this signal should apply to. The signal_flags gives the flags for the signal (whether to run the class closure before or after handlers, etc). The accumulator related arguments can be ignored (pass NULL), and so can the c_marshaller argument (it is only needed if you intend to connect C handlers to the signal). The last three arguments give the GType's of the return value and parameters for the signal.

The class_closure argument takes a closure that represents the `class handler' for the signal. That is, a handler that will always be called when the signal is emitted. Usually, this would be a different type of closure to that passed to g_signal_connect_closure. Instead of just wrapping a function, it will usually call a virtual function of the GObject wrapper (so that the class handler can be overidden in subclasses). The naming convention used for the class handler used in PyGTK and Inti is "do_" prepended to the signal name (XXXX - what do other bindings do?). It is recommended that other language bindings use something similar.

In the case of bindings for languages with good introspective capabilities, you may be able to call a method if you know only its name. Since the name of the class handler is derived from the signal name (which can be derived from the signal id passed in the invocation hint for the marshaller), you can write a class closure marshal function that can handle all possible signals. This aproach is used in the python bindings where a single closure is used as the class closure for all signals created in python. See the pyg_signal_class_closure_get function in gobjectmodule.c file in pygtk for details.