Mercurial > ~mikael > mcabber > hg
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 |