Signals

Signals

Each object is encapsulated in Gtk programming. And it is not recommended to use global variables because they are prone to make the program complicated. So, we need something to communicate between objects. There are two ways to do so.

The caller of methods or signals are usually out of the object. One of the difference between these two is that the object is active or passive. In methods, objects passively respond to the caller. In signals, objects actively send signals to handlers.

GObject signals are registered, connected and emitted.

  1. Signals are registered in the class. The registration is done usually when the class is initialized. Signals can have a default handler, which is sometimes called “object method handler”. It is not a user handler connected by g_signal_connect family functions. A default handler is always called on any instance of the class.
  2. Signals are connected to handlers by the macro g_signal_connect or its family functions. The connection is usually done out of the object. One important thing is that signals are connected on a certain instance. Suppose there exist two GtkButton instances A, B and a function C. Even if you connected the “clicked” signal on A to C, B and C are not connected.
  3. When Signals are emitted, the connected handlers are invoked. Signals are emitted on the instance of the class.

Signal registration

In TfeTextView, two signals are registered.

A static variable or array is used to store signal ID.

enum {
  CHANGE_FILE,
  OPEN_RESPONSE,
  NUMBER_OF_SIGNALS
};

static guint tfe_text_view_signals[NUMBER_OF_SIGNALS];

Signals are registered in the class initialization function.

static void
tfe_text_view_class_init (TfeTextViewClass *class) {
  GObjectClass *object_class = G_OBJECT_CLASS (class);

  object_class->dispose = tfe_text_view_dispose;
  tfe_text_view_signals[CHANGE_FILE] = g_signal_new ("change-file",
                                 G_TYPE_FROM_CLASS (class),
                                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                 0 /* class offset */,
                                 NULL /* accumulator */,
                                 NULL /* accumulator data */,
                                 NULL /* C marshaller */,
                                 G_TYPE_NONE /* return_type */,
                                 0     /* n_params */
                                 );
  tfe_text_view_signals[OPEN_RESPONSE] = g_signal_new ("open-response",
                                 G_TYPE_FROM_CLASS (class),
                                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                 0 /* class offset */,
                                 NULL /* accumulator */,
                                 NULL /* accumulator data */,
                                 NULL /* C marshaller */,
                                 G_TYPE_NONE /* return_type */,
                                 1     /* n_params */,
                                 G_TYPE_INT
                                 );
}

The handlers are declared as follows.

/* "change-file" signal handler */
void
user_function (TfeTextView *tv,
               gpointer user_data)

/* "open-response" signal handler */
void
user_function (TfeTextView *tv,
               TfeTextViewOpenResponseType response-id,
               gpointer user_data)

The values of the type TfeTextViewOpenResponseType are defined in tfetextview.h.

/* "open-response" signal response */
enum TfeTextViewOpenResponseType
{
  TFE_OPEN_RESPONSE_SUCCESS,
  TFE_OPEN_RESPONSE_CANCEL,
  TFE_OPEN_RESPONSE_ERROR
};

Signal connection

A signal and a handler are connected by the function macro g_signal_connect. There are some similar function macros like g_signal_connect_after, g_signal_connect_swapped and so on. However, g_signal_connect is used most often. The signals “change-file” and “open-response” are connected to their callback functions out of the TfeTextView object. Those callback functions are defined by users.

For example, callback functions are defined in src/tfe6/tfewindow.c and their names are file_changed_cb and open_response_cb. They will be explained later.

g_signal_connect (GTK_TEXT_VIEW (tv), "change-file", G_CALLBACK (file_changed_cb), nb);

g_signal_connect (TFE_TEXT_VIEW (tv), "open-response", G_CALLBACK (open_response_cb), nb);

Signal emission

A signal is emitted on the instance. A function g_signal_emit is used to emit the signal. The following lines are extracted from src/tfetextview/tfetextview.c. Each line comes from a different line.

g_signal_emit (tv, tfe_text_view_signals[CHANGE_FILE], 0);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_SUCCESS);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_CANCEL);
g_signal_emit (tv, tfe_text_view_signals[OPEN_RESPONSE], 0, TFE_OPEN_RESPONSE_ERROR);