Mercurial > hg
comparison mcabber/doc/HOWTO_modules.txt @ 1735:5093b5ca1572
New modules loading scheme
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Thu, 04 Mar 2010 13:03:20 +0200 |
parents | b09f82f61745 |
children | 4a7c7900f600 |
comparison
equal
deleted
inserted
replaced
1734:eae4a2637f2c | 1735:5093b5ca1572 |
---|---|
3 | 3 |
4 Mcabber module writing brief howto | 4 Mcabber module writing brief howto |
5 | 5 |
6 =========================================== | 6 =========================================== |
7 | 7 |
8 Mcabber loads modules via glib's GModule. | 8 To obtain information on module mcabber uses struct |
9 | 9 module_info_t, that module should provide in public |
10 Thus, in your module you can provide functions | 10 variable with name info_<modulename>. If module name |
11 | 11 contains any extra symbols except [a-z0-9_] they should |
12 -------------------------------------------------------- | 12 be replaced with '_'. |
13 | |
14 -------------------------------------------------------- | |
15 #include <mcabber/modules.h> | |
16 | |
17 typedef void (*module_init_t)(void); | |
18 typedef void (*module_uninit_t)(void); | |
19 | |
20 typedef struct { | |
21 const gchar *mcabber_version; | |
22 module_init_t init; | |
23 module_uninit_t uninit; | |
24 const gchar **requires; | |
25 } module_info_t; | |
26 -------------------------------------------------------- | |
27 | |
28 Callbacks init and uninit will be called after module | |
29 and it's dependencies loading. 'requires' should contain | |
30 NULL-terminated list of module names, that should be loaded | |
31 before this. 'mcabber_version' is required and should contain | |
32 mcabber version, that this module is designed to work with. | |
33 Three other fields may be NULL. | |
34 | |
35 To load modules, mcabber uses glib's GModule, thus, in your | |
36 module you can also use functions | |
37 | |
38 -------------------------------------------------------- | |
39 #include <glib.h> | |
40 #include <gmodule.h> | |
41 | |
13 const gchar* g_module_check_init (GModule *module); | 42 const gchar* g_module_check_init (GModule *module); |
14 void g_module_unload (GModule *module); | 43 void g_module_unload (GModule *module); |
15 -------------------------------------------------------- | 44 -------------------------------------------------------- |
16 | 45 |
17 to do something when module is loaded and unloaded. On | 46 to do something before any version/dependency checks will |
18 success g_module_check_init should return NULL, and | 47 be performed when module is loaded/unloaded. On success |
19 error message otherwise. | 48 g_module_check_init should return NULL, and error message |
49 otherwise. | |
20 | 50 |
21 As module is loaded, you can use mcabber functions, | 51 As module is loaded, you can use mcabber functions, |
22 declared in mcabber's header files (though you should | 52 declared in mcabber's header files (though you should |
23 consider, that they may change their calling conventions | 53 consider, that they may change their calling conventions |
24 some day). | 54 some day). |
25 | 55 |
26 I will not explain them all, there are too much of | 56 I will not explain them all, there are too much of |
27 them, but will provide description for those, provided | 57 them, but will provide description for those, provided |
28 especially for module writers. | 58 especially for module writers. |
59 | |
60 -------------------------------------------------------- | |
61 #include <mcabber/modules.h> | |
62 | |
63 const gchar *module_load (const gchar *name, | |
64 gboolean manual, | |
65 gboolean force); | |
66 const gchar *module_unload (const gchar *name, | |
67 gboolean manual, | |
68 gboolean force); | |
69 -------------------------------------------------------- | |
70 | |
71 These functions load and unload modules respectively. | |
72 You can use them to handle optional dependencies. What | |
73 happens, when module is loaded: | |
74 - check if module is present, and if present just | |
75 increase it's reference count | |
76 - load .so via glib (and call g_module_check_init, if | |
77 present) | |
78 - check for information structure presence | |
79 - check target mcabber version compatibility | |
80 - load modules, that this module requires (note, that | |
81 dependency problems will be reported as error | |
82 invariably, force flag have no effect on this check) | |
83 - module placed into a list of modules | |
84 - module init routine is called | |
85 And when unloaded: | |
86 - check if module is present | |
87 - decrease reference count, if it is not zero, return | |
88 - run module uninit routine | |
89 - unload modules, that were loaded as dependencies for | |
90 this | |
91 - remove from modules list | |
92 They return error message or NULL in case of success. | |
93 'manual' flag indicates, that module will be loaded by | |
94 direct user request. It serves the purpose of tracking | |
95 user and automatic references (user can have only one). | |
96 'force' flag on module loading causes mcabber to ignore | |
97 most of the loading errors. On unload it forces | |
98 unloading even if reference count is not zero. | |
29 | 99 |
30 -------------------------------------------------------- | 100 -------------------------------------------------------- |
31 #include <mcabber/commands.h> | 101 #include <mcabber/commands.h> |
32 | 102 |
33 void cmd_add (const char *name, const char *help, | 103 void cmd_add (const char *name, const char *help, |
198 -------------------------------------------------------- | 268 -------------------------------------------------------- |
199 | 269 |
200 Now, compile this file (hello.c) with | 270 Now, compile this file (hello.c) with |
201 | 271 |
202 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \ | 272 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \ |
203 gmodule-2.0` -c hello.c | 273 gmodule-2.0 mcabber` -c hello.c |
204 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ | 274 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ |
205 `pkg-config --libs glib-2.0 gmodule-2.0` -o libhello.la \ | 275 `pkg-config --libs glib-2.0 gmodule-2.0 mcabber` \ |
206 hello.lo | 276 -o libhello.la hello.lo |
207 | 277 |
208 (you should substitute /usr/lib/mcabber to directory, where | 278 (you should substitute /usr/lib/mcabber to directory, where |
209 your modules are located) and then install obtained module with | 279 your modules are located) and then install obtained module with |
210 | 280 |
211 libtool --mode=install install libhello.la \ | 281 libtool --mode=install install libhello.la \ |
214 Note, that you, most likely need not run suggested by libtool | 284 Note, that you, most likely need not run suggested by libtool |
215 finish action, as we're working with module object, not system- | 285 finish action, as we're working with module object, not system- |
216 wide library, but maybe some systems require that. | 286 wide library, but maybe some systems require that. |
217 | 287 |
218 Now, set modules_dir mcabber variable to point to your modules | 288 Now, set modules_dir mcabber variable to point to your modules |
219 dir, and try to run /load hello. If all goes well, you should | 289 dir, and try to run /module -f load hello. If all goes well, |
220 see in status buffer message "Hello World!". | 290 you should see in status buffer message "Hello World!" (as |
221 Now unload module by running command /unload hello, that | 291 well as some complaints, as we forced module loading). |
222 should bring up message "Bye, World!". | 292 Now unload module by running command /module unload hello, |
293 that should bring up message "Bye, World!". | |
223 | 294 |
224 That's it, you just created very simple dynamically loadable | 295 That's it, you just created very simple dynamically loadable |
225 mcabber module. | 296 mcabber module. But, as you noticed, it needs force to be |
297 loaded. Now, let's add information structure, that mcabber | |
298 wants. | |
299 | |
300 ========================== | |
301 | |
302 Example: info struct | |
303 | |
304 ========================== | |
305 | |
306 -------------------------------------------------------- | |
307 #include <mcabber/logprint.h> | |
308 /* module_info_t definition */ | |
309 #include <mcabber/modules.h> | |
310 | |
311 /* Print something on module loading */ | |
312 void hello_init (void) | |
313 { | |
314 scr_LogPrint (LPRINT_NORMAL, "Hello, World!"); | |
315 } | |
316 | |
317 /* ... and unloading */ | |
318 void hello_uninit (void) | |
319 { | |
320 scr_LogPrint (LPRINT_NORMAL, "Bye, World!"); | |
321 } | |
322 | |
323 module_info_t info_hello = { | |
324 .mcabber_version = "0.10.0", | |
325 .requires = NULL, | |
326 .init = hello_init, | |
327 .uninit = hello_uninit, | |
328 }; | |
329 | |
330 /* The End */ | |
331 -------------------------------------------------------- | |
332 | |
333 Here we now do not use glib nor gmodule, so, we can omit | |
334 them in compilation lines: | |
335 | |
336 libtool --mode=compile gcc `pkg-config --cflags mcabber` \ | |
337 -c hello.c | |
338 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ | |
339 `pkg-config --libs mcabber` -o libhello.la hello.lo | |
340 | |
341 Again compile it, copy, and try to load, now without -f | |
342 flag. As you may notice, when loading previous example, | |
343 mcabber first printed "Hello, World!", and only then | |
344 complaint about module not having information struct. | |
345 That's because g_module_check_init is called right | |
346 after module loading, before mcabber even have a chance | |
347 to look at module, while .init from info struct is | |
348 called afterwards by mcabber itself. You can try to | |
349 introduce some error (eg too high or missing target | |
350 mcabber version) and see the difference. | |
226 | 351 |
227 ======================= | 352 ======================= |
228 | 353 |
229 Example: command | 354 Example: command |
230 | 355 |
231 ======================= | 356 ======================= |
232 | 357 |
233 Now, let's allow our module to do some real work. | 358 Now, let's allow our module to do some real work. |
234 | 359 |
235 -------------------------------------------------------- | 360 -------------------------------------------------------- |
236 #include <glib.h> | |
237 #include <gmodule.h> | |
238 | |
239 #include <mcabber/logprint.h> | 361 #include <mcabber/logprint.h> |
240 #include <mcabber/commands.h> | 362 #include <mcabber/commands.h> |
363 #include <mcabber/modules.h> | |
241 | 364 |
242 /* Handler for command */ | 365 /* Handler for command */ |
243 void do_hello (char *args) | 366 void do_hello (char *args) |
244 { | 367 { |
245 /* args contains command line with command | 368 /* args contains command line with command |
247 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", | 370 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", |
248 *args != '\0' ? args : "World"); | 371 *args != '\0' ? args : "World"); |
249 } | 372 } |
250 | 373 |
251 /* Register command */ | 374 /* Register command */ |
252 const gchar* g_module_check_init (GModule *module) | 375 void hello_init (void) |
253 { | 376 { |
254 cmd_add ("hello", "", 0, 0, do_hello, NULL); | 377 cmd_add ("hello", "", 0, 0, do_hello, NULL); |
255 return NULL; | |
256 } | 378 } |
257 | 379 |
258 /* Unregister command */ | 380 /* Unregister command */ |
259 void g_module_unload (GModule *module) | 381 void hello_uninit (void) |
260 { | 382 { |
261 cmd_del ("hello"); | 383 cmd_del ("hello"); |
384 } | |
385 | |
386 module_info_t hello_info = { | |
387 .mcabber_version = "0.10.0", | |
388 .requires = NULL, | |
389 .init = hello_init, | |
390 .uninit = hello_uninit, | |
262 } | 391 } |
263 | 392 |
264 /* The End */ | 393 /* The End */ |
265 -------------------------------------------------------- | 394 -------------------------------------------------------- |
266 | 395 |
279 Now let's investigate how to provide custom completion to | 408 Now let's investigate how to provide custom completion to |
280 your commands. You can as well use built-in completions, | 409 your commands. You can as well use built-in completions, |
281 their IDs are listed in compl.h. | 410 their IDs are listed in compl.h. |
282 | 411 |
283 -------------------------------------------------------- | 412 -------------------------------------------------------- |
284 #include <glib.h> | |
285 #include <gmodule.h> | |
286 | |
287 #include <mcabber/logprint.h> | 413 #include <mcabber/logprint.h> |
288 #include <mcabber/commands.h> | 414 #include <mcabber/commands.h> |
415 #include <mcabber/modules.h> | |
289 #include <mcabber/compl.h> | 416 #include <mcabber/compl.h> |
290 | 417 |
291 static guint hello_cid = 0; | 418 static guint hello_cid = 0; |
292 | 419 |
293 /* hello command handler */ | 420 /* hello command handler */ |
301 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", | 428 scr_LogPrint (LPRINT_NORMAL, "Hello, %s!", |
302 *args != '\0' ? args : "World"); | 429 *args != '\0' ? args : "World"); |
303 } | 430 } |
304 | 431 |
305 /* Initialization */ | 432 /* Initialization */ |
306 const gchar* g_module_check_init (GModule *module) | 433 void hello_init (void) |
307 { | 434 { |
308 /* Obtain handle for our completion | 435 /* Obtain handle for our completion |
309 * category */ | 436 * category */ |
310 hello_cid = compl_new_category (); | 437 hello_cid = compl_new_category (); |
311 if (hello_cid) | 438 if (hello_cid) |
313 * completion list */ | 440 * completion list */ |
314 compl_add_category_word (hello_cid, | 441 compl_add_category_word (hello_cid, |
315 "World"); | 442 "World"); |
316 cmd_add ("hello", "", hello_cid, 0, do_hello, | 443 cmd_add ("hello", "", hello_cid, 0, do_hello, |
317 NULL); | 444 NULL); |
318 return NULL; | |
319 } | 445 } |
320 | 446 |
321 /* Deinitialization */ | 447 /* Deinitialization */ |
322 void g_module_unload (GModule *module) | 448 void hello_uninit (void) |
323 { | 449 { |
324 /* Give back category handle */ | 450 /* Give back category handle */ |
325 if (hello_cid) | 451 if (hello_cid) |
326 compl_del_category (hello_cid); | 452 compl_del_category (hello_cid); |
327 cmd_del ("hello"); | 453 cmd_del ("hello"); |
454 } | |
455 | |
456 module_info_t hello_info = { | |
457 .mcabber_version = "0.10.0", | |
458 .requires = NULL, | |
459 .init = hello_init, | |
460 .uninit = hello_uninit, | |
328 } | 461 } |
329 | 462 |
330 /* The End */ | 463 /* The End */ |
331 -------------------------------------------------------- | 464 -------------------------------------------------------- |
332 | 465 |
347 to do this? I am not satisfied with default mcabber's | 480 to do this? I am not satisfied with default mcabber's |
348 builtin beeper flexibility. I wanted beeping on any | 481 builtin beeper flexibility. I wanted beeping on any |
349 muc conference message, not just ones, directed to me. | 482 muc conference message, not just ones, directed to me. |
350 | 483 |
351 -------------------------------------------------------- | 484 -------------------------------------------------------- |
352 #include <glib.h> | |
353 #include <gmodule.h> | |
354 #include <string.h> | 485 #include <string.h> |
355 | 486 |
356 #include <mcabber/logprint.h> | 487 #include <mcabber/logprint.h> |
357 #include <mcabber/commands.h> | 488 #include <mcabber/commands.h> |
358 #include <mcabber/compl.h> | 489 #include <mcabber/compl.h> |
359 #include <mcabber/hooks.h> | 490 #include <mcabber/hooks.h> |
360 #include <mcabber/screen.h> | 491 #include <mcabber/screen.h> |
361 #include <mcabber/settings.h> | 492 #include <mcabber/settings.h> |
493 #include <mcabber/module.h> | |
362 | 494 |
363 static guint beep_cid = 0; | 495 static guint beep_cid = 0; |
364 | 496 |
365 /* Event handler */ | 497 /* Event handler */ |
366 void beep_hh (guint32 hid, hk_arg_t *args, gpointer userdata) | 498 void beep_hh (guint32 hid, hk_arg_t *args, gpointer userdata) |
398 scr_LogPrint (LPRINT_NORMAL, | 530 scr_LogPrint (LPRINT_NORMAL, |
399 "Beep on messages is disabled"); | 531 "Beep on messages is disabled"); |
400 } | 532 } |
401 | 533 |
402 /* Initialization */ | 534 /* Initialization */ |
403 const gchar* g_module_check_init (GModule *module) | 535 void beep_init (void) |
404 { | 536 { |
405 /* Create completions */ | 537 /* Create completions */ |
406 beep_cid = compl_new_category (); | 538 beep_cid = compl_new_category (); |
407 if (beep_cid) { | 539 if (beep_cid) { |
408 compl_add_category_word (beep_cid, "enable"); | 540 compl_add_category_word (beep_cid, "enable"); |
412 cmd_add ("beep", "", beep_cid, 0, do_beep, NULL); | 544 cmd_add ("beep", "", beep_cid, 0, do_beep, NULL); |
413 /* Add handler | 545 /* Add handler |
414 * We are only interested in incoming message events | 546 * We are only interested in incoming message events |
415 */ | 547 */ |
416 hk_add_handler (beep_hh, HOOK_MESSAGE_IN, NULL); | 548 hk_add_handler (beep_hh, HOOK_MESSAGE_IN, NULL); |
417 return NULL; | |
418 } | 549 } |
419 | 550 |
420 /* Deinitialization */ | 551 /* Deinitialization */ |
421 void g_module_unload (GModule *module) | 552 void beep_uninit (void) |
422 { | 553 { |
423 /* Unregister event handler */ | 554 /* Unregister event handler */ |
424 hk_del_handler (beep_hh, NULL); | 555 hk_del_handler (beep_hh, NULL); |
425 /* Unregister command */ | 556 /* Unregister command */ |
426 cmd_del ("beep"); | 557 cmd_del ("beep"); |
427 /* Give back completion handle */ | 558 /* Give back completion handle */ |
428 if (beep_cid) | 559 if (beep_cid) |
429 compl_del_category (beep_cid); | 560 compl_del_category (beep_cid); |
430 } | 561 } |
431 | 562 |
563 module_info_t beep_info = { | |
564 .mcabber_version = "0.10.0", | |
565 .requires = NULL, | |
566 .init = beep_init, | |
567 .uninit = beep_uninit, | |
568 } | |
569 | |
432 /* The End */ | 570 /* The End */ |
433 -------------------------------------------------------- | 571 -------------------------------------------------------- |
434 | |
435 Note, that to compile this we also need to add loudmouth-1.0 | |
436 to pkg-config command line, so, you will have something like | |
437 | |
438 libtool --mode=compile gcc `pkg-config --cflags glib-2.0 \ | |
439 gmodule-2.0 loudmouth-1.0` -c beep.c | |
440 libtool --mode=link gcc -module -rpath /usr/lib/mcabber/ \ | |
441 `pkg-config --cflags glib-2.0 gmodule-2.0` -o libbeep.la \ | |
442 beep.lo | |
443 libtool --mode=install install libbeep.la \ | |
444 /usr/lib/mcabber/libbeep.la | |
445 | 572 |
446 If you use CMake (as do I), corresponding CMakeLists.txt | 573 If you use CMake (as do I), corresponding CMakeLists.txt |
447 snippet: | 574 snippet: |
448 | 575 |
449 -------------------------------------------------------- | 576 -------------------------------------------------------- |
450 cmake_minimum_required(VERSION 2.6) | 577 cmake_minimum_required(VERSION 2.6) |
451 project(beep C) | 578 project(beep C) |
452 | 579 |
453 set(MCABBER_INCLUDE_DIR "/usr/include" CACHE FILEPATH | |
454 "Path to mcabber headers") | |
455 | |
456 find_package(PkgConfig REQUIRED) | 580 find_package(PkgConfig REQUIRED) |
457 pkg_check_modules(GLIB REQUIRED glib-2.0) | 581 pkg_check_modules(MCABBER REQUIRED mcabber) |
458 pkg_check_modules(GMODULE REQUIRED gmodule-2.0) | |
459 pkg_check_modules(LM REQUIRED loudmouth-1.0) | |
460 # this one should be before any target definitions | 582 # this one should be before any target definitions |
461 link_directories(${GLIB_LIBRARY_DIRS} | 583 link_directories(${MCABBER_LIBRARY_DIRS}) |
462 ${GMODULE_LIBRARY_DIRS}) | |
463 | 584 |
464 add_library(beep MODULE beep.c) | 585 add_library(beep MODULE beep.c) |
465 | 586 |
466 include_directories(SYSTEM ${GLIB_INCLUDE_DIRS} | 587 include_directories(SYSTEM ${MCABBER_INCLUDE_DIRS}) |
467 ${GMODULE_INCLUDE_DIRS} | 588 target_link_libraries(beep ${MCABBER_LIBRARIES) |
468 ${LM_INCLUDE_DIRS} | |
469 ${MCABBER_INCLUDE_DIR}) | |
470 target_link_libraries(beep ${GLIB_LIBRARIES} | |
471 ${GMODULE_LIBRARIES}) | |
472 include_directories(${beep_SOURCE_DIR} | 589 include_directories(${beep_SOURCE_DIR} |
473 ${beep_BINARY_DIR}) | 590 ${beep_BINARY_DIR}) |
474 | 591 |
475 install(TARGETS beep DESTINATION lib/mcabber) | 592 install(TARGETS beep DESTINATION lib/mcabber) |
476 -------------------------------------------------------- | 593 -------------------------------------------------------- |
594 | |
595 =========================== | |
596 | |
597 Example: dependencies | |
598 | |
599 =========================== | |
600 | |
601 I will not provide here a complete example of two | |
602 modules, one of which depends on other, only some | |
603 use cases. | |
604 | |
605 Info struct for module, that depends on two other | |
606 modules: | |
607 | |
608 -------------------------------------------------------- | |
609 #include <mcabber/modules.h> | |
610 | |
611 const gchar *a_deps[] = { "b", "c", NULL }; | |
612 | |
613 module_info_t info_a = { | |
614 .mcabber_version = "0.10.0", | |
615 .requires = a_deps, | |
616 .init = a_init, | |
617 .uninit = a_uninit, | |
618 }; | |
619 -------------------------------------------------------- | |
620 | |
621 If your module needs to "authenticate" mcabber version | |
622 too, this can be done in g_module_check_init: | |
623 | |
624 -------------------------------------------------------- | |
625 #include <glib.h> | |
626 #include <gmodule.h> | |
627 | |
628 #include <mcabber/main.h> | |
629 | |
630 const gchar *g_module_check_init (GModule *module) | |
631 { | |
632 char *ver = mcabber_version (); | |
633 | |
634 // ver now contains version in format | |
635 // X.X.X[-xxx][ (XXXXXXXXX)] | |
636 if (...) | |
637 return "Incompatible mcabber version"; | |
638 | |
639 g_free (ver); | |
640 return NULL; | |
641 } | |
642 -------------------------------------------------------- | |
643 | |
644 Also you can use glib check_init routine to modify | |
645 module information, that will be checked by mcabber, | |
646 eg. if you want your module to always pass mcabber | |
647 version check, you can assign version, obtained from | |
648 mcabber_version() to corresponding field in your struct. | |
649 Or you can modify your module's dependencies, though | |
650 direct module_load() will have the same effect, and | |
651 can be used for optional dependencies, that your module | |
652 can work without. | |
653 | |
654 Note: remember, that g_module_check_init will be always | |
655 called, even if later module will not pass checks, thus: | |
656 - do not use functions from other modules there; | |
657 - provide g_module_unload to undo anything, check_init | |
658 has done. | |
477 | 659 |
478 ============== | 660 ============== |
479 | 661 |
480 Further | 662 Further |
481 | 663 |
507 a more suitable version of text in question). | 689 a more suitable version of text in question). |
508 | 690 |
509 -- Myhailo Danylenko | 691 -- Myhailo Danylenko |
510 -- mailto:isbear@ukrpost.net | 692 -- mailto:isbear@ukrpost.net |
511 -- xmpp:isbear@unixzone.org.ua | 693 -- xmpp:isbear@unixzone.org.ua |
512 -- Mon, 18 Jan 2010 15:52:40 +0200 | 694 -- Thu, 04 Mar 2010 09:32:38 +0200 |
513 | 695 |