Kaydet (Commit) ef9360c7 authored tarafından Colomban Wendling's avatar Colomban Wendling

Add plugin_{idle_add,timeout_add,timeout_add_seconds}() to the plugin API

These functions does the same as the corresponding GLib functions but
makes sure that the added GSource will be removed when the plugin is
unloaded, preventing possible crashes.

These are only convenience functions for the plugin author not to have to
care about the case the plugin gets unloaded, he can still manually
manage hes GSources if he wants to.

git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@5650 ea778897-0a13-0410-b9d1-a72fbfd435f5
üst 22038400
2011-03-30 Colomban Wendling <colomban(at)geany(dot)org>
* src/plugindata.h, src/pluginprivate.h, src/plugins.c,
src/pluginutils.c, src/pluginutils.h plugins/geanyfunctions.h:
Add plugin_idle_add(), plugin_timeout_add() and
plugin_timeout_add_seconds() to the plugin API. These are
convenience wrappers to ensure the added timeouts are properly
removed when unloading the plugin, preventing possible crashes.
2011-03-29 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* doc/geany.txt, doc/geany.html:
......
......@@ -26,6 +26,12 @@
geany_functions->p_plugin->plugin_set_key_group
#define plugin_show_configure \
geany_functions->p_plugin->plugin_show_configure
#define plugin_timeout_add \
geany_functions->p_plugin->plugin_timeout_add
#define plugin_timeout_add_seconds \
geany_functions->p_plugin->plugin_timeout_add_seconds
#define plugin_idle_add \
geany_functions->p_plugin->plugin_idle_add
#define document_new_file \
geany_functions->p_document->document_new_file
#define document_get_current \
......
......@@ -54,7 +54,7 @@
* @warning You should not test for values below 200 as previously
* @c GEANY_API_VERSION was defined as an enum value, not a macro.
*/
#define GEANY_API_VERSION 204
#define GEANY_API_VERSION 205
/** The Application Binary Interface (ABI) version, incremented whenever
* existing fields in the plugin data types have to be changed or reordered.
......@@ -645,6 +645,11 @@ typedef struct PluginFuncs
struct GeanyKeyGroup* (*plugin_set_key_group)(GeanyPlugin *plugin,
const gchar *section_name, gsize count, _GeanyKeyGroupCallback callback);
void (*plugin_show_configure)(GeanyPlugin *plugin);
guint (*plugin_timeout_add) (GeanyPlugin *plugin, guint interval, GSourceFunc function,
gpointer data);
guint (*plugin_timeout_add_seconds) (GeanyPlugin *plugin, guint interval,
GSourceFunc function, gpointer data);
guint (*plugin_idle_add) (GeanyPlugin *plugin, GSourceFunc function, gpointer data);
}
PluginFuncs;
......
......@@ -57,6 +57,7 @@ typedef struct GeanyPluginPrivate
GeanyKeyGroup *key_group;
GeanyAutoSeparator toolbar_separator;
GArray *signal_ids; /* SignalConnection's to disconnect when unloading */
GList *sources; /* GSources to destroy when unloading */
}
GeanyPluginPrivate;
......
......@@ -85,7 +85,10 @@ static PluginFuncs plugin_funcs = {
&plugin_module_make_resident,
&plugin_signal_connect,
&plugin_set_key_group,
&plugin_show_configure
&plugin_show_configure,
&plugin_timeout_add,
&plugin_timeout_add_seconds,
&plugin_idle_add
};
static DocumentFuncs doc_funcs = {
......@@ -760,6 +763,22 @@ static void remove_callbacks(Plugin *plugin)
}
static void remove_sources(Plugin *plugin)
{
GList *item;
item = plugin->sources;
while (item != NULL)
{
GList *next = item->next; /* cache the next pointer because current item will be freed */
g_source_destroy(item->data);
item = next;
}
/* don't free the list here, it is allocated inside each source's data */
}
static gboolean is_active_plugin(Plugin *plugin)
{
return (g_list_find(active_plugin_list, plugin) != NULL);
......@@ -776,6 +795,7 @@ plugin_cleanup(Plugin *plugin)
plugin->cleanup();
remove_callbacks(plugin);
remove_sources(plugin);
if (plugin->key_group)
keybindings_free_group(plugin->key_group);
......
......@@ -131,6 +131,143 @@ void plugin_signal_connect(GeanyPlugin *plugin,
}
typedef struct PluginSourceData
{
Plugin *plugin;
GList list_link; /* element of plugin->sources cointaining this GSource */
GSourceFunc function;
gpointer user_data;
} PluginSourceData;
/* use GSlice if available */
#if GLIB_CHECK_VERSION(2,10,0)
# define PSD_ALLOC() (g_slice_alloc(sizeof(PluginSourceData)))
# define PSD_FREE(psd) (g_slice_free1(sizeof(PluginSourceData), (psd)))
#else
# define PSD_ALLOC() (g_malloc(sizeof(PluginSourceData)))
# define PSD_FREE(psd) (g_free(psd))
#endif
/* prepend psd->list_link to psd->plugin->sources */
static void psd_register(PluginSourceData *psd, GSource *source)
{
psd->list_link.data = source;
psd->list_link.prev = NULL;
psd->list_link.next = psd->plugin->sources;
if (psd->list_link.next)
psd->list_link.next->prev = &psd->list_link;
psd->plugin->sources = &psd->list_link;
}
/* removes psd->list_link from psd->plugin->sources */
static void psd_unregister(PluginSourceData *psd)
{
if (psd->list_link.next)
psd->list_link.next->prev = psd->list_link.prev;
if (psd->list_link.prev)
psd->list_link.prev->next = psd->list_link.next;
else /* we were the first of the list, update the plugin->sources pointer */
psd->plugin->sources = psd->list_link.next;
}
static void on_plugin_source_destroy(gpointer data)
{
PluginSourceData *psd = data;
psd_unregister(psd);
PSD_FREE(psd);
}
static gboolean on_plugin_source_callback(gpointer data)
{
PluginSourceData *psd = data;
return psd->function(psd->user_data);
}
/* adds the given source to the default GMainContext and to the list of sources to remove at plugin
* unloading time */
static guint plugin_source_add(GeanyPlugin *plugin, GSource *source, GSourceFunc func, gpointer data)
{
guint id;
PluginSourceData *psd = PSD_ALLOC();
psd->plugin = plugin->priv;
psd->function = func;
psd->user_data = data;
g_source_set_callback(source, on_plugin_source_callback, psd, on_plugin_source_destroy);
psd_register(psd, source);
id = g_source_attach(source, NULL);
g_source_unref(source);
return id;
}
/** Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
* preventing it to run after the plugin has been unloaded (which may lead to a segfault).
*
* @param plugin Must be @ref geany_plugin.
* @param interval The time between calls to the function, in milliseconds.
* @param function The function to call after the given timeout.
* @param data The user data passed to the function.
* @return the ID of the event source (you generally won't need it, or better use g_timeout_add()
* directly if you want to manage this event source manually).
*
* @see g_timeout_add()
* @since 0.21, plugin API 205.
*/
guint plugin_timeout_add(GeanyPlugin *plugin, guint interval, GSourceFunc function, gpointer data)
{
return plugin_source_add(plugin, g_timeout_source_new(interval), function, data);
}
/** Adds a GLib main loop timeout callback that will be removed when unloading the plugin,
* preventing it to run after the plugin has been unloaded (which may lead to a segfault).
*
* @param plugin Must be @ref geany_plugin.
* @param interval The time between calls to the function, in seconds.
* @param function The function to call after the given timeout.
* @param data The user data passed to the function.
* @return the ID of the event source (you generally won't need it, or better use
* g_timeout_add_seconds() directly if you want to manage this event source manually).
*
* @see g_timeout_add_seconds()
* @since 0.21, plugin API 205.
*/
guint plugin_timeout_add_seconds(GeanyPlugin *plugin, guint interval, GSourceFunc function,
gpointer data)
{
return plugin_source_add(plugin, g_timeout_source_new_seconds(interval), function, data);
}
/** Adds a GLib main loop IDLE callback that will be removed when unloading the plugin, preventing
* it to run after the plugin has been unloaded (which may lead to a segfault).
*
* @param plugin Must be @ref geany_plugin.
* @param function The function to call in IDLE time.
* @param data The user data passed to the function.
* @return the ID of the event source (you generally won't need it, or better use g_idle_add()
* directly if you want to manage this event source manually).
*
* @see g_idle_add()
* @since 0.21, plugin API 205.
*/
guint plugin_idle_add(GeanyPlugin *plugin, GSourceFunc function, gpointer data)
{
return plugin_source_add(plugin, g_idle_source_new(), function, data);
}
/** Sets up or resizes a keybinding group for the plugin.
* You should then call keybindings_set_item() for each keybinding in the group.
* @param plugin Must be @ref geany_plugin.
......
......@@ -43,6 +43,14 @@ void plugin_signal_connect(struct GeanyPlugin *plugin,
GObject *object, const gchar *signal_name, gboolean after,
GCallback callback, gpointer user_data);
guint plugin_timeout_add(struct GeanyPlugin *plugin, guint interval, GSourceFunc function,
gpointer data);
guint plugin_timeout_add_seconds(struct GeanyPlugin *plugin, guint interval, GSourceFunc function,
gpointer data);
guint plugin_idle_add(struct GeanyPlugin *plugin, GSourceFunc function, gpointer data);
struct GeanyKeyGroup *plugin_set_key_group(struct GeanyPlugin *plugin,
const gchar *section_name, gsize count, GeanyKeyGroupCallback callback);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment