Work in progress, just for review
This commit is contained in:
parent
0de8169466
commit
a313ea87a5
2 changed files with 271 additions and 0 deletions
159
src/lib/plugin.c
Normal file
159
src/lib/plugin.c
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#include <stdio.h>
|
||||
#include <search.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
NOBUG_DEFINE_FLAG (cinelerra_plugin);
|
||||
|
||||
|
||||
struct cinelerra_plugin
|
||||
{
|
||||
const char* name; /*TODO store full and short name, sort by shortname?*/
|
||||
|
||||
/* use count for all interfaces of this plugin */
|
||||
unsigned use_count;
|
||||
|
||||
time_t last;
|
||||
|
||||
/* dlopen handle */
|
||||
void* handle;
|
||||
};
|
||||
|
||||
/* global plugin registry */
|
||||
void* cinelerra_plugin_registry = NULL;
|
||||
|
||||
|
||||
static int
|
||||
cmp_plugin_name (const void* a, const void* b)
|
||||
{
|
||||
return strcmp (((struct cinelerra_plugin*) a)->name, ((struct cinelerra_plugin*) b)->name);
|
||||
}
|
||||
|
||||
struct cinelerra_interface*
|
||||
cinelerra_interface_open (const char* name, const char* interface, size_t min_revision)
|
||||
{
|
||||
REQUIRE (min_revision > sizeof(cinelerra_interface), "try to use an empty interface eh?");
|
||||
REQUIRE (interface, "interface name must be defined");
|
||||
|
||||
struct cinelerra_plugin plugin;
|
||||
struct cinelerra_plugin** found;
|
||||
|
||||
// TODO cook somewhere (pluginpath+name+.so)
|
||||
plugin.name = name;
|
||||
|
||||
found = tsearch (&plugin, &cinelerra_plugin_registry, cmp_plugin_name);
|
||||
if (!found)
|
||||
return NULL;
|
||||
|
||||
if (*found == &plugin)
|
||||
{
|
||||
NOTICE (cinelerra_plugin, "new plugin");
|
||||
|
||||
/* create new item */
|
||||
*found = malloc (sizeof (struct cinelerra_plugin));
|
||||
if (!*found)
|
||||
goto ealloc1;
|
||||
|
||||
(*found)->name = strdup (name);
|
||||
if (!(*found)->name)
|
||||
goto ealloc2;
|
||||
|
||||
(*found)->use_count = 0;
|
||||
|
||||
(*found)->handle = dlopen (name, RTLD_NOW|RTLD_LOCAL);
|
||||
if (!(*found)->handle)
|
||||
{
|
||||
ERROR (cinelerra_plugin, "dlopen failed: %s", dlerror());
|
||||
goto edlopen;
|
||||
}
|
||||
|
||||
/* if the plugin defines a 'cinelerra_plugin_init' function, we call it, must return !0 on success */
|
||||
int (*init)(void) = dlsym((*found)->handle, "cinelerra_plugin_init");
|
||||
if (init && init())
|
||||
{
|
||||
ERROR (cinelerra_plugin, "cinelerra_plugin_init failed: %s: %s", name, interface);
|
||||
goto einit;
|
||||
}
|
||||
}
|
||||
/* we have the plugin, now get the interface descriptor */
|
||||
struct cinelerra_interface* ret;
|
||||
|
||||
ret = dlsym ((*found)->handle, interface);
|
||||
TODO ("need some way to tell 'interface not provided by plugin', maybe cinelerra_plugin_error()?");
|
||||
if (!ret)
|
||||
{
|
||||
ERROR (cinelerra_plugin, "plugin %s doesnt provide interface %s", name, interface);
|
||||
goto edlsym;
|
||||
}
|
||||
|
||||
/* is the interface older than required? */
|
||||
if (ret->size < min_revision)
|
||||
{
|
||||
ERROR (cinelerra_plugin, "plugin %s provides older interface %s revision than required", name, interface);
|
||||
goto erevision;
|
||||
}
|
||||
|
||||
ret->plugin = *found;
|
||||
|
||||
/* if the interface provides a 'open' function, call it now, must return !0 on success */
|
||||
if (ret->open && ret->open())
|
||||
{
|
||||
ERROR (cinelerra_plugin, "open hook indicated an error");
|
||||
goto eopen;
|
||||
}
|
||||
|
||||
(*found)->use_count++;
|
||||
ret->use_count++;
|
||||
|
||||
return ret;
|
||||
|
||||
/* Error cleanup */
|
||||
einit:
|
||||
dlclose((*found)->handle);
|
||||
eopen:
|
||||
erevision:
|
||||
edlsym:
|
||||
edlopen:
|
||||
free ((char*)(*found)->name);
|
||||
ealloc2:
|
||||
free (*found);
|
||||
ealloc1:
|
||||
*found = &plugin;
|
||||
tdelete (&plugin, &cinelerra_plugin_registry, cmp_plugin_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
cinelerra_interface_close (struct cinelerra_interface* self)
|
||||
{
|
||||
if(!self)
|
||||
return;
|
||||
|
||||
struct cinelerra_plugin* plugin = self->plugin;
|
||||
|
||||
plugin->use_count--;
|
||||
self->use_count--;
|
||||
|
||||
/* if the interface provides a 'close' function, call it now */
|
||||
if (self->close)
|
||||
self->close();
|
||||
|
||||
/* plugin not longer in use, unload it */
|
||||
if (!plugin->use_count)
|
||||
{
|
||||
TODO ("we dont want to close here, instead store time of recent use and make a expire run, already planned in my head");
|
||||
|
||||
/* if the plugin defines a 'cinelerra_plugin_destroy' function, we call it */
|
||||
int (*destroy)(void) = dlsym(plugin->handle, "cinelerra_plugin_destroy");
|
||||
if (destroy)
|
||||
destroy();
|
||||
|
||||
/* and now cleanup */
|
||||
tdelete (plugin, &cinelerra_plugin_registry, cmp_plugin_name);
|
||||
free ((char*)plugin->name);
|
||||
dlclose(plugin->handle);
|
||||
free (plugin);
|
||||
}
|
||||
}
|
||||
112
src/lib/plugin.h
Normal file
112
src/lib/plugin.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef CINELERRA_PLUGIN_H
|
||||
#define CINELERRA_PLUGIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#elif 0
|
||||
} /*eek, fixes emacs indenting for now*/
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <nobug.h>
|
||||
|
||||
NOBUG_DECLARE_FLAG (cinelerra_plugin);
|
||||
|
||||
/* tool macros*/
|
||||
#define CINELERRA_INTERFACE_TYPE(name, version) struct cinelerra_interface_##name##_##version
|
||||
#define CINELERRA_INTERFACE_CAST(name, version) (CINELERRA_INTERFACE_TYPE(name, version)*)
|
||||
|
||||
/* Interface definition */
|
||||
#define CINELERRA_INTERFACE(name, version, ...) \
|
||||
CINELERRA_INTERFACE_TYPE(name, version) \
|
||||
{ \
|
||||
struct cinelerra_interface interface_header_; \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define CINELERRA_INTERFACE_PROTO(ret, name, params) ret (*name) params;
|
||||
|
||||
/* Interface instantiation */
|
||||
#define CINELERRA_INTERFACE_IMPLEMENT(iname, version, name, open, close, ...) \
|
||||
CINELERRA_INTERFACE_TYPE(iname, version) name##_##version = \
|
||||
{ \
|
||||
{ \
|
||||
version, \
|
||||
sizeof(CINELERRA_INTERFACE_TYPE(iname, version)), \
|
||||
NULL, \
|
||||
0, \
|
||||
open, \
|
||||
close \
|
||||
}, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#define CINELERRA_INTERFACE_FUNC(protoname, funcname) funcname,
|
||||
|
||||
|
||||
|
||||
/* internally used */
|
||||
struct cinelerra_plugin;
|
||||
|
||||
/**
|
||||
* This is the header for any interface exported by plugins.
|
||||
* Real interfaces append their functions at the end. There are few standard functions on each Interface
|
||||
* Every function is required to be implemnted.
|
||||
*/
|
||||
struct cinelerra_interface
|
||||
{
|
||||
/// interface version number
|
||||
unsigned version;
|
||||
/// size of the full structure is used to determine the revision (adding a new function increments the size)
|
||||
size_t size;
|
||||
/// back reference to plugin
|
||||
struct cinelerra_plugin* plugin;
|
||||
/// incremented for each user of this interface and decremented when closed
|
||||
unsigned use_count;
|
||||
|
||||
/// called for each open of this interface
|
||||
int (*open)(void);
|
||||
/// called for each close of this interface
|
||||
int (*close)(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make an interface available.
|
||||
* To use an interface provided by a plugin it must be opened first. It is allowed to open an interface more than once.
|
||||
* Each open must be paired with a close.
|
||||
* @param plugin name of the plugin to use.
|
||||
* @param name name of the interface to open.
|
||||
* @param min_revision the size of the interface structure is used as measure of a minimal required revision (new functions are appended at the end)
|
||||
* @return handle to the interface or NULL in case of a error. The application shall cast this handle to the actual interface type.
|
||||
*/
|
||||
struct cinelerra_interface*
|
||||
cinelerra_interface_open (const char* plugin, const char* name, size_t min_revision);
|
||||
|
||||
|
||||
/**
|
||||
* Close an interface. Does not free associated resources
|
||||
* Calling this function with self==NULL is legal. Every interface handle must be closed only once.
|
||||
* @param self interface to be closed
|
||||
*/
|
||||
void
|
||||
cinelerra_interface_close (struct cinelerra_interface* self);
|
||||
|
||||
/**
|
||||
* Tries to unload a plugin.
|
||||
* When the Plugin is unused, then all resources associated with it are freed and it will be removed from memory
|
||||
* @param plugin name of the plugin to be unloaded.
|
||||
* @return 0 on success, else the number if users which keeping the plugin loaded
|
||||
*/
|
||||
int
|
||||
cinelerra_plugin_unload (const char* plugin);
|
||||
|
||||
void
|
||||
cinelerra_plugin_expire (time_t age);
|
||||
|
||||
const char*
|
||||
cinelerra_plugin_error ();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* CINELERRA_PLUGIN_H */
|
||||
Loading…
Reference in a new issue