Trac is being migrated to new services! Issues can be found in our new YouTrack instance and WIKI pages can be found on our website.

Changes between Initial Version and Version 1 of CHowTo/BasicPluginHowto


Ignore:
Timestamp:
Jul 15, 2007, 10:43:47 PM (17 years ago)
Author:
John Bailey
Comment:

Migrate the C Plugin How-To from the doxygen stuff to the wiki

Legend:

Unmodified
Added
Removed
Modified
  • CHowTo/BasicPluginHowto

    v1 v1  
     1= C Plugin How-To =
     2
     3[[TOC(inline,noheading)]]
     4
     5== Introduction ==
     6This How-To document is designed to provide a basic overview of writing a plugin for Pidgin, Finch, and/or libpurple in our native language, C.  We'll start with a basic "Hello, World!" plugin and go from there.  Let's dig in, shall we?
     7
     8As we've already stated, C plugins are native plugins.  They have complete access to all of the API provided by Pidgin, Finch, and libpurple.  This means that a C plugin can do basically anything it wants to do.  All our protocol plugins and our loader plugins (Mono, Perl, and Tcl) are written in C.
     9
     10== Getting Started ==
     11To develop a plugin, you need to have an installation of libpurple that includes development headers, at minimum.  To write a plugin that includes features requiring Pidgin or Finch functionality, you will need to also have Pidgin or Finch installed, complete with development headers.
     12
     13It will actually be easier for the first-time plugin author to install the development dependencies and then have a copy of the corresponding Pidgin source tarball.  For example, if you're running 2.0.2 on a Debian Sid (unstable) or testing system, you would do the following:
     14 {{{
     15# apt-get build-dep pidgin
     16 }}}
     17
     18Similarly, on a Fedora system, you would do this:
     19 {{{
     20# yum install pidgin-dev
     21 }}}
     22
     23People using Windows should read the [wiki:BuildingWinPidgin Building Windows Pidgin] page on this wiki to set up the necessary build environment.
     24
     25The command above for Debian systems will install only the build dependencies of Pidgin, Finch, and libpurple.  The command shown for Fedora will install all of those, as well as the development headers for libpurple, Finch, and Pidgin.
     26
     27Once you have installed these dependencies, download a source tarball.  You will want to use the source tarball that matches the version of Pidgin you are running, as it's generally a good idea to compile against the same version you run your plugins with.
     28
     29For the purposes of this How-To, we will assume the following:
     30  * You understand the concept of `~` being your home directory
     31  * You are working within the directory `~/development`.
     32  * You are using Pidgin 2.0.2.
     33
     34If you are using a different version of Pidgin, simply replace references to 2.0.2 with whichever version you are using.  If you wish to use a path other than `~/development` (for example, Windows users correctly following the instructions will have `~/win32-dev` and their Pidgin tarball will be extracted in `~` to match), simply treat our references to `~/development` as a reference to the directory in which you extract the tarball.
     35
     36Now, extract the tarball:
     37 {{{
     38~/development $ tar -jxvf pidgin-2.0.2.tar.bz2
     39 }}}
     40
     41Next, change to the `pidgin-2.0.2` directory.  If you are using Windows, run `make -f Makefile.mingw` to build Pidgin.  If you are using another platform, run `./configure` and then `make` after the configuration is complete.  You may wish to customize the arguments to the configure script, which is beyond the scope of this document.  '''If you are using Windows, DO __''NOT''__ under any circumstances use the configure script!  Any executables or .dll files derived from a Pidgin tree that has been configured via the configure script WILL __''NOT''__ WORK outside of Cygwin, if they will build at all.'''
     42
     43Now that everything is compiled, we can get to the real work of developing your first plugin, but first a point to be well aware of.  ''ALL'' C plugins must define `PURPLE_PLUGINS` by using the `#define` preprocessor directive.  This definition ''must'' occur before including any libpurple, Pidgin, or Finch header files.  Failure to have `#define PURPLE_PLUGINS` in your source file leads to very strange errors that are difficult to diagnose.  Just don't forget to do it!
     44
     45== Hello, World! ==
     46Every programming language's tutorial has its own Hello, World! first application.  Why should our plugin tutorial be any different?  Let's dig in.  First, go to `~/development/pidgin-2.0.2/libpurple/plugins` and create the file helloworld.c with your favorite editor.  The contents of the file will be this:
     47 {{{
     48#!c
     49#define PURPLE_PLUGINS
     50
     51#include <glib.h>
     52
     53#include "notify.h"
     54#include "plugin.h"
     55#include "version.h"
     56
     57static gboolean
     58plugin_load(PurplePlugin *plugin) {
     59    purple_notify_message(plugin, PURPLE_NOTIFY_MSG_INFO, "Hello World!",
     60                        "This is the Hello World! plugin :)", NULL, NULL, NULL);
     61
     62    return TRUE;
     63}
     64
     65static PurplePluginInfo info = {
     66    PURPLE_PLUGIN_MAGIC,
     67    PURPLE_MAJOR_VERSION,
     68    PURPLE_MINOR_VERSION,
     69    PURPLE_PLUGIN_STANDARD,
     70    NULL,
     71    0,
     72    NULL,
     73    PURPLE_PRIORITY_DEFAULT,
     74
     75    "core-hello_world",
     76    "Hello World!",
     77    "1.1",
     78
     79    "Hello World Plugin",         
     80    "Hello World Plugin",         
     81    NULL,                         
     82    "http://helloworld.tld",     
     83   
     84    plugin_load,                   
     85    NULL,                         
     86    NULL,                         
     87                                   
     88    NULL,                         
     89    NULL,                         
     90    NULL,                       
     91    NULL,                   
     92    NULL,                         
     93    NULL,                         
     94    NULL,                         
     95    NULL                           
     96};                               
     97   
     98static void                       
     99init_plugin(PurplePlugin *plugin)
     100{                                 
     101}
     102
     103PURPLE_INIT_PLUGIN(hello_world, init_plugin, info)
     104 }}}
     105
     106So, what does all this mean?  Well, let's go through this with a line by line analysis.  The `#define PURPLE_PLUGINS` is required, as we mentioned above.
     107
     108Next, we include glib.h, the header for !GLib.  We do this mainly for the `gboolean` typedef and the other !GLib wrappers for the standard C types.  After this, we include `notify.h`, `plugin.h`, and `version.h`.  The `plugin.h` header includes definitions that all plugins need, such as the type `PurplePluginInfo` and the macros `PURPLE_PLUGIN_MAGIC` and `PURPLE_INIT_PLUGIN()`.
     109
     110The `version.h` header defines `PURPLE_MAJOR_VERSION` and `PURPLE_MINOR_VERSION`.  There's not much that you need to know about anything in `version.h`, except that they are required and will prevent your plugin from crashing Pidgin if it's probed by an older or newer Pidgin, such as 1.5.1 or 3.0.0 (which of course does not yet exist at the time of this writing), where things are different and your plugin doesn't know about the differences.
     111
     112The `notify.h` header defines a bunch of notification functions.  For the scope of this part of the tutorial, it's not important to know anything about the contents of the file.  We'll use it for one function call to prove that our plugin works.  Later sections of the tutorial will come back to the notify API and flesh it out a bit more for readers.
     113
     114Moving on, the `plugin_load` function is defined.  This is not a required function to implement, but if you choose to implement it, libpurple will call the function when the plugin is loaded.  You can use it to initialize any global variables in your source file, to connect to signals, and so on.  Here, we'll use it just to display a message by calling a function from `notify.h`.
     115
     116Next in our source file we have the declaration and initialization of a global variable.  We called this variable info, and it is of the type `PurplePluginInfo`.  Every plugin is required to declare and initialize a variable of this type in order to tell libpurple about the plugin.  This next code snippet is the exact same struct declaration and initialization from the snippet above, but with some comments included to explain the function of each member of the struct.
     117 {{{
     118#!c
     119static PurplePluginInfo info = {
     120    PURPLE_PLUGIN_MAGIC,    /* Plugin magic, this must always be
     121                               PURPLE_PLUGIN_MAGIC.*/
     122
     123    PURPLE_MAJOR_VERSION,   /* This is also defined in libpurple.  It helps
     124                               libpurple's plugin system determine which version
     125                               of libpurple this plugin was compiled for, and
     126                               whether loading it will cause problems. */
     127
     128    PURPLE_MINOR_VERSION,   /* See previous */
     129
     130    PURPLE_PLUGIN_STANDARD, /* PurplePluginType: There are 4 different values for
     131                               this field.  The first is PURPLE_PLUGIN_UNKNOWN,
     132                               which should not be used.  The second is
     133                               PURPLE_PLUGIN_STANDARD; this is the value most
     134                               plugins will use. Next, we have PURPLE_PLUGIN_LOADER;
     135                               this is the type you want to use if your plugin will
     136                               make it possible to load non-native plugins.  For
     137                               example, the Perl and Tcl loader plugins are of this
     138                               type.  Last, we have PURPLE_PLUGIN_PROTOCOL.  If your
     139                               plugin is going to allow the user to connect to
     140                               another network, this is the type you'd want to use. */
     141
     142    NULL,                   /* This field is the UI requirement.  If you're writing
     143                               a core plugin, this must be NULL and the plugin must
     144                               not contain any UI code.  If you're writing a Pidgin
     145                               plugin, you need to use PIDGIN_PLUGIN_TYPE.  If you
     146                               are writing a Finch plugin, you would use
     147                               FINCH_PLUGIN_TYPE.*/
     148
     149    0,                      /* This field is for plugin flags.  Currently, the only
     150                               flag available to plugins is invisible
     151                               (PURPLE_PLUGIN_FLAG_INVISIBLE). It causes the plugin
     152                               NOT to appear in the list of plugins. */
     153
     154    NULL,                   /* This is a GList of plugin dependencies.  In other words,
     155                               it's a GList of plugin id's that your plugin depends on.
     156                               Set this value to NULL no matter what.  If your plugin
     157                               has dependencies, set them at run-time in the
     158                               plugin_init function. */
     159
     160    PURPLE_PRIORITY_DEFAULT,/* This is the priority libpurple with give your plugin.
     161                               There are three possible values for this field,
     162                               PURPLE_PRIORITY_DEFAULT, PURPLE_PRIORITY_HIGHEST, and
     163                               PURPLE_PRIORITY_LOWEST. */
     164
     165    "core-hello_world",     /* This is your plugin's id.  There is a whole page dedicated
     166                               to this in the Related Pages section of the API docs. */
     167
     168    "Hello World!",         /* This is your plugin's name.  This is what will be
     169                               displayed for your plugin in the UI. */
     170
     171    "1.1",                  /* This is the version of your plugin. */
     172
     173    "Hello World Plugin",   /* This is the summary of your plugin.  It should be a short
     174                               blurb.  The UI determines where, if at all, to display
     175                               this. */
     176
     177    "Hello World Plugin",   /* This is the description of your plugin. It can be as long
     178                               and as descriptive as you like.  And like the summary,
     179                               it's up to the UI where, if at all, to display this (and
     180                               how much to display). */
     181
     182    "My Name <email@helloworld.tld">, /* This is where you can put your name and e-mail
     183                                         address. */
     184
     185    "http://helloworld.tld",/* This is the website for the plugin.  This tells users
     186                               where to find new versions, report bugs, etc. */
     187
     188    plugin_load,            /* This is a pointer to a function for libpurple to call when
     189                               it is loading the plugin.  It should be of the type:
     190
     191                                  gboolean plugin_load(PurplePlugin *plugin)
     192
     193                               Returning FALSE will stop the loading of the plugin.
     194                               Anything else would evaluate as TRUE and the plugin will
     195                               continue to load. */
     196
     197    NULL,                   /* Same as above except it is called when libpurple tries to
     198                               unload your plugin.  It should be of the type:
     199
     200                                  gboolean plugin_unload(PurplePlugin *plugin)
     201
     202                               Returning TRUE will tell libpurple to continue unloading
     203                               while FALSE will stop the unloading of your plugin. */
     204
     205    NULL,                   /* Similar to the two above members, except this is called
     206                               when libpurple tries to destory the plugin.  This is
     207                               generally only called when for some reason or another the
     208                               plugin fails to probe correctly.  It should be of the type:
     209
     210                                  void plugin_destroy(PurplePlugin *plugin) */
     211
     212    NULL,                   /* This is a pointer to a UI-specific struct.  For a Pidgin
     213                               plugin it will be a pointer to a PidginPluginUiInfo
     214                               struct, for example. */
     215
     216    NULL,                   /* This is a pointer to either a PurplePluginLoaderInfo
     217                               struct or a PurplePluginProtocolInfo struct, and is
     218                               beyond the scope of this document. */
     219
     220    NULL,                   /* This is a pointer to a PurplePluginUiInfo struct.  It is
     221                               a core/ui split way for core plugins to have a UI
     222                               configuration frame.  You can find an example of this
     223                               code in libpurple/plugins/pluginpref_example.c */
     224
     225    NULL,                   /* This is a function pointer where you can define "plugin
     226                               actions".  The UI controls how they're displayed.  It
     227                               should be of the type:
     228
     229                                  GList *function_name(PurplePlugin *plugin,
     230                                                       gpointer context)
     231
     232                               It must return a GList of PurplePluginActions. */
     233
     234    NULL,                   /* This is a pointer reserved for future use.  We set it to
     235                               NULL to indicate we don't need it. */
     236
     237    NULL,                   /* This is a pointer reserved for future use.  We set it to
     238                               NULL to indicate we don't need it. */
     239
     240    NULL,                   /* This is a pointer reserved for future use.  We set it to
     241                               NULL to indicate we don't need it. */
     242
     243    NULL                    /* This is a pointer reserved for future use.  We set it to
     244                               NULL to indicate we don't need it. */
     245};
     246 }}}
     247
     248Finally in our source file, we have the `init_plugin` function and the call to the macro `PURPLE_INIT_PLUGIN`.  The `init_plugin` function is called whenever libpurple probes the plugin.  Most plugins will add their prefs to the prefs tree here (more on prefs in another section of the tutorials), as well as do the necessary initialization for translation support (more on that in other documentation).
     249
     250Every plugin '''''MUST''''' call the `PURPLE_INIT_PLUGIN` macro.  The call to this macro tells libpurple some very basic things about your plugin, like what name to use if someone compiles your plugin statically into a binary, which function is the `init_plugin` function (because you can call it anything!), and the name of the `PurplePluginInfo` structure (which also can be called by any name you want).  Libpurple reads this information when probing the plugin.  If this call is missing, the plugin will not successfully probe and cannot ever load.
     251
     252== Compile, Install, and Load the Hello, World! Plugin ==
     253Now that we have a complete plugin source file, we need to compile it.  From `~/development/pidgin-2.0.2/libpurple/plugins`, run `make helloworld.so` (if using Windows, `make -f Makefile.mingw helloworld.dll`).  If you copied and pasted the code correctly, this should compile without incident.  You can move the resulting .so file to ~/.purple/plugins (move the resulting .dll file to `%APPDATA%\.purple\plugins` on Windows).  Now when you open the Plugins dialog in Pidgin or Finch, the plugin should show up.  When you load it you should see a message pop up.
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!