/* This file is part of GNUnet (C) 2003, 2004, 2005 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/common/helper.c * @brief This file contains some GUI helper functions * @author Igor Wronsky * @author Christian Grothoff */ #include "platform.h" #include "gnunetgtk_common.h" #define HELPER_DEBUG NO typedef struct { Semaphore * sem; void * args; SimpleCallback func; } SaveCall; typedef struct Plugin { struct Plugin * next; char * name; void * library; } Plugin; static GladeXML * mainXML; static char * gladeFile; static GladeXML * statusXML; static GtkWidget * infoWindow; static GtkWidget * infoWindowTextView; /** * the main thread */ static PTHREAD_T mainThread; static SaveCall ** psc; static unsigned int pscCount; static Mutex sclock; static int saveCallsUp; static Plugin * plugin; static gboolean saveCallWrapper(gpointer data) { SaveCall * call = data; call->func(call->args); if (call->sem != NULL) SEMAPHORE_UP(call->sem); return FALSE; } /** * Call a callback function from the mainloop/main thread ("SaveCall"). * Since GTK doesn't work with multi-threaded applications under Windows, * all GTK operations have to be done in the main thread */ void gtkSaveCall(SimpleCallback func, void * args) { SaveCall call; int i; MUTEX_LOCK(&sclock); if ( (saveCallsUp == NO) || (! PTHREAD_SELF_TEST(&mainThread)) ) { call.args = args; call.func = func; call.sem = SEMAPHORE_NEW(0); GROW(psc, pscCount, pscCount+1); psc[pscCount-1] = &call; MUTEX_UNLOCK(&sclock); gtk_idle_add(&saveCallWrapper, &call); SEMAPHORE_DOWN(call.sem); /* remove from psc list */ MUTEX_LOCK(&sclock); for (i=0;iargs)) psc[i]->func(psc[i]); MUTEX_UNLOCK(&sclock); gnunet_util_sleep(50 * cronMILLIS); /* sleep here is somewhat important, first of all, after completion we need to give the semaphore-mechanism time to remove the save-call from the list to avoid running it twice; also, this function might be called in a tight loop (see search.c), so we should give the other threads some time to run. */ return YES; } /** * Callback for handling "delete_event": close the window */ gint on_statusWindow_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data) { return FALSE; } /** * Closure for doInfoMessage. */ typedef struct { int doPopup; char * note; } InfoMessage; /** * Callback for infoMessage() */ static void doInfoMessage(void * args) { const InfoMessage * info = args; GtkTextIter iter; GtkTextBuffer * buffer; if (info->doPopup==YES) gtk_widget_show(infoWindow); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(infoWindowTextView)); gtk_text_buffer_get_iter_at_offset(buffer, &iter, -1); gtk_text_buffer_insert(buffer, &iter, info->note, -1); } /** * Appends a message to the info window * * @param doPopup do we open the window, YES or NO */ void infoMessage(int doPopup, const char * format, ...) { va_list args; InfoMessage info; va_start(args, format); info.note = g_strdup_vprintf(format, args); va_end(args); info.doPopup = doPopup; gtkSaveCall(&doInfoMessage, &info); g_free(info.note); } static void saveAddLogEntry(void * args) { static GtkWidget * s = NULL; static int once = 1; static guint id; if (once) { once = 0; s = glade_xml_get_widget(mainXML, "statusbar"); id = gtk_statusbar_get_context_id(GTK_STATUSBAR(s), "LOG"); } else gtk_statusbar_pop(GTK_STATUSBAR(s), id); gtk_statusbar_push(GTK_STATUSBAR(s), id, (const char*) args); } /** * Appends a log entry to the info window * * @param txt the log entry * */ void addLogEntry(const char * txt) { infoMessage(NO, txt); gtkSaveCall(&saveAddLogEntry, (void*) txt); } /** * Simple accessor method. */ const char * getGladeFileName() { return gladeFile; } /** * Simple accessor method. */ GladeXML * getMainXML() { return mainXML; } static void connector(const gchar *handler_name, GObject *object, const gchar *signal_name, const gchar *signal_data, GObject *connect_object, gboolean after, gpointer user_data) { GladeXML * xml = user_data; Plugin * plug; void * method; plug = plugin; method = NULL; while (plug != NULL) { method = trybindDynamicMethod(plug->library, "", handler_name); if (method != NULL) break; plug = plug->next; } if (method == NULL) { LOG(LOG_DEBUG, _("Failed to find handler for '%s'\n"), handler_name); return; } glade_xml_signal_connect(xml, handler_name, (GCallback) method); } void connectGladeWithPlugins(GladeXML * xml) { glade_xml_signal_autoconnect_full(xml, &connector, xml); } typedef void (*PlainCall)(); static void loadPlugin(const char * name) { Plugin * p; void * lib; PlainCall init; lib = loadDynamicLibrary("libgnunetgtkmodule_", name); if (lib == NULL) { LOG(LOG_WARNING, _("Failed to load plugin '%s'\n"), name); return; } p = MALLOC(sizeof(Plugin)); p->name = STRDUP(name); p->next = plugin; p->library = lib; plugin = p; init = trybindDynamicMethod(lib, "init_", name); if (init != NULL) init(); } static void loadPlugins(const char * names) { char * dup; char * next; const char * pos; if (names == NULL) return; dup = STRDUP(names); next = dup; do { while (*next == ' ') next++; pos = next; while ( (*next != '\0') && (*next != ' ') ) next++; if (*next == '\0') { next = NULL; /* terminate! */ } else { *next = '\0'; /* add 0-termination for pos */ next++; } if (strlen(pos) > 0) { LOG(LOG_DEBUG, "Loading plugin '%s'\n", pos); loadPlugin(pos); } } while (next != NULL); FREE(dup); } static void unloadPlugin(Plugin * plug) { PlainCall done; done = trybindDynamicMethod(plug->library, "done_", plug->name); if (done != NULL) done(); unloadDynamicLibrary(plug->library); FREE(plug->name); FREE(plug); } void initGNUnetGTKCommon() { char * load; MUTEX_CREATE_RECURSIVE(&sclock); PTHREAD_GET_SELF(&mainThread); saveCallsUp = YES; /* load the interface */ #ifdef MINGW gladeFile = MALLOC(_MAX_PATH + 1); plibc_conv_to_win_path(PACKAGE_DATA_DIR"/gnunet-gtk.glade", gladeFile); #else gladeFile = STRDUP(PACKAGE_DATA_DIR"/gnunet-gtk.glade"); #endif mainXML = glade_xml_new(gladeFile, "mainWindow", NULL); statusXML = glade_xml_new(getGladeFileName(), "statusWindow", NULL); infoWindow = glade_xml_get_widget(statusXML, "statusWindow"); infoWindowTextView = glade_xml_get_widget(statusXML, "messageWindowTextView"); /* load the plugins */ load = getConfigurationString("GNUNET-GTK", "PLUGINS"); if (load == NULL) load = STRDUP("about daemon fs"); loadPlugins(load); FREE(load); connectGladeWithPlugins(mainXML); connectGladeWithPlugins(statusXML); } void doneGNUnetGTKCommon() { int i; /* unload the plugins */ while (plugin != NULL) { Plugin * next; next = plugin->next; unloadPlugin(plugin); plugin = next; } gtk_widget_destroy(infoWindow); infoWindow = NULL; UNREF(statusXML); UNREF(mainXML); mainXML = NULL; FREE(gladeFile); gladeFile = NULL; saveCallsUp = NO; PTHREAD_REL_SELF(&mainThread); MUTEX_LOCK(&sclock); for (i=0;ifunc(psc[i]); i = pscCount; MUTEX_UNLOCK(&sclock); /* wait until all PSC-jobs have left the gtkSaveCall method before destroying the mutex! */ while (i != 0) { gnunet_util_sleep(50 * cronMILLIS); MUTEX_LOCK(&sclock); i = pscCount; MUTEX_UNLOCK(&sclock); } MUTEX_DESTROY(&sclock); } /* end of helper.c */