[Geany-Devel] RFC: Proxy plugins

Thomas Martitz kugel at xxxxx
Wed May 7 06:40:13 UTC 2014


I'm (as of now) motivated to implement proxy plugins (to my amusement, 
until splitwindow2 gets finally reviewed...). Therefore I would like to 
receive comments on my proposed concept and APIs (further below).

I would also love to discuss about better terms for proxy and "the 
proxied plugins". I think some adorable internal code/nick names could 
help us to not get confused in this business.

Note that I have implemented very little yet, so there might be unknown 
roadblocks and things can easily change. Please also note that I intend 
to achieve the goals without breaking plugin API/ABI.

The concept:

The basic idea is that proxy plugins initially call a Geany API to 
register themselves as proxies, providing criterias to select potential 
plugins (for now, this is only a list of file extensions) and a few 
hooks to call into the plugin when Geany needs it.

Geany will match files against these criterias and, on match, call into 
the plugin for the first time. This call should probe whether or not the 
plugin can actually handle the plugin. I think this is needed because a) 
criterias/file extensions might be ambiguous and b) the plugin might be 
written against an incompatible version of the proxy (think python 2 vs 3).

Then when Geany generates the plugin list (i.e. when the user opens 
plugin manager (PM) dialog) it'll call a second time into the proxy. 
This time the purpose is to load the actual plugin, and install 
plugin-specific init(), configure(), help() and cleanup() hooks. Unlike 
in classic plugins these hooks must call into proxy, where they have to 
be dispatched to call the actual plugin code (this is why we call them 
proxy right?).

Once loaded, Geany generally doesn't know/care about the difference 
between classic plugins and proxied plugins. This achieves the ultimate 
goal: That proxied plugins transparently integrate into the PM dialog 
and become first-class citizens.

Part of the proposed concept is to add a "fake proxy" for the builtin 
.so-file support. This enables to use the same code for all plugins 
(proxied or not). This should greatly reduce the maintenance effort. The 
fake proxy naturally must be compiled in but otherwise exposes the same 
APIs as actual proxies.

The APIs:

This propsal adds 3 major functions and alters the 4 existing plugin hooks.

plugin_register_proxy(GeanyPlugin *proxy_plugin, ProxyPluginInfo *info, 
gpointer user_data);

This is initially called by the proxy and lets Geany know about it. 
ProxyPluginInfo contains the criterias and pointers to the probe() and 
load() functions. user_data is proxy-specific and passed to probe().

gboolean (*probe)(const gchar *file, gpointer user_data, gpointer 

This is implemented in the proxy and should return true when it can 
handle the plugin, it basically replaces the existing 
utils_str_casecmp(G_MODULE_SUFFIX, ...) call. There is a catch though, 
while probe() should be as fast as possible, I expect that some proxies 
still need to load the file half-way or even in full to make that 
decision. In order to maintain that state, it can set the plugin_data 
pointer. This will be passed to the load function. This way loading the 
plugin again can be avoided.

gboolean (*load)(const gchar *file, gpointer user_data, gpointer 

The same function signature, but a different purpose. The proxy should 
now fully load the plugin specified with file such that is ready to run 
(it has the same signature in case it needs to load in probe() already, 
then this function can be the same as probe()).

As for the existing hooks, these need gain a parameter (gpointer 
plugin_data). Because the hooks can be indirect now, the proxy needs 
plugin_data to be able to properly dispatch the hooks on a per-plugin 
basis. I.e. the hooks become:

void (*init) (GeanyData *data, gpointer plugin_data);
GtkWidget* (*configure) (GtkDialog *dialog, gpointer plugin_data);
void (*configure_single) (GtkWidget *parent, gpointer plugin_data);
void (*help) (gpointer plugin_data);
void (*cleanup) (gpointer plugin_data);

Because the plugin_data parameter is exclusively for the proxy, the 
actual plugins do not receive it. They still implement the 
non-plugin_data variant. The parameter is added to the end so that these 
can still point to the module symbols for .so file plugins.

That's it so far. I think this proposal should allow to reach our goal 
(first class plugin scripts) while avoiding to break plugins ABI/API.

Best regards.

More information about the Devel mailing list