2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
22 #ifndef __SYNAPSE_H__
\r
23 #define __SYNAPSE_H__
\r
27 code and utilities to deal with dynamic components programming
\r
29 "the point at which a nervous impulse passes from one neuron to another"
\r
33 STL for some algorithms and data structures
\r
34 glib for Str.h (Str class)
\r
36 this is a utility library, it provides typical synapse client and server
\r
37 could be split into two independant libraries actually, the server part and the client part
\r
38 (that's just a matter of reducing binary size)
\r
41 // compile time settings
\r
43 #define SYNAPSE_VERBOSE // be verbosive about the loading process
\r
46 // ydnar: required for os x
\r
47 #if defined (__APPLE__)
\r
48 #include <sys/types.h>
\r
51 #if defined (__linux__) || defined (__APPLE__)
\r
53 #include <dirent.h>
\r
57 #include <windows.h>
\r
61 #define SYNAPSE_DLL_EXPORT WINAPI
\r
62 #elif defined(__linux__) || defined(__APPLE__) /* ydnar */
\r
63 #define SYNAPSE_DLL_EXPORT
\r
65 #error unknown architecture
\r
68 // NOTE TTimo: VC6 crap, gets confused when some variable names in function declarations
\r
69 // are 'allocator' or 'list'
\r
70 // if you #include glib *after* STL, you get those errors .. better be safe then
\r
73 #include "libxml/parser.h"
\r
75 #include "irefcount.h"
\r
76 #include "gtkr_list.h"
\r
77 #include "gtkr_vector.h"
\r
82 use when API change make things incompatible at synapse level
\r
83 i.e. entry point and classes API changes
\r
85 #define SYNAPSE_VERSION "3"
\r
88 =======================================================================
\r
89 diagnostic printing facility
\r
90 independently from any API negociation stuff,
\r
91 we need a diagnostic facility that's available at all times
\r
92 =======================================================================
\r
97 prototype to provide to synapse to redirect the output appropriately
\r
99 typedef void (* PFN_SYN_PRINTF_VA) (const char *text, va_list args);
\r
100 void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf); ///< change the handler, set back to NULL for default
\r
102 use this for synapse code diagnostics, it will be piped through the handler if necessary
\r
104 void Syn_Printf (const char *text, ...);
\r
108 =======================================================================
\r
110 =======================================================================
\r
114 description of an API:
\r
115 a module requires and provides several APIs
\r
116 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
117 the exception being the 'resolve' operation of a given client, which we 'activate'
\r
118 (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
121 typedef enum { SYN_UNKNOWN = 0, SYN_PROVIDE, SYN_REQUIRE, SYN_REQUIRE_ANY } EAPIType;
\r
123 #define MAX_APINAME 128
\r
124 typedef struct APIDescriptor_s
\r
127 major version, this must be UNIQUE for each API
\r
128 NOTE: we used to rely on GUID for this, that was a good solution to make sure we never get conflicts
\r
129 but it was a bit overkill, so we dropped and use a string now
\r
131 char major_name[MAX_APINAME];
\r
133 what kind of interface
\r
134 for instance for "image" API, "tga" "jpg" etc.
\r
136 char minor_name[MAX_APINAME];
\r
137 EAPIType mType; ///< is this an API we provide or an API we require
\r
139 pointer to the table to be filled in
\r
140 this is valid for SYN_REQUIRE APIs only
\r
143 bool mbTableInitDone; ///< turned to true by the server after the function table has been filled in
\r
145 gives the size of the expected function table
\r
149 refcounts how many times this API is being used through the app
\r
150 this is valid for SYN_PROVIDE APIs only
\r
155 typedef struct XMLConfigEntry_s {
\r
160 } XMLConfigEntry_t;
\r
163 \class CSynapseAPIManager
\r
164 derive from this class if you want to manage several APIs through the same object
\r
165 (typically, loading plugins, or an unknown number of APIs that match some criterions)
\r
166 this class has some pure virtual members that need to be implemented by the childs
\r
168 we deal with two types of API managers:
\r
169 - the 'loose' ones have a matching pattern and load everything that matches criterions
\r
170 typically used for plugins
\r
171 - the 'list' ones have a fixed list of things they require. They are used to provide
\r
172 easy access to multiple interfaces
\r
174 those two types of managers are not stored in the same structs, and not handled the
\r
175 same way. For instance the 'list' manager will require ALL it's APIs to be loaded, or
\r
176 the init will fail. They also play a role in the client activation.
\r
178 apart from the multiple API management facility, the main difference with static tables
\r
179 and individual calls to CSynapseClient::AddAPI is the fact that the APIDescriptor_t are
\r
180 allocated on demand
\r
183 /* we do some matching, or store minors list, the strings need to be bigger */
\r
184 #define MAX_PATTERN_STRING 512
\r
186 /*! \enum EAPIManagerType
\r
187 \brief type of this manager, loosely matching with "*", or a fixed list of required interfaces
\r
189 typedef enum { API_MATCH = 0, API_LIST } EAPIManagerType;
\r
191 class CSynapseAPIManager : public IRefCounted
\r
193 EAPIManagerType mType;
\r
195 // the list of APIs we have obtained (SYN_REQUIRE_ANY)
\r
196 vector< APIDescriptor_t * > mAPIs;
\r
198 pattern for matching the major version
\r
199 NOTE: only supported for now: exact match
\r
201 char major_pattern[MAX_PATTERN_STRING];
\r
203 pattern for matching the minor
\r
205 char minor_pattern[MAX_PATTERN_STRING];
\r
208 CSynapseAPIManager() { mType = API_MATCH; }
\r
209 virtual ~CSynapseAPIManager();
\r
211 EAPIManagerType GetType() { return mType; }
\r
212 void SetType(EAPIManagerType type) { mType = type; }
\r
215 set the API matching pattern
\r
217 any minor for a given major, for instance: PLUGIN_MAJOR, "*"
\r
218 a space seperated list of minors for a given major: IMAGE_MAJOR, "tga jpg"
\r
220 void SetMatchAPI(const char *major, const char *minor);
\r
224 start building a SYN_REQUIRE_ANY descriptor from a SYN_PROVIDE interface that we found matching
\r
226 static APIDescriptor_t* PrepareRequireAPI(APIDescriptor_t *pAPI);
\r
229 for managers that require a fixed list of things, we are not active until everything has been loaded up
\r
230 managers that work on a loose pattern like "*" are always active (since they don't know what they want for sure)
\r
232 bool CheckSetActive();
\r
235 the manager answers wether it wants to load this or not
\r
236 we provide a default implementation, but this can be completely overriden if needed
\r
237 see SetMatchAPI for the documentation of the default implementation
\r
238 NOTE: this should only be called on API_MATCH type of managers
\r
240 virtual bool MatchAPI(const char *major, const char *minor);
\r
243 build an APIDescriptor_t configured as SYN_REQUIRE_ANY from the SYN_PROVIDE API we found
\r
244 used when we scan the available interfaces for a match that would be interesting to this manager
\r
245 NOTE: only for API_MATCH managers
\r
247 virtual APIDescriptor_t *BuildRequireAPI(APIDescriptor_t *pAPI) { return NULL; }
\r
250 below is relevant to API_LIST only ---------------------------------------------------------------------
\r
254 fill in the table info to this descriptor, store it as a new slot
\r
255 NOTE: only for API_LIST
\r
257 virtual void FillAPITable(APIDescriptor_t *pAPI) { }
\r
260 initialize the list of APIDescriptor_t* with all the stuff we expect
\r
262 void InitializeAPIList();
\r
265 access the API descriptors
\r
268 APIDescriptor_t *GetAPI(int);
\r
272 \class CSynapseClient
\r
274 class CSynapseServer; // forward declare
\r
275 class CSynapseClient : public IRefCounted
\r
278 this flag indicates wether this client is active
\r
279 i.e. wether you can ask it for interfaces
\r
280 this is either a client for which all required interfaces have been filled in
\r
281 or a client we are trying to resolve (i.e. load with all it's stuff)
\r
286 we store APIDescriptor_t*, the module fills that in at startup
\r
288 vector<APIDescriptor_t *> mAPIDescriptors;
\r
291 managers for multiple APIs management
\r
292 mManagersMatch are managers with loose matching / undefined number of APIs
\r
293 mManagersList are managers with a fixed list of required interfaces
\r
295 vector<CSynapseAPIManager *> mManagersMatch;
\r
296 vector<CSynapseAPIManager *> mManagersList;
\r
299 friend class CSynapseServer;
\r
301 use of this is restricted to the server, expecting it knows what it is doing
\r
302 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
304 void ForceSetActive() { mbActive = true; }
\r
308 virtual ~CSynapseClient();
\r
310 int GetAPICount(); ///< returns the number of APIs that this module provides
\r
311 APIDescriptor_t* GetAPIDescriptor(int); ///< retrieve specific information about on of the APIs
\r
314 Add the API to the CSynapseClient information
\r
317 minor can be NULL, some APIs don't need to have a 'minor' description
\r
320 SYN_PROVIDE: means this is an API we provide if anyone needs it
\r
321 SYN_REQUIRE: means this is an API we will require for operation
\r
322 SYN_REQUIRE_ANY: means this is an API we want to load *any* minor found
\r
323 (for instance a list of image fornats, or the plugins)
\r
327 only valid for SYN_REQUIRE APIs
\r
330 the size of the function table
\r
331 if SYN_REQUIRE, you should set the size in pTable and AddAPI will work it out
\r
332 if SYN_PROVIDE, you need to provide this parameter
\r
335 operation may fail, since we have a few safe checks
\r
337 bool AddAPI(const char *major, const char *minor = NULL, int size = 0, EAPIType type = SYN_PROVIDE, void *pTable = NULL);
\r
340 Add an API manager to the client
\r
341 this class is designed to handle multiple APIs
\r
342 is not memory managed by CSynapseClient (should it? or ref counted maybe?)
\r
343 writing it with support for multiple managers, that may be a bit overkill right now
\r
345 void AddManager(CSynapseAPIManager *pManager);
\r
347 int GetManagerMatchCount(); ///< how many API managers
\r
348 CSynapseAPIManager* GetManagerMatch(int); ///< get corresponding API manager
\r
350 int GetManagerListCount(); ///< how many API managers
\r
351 CSynapseAPIManager* GetManagerList(int); ///< get corresponding API manager
\r
354 each client has to implement this function itself
\r
355 it will fill in the function table
\r
356 and increment the ref counting in it's own SYN_PROVIDE descriptor
\r
357 returns a bool, false if you ask for an API that's not available
\r
359 virtual bool RequestAPI(APIDescriptor_t *pAPI) = 0;
\r
362 return the build date, can be overriden by client module
\r
364 virtual const char* GetInfo();
\r
367 \brief a shirt name to identify the client
\r
368 we use this string to identify individual clients, for instance when some XML configuration nodes are required
\r
369 should be unique, the synapse server should't accept multiple occurences?
\r
371 virtual const char* GetName() { return ""; }
\r
373 bool IsActive() { return mbActive; }
\r
375 check wether all interfaces have been filled in
\r
376 in which case we will switch to 'activated' state, that is this client can provide interfaces to others now
\r
378 bool CheckSetActive();
\r
381 \brief called when the client is being shutdown, before the dlclose happens
\r
382 this is the last call before the dlclose, there's no turning back
\r
383 just do what you have to do before you die.. decref and stuff
\r
388 override this one in clients that need to proceed through some init steps when activated
\r
389 if returning false, the init will abort
\r
391 virtual bool OnActivate() { return true; }
\r
394 \brief walk the XML config and initialize from structures
\r
395 when you use this function, OnActivate will also make sure all the interfaces listed were properly initialized
\r
396 two tables, one for the regular single interface, one for the listings
\r
397 need to store for later and check in OnActivate
\r
399 \param pServer, pass the server to talk to
\r
400 NOTE: might want to store it in the class if that's needed too often
\r
402 \param client_name, the name of the client node to look for. If NULL, use GetName()
\r
404 \return wether all APIs given were successfully found in the config
\r
406 bool ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] );
\r
410 prototype for the only exported function needed in a synapse client
\r
412 #define NAME_SYNAPSE_ENUMERATEINTERFACES "Synapse_EnumerateInterfaces"
\r
414 class CSynapseServer; // forward declare
\r
415 typedef CSynapseClient* (SYNAPSE_DLL_EXPORT *PFN_SYNAPSE_ENUMERATEINTERFACES)(const char *version, CSynapseServer *server);
\r
418 a derived version of CSynapseClient that can be used to provide builtin module without having to go through DLLs
\r
419 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
421 class CSynapseBuiltinClient : public CSynapseClient
\r
424 CSynapseBuiltinClient() {}
\r
425 virtual ~CSynapseBuiltinClient() {}
\r
427 virtual void EnumerateInterfaces(CSynapseServer *server) = 0;
\r
432 =======================================================================
\r
434 =======================================================================
\r
439 \brief we can have clients that are builtin to a server
\r
441 typedef enum { SYN_SO, SYN_BUILTIN } EClientType;
\r
444 server side slot for a synapse client
\r
445 is OS dependant, except for the ISynapseClient part
\r
447 class CSynapseClientSlot
\r
451 \todo cleanup, make that private with accessors
\r
453 #if defined(__linux__) || defined(__APPLE__)
\r
454 void *mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
\r
455 #elif defined(_WIN32)
\r
456 HMODULE mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
\r
458 PFN_SYNAPSE_ENUMERATEINTERFACES mpEnumerate; ///< function pointer to the enumeration entry point (invalid if SYN_BUILTIN)
\r
460 CSynapseClient *mpClient; ///< the full client API
\r
461 Str mFileName; ///< path to the file
\r
466 \brief release the shared object. NOTE: OS dependent
\r
470 CSynapseClientSlot() { mpDLL = NULL; mpEnumerate = NULL; mpClient = NULL; mType = SYN_SO; }
\r
472 NOTE: the slot is stored as static object, and copy constructors used
\r
474 virtual ~CSynapseClientSlot() { }
\r
479 \class CSynapseServer
\r
480 dynamic modules manager class
\r
481 this class provides the server functionality:
\r
482 initialize, get a list of modules, load them, link them together..
\r
484 class CSynapseServer : public IRefCounted
\r
486 list<char *> mSearchPaths;
\r
487 list<CSynapseClientSlot> mClients;
\r
490 used for resolve operations
\r
492 list<APIDescriptor_t*> mStack;
\r
494 set this when mStack is modified with new stuff to resolve
\r
495 NOTE: if this hack becomes too tricky to use we could just encapsulate mStack
\r
497 bool mbStackChanged;
\r
500 xmlNodePtr mpFocusedNode; ///< currently focused node while we are scanning the config (strictly for GetNextConfig usage)
\r
501 xmlNodePtr mpCurrentClientConfig;
\r
503 stores the allocated strings for each call to GetNextConfig
\r
504 need to be freed if != NULL
\r
506 xmlChar *m_api_name;
\r
510 push required interfaces for this client into the stack of things to be resolved
\r
511 it is possible that several mathing interfaces be in the stack at the same time
\r
512 (for instance several modules that want to get the VFS)
\r
513 but we should never have the same APIDescriptor_t twice
\r
514 NOTE: as this function is called repeatedly during the resolve (because the list of required things is refining),
\r
515 we often have to drop APIDescriptor_t requests that are already there.
\r
516 NOTE CSynapseAPIManager: if there are CSynapseAPIManager objects in the CSynapseClient,
\r
517 we will scan and push all the matching APIs too
\r
519 void PushRequired(CSynapseClient *pClient);
\r
522 work on resolving this particular APIDescriptor_t
\r
523 returns true if we were able to resolve the interface
\r
524 returns false otherwise
\r
525 if the API was found, but not requested because of more required APIs, we push them in mStack
\r
527 bool ResolveAPI(APIDescriptor_t* pAPI);
\r
530 push an APIDescriptor_t* into the stack of things to be resolved
\r
531 will check that this is not already present first
\r
532 will update the mbStackChanged flag
\r
534 void TryPushStack(APIDescriptor_t *);
\r
537 \brief 'client shutdown' (see libs/synapse/docs/unload.txt)
\r
538 performs a 'client shutdown'
\r
539 will free the DLL module
\r
540 before calling here, the client must be in a 'non active' state
\r
541 (i.e. it was not used at all during startup, or we have properly done a 'release' already)
\r
542 we scan the mStack for the SYN_REQUIRE that this client owns, and remove them
\r
543 \param iSlot is an mClients iterator, invalid when the function returns as the item will have been removed from the list
\r
544 \return the iterator afer erase call so that the caller iteration can continue
\r
546 list<CSynapseClientSlot>::iterator ShutdownClient(list<CSynapseClientSlot>::iterator iSlot);
\r
549 \brief actual implementation of the Resolve function
\r
551 bool DoResolve(CSynapseClient *pClient);
\r
555 virtual ~CSynapseServer();
\r
557 void AddSearchPath(char*); ///< add a new directory to the module search path
\r
559 do the big thing, scan for modules, scan their APIs, load up everything
\r
560 providing pf is optional, will set the diagnostics printing
\r
561 \param conf_file is the XML configuration file for the intialization (see docs/runtime.txt)
\r
562 \return false if the init failed (for instance not found/invalid conf file
\r
564 bool Initialize(const char* conf_file = NULL, PFN_SYN_PRINTF_VA pf = NULL);
\r
567 enumerate the interfaces for a given module
\r
568 this will load it, query it's entry point, and request the APIs
\r
570 void EnumerateInterfaces(Str &);
\r
573 enumerate the interfaces for a module that is builtin to the server
\r
575 void EnumerateBuiltinModule(CSynapseBuiltinClient *);
\r
578 \brief resolve the function table loading for this client
\r
579 if the client is not listed in the known slots yet, it will be added
\r
580 wraps around internal DoResolve implementation to unload the unused modules
\r
581 \return wether the resolution has been successful
\r
583 bool Resolve(CSynapseClient *pClient);
\r
586 \brief shutdown all the clients. Should only be called when the core is about to exit
\r
587 this will force all clients to shutdown. it may destroy refcounted APIs and stuff
\r
588 \todo hafta use the release/refresh code before doing actual shutdown
\r
589 (i.e. when that code is written later on)
\r
590 we need to 'broadcast' to all the clients .. that all the modules are going to be reloaded sorta
\r
591 should clear up as many interfaces as possible to avoid unexpected crashes in the final stages of app exit
\r
596 diagnostic print function
\r
598 it is essential that those functions should be virtual,
\r
599 otherwise when accessing the g_pPrintf global we could mismatch
\r
600 (happens because the same library is linked into server and client)
\r
602 virtual PFN_SYN_PRINTF_VA Get_Syn_Printf();
\r
605 \return true if those APIs are matching
\r
606 we provide two APIs for convenience, actual implementation is MatchAPI
\r
607 the minors have to be both NULL, or equal, or one the minors be '*'
\r
608 NOTE: the '*' minor should ONLY be used on an API that will be unique. It is hackish and kinda dangerous
\r
610 static bool MatchAPI( APIDescriptor_t *p1, APIDescriptor_t *p2 );
\r
611 static bool MatchAPI( const char* major1, const char* minor1, const char* major2, const char* minor2 );
\r
613 #if defined(_WIN32)
\r
615 utility function to retrieve formatted GetLastError message
\r
616 ANSI text, static string
\r
618 static const char* FormatGetLastError();
\r
622 dump the stack of interfaces to be solved
\r
623 this is used when synapse initialization failed to quickly identify the missing/broken pieces
\r
628 general purpose information, list what modules are loaded up
\r
630 void DumpActiveClients();
\r
633 \brief select the config node that has this name
\r
634 call this to locate the right node in XML config
\r
635 this will focus and get ready to walk through the api nodes
\r
636 \return wether the config node was correctly selected
\r
638 bool SelectClientConfig(const char *client_name);
\r
641 \brief walk through the apis
\r
642 the pointers don't need to be freed
\r
643 you need to copy them over as they are invalidated between each call to GetNextConfig
\r
644 \return false when all apis have been parsed
\r
646 bool GetNextConfig(char **api_name, char **minor);
\r
649 \brief read the minor for a given api in the current config
\r
650 \return false if this node doesn't exist
\r
652 bool GetConfigForAPI( const char *api, char **minor );
\r
655 returns the filename of the module that the passed on client exists in
\r
657 const char *GetModuleFilename(CSynapseClient *pClient);
\r