-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
-*/\r
-\r
-#ifndef __SYNAPSE_H__\r
-#define __SYNAPSE_H__\r
-\r
-/*!\r
-synapse library\r
-code and utilities to deal with dynamic components programming\r
-\r
-"the point at which a nervous impulse passes from one neuron to another"\r
-\r
-dependencies:\r
- libxml for parsing\r
- STL for some algorithms and data structures\r
- glib for Str.h (Str class)\r
- \r
-this is a utility library, it provides typical synapse client and server\r
-could be split into two independant libraries actually, the server part and the client part\r
-(that's just a matter of reducing binary size) \r
-*/\r
-\r
-// compile time settings\r
-#ifdef _DEBUG\r
- #define SYNAPSE_VERBOSE // be verbosive about the loading process\r
-#endif\r
-\r
-// ydnar: required for os x\r
-#if defined (__APPLE__)\r
- #include <sys/types.h>\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
- #include <dlfcn.h>\r
- #include <dirent.h> \r
-#endif\r
-\r
-#if defined(_WIN32)\r
- #include <windows.h>\r
-#endif\r
-\r
-#if defined(_WIN32)\r
- #define SYNAPSE_DLL_EXPORT WINAPI\r
-#elif defined(__linux__) || defined(__APPLE__) /* ydnar */\r
- #define SYNAPSE_DLL_EXPORT\r
-#else\r
- #error unknown architecture\r
-#endif\r
-\r
-// NOTE TTimo: VC6 crap, gets confused when some variable names in function declarations\r
-// are 'allocator' or 'list'\r
-// if you #include glib *after* STL, you get those errors .. better be safe then\r
-#include <glib.h>\r
-\r
-#include "libxml/parser.h"\r
-\r
-#include "irefcount.h"\r
-#include "gtkr_list.h"\r
-#include "gtkr_vector.h"\r
-\r
-#include "str.h"\r
-\r
-/*!\r
-use when API change make things incompatible at synapse level\r
-i.e. entry point and classes API changes\r
-*/\r
-#define SYNAPSE_VERSION "3"\r
-\r
-/*!\r
-=======================================================================\r
-diagnostic printing facility\r
-independently from any API negociation stuff,\r
-we need a diagnostic facility that's available at all times\r
-=======================================================================\r
-*/\r
-extern "C"\r
-{\r
-/*!\r
-prototype to provide to synapse to redirect the output appropriately \r
-*/ \r
-typedef void (* PFN_SYN_PRINTF_VA) (const char *text, va_list args);\r
-void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf); ///< change the handler, set back to NULL for default\r
-/*!\r
-use this for synapse code diagnostics, it will be piped through the handler if necessary\r
-*/\r
-void Syn_Printf (const char *text, ...);\r
-};\r
- \r
-/*\r
-=======================================================================\r
-client\r
-=======================================================================\r
-*/\r
-\r
-/*!\r
-description of an API:\r
-a module requires and provides several APIs\r
-the basic rule is that we will avoid asking an API from a module if the APIs it requires are not filled in yet\r
-the exception being the 'resolve' operation of a given client, which we 'activate'\r
-(that is we make the interfaces it provides available, leave the ones it requires unsolved, and try to get back to a stable situation)\r
-*/\r
-\r
-typedef enum { SYN_UNKNOWN = 0, SYN_PROVIDE, SYN_REQUIRE, SYN_REQUIRE_ANY } EAPIType;\r
-\r
-#define MAX_APINAME 128\r
-typedef struct APIDescriptor_s\r
-{\r
- /*!\r
- major version, this must be UNIQUE for each API\r
- NOTE: we used to rely on GUID for this, that was a good solution to make sure we never get conflicts\r
- but it was a bit overkill, so we dropped and use a string now\r
- */\r
- char major_name[MAX_APINAME];\r
- /*!\r
- what kind of interface\r
- for instance for "image" API, "tga" "jpg" etc.\r
- */\r
- char minor_name[MAX_APINAME];\r
- EAPIType mType; ///< is this an API we provide or an API we require\r
- /*!\r
- pointer to the table to be filled in\r
- this is valid for SYN_REQUIRE APIs only\r
- */\r
- void *mpTable;\r
- bool mbTableInitDone; ///< turned to true by the server after the function table has been filled in\r
- /*!\r
- gives the size of the expected function table \r
- */\r
- int mSize;\r
- /*!\r
- refcounts how many times this API is being used through the app\r
- this is valid for SYN_PROVIDE APIs only\r
- */\r
- int mRefCount;\r
-} APIDescriptor_t;\r
-\r
-typedef struct XMLConfigEntry_s {\r
- const char *api;\r
- EAPIType type;\r
- int size;\r
- void *pTable;\r
-} XMLConfigEntry_t;\r
-\r
-/*!\r
-\class CSynapseAPIManager\r
-derive from this class if you want to manage several APIs through the same object\r
-(typically, loading plugins, or an unknown number of APIs that match some criterions)\r
-this class has some pure virtual members that need to be implemented by the childs\r
-\r
-we deal with two types of API managers:\r
-- the 'loose' ones have a matching pattern and load everything that matches criterions\r
- typically used for plugins\r
-- the 'list' ones have a fixed list of things they require. They are used to provide\r
- easy access to multiple interfaces\r
-\r
-those two types of managers are not stored in the same structs, and not handled the\r
-same way. For instance the 'list' manager will require ALL it's APIs to be loaded, or\r
-the init will fail. They also play a role in the client activation.\r
-\r
-apart from the multiple API management facility, the main difference with static tables\r
-and individual calls to CSynapseClient::AddAPI is the fact that the APIDescriptor_t are\r
-allocated on demand\r
-*/\r
-\r
-/* we do some matching, or store minors list, the strings need to be bigger */\r
-#define MAX_PATTERN_STRING 512\r
-\r
-/*! \enum EAPIManagerType\r
- \brief type of this manager, loosely matching with "*", or a fixed list of required interfaces\r
-*/\r
-typedef enum { API_MATCH = 0, API_LIST } EAPIManagerType;\r
-\r
-class CSynapseAPIManager : public IRefCounted\r
-{\r
- EAPIManagerType mType;\r
- \r
- // the list of APIs we have obtained (SYN_REQUIRE_ANY)\r
- vector< APIDescriptor_t * > mAPIs;\r
- /*!\r
- pattern for matching the major version\r
- NOTE: only supported for now: exact match\r
- */\r
- char major_pattern[MAX_PATTERN_STRING];\r
- /*!\r
- pattern for matching the minor\r
- */\r
- char minor_pattern[MAX_PATTERN_STRING]; \r
- \r
-public:\r
- CSynapseAPIManager() { mType = API_MATCH; }\r
- virtual ~CSynapseAPIManager();\r
-\r
- EAPIManagerType GetType() { return mType; }\r
- void SetType(EAPIManagerType type) { mType = type; }\r
- \r
- /*!\r
- set the API matching pattern\r
- supported syntax:\r
- any minor for a given major, for instance: PLUGIN_MAJOR, "*"\r
- a space seperated list of minors for a given major: IMAGE_MAJOR, "tga jpg"\r
- */\r
- void SetMatchAPI(const char *major, const char *minor);\r
- \r
- /*!\r
- utility function\r
- start building a SYN_REQUIRE_ANY descriptor from a SYN_PROVIDE interface that we found matching\r
- */\r
- static APIDescriptor_t* PrepareRequireAPI(APIDescriptor_t *pAPI); \r
- \r
- /*!\r
- for managers that require a fixed list of things, we are not active until everything has been loaded up\r
- managers that work on a loose pattern like "*" are always active (since they don't know what they want for sure)\r
- */\r
- bool CheckSetActive();\r
- \r
- /*!\r
- the manager answers wether it wants to load this or not\r
- we provide a default implementation, but this can be completely overriden if needed\r
- see SetMatchAPI for the documentation of the default implementation\r
- NOTE: this should only be called on API_MATCH type of managers\r
- */\r
- virtual bool MatchAPI(const char *major, const char *minor);\r
- \r
- /*!\r
- build an APIDescriptor_t configured as SYN_REQUIRE_ANY from the SYN_PROVIDE API we found\r
- used when we scan the available interfaces for a match that would be interesting to this manager\r
- NOTE: only for API_MATCH managers\r
- */\r
- virtual APIDescriptor_t *BuildRequireAPI(APIDescriptor_t *pAPI) { return NULL; }\r
- \r
- /*!\r
- below is relevant to API_LIST only ---------------------------------------------------------------------\r
- */\r
-\r
- /*!\r
- fill in the table info to this descriptor, store it as a new slot\r
- NOTE: only for API_LIST\r
- */\r
- virtual void FillAPITable(APIDescriptor_t *pAPI) { }\r
- \r
- /*!\r
- initialize the list of APIDescriptor_t* with all the stuff we expect\r
- */\r
- void InitializeAPIList();\r
- \r
- /*!\r
- access the API descriptors\r
- */\r
- int GetAPICount();\r
- APIDescriptor_t *GetAPI(int);\r
-};\r
-\r
-/*!\r
-\class CSynapseClient\r
-*/\r
-class CSynapseServer; // forward declare\r
-class CSynapseClient : public IRefCounted\r
-{\r
- /*!\r
- this flag indicates wether this client is active\r
- i.e. wether you can ask it for interfaces\r
- this is either a client for which all required interfaces have been filled in\r
- or a client we are trying to resolve (i.e. load with all it's stuff)\r
- */\r
- bool mbActive;\r
- \r
- /*!\r
- we store APIDescriptor_t*, the module fills that in at startup\r
- */\r
- vector<APIDescriptor_t *> mAPIDescriptors;\r
- \r
- /*!\r
- managers for multiple APIs management\r
- mManagersMatch are managers with loose matching / undefined number of APIs\r
- mManagersList are managers with a fixed list of required interfaces\r
- */\r
- vector<CSynapseAPIManager *> mManagersMatch;\r
- vector<CSynapseAPIManager *> mManagersList;\r
- \r
-protected:\r
- friend class CSynapseServer;\r
- /*!\r
- use of this is restricted to the server, expecting it knows what it is doing\r
- will make the client we are trying to resolve able to provide interfaces even though all interfaces it requires have not been filled in yet.\r
- */\r
- void ForceSetActive() { mbActive = true; }\r
- \r
-public:\r
- CSynapseClient();\r
- virtual ~CSynapseClient();\r
-\r
- int GetAPICount(); ///< returns the number of APIs that this module provides\r
- APIDescriptor_t* GetAPIDescriptor(int); ///< retrieve specific information about on of the APIs\r
-\r
- /*!\r
- Add the API to the CSynapseClient information\r
- \r
- \param minor\r
- minor can be NULL, some APIs don't need to have a 'minor' description \r
- \r
- \param type\r
- SYN_PROVIDE: means this is an API we provide if anyone needs it\r
- SYN_REQUIRE: means this is an API we will require for operation\r
- SYN_REQUIRE_ANY: means this is an API we want to load *any* minor found\r
- (for instance a list of image fornats, or the plugins)\r
- \r
- \param pTable \r
- the function table\r
- only valid for SYN_REQUIRE APIs\r
- \r
- \param size\r
- the size of the function table\r
- if SYN_REQUIRE, you should set the size in pTable and AddAPI will work it out\r
- if SYN_PROVIDE, you need to provide this parameter\r
- \r
- returns a bool:\r
- operation may fail, since we have a few safe checks \r
- */\r
- bool AddAPI(const char *major, const char *minor = NULL, int size = 0, EAPIType type = SYN_PROVIDE, void *pTable = NULL);\r
- \r
- /*!\r
- Add an API manager to the client\r
- this class is designed to handle multiple APIs\r
- is not memory managed by CSynapseClient (should it? or ref counted maybe?)\r
- writing it with support for multiple managers, that may be a bit overkill right now\r
- */\r
- void AddManager(CSynapseAPIManager *pManager);\r
- \r
- int GetManagerMatchCount(); ///< how many API managers\r
- CSynapseAPIManager* GetManagerMatch(int); ///< get corresponding API manager\r
-\r
- int GetManagerListCount(); ///< how many API managers\r
- CSynapseAPIManager* GetManagerList(int); ///< get corresponding API manager\r
- \r
- /*!\r
- each client has to implement this function itself\r
- it will fill in the function table\r
- and increment the ref counting in it's own SYN_PROVIDE descriptor\r
- returns a bool, false if you ask for an API that's not available\r
- */\r
- virtual bool RequestAPI(APIDescriptor_t *pAPI) = 0;\r
- \r
- /*!\r
- return the build date, can be overriden by client module\r
- */\r
- virtual const char* GetInfo();\r
- \r
- /*!\r
- \brief a shirt name to identify the client\r
- we use this string to identify individual clients, for instance when some XML configuration nodes are required\r
- should be unique, the synapse server should't accept multiple occurences? \r
- */\r
- virtual const char* GetName() { return ""; }\r
- \r
- bool IsActive() { return mbActive; }\r
- /*!\r
- check wether all interfaces have been filled in\r
- in which case we will switch to 'activated' state, that is this client can provide interfaces to others now\r
- */\r
- bool CheckSetActive();\r
- \r
- /*!\r
- \brief called when the client is being shutdown, before the dlclose happens\r
- this is the last call before the dlclose, there's no turning back\r
- just do what you have to do before you die.. decref and stuff\r
- */\r
- void Shutdown();\r
- \r
- /*!\r
- override this one in clients that need to proceed through some init steps when activated\r
- if returning false, the init will abort\r
- */\r
- virtual bool OnActivate() { return true; }\r
- \r
- /*!\r
- \brief walk the XML config and initialize from structures\r
- when you use this function, OnActivate will also make sure all the interfaces listed were properly initialized\r
- two tables, one for the regular single interface, one for the listings\r
- need to store for later and check in OnActivate\r
- \r
- \param pServer, pass the server to talk to\r
- NOTE: might want to store it in the class if that's needed too often\r
- \r
- \param client_name, the name of the client node to look for. If NULL, use GetName()\r
- \r
- \return wether all APIs given were successfully found in the config\r
- */\r
- bool ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] );\r
-};\r
-\r
-/*!\r
-prototype for the only exported function needed in a synapse client\r
-*/\r
-#define NAME_SYNAPSE_ENUMERATEINTERFACES "Synapse_EnumerateInterfaces"\r
-\r
-class CSynapseServer; // forward declare\r
-typedef CSynapseClient* (SYNAPSE_DLL_EXPORT *PFN_SYNAPSE_ENUMERATEINTERFACES)(const char *version, CSynapseServer *server);\r
-\r
-/*!\r
-a derived version of CSynapseClient that can be used to provide builtin module without having to go through DLLs\r
-this is useful for things we want to isolate behind an abstract API, but that we feel better about having present at all times (such as .def class loader)\r
-*/\r
-class CSynapseBuiltinClient : public CSynapseClient\r
-{\r
- public:\r
- CSynapseBuiltinClient() {}\r
- virtual ~CSynapseBuiltinClient() {}\r
- \r
- virtual void EnumerateInterfaces(CSynapseServer *server) = 0;\r
- \r
-};\r
-\r
-/*\r
-=======================================================================\r
-server\r
-=======================================================================\r
-*/\r
-\r
-/*!\r
- \enum EClientType\r
- \brief we can have clients that are builtin to a server\r
-*/\r
-typedef enum { SYN_SO, SYN_BUILTIN } EClientType;\r
-\r
-/*!\r
-server side slot for a synapse client\r
-is OS dependant, except for the ISynapseClient part\r
-*/\r
-class CSynapseClientSlot\r
-{\r
-public:\r
- /*!\r
- \todo cleanup, make that private with accessors\r
- */\r
-#if defined(__linux__) || defined(__APPLE__)\r
- void *mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)\r
-#elif defined(_WIN32)\r
- HMODULE mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)\r
-#endif\r
- PFN_SYNAPSE_ENUMERATEINTERFACES mpEnumerate; ///< function pointer to the enumeration entry point (invalid if SYN_BUILTIN)\r
-\r
- CSynapseClient *mpClient; ///< the full client API\r
- Str mFileName; ///< path to the file\r
- \r
- EClientType mType;\r
-\r
- /*!\r
- \brief release the shared object. NOTE: OS dependent \r
- */\r
- void ReleaseSO();\r
- \r
- CSynapseClientSlot() { mpDLL = NULL; mpEnumerate = NULL; mpClient = NULL; mType = SYN_SO; }\r
- /*!\r
- NOTE: the slot is stored as static object, and copy constructors used \r
- */\r
- virtual ~CSynapseClientSlot() { }\r
-\r
-};\r
-\r
-/*!\r
-\class CSynapseServer\r
-dynamic modules manager class\r
-this class provides the server functionality:\r
-initialize, get a list of modules, load them, link them together..\r
-*/\r
-class CSynapseServer : public IRefCounted\r
-{\r
- list<char *> mSearchPaths;\r
- list<CSynapseClientSlot> mClients;\r
- \r
- /*!\r
- used for resolve operations\r
- */\r
- list<APIDescriptor_t*> mStack;\r
- /*!\r
- set this when mStack is modified with new stuff to resolve\r
- NOTE: if this hack becomes too tricky to use we could just encapsulate mStack\r
- */\r
- bool mbStackChanged;\r
- \r
- xmlDocPtr mpDoc;\r
- xmlNodePtr mpFocusedNode; ///< currently focused node while we are scanning the config (strictly for GetNextConfig usage)\r
- xmlNodePtr mpCurrentClientConfig;\r
- /*!\r
- stores the allocated strings for each call to GetNextConfig\r
- need to be freed if != NULL\r
- */\r
- xmlChar *m_api_name;\r
- gchar *m_content;\r
- \r
- /*!\r
- push required interfaces for this client into the stack of things to be resolved\r
- it is possible that several mathing interfaces be in the stack at the same time\r
- (for instance several modules that want to get the VFS)\r
- but we should never have the same APIDescriptor_t twice\r
- NOTE: as this function is called repeatedly during the resolve (because the list of required things is refining),\r
- we often have to drop APIDescriptor_t requests that are already there.\r
- NOTE CSynapseAPIManager: if there are CSynapseAPIManager objects in the CSynapseClient,\r
- we will scan and push all the matching APIs too\r
- */\r
- void PushRequired(CSynapseClient *pClient);\r
- \r
- /*!\r
- work on resolving this particular APIDescriptor_t\r
- returns true if we were able to resolve the interface\r
- returns false otherwise\r
- if the API was found, but not requested because of more required APIs, we push them in mStack\r
- */\r
- bool ResolveAPI(APIDescriptor_t* pAPI);\r
- \r
- /*!\r
- push an APIDescriptor_t* into the stack of things to be resolved\r
- will check that this is not already present first\r
- will update the mbStackChanged flag\r
- */\r
- void TryPushStack(APIDescriptor_t *);\r
- \r
- /*!\r
- \brief 'client shutdown' (see libs/synapse/docs/unload.txt)\r
- performs a 'client shutdown'\r
- will free the DLL module\r
- before calling here, the client must be in a 'non active' state\r
- (i.e. it was not used at all during startup, or we have properly done a 'release' already)\r
- we scan the mStack for the SYN_REQUIRE that this client owns, and remove them\r
- \param iSlot is an mClients iterator, invalid when the function returns as the item will have been removed from the list\r
- \return the iterator afer erase call so that the caller iteration can continue\r
- */\r
- list<CSynapseClientSlot>::iterator ShutdownClient(list<CSynapseClientSlot>::iterator iSlot);\r
- \r
- /*!\r
- \brief actual implementation of the Resolve function\r
- */\r
- bool DoResolve(CSynapseClient *pClient); \r
- \r
-public:\r
- CSynapseServer();\r
- virtual ~CSynapseServer();\r
-\r
- void AddSearchPath(char*); ///< add a new directory to the module search path\r
- /*!\r
- do the big thing, scan for modules, scan their APIs, load up everything\r
- providing pf is optional, will set the diagnostics printing\r
- \param conf_file is the XML configuration file for the intialization (see docs/runtime.txt)\r
- \return false if the init failed (for instance not found/invalid conf file\r
- */\r
- bool Initialize(const char* conf_file = NULL, PFN_SYN_PRINTF_VA pf = NULL);\r
-\r
- /*!\r
- enumerate the interfaces for a given module\r
- this will load it, query it's entry point, and request the APIs\r
- */\r
- void EnumerateInterfaces(Str &);\r
- \r
- /*!\r
- enumerate the interfaces for a module that is builtin to the server\r
- */\r
- void EnumerateBuiltinModule(CSynapseBuiltinClient *);\r
- \r
- /*!\r
- \brief resolve the function table loading for this client\r
- if the client is not listed in the known slots yet, it will be added\r
- wraps around internal DoResolve implementation to unload the unused modules\r
- \return wether the resolution has been successful \r
- */\r
- bool Resolve(CSynapseClient *pClient);\r
- \r
- /*!\r
- \brief shutdown all the clients. Should only be called when the core is about to exit\r
- this will force all clients to shutdown. it may destroy refcounted APIs and stuff\r
- \todo hafta use the release/refresh code before doing actual shutdown\r
- (i.e. when that code is written later on)\r
- we need to 'broadcast' to all the clients .. that all the modules are going to be reloaded sorta\r
- should clear up as many interfaces as possible to avoid unexpected crashes in the final stages of app exit\r
- */\r
- void Shutdown();\r
- \r
- /*!\r
- diagnostic print function\r
- NOTE:\r
- it is essential that those functions should be virtual,\r
- otherwise when accessing the g_pPrintf global we could mismatch\r
- (happens because the same library is linked into server and client)\r
- */\r
- virtual PFN_SYN_PRINTF_VA Get_Syn_Printf();\r
- \r
- /*!\r
- \return true if those APIs are matching\r
- we provide two APIs for convenience, actual implementation is MatchAPI\r
- the minors have to be both NULL, or equal, or one the minors be '*'\r
- NOTE: the '*' minor should ONLY be used on an API that will be unique. It is hackish and kinda dangerous\r
- */\r
- static bool MatchAPI( APIDescriptor_t *p1, APIDescriptor_t *p2 );\r
- static bool MatchAPI( const char* major1, const char* minor1, const char* major2, const char* minor2 );\r
- \r
-#if defined(_WIN32)\r
- /*!\r
- utility function to retrieve formatted GetLastError message\r
- ANSI text, static string\r
- */\r
- static const char* FormatGetLastError();\r
-#endif\r
-\r
- /*!\r
- dump the stack of interfaces to be solved\r
- this is used when synapse initialization failed to quickly identify the missing/broken pieces\r
- */\r
- void DumpStack();\r
- \r
- /*!\r
- general purpose information, list what modules are loaded up\r
- */\r
- void DumpActiveClients();\r
- \r
- /*!\r
- \brief select the config node that has this name\r
- call this to locate the right node in XML config\r
- this will focus and get ready to walk through the api nodes\r
- \return wether the config node was correctly selected\r
- */\r
- bool SelectClientConfig(const char *client_name);\r
- \r
- /*!\r
- \brief walk through the apis\r
- the pointers don't need to be freed\r
- you need to copy them over as they are invalidated between each call to GetNextConfig\r
- \return false when all apis have been parsed\r
- */\r
- bool GetNextConfig(char **api_name, char **minor);\r
- \r
- /*!\r
- \brief read the minor for a given api in the current config\r
- \return false if this node doesn't exist\r
- */\r
- bool GetConfigForAPI( const char *api, char **minor );\r
-\r
- /*!\r
- returns the filename of the module that the passed on client exists in\r
- */\r
- const char *GetModuleFilename(CSynapseClient *pClient);\r
-};\r
-\r
-#endif\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __SYNAPSE_H__
+#define __SYNAPSE_H__
+
+/*!
+synapse library
+code and utilities to deal with dynamic components programming
+
+"the point at which a nervous impulse passes from one neuron to another"
+
+dependencies:
+ libxml for parsing
+ STL for some algorithms and data structures
+ glib for Str.h (Str class)
+
+this is a utility library, it provides typical synapse client and server
+could be split into two independant libraries actually, the server part and the client part
+(that's just a matter of reducing binary size)
+*/
+
+// compile time settings
+#ifdef _DEBUG
+ #define SYNAPSE_VERBOSE // be verbosive about the loading process
+#endif
+
+// ydnar: required for os x
+#if defined (__APPLE__)
+ #include <sys/types.h>
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+ #include <dlfcn.h>
+ #include <dirent.h>
+#endif
+
+#if defined(_WIN32)
+ #include <windows.h>
+#endif
+
+#if defined(_WIN32)
+ #define SYNAPSE_DLL_EXPORT WINAPI
+#elif defined(__linux__) || defined(__APPLE__) /* ydnar */
+ #define SYNAPSE_DLL_EXPORT
+#else
+ #error unknown architecture
+#endif
+
+// NOTE TTimo: VC6 crap, gets confused when some variable names in function declarations
+// are 'allocator' or 'list'
+// if you #include glib *after* STL, you get those errors .. better be safe then
+#include <glib.h>
+
+#include "libxml/parser.h"
+
+#include "irefcount.h"
+#include "gtkr_list.h"
+#include "gtkr_vector.h"
+
+#include "str.h"
+
+/*!
+use when API change make things incompatible at synapse level
+i.e. entry point and classes API changes
+*/
+#define SYNAPSE_VERSION "3"
+
+/*!
+=======================================================================
+diagnostic printing facility
+independently from any API negociation stuff,
+we need a diagnostic facility that's available at all times
+=======================================================================
+*/
+extern "C"
+{
+/*!
+prototype to provide to synapse to redirect the output appropriately
+*/
+typedef void (* PFN_SYN_PRINTF_VA) (const char *text, va_list args);
+void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf); ///< change the handler, set back to NULL for default
+/*!
+use this for synapse code diagnostics, it will be piped through the handler if necessary
+*/
+void Syn_Printf (const char *text, ...);
+};
+
+/*
+=======================================================================
+client
+=======================================================================
+*/
+
+/*!
+description of an API:
+a module requires and provides several APIs
+the basic rule is that we will avoid asking an API from a module if the APIs it requires are not filled in yet
+the exception being the 'resolve' operation of a given client, which we 'activate'
+(that is we make the interfaces it provides available, leave the ones it requires unsolved, and try to get back to a stable situation)
+*/
+
+typedef enum { SYN_UNKNOWN = 0, SYN_PROVIDE, SYN_REQUIRE, SYN_REQUIRE_ANY } EAPIType;
+
+#define MAX_APINAME 128
+typedef struct APIDescriptor_s
+{
+ /*!
+ major version, this must be UNIQUE for each API
+ NOTE: we used to rely on GUID for this, that was a good solution to make sure we never get conflicts
+ but it was a bit overkill, so we dropped and use a string now
+ */
+ char major_name[MAX_APINAME];
+ /*!
+ what kind of interface
+ for instance for "image" API, "tga" "jpg" etc.
+ */
+ char minor_name[MAX_APINAME];
+ EAPIType mType; ///< is this an API we provide or an API we require
+ /*!
+ pointer to the table to be filled in
+ this is valid for SYN_REQUIRE APIs only
+ */
+ void *mpTable;
+ bool mbTableInitDone; ///< turned to true by the server after the function table has been filled in
+ /*!
+ gives the size of the expected function table
+ */
+ int mSize;
+ /*!
+ refcounts how many times this API is being used through the app
+ this is valid for SYN_PROVIDE APIs only
+ */
+ int mRefCount;
+} APIDescriptor_t;
+
+typedef struct XMLConfigEntry_s {
+ const char *api;
+ EAPIType type;
+ int size;
+ void *pTable;
+} XMLConfigEntry_t;
+
+/*!
+\class CSynapseAPIManager
+derive from this class if you want to manage several APIs through the same object
+(typically, loading plugins, or an unknown number of APIs that match some criterions)
+this class has some pure virtual members that need to be implemented by the childs
+
+we deal with two types of API managers:
+- the 'loose' ones have a matching pattern and load everything that matches criterions
+ typically used for plugins
+- the 'list' ones have a fixed list of things they require. They are used to provide
+ easy access to multiple interfaces
+
+those two types of managers are not stored in the same structs, and not handled the
+same way. For instance the 'list' manager will require ALL it's APIs to be loaded, or
+the init will fail. They also play a role in the client activation.
+
+apart from the multiple API management facility, the main difference with static tables
+and individual calls to CSynapseClient::AddAPI is the fact that the APIDescriptor_t are
+allocated on demand
+*/
+
+/* we do some matching, or store minors list, the strings need to be bigger */
+#define MAX_PATTERN_STRING 512
+
+/*! \enum EAPIManagerType
+ \brief type of this manager, loosely matching with "*", or a fixed list of required interfaces
+*/
+typedef enum { API_MATCH = 0, API_LIST } EAPIManagerType;
+
+class CSynapseAPIManager : public IRefCounted
+{
+ EAPIManagerType mType;
+
+ // the list of APIs we have obtained (SYN_REQUIRE_ANY)
+ vector< APIDescriptor_t * > mAPIs;
+ /*!
+ pattern for matching the major version
+ NOTE: only supported for now: exact match
+ */
+ char major_pattern[MAX_PATTERN_STRING];
+ /*!
+ pattern for matching the minor
+ */
+ char minor_pattern[MAX_PATTERN_STRING];
+
+public:
+ CSynapseAPIManager() { mType = API_MATCH; }
+ virtual ~CSynapseAPIManager();
+
+ EAPIManagerType GetType() { return mType; }
+ void SetType(EAPIManagerType type) { mType = type; }
+
+ /*!
+ set the API matching pattern
+ supported syntax:
+ any minor for a given major, for instance: PLUGIN_MAJOR, "*"
+ a space seperated list of minors for a given major: IMAGE_MAJOR, "tga jpg"
+ */
+ void SetMatchAPI(const char *major, const char *minor);
+
+ /*!
+ utility function
+ start building a SYN_REQUIRE_ANY descriptor from a SYN_PROVIDE interface that we found matching
+ */
+ static APIDescriptor_t* PrepareRequireAPI(APIDescriptor_t *pAPI);
+
+ /*!
+ for managers that require a fixed list of things, we are not active until everything has been loaded up
+ managers that work on a loose pattern like "*" are always active (since they don't know what they want for sure)
+ */
+ bool CheckSetActive();
+
+ /*!
+ the manager answers wether it wants to load this or not
+ we provide a default implementation, but this can be completely overriden if needed
+ see SetMatchAPI for the documentation of the default implementation
+ NOTE: this should only be called on API_MATCH type of managers
+ */
+ virtual bool MatchAPI(const char *major, const char *minor);
+
+ /*!
+ build an APIDescriptor_t configured as SYN_REQUIRE_ANY from the SYN_PROVIDE API we found
+ used when we scan the available interfaces for a match that would be interesting to this manager
+ NOTE: only for API_MATCH managers
+ */
+ virtual APIDescriptor_t *BuildRequireAPI(APIDescriptor_t *pAPI) { return NULL; }
+
+ /*!
+ below is relevant to API_LIST only ---------------------------------------------------------------------
+ */
+
+ /*!
+ fill in the table info to this descriptor, store it as a new slot
+ NOTE: only for API_LIST
+ */
+ virtual void FillAPITable(APIDescriptor_t *pAPI) { }
+
+ /*!
+ initialize the list of APIDescriptor_t* with all the stuff we expect
+ */
+ void InitializeAPIList();
+
+ /*!
+ access the API descriptors
+ */
+ int GetAPICount();
+ APIDescriptor_t *GetAPI(int);
+};
+
+/*!
+\class CSynapseClient
+*/
+class CSynapseServer; // forward declare
+class CSynapseClient : public IRefCounted
+{
+ /*!
+ this flag indicates wether this client is active
+ i.e. wether you can ask it for interfaces
+ this is either a client for which all required interfaces have been filled in
+ or a client we are trying to resolve (i.e. load with all it's stuff)
+ */
+ bool mbActive;
+
+ /*!
+ we store APIDescriptor_t*, the module fills that in at startup
+ */
+ vector<APIDescriptor_t *> mAPIDescriptors;
+
+ /*!
+ managers for multiple APIs management
+ mManagersMatch are managers with loose matching / undefined number of APIs
+ mManagersList are managers with a fixed list of required interfaces
+ */
+ vector<CSynapseAPIManager *> mManagersMatch;
+ vector<CSynapseAPIManager *> mManagersList;
+
+protected:
+ friend class CSynapseServer;
+ /*!
+ use of this is restricted to the server, expecting it knows what it is doing
+ will make the client we are trying to resolve able to provide interfaces even though all interfaces it requires have not been filled in yet.
+ */
+ void ForceSetActive() { mbActive = true; }
+
+public:
+ CSynapseClient();
+ virtual ~CSynapseClient();
+
+ int GetAPICount(); ///< returns the number of APIs that this module provides
+ APIDescriptor_t* GetAPIDescriptor(int); ///< retrieve specific information about on of the APIs
+
+ /*!
+ Add the API to the CSynapseClient information
+
+ \param minor
+ minor can be NULL, some APIs don't need to have a 'minor' description
+
+ \param type
+ SYN_PROVIDE: means this is an API we provide if anyone needs it
+ SYN_REQUIRE: means this is an API we will require for operation
+ SYN_REQUIRE_ANY: means this is an API we want to load *any* minor found
+ (for instance a list of image fornats, or the plugins)
+
+ \param pTable
+ the function table
+ only valid for SYN_REQUIRE APIs
+
+ \param size
+ the size of the function table
+ if SYN_REQUIRE, you should set the size in pTable and AddAPI will work it out
+ if SYN_PROVIDE, you need to provide this parameter
+
+ returns a bool:
+ operation may fail, since we have a few safe checks
+ */
+ bool AddAPI(const char *major, const char *minor = NULL, int size = 0, EAPIType type = SYN_PROVIDE, void *pTable = NULL);
+
+ /*!
+ Add an API manager to the client
+ this class is designed to handle multiple APIs
+ is not memory managed by CSynapseClient (should it? or ref counted maybe?)
+ writing it with support for multiple managers, that may be a bit overkill right now
+ */
+ void AddManager(CSynapseAPIManager *pManager);
+
+ int GetManagerMatchCount(); ///< how many API managers
+ CSynapseAPIManager* GetManagerMatch(int); ///< get corresponding API manager
+
+ int GetManagerListCount(); ///< how many API managers
+ CSynapseAPIManager* GetManagerList(int); ///< get corresponding API manager
+
+ /*!
+ each client has to implement this function itself
+ it will fill in the function table
+ and increment the ref counting in it's own SYN_PROVIDE descriptor
+ returns a bool, false if you ask for an API that's not available
+ */
+ virtual bool RequestAPI(APIDescriptor_t *pAPI) = 0;
+
+ /*!
+ return the build date, can be overriden by client module
+ */
+ virtual const char* GetInfo();
+
+ /*!
+ \brief a shirt name to identify the client
+ we use this string to identify individual clients, for instance when some XML configuration nodes are required
+ should be unique, the synapse server should't accept multiple occurences?
+ */
+ virtual const char* GetName() { return ""; }
+
+ bool IsActive() { return mbActive; }
+ /*!
+ check wether all interfaces have been filled in
+ in which case we will switch to 'activated' state, that is this client can provide interfaces to others now
+ */
+ bool CheckSetActive();
+
+ /*!
+ \brief called when the client is being shutdown, before the dlclose happens
+ this is the last call before the dlclose, there's no turning back
+ just do what you have to do before you die.. decref and stuff
+ */
+ void Shutdown();
+
+ /*!
+ override this one in clients that need to proceed through some init steps when activated
+ if returning false, the init will abort
+ */
+ virtual bool OnActivate() { return true; }
+
+ /*!
+ \brief walk the XML config and initialize from structures
+ when you use this function, OnActivate will also make sure all the interfaces listed were properly initialized
+ two tables, one for the regular single interface, one for the listings
+ need to store for later and check in OnActivate
+
+ \param pServer, pass the server to talk to
+ NOTE: might want to store it in the class if that's needed too often
+
+ \param client_name, the name of the client node to look for. If NULL, use GetName()
+
+ \return wether all APIs given were successfully found in the config
+ */
+ bool ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] );
+};
+
+/*!
+prototype for the only exported function needed in a synapse client
+*/
+#define NAME_SYNAPSE_ENUMERATEINTERFACES "Synapse_EnumerateInterfaces"
+
+class CSynapseServer; // forward declare
+typedef CSynapseClient* (SYNAPSE_DLL_EXPORT *PFN_SYNAPSE_ENUMERATEINTERFACES)(const char *version, CSynapseServer *server);
+
+/*!
+a derived version of CSynapseClient that can be used to provide builtin module without having to go through DLLs
+this is useful for things we want to isolate behind an abstract API, but that we feel better about having present at all times (such as .def class loader)
+*/
+class CSynapseBuiltinClient : public CSynapseClient
+{
+ public:
+ CSynapseBuiltinClient() {}
+ virtual ~CSynapseBuiltinClient() {}
+
+ virtual void EnumerateInterfaces(CSynapseServer *server) = 0;
+
+};
+
+/*
+=======================================================================
+server
+=======================================================================
+*/
+
+/*!
+ \enum EClientType
+ \brief we can have clients that are builtin to a server
+*/
+typedef enum { SYN_SO, SYN_BUILTIN } EClientType;
+
+/*!
+server side slot for a synapse client
+is OS dependant, except for the ISynapseClient part
+*/
+class CSynapseClientSlot
+{
+public:
+ /*!
+ \todo cleanup, make that private with accessors
+ */
+#if defined(__linux__) || defined(__APPLE__)
+ void *mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
+#elif defined(_WIN32)
+ HMODULE mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
+#endif
+ PFN_SYNAPSE_ENUMERATEINTERFACES mpEnumerate; ///< function pointer to the enumeration entry point (invalid if SYN_BUILTIN)
+
+ CSynapseClient *mpClient; ///< the full client API
+ Str mFileName; ///< path to the file
+
+ EClientType mType;
+
+ /*!
+ \brief release the shared object. NOTE: OS dependent
+ */
+ void ReleaseSO();
+
+ CSynapseClientSlot() { mpDLL = NULL; mpEnumerate = NULL; mpClient = NULL; mType = SYN_SO; }
+ /*!
+ NOTE: the slot is stored as static object, and copy constructors used
+ */
+ virtual ~CSynapseClientSlot() { }
+
+};
+
+/*!
+\class CSynapseServer
+dynamic modules manager class
+this class provides the server functionality:
+initialize, get a list of modules, load them, link them together..
+*/
+class CSynapseServer : public IRefCounted
+{
+ list<char *> mSearchPaths;
+ list<CSynapseClientSlot> mClients;
+
+ /*!
+ used for resolve operations
+ */
+ list<APIDescriptor_t*> mStack;
+ /*!
+ set this when mStack is modified with new stuff to resolve
+ NOTE: if this hack becomes too tricky to use we could just encapsulate mStack
+ */
+ bool mbStackChanged;
+
+ xmlDocPtr mpDoc;
+ xmlNodePtr mpFocusedNode; ///< currently focused node while we are scanning the config (strictly for GetNextConfig usage)
+ xmlNodePtr mpCurrentClientConfig;
+ /*!
+ stores the allocated strings for each call to GetNextConfig
+ need to be freed if != NULL
+ */
+ xmlChar *m_api_name;
+ gchar *m_content;
+
+ /*!
+ push required interfaces for this client into the stack of things to be resolved
+ it is possible that several mathing interfaces be in the stack at the same time
+ (for instance several modules that want to get the VFS)
+ but we should never have the same APIDescriptor_t twice
+ NOTE: as this function is called repeatedly during the resolve (because the list of required things is refining),
+ we often have to drop APIDescriptor_t requests that are already there.
+ NOTE CSynapseAPIManager: if there are CSynapseAPIManager objects in the CSynapseClient,
+ we will scan and push all the matching APIs too
+ */
+ void PushRequired(CSynapseClient *pClient);
+
+ /*!
+ work on resolving this particular APIDescriptor_t
+ returns true if we were able to resolve the interface
+ returns false otherwise
+ if the API was found, but not requested because of more required APIs, we push them in mStack
+ */
+ bool ResolveAPI(APIDescriptor_t* pAPI);
+
+ /*!
+ push an APIDescriptor_t* into the stack of things to be resolved
+ will check that this is not already present first
+ will update the mbStackChanged flag
+ */
+ void TryPushStack(APIDescriptor_t *);
+
+ /*!
+ \brief 'client shutdown' (see libs/synapse/docs/unload.txt)
+ performs a 'client shutdown'
+ will free the DLL module
+ before calling here, the client must be in a 'non active' state
+ (i.e. it was not used at all during startup, or we have properly done a 'release' already)
+ we scan the mStack for the SYN_REQUIRE that this client owns, and remove them
+ \param iSlot is an mClients iterator, invalid when the function returns as the item will have been removed from the list
+ \return the iterator afer erase call so that the caller iteration can continue
+ */
+ list<CSynapseClientSlot>::iterator ShutdownClient(list<CSynapseClientSlot>::iterator iSlot);
+
+ /*!
+ \brief actual implementation of the Resolve function
+ */
+ bool DoResolve(CSynapseClient *pClient);
+
+public:
+ CSynapseServer();
+ virtual ~CSynapseServer();
+
+ void AddSearchPath(char*); ///< add a new directory to the module search path
+ /*!
+ do the big thing, scan for modules, scan their APIs, load up everything
+ providing pf is optional, will set the diagnostics printing
+ \param conf_file is the XML configuration file for the intialization (see docs/runtime.txt)
+ \return false if the init failed (for instance not found/invalid conf file
+ */
+ bool Initialize(const char* conf_file = NULL, PFN_SYN_PRINTF_VA pf = NULL);
+
+ /*!
+ enumerate the interfaces for a given module
+ this will load it, query it's entry point, and request the APIs
+ */
+ void EnumerateInterfaces(Str &);
+
+ /*!
+ enumerate the interfaces for a module that is builtin to the server
+ */
+ void EnumerateBuiltinModule(CSynapseBuiltinClient *);
+
+ /*!
+ \brief resolve the function table loading for this client
+ if the client is not listed in the known slots yet, it will be added
+ wraps around internal DoResolve implementation to unload the unused modules
+ \return wether the resolution has been successful
+ */
+ bool Resolve(CSynapseClient *pClient);
+
+ /*!
+ \brief shutdown all the clients. Should only be called when the core is about to exit
+ this will force all clients to shutdown. it may destroy refcounted APIs and stuff
+ \todo hafta use the release/refresh code before doing actual shutdown
+ (i.e. when that code is written later on)
+ we need to 'broadcast' to all the clients .. that all the modules are going to be reloaded sorta
+ should clear up as many interfaces as possible to avoid unexpected crashes in the final stages of app exit
+ */
+ void Shutdown();
+
+ /*!
+ diagnostic print function
+ NOTE:
+ it is essential that those functions should be virtual,
+ otherwise when accessing the g_pPrintf global we could mismatch
+ (happens because the same library is linked into server and client)
+ */
+ virtual PFN_SYN_PRINTF_VA Get_Syn_Printf();
+
+ /*!
+ \return true if those APIs are matching
+ we provide two APIs for convenience, actual implementation is MatchAPI
+ the minors have to be both NULL, or equal, or one the minors be '*'
+ NOTE: the '*' minor should ONLY be used on an API that will be unique. It is hackish and kinda dangerous
+ */
+ static bool MatchAPI( APIDescriptor_t *p1, APIDescriptor_t *p2 );
+ static bool MatchAPI( const char* major1, const char* minor1, const char* major2, const char* minor2 );
+
+#if defined(_WIN32)
+ /*!
+ utility function to retrieve formatted GetLastError message
+ ANSI text, static string
+ */
+ static const char* FormatGetLastError();
+#endif
+
+ /*!
+ dump the stack of interfaces to be solved
+ this is used when synapse initialization failed to quickly identify the missing/broken pieces
+ */
+ void DumpStack();
+
+ /*!
+ general purpose information, list what modules are loaded up
+ */
+ void DumpActiveClients();
+
+ /*!
+ \brief select the config node that has this name
+ call this to locate the right node in XML config
+ this will focus and get ready to walk through the api nodes
+ \return wether the config node was correctly selected
+ */
+ bool SelectClientConfig(const char *client_name);
+
+ /*!
+ \brief walk through the apis
+ the pointers don't need to be freed
+ you need to copy them over as they are invalidated between each call to GetNextConfig
+ \return false when all apis have been parsed
+ */
+ bool GetNextConfig(char **api_name, char **minor);
+
+ /*!
+ \brief read the minor for a given api in the current config
+ \return false if this node doesn't exist
+ */
+ bool GetConfigForAPI( const char *api, char **minor );
+
+ /*!
+ returns the filename of the module that the passed on client exists in
+ */
+ const char *GetModuleFilename(CSynapseClient *pClient);
+};
+
+#endif