[geany/geany] 721009: plugins: plugin loader redesign
Thomas Martitz
git-noreply at xxxxx
Sun Aug 23 13:23:20 UTC 2015
Branch: refs/heads/master
Author: Thomas Martitz <kugel at rockbox.org>
Committer: Thomas Martitz <kugel at rockbox.org>
Date: Sun, 23 Aug 2015 13:23:20 UTC
Commit: 721009e262e9bc279122566a60e14af0048d9094
https://github.com/geany/geany/commit/721009e262e9bc279122566a60e14af0048d9094
Log Message:
-----------
plugins: plugin loader redesign
The old plugin loader has a number of deficiencies:
- plugins need to export a couple of callback functions into the global namespace
- plugins need to export data pointers, that are written by Geany
- the exported functions have no user_data param, so there is no way to
pass context/state back to the plugin (it needs global storage for that)
- plugin registration is implicit, plugins have no way to not register themselves
(it may want that due to missing runtime dependencies)
- plugins perform the ABI/API verification, and even though we provide a
convinience wrapper, it may get that wrong
As a result, I designed a new loader with the following design principles
- semantics of callbacks should not change, but they they shouldn't be mess
with the global namespace
- each callback receives a self-identifying param (the GeanyPlugin instance) and
a plugin-defined data pointer for their own use
- explicit registration through a new API function
- in-core API/ABI checks
The following principles shall be left unchanged:
- The scan is done on startup and when the PM dialog is opened
- Geany allocates GeanyPluginPrivate for each plugin, and GeanyPlugin is
a member of it
- Geany initially probes for the validity of the plugin, including file type
and API/ABI check, thus Geany has the last word in determining what a
plugin is
- the PM dialog is updated with the proper, translated plugin information
- the PM dialog GUI and user interaction in general is unchanged
With the redesign, plugins export a single function: geany_load_module().
This is called when the GModule is loaded. The main purpose of this function
is to call geany_plugin_register() (new API function) to register the plugin.
This is the only function that is learned about through g_module_symbol().
Within this call the plugin should
a) set the localized info fields of GeanyPlugin::info
b) pass compiled-against and minimum API version as well as compiled-against
ABI version, to allow Geany to verify compatibility
c) pass a pointer to an instance of GeanyPluginFuncs
which holds pointers to enhanced versions of the known callbacks (except
configure_single which is dropped).
d) optionally pass a plugin-private data pointer for later callbacks
Enhanced means that all callbacks receive the GeanyPlugin pointer as the first
and a pdata pointer as the last. pdata is private to the plugin and is set
by geany_plugin_register().
The callbacks need (should) not be globally defined anymore, and the global
GeanyData, GeanyPlugin and GeanyFunctions pointers are ignored and not set
anymore. GeanyData is available through GeanyPlugin::geany_data.
Modified Paths:
--------------
src/plugindata.h
src/pluginprivate.h
src/plugins.c
src/pluginutils.c
Modified: src/plugindata.h
72 lines changed, 61 insertions(+), 11 deletions(-)
===================================================================
@@ -108,17 +108,6 @@ typedef struct PluginInfo
PluginInfo;
-/** Basic information for the plugin and identification.
- * @see geany_plugin. */
-typedef struct GeanyPlugin
-{
- PluginInfo *info; /**< Fields set in plugin_set_info(). */
-
- struct GeanyPluginPrivate *priv; /* private */
-}
-GeanyPlugin;
-
-
/** Sets the plugin name and some other basic information about a plugin.
*
* @note If you want some of the arguments to be translated, see @ref PLUGIN_SET_TRANSLATABLE_INFO()
@@ -248,6 +237,16 @@ GeanyData;
#define geany geany_data /**< Simple macro for @c geany_data that reduces typing. */
+/** Basic information for the plugin and identification.
+ * @see geany_plugin. */
+typedef struct GeanyPlugin
+{
+ PluginInfo *info; /**< Fields set in plugin_set_info(). */
+ GeanyData *geany_data; /**< Pointer to global GeanyData intance */
+
+ struct GeanyPluginPrivate *priv; /* private */
+}
+GeanyPlugin;
#ifndef GEANY_PRIVATE
@@ -263,8 +262,59 @@ void plugin_configure_single(GtkWidget *parent);
void plugin_help(void);
void plugin_cleanup(void);
+/** Called by Geany when a plugin library is loaded.
+ *
+ * This is the original entry point. Implement and export this function to be loadable at all.
+ * Then fill in GeanyPlugin::info and GeanyPlugin::funcs of the passed @p plugin. Finally
+ * GEANY_PLUGIN_REGISTER() and specify a minimum supported API version.
+ *
+ * @param plugin The unique plugin handle to your plugin. You must set some fields here.
+ *
+ * @since 1.26 (API 225)
+ */
+gboolean geany_load_module(GeanyPlugin *plugin);
+
#endif
+/** Callback functions that need to be implemented for every plugin.
+ *
+ * These callbacks should be registered by the plugin within Geany's call to
+ * geany_load_module() by calling geany_plugin_register() with an instance of this type.
+ *
+ * Geany will then call the callbacks at appropriate times. Each gets passed the
+ * plugin-defined data pointer as well as the corresponding GeanyPlugin instance
+ * pointer.
+ *
+ * @since 1.26 (API 225)
+ **/
+typedef struct GeanyPluginFuncs
+{
+ /** Array of plugin-provided signal handlers @see PluginCallback */
+ PluginCallback *callbacks;
+ /** Called to initialize the plugin, when the user activates it (must not be @c NULL) */
+ void (*init) (GeanyPlugin *plugin, gpointer pdata);
+ /** plugins configure dialog, optional (can be @c NULL) */
+ GtkWidget* (*configure) (GeanyPlugin *plugin, GtkDialog *dialog, gpointer pdata);
+ /** Called when the plugin should show some help, optional (can be @c NULL) */
+ void (*help) (GeanyPlugin *plugin, gpointer pdata);
+ /** Called when the plugin is disabled or when Geany exits (must not be @c NULL) */
+ void (*cleanup) (GeanyPlugin *plugin, gpointer pdata);
+}
+GeanyPluginFuncs;
+
+gboolean geany_plugin_register(GeanyPlugin *plugin, gint api_version, gint min_api_version,
+ gint abi_version, GeanyPluginFuncs *cbs, gpointer pdata);
+
+/** Convinience macro to register a plugin.
+ *
+ * It simply calls geany_plugin_register() with GEANY_API_VERSION and GEANY_ABI_VERSION.
+ *
+ * @since 1.26 (API 225)
+ **/
+#define GEANY_PLUGIN_REGISTER(plugin, min_api_version) \
+ geany_plugin_register((plugin), GEANY_API_VERSION, \
+ (min_api_version), GEANY_ABI_VERSION)
+
/* Deprecated aliases */
#ifndef GEANY_DISABLE_DEPRECATED
Modified: src/pluginprivate.h
36 lines changed, 31 insertions(+), 5 deletions(-)
===================================================================
@@ -32,6 +32,8 @@
G_BEGIN_DECLS
+typedef struct GeanyData GeanyData;
+
typedef struct SignalConnection
{
GObject *object;
@@ -39,6 +41,22 @@ typedef struct SignalConnection
}
SignalConnection;
+typedef struct _GeanyPluginFuncsLegacy
+{
+ void (*init) (GeanyData *data); /* Called when the plugin is enabled */
+ GtkWidget* (*configure) (GtkDialog *dialog); /* plugins configure dialog, optional */
+ void (*configure_single) (GtkWidget *parent); /* plugin configure dialog, optional */
+ void (*help) (void); /* Called when the plugin should show some help, optional */
+ void (*cleanup) (void); /* Called when the plugin is disabled or when Geany exits */
+ void (*set_info) (PluginInfo *info); /* Called to let the plugin provide metadata for the PM dialog */
+}
+GeanyPluginFuncsLegacy;
+
+typedef enum _LoadedFlags {
+ LOADED_OK = 0x01,
+ IS_LEGACY = 0x02,
+}
+LoadedFlags;
typedef struct GeanyPluginPrivate
{
@@ -47,11 +65,13 @@ typedef struct GeanyPluginPrivate
PluginInfo info; /* plugin name, description, etc */
GeanyPlugin public; /* fields the plugin can read */
- void (*init) (GeanyData *data); /* Called when the plugin is enabled */
- GtkWidget* (*configure) (GtkDialog *dialog); /* plugins configure dialog, optional */
- void (*configure_single) (GtkWidget *parent); /* plugin configure dialog, optional */
- void (*help) (void); /* Called when the plugin should show some help, optional */
- void (*cleanup) (void); /* Called when the plugin is disabled or when Geany exits */
+ union {
+ GeanyPluginFuncs n; /* new-style callbacks, set by geany_plugin_register()
+ * NULL for legacy plugins (they do not call
+ * geany_plugin_register()) */
+ GeanyPluginFuncsLegacy l; /* old callbacks, complete with set_info(), version_check()
+ * and configure_single. Deprecated */
+ } cbs;
/* extra stuff */
PluginFields fields;
@@ -59,9 +79,15 @@ typedef struct GeanyPluginPrivate
GeanyAutoSeparator toolbar_separator;
GArray *signal_ids; /* SignalConnection's to disconnect when unloading */
GList *sources; /* GSources to destroy when unloading */
+
+ gpointer cb_data; /* user data passed back to functions in GeanyPluginFuncs */
+ LoadedFlags flags; /* bit-or of LoadedFlags */
}
GeanyPluginPrivate;
+#define PLUGIN_LOADED_OK(p) (((p)->flags & LOADED_OK) != 0)
+#define PLUGIN_IS_LEGACY(p) (((p)->flags & IS_LEGACY) != 0)
+
typedef GeanyPluginPrivate Plugin; /* shorter alias */
Modified: src/plugins.c
349 lines changed, 226 insertions(+), 123 deletions(-)
===================================================================
@@ -162,37 +162,26 @@ static Plugin *find_active_plugin_by_name(const gchar *filename)
}
+/* Mimics plugin_version_check() of legacy plugins for use with plugin_check_version() below */
+#define PLUGIN_VERSION_CODE(api, abi) ((abi) != GEANY_ABI_VERSION ? -1 : (api))
+
static gboolean
-plugin_check_version(GModule *module)
+plugin_check_version(Plugin *plugin, int plugin_version_code)
{
- gint (*version_check)(gint) = NULL;
-
- g_module_symbol(module, "plugin_version_check", (void *) &version_check);
-
- if (G_UNLIKELY(! version_check))
+ GModule *module = plugin->module;
+ if (plugin_version_code < 0)
{
- geany_debug("Plugin \"%s\" has no plugin_version_check() function - ignoring plugin!",
- g_module_name(module));
+ msgwin_status_add(_("The plugin \"%s\" is not binary compatible with this "
+ "release of Geany - please recompile it."), g_module_name(module));
+ geany_debug("Plugin \"%s\" is not binary compatible with this "
+ "release of Geany - recompile it.", g_module_name(module));
return FALSE;
}
- else
+ if (plugin_version_code > GEANY_API_VERSION)
{
- gint result = version_check(GEANY_ABI_VERSION);
-
- if (result < 0)
- {
- msgwin_status_add(_("The plugin \"%s\" is not binary compatible with this "
- "release of Geany - please recompile it."), g_module_name(module));
- geany_debug("Plugin \"%s\" is not binary compatible with this "
- "release of Geany - recompile it.", g_module_name(module));
- return FALSE;
- }
- if (result > GEANY_API_VERSION)
- {
- geany_debug("Plugin \"%s\" requires a newer version of Geany (API >= v%d).",
- g_module_name(module), result);
- return FALSE;
- }
+ geany_debug("Plugin \"%s\" requires a newer version of Geany (API >= v%d).",
+ g_module_name(module), plugin_version_code);
+ return FALSE;
}
return TRUE;
}
@@ -269,56 +258,164 @@ static gint cmp_plugin_names(gconstpointer a, gconstpointer b)
}
-static void
-plugin_load(Plugin *plugin)
+/** Register a plugin to Geany.
+ *
+ * The plugin will show up in the plugin manager. The user can interact with
+ * it based on the callbacks it provides and installed GUI elements.
+ *
+ * The return value must be checked. It may be FALSE if the plugin failed to register which can
+ * mainly happen for two reasons (future Geany versions may add new failure conditions):
+ * - Not all mandatory fields of GeanyPlugin have been set.
+ * - The ABI or API versions reported by the plugin are incompatible with the running Geany.
+ *
+ * Do not call this directly. Use GEANY_PLUGIN_REGISTER() instead which automatically
+ * handles @a api_version and @a abi_version.
+ *
+ * @param plugin The plugin provided by Geany
+ * @param api_version The API version the plugin is compiled against (pass GEANY_API_VERSION)
+ * @param min_api_version The minimum API version required by the plugin
+ * @param abi_version The exact ABI version the plugin is compiled against (pass GEANY_ABI_VERSION)
+ * @param cbs A statically allocated @ref GeanyPluginFuncs structure filled with callbacks
+ * @param pdata A data pointer to store plugin-specific data, will be passed to the plugin's callbacks
+ *
+ * @return TRUE if the plugin was successfully registered. Otherwise FALSE.
+ *
+ * @since 1.26 (API 225)
+ * @see GEANY_PLUGIN_REGISTER()
+ **/
+GEANY_API_SYMBOL
+gboolean geany_plugin_register(GeanyPlugin *plugin, gint api_version, gint min_api_version,
+ gint abi_version, GeanyPluginFuncs *cbs, gpointer pdata)
{
- GeanyPlugin **p_geany_plugin;
- PluginCallback *callbacks;
- PluginInfo **p_info;
- PluginFields **plugin_fields;
-
- /* set these symbols before plugin_init() is called
- * we don't set geany_data since it is set directly by plugin_new() */
- g_module_symbol(plugin->module, "geany_plugin", (void *) &p_geany_plugin);
- if (p_geany_plugin)
- *p_geany_plugin = &plugin->public;
- g_module_symbol(plugin->module, "plugin_info", (void *) &p_info);
- if (p_info)
- *p_info = &plugin->info;
- g_module_symbol(plugin->module, "plugin_fields", (void *) &plugin_fields);
- if (plugin_fields)
- *plugin_fields = &plugin->fields;
- read_key_group(plugin);
-
- /* start the plugin */
- g_return_if_fail(plugin->init);
- plugin->init(&geany_data);
-
- /* store some function pointers for later use */
- g_module_symbol(plugin->module, "plugin_configure", (void *) &plugin->configure);
- g_module_symbol(plugin->module, "plugin_configure_single", (void *) &plugin->configure_single);
- if (app->debug_mode && plugin->configure && plugin->configure_single)
- g_warning("Plugin '%s' implements plugin_configure_single() unnecessarily - "
- "only plugin_configure() will be used!",
- plugin->info.name);
+ Plugin *p;
- g_module_symbol(plugin->module, "plugin_help", (void *) &plugin->help);
- g_module_symbol(plugin->module, "plugin_cleanup", (void *) &plugin->cleanup);
- if (plugin->cleanup == NULL)
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ p = plugin->priv;
+ /* Prevent registering incompatible plugins. */
+ if (! plugin_check_version(p, PLUGIN_VERSION_CODE(api_version, abi_version)))
+ return FALSE;
+ /* If it ever becomes necessary we can save the api version in Plugin
+ * and apply compat code on a per-plugin basis, because we learn about
+ * the requested API version here. Also if we add to GeanyPluginFuncs then
+ * we have to inspect the plugin's api so that we don't misinterpret
+ * function pointers the plugin doesn't know anything about. */
+ p->cbs.n = *cbs;
+ p->cb_data = pdata;
+
+ /* Only init and cleanup callbacks are truly mandatory. */
+ if (! cbs->init || ! cbs->cleanup)
+ {
+ geany_debug("Plugin '%s' has no %s function - ignoring plugin!",
+ cbs->init ? "cleanup" : "init", g_module_name(p->module));
+ }
+ else
{
- if (app->debug_mode)
+ /* Yes, name is checked again later on, however we want return FALSE here
+ * to signal the error back to the plugin (but we don't print the message twice) */
+ if (! EMPTY(p->info.name))
+ p->flags = LOADED_OK;
+ }
+
+ return PLUGIN_LOADED_OK(p);
+}
+
+
+/* This function is the equivalent of geany_plugin_register() for legacy-style
+ * plugins which we continue to load for the time being. */
+static void register_legacy_plugin(Plugin *plugin, GModule *module)
+{
+ gint (*p_version_check) (gint abi_version);
+ void (*p_set_info) (PluginInfo *info);
+ GeanyData **p_geany_data;
+
+#define CHECK_FUNC(__x, p) \
+ if (! g_module_symbol(module, "plugin_" #__x, (void *) (p))) \
+ { \
+ geany_debug("Plugin \"%s\" has no plugin_" #__x "() function - ignoring plugin!", \
+ g_module_name(plugin->module)); \
+ return; \
+ }
+ CHECK_FUNC(version_check, &p_version_check);
+ CHECK_FUNC(set_info, &p_set_info);
+ CHECK_FUNC(init, &plugin->cbs.l.init);
+#undef CHECK_FUNC
+
+ /* We must verify the version first. If the plugin has become incompatible any
+ * further actions should be considered invalid and therefore skipped. */
+ if (! plugin_check_version(plugin, p_version_check(GEANY_ABI_VERSION)))
+ return;
+
+ /* Since the version check passed we can proceed with setting basic fields and
+ * calling its set_info() (which might want to call Geany functions already). */
+ g_module_symbol(module, "geany_data", (void *) &p_geany_data);
+ if (p_geany_data)
+ *p_geany_data = &geany_data;
+ /* Read plugin name, etc. name is mandatory but that's enforced in the common code. */
+ p_set_info(&plugin->info);
+
+ /* If all went well we can set the remaining callbacks and let it go for good. */
+ g_module_symbol(module, "plugin_configure", (void *) &plugin->cbs.l.configure);
+ g_module_symbol(module, "plugin_configure_single", (void *) &plugin->cbs.l.configure_single);
+ g_module_symbol(module, "plugin_help", (void *) &plugin->cbs.l.help);
+ g_module_symbol(module, "plugin_cleanup", (void *) &plugin->cbs.l.cleanup);
+
+ if (app->debug_mode)
+ {
+ if (plugin->cbs.l.configure && plugin->cbs.l.configure_single)
+ g_warning("Plugin '%s' implements plugin_configure_single() unnecessarily - "
+ "only plugin_configure() will be used!",
+ plugin->info.name);
+ if (plugin->cbs.l.cleanup == NULL)
g_warning("Plugin '%s' has no plugin_cleanup() function - there may be memory leaks!",
plugin->info.name);
}
- /* now read any plugin-owned data that might have been set in plugin_init() */
+ plugin->flags = LOADED_OK | IS_LEGACY;
+}
+
+
+static void
+plugin_load(Plugin *plugin)
+{
+ PluginCallback *callbacks;
+
+ if (PLUGIN_IS_LEGACY(plugin))
+ {
+ GeanyPlugin **p_geany_plugin;
+ PluginInfo **p_info;
+ PluginFields **plugin_fields;
+ /* set these symbols before plugin_init() is called
+ * we don't set geany_data since it is set directly by plugin_new() */
+ g_module_symbol(plugin->module, "geany_plugin", (void *) &p_geany_plugin);
+ if (p_geany_plugin)
+ *p_geany_plugin = &plugin->public;
+ g_module_symbol(plugin->module, "plugin_info", (void *) &p_info);
+ if (p_info)
+ *p_info = &plugin->info;
+ g_module_symbol(plugin->module, "plugin_fields", (void *) &plugin_fields);
+ if (plugin_fields)
+ *plugin_fields = &plugin->fields;
+ read_key_group(plugin);
+
+ /* start the plugin */
+ plugin->cbs.l.init(&geany_data);
+
+ /* now read any plugin-owned data that might have been set in plugin_init() */
+ if (plugin->fields.flags & PLUGIN_IS_DOCUMENT_SENSITIVE)
+ {
+ ui_add_document_sensitive(plugin->fields.menu_item);
+ }
- if (plugin->fields.flags & PLUGIN_IS_DOCUMENT_SENSITIVE)
+ g_module_symbol(plugin->module, "plugin_callbacks", (void *) &callbacks);
+ }
+ else
{
- ui_add_document_sensitive(plugin->fields.menu_item);
+ plugin->cbs.n.init(&plugin->public, plugin->cb_data);
+ callbacks = plugin->cbs.n.callbacks;
}
- g_module_symbol(plugin->module, "plugin_callbacks", (void *) &callbacks);
+ /* new-style plugins set their callbacks in geany_load_module() */
if (callbacks)
add_callbacks(plugin, callbacks);
@@ -327,8 +424,7 @@ plugin_load(Plugin *plugin)
* sorted by plugin name */
active_plugin_list = g_list_insert_sorted(active_plugin_list, plugin, cmp_plugin_names);
- geany_debug("Loaded: %s (%s)", plugin->filename,
- FALLBACK(plugin->info.name, "<Unknown>"));
+ geany_debug("Loaded: %s (%s)", plugin->filename, plugin->info.name);
}
@@ -342,8 +438,7 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list)
{
Plugin *plugin;
GModule *module;
- GeanyData **p_geany_data;
- void (*plugin_set_info)(PluginInfo*);
+ gboolean (*p_geany_load_module)(GeanyPlugin *);
g_return_val_if_fail(fname, NULL);
g_return_val_if_fail(g_module_supported(), NULL);
@@ -387,60 +482,42 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list)
return NULL;
}
- if (! plugin_check_version(module))
+ plugin = g_new0(Plugin, 1);
+ plugin->module = module;
+ plugin->filename = g_strdup(fname);
+ plugin->public.geany_data = &geany_data;
+ plugin->public.priv = plugin;
+ /* Fields of plugin->info must to be initialized by the plugin */
+ plugin->public.info = &plugin->info;
+
+ g_module_symbol(module, "geany_load_module", (void *) &p_geany_load_module);
+ if (p_geany_load_module)
{
- if (! g_module_close(module))
- g_warning("%s: %s", fname, g_module_error());
- return NULL;
+ /* This is a new style plugin. It should fill in plugin->info and then call
+ * geany_plugin_register() in its geany_load_module() to successfully load.
+ * The ABI and API checks are performed by geany_plugin_register() (i.e. by us).
+ * We check the LOADED_OK flag separately to protect us against buggy plugins
+ * who ignore the result of geany_plugin_register() and register anyway */
+ p_geany_load_module(&plugin->public);
}
-
- g_module_symbol(module, "plugin_set_info", (void *) &plugin_set_info);
- if (plugin_set_info == NULL)
+ else
{
- geany_debug("No plugin_set_info() defined for \"%s\" - ignoring plugin!", fname);
-
- if (! g_module_close(module))
- g_warning("%s: %s", fname, g_module_error());
- return NULL;
+ /* This is the legacy / deprecated code path. It does roughly the same as
+ * geany_load_module() and geany_plugin_register() together for the new ones */
+ register_legacy_plugin(plugin, module);
}
- plugin = g_new0(Plugin, 1);
-
- /* set basic fields here to allow plugins to call Geany functions in set_info() */
- g_module_symbol(module, "geany_data", (void *) &p_geany_data);
- if (p_geany_data)
- *p_geany_data = &geany_data;
-
- /* read plugin name, etc. */
- plugin_set_info(&plugin->info);
- if (G_UNLIKELY(EMPTY(plugin->info.name)))
+ if (! PLUGIN_LOADED_OK(plugin))
{
- geany_debug("No plugin name set in plugin_set_info() for \"%s\" - ignoring plugin!",
- fname);
-
- if (! g_module_close(module))
- g_warning("%s: %s", fname, g_module_error());
- g_free(plugin);
- return NULL;
+ geany_debug("Failed to load \"%s\" - ignoring plugin!", fname);
+ goto err;
}
- g_module_symbol(module, "plugin_init", (void *) &plugin->init);
- if (plugin->init == NULL)
+ if (EMPTY(plugin->info.name))
{
- geany_debug("Plugin '%s' has no plugin_init() function - ignoring plugin!",
- plugin->info.name);
-
- if (! g_module_close(module))
- g_warning("%s: %s", fname, g_module_error());
- g_free(plugin);
- return NULL;
+ geany_debug("No plugin name set for \"%s\" - ignoring plugin!", fname);
+ goto err;
}
- /*geany_debug("Initializing plugin '%s'", plugin->info.name);*/
-
- plugin->filename = g_strdup(fname);
- plugin->module = module;
- plugin->public.info = &plugin->info;
- plugin->public.priv = plugin;
if (load_plugin)
plugin_load(plugin);
@@ -449,6 +526,13 @@ plugin_new(const gchar *fname, gboolean load_plugin, gboolean add_to_list)
plugin_list = g_list_prepend(plugin_list, plugin);
return plugin;
+
+err:
+ if (! g_module_close(module))
+ g_warning("%s: %s", fname, g_module_error());
+ g_free(plugin->filename);
+ g_free(plugin);
+ return NULL;
}
@@ -529,8 +613,11 @@ plugin_cleanup(Plugin *plugin)
{
GtkWidget *widget;
- if (plugin->cleanup)
- plugin->cleanup();
+ /* With geany_plugin_register() cleanup is mandatory */
+ if (! PLUGIN_IS_LEGACY(plugin))
+ plugin->cbs.n.cleanup(&plugin->public, plugin->cb_data);
+ else if (plugin->cbs.l.cleanup)
+ plugin->cbs.l.cleanup();
remove_callbacks(plugin);
remove_sources(plugin);
@@ -851,7 +938,12 @@ gboolean plugins_have_preferences(void)
foreach_list(item, active_plugin_list)
{
Plugin *plugin = item->data;
- if (plugin->configure != NULL || plugin->configure_single != NULL)
+ gboolean result;
+ if (! PLUGIN_IS_LEGACY(plugin))
+ result = plugin->cbs.n.configure != NULL;
+ else
+ result = plugin->cbs.l.configure != NULL || plugin->cbs.l.configure_single != NULL;
+ if (result)
return TRUE;
}
@@ -892,17 +984,23 @@ static PluginManagerWidgets pm_widgets;
static void pm_update_buttons(Plugin *p)
{
- gboolean is_active = FALSE;
gboolean has_configure = FALSE;
gboolean has_help = FALSE;
gboolean has_keybindings = FALSE;
- if (p != NULL)
+ if (p != NULL && is_active_plugin(p))
{
- is_active = is_active_plugin(p);
- has_configure = (p->configure || p->configure_single) && is_active;
- has_help = p->help != NULL && is_active;
- has_keybindings = p->key_group && p->key_group->plugin_key_count > 0 && is_active;
+ if (PLUGIN_IS_LEGACY(p))
+ {
+ has_configure = p->cbs.l.configure || p->cbs.l.configure_single;
+ has_help = p->cbs.l.help != NULL;
+ }
+ else
+ {
+ has_configure = p->cbs.n.configure != NULL;
+ has_help = p->cbs.n.help != NULL;
+ }
+ has_keybindings = p->key_group && p->key_group->plugin_key_count;
}
gtk_widget_set_sensitive(pm_widgets.configure_button, has_configure);
@@ -1239,8 +1337,13 @@ static void pm_on_plugin_button_clicked(G_GNUC_UNUSED GtkButton *button, gpointe
{
if (GPOINTER_TO_INT(user_data) == PM_BUTTON_CONFIGURE)
plugin_show_configure(&p->public);
- else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_HELP && p->help != NULL)
- p->help();
+ else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_HELP)
+ {
+ if (PLUGIN_IS_LEGACY(p))
+ p->cbs.l.help();
+ else
+ p->cbs.n.help(&p->public, p->cb_data);
+ }
else if (GPOINTER_TO_INT(user_data) == PM_BUTTON_KEYBINDINGS && p->key_group && p->key_group->plugin_key_count > 0)
keybindings_dialog_show_prefs_scroll(p->info.name);
}
Modified: src/pluginutils.c
20 lines changed, 13 insertions(+), 7 deletions(-)
===================================================================
@@ -310,7 +310,7 @@ GeanyKeyGroup *plugin_set_key_group(GeanyPlugin *plugin,
static void on_pref_btn_clicked(gpointer btn, Plugin *p)
{
- p->configure_single(main_widgets.window);
+ p->cbs.l.configure_single(main_widgets.window);
}
@@ -318,10 +318,16 @@ static GtkWidget *create_pref_page(Plugin *p, GtkWidget *dialog)
{
GtkWidget *page = NULL; /* some plugins don't have prefs */
- if (p->configure)
+ if (!PLUGIN_IS_LEGACY(p))
{
- page = p->configure(GTK_DIALOG(dialog));
+ if (p->cbs.n.configure)
+ page = p->cbs.n.configure(&p->public, GTK_DIALOG(dialog), p->cb_data);
+ }
+ else if (p->cbs.l.configure)
+ page = p->cbs.l.configure(GTK_DIALOG(dialog));
+ if (page)
+ {
if (! GTK_IS_WIDGET(page))
{
geany_debug("Invalid widget returned from plugin_configure() in plugin \"%s\"!",
@@ -338,7 +344,7 @@ static GtkWidget *create_pref_page(Plugin *p, GtkWidget *dialog)
gtk_box_pack_start(GTK_BOX(page), align, TRUE, TRUE, 0);
}
}
- else if (p->configure_single)
+ else if (PLUGIN_IS_LEGACY(p) && p->cbs.l.configure_single)
{
GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
GtkWidget *btn;
@@ -421,12 +427,12 @@ void plugin_show_configure(GeanyPlugin *plugin)
}
p = plugin->priv;
- if (p->configure)
+ if (!PLUGIN_IS_LEGACY(p) || p->cbs.l.configure)
configure_plugins(p);
else
{
- g_return_if_fail(p->configure_single);
- p->configure_single(main_widgets.window);
+ g_return_if_fail(p->cbs.l.configure_single);
+ p->cbs.l.configure_single(main_widgets.window);
}
}
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Commits
mailing list