| |
1 <?xml version='1.0' encoding="ISO-8859-1"?> |
| |
2 <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" |
| |
3 "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ |
| |
4 ]> |
| |
5 <chapter id="chapter-tut-signals"> |
| |
6 <title>Signals tutorial</title> |
| |
7 |
| |
8 <sect2 id="tut-signals-introduction"> |
| |
9 <title>Introduction</title> |
| |
10 <para> |
| |
11 The libpurple signals interface is used for general event notification, such |
| |
12 as plugins being loaded or unloaded, allowing the GUI frontend to respond |
| |
13 appropriately to changing internal data. Unfortunately, its use is not at all |
| |
14 obvious from the information in the header files. This document uses code |
| |
15 snippets from the Pidgin/libpurple plugin systems to illustrate the proper |
| |
16 use of signals. |
| |
17 </para> |
| |
18 </sect2> |
| |
19 |
| |
20 <sect2 id="tut-signals-overview"> |
| |
21 <title>Overview of Purple-signals</title> |
| |
22 <para> |
| |
23 Signals in libpurple are very similar to those in GTK+. When certain events |
| |
24 happen, a named signal is "emitted" from a certain object. Emitting the |
| |
25 signal triggers a series of callbacks that have been "connected" to that |
| |
26 signal for that object. These callbacks take appropriate action in response |
| |
27 to the signal. |
| |
28 </para> |
| |
29 </sect2> |
| |
30 |
| |
31 <sect2 id="tut-signals-registering"> |
| |
32 <title>Registering a Signal</title> |
| |
33 <para> |
| |
34 The first step of using a signal is registering it with libpurple so that |
| |
35 callbacks may be connected to it. This is done using |
| |
36 <link linkend="purple-signal-register"><function>purple_signal_register()</function></link>. |
| |
37 Here is a slightly modified example from |
| |
38 <link linkend="purple-plugins-init"><function>purple_plugins_init</function></link> |
| |
39 in <literal>plugin.c</literal>: |
| |
40 |
| |
41 <programlisting> |
| |
42 purple_signal_register(purple_plugins_get_handle(), /* Instance */ |
| |
43 "plugin-load", /* Signal name */ |
| |
44 purple_marshal_VOID__POINTER,/* Marshal function */ |
| |
45 G_TYPE_NONE, /* Callback return type */ |
| |
46 1, /* Number of callback arguments (not including void *data) */ |
| |
47 PURPLE_TYPE_PLUGIN /* Type of first callback argument */ |
| |
48 ); |
| |
49 </programlisting> |
| |
50 </para> |
| |
51 |
| |
52 <sect3> |
| |
53 <title>Instance</title> |
| |
54 <para> |
| |
55 A reference to the object from which this signal is emitted, and to which |
| |
56 potential callbacks should be connected. In this case, it will be the entire |
| |
57 plugin module emitting the signal. |
| |
58 </para> |
| |
59 </sect3> |
| |
60 |
| |
61 <sect3> |
| |
62 <title>Signal Name</title> |
| |
63 <para> |
| |
64 Unique identifier for the signal itself. |
| |
65 </para> |
| |
66 </sect3> |
| |
67 |
| |
68 <sect3> |
| |
69 <title>Callback function definition</title> |
| |
70 <para> |
| |
71 The rest of the arguments specify the form of the callback function. |
| |
72 |
| |
73 <itemizedlist> |
| |
74 <listitem><para><emphasis>Marshal function</emphasis></para><para> |
| |
75 <literal>purple_marshal_VOID__POINTER</literal> represents the callback |
| |
76 function prototype, not including a "data" argument, explained later. The form |
| |
77 is <literal>purple_marshal_RETURNVALUETYPE__ARG1TYPE_ARG2TYPE_ETC</literal>. |
| |
78 See <link linkend="libpurple-signals">signals.h</link> for more possible types. |
| |
79 </para> |
| |
80 |
| |
81 <para> |
| |
82 In this case, the callback will have the form |
| |
83 <programlisting> |
| |
84 void cb(void *arg1, void *data) |
| |
85 </programlisting> |
| |
86 </para> |
| |
87 |
| |
88 <para> |
| |
89 If <literal>purple_marshal_BOOLEAN__POINTER_POINTER_POINTER</literal> were |
| |
90 specified, it would be: |
| |
91 <programlisting> |
| |
92 gboolean cb(void *arg1, void *arg2, void *arg3, void *data) |
| |
93 </programlisting> |
| |
94 |
| |
95 The <literal>void *data</literal> argument at the end of each callback function |
| |
96 provides the data argument given to |
| |
97 <link linkend="purple-signal-connect"><function>purple_signal_connect()</function></link>. |
| |
98 </para></listitem> |
| |
99 |
| |
100 <listitem><para><emphasis>Callback return type</emphasis></para><para> |
| |
101 In our case, this is G_TYPE_NONE, meaning "returns void". |
| |
102 <!-- TODO This could be described better. --> |
| |
103 </para></listitem> |
| |
104 |
| |
105 <listitem><para><emphasis>Number of callback arguments</emphasis></para><para> |
| |
106 The number of arguments (not including <literal>data</literal>) that the callback function |
| |
107 will take. |
| |
108 </para></listitem> |
| |
109 |
| |
110 <listitem><para><emphasis>Type of argument</emphasis></para><para> |
| |
111 <literal>PURPLE_TYPE_PLUGIN</literal> specifies that the first argument given to the callback |
| |
112 will be a <literal>PurplePlugin*</literal>. You will need as many "type of argument" |
| |
113 arguments to |
| |
114 <link linkend="purple-signal-register"><function>purple_signal_register</function></link> |
| |
115 as you specified in |
| |
116 "Number of arguments" above. |
| |
117 |
| |
118 <!-- TODO Describe this more. --> |
| |
119 </para></listitem> |
| |
120 </itemizedlist> |
| |
121 </para> |
| |
122 </sect3> |
| |
123 </sect2> |
| |
124 |
| |
125 <sect2 id="tut-signals-connect"> |
| |
126 <title>Connecting to the signal</title> |
| |
127 <para> |
| |
128 Once the signal is registered, you can connect callbacks to it. First, you |
| |
129 must define a callback function, such as this one from gtkplugin.c : |
| |
130 <programlisting> |
| |
131 static void plugin_load_cb(PurplePlugin *plugin, gpointer data) |
| |
132 { |
| |
133 GtkTreeView *view = (GtkTreeView *)data; |
| |
134 plugin_loading_common(plugin, view, TRUE); |
| |
135 } |
| |
136 </programlisting> |
| |
137 Note that the callback function prototype matches that specified in the call |
| |
138 to <link linkend="purple-signal-register"><function>purple_signal_register()</function></link> |
| |
139 above. |
| |
140 </para> |
| |
141 |
| |
142 <para> |
| |
143 Once the callback function is defined, you can connect it to the signal. |
| |
144 Again from gtkplugin.c , in <function>pidgin_plugin_dialog_show()</function>: |
| |
145 <programlisting> |
| |
146 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", /* What to connect to */ |
| |
147 plugin_dialog, /* Object receiving the signal */ |
| |
148 PURPLE_CALLBACK(plugin_load_cb), /* Callback function */ |
| |
149 event_view, /* Data to pass to the callback function |
| |
150 ); |
| |
151 </programlisting> |
| |
152 </para> |
| |
153 |
| |
154 <para> |
| |
155 The first two arguments ("What to connect to") specify the object emitting |
| |
156 the signal (the plugin module) and what signal to listen for ("plugin-load"). |
| |
157 </para> |
| |
158 |
| |
159 <para> |
| |
160 The object receiving the signal is <literal>plugin_dialog</literal> , the Pidgin plugins |
| |
161 dialog. When <literal>plugin_dialog</literal> is deleted, then |
| |
162 <literal>purple_signals_disconnect_by_handle(plugin_dialog)</literal> should be called to |
| |
163 remove all signal connections it is associated with. |
| |
164 </para> |
| |
165 |
| |
166 <para> |
| |
167 The callback function is given using a helper macro, and finally the |
| |
168 <literal>data</literal> argument to be passed to |
| |
169 <literal>plugin_load_cb</literal> is given as <literal>event_view</literal>, |
| |
170 a pointer to the GTK widget that <literal>plugin_load_cb</literal> needs to update. |
| |
171 </para> |
| |
172 </sect2> |
| |
173 |
| |
174 <sect2 id="tut-signals-emit-signal"> |
| |
175 <title>Emitting a signal</title> |
| |
176 <para> |
| |
177 Connecting callbacks to signals is all well and good, but how do you "fire" |
| |
178 the signal and trigger the callback? At some point, you must "emit" the |
| |
179 signal, which immediately calls all connected callbacks. |
| |
180 </para> |
| |
181 |
| |
182 <para> |
| |
183 As seen in <link linkend="purple-plugin-load"><function>purple_plugin_load</function></link> |
| |
184 in plugin.c: |
| |
185 <programlisting> |
| |
186 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin); |
| |
187 </programlisting> |
| |
188 This causes the signal "plugin-load" to be emitted from the plugin module |
| |
189 (given by |
| |
190 <link linkend="purple-plugins-get-handle"><function>purple_plugins_get_handle</function></link>), |
| |
191 with the newly loaded plugin as |
| |
192 the argument to pass to any registered callback functions. |
| |
193 </para> |
| |
194 |
| |
195 <para> |
| |
196 In our example, <literal>plugin_load_cb</literal> is called immediately as |
| |
197 <programlisting> |
| |
198 plugin_load_cb(plugin, event_view); |
| |
199 </programlisting> |
| |
200 and does whatever it does. |
| |
201 </para> |
| |
202 </sect2> |
| |
203 </chapter> |