changeset 425:ae7c762775cd

[gaim-migrate @ 435] More mods to how plugins work. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Fri, 23 Jun 2000 04:15:51 +0000
parents 22700acd9b49
children 50489ea9f4ec
files plugins/ChangeLog plugins/SIGNALS plugins/toc_commands.c src/gaim.h src/gnome_applet_mgr.c src/plugins.c
diffstat 6 files changed, 108 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/ChangeLog	Wed Jun 21 19:43:05 2000 +0000
+++ b/plugins/ChangeLog	Fri Jun 23 04:15:51 2000 +0000
@@ -2,15 +2,22 @@
 	It's 3 am the night before finals, it's obviously a good time to hack
 	gaim.
 
+	This became quite long, and if you don't want to read it all, here's
+	the important stuff summed up:
+	- 9 new events (see SIGNALS file for more details)
+	- int gaim_plugin_init(void *) (no longer returns void, see error.c)
+	- void gaim_plugin_unload(void *) (to allow plugin to remove itself)
+	- can only load 1 instance of the same plugin
+
 	The first thing to note is that there are about 9 new events plugins
 	can attach to, most of them dealing with chat, since I know that was a
 	big thing that was missing. Please note that I was nice and decided to
 	tack these extra events onto the end of the enum, which means that
 	plugins do not have to be recompiled in order for them to still work.
 
-	The big thing to note is that gaim_plugin_init no longer returns void,
-	but int.  If it returns 0+, gaim interprets this as there being no
-	error, and continues with loading as normal. (This should be backwards-
+	The big change is that gaim_plugin_init no longer returns void, but
+	int.  If it returns 0+, gaim interprets this as there being no error,
+	and continues with loading as normal. (This should be backwards-
 	compatible: returning 0/1 is the equivalent of returning void.) If it
 	returns a number less than 0, there was an error loading detected by
 	the plugin. At that point, gaim will try to clean things up by removing
@@ -22,6 +29,31 @@
 	back to normal. If any of that was confusing, it was confusing to me,
 	too. I added a plugin, error.c, which should help clear things up.
 
+	Another big thing to note is that plugins can unload themselves. A good
+	example of why this is useful is a ticker plugin. If the user closes
+	the ticker window, they obviously want the plugin to be unloaded. Gaim
+	has no way of knowing that; therefore, the plugin must tell gaim that
+	it is to be unloaded. To have a plugin unload itself, simply call
+	gaim_plugin_unload(void *) (the void* is the handle passed to
+	gaim_plugin_init). Because you are explicitly asking to be removed,
+	gaim assumes that you have done any cleanup already, and so does not
+	call gaim_plugin_remove. Rather, it simply removes your callbacks and
+	unloads the plugin. (There is some trickery to this. Think about it:
+	your plugin calls the function, your plugin is unloaded, and execution
+	returns to your plugin, which no longer exists. This would cause a
+	segfault if it behaved exactly as described. Instead, the plugin is
+	removed from the list of plugins, and removed 5 seconds later. By then
+	the plugin should be effectively gone, though still in memory.)
+
+	In previous versions of gaim, you could load multiple copies of the
+	same plugin. This is no longer the case. The reason for this was that
+	there were not two instances of the plugin in memory; rather, one copy
+	and two structures representing the same plugin. Then, the callbacks
+	would be called twice (since the plugin would most likely act the same
+	across multiple instances), and when one was unloaded, all callbacks
+	for both instances would be removed. Rather than deal with two copies
+	of the same plugin, it is easier and cleaner to only handle one.
+
 	There is a new event, event_quit, which signifies that gaim has exited
 	correctly (i.e. didn't segfault). Also, after this event is called, all
 	plugins are removed, and their gaim_plugin_remove function is called.
--- a/plugins/SIGNALS	Wed Jun 21 19:43:05 2000 +0000
+++ b/plugins/SIGNALS	Fri Jun 23 04:15:51 2000 +0000
@@ -192,6 +192,5 @@
 	Called when gaim quits normally.  This can be called from either the
 	signed on state or the signed off state (from either the Cancel button
 	in the login window or the Quit option in the File menu on the buddy
-	list). Note that for the applet, this will never be called. If gaim
-	dies or is murdered, this won't be called. It's not my fault, it's
-	Seg's.
+	list). If gaim dies or is murdered, this won't be called. It's not my
+	fault, it's Seg's.
--- a/plugins/toc_commands.c	Wed Jun 21 19:43:05 2000 +0000
+++ b/plugins/toc_commands.c	Fri Jun 23 04:15:51 2000 +0000
@@ -9,6 +9,11 @@
 	sflap_send(entry_text, strlen(entry_text), TYPE_DATA);
 }
 
+void destroy_callback(GtkWidget *widget, void *handle) {
+	gtk_widget_destroy(widget);
+	gaim_plugin_unload(handle);
+}
+
 GtkWidget *window;
 void gaim_plugin_init(void *h) {
 	GtkWidget *entry;
@@ -23,6 +28,10 @@
 	gtk_container_add(GTK_CONTAINER(window), entry);
 	gtk_widget_show(entry);
 
+	gtk_signal_connect(GTK_OBJECT(window), "destroy",
+			   (GtkSignalFunc)destroy_callback,
+			   h);
+
 	gtk_widget_show(window);
 }
 
--- a/src/gaim.h	Wed Jun 21 19:43:05 2000 +0000
+++ b/src/gaim.h	Fri Jun 23 04:15:51 2000 +0000
@@ -140,10 +140,11 @@
 
 #ifdef GAIM_PLUGINS
 struct gaim_plugin {
-	char  *name;
-	char  *filename;
-	char  *description;
-	void  *handle;
+	char *name;
+	char *filename;
+	char *description;
+	void *handle;
+	int   remove;
 };
 
 enum gaim_event {
@@ -384,7 +385,7 @@
 #define TYPE_SIGNOFF   4
 #define TYPE_KEEPALIVE 5
 
-#define REVISION "gaim:$Revision: 433 $"
+#define REVISION "gaim:$Revision: 435 $"
 #define FLAPON "FLAPON\r\n\r\n"
 
 #define ROAST "Tic/Toc"
@@ -522,6 +523,7 @@
 extern void remove_chat_buddy(struct buddy_chat *, char *);
 extern void show_new_buddy_chat(struct buddy_chat *);
 extern void setup_buddy_chats();
+extern void do_quit();
 
 
 
@@ -701,6 +703,7 @@
 extern void load_plugin (char *);
 extern void gaim_signal_connect(void *, enum gaim_event, void *, void *);
 extern void gaim_signal_disconnect(void *, enum gaim_event, void *);
+extern void gaim_plugin_unload(void *);
 #endif
 
 /* Functions in prefs.c */
--- a/src/gnome_applet_mgr.c	Wed Jun 21 19:43:05 2000 +0000
+++ b/src/gnome_applet_mgr.c	Fri Jun 23 04:15:51 2000 +0000
@@ -617,6 +617,8 @@
 					      
 	gtk_signal_connect( GTK_OBJECT(applet), "button_press_event", GTK_SIGNAL_FUNC( AppletClicked), NULL);
 
+	gtk_signal_connect( GTK_OBJECT(applet), "destroy", GTK_SIGNAL_FUNC( do_quit), NULL);
+
 #ifdef HAVE_PANEL_PIXEL_SIZE
 	gtk_signal_connect(GTK_OBJECT(applet), "change_pixel_size",
 			GTK_SIGNAL_FUNC(applet_change_pixel_size), NULL);
--- a/src/plugins.c	Wed Jun 21 19:43:05 2000 +0000
+++ b/src/plugins.c	Fri Jun 23 04:15:51 2000 +0000
@@ -61,7 +61,7 @@
 static GtkWidget *plugtext;
 static GtkWidget *plugwindow;
 
-static GtkWidget *config;
+static GtkWidget *config = NULL;
 static guint confighandle = 0;
 
 /* --------------- Function Declarations --------------------- */
@@ -71,6 +71,7 @@
 
        void gaim_signal_connect   (void *, enum gaim_event, void *, void *);
        void gaim_signal_disconnect(void *, enum gaim_event, void *);
+       void gaim_plugin_unload    (void *);
 
 static void destroy_plugins  (GtkWidget *, gpointer);
 static void load_file        (GtkWidget *, gpointer);
@@ -139,6 +140,7 @@
 
 void load_plugin(char *filename) {
 	struct gaim_plugin *plug;
+	GList *c = plugins;
 	int (*gaim_plugin_init)();
 	char *(*gaim_plugin_error)(int);
 	char *(*cfunc)();
@@ -147,6 +149,17 @@
 	char *plugin_error;
 
 	if (filename == NULL) return;
+	/* i shouldn't be checking based solely on path, but i'm lazy */
+	while (c) {
+		plug = (struct gaim_plugin *)c->data;
+		if (!strcmp(filename, plug->filename)) {
+			sprintf(debug_buff, _("Already loaded %s, "
+						"not reloading.\n"), filename);
+			debug_print(debug_buff);
+			return;
+		}
+		c = c->next;
+	}
 	plug = g_malloc(sizeof *plug);
 	if (filename[0] != '/') {
 		char *buf = g_malloc(BUF_LEN);
@@ -157,7 +170,7 @@
 		plug->filename = g_strdup(filename);
 	sprintf(debug_buff, "Loading %s\n", filename);
 	debug_print(debug_buff);
-	/* do NOT OR with RTLD_GLOBAL, otherwise plugins may conflict
+	/* do NOT `OR' with RTLD_GLOBAL, otherwise plugins may conflict
 	 * (it's really just a way to work around other people's bad
 	 * programming, by not using RTLD_GLOBAL :P ) */
 	plug->handle = dlopen(plug->filename, RTLD_LAZY);
@@ -345,7 +358,7 @@
 	GtkWidget *label;
 	GtkWidget *list_item;
 
-	if (pluglist == NULL) return;
+	if (plugwindow == NULL) return;
 
 	gtk_list_clear_items(GTK_LIST(pluglist), 0, -1);
 	while (plugs) {
@@ -376,20 +389,49 @@
 	struct gaim_plugin *p;
 	void (*gaim_plugin_remove)();
 	char *error;
-	GList *c = callbacks;
-	struct gaim_callback *g;
 
 	i = GTK_LIST(pluglist)->selection;
 
 	if (i == NULL) return;
 
 	p = gtk_object_get_user_data(GTK_OBJECT(i->data));
-	sprintf(debug_buff, "Unloading %s\n", p->filename);
-	debug_print(debug_buff);
 
 	gaim_plugin_remove = dlsym(p->handle, "gaim_plugin_remove");
 	if ((error = (char *)dlerror()) == NULL)
 		(*gaim_plugin_remove)();
+
+	gaim_plugin_unload(p->handle);
+}
+
+static void remove_callback(struct gaim_plugin *p) {
+	gtk_timeout_remove(p->remove);
+	dlclose(p->handle);
+	g_free(p);
+}
+
+/* gaim_plugin_unload serves 2 purposes: 1. so plugins can unload themselves
+ * 					 2. to make my life easier */
+void gaim_plugin_unload(void *handle) {
+	GList *i;
+	struct gaim_plugin *p = NULL;
+	GList *c = callbacks;
+	struct gaim_callback *g;
+
+	i = plugins;
+	while (i) {
+		p = (struct gaim_plugin *)i->data;
+		if (handle == p->handle)
+			break;
+		p = NULL;
+		i = i->next;
+	}
+
+	if (!p)
+		return;
+
+	sprintf(debug_buff, "Unloading %s\n", p->filename);
+	debug_print(debug_buff);
+
 	sprintf(debug_buff, "%d callbacks to search\n", g_list_length(callbacks));
 	debug_print(debug_buff);
 	while (c) {
@@ -408,12 +450,11 @@
 			c = c->next;
 		}
 	}
-	dlclose(p->handle);
+	p->remove = gtk_timeout_add(5000, (GtkFunction)remove_callback, p);
 
 	plugins = g_list_remove(plugins, p);
 	g_free(p->filename);
-	g_free(p);
-	gtk_widget_set_sensitive(config, 0);
+	if (config) gtk_widget_set_sensitive(config, 0);
 	update_show_plugins();
 	save_prefs();
 }
@@ -448,6 +489,7 @@
 	if (plugwindow)
 		gtk_widget_destroy(plugwindow);
 	plugwindow = NULL;
+	config = NULL;
 }
 
 void gaim_signal_connect(void *handle, enum gaim_event which,