Geany  dev
Plugin HowTo

Introduction

Since Geany 0.12 there is a plugin interface to extend Geany's functionality and add new features. This document gives a brief overview about how to add new plugins by writing a simple "Hello World" plugin in C or C++.

Build environment

To be able to write plugins for Geany, you need the source code and some development packages for GTK and its dependencies. The following will only describe the way to compile and build plugins on Unix-like systems [1]. If you already have the Geany source code and compiled it from them, you can skip the following.

First you need to have Geany installed. Then install the development files for GTK and its dependencies. The easiest way to do this is to use your distribution's package management system, e.g. on Debian and Ubuntu systems you can use

apt-get install libgtk2.0-dev intltool

This will install all necessary files to be able to compile plugins for Geany. On other distributions, the package names and commands to use may differ.

Basically, you are done at this point and could continue with writing the plugin code.

[1] For Windows, it is basically the same but you might have some more work on setting up the general build environment(compiler, GTK development files, ...). This is described on Geany's website at http://www.geany.org/Support/BuildingOnWin32.

"Hello World"

Note
This section describes the new entry points for plugins introduced with Geany 1.26. A short summary of the legacy entry points is given at page Porting guide from legacy entry points to the current ones but they are deprecated should not be used any more.

When writing a plugin you will find a couple of functions which are mandatory and some which can be implemented optionally for implementing some useful features once your plugin becomes more powerful. For example to provide a configuration or help dialog.

First steps for any Plugin

You should start your plugin with including <geanyplugin.h> and exporting a function named geany_load_module(). In this function you must fill in basic information that Geany uses to learn more about your plugin and present it to the user. You also must define some hooks that enable Geany to actually execute your code.

Please also do not forget about license headers which are by convention at the start of source files. You can use templates provided by Geany to get started. Without a proper license it will be difficult for packagers to pick up and distribute your plugin.

As mentioned above, start with the very fundamental header that gets you all goodies of Geany's plugin API. geanyplugin.h includes all of the Geany API and also the necessary GTK header files so there is no need to include gtk/gtk.h yourself. In fact it includes a utility header that helps supporting GTK+2 and GTK+3 in the same source.

#include <geanyplugin.h>
Note
If you use autoconf then config.h must be included even before that as usual.

Now you can go on and write your first lines for your new plugin. As mentioned before, you will need to implement a couple of functions. The first mandatory one is geany_load_module(). Geany uses the presence fo this function to identify a library as a plugin. When Geany scans the pre-defined and user-configured plugin directories, it will take a look at each shared library (or DLL on Windows) to see if it exports a geany_load_module() symbol. Files lacking these will be ignored. The second mandatory one is an initialization function that is only called when the plugin becomes actually enabled (by the user or at startup).

Registering a Plugin

Geany will always invoke this geany_load_module(), regardless of whether the user activates your plugin. In fact its purpose to probe if the plugin should even be presented to the user. Therefore you must use this function to register your plugin. Geany will pass a pointer to a GeanyPlugin instance which acts as a unique handle to your plugin. Use this pointer for registering and later API calls. It won't change for the life time of the plugin. Registering the plugin consists of a number of steps:

  1. Filling in GeanyPlugin::info with metadata that is shown to the user. Filling in all of them is recommended to provide the best user experience, but only the name is truly mandatory. Since all of the strings are shown to the user they should be human readable.
  2. Filling in GeanyPlugin::funcs with function pointers that are called by Geany. init and cleanup are mandatory, the other ones depend on how advanced your plugin is. Furthermore, init is called on startup and when the user activates your plugin in the Plugin Manager, and cleanup is called on exit and when the user deactivates it. So use these to do advanced initialization and teardown as to not waste resources when the plugin is not even enabled.
  3. Actually registering by calling GEANY_PLUGIN_REGISTER() or GEANY_PLUGIN_REGISTER_FULL().
    • Usually you should use GEANY_PLUGIN_REGISTER() to register your plugin, passing the GeanyPlugin pointer that you received and filled out as above. GEANY_PLUGIN_REGISTER() also takes the minimum API version number you want to support (see On API and ABI Versions for details). Please also check the return value. Geany may refuse to load your plugin due to incompatibilities, you should then abort any extra setup. GEANY_PLUGIN_REGISTER() is a macro wrapping geany_plugin_register() which takes additional the API and ABI that you should not pass manually.
    • If you require a plugin-specific context or state to be passed to your GeanyPlugin::funcs then use GEANY_PLUGIN_REGISTER_FULL() to register. This one takes additional parameters for adding user data to your plugin. That user data pointer is subsequently passed back to your functions. It allows, for example, to set instance pointer to objects in case your plugin isn't written in pure C, enabling you to use member functions as plugin functions. You may also set such data later on, for example in your GeanyPluginFuncs::init routine to defer costly allocations to when the plugin is actually activated by the user. However, you then have to call geany_plugin_set_data().

On API and ABI Versions

As previously mentioned geany_plugin_register() takes a number of versions as arguments:

  1. api_version
  2. min_api_version
  3. abi_version

These refer to Geany's versioning scheme to manage plugin compatibility. The following rules apply:

Instead of calling geany_plugin_register() directly it is very highly recommended to use GEANY_PLUGIN_REGISTER(). This is a convenient way to pass Geany's current API and ABI versions without requiring future code changes whenever either one changes. In fact, the promise that plugins need to be just recompiled on ABI change can hold if the plugins use this macro. You still want to pass the API version needed at minimum to run your plugin. The value is defined in plugindata.h by GEANY_API_VERSION. In most cases this should be your minimum. Nevertheless when setting this value, you should choose the lowest possible version here to make the plugin compatible with a bigger number of versions of Geany. The absolute minimum is 225 which introduced the new plugin entry points.

To increase your flexibility the API version of the running Geany is passed to geany_load_module(). You can use this information to toggle API-specific code. This comes handy, for example to enable optional code that requires a recent API version without raising your minimum required API version. This enables running the plugin against more Geany versions, although perhaps at reduced functionality.

Example

Going back to our "Hello World" plugin here is example code that properly adds the HelloWorld plugin to Geany.

/* License blob */
#include <geanyplugin.h>
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
printf("Hello World from plugin!\n");
/* Perform advanced set up here */
return TRUE;
}
static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
printf("Bye World :-(\n");
}
G_MODULE_EXPORT
{
/* Step 1: Set metadata */
plugin->info->name = "HelloWorld";
plugin->info->description = "Just another tool to say hello world";
plugin->info->version = "1.0";
plugin->info->author = "John Doe <john.doe@example.org>";
/* Step 2: Set functions */
plugin->funcs->init = hello_init;
plugin->funcs->cleanup = hello_cleanup;
/* Step 3: Register! */
if (GEANY_PLUGIN_REGISTER(plugin, 225))
return;
/* alternatively:
GEANY_PLUGIN_REGISTER_FULL(plugin, 225, data, free_func); */
}

If you think this plugin seems not to implement any functionality right now and only wastes some memory, you are right. But it should compile and load/unload in Geany nicely. Now you have the very basic layout of a new plugin. Great, isn't it?

If you would rather write the plugin in C++, you can do that by marking geany_load_module() as extern "C" , for example:

extern "C" void geany_load_module(GeanyPlugin *plugin)
{
}

You can also create an instance of a class and set that as data pointer (with GEANY_PLUGIN_REGISTER_FULL()). With small wrappers that shuffle the parameters you can even use member functions for GeanyPlugin::funcs etc.

Building

First make plugin.o:

gcc -c plugin.c -fPIC `pkg-config --cflags geany`

Then make the plugin library plugin.so (or plugin.dll on Windows):

gcc plugin.o -o plugin.so -shared `pkg-config --libs geany`

If all went OK, put the library into one of the paths Geany looks for plugins, e.g. $prefix/lib/geany. See Installation paths for details.

If you are writing the plugin in C++, then you will need to use your C++ compiler here, for example g++.

Adding functionality

Let's go on and implement some real functionality.

As mentioned before, GeanyPluginFuncs::init() will be called when the plugin is activated by Geany. So it should implement everything that needs to be done during startup. In this case, we'd like to add a menu item to Geany's Tools menu which runs a dialog printing "Hello World".

static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
GtkWidget *main_menu_item;
// Create a new menu item and show it
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
// Attach the new menu item to the Tools menu
gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu),
main_menu_item);
// Connect the menu item with a callback function
// which is called when the item is clicked
g_signal_connect(main_menu_item, "activate",
G_CALLBACK(item_activate_cb), NULL);
return TRUE;
}

This will add an item to the Tools menu and connect this item to a function which implements what should be done when the menu item is activated by the user. This is done by g_signal_connect(). The Tools menu can be accessed with plugin->geany_data->main_widgets->tools_menu. The structure GeanyMainWidgets contains pointers to all main GUI elements in Geany.

Geany has a simple API for showing message dialogs. So our function contains only a few lines:

static void item_activate_cb(GtkMenuItem *menuitem, gpointer user_data)
{
dialogs_show_msgbox(GTK_MESSAGE_INFO, "Hello World");
}

For the moment you don't need to worry about the parameters of that function.

Now we need to clean up properly when the plugin is unloaded.

To remove the menu item from the Tools menu you can use gtk_widget_destroy().

First you should add gtk_widget_destroy() to your GeanyPluginFuncs::cleanup() function. The argument for gtk_widget_destroy() is the widget object you created earlier in GeanyPluginFuncs::init(). To be able to access this pointer in GeanyPluginFuncs::cleanup() you can use geany_plugin_set_data() to set plugin-defined data pointer to the widget. Alternatively, you can store the pointer in some global variable so its visibility will increase and it can be accessed in all functions.

/* alternative: global variable:
static GtkWidget *main_menu_item;
*/
// ...
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
GtkWidget *main_menu_item;
// Create a new menu item and show it
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
// ...
geany_plugin_set_data(plugin, main_menu_item, NULL);
return TRUE;
}
static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
GtkWidget *main_menu_item = (GtkWidget *) pdata;
// ...
gtk_widget_destroy(main_menu_item);
}

This will ensure your menu item is removed from the Tools menu as well as from memory once your plugin is unloaded, so you don't leave any memory leaks. Once this is done, your first plugin is ready. Congratulations!

Complete listing (without comments)

#include <geanyplugin.h>
static void item_activate_cb(GtkMenuItem *menuitem, gpointer user_data)
{
dialogs_show_msgbox(GTK_MESSAGE_INFO, "Hello World");
}
static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
GtkWidget *main_menu_item;
// Create a new menu item and show it
main_menu_item = gtk_menu_item_new_with_mnemonic("Hello World");
gtk_widget_show(main_menu_item);
gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu),
main_menu_item);
g_signal_connect(main_menu_item, "activate",
G_CALLBACK(item_activate_cb), NULL);
geany_plugin_set_data(plugin, main_menu_item, NULL);
return TRUE;
}
static void hello_cleanup(GeanyPlugin *plugin, gpointer pdata)
{
GtkWidget *main_menu_item = (GtkWidget *) pdata;
gtk_widget_destroy(main_menu_item);
}
G_MODULE_EXPORT
{
plugin->info->name = "HelloWorld";
plugin->info->description = "Just another tool to say hello world";
plugin->info->version = "1.0";
plugin->info->author = "John Doe <john.doe@example.org>";
plugin->funcs->init = hello_init;
plugin->funcs->cleanup = hello_cleanup;
GEANY_PLUGIN_REGISTER(plugin, 225);
}

Now you might like to look at Geany's source code for core plugins such as plugins/demoplugin.c.

Further Improvements and next steps

Translatable plugin information

After having written our first plugin, there is still room for improvement.

By default, geany_load_module() is not prepared to allow translation of the basic plugin information, except plugins which are shipped with Geany's core distribution, because custom gettext catalogs are not setup. Since most plugins are not shipped with Geany's core, it makes sense to setup gettext when the plugin is loaded so that it gets translated inside Geany's Plugin Manager. The solution is to call the API function main_locale_init() inside geany_load_module() and then use gettext's _() as usual.

The invocation will most probably look similar to this:

// ...
main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
plugin->info->name = _("HelloWorld");
plugin->info->description = _("Just another tool to say hello world");
plugin->info->version = "1.0";
plugin->info->author = "John Doe <john.doe@example.org>";

The LOCALEDIR and the GETTEXT_PACKAGE parameters are usually set inside the build system.

As you can see the author's information is not marked as translatable in this example. The community has agreed that the best practice here is to use, if possible, the latin version of the author's name followed by the native spelling inside parenthesis, where applicable.

Using i18n/l10n inside Plugin

You can (and should) also mark other strings beside the plugin's meta information as translatable. Strings used in menu entries, information boxes or configuration dialogs should be translatable as well.

static gboolean hello_init(GeanyPlugin *plugin, gpointer pdata)
{
main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
main_menu_item = gtk_menu_item_new_with_mnemonic(_("Hello World"));
// ...
}