doc/reference/libpurple/tut_signals.xml

Fri, 01 Oct 2021 17:49:31 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 01 Oct 2021 17:49:31 -0500
changeset 41061
94a357cfec6c
parent 37056
2099e72f5039
permissions
-rw-r--r--

move the zephyr protocol plugins icons to a resource in the plugin

Testing Done:
Ran in devenv with a logo inverted (which was reverted before committing).

Reviewed at https://reviews.imfreedom.org/r/981/

<?xml version='1.0' encoding="ISO-8859-1"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
]>
<chapter id="chapter-tut-signals">
  <title>Signals tutorial</title>

  <sect2 id="tut-signals-introduction">
  <title>Introduction</title>
  <para>
  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.
  </para>
  </sect2>

  <sect2 id="tut-signals-overview">
  <title>Overview of Purple-signals</title>
  <para>
  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.
  </para>
  </sect2>

  <sect2 id="tut-signals-registering">
  <title>Registering a Signal</title>
  <para>
  The first step of using a signal is registering it with libpurple so that
  callbacks may be connected to it. This is done using
  <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>.
  Here is a slightly modified example from
  <link linkend="purple-plugins-init"><function>purple_plugins_init()</function></link>
  in <literal>plugins.c</literal>:

<programlisting>
purple_signal_register(purple_plugins_get_handle(), /* Instance */
                       "plugin-load",               /* Signal name */
                       purple_marshal_VOID__POINTER,/* Marshal function */
                       G_TYPE_NONE,                 /* Callback return type */
                       1,                           /* Number of callback arguments (not including void *data) */
                       PURPLE_TYPE_PLUGIN           /* Type of first callback argument */
);
</programlisting>
  </para>

  <sect3>
  <title>Instance</title>
  <para>
  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.
  </para>
  </sect3>
  
  <sect3>
  <title>Signal Name</title>
  <para>
  Unique identifier for the signal itself.
  </para>
  </sect3>

  <sect3>
  <title>Callback function definition</title>
  <para>
  The rest of the arguments specify the form of the callback function.

  <itemizedlist>
    <listitem><para><emphasis>Marshal function</emphasis></para><para>
  <literal>purple_marshal_VOID__POINTER</literal> represents the callback
  function prototype, not including a "data" argument, explained later. The form
  is  <literal>purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC</literal>.
  See <link linkend="libpurple-signals">signals.h</link> for more possible types.
  </para>

  <para>
  In this case, the callback will have the form
<programlisting>
void cb(void *arg1, void *data)
</programlisting>
  </para>

  <para>
  If <literal>purple_marshal_BOOLEAN__POINTER_POINTER_POINTER</literal> were
  specified, it would be:
<programlisting>
gboolean cb(void *arg1, void *arg2, void *arg3, void *data)
</programlisting>

  The <literal>void *data</literal> argument at the end of each callback function
  provides the data argument given to
  <link linkend="purple-signal-connect"><function>purple_signal_connect()</function></link>.
    </para></listitem>

    <listitem><para><emphasis>Callback return type</emphasis></para><para>
  In our case, this is G_TYPE_NONE, meaning "returns void".
<!-- TODO This could be described better. -->
    </para></listitem>

    <listitem><para><emphasis>Number of callback arguments</emphasis></para><para>
  The number of arguments (not including <literal>data</literal>) that the callback function
  will take.
    </para></listitem>

    <listitem><para><emphasis>Type of argument</emphasis></para><para>
  <literal>PURPLE_TYPE_PLUGIN</literal> specifies that the first argument given to the callback
  will be a <literal>PurplePlugin*</literal>. You will need as many "type of argument"
  arguments to
  <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>
  as you specified in
  "Number of arguments" above.

<!-- TODO Describe this more. -->
    </para></listitem>
  </itemizedlist>
  </para>
  </sect3>
  </sect2>

  <sect2 id="tut-signals-connect">
  <title>Connecting to the signal</title>
  <para>
  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 :
<programlisting>
static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
{
	GtkTreeView *view = (GtkTreeView *)data;
	plugin_loading_common(plugin, view, TRUE);
}
</programlisting>
  Note that the callback function prototype matches that specified in the call
  to <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>
  above.
  </para>

  <para>
  Once the callback function is defined, you can connect it to the signal.
  Again from gtkplugin.c , in <function>pidgin_plugin_dialog_show()</function>:
<programlisting>
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
);
</programlisting>
  </para>

  <para>
  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").
  </para>

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

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

  <sect2 id="tut-signals-emit-signal">
  <title>Emitting a signal</title>
  <para>
  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.
  </para>

  <para>
  As seen in <link linkend="purple-plugin-load"><function>purple_plugin_load()</function></link>
  in plugin.c:
<programlisting>
purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
</programlisting>
  This causes the signal "plugin-load" to be emitted from the plugin module
  (given by
  <link linkend="purple-plugins-get-handle"><function>purple_plugins_get_handle()</function></link>),
  with the newly loaded plugin as
  the argument to pass to any registered callback functions.
  </para>

  <para>
  In our example, <literal>plugin_load_cb</literal> is called immediately as
<programlisting>
plugin_load_cb(plugin, event_view);
</programlisting>
  and does whatever it does.
  </para>
 </sect2>
</chapter>

mercurial