pidgin 2.14.14dev
Signals HOWTO

Introduction

The libpurple signals interface is used for general event notification, such as plugins being loaded or unloaded, allowing the GUI frontend to respond appropriately to changing internal data. Unfortunately, its use is not at all obvious from the information in the header files. This document uses code snippets from the Pidgin/libpurple plugin systems to illustrate the proper use of signals.

Overview of Signals

Signals in libpurple are very similar to those in GTK+. When certain events happen, a named signal is "emitted" from a certain object. Emitting the signal triggers a series of callbacks that have been "connected" to that signal for that object. These callbacks take appropriate action in response to the signal.

Registering a Signal

The first step of using a signal is registering it with libpurple so that callbacks may be connected to it. This is done using purple_signal_register() Here is a slightly modified example from purple_plugins_init in libpurple/plugin.c :

"plugin-load", /* Signal name */
purple_marshal_VOID__POINTER,/* Marshal function */
NULL, /* Callback return value type */
1, /* Number of callback arguments (not including void *data) */
purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) /* Type of first callback argument */
);
void * purple_plugins_get_handle(void)
Returns the plugin subsystem handle.
gulong purple_signal_register(void *instance, const char *signal, PurpleSignalMarshalFunc marshal, PurpleValue *ret_value, int num_values,...)
Registers a signal in an instance.
PurpleValue * purple_value_new(PurpleType type,...)
Creates a new PurpleValue.
@ PURPLE_TYPE_SUBTYPE
Subtype.
Definition: value.h:37

Instance

A reference to the object from which this signal is emitted, and to which potential callbacks should be connected. In this case, it will be the entire plugin module emitting the signal.

Signal Name

Unique identifier for the signal itself.

Callback function definition

The rest of the arguments specify the form of the callback function.

Marshal Function

purple_marshal_VOID__POINTER represents the callback function prototype, not including a "data" argument, explained later. The form is purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC. See signals.h for more possible types.

In this case, the callback will have the form

void cb(void *arg1, void *data)

If purple_marshal_BOOLEAN__POINTER_POINTER_POINTER were specified, it would be:

gboolean cb(void *arg1, void *arg2, void *arg3, void *data)

The void *data argument at the end of each callback function provides the data argument given to purple_signal_connect() .

Callback return value type

In our case, this is NULL, meaning "returns void".

Todo:
This could be described better.

Number of arguments

The number of arguments (not including data ) that the callback function will take.

Type of argument

purple_value_new(PURPLE_TYPE_SUBTYPE,PURPLE_SUBTYPE_PLUGIN) specifies that the first argument given to the callback will be a PurplePlugin* . You will need as many "type of argument" arguments to purple_signal_register() as you specified in "Number of arguments" above.

Todo:
Describe this more.
See also
value.h

Connecting to the signal

Once the signal is registered, you can connect callbacks to it. First, you must define a callback function, such as this one from gtkplugin.c :

static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
{
GtkTreeView *view = (GtkTreeView *)data;
plugin_loading_common(plugin, view, TRUE);
}
A plugin handle.
Definition: plugin.h:152

Note that the callback function prototype matches that specified in the call to purple_signal_register() above.

Once the callback function is defined, you can connect it to the signal. Again from gtkplugin.c , in pidgin_plugin_dialog_show() :

purple_signal_connect(purple_plugins_get_handle(), "plugin-load", /* What to connect to */
plugin_dialog, /* Object receiving the signal */
PURPLE_CALLBACK(plugin_load_cb), /* Callback function */
event_view, /* Data to pass to the callback function
);
gulong purple_signal_connect(void *instance, const char *signal, void *handle, PurpleCallback func, void *data)
Connects a signal handler to a signal for a particular object.

The first two arguments ("What to connect to") specify the object emitting the signal (the plugin module) and what signal to listen for ("plugin-load").

The object receiving the signal is plugin_dialog , the Pidgin plugins dialog. When plugin_dialog is deleted, then purple_signals_disconnect_by_handle(plugin_dialog) should be called to remove all signal connections it is associated with.

The callback function is given using a helper macro, and finally the data argument to be passed to plugin_load_cb is given as event_view, a pointer to the GTK widget that plugin_load_cb needs to update.

Emitting a signal

Connecting callbacks to signals is all well and good, but how do you "fire" the signal and trigger the callback? At some point, you must "emit" the signal, which immediately calls all connected callbacks.

As seen in purple_plugin_load() in plugin.c :

void purple_signal_emit(void *instance, const char *signal,...)
Emits a signal.

This causes the signal "plugin-load" to be emitted from the plugin module (given by purple_plugins_get_handle() ), with the newly loaded plugin as the argument to pass to any registered callback functions.

In our example, plugin_load_cb is called immediately as

plugin_load_cb(plugin, event_view);

and does whatever it does.