view libvisual/lv_plugin.c @ 23:0db4a1dc75c4 trunk

[svn] libvisual. P3 detection appears to be borked. I'll work on it later.
author nenolod
date Mon, 24 Oct 2005 23:13:56 -0700
parents
children
line wrap: on
line source

/* Libvisual - The audio visualisation framework.
 * 
 * Copyright (C) 2004, 2005 Dennis Smit <ds@nerds-incorporated.org>
 *
 * Authors: Dennis Smit <ds@nerds-incorporated.org>
 *
 * $Id:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <dirent.h>

#include "lvconfig.h"
#include "lv_libvisual.h"
#include "lv_plugin.h"
#include "lv_log.h"
#include "lv_mem.h"

extern VisList *__lv_plugins;

static int plugin_info_dtor (VisObject *object);
static int plugin_ref_dtor (VisObject *object);
static int plugin_environ_dtor (VisObject *object);
static int plugin_dtor (VisObject *object);

static int plugin_add_dir_to_list (VisList *list, const char *dir);
static char *get_delim_node (const char *str, char delim, int index);

static int plugin_info_dtor (VisObject *object)
{
	VisPluginInfo *pluginfo = VISUAL_PLUGININFO (object);

	if (pluginfo->plugname != NULL)
		visual_mem_free (pluginfo->plugname);

	if (pluginfo->type != NULL)
		visual_mem_free (pluginfo->type);

	if (pluginfo->name != NULL)
		visual_mem_free (pluginfo->name);

	if (pluginfo->author != NULL)
		visual_mem_free (pluginfo->author);

	if (pluginfo->version != NULL)
		visual_mem_free (pluginfo->version);

	if (pluginfo->about != NULL)
		visual_mem_free (pluginfo->about);

	if (pluginfo->help != NULL)
		visual_mem_free (pluginfo->help);

	pluginfo->plugname = NULL;
	pluginfo->type = NULL;
	pluginfo->name = NULL;
	pluginfo->author = NULL;
	pluginfo->version = NULL;
	pluginfo->about = NULL;
	pluginfo->help = NULL;

	return VISUAL_OK;
}

static int plugin_ref_dtor (VisObject *object)
{
	VisPluginRef *ref = VISUAL_PLUGINREF (object);

	if (ref->file != NULL)
		visual_mem_free (ref->file);

	if (ref->usecount > 0)
		visual_log (VISUAL_LOG_CRITICAL, "A plugin reference with %d instances has been destroyed.", ref->usecount);

	if (ref->info != NULL)
		visual_object_unref (VISUAL_OBJECT (ref->info));
	
	ref->file = NULL;
	ref->info = NULL;

	return VISUAL_OK;
}

static int plugin_environ_dtor (VisObject *object)
{
	VisPluginEnviron *enve = VISUAL_PLUGINENVIRON (object);

	if (enve->environ != NULL)
		visual_object_unref (enve->environ);

	enve->environ = NULL;

	return VISUAL_OK;
}

static int plugin_dtor (VisObject *object)
{
	VisPluginData *plugin = VISUAL_PLUGINDATA (object);

	if (plugin->ref != NULL)	
		visual_object_unref (VISUAL_OBJECT (plugin->ref));

	if (plugin->params != NULL)
		visual_object_unref (VISUAL_OBJECT (plugin->params));

	visual_list_destroy_elements (&plugin->environ);	
	
	plugin->ref = NULL;
	plugin->params = NULL;

	return VISUAL_OK;
}

static char *get_delim_node (const char *str, char delim, int index)
{
	char *buf;
	const char *start;
	const char *end = str;
	int i = 0;

	do {
		start = end;

		end = strchr (start + 1, delim);

		if (i == index) {
			/* Last section doesn't contain a delim */
			if (end == NULL)
				end = str + strlen (str);
			
			/* Cut off the delim that is in front */
			if (i > 0)
				start++;
			
			break;
		}

		i++;

	} while (end != NULL);

	if (end == NULL)
		return NULL;

	buf = visual_mem_malloc0 ((end - start) + 1);
	strncpy (buf, start, end - start);

	return buf;
}

/**
 * @defgroup VisPlugin VisPlugin
 * @{
 */

/**
 * Creates a new VisPluginInfo structure.
 *
 * @return A newly allocated VisPluginInfo
 */
VisPluginInfo *visual_plugin_info_new ()
{
	VisPluginInfo *pluginfo;

	pluginfo = visual_mem_new0 (VisPluginInfo, 1);

	/* Do the VisObject initialization */
	visual_object_initialize (VISUAL_OBJECT (pluginfo), TRUE, plugin_info_dtor);

	return pluginfo;
}

/**
 * Copies data from one VisPluginInfo to another, this does not copy everything
 * but only things that are needed in the local copy for the plugin registry.
 *
 * @param dest Pointer to the destination VisPluginInfo in which some data is copied.
 * @param src Pointer to the source VisPluginInfo from which some data is copied.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_INFO_NULL on failure.
 */
int visual_plugin_info_copy (VisPluginInfo *dest, VisPluginInfo *src)
{
	visual_log_return_val_if_fail (dest != NULL, -VISUAL_ERROR_PLUGIN_INFO_NULL);
	visual_log_return_val_if_fail (src != NULL, -VISUAL_ERROR_PLUGIN_INFO_NULL);

	visual_mem_copy (dest, src, sizeof (VisPluginInfo));

	dest->plugname = strdup (src->plugname);
	dest->type = strdup (src->type);
	dest->name = strdup (src->name);
	dest->author = strdup (src->author);
	dest->version = strdup (src->version);
	dest->about = strdup (src->about);
	dest->help = strdup (src->help);

	return VISUAL_OK;
}

/**
 * Pumps the queued events into the plugin it's event handler if it has one.
 *
 * @param plugin Pointer to a VisPluginData of which the events need to be pumped into
 *	the handler.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL or -VISUAL_ERROR_PLUGIN_NO_EVENT_HANDLER on failure.
 */
int visual_plugin_events_pump (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);

	if (plugin->info->events != NULL) {
		plugin->info->events (plugin, &plugin->eventqueue);

		return VISUAL_OK;
	}

	return -VISUAL_ERROR_PLUGIN_NO_EVENT_HANDLER;
}

/**
 * Gives the event queue from a VisPluginData. This queue needs to be used
 * when you want to send events to the plugin.
 *
 * @see visual_plugin_events_pump
 *
 * @param plugin Pointer to the VisPluginData from which we want the queue.
 *
 * @return A pointer to the requested VisEventQueue or NULL on failure.
 */
VisEventQueue *visual_plugin_get_eventqueue (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, NULL);

	return &plugin->eventqueue;
}

/**
 * Sets a VisUIWidget as top user interface widget for the plugin. When a VisUI
 * tree is requested by a client, to render a configuration userinterface, this
 * VisUIWidget is used as top widget.
 *
 * @param plugin Pointer to the VisPluginData to which we set the VisUIWidget as top widget.
 * @param widget Pointer to the VisUIWidget that we use as top widget for the user interface.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL on failure.
 */
int visual_plugin_set_userinterface (VisPluginData *plugin, VisUIWidget *widget)
{
	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);

	plugin->userinterface = widget;

	return VISUAL_OK;
}

/**
 * Retrieves the VisUI top widget for the plugin.
 *
 * @param plugin Pointer to the VisPluginData of which we request the VisUIWidget that serves as top widget.
 *
 * @return Pointer to the VisUIWidget that serves as top widget, possibly NULL.
 */
VisUIWidget *visual_plugin_get_userinterface (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, NULL);

	return plugin->userinterface;
}

/**
 * Gives the VisPluginInfo related to a VisPluginData.
 *
 * @param plugin The VisPluginData of which the VisPluginInfo is requested.
 *
 * @return The VisPluginInfo within the VisPluginData, or NULL on failure.
 */
VisPluginInfo *visual_plugin_get_info (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, NULL);

	return plugin->info;
}

/**
 * Gives the VisParamContainer related to a VisPluginData.
 *
 * @param plugin The VisPluginData of which the VisParamContainer is requested.
 *
 * @return The VisParamContainer within the VisPluginData, or NULL on failure.
 */
VisParamContainer *visual_plugin_get_params (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, NULL);

	return plugin->params;
}

/**
 * Gives the VisRandomContext related to a VisPluginData.
 *
 * @param plugin The VisPluginData of which the VisRandomContext is requested.
 *
 * @return The VisRandomContext within the VisPluginDAta, or NULL on failure.
 */
VisRandomContext *visual_plugin_get_random_context (VisPluginData *plugin)
{
	visual_log_return_val_if_fail (plugin != NULL, NULL);

	return &plugin->random;
}

/**
 * Retrieves the plugin specific part of a plugin.
 *
 * @see VISUAL_PLUGIN_ACTOR
 * @see VISUAL_PLUGIN_INPUT
 * @see VISUAL_PLUGIN_MORPH
 * 
 * @param plugin The pointer to the VisPluginData from which we want the plugin specific part.
 *
 * @return Void * pointing to the plugin specific part which can be cast.
 */
void *visual_plugin_get_specific (VisPluginData *plugin)
{
	VisPluginInfo *pluginfo;

	visual_log_return_val_if_fail (plugin != NULL, NULL);

	pluginfo = visual_plugin_get_info (plugin);
	visual_log_return_val_if_fail (pluginfo != NULL, NULL);
	
	return pluginfo->plugin;
}

/**
 * Creates a new VisPluginRef structure.
 *
 * The VisPluginRef contains data for the plugin loader.
 *
 * @return Newly allocated VisPluginRef.
 */
VisPluginRef *visual_plugin_ref_new ()
{
	VisPluginRef *ref;

	ref = visual_mem_new0 (VisPluginRef, 1);

	/* Do the VisObject initialization */
	visual_object_initialize (VISUAL_OBJECT (ref), TRUE, plugin_ref_dtor);

	return ref;
}

/**
 * Creates a new VisPluginData structure.
 *
 * @return A newly allocated VisPluginData.
 */
VisPluginData *visual_plugin_new ()
{
	VisPluginData *plugin;

	plugin = visual_mem_new0 (VisPluginData, 1);
	
	/* Do the VisObject initialization */
	visual_object_initialize (VISUAL_OBJECT (plugin), TRUE, plugin_dtor);

	plugin->params = visual_param_container_new ();

	return plugin;
}

/**
 * Gives a VisList that contains references to all the plugins in the registry.
 *
 * @see VisPluginRef
 * 
 * @return VisList of references to all the libvisual plugins.
 */
VisList *visual_plugin_get_registry ()
{
	return __lv_plugins;
}

/**
 * Gives a newly allocated VisList with references for one plugin type.
 *
 * @see VisPluginRef
 *
 * @param pluglist Pointer to the VisList that contains the plugin registry.
 * @param domain The plugin type that is filtered for.
 *
 * @return Newly allocated VisList that is a filtered version of the plugin registry.
 */
VisList *visual_plugin_registry_filter (VisList *pluglist, const char *domain)
{
	VisList *list;
	VisListEntry *entry = NULL;
	VisPluginRef *ref;

	visual_log_return_val_if_fail (pluglist != NULL, NULL);

	list = visual_list_new (visual_object_list_destroyer);

	if (list == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot create a new list");

		return NULL;
	}

	while ((ref = visual_list_next (pluglist, &entry)) != NULL) {
		
		if (visual_plugin_type_member_of (ref->info->type, domain)) {
			visual_object_ref (VISUAL_OBJECT (ref));
			
			visual_list_add (list, ref);
		}
	}

	return list;
}

/**
 * Get the next plugin based on it's name.
 *
 * @see visual_plugin_registry_filter
 * 
 * @param list Pointer to the VisList containing the plugins. Adviced is to filter
 *	this list first using visual_plugin_registry_filter.
 * @param name Name of a plugin entry of which we want the next entry or NULL to get
 * 	the first entry.
 *
 * @return The name of the next plugin or NULL on failure.
 */
const char *visual_plugin_get_next_by_name (VisList *list, const char *name)
{
	VisListEntry *entry = NULL;
	VisPluginRef *ref;
	int tagged = FALSE;

	visual_log_return_val_if_fail (list != NULL, NULL);

	while ((ref = visual_list_next (list, &entry)) != NULL) {
		if (name == NULL)
			return ref->info->plugname;

		if (tagged == TRUE)
			return ref->info->plugname;

		if (strcmp (name, ref->info->plugname) == 0)
			tagged = TRUE;
	}

	return NULL;
}

/**
 * Get the previous plugin based on it's name.
 *
 * @see visual_plugin_registry_filter
 * 
 * @param list Pointer to the VisList containing the plugins. Adviced is to filter
 *	this list first using visual_plugin_registry_filter.
 * @param name Name of a plugin entry of which we want the previous entry or NULL to get
 * 	the last entry.
 *
 * @return The name of the next plugin or NULL on failure.
 */
const char *visual_plugin_get_prev_by_name (VisList *list, const char *name)
{
	VisListEntry *entry = NULL;
	VisPluginRef *ref, *pref = NULL;
	
	visual_log_return_val_if_fail (list != NULL, NULL);

	if (name == NULL) {
		ref = visual_list_get (list, visual_list_count (list) - 1);
		
		if (ref == NULL)
			return NULL;
		
		return ref->info->plugname;
	}

	while ((ref = visual_list_next (list, &entry)) != NULL) {
		if (strcmp (name, ref->info->plugname) == 0) {
			if (pref != NULL)
				return pref->info->plugname;
			else
				return NULL;
		}

		pref = ref;
	}

	return NULL;
}

static int plugin_add_dir_to_list (VisList *list, const char *dir)
{
	VisPluginRef **ref;
	char temp[1024];
	struct dirent **namelist;
	int i, j, n, len;
	int cnt = 0;

	n = scandir (dir, &namelist, 0, alphasort);

	if (n < 0)
		return -1;

	/* Free the . and .. entries */
	visual_mem_free (namelist[0]);
	visual_mem_free (namelist[1]);

	for (i = 2; i < n; i++) {
		ref = NULL;

		snprintf (temp, 1023, "%s/%s", dir, namelist[i]->d_name);

		len = strlen (temp);
		if (len > 3 && (strncmp (&temp[len - 3], ".so", 3)) == 0)
			ref = visual_plugin_get_references (temp, &cnt);

		if (ref != NULL) {
			for (j = 0; j < cnt; j++) 
				visual_list_add (list, ref[j]);
		
			/* This is the pointer pointer pointer, not a ref itself */
			visual_mem_free (ref);
		}

		visual_mem_free (namelist[i]);
	}

	visual_mem_free (namelist);

	return 0;
}

/**
 * Private function to unload a plugin. After calling this function the
 * given argument is no longer usable.
 *
 * @param plugin Pointer to the VisPluginData that needs to be unloaded.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL, -VISUAL_ERROR_PLUGIN_HANDLE_NULL or
 *	-VISUAL_ERROR_PLUGIN_REF_NULL on failure.
 */
int visual_plugin_unload (VisPluginData *plugin)
{
	VisPluginRef *ref;

	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);

	ref = plugin->ref;

	/* Not loaded */
	if (plugin->handle == NULL) {
		visual_object_unref (VISUAL_OBJECT (plugin));

		visual_log (VISUAL_LOG_CRITICAL, "Tried unloading a plugin that never has been loaded.");

		return -VISUAL_ERROR_PLUGIN_HANDLE_NULL;
	}
	
	if (plugin->realized == TRUE)
		plugin->info->cleanup (plugin);

	if (plugin->info->plugin != NULL)
		visual_object_unref (VISUAL_OBJECT (plugin->info->plugin));

	if (plugin->info != NULL)
		visual_object_unref (VISUAL_OBJECT (plugin->info));

	dlclose (plugin->handle);
	plugin->info = NULL;

	if (ref != NULL) {
		if (ref->usecount > 0)
			ref->usecount--;
	}

	visual_param_container_set_eventqueue (plugin->params, NULL);

	visual_object_unref (VISUAL_OBJECT (plugin));
	
	return VISUAL_OK;
}

/**
 * Private function to load a plugin.
 *
 * @param ref Pointer to the VisPluginRef containing information about
 *	the plugin that needs to be loaded.
 *
 * @return A newly created and loaded VisPluginData.
 */
VisPluginData *visual_plugin_load (VisPluginRef *ref)
{
	VisPluginData *plugin;
	VisTime time_;
	VisPluginInfo *pluginfo;
	VisPluginGetInfoFunc get_plugin_info;
	void *handle;
	int cnt;

	visual_log_return_val_if_fail (ref != NULL, NULL);
	visual_log_return_val_if_fail (ref->info != NULL, NULL);

	/* Check if this plugin is reentrant */
	if (ref->usecount > 0 && (ref->info->flags & VISUAL_PLUGIN_FLAG_NOT_REENTRANT)) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot load plugin %s, the plugin is already loaded and is not reentrant.",
				ref->info->plugname);

		return NULL;
	}

	handle = dlopen (ref->file, RTLD_LAZY);

	if (handle == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot load plugin: %s", dlerror ());

		return NULL;
	}

	get_plugin_info = (VisPluginGetInfoFunc) dlsym (handle, "get_plugin_info");
	
	if (get_plugin_info == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot initialize plugin: %s", dlerror ());

		dlclose (handle);

		return NULL;
	}

	pluginfo = VISUAL_PLUGININFO (get_plugin_info (&cnt));

	if (pluginfo == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot get plugin info while loading.");

		dlclose (handle);
		
		return NULL;
	}

	plugin = visual_plugin_new ();
	plugin->ref = ref;
	plugin->info = &pluginfo[ref->index];

	visual_object_ref (VISUAL_OBJECT (ref));

	ref->usecount++;
	plugin->realized = FALSE;
	plugin->handle = handle;

	/* Now the plugin is set up and ready to be realized, also random seed it's random context */
	visual_time_get (&time_);
	visual_random_context_set_seed (&plugin->random, time_.tv_usec);

	return plugin;
}

/**
 * Private function to realize the plugin. This initializes the plugin.
 *
 * @param plugin Pointer to the VisPluginData that needs to be realized.
 * 
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL or -VISUAL_ERROR_PLUGIN_ALREADY_REALIZED on failure.
 */
int visual_plugin_realize (VisPluginData *plugin)
{
	VisParamContainer *paramcontainer;

	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);

	if (plugin->realized == TRUE)
		return -VISUAL_ERROR_PLUGIN_ALREADY_REALIZED;

	paramcontainer = visual_plugin_get_params (plugin);
	visual_param_container_set_eventqueue (paramcontainer, &plugin->eventqueue);

	plugin->info->init (plugin);
	plugin->realized = TRUE;

	return VISUAL_OK;
}

/**
 * Private function to create VisPluginRefs from plugins.
 *
 * @param pluginpath The full path and filename to the plugin of which a reference
 *	needs to be obtained.
 * @param count Int pointer that will contain the number of VisPluginRefs returned.
 *
 * @return The optionally newly allocated VisPluginRefs for the plugin.
 */
VisPluginRef **visual_plugin_get_references (const char *pluginpath, int *count)
{
	VisPluginRef **ref;
	VisPluginInfo *plug_info;
	VisPluginInfo *dup_info;
	const char *plug_name;
	VisPluginGetInfoFunc get_plugin_info;
	void *handle;
	int cnt = 1, i;

	visual_log_return_val_if_fail (pluginpath != NULL, NULL);

	handle = dlopen (pluginpath, RTLD_LAZY);
	
	if (handle == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot load plugin: %s", dlerror ());

		return NULL;
	}

	get_plugin_info = (VisPluginGetInfoFunc) dlsym (handle, "get_plugin_info");

	if (get_plugin_info == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot initialize plugin: %s", dlerror ());

		dlclose (handle);

		return NULL;
	}

	plug_info = VISUAL_PLUGININFO (get_plugin_info (&cnt));

	if (plug_info == NULL) {
		visual_log (VISUAL_LOG_CRITICAL, "Cannot get plugin info");

		dlclose (handle);
		
		return NULL;
	}

	/* FIXME we do leak the object when it fails the sanity check, tho
	 * it is not always safe (with VERY stuff laying around) to unref it. */
	
	/* Check for API and struct size */
	if (plug_info[0].struct_size != sizeof (VisPluginInfo) ||
			plug_info[0].api_version != VISUAL_PLUGIN_API_VERSION) {

		visual_log (VISUAL_LOG_CRITICAL, "Plugin %s is not compatible with version %s of libvisual",
				pluginpath, visual_get_version ());

		dlclose (handle);

		return NULL;
	}

	ref = visual_mem_new0 (VisPluginRef *, cnt);
	
	for (i = 0; i < cnt; i++) {
		ref[i] = visual_plugin_ref_new ();

		dup_info = visual_plugin_info_new ();
		visual_plugin_info_copy (dup_info, &plug_info[i]);
		
		ref[i]->index = i;
		ref[i]->info = dup_info;
		ref[i]->file = strdup (pluginpath);

		visual_object_unref (plug_info[i].plugin);
		visual_object_unref (VISUAL_OBJECT (&plug_info[i]));
	}

	dlclose (handle);
	
	*count = cnt;	

	return ref;
}

/**
 * Private function to create the complete plugin registry from a set of paths.
 *
 * @param paths A pointer list to a set of paths.
 *
 * @return A newly allocated VisList containing the plugin registry for the set of paths.
 */
VisList *visual_plugin_get_list (const char **paths)
{
	VisList *list;
	int i = 0;

	list = visual_list_new (visual_object_list_destroyer);
	
	while (paths[i] != NULL) {
		if (plugin_add_dir_to_list (list, paths[i]) < 0) {
			visual_log (VISUAL_LOG_WARNING, "Failed to add the %s directory to the plugin registry",
					paths[i]);
		}

		i++;
	}
	
	return list;
}

/**
 * Private function to find a plugin in a plugin registry.
 *
 * @param list Pointer to a VisList containing VisPluginRefs in which
 *	the search is done.
 * @param name The name of the plugin we're looking for.
 *
 * @return The VisPluginRef for the plugin if found, or NULL when not found.
 */
VisPluginRef *visual_plugin_find (VisList *list, const char *name)
{
	VisListEntry *entry = NULL;
	VisPluginRef *ref;

	while ((ref = visual_list_next (list, &entry)) != NULL) {

		if (ref->info->plugname == NULL)
			continue;

		if (strcmp (name, ref->info->plugname) == 0)
			return ref;
	}

	return NULL;
}

/**
 * Gives the VISUAL_PLUGIN_API_VERSION value for which the library is compiled.
 * This can be used to check against for API/ABI compatibility check.
 *
 * @return The VISUAL_PLUGIN_API_VERSION define value.
 */
int visual_plugin_get_api_version ()
{
	return VISUAL_PLUGIN_API_VERSION;
}

/**
 * Retrieves the VisSongInfo from a VisActorPlugin.
 *
 * @param actplugin Pointer to the VisActorPlugin from which the VisSongInfo is requested.
 *
 * @return The requested VisSongInfo or NULL on failure.
 */
VisSongInfo *visual_plugin_actor_get_songinfo (VisActorPlugin *actplugin)
{
	visual_log_return_val_if_fail (actplugin != NULL, NULL);

	return &actplugin->songinfo;
}

/**
 * Get the domain part from a plugin type string.
 *
 * @param type The type string.
 *
 * @return A newly allocated string containing the domain part of this plugin type, or NULL on failure.
 */
const char *visual_plugin_type_get_domain (const char *type)
{
	visual_log_return_val_if_fail (type != NULL, NULL);

	return get_delim_node (type, ':', 0);
}

/**
 * Get the package part from a plugin type string.
 *
 * @param type The type string.
 *
 * @return A newly allocated string containing the package part of this plugin type, or NULL on failure.
 */
const char *visual_plugin_type_get_package (const char *type)
{
	visual_log_return_val_if_fail (type != NULL, NULL);

	return get_delim_node (type, ':', 1);
}

/**
 * Get the type part from a plugin type string.
 *
 * @param type The type string.
 *
 * @return A newly allocated string containing the type part of this plugin type, or NULL on failure.
 */
const char *visual_plugin_type_get_type (const char *type)
{
	visual_log_return_val_if_fail (type != NULL, NULL);

	return get_delim_node (type, ':', 2);
}

/**
 * Get the depth of a plugin type string.
 *
 * @param type The type string.
 *
 * @return A VisPluginTypeDepth enum value that describes out of how many parts this plugin
 *	type string consists, -VISUAL_ERROR_NULL on failure.
 */
VisPluginTypeDepth visual_plugin_type_get_depth (const char *type)
{
	int i = 0;

	visual_log_return_val_if_fail (type != NULL, -VISUAL_ERROR_NULL);

	while (i < VISUAL_PLUGIN_TYPE_DEPTH_TYPE) {
		char *part;

		part = get_delim_node (type, ':', i);

		if (part == NULL)
			break;

		i++;

		visual_mem_free (part);
	}

	return i;
}

/**
 * Check if a certain plugin type string falls within the domain of the other.
 *
 * @param domain The domain in which the type string should fall.
 * @param type The type string that is checked against the given domain.
 *
 * @return TRUE if it falls within the domain, FALSE when not, -VISUAL_ERROR_NULL on failure
 */
int visual_plugin_type_member_of (const char *domain, const char *type)
{
	char *comp1;
	char *comp2;
	int diff = 0;
	int i = 0;

	visual_log_return_val_if_fail (type != NULL, -VISUAL_ERROR_NULL);

	while (i < visual_plugin_type_get_depth (domain)) {
		comp1 = get_delim_node (domain, ':', i);
		comp2 = get_delim_node (type, ':', i);

		if (comp1 == NULL)
			return FALSE;

		if (comp2 == NULL)
			return FALSE;

		if (strcmp (comp1, comp2) != 0)
			diff++;

		visual_mem_free (comp1);
		visual_mem_free (comp2);

		i++;
	}

	if (diff > 0)
		return FALSE;

	return TRUE;
}

/**
 * Creates a VisPluginEnviron structure.
 *
 * @param type The Environ type that is requested.
 * @param envobj The VisObject connected to this Environ type.
 *
 * @return A newly allocated VisPluginEnviron, or NULL on failure.
 */
VisPluginEnviron *visual_plugin_environ_new (const char *type, VisObject *envobj)
{
	VisPluginEnviron *enve;

	enve = visual_mem_new0 (VisPluginEnviron, 1);

	/* Do the VisObject initialization */
	visual_object_initialize (VISUAL_OBJECT (enve), TRUE, plugin_environ_dtor);

	enve->type = type;
	enve->environ = envobj;
	
	return enve;
}

/**
 * Adds a VisPluginEnviron to the plugin it's environment list.
 *
 * @param plugin Pointer to the VisPluginData to which the VisPluginEnviron is added.
 * @param enve Pointer to the VisPluginEnviron that is added to the VisPluginData.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL, -VISUAL_ERROR_PLUGIN_ENVIRON_NULL,
 *	-VISUAL_ERROR_NULL or error values returned by visual_list_add() on failure.
 */
int visual_plugin_environ_add (VisPluginData *plugin, VisPluginEnviron *enve)
{
	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);
	visual_log_return_val_if_fail (enve != NULL, -VISUAL_ERROR_PLUGIN_ENVIRON_NULL);
	visual_log_return_val_if_fail (enve->type != NULL, -VISUAL_ERROR_NULL);

	visual_plugin_environ_remove (plugin, enve->type);
		
	return visual_list_add (&plugin->environ, enve);
}

/**
 * Removes a VisPluginEnviron from the plugin it's environment list.
 *
 * @param plugin Pointer to the VisPluginData from which the VisPluginEnviron is removed.
 * @param type The Environ type that is removed.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_PLUGIN_NULL or -VISUAL_ERROR_NULL on failure.
 */
int visual_plugin_environ_remove (VisPluginData *plugin, const char *type)
{
	VisPluginEnviron *enve;
	VisListEntry *le = NULL;

	visual_log_return_val_if_fail (plugin != NULL, -VISUAL_ERROR_PLUGIN_NULL);
	visual_log_return_val_if_fail (type != NULL, -VISUAL_ERROR_NULL);

	while ((enve = visual_list_next (&plugin->environ, &le)) != NULL) {
		
		/* Remove from list */
		if (strcmp (enve->type, type) == 0) {
			visual_list_delete (&plugin->environ, &le);

			visual_object_unref (VISUAL_OBJECT (enve));

			return VISUAL_OK;
		}
	}

	return VISUAL_OK;
}

/**
 * Retrieves a VisPluginEnviron from the plugin it's environment list.
 *
 * @param plugin Pointer to the VisPluginData from which the VisPluginEnviron is requested.
 * @param type The Environ type that is requested.
 *
 * @return The requested VisPluginEnviron it's environ specific VisObject, or NULL on failure
 */
VisObject *visual_plugin_environ_get (VisPluginData *plugin, const char *type)
{
	VisPluginEnviron *enve;
	VisListEntry *le = NULL;
	
	visual_log_return_val_if_fail (plugin != NULL, NULL);
	visual_log_return_val_if_fail (type != NULL, NULL);

	while ((enve = visual_list_next (&plugin->environ, &le)) != NULL) {
		
		if (strcmp (enve->type, type) == 0)
			return enve->environ;
	}

	return NULL;
}

/**
 * @}
 */