summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FL/Fl_Plugin.H105
-rw-r--r--FL/Fl_Preferences.H16
-rw-r--r--src/Fl_Preferences.cxx205
3 files changed, 316 insertions, 10 deletions
diff --git a/FL/Fl_Plugin.H b/FL/Fl_Plugin.H
new file mode 100644
index 000000000..dcfc3bcdb
--- /dev/null
+++ b/FL/Fl_Plugin.H
@@ -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 $".
+//
diff --git a/FL/Fl_Preferences.H b/FL/Fl_Preferences.H
index 44d007f90..798e421d6 100644
--- a/FL/Fl_Preferences.H
+++ b/FL/Fl_Preferences.H
@@ -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;
-
};
diff --git a/src/Fl_Preferences.cxx b/src/Fl_Preferences.cxx
index 8864604a6..d29a1b6b8 100644
--- a/src/Fl_Preferences.cxx
+++ b/src/Fl_Preferences.cxx
@@ -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$".
//