comparison mcabber/doc/HOWTO_modules.txt @ 1830:7befbd8e932d

Update module howto
author Mikael Berthe <mikael@lilotux.net>
date Sat, 27 Mar 2010 13:35:46 +0100
parents d8442bcb33b7
children ea3f9b4f3558
comparison
equal deleted inserted replaced
1829:c059c5c98de6 1830:7befbd8e932d
3 3
4 Mcabber module writing brief howto 4 Mcabber module writing brief howto
5 5
6 =========================================== 6 ===========================================
7 7
8 To obtain information on module mcabber uses struct 8 To obtain information on module mcabber uses struct module_info_t, that
9 module_info_t, that module should provide in public 9 module should provide in public variable with name info_<modulename>.
10 variable with name info_<modulename>. If module name 10 If the module name contains any extra symbols except [a-z0-9_] they
11 contains any extra symbols except [a-z0-9_] they should 11 should be replaced with '_'.
12 be replaced with '_'. 12
13 13 ------------------------------------------------------------------------
14 --------------------------------------------------------
15 #include <mcabber/modules.h> 14 #include <mcabber/modules.h>
16 15
17 typedef void (*module_init_t)(void); 16 typedef void (*module_init_t)(void);
18 typedef void (*module_uninit_t)(void); 17 typedef void (*module_uninit_t)(void);
19 18
26 const gchar **requires; 25 const gchar **requires;
27 module_init_t init; 26 module_init_t init;
28 module_uninit_t uninit; 27 module_uninit_t uninit;
29 module_info_t *next; 28 module_info_t *next;
30 }; 29 };
31 -------------------------------------------------------- 30 ------------------------------------------------------------------------
32 31
33 Callbacks init and uninit will be called after module 32 Callbacks init and uninit will be called after module and its
34 and it's dependencies loading. 'requires' can contain a 33 dependencies loading. 'requires' can contain a NULL-terminated list of
35 NULL-terminated list of module names, that should be loaded 34 module names, that should be loaded before this. 'branch' and 'api' are
36 before this. 'branch' and 'api' are required and should 35 required and should contain mcabber branch and api version, that this
37 contain mcabber branch and api version, that this module is 36 module is designed to work with. For these values see ChangeLog.api.
38 designed to work with. For these values see ChangeLog.api. 37 'version' and 'description' fields are optional and just provide user
39 'version' and 'description' fields are optional and just 38 with additional information about the module. 'description' field can
40 provide user with additional information about module. 39 contain newlines. The 'next' field can contain pointer to the next
41 'description' field can contain newlines. 'next' field can 40 struct with another branch of mcabber, if your module can work with
42 contain pointer to the next struct with another branch of 41 multiple branches.
43 mcabber, if your module can work with multiple branches. 42
44 43 To load modules, mcabber uses glib's GModule, thus, in your module you
45 To load modules, mcabber uses glib's GModule, thus, in your 44 can also use functions
46 module you can also use functions 45
47 46 ------------------------------------------------------------------------
48 --------------------------------------------------------
49 #include <glib.h> 47 #include <glib.h>
50 #include <gmodule.h> 48 #include <gmodule.h>
51 49
52 const gchar* g_module_check_init (GModule *module); 50 const gchar* g_module_check_init (GModule *module);
53 void g_module_unload (GModule *module); 51 void g_module_unload (GModule *module);
54 -------------------------------------------------------- 52 ------------------------------------------------------------------------
55 53
56 to do something before any version/dependency checks will 54 to do something before any version/dependency check is performed when
57 be performed when module is loaded/unloaded. On success 55 your module is loaded/unloaded. On success g_module_check_init should
58 g_module_check_init should return NULL, and error message 56 return NULL, and error message otherwise.
59 otherwise. 57
60 58 As module is loaded, you can use mcabber functions, declared in
61 As module is loaded, you can use mcabber functions, 59 mcabber's header files (though you should consider, that they may change
62 declared in mcabber's header files (though you should 60 their calling conventions some day).
63 consider, that they may change their calling conventions 61
64 some day). 62 I will not explain them all, there are too much of them, but will
65 63 provide description for those, provided especially for module writers.
66 I will not explain them all, there are too much of 64
67 them, but will provide description for those, provided 65 ------------------------------------------------------------------------
68 especially for module writers.
69
70 --------------------------------------------------------
71 #include <mcabber/modules.h> 66 #include <mcabber/modules.h>
72 67
73 const gchar *module_load (const gchar *name, 68 const gchar *module_load (const gchar *name,
74 gboolean manual, 69 gboolean manual,
75 gboolean force); 70 gboolean force);
76 const gchar *module_unload (const gchar *name, 71 const gchar *module_unload (const gchar *name,
77 gboolean manual, 72 gboolean manual,
78 gboolean force); 73 gboolean force);
79 -------------------------------------------------------- 74 ------------------------------------------------------------------------
80 75
81 These functions load and unload modules respectively. 76 These functions load and unload modules respectively. You can use them
82 You can use them to handle optional dependencies. What 77 to handle optional dependencies. What happens, when module is loaded:
83 happens, when module is loaded: 78 - check if module is present, and if present just increase it's
84 - check if module is present, and if present just 79 reference count
85 increase it's reference count 80 - load .so via glib (and call g_module_check_init, if present)
86 - load .so via glib (and call g_module_check_init, if
87 present)
88 - check for information structure presence 81 - check for information structure presence
89 - find suitable branch and check api version 82 - find suitable branch and check api version compatibility
90 compatibility 83 - load modules, that this module requires (note, that dependency
91 - load modules, that this module requires (note, that 84 problems will be reported as error invariably, force flag have no
92 dependency problems will be reported as error 85 effect on this check)
93 invariably, force flag have no effect on this check)
94 - module placed into a list of modules 86 - module placed into a list of modules
95 - module init routine is called 87 - module init routine is called
96 And when unloaded: 88 And when unloaded:
97 - check if module is present 89 - check if module is present
98 - decrease reference count, if it is not zero, return 90 - decrease reference count, if it is not zero, return
99 - run module uninit routine 91 - run module uninit routine
100 - unload modules, that were loaded as dependencies for 92 - unload modules, that were loaded as dependencies for this one
101 this
102 - remove from modules list 93 - remove from modules list
103 They return error message or NULL in case of success. 94 They return error message or NULL in case of success. 'manual' flag
104 'manual' flag indicates, that module will be loaded by 95 indicates, that module will be loaded by direct user request. It serves
105 direct user request. It serves the purpose of tracking 96 the purpose of tracking user and automatic references (user can have
106 user and automatic references (user can have only one). 97 only one). 'force' flag on module loading causes mcabber to ignore most
107 'force' flag on module loading causes mcabber to ignore 98 of the loading errors. On unload it forces unloading even if reference
108 most of the loading errors. On unload it forces 99 count is not zero.
109 unloading even if reference count is not zero. 100
110 101 ------------------------------------------------------------------------
111 --------------------------------------------------------
112 #include <mcabber/commands.h> 102 #include <mcabber/commands.h>
113 103
114 void cmd_add (const char *name, const char *help, 104 void cmd_add (const char *name, const char *help,
115 guint flags1, guint flags2, 105 guint flags1, guint flags2,
116 void (*f)(char*), gpointer userdata); 106 void (*f)(char*), gpointer userdata);
117 void cmd_del (const char *name); 107 void cmd_del (const char *name);
118 -------------------------------------------------------- 108 ------------------------------------------------------------------------
119 109
120 These two functions are provided to declare mcabber 110 These two functions are provided to declare mcabber commands, offered by
121 commands, offered by your module. 111 your module.
122 - name is a command name. 112 - name is a command name.
123 - help is a short description of your command, however 113 - help is a short description of your command, however for now it is
124 for now it is not used at all and can be omitted. 114 not used at all and can be omitted.
125 - flags are completion identifiers for first and second 115 - flags are completion identifiers for first and second command
126 command arguments, for list of built-in completions, 116 arguments, for list of built-in completions, see compl.h. You can
127 see compl.h. You can declare your own completion 117 declare your own completion lists, using functions from compl.h,
128 lists, using functions from compl.h, described later. 118 described later.
129 - f is a user-provided callback function, that will be 119 - f is a user-provided callback function, that will be called upon
130 called upon executing mcabber command. If you will 120 executing mcabber command. If you will provide non-NULL userdata,
131 provide non-NULL userdata, function must be of type 121 function must be of type
132 void (*f) (char *commandline, gpointer userdata). 122 void (*f) (char *commandline, gpointer userdata).
133 - userdata is a pointer to data, transparently passed 123 - userdata is a pointer to data, transparently passed to callback.
134 to callback. See f description. 124 See f description.
135 125
136 -------------------------------------------------------- 126 ------------------------------------------------------------------------
137 #include <mcabber/compl.h> 127 #include <mcabber/compl.h>
138 128
139 guint compl_new_category (void); 129 guint compl_new_category (void);
140 void compl_del_category (guint id); 130 void compl_del_category (guint id);
141 131
142 void compl_add_category_word (guint categ, 132 void compl_add_category_word (guint categ,
143 const char *command); 133 const char *command);
144 void compl_del_category_word (guint categ, 134 void compl_del_category_word (guint categ,
145 const char *word); 135 const char *word);
146 GSList *compl_get_category_list (guint cat_flags, 136 GSList *compl_get_category_list (guint cat_flags,
147 guint *dynlist); 137 guint *dynlist);
148 -------------------------------------------------------- 138 ------------------------------------------------------------------------
149 139
150 These functions allow you to define and manage word 140 These functions allow you to define and manage word lists for completion
151 lists for completion categories, used by your commands. 141 categories, used by your commands. First you need to obtain handle for
152 First you need to obtain handle for completion type, 142 completion type, that you later will supply as flags, when declaring
153 that you later will supply as flags, when declaring 143 your commands. For that use function compl_new_category. It returns
154 your commands. For that use function compl_new_category. 144 new category id, or zero if mcabber runs out of completion ids (for now
155 It returns new category id or zero, if mcabber runs 145 there are only 32 ids available, and 20 of them are already used for
156 out of completion ids (for now there are only 32 ids 146 builtin commands). compl_del_category allows you to delete user-defined
157 available, and 20 of them are already taken by builtin 147 category, deleting all words in it too.
158 commands). compl_del_category allows you to delete 148
159 user-defined category, deleting all words in it too. 149 Now, that you have a completion category, you can at any time add or
160 150 delete words from its completion list. To do that, use the functions
161 Now, that you have a completion category, you can at any 151 compl_add_category_word and compl_del_category_word. You can obtain
162 time add or delete words from it's completion list. 152 current contents of category by using gompl_get_category_list. If after
163 For that use functions compl_add_category_word and 153 execution dynlist is TRUE, you should free obtained list of words (both,
164 compl_del_category_word. You can obtain current contents 154 words and list).
165 of category by using gompl_get_category_list. If after 155
166 execution dynlist is TRUE, you should free obtained 156 ------------------------------------------------------------------------
167 list of words (both, words and list).
168
169 --------------------------------------------------------
170 #include <mcabber/hooks.h> 157 #include <mcabber/hooks.h>
171 158
172 typedef struct { 159 typedef struct {
173 const char *name; 160 const char *name;
174 const char *value; 161 const char *value;
175 } hk_arg_t; 162 } hk_arg_t;
176 163
177 typedef void (*hk_handler_t) (guint32 hookid, 164 typedef guint (*hk_handler_t) (const gchar *hookname,
178 hk_arg_t *args, 165 hk_arg_t *args,
179 gpointer userdata); 166 gpointer userdata);
180 167
181 void hk_add_handler (hk_handler_t handler, 168 guint hk_add_handler (hk_handler_t handler,
182 guint32 flags, 169 const gchar *hookname,
183 gpointer userdata); 170 gint priority,
184 void hk_del_handler (hk_handler_t handler, 171 gpointer userdata);
185 gpointer userdata); 172 void hk_del_handler (const gchar *hookname,
186 -------------------------------------------------------- 173 guint hid);
187 174 ------------------------------------------------------------------------
188 These functions allow your module to react to events, 175
189 such as incoming and outgoing messages, buddy status 176 These functions allow your module to react to events, such as incoming
190 changes and sever connection establishment or breakup. 177 and outgoing messages, buddy status changes and server connection
191 Flags field specifies mask of events, upon which this 178 establishment or breakup. The hookname string specifies the events the
192 handler should be called. Flags, that comprise this 179 handler wants to subscribe to. The available strings can be found in
193 mask can be found in hooks.h. You can specify not yet 180 hooks.h.
194 used flags in mask, if you need to handle all events. 181
195 Handler can determine, which event is occured by 182 The hk_add_handler() function will return a handler id which you will
196 hookid argument and by a "hook" field in args, that 183 use to remove the handler with hk_del_handler().
197 may provide more precise information in some cases. 184 Args argument is a list of hk_arg_t structures, terminated by structure,
198 Args argument is a list of hk_arg_t structures, 185 whose name field is set to NULL.
199 terminated by structure, whose name field is set to 186
200 NULL. Usually the "hook" field is in the first 187 Your handler should return one of the values in the hk_handler_result
201 structure of the list, however it is not guaranted, 188 enum (see hooks.h), usually HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS so
202 that this will be so forever. 189 that other handlers can be triggers as well.
203 190
204 Currently there are next events possible: 191 A handler can determine which event has occured by checking the hookname
205 - hook-message-in (HOOK_MESSAGE_IN) with parameters 192 argument (a same hook handler can subscribe to several events by using
193 hk_add_handler() several times).
194
195 Currently the following events exist:
196 - hook-pre-message-in (HOOK_PRE_MESSAGE_IN) with parameters
206 * jid - sender of the incoming message 197 * jid - sender of the incoming message
207 * message - message body, converted to locale 198 * resource - resource of the incoming message
208 charset 199 * message - message body, converted to locale charset
209 * groupchat ("true" or "false") 200 * groupchat - ("true" or "false")
201 - hook-post-message-in (HOOK_POST_MESSAGE_IN) with parameters
202 * jid - sender of the incoming message
203 * resource - resource of the incoming message
204 * message - message body, converted to locale charset
205 * groupchat - ("true" or "false")
206 * attention - In a MUC message, true if you've been highlighted
207 In a regular message, true if the sender has requested your
208 attention (only implemented for MUC currently)
210 - hook-message-out (HOOK_MESSAGE_OUT) with parameters 209 - hook-message-out (HOOK_MESSAGE_OUT) with parameters
211 * jid - recipient of the outgoing message 210 * jid - recipient of the outgoing message
212 * message - message body, converted to locale 211 * message - message body, converted to locale charset
213 charset
214 - hook-status-change (HOOK_STATUS_CHANGE) with 212 - hook-status-change (HOOK_STATUS_CHANGE) with
215 parameters 213 parameters
216 * jid - buddy, whose status has changed 214 * jid - buddy, whose status has changed
217 * resource - resource, whose status has changed 215 * resource - resource, whose status has changed
218 * old_status - old status of the buddy, one-char 216 * old_status - old status of the buddy, one-char string,
219 string, representing mcabber status letter - 217 representing mcabber status letter - one of 'ofdna?_'.
220 one of 'ofdna?_'. 218 * new_status - new buddy status. Same as above.
221 * new_status - new buddy status. The same as 219 * message - new status message. Old one should still be
222 old_status. 220 available to module as the current buddy's message.
223 * message - new status message. Old one should be
224 still available to module as the current buddy's
225 message.
226 - hook-my-status-change (HOOK_MY_STATUS_CHANGE) with 221 - hook-my-status-change (HOOK_MY_STATUS_CHANGE) with
227 parameters 222 parameters
228 * new_status - user's new status, see 223 * new_status - user's new status, see
229 hook-status-change. Old one should still be 224 hook-status-change. Old one should still be
230 available as the current status of the user. 225 available as the current status of the user.
231 * message - new status message 226 * message - new status message
232 - hook-post-connect (HOOK_POST_CONNECT) with no parameters 227 - hook-post-connect (HOOK_POST_CONNECT) with no parameters
233 - hook-pre-disconnect (HOOK_PRE_DICSONNECT) with no 228 - hook-pre-disconnect (HOOK_PRE_DISCONNECT) with no parameters
234 parameters 229 - hook-unread-list-change (HOOK_UNREAD_LIST_CHANGE)
235 230 * unread - number of buffers with the pending message flag (#)
236 -------------------------------------------------------- 231 * attention - number of non-MUC buffers with the attention sign (!)
232 * muc_unread - number of MUC buffers with the unread message flag
233 * muc_attention - number of MUC buffers with the attention sign
234
235
236 ------------------------------------------------------------------------
237 #include <mcabber/xmpp_helper.h> 237 #include <mcabber/xmpp_helper.h>
238 238
239 void xmpp_add_feature (const char *xmlns); 239 void xmpp_add_feature (const char *xmlns);
240 void xmpp_del_feature (const char *xmlns); 240 void xmpp_del_feature (const char *xmlns);
241 -------------------------------------------------------- 241 ------------------------------------------------------------------------
242 242
243 These functions may be useful, if your module implements 243 These functions may be useful, if your module implements some additional
244 some additional functionality to mcabber, that should be 244 functionality to mcabber, that should be advertised in a client's
245 advertised in a client's discovery features list. 245 discovery features list.
246 246
247 ===================== 247 =====================
248 248
249 Example: hello 249 Example: hello
250 250
251 ===================== 251 =====================
252 252
253 Now, let's write a simple module, called "hello", that 253 Now, let's write a simple module, called "hello", that will do no more
254 will do no more than just print something on loading 254 than just print something on loading and unloading.
255 and unloading. 255
256 256 ------------------------------------------------------------------------
257 --------------------------------------------------------
258 #include <glib.h> 257 #include <glib.h>
259 #include <gmodule.h> 258 #include <gmodule.h>
260 259
261 /* We will use scr_LogPrint mcabber function, 260 /* We will use scr_log_print() mcabber function,
262 that does mcabber's messages output */ 261 that does mcabber's messages output */
263 #include <mcabber/logprint.h> 262 #include <mcabber/logprint.h>
264 263
265 /* Print something on module loading */ 264 /* Print something on module loading */
266 const gchar* g_module_check_init (GModule *module) 265 const gchar* g_module_check_init(GModule *module)
267 { 266 {
268 scr_LogPrint (LPRINT_NORMAL, "Hello, World!"); 267 scr_log_print(LPRINT_NORMAL, "Hello, World!");
269 return NULL; 268 return NULL;
270 } 269 }
271 270
272 /* ... and unloading */ 271 /* ... and unloading */
273 void g_module_unload (GModule *module) 272 void g_module_unload(GModule *module)
274 { 273 {
275 scr_LogPrint (LPRINT_NORMAL, "Bye, World!"); 274 scr_log_print(LPRINT_NORMAL, "Bye, World!");
276 } 275 }
277 276
278 /* The End */ 277 /* The End */
279 -------------------------------------------------------- 278 ------------------------------------------------------------------------
280 279
281 Now, compile this file (hello.c) with 280 Now, compile this file (hello.c) with
282 281
283 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \ 282 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \
284 gmodule-2.0 mcabber` -c hello.c 283 gmodule-2.0 mcabber` -c hello.c
285 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ 284 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \
286 `pkg-config --libs glib-2.0 gmodule-2.0 mcabber` \ 285 `pkg-config --libs glib-2.0 gmodule-2.0 mcabber` \
287 -o libhello.la hello.lo 286 -o libhello.la hello.lo
288 287
289 (you should substitute /usr/lib/mcabber to directory, where 288 (you should substitute /usr/lib/mcabber to the directory where
290 your modules are located) and then install obtained module with 289 your modules are located) and then install obtained module with
291 290
292 libtool --mode=install install libhello.la \ 291 libtool --mode=install install libhello.la \
293 /usr/lib/mcabber/libhello.la 292 /usr/lib/mcabber/libhello.la
294 293
295 Note, that you, most likely need not run suggested by libtool 294 Note that you most likely need not run suggested by libtool finish
296 finish action, as we're working with module object, not system- 295 action, as we're working with module object, not system- wide library,
297 wide library, but maybe some systems require that. 296 but maybe some systems require that.
298 297
299 Now, set modules_dir mcabber variable to point to your modules 298 Now, set modules_dir mcabber variable to point to your modules
300 dir, and try to run /module -f load hello. If all goes well, 299 directory, and try to run /module -f load hello. If all goes well,
301 you should see in status buffer message "Hello World!" (as 300 you should see in status buffer message "Hello World!" (as well as
302 well as some complaints, as we forced module loading). 301 some complaints, as we forced module loading).
303 Now unload module by running command /module unload hello, 302 Now unload module by running command /module unload hello,
304 that should bring up message "Bye, World!". 303 that should bring up message "Bye, World!".
305 304
306 That's it, you just created very simple dynamically loadable 305 That's it, you just created very simple dynamically loadable mcabber
307 mcabber module. But, as you noticed, it needs force to be 306 module. But, as you noticed, it needs force to be loaded. Now, let's
308 loaded. Now, let's add information structure, that mcabber 307 add the information structure that mcabber wants.
309 wants.
310 308
311 ========================== 309 ==========================
312 310
313 Example: info struct 311 Example: info struct
314 312
315 ========================== 313 ==========================
316 314
317 -------------------------------------------------------- 315 ------------------------------------------------------------------------
318 #include <mcabber/logprint.h> 316 #include <mcabber/logprint.h>
319 /* module_info_t definition */ 317 /* module_info_t definition */
320 #include <mcabber/modules.h> 318 #include <mcabber/modules.h>
321 319
322 /* Print something on module loading */ 320 /* Print something on module loading */
323 void hello_init (void) 321 void hello_init(void)
324 { 322 {
325 scr_LogPrint (LPRINT_NORMAL, "Hello, World!"); 323 scr_log_print(LPRINT_NORMAL, "Hello, World!");
326 } 324 }
327 325
328 /* ... and unloading */ 326 /* ... and unloading */
329 void hello_uninit (void) 327 void hello_uninit(void)
330 { 328 {
331 scr_LogPrint (LPRINT_NORMAL, "Bye, World!"); 329 scr_log_print(LPRINT_NORMAL, "Bye, World!");
332 } 330 }
333 331
334 module_info_t info_hello = { 332 module_info_t info_hello = {
335 .branch = "dev", 333 .branch = "dev",
336 .api = 1, 334 .api = 1,
335 .version = "0.0.1",
336 .description = "Hello world module\n"
337 " (as well as bye world module)",
337 .requires = NULL, 338 .requires = NULL,
338 .init = hello_init, 339 .init = hello_init,
339 .uninit = hello_uninit, 340 .uninit = hello_uninit,
340 .version = "0.0.1",
341 .description = "Hello world module\n"
342 "(as well as bye world module)",
343 .next = NULL, 341 .next = NULL,
344 }; 342 };
345 343
346 /* The End */ 344 /* The End */
347 -------------------------------------------------------- 345 ------------------------------------------------------------------------
348 346
349 Here we now do not use glib nor gmodule, so, we can omit 347 Here we still do not use glib nor gmodule, so, we can omit them in
350 them in compilation lines: 348 compilation lines:
351 349
352 libtool --mode=compile gcc `pkg-config --cflags mcabber` \ 350 libtool --mode=compile gcc `pkg-config --cflags mcabber` \
353 -c hello.c 351 -c hello.c
354 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ 352 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \
355 `pkg-config --libs mcabber` -o libhello.la hello.lo 353 `pkg-config --libs mcabber` -o libhello.la hello.lo
356 354
357 Again compile it, copy, and try to load, now without -f 355 Again compile it, copy, and try to load, now without -f flag. As you
358 flag. As you may notice, when loading previous example, 356 may notice, when loading previous example, mcabber first printed "Hello,
359 mcabber first printed "Hello, World!", and only then 357 World!", and only then complaint about module not having information
360 complaint about module not having information struct. 358 struct. That's because g_module_check_init is called right after module
361 That's because g_module_check_init is called right 359 loading, before mcabber even has a chance to look at module, while .init
362 after module loading, before mcabber even have a chance 360 from info struct is called afterwards by mcabber itself. You can try to
363 to look at module, while .init from info struct is 361 introduce some error (e.g. too high or missing target mcabber version)
364 called afterwards by mcabber itself. You can try to 362 and see the difference.
365 introduce some error (eg too high or missing target
366 mcabber version) and see the difference.
367 363
368 ======================= 364 =======================
369 365
370 Example: command 366 Example: command
371 367
372 ======================= 368 =======================
373 369
374 Now, let's allow our module to do some real work. 370 Now, let's allow our module to do some real work.
375 371
376 -------------------------------------------------------- 372 ------------------------------------------------------------------------
377 #include <mcabber/logprint.h> 373 #include <mcabber/logprint.h>
378 #include <mcabber/commands.h> 374 #include <mcabber/commands.h>
379 #include <mcabber/modules.h> 375 #include <mcabber/modules.h>
380 376
381 /* Handler for command */ 377 /* Handler for command */
382 void do_hello (char *args) 378 void do_hello(char *args)
383 { 379 {
384 /* args contains command line with command 380 /* args contains command line with command
385 * name and any spaces after it stripped */ 381 * name and any spaces after it stripped */
386 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", 382 scr_log_print(LPRINT_NORMAL, "Hello, %s!",
387 *args != '\0' ? args : "World"); 383 *args != '\0' ? args : "World");
388 } 384 }
389 385
390 /* Register command */ 386 /* Register command */
391 void hello_init (void) 387 void hello_init(void)
392 { 388 {
393 cmd_add ("hello", "", 0, 0, do_hello, NULL); 389 cmd_add("hello", "", 0, 0, do_hello, NULL);
394 } 390 }
395 391
396 /* Unregister command */ 392 /* Unregister command */
397 void hello_uninit (void) 393 void hello_uninit(void)
398 { 394 {
399 cmd_del ("hello"); 395 cmd_del("hello");
400 } 396 }
401 397
402 module_info_t hello_info = { 398 module_info_t hello_info = {
403 .branch = "dev", 399 .branch = "dev",
404 .api = 1, 400 .api = 1,
401 .version = "0.0.2",
402 .description = "Hello world module\n"
403 " Provides command /hello",
405 .requires = NULL, 404 .requires = NULL,
406 .init = hello_init, 405 .init = hello_init,
407 .uninit = hello_uninit, 406 .uninit = hello_uninit,
408 .version = "0.0.2",
409 .description = "Hello world module\n"
410 "Provides command /hello",
411 .next = NULL, 407 .next = NULL,
412 } 408 }
413 409
414 /* The End */ 410 /* The End */
415 -------------------------------------------------------- 411 ------------------------------------------------------------------------
416 412
417 Now, compile it and try to load and run /hello with some 413 Now, compile it and try to load and run /hello with some arguments.
418 arguments. 414
419 415 Note, that we used one-argument version of command handler, as we have
420 Note, that we used one-argument version of command 416 specified no userdata.
421 handler, as we specified no userdata.
422 417
423 ========================== 418 ==========================
424 419
425 Example: completion 420 Example: completion
426 421
427 ========================== 422 ==========================
428 423
429 Now let's investigate how to provide custom completion to 424 Now let's investigate how to provide custom completion to your commands.
430 your commands. You can as well use built-in completions, 425 You can as well use built-in completions, their IDs are listed in
431 their IDs are listed in compl.h. 426 compl.h.
432 427
433 -------------------------------------------------------- 428 ------------------------------------------------------------------------
434 #include <mcabber/logprint.h> 429 #include <mcabber/logprint.h>
435 #include <mcabber/commands.h> 430 #include <mcabber/commands.h>
436 #include <mcabber/modules.h> 431 #include <mcabber/modules.h>
437 #include <mcabber/compl.h> 432 #include <mcabber/compl.h>
438 433
439 static guint hello_cid = 0; 434 static guint hello_cid = 0;
440 435
441 /* hello command handler */ 436 /* hello command handler */
442 void do_hello (char *args) 437 void do_hello(char *args)
443 { 438 {
444 /* If argument is provided, add it to 439 /* If argument is provided, add it to
445 * completions list. */ 440 * completions list. */
446 if (hello_cid && *args != '\0') 441 if (hello_cid && *args != '\0')
447 compl_add_category_word (hello_cid, 442 compl_add_category_word(hello_cid,
448 args); 443 args);
449 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", 444 scr_log_print(LPRINT_NORMAL, "Hello, %s!",
450 *args != '\0' ? args : "World"); 445 *args != '\0' ? args : "World");
451 } 446 }
452 447
453 /* Initialization */ 448 /* Initialization */
454 void hello_init (void) 449 void hello_init(void)
455 { 450 {
456 /* Obtain handle for our completion 451 /* Obtain handle for our completion
457 * category */ 452 * category */
458 hello_cid = compl_new_category (); 453 hello_cid = compl_new_category();
459 if (hello_cid) 454 if (hello_cid)
460 /* Add known default word to 455 /* Add known default word to
461 * completion list */ 456 * completion list */
462 compl_add_category_word (hello_cid, 457 compl_add_category_word(hello_cid,
463 "World"); 458 "World");
464 cmd_add ("hello", "", hello_cid, 0, do_hello, 459 cmd_add("hello", "", hello_cid, 0, do_hello,
465 NULL); 460 NULL);
466 } 461 }
467 462
468 /* Deinitialization */ 463 /* Deinitialization */
469 void hello_uninit (void) 464 void hello_uninit(void)
470 { 465 {
471 /* Give back category handle */ 466 /* Give back category handle */
472 if (hello_cid) 467 if (hello_cid)
473 compl_del_category (hello_cid); 468 compl_del_category(hello_cid);
474 cmd_del ("hello"); 469 cmd_del("hello");
475 } 470 }
476 471
477 module_info_t hello_info = { 472 module_info_t hello_info = {
478 .branch = "dev", 473 .branch = "dev",
479 .api = 1, 474 .api = 1,
475 .version = "0.0.3",
476 .description = "Hello world module"
477 " Provides command /hello with completion",
480 .requires = NULL, 478 .requires = NULL,
481 .init = hello_init, 479 .init = hello_init,
482 .uninit = hello_uninit, 480 .uninit = hello_uninit,
483 .version = "0.0.3",
484 .description = "Hello world module"
485 "Provides command /hello with completion",
486 .next = NULL, 481 .next = NULL,
487 } 482 }
488 483
489 /* The End */ 484 /* The End */
490 -------------------------------------------------------- 485 ------------------------------------------------------------------------
491 486
492 Now you can use completion for hello command. Note, that 487 Now you can use completion for hello command. Note, that this code have
493 this code have some serious simplifications, made for 488 some serious simplifications, made for simplicity reasons. For now,
494 simplicity reasons. For now, compl_add_category_word 489 compl_add_category_word does not checks, if word already exists in
495 does not checks, if word already exists in completions 490 completions list (although it is marked as TODO, so, some day it will),
496 list (although it is marked as TODO, so, some day it 491 so, we should check it ourselves.
497 will), so, we should check it ourselves.
498 492
499 ===================== 493 =====================
500 494
501 Example: hooks 495 Example: hooks
502 496
503 ===================== 497 =====================
504 498
505 Now let's implement our own beeper. Why anyone may wish 499 Now let's implement our own beeper. Why may anyone wish to do this?
506 to do this? I am not satisfied with default mcabber's 500 I am not satisfied with default mcabber's builtin beeper flexibility.
507 builtin beeper flexibility. I wanted beeping on any 501 I wanted beeping on any MUC conference message, not just the ones
508 muc conference message, not just ones, directed to me. 502 directed to me.
509 503
510 -------------------------------------------------------- 504 ------------------------------------------------------------------------
511 #include <string.h> 505 #include <string.h>
512 506
513 #include <mcabber/logprint.h> 507 #include <mcabber/logprint.h>
514 #include <mcabber/commands.h> 508 #include <mcabber/commands.h>
515 #include <mcabber/compl.h> 509 #include <mcabber/compl.h>
516 #include <mcabber/hooks.h> 510 #include <mcabber/hooks.h>
517 #include <mcabber/screen.h> 511 #include <mcabber/screen.h>
518 #include <mcabber/settings.h> 512 #include <mcabber/settings.h>
519 #include <mcabber/module.h> 513 #include <mcabber/module.h>
520 514
521 static guint beep_cid = 0; 515 static guint beep_cid = 0; /* Command completion category id */
516 static guint beep_hid = 0; /* Hook handler id */
522 517
523 /* Event handler */ 518 /* Event handler */
524 void beep_hh (guint32 hid, hk_arg_t *args, gpointer userdata) 519 static guint beep_hh(const gchar *hookname, hk_arg_t *args,
520 gpointer userdata)
525 { 521 {
526 /* Check if beeping is enabled */ 522 /* Check if beeping is enabled */
527 if (settings_opt_get_int ("beep_enable")) 523 if (settings_opt_get_int("beep_enable"))
528 /* *BEEP*! */ 524 scr_Beep(); /* *BEEP*! */
529 scr_Beep (); 525
526 return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
530 } 527 }
531 528
532 /* beep command handler */ 529 /* beep command handler */
533 void do_beep (char *args) 530 static void do_beep(char *args)
534 { 531 {
535 /* Check arguments, and if recognized, 532 /* Check arguments, and if recognized,
536 * set mcabber option accordingly */ 533 * set mcabber option accordingly */
537 if (!strcmp (args, "enable") || 534 if (!strcmp(args, "enable") ||
538 !strcmp (args, "on") || 535 !strcmp(args, "on") ||
539 !strcmp (args, "yes") || 536 !strcmp(args, "yes") ||
540 !strcmp (args, "1")) 537 !strcmp(args, "1"))
541 settings_set (SETTINGS_TYPE_OPTION, 538 settings_set(SETTINGS_TYPE_OPTION,
542 "beep_enable", "1"); 539 "beep_enable", "1");
543 else if (!strcmp (args, "disable") || 540 else if (!strcmp(args, "disable") ||
544 !strcmp (args, "off") || 541 !strcmp(args, "off") ||
545 !strcmp (args, "no") || 542 !strcmp(args, "no") ||
546 !strcmp (args, "0")) 543 !strcmp(args, "0"))
547 settings_set (SETTINGS_TYPE_OPTION, 544 settings_set(SETTINGS_TYPE_OPTION,
548 "beep_enable", "0"); 545 "beep_enable", "0");
549 546
550 /* Output current state, either if state is 547 /* Output current state, either if state is
551 * changed and if argument is not recognized */ 548 * changed and if argument is not recognized */
552 if (settings_opt_get_int ("beep_enable")) 549 if (settings_opt_get_int("beep_enable"))
553 scr_LogPrint (LPRINT_NORMAL, 550 scr_log_print(LPRINT_NORMAL,
554 "Beep on messages is enabled"); 551 "Beep on messages is enabled");
555 else 552 else
556 scr_LogPrint (LPRINT_NORMAL, 553 scr_log_print(LPRINT_NORMAL,
557 "Beep on messages is disabled"); 554 "Beep on messages is disabled");
558 } 555 }
559 556
560 /* Initialization */ 557 /* Initialization */
561 void beep_init (void) 558 static void beep_init (void)
562 { 559 {
563 /* Create completions */ 560 /* Create completions */
564 beep_cid = compl_new_category (); 561 beep_cid = compl_new_category();
565 if (beep_cid) { 562 if (beep_cid) {
566 compl_add_category_word (beep_cid, "enable"); 563 compl_add_category_word(beep_cid, "enable");
567 compl_add_category_word (beep_cid, "disable"); 564 compl_add_category_word(beep_cid, "disable");
568 } 565 }
569 /* Add command */ 566 /* Add command */
570 cmd_add ("beep", "", beep_cid, 0, do_beep, NULL); 567 cmd_add("beep", "", beep_cid, 0, do_beep, NULL);
571 /* Add handler 568 /* Add handler
572 * We are only interested in incoming message events 569 * We are only interested in incoming message events
573 */ 570 */
574 hk_add_handler (beep_hh, HOOK_MESSAGE_IN, NULL); 571 beep_hid = hk_add_handler(beep_hh, HOOK_POST_MESSAGE_IN,
572 G_PRIORITY_DEFAULT_IDLE, NULL);
575 } 573 }
576 574
577 /* Deinitialization */ 575 /* Deinitialization */
578 void beep_uninit (void) 576 void beep_uninit(void)
579 { 577 {
580 /* Unregister event handler */ 578 /* Unregister event handler */
581 hk_del_handler (beep_hh, NULL); 579 hk_del_handler(HOOK_POST_MESSAGE_IN, beep_hid);
582 /* Unregister command */ 580 /* Unregister command */
583 cmd_del ("beep"); 581 cmd_del("beep");
584 /* Give back completion handle */ 582 /* Give back completion handle */
585 if (beep_cid) 583 if (beep_cid)
586 compl_del_category (beep_cid); 584 compl_del_category(beep_cid);
587 } 585 }
588 586
589 module_info_t beep_info = { 587 module_info_t beep_info = {
590 .branch = "dev", 588 .branch = "dev",
591 .api = 1, 589 .api = 1,
590 .version = "0.0.1",
591 .description = "Simple beeper module\n"
592 " Recognizes option beep_enable\n"
593 " Provides command /beep",
592 .requires = NULL, 594 .requires = NULL,
593 .init = beep_init, 595 .init = beep_init,
594 .uninit = beep_uninit, 596 .uninit = beep_uninit,
595 .version = "0.0.1",
596 .description = "Simple beeper module\n"
597 "Recognizes option beep_enable\n"
598 "Provides command /beep",
599 .next = NULL, 597 .next = NULL,
600 } 598 }
601 599
602 /* The End */ 600 /* The End */
603 -------------------------------------------------------- 601 ------------------------------------------------------------------------
604 602
605 If you use CMake (as do I), corresponding CMakeLists.txt 603 If you use CMake (as do I), corresponding CMakeLists.txt
606 snippet: 604 snippet:
607 605
608 -------------------------------------------------------- 606 ------------------------------------------------------------------------
609 cmake_minimum_required(VERSION 2.6) 607 cmake_minimum_required(VERSION 2.6)
610 project(beep C) 608 project(beep C)
611 609
612 find_package(PkgConfig REQUIRED) 610 find_package(PkgConfig REQUIRED)
613 pkg_check_modules(MCABBER REQUIRED mcabber) 611 pkg_check_modules(MCABBER REQUIRED mcabber)
620 target_link_libraries(beep ${MCABBER_LIBRARIES) 618 target_link_libraries(beep ${MCABBER_LIBRARIES)
621 include_directories(${beep_SOURCE_DIR} 619 include_directories(${beep_SOURCE_DIR}
622 ${beep_BINARY_DIR}) 620 ${beep_BINARY_DIR})
623 621
624 install(TARGETS beep DESTINATION lib/mcabber) 622 install(TARGETS beep DESTINATION lib/mcabber)
625 -------------------------------------------------------- 623 ------------------------------------------------------------------------
626 624
627 =========================== 625 ===========================
628 626
629 Example: dependencies 627 Example: dependencies
630 628
631 =========================== 629 ===========================
632 630
633 I will not provide here a complete example of two 631 I will not provide here a complete example of two modules, one of which
634 modules, one of which depends on other, only some 632 depends on other, only some use cases.
635 use cases. 633
636 634 Info struct for module, that depends on two other modules:
637 Info struct for module, that depends on two other 635
638 modules: 636 ------------------------------------------------------------------------
639
640 --------------------------------------------------------
641 #include <mcabber/modules.h> 637 #include <mcabber/modules.h>
642 638
643 const gchar *a_deps[] = { "b", "c", NULL }; 639 const gchar *a_deps[] = { "b", "c", NULL };
644 640
645 module_info_t info_a = { 641 module_info_t info_a = {
646 .branch = "dev", 642 .branch = "dev",
647 .api = 1, 643 .api = 1,
644 .version = NULL,
645 .description = NULL,
648 .requires = a_deps, 646 .requires = a_deps,
649 .init = a_init, 647 .init = a_init,
650 .uninit = a_uninit, 648 .uninit = a_uninit,
651 .version = NULL,
652 .description = NULL,
653 .next = NULL, 649 .next = NULL,
654 }; 650 };
655 -------------------------------------------------------- 651 ------------------------------------------------------------------------
656 652
657 If your module needs to "authenticate" mcabber version 653 If your module needs to "authenticate" mcabber version too, this can be
658 too, this can be done in g_module_check_init: 654 done in g_module_check_init:
659 655
660 -------------------------------------------------------- 656 ------------------------------------------------------------------------
661 #include <glib.h> 657 #include <glib.h>
662 #include <gmodule.h> 658 #include <gmodule.h>
663 659
664 #include <mcabber/main.h> 660 #include <mcabber/main.h>
665 #include <mcabber/modules.h> 661 #include <mcabber/modules.h>
677 error = "Incompatible mcabber version"; 673 error = "Incompatible mcabber version";
678 674
679 g_free (ver); 675 g_free (ver);
680 return error; 676 return error;
681 } 677 }
682 -------------------------------------------------------- 678 ------------------------------------------------------------------------
683 679
684 Also you can use glib check_init routine to modify 680 Also you can use glib check_init routine to modify module information,
685 module information, that will be checked by mcabber, 681 that will be checked by mcabber, e.g. if you want your module to always
686 eg. if you want your module to always pass mcabber 682 pass mcabber version check, you can assign branch information, obtained
687 version check, you can assign branch information, 683 from mcabber_... variables to corresponding fields in your struct.
688 obtained from mcabber_... variables to corresponding 684 Or you can modify your module's dependencies, though direct
689 fields in your struct. 685 module_load() will have the same effect, and can be used for optional
690 Or you can modify your module's dependencies, though 686 dependencies, that your module can still work without.
691 direct module_load() will have the same effect, and 687
692 can be used for optional dependencies, that your module 688 Note: remember, that g_module_check_init will be always called, even if
693 can still work without. 689 later the module will not pass checks, thus:
694
695 Note: remember, that g_module_check_init will be always
696 called, even if later module will not pass checks, thus:
697 - do not use functions from other modules there; 690 - do not use functions from other modules there;
698 - provide g_module_unload to undo anything, check_init 691 - provide g_module_unload to undo anything, check_init has done.
699 has done.
700 692
701 ============== 693 ==============
702 694
703 Further 695 Further
704 696
705 ============== 697 ==============
706 698
707 As mcabber now uses glib mainloop, you can use glib's 699 As mcabber now uses glib mainloop, you can use glib's event sources, for
708 event sources, for example, fifo reading already uses 700 example, fifo reading already uses GIOChannels for non-blocking IO.
709 GIOChannels for non-blocking IO. 701
710 702 You can extend XMPP part of mcabber functionality by providing lm
711 You can extend xmpp part of mcabber functionality by 703 message handlers with high priority and allowing unhandled by your
712 providing lm message handlers with high priority and 704 handler messages be taken care by mcabber's handlers on normal priority
713 allowing unhandled by your handler messages be taken 705 level. This is where you may need to modify set of advertised supported
714 care by mcabber's handlers on normal priority level. 706 disco features.
715 This is where you may need to modify set of advertised 707
716 supported disco features. 708 Many useful examples can be found in my modules, that can be found at
717 709 http://isbear.unixzone.org.ua/source.
718 Many useful examples can be found in my modules, that 710
719 can be found at http://isbear.unixzone.org.ua/source. 711 If you think, that your module needs to change something, hardcoded in
720 712 current implementation - feel free to mail me or join mcabber's MUC room
721 If you think, that your module needs to change 713 and discuss it - for now we have only implemented things, that we have
722 something, hardcoded in current implementation - feel 714 found necessary for our own modules.
723 free to mail me or join mcabber's MUC room and 715
724 discuss this - for now I have only implemented things, 716 Also I am not native English speaker, so, if you find some errors or
725 that I found necessary for written by me modules. 717 non-natural constructs in this howto, please, inform me (I will be glad,
726 718 if you also provide a more suitable version of text in question).
727 Also I am not native English speaker, so, if you find
728 some errors or non-natural constructs in this howto,
729 please, inform me (I will be glad, if you also provide
730 a more suitable version of text in question).
731 719
732 -- Myhailo Danylenko 720 -- Myhailo Danylenko
733 -- mailto:isbear@ukrpost.net 721 -- mailto:isbear@ukrpost.net
734 -- xmpp:isbear@unixzone.org.ua 722 -- xmpp:isbear@unixzone.org.ua
735 -- Sat, 13 Mar 2010 12:18:17 +0200 723 -- Sat, 27 Mar 2010 13:30:00 +0100
736 724