Still not having added Fl_Tree and Fl_Table to Fluid, I remembered the plugin concept we had early on. It occured to me that writing plugins must not be difficult, and that FLTK already has everything needed. So here it is, a plugin implementation for FLTK. The MSWindows/Cygwin implementation is untested due to lack of a machine. The dynamic loading still needs a test implementation. Comments welcome.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@7023 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
parent
51acfa41eb
commit
416f5b0dcd
105
FL/Fl_Plugin.H
Normal file
105
FL/Fl_Plugin.H
Normal file
@ -0,0 +1,105 @@
|
||||
//
|
||||
// "$Id: Fl_Plugin.H 6995 2010-01-12 08:48:55Z matt $"
|
||||
//
|
||||
// A Plugin system for FLTK, implemented in Fl_Preferences.cxx.
|
||||
//
|
||||
// Copyright 2002-2010 by Matthias Melcher.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
// USA.
|
||||
//
|
||||
// Please report all bugs and problems on the following page:
|
||||
//
|
||||
// http://www.fltk.org/str.php
|
||||
//
|
||||
|
||||
/* \file
|
||||
Fl_Plugin class . */
|
||||
|
||||
#ifndef Fl_Plugin_H
|
||||
# define Fl_Plugin_H
|
||||
|
||||
# include "Fl_Preferences.H"
|
||||
|
||||
|
||||
/**
|
||||
\brief Fl_Plugin allows link-time and run-time integration of binary modules.
|
||||
|
||||
Fl_Plugin and Fl_Plugin_Manager provide a small and simple solution for
|
||||
linking C++ classes at run-time, or optionally linking modules at compile
|
||||
time without the need to change the main application.
|
||||
|
||||
Fl_Plugin_Manager uses static initialisation to create the plugin interface
|
||||
early during startup. Plugins are stored in a temporary database, organized
|
||||
in classes.
|
||||
|
||||
Plugins should derive a new class from Fl_Plugin as a base:
|
||||
\code
|
||||
class My_Plugin : public Fl_Plugin {
|
||||
public:
|
||||
My_Plugin() : Fl_Plugin("effects", "blur") { }
|
||||
void do_something(...);
|
||||
};
|
||||
My_Plugin blur_plugin();
|
||||
\endcode
|
||||
|
||||
Plugins can be put into modules and either linked befor distribution, or loaded
|
||||
from dynamically linkable files. An Fl_Plugin_Manager is used to list and
|
||||
access all currently loaded plugins.
|
||||
\code
|
||||
Fl_Plugin_Manager mgr("effects");
|
||||
int i, n = mgr.plugins();
|
||||
for (i=0; i<n; i++) {
|
||||
My_Plugin *pin = (My_Plugin*)mgr.plugin(i);
|
||||
pin->do_something();
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
class FL_EXPORT Fl_Plugin
|
||||
{
|
||||
Fl_Preferences::ID id;
|
||||
public:
|
||||
Fl_Plugin(const char *klass, const char *name);
|
||||
virtual ~Fl_Plugin();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
\brief Fl_Plugin_Manager manages link-time and run-time plugin binaries.
|
||||
\see Fl_Plugin
|
||||
*/
|
||||
class FL_EXPORT Fl_Plugin_Manager : public Fl_Preferences
|
||||
{
|
||||
public:
|
||||
Fl_Plugin_Manager(const char *klass);
|
||||
~Fl_Plugin_Manager();
|
||||
|
||||
/** \brief Return the number of plugins in the klass.
|
||||
*/
|
||||
int plugins() { return groups(); }
|
||||
Fl_Plugin *plugin(int index);
|
||||
Fl_Preferences::ID addPlugin(const char *name, Fl_Plugin *plugin);
|
||||
|
||||
static void removePlugin(Fl_Preferences::ID id);
|
||||
static int load(const char *filename);
|
||||
static int loadAll(const char *filepath, const char *pattern=0);
|
||||
};
|
||||
|
||||
|
||||
#endif // !Fl_Preferences_H
|
||||
|
||||
//
|
||||
// End of "$Id: Fl_Preferences.H 6995 2010-01-12 08:48:55Z matt $".
|
||||
//
|
||||
@ -61,7 +61,7 @@
|
||||
reasons. One application can have multiple preferences files.
|
||||
Extensive binary data however should be stored in separate
|
||||
files: see getUserdataPath().
|
||||
|
||||
|
||||
\note Starting with FLTK 1.3, preference databases are expected to
|
||||
be in utf8 encoding. Previous databases were stored in the
|
||||
current chracter set or code page which renders them incompatible
|
||||
@ -97,12 +97,17 @@ public:
|
||||
Fl_Preferences( Fl_Preferences *parent, const char *group );
|
||||
Fl_Preferences( Fl_Preferences &parent, int groupIndex );
|
||||
Fl_Preferences( Fl_Preferences *parent, int groupIndex );
|
||||
Fl_Preferences(const Fl_Preferences&);
|
||||
Fl_Preferences( ID id );
|
||||
~Fl_Preferences();
|
||||
|
||||
/** Return an ID that can later be reused to open more references to this dataset.
|
||||
*/
|
||||
ID id() { return (ID)node; }
|
||||
|
||||
/** Remove the group with this ID from a databse.
|
||||
*/
|
||||
static char remove(ID id) { return ((Node*)id)->remove(); }
|
||||
|
||||
/** Return the name of this entry.
|
||||
*/
|
||||
@ -189,13 +194,12 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
// make the following functions unavailable
|
||||
Fl_Preferences();
|
||||
Fl_Preferences(const Fl_Preferences&);
|
||||
Fl_Preferences() : node(0), rootNode(0) { }
|
||||
Fl_Preferences &operator=(const Fl_Preferences&);
|
||||
|
||||
static char nameBuffer[128];
|
||||
static char uuidBuffer[40];
|
||||
static Fl_Preferences *runtimePrefs;
|
||||
|
||||
class RootNode;
|
||||
|
||||
@ -253,6 +257,7 @@ private:
|
||||
public:
|
||||
RootNode( Fl_Preferences *, Root root, const char *vendor, const char *application );
|
||||
RootNode( Fl_Preferences *, const char *path, const char *vendor, const char *application );
|
||||
RootNode( Fl_Preferences * );
|
||||
~RootNode();
|
||||
int read();
|
||||
int write();
|
||||
@ -260,9 +265,10 @@ private:
|
||||
};
|
||||
friend class RootNode;
|
||||
|
||||
protected:
|
||||
|
||||
Node *node;
|
||||
RootNode *rootNode;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Preferences.H>
|
||||
#include <FL/Fl_Plugin.H>
|
||||
#include <FL/Fl_Tree.H>
|
||||
#include <FL/filename.H>
|
||||
|
||||
@ -40,6 +41,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
# include <windows.h>
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
// Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
|
||||
@ -49,8 +51,10 @@
|
||||
#elif defined (__APPLE__)
|
||||
# include <Carbon/Carbon.h>
|
||||
# include <unistd.h>
|
||||
# include <dlfcn.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@ -65,6 +69,7 @@ typedef RPC_STATUS (WINAPI* uuid_func)(UUID __RPC_FAR *Uuid);
|
||||
|
||||
char Fl_Preferences::nameBuffer[128];
|
||||
char Fl_Preferences::uuidBuffer[40];
|
||||
Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
|
||||
|
||||
/**
|
||||
* Returns a UUID as generated by the system.
|
||||
@ -198,8 +203,13 @@ const char *Fl_Preferences::newUUID()
|
||||
name of your application. Both vendor and
|
||||
application must be valid relative UNIX pathnames and
|
||||
may contain '/'s to create deeper file structures.
|
||||
|
||||
A set of Preferences marked "run-time" exists exactly one per application and
|
||||
only as long as the application runs. It can be used as a database for
|
||||
volatile information. FLTK uses it to register plugins at run-time.
|
||||
|
||||
\param[in] root can be \c USER or \c SYSTEM for user specific or system wide preferences
|
||||
\param[in] root can be \c USER or \c SYSTEM for user specific or system wide
|
||||
preferences
|
||||
\param[in] vendor unique text describing the company or author of this file
|
||||
\param[in] application unique text describing the application
|
||||
*/
|
||||
@ -249,10 +259,26 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group )
|
||||
|
||||
|
||||
/**
|
||||
\brief Create or access a group of preferences using a name.
|
||||
\param[in] parent the parameter parent is a pointer to the parent group.
|
||||
\p Parent may be \p NULL. It then refers to an application internal
|
||||
database which exists only once, and remains in RAM only until the
|
||||
application quits. This databse is used to manage plugins and other
|
||||
data indexes by strings.
|
||||
\param[in] group a group name that is used as a key into the databse
|
||||
\see Fl_Preferences( Fl_Preferences&, const char *group )
|
||||
*/
|
||||
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group )
|
||||
{
|
||||
if (parent==0) {
|
||||
if (!runtimePrefs) {
|
||||
runtimePrefs = new Fl_Preferences();
|
||||
runtimePrefs->node = new Node( "." );
|
||||
runtimePrefs->rootNode = new RootNode( runtimePrefs );
|
||||
runtimePrefs->node->setRoot(rootNode);
|
||||
}
|
||||
parent = runtimePrefs;
|
||||
}
|
||||
rootNode = parent->rootNode;
|
||||
node = parent->node->addChild( group );
|
||||
}
|
||||
@ -313,6 +339,25 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id )
|
||||
rootNode = node->findRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
Create another reference to a Preferences group.
|
||||
*/
|
||||
Fl_Preferences::Fl_Preferences(const Fl_Preferences &rhs)
|
||||
: node(rhs.node),
|
||||
rootNode(rhs.rootNode)
|
||||
{ }
|
||||
|
||||
/**
|
||||
Assign another reference to a Preference group.
|
||||
*/
|
||||
Fl_Preferences &Fl_Preferences::operator=(const Fl_Preferences &rhs) {
|
||||
if (&rhs != this) {
|
||||
node = rhs.node;
|
||||
rootNode = rhs.rootNode;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The destructor removes allocated resources. When used on the
|
||||
@ -1025,6 +1070,10 @@ static void makePathForFile( const char *path )
|
||||
// create the root node
|
||||
// - construct the name of the file that will hold our preferences
|
||||
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
|
||||
: prefs_(prefs),
|
||||
filename_(0L),
|
||||
vendor_(0L),
|
||||
application_(0L)
|
||||
{
|
||||
char filename[ FL_PATH_MAX ]; filename[0] = 0;
|
||||
#ifdef WIN32
|
||||
@ -1141,7 +1190,6 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char
|
||||
"%s/%s.prefs", vendor, application);
|
||||
#endif
|
||||
|
||||
prefs_ = prefs;
|
||||
filename_ = strdup(filename);
|
||||
vendor_ = strdup(vendor);
|
||||
application_ = strdup(application);
|
||||
@ -1152,6 +1200,10 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char
|
||||
// create the root node
|
||||
// - construct the name of the file that will hold our preferences
|
||||
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
|
||||
: prefs_(prefs),
|
||||
filename_(0L),
|
||||
vendor_(0L),
|
||||
application_(0L)
|
||||
{
|
||||
if (!vendor)
|
||||
vendor = "unknown";
|
||||
@ -1163,13 +1215,22 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, con
|
||||
snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
|
||||
filename_ = strdup(filename);
|
||||
}
|
||||
prefs_ = prefs;
|
||||
vendor_ = strdup(vendor);
|
||||
application_ = strdup(application);
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
// create a root node that exists only on RAM and can not be read or written to
|
||||
// a file
|
||||
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
|
||||
: prefs_(prefs),
|
||||
filename_(0L),
|
||||
vendor_(0L),
|
||||
application_(0L)
|
||||
{
|
||||
}
|
||||
|
||||
// destroy the root node and all depending nodes
|
||||
Fl_Preferences::RootNode::~RootNode()
|
||||
{
|
||||
@ -1194,9 +1255,14 @@ Fl_Preferences::RootNode::~RootNode()
|
||||
// read a preferences file and construct the group tree and with all entry leafs
|
||||
int Fl_Preferences::RootNode::read()
|
||||
{
|
||||
if (!filename_) // RUNTIME preferences
|
||||
return -1;
|
||||
|
||||
char buf[1024];
|
||||
FILE *f = fl_fopen( filename_, "rb" );
|
||||
if ( !f ) return 0;
|
||||
if ( !f )
|
||||
return -1;
|
||||
|
||||
fgets( buf, 1024, f );
|
||||
fgets( buf, 1024, f );
|
||||
fgets( buf, 1024, f );
|
||||
@ -1236,9 +1302,14 @@ int Fl_Preferences::RootNode::read()
|
||||
// write the group tree and all entry leafs
|
||||
int Fl_Preferences::RootNode::write()
|
||||
{
|
||||
if (!filename_) // RUNTIME preferences
|
||||
return -1;
|
||||
|
||||
fl_make_path_for_file(filename_);
|
||||
FILE *f = fl_fopen( filename_, "wb" );
|
||||
if ( !f ) return 1;
|
||||
if ( !f )
|
||||
return -1;
|
||||
|
||||
fprintf( f, "; FLTK preferences file format 1.0\n" );
|
||||
fprintf( f, "; vendor: %s\n", vendor_ );
|
||||
fprintf( f, "; application: %s\n", application_ );
|
||||
@ -1250,6 +1321,9 @@ int Fl_Preferences::RootNode::write()
|
||||
// get the path to the preferences directory
|
||||
char Fl_Preferences::RootNode::getPath( char *path, int pathlen )
|
||||
{
|
||||
if (!filename_) // RUNTIME preferences
|
||||
return -1;
|
||||
|
||||
strlcpy( path, filename_, pathlen);
|
||||
|
||||
char *s;
|
||||
@ -1673,6 +1747,127 @@ char Fl_Preferences::Node::copyTo(Fl_Tree *tree, Fl_Tree_Item *ti)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Create a plugin.
|
||||
*
|
||||
* \param[in] klass plugins are grouped in classes
|
||||
* \param[in] name every plugin should have a unique name
|
||||
*/
|
||||
Fl_Plugin::Fl_Plugin(const char *klass, const char *name)
|
||||
: id(0)
|
||||
{
|
||||
Fl_Plugin_Manager pm(klass);
|
||||
id = pm.addPlugin(name, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clear the plugin and remove it from the database.
|
||||
*/
|
||||
Fl_Plugin::~Fl_Plugin()
|
||||
{
|
||||
if (id)
|
||||
Fl_Plugin_Manager::remove(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Manage all plugins belonging to one class.
|
||||
*/
|
||||
Fl_Plugin_Manager::Fl_Plugin_Manager(const char *klass)
|
||||
: Fl_Preferences(0, Fl_Preferences::Name("%s/%s", "plugins", klass))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Remove the plugin manager.
|
||||
*
|
||||
* Calling this does not remove the database itself or any plugins. It just
|
||||
* removes the reference to the database.
|
||||
*/
|
||||
Fl_Plugin_Manager::~Fl_Plugin_Manager()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the address of a plugin by index.
|
||||
*/
|
||||
Fl_Plugin *Fl_Plugin_Manager::plugin(int index)
|
||||
{
|
||||
char buf[32];
|
||||
Fl_Plugin *ret = 0;
|
||||
Fl_Preferences pin(this, index);
|
||||
pin.get("address", buf, "@0", 32);
|
||||
sscanf(buf, "@%p", &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This function adds a new plugin to the databse.
|
||||
*
|
||||
* There is no need to call this function explicitly. Every Fl_Plugin constructor
|
||||
* will call this function at initialization time.
|
||||
*/
|
||||
Fl_Preferences::ID Fl_Plugin_Manager::addPlugin(const char *name, Fl_Plugin *plugin)
|
||||
{
|
||||
char buf[32];
|
||||
Fl_Preferences pin(this, name);
|
||||
snprintf(buf, 32, "@%p", plugin);
|
||||
pin.set("address", buf);
|
||||
return pin.id();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Remove any plugin.
|
||||
*
|
||||
* There is no need to call this function explicitly. Every Fl_Plugin destructor
|
||||
* will call this function at destruction time.
|
||||
*/
|
||||
void Fl_Plugin_Manager::removePlugin(Fl_Preferences::ID id)
|
||||
{
|
||||
Fl_Preferences::remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load a module from disk.
|
||||
*
|
||||
* A module must be a dynamically linkable file for the given operating system.
|
||||
* When loading a module, its +init function will be called which in turn calls
|
||||
* the constructor of all statically initialized Fl_Plugin classes and adds
|
||||
* them to the database.
|
||||
*/
|
||||
int Fl_Plugin_Manager::load(const char *filename)
|
||||
{
|
||||
// the functions below will autmaticaly load plugins that are defined:
|
||||
// Fl_My_Plugin plugin();
|
||||
#ifdef WIN32
|
||||
HMODULE dl = LoadLibrary(filename);
|
||||
#else
|
||||
void * dl = dlopen(filename, RTLD_LAZY);
|
||||
#endif
|
||||
// There is no way of unloading a plugin!
|
||||
return (dl!=0) ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Use this function to load a whole directory full of modules.
|
||||
*/
|
||||
int Fl_Plugin_Manager::loadAll(const char *filepath, const char *pattern)
|
||||
{
|
||||
struct dirent **dir;
|
||||
int i, n = fl_filename_list(filepath, &dir);
|
||||
for (i=0; i<n; i++) {
|
||||
struct dirent *e = dir[i];
|
||||
if (pattern==0 || fl_filename_match(e->d_name, pattern)) {
|
||||
load(Fl_Preferences::Name("%s%s", filepath, e->d_name));
|
||||
}
|
||||
free(e);
|
||||
}
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// End of "$Id$".
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user