diff options
| -rw-r--r-- | FL/Fl_Plugin.H | 105 | ||||
| -rw-r--r-- | FL/Fl_Preferences.H | 16 | ||||
| -rw-r--r-- | src/Fl_Preferences.cxx | 205 |
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$". // |
