SF.net SVN: geany: [2355] trunk

eht16 at users.sourceforge.net eht16 at xxxxx
Mon Mar 17 15:48:40 UTC 2008


Revision: 2355
          http://geany.svn.sourceforge.net/geany/?rev=2355&view=rev
Author:   eht16
Date:     2008-03-17 08:48:15 -0700 (Mon, 17 Mar 2008)

Log Message:
-----------
Rework internal handling of plugin/module loading.
At startup, only load enabled plugins, all others are only loaded when the plugin manager is opened and get completely unloaded when it is closed.

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/src/plugins.c

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2008-03-17 14:56:08 UTC (rev 2354)
+++ trunk/ChangeLog	2008-03-17 15:48:15 UTC (rev 2355)
@@ -6,6 +6,11 @@
    Fix broken auto multiline comment with files in CR/LF mode.
  * src/win32.c:
    Fix encoding problems with project file dialogs.
+ * src/plugins.c:
+   Rework internal handling of plugin/module loading.
+   At startup, only load enabled plugins, all others are only loaded
+   when the plugin manager is opened and get completely unloaded when
+   it is closed.
 
 
 2008-03-15  Enrico Tröger  <enrico(dot)troeger(at)uvena(dot)de>

Modified: trunk/src/plugins.c
===================================================================
--- trunk/src/plugins.c	2008-03-17 14:56:08 UTC (rev 2354)
+++ trunk/src/plugins.c	2008-03-17 15:48:15 UTC (rev 2355)
@@ -80,12 +80,21 @@
 Plugin;
 
 
-static GList *plugin_list = NULL; 		/* list of all available, loadable plugins */
-static GList *active_plugin_list = NULL; /* list of only actually loaded plugins */
+/* list of all available, loadable plugins, only valid as long as the plugin manager dialog is
+ * opened, afterwards it will be destroyed */
+static GList *plugin_list = NULL;
+static GList *active_plugin_list = NULL; /* list of only actually loaded plugins, always valid */
 static GtkWidget *separator = NULL;
 static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data);
 
 
+enum
+{
+	PLUGIN_FREE_NON_ACTIVE,
+	PLUGIN_FREE_ALL
+};
+
+
 static DocumentFuncs doc_funcs = {
 	&document_new_file,
 	&document_get_cur_idx,
@@ -255,8 +264,7 @@
 
 
 /* Prevent the same plugin filename being loaded more than once.
- * Note: g_module_name always returns the .so name, even when Plugin::filename
- * is an .la file. */
+ * Note: g_module_name always returns the .so name, even when Plugin::filename is an .la file. */
 static gboolean
 plugin_loaded(GModule *module)
 {
@@ -277,11 +285,44 @@
 		}
 		g_free(basename_loaded);
 	}
+	/* Look also through the list of active plugins. This prevents problems when we have the same
+	 * plugin in libdir/geany/ AND in configdir/plugins/ and the one in libdir/geany/ is loaded
+	 * as active plugin. The plugin manager list would only take the one in configdir/geany/ and
+	 * the plugin manager would list both plugins. Additionally, unloading the active plugin
+	 * would cause a crash. */
+	for (item = active_plugin_list; item != NULL; item = g_list_next(item))
+	{
+		basename_loaded = g_path_get_basename(g_module_name(((Plugin*)item->data)->module));
+
+		if (utils_str_equal(basename_module, basename_loaded))
+		{
+			g_free(basename_loaded);
+			g_free(basename_module);
+			return TRUE;
+		}
+		g_free(basename_loaded);
+	}
 	g_free(basename_module);
 	return FALSE;
 }
 
 
+static Plugin *find_active_plugin_by_name(const gchar *filename)
+{
+	GList *item;
+
+	g_return_val_if_fail(filename, FALSE);
+
+	for (item = active_plugin_list; item != NULL; item = g_list_next(item))
+	{
+		if (utils_str_equal(filename, ((Plugin*)item->data)->filename))
+			return item->data;
+	}
+
+	return NULL;
+}
+
+
 static gboolean
 plugin_check_version(GModule *module)
 {
@@ -390,9 +431,10 @@
 /* Load and init a plugin.
  * init_plugin decides whether the plugin's init() function should be called or not. If it is
  * called, the plugin will be started, if not the plugin will be read only (for the list of
- * available plugins in the plugin manager). */
+ * available plugins in the plugin manager).
+ * When add_to_list is set, the plugin will be added to the plugin manager's plugin_list. */
 static Plugin*
-plugin_new(const gchar *fname, gboolean init_plugin)
+plugin_new(const gchar *fname, gboolean init_plugin, gboolean add_to_list)
 {
 	Plugin *plugin;
 	GModule *module;
@@ -403,6 +445,17 @@
 	g_return_val_if_fail(fname, NULL);
 	g_return_val_if_fail(g_module_supported(), NULL);
 
+	/* find the plugin in the list of already loaded, active plugins and use it, otherwise
+	 * load the module */
+	plugin = find_active_plugin_by_name(fname);
+	if (plugin != NULL)
+	{
+		geany_debug("Plugin \"%s\" already loaded.", fname);
+		if (add_to_list)
+			plugin_list = g_list_append(plugin_list, plugin);
+		return plugin;
+	}
+
 	/* Don't use G_MODULE_BIND_LAZY otherwise we can get unresolved symbols at runtime,
 	 * causing a segfault. Without that flag the module will safely fail to load.
 	 * G_MODULE_BIND_LOCAL also helps find undefined symbols e.g. app when it would
@@ -447,8 +500,6 @@
 	plugin->filename = g_strdup(fname);
 	plugin->module = module;
 
-	plugin_list = g_list_append(plugin_list, plugin);
-
 	g_module_symbol(module, "geany_data", (void *) &p_geany_data);
 	if (p_geany_data)
 		*p_geany_data = &geany_data;
@@ -466,11 +517,12 @@
 				info()->name);
 	}
 
-	/* only initialise the plugin if it should be loaded */
 	if (init_plugin)
-	{
 		plugin_init(plugin);
-	}
+
+	if (add_to_list)
+		plugin_list = g_list_append(plugin_list, plugin);
+
 	return plugin;
 }
 
@@ -488,17 +540,17 @@
 }
 
 
+static gboolean is_active_plugin(Plugin *plugin)
+{
+	return (g_list_find(active_plugin_list, plugin) != NULL);
+}
+
+
 static void
 plugin_unload(Plugin *plugin)
 {
-	g_return_if_fail(plugin);
-	g_return_if_fail(plugin->module);
 
-	/* only do cleanup if the plugin was actually loaded */
-	if (! g_list_find(active_plugin_list, plugin))
-		return;
-
-	if (plugin->cleanup)
+	if (is_active_plugin(plugin) && plugin->cleanup)
 		plugin->cleanup();
 
 	remove_callbacks(plugin);
@@ -512,18 +564,25 @@
 
 
 static void
-plugin_free(Plugin *plugin)
+plugin_free(Plugin *plugin, gpointer data)
 {
 	g_return_if_fail(plugin);
 	g_return_if_fail(plugin->module);
 
+	/* don't do anything when closing the plugin manager and it is an active plugin */
+	if (GPOINTER_TO_INT(data) == PLUGIN_FREE_NON_ACTIVE && is_active_plugin(plugin))
+		return;
+
 	plugin_unload(plugin);
 
-	if (! g_module_close(plugin->module))
+	if (plugin->module != NULL && ! g_module_close(plugin->module))
 		g_warning("%s: %s", plugin->filename, g_module_error());
 
+	plugin_list = g_list_remove(plugin_list, plugin);
+
 	g_free(plugin->filename);
 	g_free(plugin);
+	plugin = NULL;
 }
 
 
@@ -539,13 +598,13 @@
 	for (i = 0; i < len; i++)
 	{
 		if (NZV(app->active_plugins[i]))
-			plugin_new(app->active_plugins[i], TRUE);
+			plugin_new(app->active_plugins[i], TRUE, FALSE);
 	}
 }
 
 
 static void
-load_plugins(const gchar *path)
+load_plugins_from_path(const gchar *path)
 {
 	GSList *list, *item;
 	gchar *fname, *tmp;
@@ -560,7 +619,7 @@
 			continue;
 
 		fname = g_strconcat(path, G_DIR_SEPARATOR_S, item->data, NULL);
-		plugin = plugin_new(fname, FALSE);
+		plugin = plugin_new(fname, FALSE, TRUE);
 		g_free(fname);
 	}
 
@@ -582,20 +641,20 @@
 #endif
 
 
-static void load_plugin_paths(void)
+static void load_plugins(void)
 {
 	gchar *path;
 
 	path = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "plugins", NULL);
 	/* first load plugins in ~/.geany/plugins/, then in $prefix/lib/geany */
-	load_plugins(path);
+	load_plugins_from_path(path);
 	g_free(path);
 #ifdef G_OS_WIN32
 	path = get_plugin_path();
 #else
 	path = g_strconcat(GEANY_LIBDIR, G_DIR_SEPARATOR_S "geany", NULL);
 #endif
-	load_plugins(path);
+	load_plugins_from_path(path);
 
 	g_free(path);
 }
@@ -647,14 +706,11 @@
 
 void plugins_free()
 {
-	if (plugin_list != NULL)
+	if (active_plugin_list != NULL)
 	{
-		g_list_foreach(plugin_list, (GFunc) plugin_free, NULL);
-		g_list_free(plugin_list);
+		g_list_foreach(active_plugin_list, (GFunc) plugin_free,	GINT_TO_POINTER(PLUGIN_FREE_ALL));
+		g_list_free(active_plugin_list);
 	}
-	if (active_plugin_list != NULL)
-		/* no need to do more here, active_plugin_list holds the same pointer as plugin_list */
-		g_list_free(active_plugin_list);
 
 	g_object_unref(geany_object);
 	geany_object = NULL; /* to mark the object as invalid for any code which tries to emit signals */
@@ -724,7 +780,6 @@
 	GtkTreeIter iter;
 	GtkTreeModel *model;
 	Plugin *p;
-	PluginInfo *pi;
 
 	if (gtk_tree_selection_get_selected(selection, &model, &iter))
 	{
@@ -733,6 +788,7 @@
 		if (p != NULL)
 		{
 			gchar *text;
+			PluginInfo *pi;
 
 			pi = p->info();
 			text = g_strdup_printf(
@@ -752,6 +808,7 @@
 static void pm_plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer data)
 {
 	gboolean old_state, state;
+	gchar *file_name;
 	GtkTreeIter iter;
 	GtkTreePath *path = gtk_tree_path_new_from_string(pth);
 	Plugin *p;
@@ -764,25 +821,22 @@
 	if (p == NULL)
 		return;
 	state = ! old_state; /* toggle the state */
-	gtk_list_store_set(pm_widgets.store, &iter, PLUGIN_COLUMN_CHECK, state, -1);
 
-	/* load/unload the plugin once it was toggled for correct behaviour of the preferences button
-	 * we want to call plugin's configure() only after init() has been called */
+	/* save the filename of the plugin */
+	file_name = g_strdup(p->filename);
+	/* remove old plugin */
+	plugin_free(p, GINT_TO_POINTER(PLUGIN_FREE_ALL));
+	/* add new one */
+	p = plugin_new(file_name, state, TRUE);
+	gtk_list_store_set(pm_widgets.store, &iter,
+		PLUGIN_COLUMN_CHECK, state,
+		PLUGIN_COLUMN_PLUGIN, p, -1);
 
-	/* plugin should be loaded, so load it if it is not already */
-	if (state && g_list_find(active_plugin_list, p) == NULL)
-	{
-		plugin_init(p);
-	}
-	/* plugin should be unloaded, so unload it if it is loaded */
-	if (! state && g_list_find(active_plugin_list, p) != NULL)
-	{
-		plugin_unload(p);
-	}
+	g_free(file_name);
 
 	/* set again the sensitiveness of the configure button */
 	gtk_widget_set_sensitive(pm_widgets.configure_button,
-		p->configure != NULL && g_list_find(active_plugin_list, p) != NULL);
+		p->configure != NULL && is_active_plugin(p));
 
 }
 
@@ -834,10 +888,9 @@
 	{
 		for (; list != NULL; list = list->next)
 		{
-			gboolean active = (g_list_find(active_plugin_list, list->data) != NULL) ? TRUE : FALSE;
 			gtk_list_store_append(store, &iter);
 			gtk_list_store_set(store, &iter,
-				PLUGIN_COLUMN_CHECK, active,
+				PLUGIN_COLUMN_CHECK, is_active_plugin(list->data),
 				PLUGIN_COLUMN_NAME, ((Plugin*)list->data)->info()->name,
 				PLUGIN_COLUMN_FILE, ((Plugin*)list->data)->filename,
 				PLUGIN_COLUMN_PLUGIN, list->data,
@@ -868,17 +921,25 @@
 }
 
 
+static void pm_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
+{
+	if (plugin_list != NULL)
+	{
+		/* remove all non-active plugins from the list */
+		g_list_foreach(plugin_list, (GFunc) plugin_free, GINT_TO_POINTER(PLUGIN_FREE_NON_ACTIVE));
+		g_list_free(plugin_list);
+		plugin_list = NULL;
+	}
+	gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+
 static void pm_show_dialog(GtkMenuItem *menuitem, gpointer user_data)
 {
 	GtkWidget *vbox, *vbox2, *label_vbox, *hbox, *swin, *label, *label2;
-	static gboolean plugin_list_loaded = FALSE;
 
 	/* before showing the dialog, we need to create the list of available plugins */
-	if (! plugin_list_loaded)
-	{
-		load_plugin_paths();
-		plugin_list_loaded = TRUE;
-	}
+	load_plugins();
 
 	pm_widgets.dialog = gtk_dialog_new_with_buttons(_("Plugins"), GTK_WINDOW(app->window),
 						GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -927,8 +988,8 @@
 	gtk_box_pack_start(GTK_BOX(vbox2), swin, TRUE, TRUE, 0);
 	gtk_box_pack_start(GTK_BOX(vbox2), label_vbox, FALSE, FALSE, 0);
 
-	g_signal_connect_swapped((gpointer) pm_widgets.dialog, "response",
-		G_CALLBACK(gtk_widget_destroy), pm_widgets.dialog);
+	g_signal_connect((gpointer) pm_widgets.dialog, "response",
+		G_CALLBACK(pm_dialog_response), NULL);
 
 	gtk_container_add(GTK_CONTAINER(vbox), vbox2);
 	gtk_widget_show_all(pm_widgets.dialog);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list