Mercurial > pidgin
comparison src/plugins.c @ 1047:ece2d1543b20
[gaim-migrate @ 1057]
Plugins now use GModule. Protocol plugins can be dynamically updated.
committer: Tailor Script <tailor@pidgin.im>
| author | Eric Warmenhoven <eric@warmenhoven.org> |
|---|---|
| date | Wed, 01 Nov 2000 22:30:36 +0000 |
| parents | 67ed2ee5be9f |
| children | d50d3abb9eb7 |
comparison
equal
deleted
inserted
replaced
| 1046:4593605da0e2 | 1047:ece2d1543b20 |
|---|---|
| 68 /* --------------- Function Declarations --------------------- */ | 68 /* --------------- Function Declarations --------------------- */ |
| 69 | 69 |
| 70 void show_plugins (GtkWidget *, gpointer); | 70 void show_plugins (GtkWidget *, gpointer); |
| 71 void load_plugin (char *); | 71 void load_plugin (char *); |
| 72 | 72 |
| 73 void gaim_signal_connect (void *, enum gaim_event, void *, void *); | 73 void gaim_signal_connect (GModule *, enum gaim_event, void *, void *); |
| 74 void gaim_signal_disconnect(void *, enum gaim_event, void *); | 74 void gaim_signal_disconnect(GModule *, enum gaim_event, void *); |
| 75 void gaim_plugin_unload (void *); | 75 void gaim_plugin_unload (GModule *); |
| 76 | 76 |
| 77 static void destroy_plugins (GtkWidget *, gpointer); | 77 static void destroy_plugins (GtkWidget *, gpointer); |
| 78 static void load_file (GtkWidget *, gpointer); | 78 static void load_file (GtkWidget *, gpointer); |
| 79 static void load_which_plugin(GtkWidget *, gpointer); | 79 static void load_which_plugin(GtkWidget *, gpointer); |
| 80 static void unload (GtkWidget *, gpointer); | 80 static void unload (GtkWidget *, gpointer); |
| 81 static void unload_immediate (GModule *); | |
| 81 static void list_clicked (GtkWidget *, struct gaim_plugin *); | 82 static void list_clicked (GtkWidget *, struct gaim_plugin *); |
| 82 static void update_show_plugins(); | 83 static void update_show_plugins(); |
| 83 static void hide_plugins (GtkWidget *, gpointer); | 84 static void hide_plugins (GtkWidget *, gpointer); |
| 84 | 85 |
| 85 /* ------------------ Code Below ---------------------------- */ | 86 /* ------------------ Code Below ---------------------------- */ |
| 144 } | 145 } |
| 145 | 146 |
| 146 void load_plugin(char *filename) { | 147 void load_plugin(char *filename) { |
| 147 struct gaim_plugin *plug; | 148 struct gaim_plugin *plug; |
| 148 GList *c = plugins; | 149 GList *c = plugins; |
| 149 int (*gaim_plugin_init)(); | 150 char *(*gaim_plugin_init)(GModule *); |
| 150 char *(*gaim_plugin_error)(int); | |
| 151 char *(*cfunc)(); | 151 char *(*cfunc)(); |
| 152 char *error; | 152 char *error; |
| 153 int retval; | 153 char *retval; |
| 154 char *plugin_error; | 154 char *tmp_filename; |
| 155 | 155 |
| 156 if (!g_module_supported()) return; | |
| 156 if (filename == NULL) return; | 157 if (filename == NULL) return; |
| 157 /* i shouldn't be checking based solely on path, but i'm lazy */ | 158 |
| 158 while (c) { | 159 while (c) { |
| 159 plug = (struct gaim_plugin *)c->data; | 160 plug = (struct gaim_plugin *)c->data; |
| 160 if (!strcmp(filename, plug->filename)) { | 161 if (!strcmp(filename, g_module_name(plug->handle))) { |
| 161 debug_printf( _("Already loaded %s, not reloading.\n"), filename); | 162 void (*gaim_plugin_remove)(); |
| 162 return; | 163 if (g_module_symbol(plug->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove)) |
| 163 } | 164 (*gaim_plugin_remove)(); |
| 164 c = g_list_next(c); | 165 |
| 166 unload_immediate(plug->handle); | |
| 167 c = plugins; | |
| 168 } else | |
| 169 c = g_list_next(c); | |
| 165 } | 170 } |
| 166 plug = g_malloc(sizeof *plug); | 171 plug = g_malloc(sizeof *plug); |
| 167 if (!g_path_is_absolute(filename)) | 172 if (!g_path_is_absolute(filename)) |
| 168 plug->filename = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, | 173 tmp_filename = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, |
| 169 PLUGIN_DIR, filename, NULL); | 174 PLUGIN_DIR, filename, NULL); |
| 170 else | 175 else |
| 171 plug->filename = g_strdup(filename); | 176 tmp_filename = g_strdup(filename); |
| 172 | 177 |
| 173 if (last_dir) | 178 if (last_dir) |
| 174 g_free(last_dir); | 179 g_free(last_dir); |
| 175 last_dir = g_dirname(plug->filename); | 180 last_dir = g_dirname(tmp_filename); |
| 176 | 181 |
| 177 debug_printf("Loading %s\n", filename); | 182 debug_printf("Loading %s\n", tmp_filename); |
| 178 /* do NOT `OR' with RTLD_GLOBAL, otherwise plugins may conflict | 183 plug->handle = g_module_open(tmp_filename, 0); |
| 179 * (it's really just a way to work around other people's bad | 184 g_free(tmp_filename); |
| 180 * programming, by not using RTLD_GLOBAL :P ) */ | |
| 181 plug->handle = dlopen(plug->filename, RTLD_LAZY); | |
| 182 if (!plug->handle) { | 185 if (!plug->handle) { |
| 183 error = (char *)dlerror(); | 186 error = (char *)g_module_error(); |
| 184 do_error_dialog(error, _("Plugin Error")); | 187 do_error_dialog(error, _("Plugin Error")); |
| 185 g_free(plug->filename); | |
| 186 g_free(plug); | 188 g_free(plug); |
| 187 return; | 189 return; |
| 188 } | 190 } |
| 189 | 191 |
| 190 gaim_plugin_init = dlsym(plug->handle, "gaim_plugin_init"); | 192 if (!g_module_symbol(plug->handle, "gaim_plugin_init", (gpointer *)&gaim_plugin_init)) { |
| 191 if ((error = (char *)dlerror()) != NULL) { | 193 do_error_dialog(g_module_error(), _("Plugin Error")); |
| 192 do_error_dialog(error, _("Plugin Error")); | 194 g_module_close(plug->handle); |
| 193 dlclose(plug->handle); | |
| 194 g_free(plug->filename); | |
| 195 g_free(plug); | 195 g_free(plug); |
| 196 return; | 196 return; |
| 197 } | 197 } |
| 198 | 198 |
| 199 retval = (*gaim_plugin_init)(plug->handle); | 199 retval = (*gaim_plugin_init)(plug->handle); |
| 200 debug_printf("loaded plugin returned %d\n", retval); | 200 debug_printf("loaded plugin returned %d\n", retval); |
| 201 if (retval < 0) { | 201 if (retval) { |
| 202 GList *c = callbacks; | 202 GList *c = callbacks; |
| 203 struct gaim_callback *g; | 203 struct gaim_callback *g; |
| 204 while (c) { | 204 while (c) { |
| 205 g = (struct gaim_callback *)c->data; | 205 g = (struct gaim_callback *)c->data; |
| 206 if (g->handle == plug->handle) { | 206 if (g->handle == plug->handle) { |
| 214 } | 214 } |
| 215 } else { | 215 } else { |
| 216 c = g_list_next(c); | 216 c = g_list_next(c); |
| 217 } | 217 } |
| 218 } | 218 } |
| 219 gaim_plugin_error = dlsym(plug->handle, "gaim_plugin_error"); | 219 do_error_dialog(retval, _("Plugin Error")); |
| 220 if ((error = (char *)dlerror()) == NULL) { | 220 g_module_close(plug->handle); |
| 221 plugin_error = (*gaim_plugin_error)(retval); | |
| 222 if (plugin_error) | |
| 223 do_error_dialog(plugin_error, _("Plugin Error")); | |
| 224 } | |
| 225 dlclose(plug->handle); | |
| 226 g_free(plug->filename); | |
| 227 g_free(plug); | 221 g_free(plug); |
| 228 return; | 222 return; |
| 229 } | 223 } |
| 230 | 224 |
| 231 plugins = g_list_append(plugins, plug); | 225 plugins = g_list_append(plugins, plug); |
| 232 | 226 |
| 233 cfunc = dlsym(plug->handle, "name"); | 227 if (g_module_symbol(plug->handle, "name", (gpointer *)&cfunc)) |
| 234 if ((error = (char *)dlerror()) == NULL) | |
| 235 plug->name = (*cfunc)(); | 228 plug->name = (*cfunc)(); |
| 236 else | 229 else |
| 237 plug->name = NULL; | 230 plug->name = NULL; |
| 238 | 231 |
| 239 cfunc = dlsym(plug->handle, "description"); | 232 if (g_module_symbol(plug->handle, "description", (gpointer *)&cfunc)) |
| 240 if ((error = (char *)dlerror()) == NULL) | |
| 241 plug->description = (*cfunc)(); | 233 plug->description = (*cfunc)(); |
| 242 else | 234 else |
| 243 plug->description = NULL; | 235 plug->description = NULL; |
| 244 | 236 |
| 245 update_show_plugins(); | 237 update_show_plugins(); |
| 330 gtk_box_pack_start(GTK_BOX(page), topbox, TRUE, TRUE, 0); | 322 gtk_box_pack_start(GTK_BOX(page), topbox, TRUE, TRUE, 0); |
| 331 gtk_box_pack_start(GTK_BOX(page), botbox, FALSE, FALSE, 0); | 323 gtk_box_pack_start(GTK_BOX(page), botbox, FALSE, FALSE, 0); |
| 332 | 324 |
| 333 while (plugs) { | 325 while (plugs) { |
| 334 p = (struct gaim_plugin *)plugs->data; | 326 p = (struct gaim_plugin *)plugs->data; |
| 335 label = gtk_label_new(p->filename); | 327 label = gtk_label_new(g_module_name(p->handle)); |
| 336 list_item = gtk_list_item_new(); | 328 list_item = gtk_list_item_new(); |
| 337 gtk_container_add(GTK_CONTAINER(list_item), label); | 329 gtk_container_add(GTK_CONTAINER(list_item), label); |
| 338 gtk_signal_connect(GTK_OBJECT(list_item), "select", | 330 gtk_signal_connect(GTK_OBJECT(list_item), "select", |
| 339 GTK_SIGNAL_FUNC(list_clicked), p); | 331 GTK_SIGNAL_FUNC(list_clicked), p); |
| 340 gtk_object_set_user_data(GTK_OBJECT(list_item), p); | 332 gtk_object_set_user_data(GTK_OBJECT(list_item), p); |
| 375 if (plugwindow == NULL) return; | 367 if (plugwindow == NULL) return; |
| 376 | 368 |
| 377 gtk_list_clear_items(GTK_LIST(pluglist), 0, -1); | 369 gtk_list_clear_items(GTK_LIST(pluglist), 0, -1); |
| 378 while (plugs) { | 370 while (plugs) { |
| 379 p = (struct gaim_plugin *)plugs->data; | 371 p = (struct gaim_plugin *)plugs->data; |
| 380 label = gtk_label_new(p->filename); | 372 label = gtk_label_new(g_module_name(p->handle)); |
| 381 list_item = gtk_list_item_new(); | 373 list_item = gtk_list_item_new(); |
| 382 gtk_container_add(GTK_CONTAINER(list_item), label); | 374 gtk_container_add(GTK_CONTAINER(list_item), label); |
| 383 gtk_signal_connect(GTK_OBJECT(list_item), "select", | 375 gtk_signal_connect(GTK_OBJECT(list_item), "select", |
| 384 GTK_SIGNAL_FUNC(list_clicked), p); | 376 GTK_SIGNAL_FUNC(list_clicked), p); |
| 385 gtk_object_set_user_data(GTK_OBJECT(list_item), p); | 377 gtk_object_set_user_data(GTK_OBJECT(list_item), p); |
| 400 | 392 |
| 401 void unload(GtkWidget *w, gpointer data) { | 393 void unload(GtkWidget *w, gpointer data) { |
| 402 GList *i; | 394 GList *i; |
| 403 struct gaim_plugin *p; | 395 struct gaim_plugin *p; |
| 404 void (*gaim_plugin_remove)(); | 396 void (*gaim_plugin_remove)(); |
| 405 char *error; | |
| 406 | 397 |
| 407 i = GTK_LIST(pluglist)->selection; | 398 i = GTK_LIST(pluglist)->selection; |
| 408 | 399 |
| 409 if (i == NULL) return; | 400 if (i == NULL) return; |
| 410 | 401 |
| 411 p = gtk_object_get_user_data(GTK_OBJECT(i->data)); | 402 p = gtk_object_get_user_data(GTK_OBJECT(i->data)); |
| 412 | 403 |
| 413 /* Attempt to call the plugin's remove function (if there) */ | 404 /* Attempt to call the plugin's remove function (if there) */ |
| 414 gaim_plugin_remove = dlsym(p->handle, "gaim_plugin_remove"); | 405 if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove)) |
| 415 if ((error = (char *)dlerror()) == NULL) | |
| 416 (*gaim_plugin_remove)(); | 406 (*gaim_plugin_remove)(); |
| 417 | 407 |
| 418 gaim_plugin_unload(p->handle); | 408 unload_immediate(p->handle); |
| 419 } | 409 } |
| 420 | 410 |
| 421 /* gaim_plugin_unload serves 2 purposes: 1. so plugins can unload themselves | 411 static void unload_for_real(void *handle) { |
| 422 * 2. to make my life easier */ | |
| 423 void gaim_plugin_unload(void *handle) { | |
| 424 GList *i; | 412 GList *i; |
| 425 struct gaim_plugin *p = NULL; | 413 struct gaim_plugin *p = NULL; |
| 426 GList *c = callbacks; | 414 GList *c = callbacks; |
| 427 struct gaim_callback *g; | 415 struct gaim_callback *g; |
| 428 | 416 |
| 436 } | 424 } |
| 437 | 425 |
| 438 if (!p) | 426 if (!p) |
| 439 return; | 427 return; |
| 440 | 428 |
| 441 sprintf(debug_buff, "Unloading %s\n", p->filename); | 429 sprintf(debug_buff, "Unloading %s\n", g_module_name(p->handle)); |
| 442 debug_print(debug_buff); | 430 debug_print(debug_buff); |
| 443 | 431 |
| 444 sprintf(debug_buff, "%d callbacks to search\n", g_list_length(callbacks)); | 432 sprintf(debug_buff, "%d callbacks to search\n", g_list_length(callbacks)); |
| 445 debug_print(debug_buff); | 433 debug_print(debug_buff); |
| 446 | 434 |
| 460 c = g_list_next(c); | 448 c = g_list_next(c); |
| 461 } | 449 } |
| 462 } | 450 } |
| 463 | 451 |
| 464 plugins = g_list_remove(plugins, p); | 452 plugins = g_list_remove(plugins, p); |
| 465 g_free(p->filename); | |
| 466 /* we don't dlclose(p->handle) in case if we still need code from the plugin later */ | |
| 467 g_free(p); | 453 g_free(p); |
| 468 if (config) gtk_widget_set_sensitive(config, 0); | 454 if (config) gtk_widget_set_sensitive(config, 0); |
| 469 update_show_plugins(); | 455 update_show_plugins(); |
| 470 save_prefs(); | 456 save_prefs(); |
| 457 } | |
| 458 | |
| 459 void unload_immediate(GModule *handle) { | |
| 460 unload_for_real(handle); | |
| 461 g_module_close(handle); | |
| 462 } | |
| 463 | |
| 464 static gint unload_timeout(GModule *handle) { | |
| 465 g_module_close(handle); | |
| 466 return FALSE; | |
| 467 } | |
| 468 | |
| 469 void gaim_plugin_unload(GModule *handle) { | |
| 470 unload_for_real(handle); | |
| 471 gtk_timeout_add(5000, (GtkFunction)unload_timeout, handle); | |
| 471 } | 472 } |
| 472 | 473 |
| 473 void list_clicked(GtkWidget *w, struct gaim_plugin *p) { | 474 void list_clicked(GtkWidget *w, struct gaim_plugin *p) { |
| 474 gchar *temp; | 475 gchar *temp; |
| 475 guint text_len; | 476 guint text_len; |
| 485 temp = g_strdup_printf("%s\n%s", p->name, p->description); | 486 temp = g_strdup_printf("%s\n%s", p->name, p->description); |
| 486 gtk_text_insert(GTK_TEXT(plugtext), NULL, NULL, NULL, temp, -1); | 487 gtk_text_insert(GTK_TEXT(plugtext), NULL, NULL, NULL, temp, -1); |
| 487 g_free(temp); | 488 g_free(temp); |
| 488 | 489 |
| 489 /* Find out if this plug-in has a configuration function */ | 490 /* Find out if this plug-in has a configuration function */ |
| 490 gaim_plugin_config = dlsym(p->handle, "gaim_plugin_config"); | 491 if (g_module_symbol(p->handle, "gaim_plugin_config", (gpointer *)&gaim_plugin_config)) { |
| 491 if ((error = (char *)dlerror()) == NULL) { | |
| 492 confighandle = gtk_signal_connect(GTK_OBJECT(config), "clicked", | 492 confighandle = gtk_signal_connect(GTK_OBJECT(config), "clicked", |
| 493 GTK_SIGNAL_FUNC(gaim_plugin_config), NULL); | 493 GTK_SIGNAL_FUNC(gaim_plugin_config), NULL); |
| 494 gtk_widget_set_sensitive(config, 1); | 494 gtk_widget_set_sensitive(config, 1); |
| 495 } else { | 495 } else { |
| 496 confighandle = 0; | 496 confighandle = 0; |
| 504 plugwindow = NULL; | 504 plugwindow = NULL; |
| 505 config = NULL; | 505 config = NULL; |
| 506 confighandle = 0; | 506 confighandle = 0; |
| 507 } | 507 } |
| 508 | 508 |
| 509 void gaim_signal_connect(void *handle, enum gaim_event which, | 509 void gaim_signal_connect(GModule *handle, enum gaim_event which, |
| 510 void *func, void *data) { | 510 void *func, void *data) { |
| 511 struct gaim_callback *call = g_new0(struct gaim_callback, 1); | 511 struct gaim_callback *call = g_new0(struct gaim_callback, 1); |
| 512 call->handle = handle; | 512 call->handle = handle; |
| 513 call->event = which; | 513 call->event = which; |
| 514 call->function = func; | 514 call->function = func; |
| 517 callbacks = g_list_append(callbacks, call); | 517 callbacks = g_list_append(callbacks, call); |
| 518 sprintf(debug_buff, "Adding callback %d\n", g_list_length(callbacks)); | 518 sprintf(debug_buff, "Adding callback %d\n", g_list_length(callbacks)); |
| 519 debug_print(debug_buff); | 519 debug_print(debug_buff); |
| 520 } | 520 } |
| 521 | 521 |
| 522 void gaim_signal_disconnect(void *handle, enum gaim_event which, void *func) { | 522 void gaim_signal_disconnect(GModule *handle, enum gaim_event which, void *func) { |
| 523 GList *c = callbacks; | 523 GList *c = callbacks; |
| 524 struct gaim_callback *g = NULL; | 524 struct gaim_callback *g = NULL; |
| 525 | 525 |
| 526 while (c) { | 526 while (c) { |
| 527 g = (struct gaim_callback *)c->data; | 527 g = (struct gaim_callback *)c->data; |
| 670 break; | 670 break; |
| 671 | 671 |
| 672 /* struct gaim_connection *, char * */ | 672 /* struct gaim_connection *, char * */ |
| 673 case event_chat_join: | 673 case event_chat_join: |
| 674 case event_chat_leave: | 674 case event_chat_leave: |
| 675 { | |
| 676 void (*function)(struct gaim_connection *, char *, void *) = | |
| 677 g->function; | |
| 678 (*function)(arg1, arg2, g->data); | |
| 679 } | |
| 680 break; | |
| 681 | |
| 682 /* char * */ | |
| 683 case event_buddy_signon: | 675 case event_buddy_signon: |
| 684 case event_buddy_signoff: | 676 case event_buddy_signoff: |
| 685 case event_buddy_away: | 677 case event_buddy_away: |
| 686 case event_buddy_back: | 678 case event_buddy_back: |
| 687 case event_buddy_idle: | 679 case event_buddy_idle: |
| 688 case event_buddy_unidle: | 680 case event_buddy_unidle: |
| 681 { | |
| 682 void (*function)(struct gaim_connection *, char *, void *) = | |
| 683 g->function; | |
| 684 (*function)(arg1, arg2, g->data); | |
| 685 } | |
| 686 break; | |
| 687 | |
| 688 /* char * */ | |
| 689 case event_new_conversation: | 689 case event_new_conversation: |
| 690 { | 690 { |
| 691 void (*function)(char *, void *) = g->function; | 691 void (*function)(char *, void *) = g->function; |
| 692 (*function)(arg1, g->data); | 692 (*function)(arg1, g->data); |
| 693 } | 693 } |
