Geany dev
|
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++.
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
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 https://www.geany.org/Support/BuildingOnWin32.
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.
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.
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 of 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).
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:
As previously mentioned geany_plugin_register() takes a number of versions as arguments:
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.
Going back to our "Hello World" plugin here is example code that properly adds the HelloWorld plugin to Geany.
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:
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.
First make plugin.o:
Then make the plugin library plugin.so (or plugin.dll on Windows):
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++
.
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".
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:
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.
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!
Now you might like to look at Geany's source code for core plugins such as plugins/demoplugin.c.
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:
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.
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.