]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/synapse.h
eol
[xonotic/netradiant.git] / libs / synapse.h
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #ifndef __SYNAPSE_H__
23 #define __SYNAPSE_H__
24
25 /*!
26 synapse library
27 code and utilities to deal with dynamic components programming
28
29 "the point at which a nervous impulse passes from one neuron to another"
30
31 dependencies:
32   libxml for parsing
33   STL for some algorithms and data structures
34   glib for Str.h (Str class)
35   
36 this is a utility library, it provides typical synapse client and server
37 could be split into two independant libraries actually, the server part and the client part
38 (that's just a matter of reducing binary size)  
39 */
40
41 // compile time settings
42 #ifdef _DEBUG
43   #define SYNAPSE_VERBOSE // be verbosive about the loading process
44 #endif
45
46 // ydnar: required for os x
47 #if defined (__APPLE__)
48   #include <sys/types.h>
49 #endif
50
51 #if defined (__linux__) || defined (__APPLE__)
52   #include <dlfcn.h>
53   #include <dirent.h>  
54 #endif
55
56 #if defined(_WIN32)
57   #include <windows.h>
58 #endif
59
60 #if defined(_WIN32)
61   #define SYNAPSE_DLL_EXPORT WINAPI
62 #elif defined(__linux__) || defined(__APPLE__)  /* ydnar */
63 //  #define SYNAPSE_DLL_EXPORT __attribute__ ((visibility ("protected")))
64   #define SYNAPSE_DLL_EXPORT
65 #else
66   #error unknown architecture
67 #endif
68
69 // NOTE TTimo: VC6 crap, gets confused when some variable names in function declarations
70 //   are 'allocator' or 'list'
71 //   if you #include glib *after* STL, you get those errors .. better be safe then
72 #include <glib.h>
73
74 #include "libxml/parser.h"
75
76 #include "irefcount.h"
77 #include "gtkr_list.h"
78 #include "gtkr_vector.h"
79
80 #include "str.h"
81
82 /*!
83 use when API change make things incompatible at synapse level
84 i.e. entry point and classes API changes
85 */
86 #define SYNAPSE_VERSION "3"
87
88 /*!
89 =======================================================================
90 diagnostic printing facility
91 independently from any API negociation stuff,
92 we need a diagnostic facility that's available at all times
93 =======================================================================
94 */
95 extern "C"
96 {
97 /*!
98 prototype to provide to synapse to redirect the output appropriately  
99 */  
100 typedef void (* PFN_SYN_PRINTF_VA) (const char *text, va_list args);
101 void Set_Syn_Printf(PFN_SYN_PRINTF_VA pf); ///< change the handler, set back to NULL for default
102 /*!
103 use this for synapse code diagnostics, it will be piped through the handler if necessary
104 */
105 void Syn_Printf (const char *text, ...);
106 };
107   
108 /*
109 =======================================================================
110 client
111 =======================================================================
112 */
113
114 /*!
115 description of an API:
116 a module requires and provides several APIs
117 the basic rule is that we will avoid asking an API from a module if the APIs it requires are not filled in yet
118 the exception being the 'resolve' operation of a given client, which we 'activate'
119 (that is we make the interfaces it provides available, leave the ones it requires unsolved, and try to get back to a stable situation)
120 */
121
122 typedef enum { SYN_UNKNOWN = 0, SYN_PROVIDE, SYN_REQUIRE, SYN_REQUIRE_ANY } EAPIType;
123
124 #define MAX_APINAME 128
125 typedef struct APIDescriptor_s
126 {
127   /*!
128   major version, this must be UNIQUE for each API
129   NOTE: we used to rely on GUID for this, that was a good solution to make sure we never get conflicts
130   but it was a bit overkill, so we dropped and use a string now
131   */
132   char major_name[MAX_APINAME];
133   /*!
134   what kind of interface
135   for instance for "image" API, "tga" "jpg" etc.
136   */
137   char minor_name[MAX_APINAME];
138   EAPIType mType; ///< is this an API we provide or an API we require
139   /*!
140   pointer to the table to be filled in
141   this is valid for SYN_REQUIRE APIs only
142   */
143   void *mpTable;
144   bool mbTableInitDone; ///< turned to true by the server after the function table has been filled in
145   /*!
146   gives the size of the expected function table  
147   */
148   int mSize;
149   /*!
150   refcounts how many times this API is being used through the app
151   this is valid for SYN_PROVIDE APIs only
152   */
153   int mRefCount;
154 } APIDescriptor_t;
155
156 typedef struct XMLConfigEntry_s {
157   const char *api;
158   EAPIType type;
159   int size;
160   void *pTable;
161 } XMLConfigEntry_t;
162
163 /*!
164 \class CSynapseAPIManager
165 derive from this class if you want to manage several APIs through the same object
166 (typically, loading plugins, or an unknown number of APIs that match some criterions)
167 this class has some pure virtual members that need to be implemented by the childs
168
169 we deal with two types of API managers:
170 - the 'loose' ones have a matching pattern and load everything that matches criterions
171   typically used for plugins
172 - the 'list' ones have a fixed list of things they require. They are used to provide
173   easy access to multiple interfaces
174
175 those two types of managers are not stored in the same structs, and not handled the
176 same way. For instance the 'list' manager will require ALL it's APIs to be loaded, or
177 the init will fail. They also play a role in the client activation.
178
179 apart from the multiple API management facility, the main difference with static tables
180 and individual calls to CSynapseClient::AddAPI is the fact that the APIDescriptor_t are
181 allocated on demand
182 */
183
184 /* we do some matching, or store minors list, the strings need to be bigger */
185 #define MAX_PATTERN_STRING 512
186
187 /*! \enum EAPIManagerType
188   \brief type of this manager, loosely matching with "*", or a fixed list of required interfaces
189 */
190 typedef enum { API_MATCH = 0,  API_LIST } EAPIManagerType;
191
192 class CSynapseAPIManager : public IRefCounted
193 {
194   EAPIManagerType mType;
195   
196   // the list of APIs we have obtained (SYN_REQUIRE_ANY)
197   vector< APIDescriptor_t * > mAPIs;
198   /*!
199   pattern for matching the major version
200   NOTE: only supported for now: exact match
201   */
202   char major_pattern[MAX_PATTERN_STRING];
203   /*!
204   pattern for matching the minor
205   */
206   char minor_pattern[MAX_PATTERN_STRING];  
207   
208 public:
209   CSynapseAPIManager() { mType = API_MATCH; }
210   virtual ~CSynapseAPIManager();
211
212   EAPIManagerType GetType() { return mType; }
213   void SetType(EAPIManagerType type) { mType = type; }
214   
215   /*!
216   set the API matching pattern
217   supported syntax:
218   any minor for a given major, for instance: PLUGIN_MAJOR, "*"
219   a space seperated list of minors for a given major: IMAGE_MAJOR, "tga jpg"
220   */
221   void SetMatchAPI(const char *major, const char *minor);
222   
223   /*!
224   utility function
225   start building a SYN_REQUIRE_ANY descriptor from a SYN_PROVIDE interface that we found matching
226   */
227   static APIDescriptor_t* PrepareRequireAPI(APIDescriptor_t *pAPI);  
228   
229   /*!
230   for managers that require a fixed list of things, we are not active until everything has been loaded up
231   managers that work on a loose pattern like "*" are always active (since they don't know what they want for sure)
232   */
233   bool CheckSetActive();
234   
235   /*!
236   the manager answers wether it wants to load this or not
237   we provide a default implementation, but this can be completely overriden if needed
238   see SetMatchAPI for the documentation of the default implementation
239   NOTE: this should only be called on API_MATCH type of managers
240   */
241   virtual bool MatchAPI(const char *major, const char *minor);
242   
243   /*!
244   build an APIDescriptor_t configured as SYN_REQUIRE_ANY from the SYN_PROVIDE API we found
245   used when we scan the available interfaces for a match that would be interesting to this manager
246   NOTE: only for API_MATCH managers
247   */
248   virtual APIDescriptor_t *BuildRequireAPI(APIDescriptor_t *pAPI) { return NULL; }
249   
250   /*!
251   below is relevant to API_LIST only ---------------------------------------------------------------------
252   */
253
254   /*!
255   fill in the table info to this descriptor, store it as a new slot
256   NOTE: only for API_LIST
257   */
258   virtual void FillAPITable(APIDescriptor_t *pAPI) { }
259   
260   /*!
261   initialize the list of APIDescriptor_t* with all the stuff we expect
262   */
263   void InitializeAPIList();
264   
265   /*!
266   access the API descriptors
267   */
268   int GetAPICount();
269   APIDescriptor_t *GetAPI(int);
270 };
271
272 /*!
273 \class CSynapseClient
274 */
275 class CSynapseServer; // forward declare
276 class CSynapseClient : public IRefCounted
277 {
278   /*!
279   this flag indicates wether this client is active
280   i.e. wether you can ask it for interfaces
281   this is either a client for which all required interfaces have been filled in
282   or a client we are trying to resolve (i.e. load with all it's stuff)
283   */
284   bool mbActive;
285   
286   /*!
287   we store APIDescriptor_t*, the module fills that in at startup
288   */
289   vector<APIDescriptor_t *> mAPIDescriptors;
290   
291   /*!
292   managers for multiple APIs management
293   mManagersMatch are managers with loose matching / undefined number of APIs
294   mManagersList are managers with a fixed list of required interfaces
295   */
296   vector<CSynapseAPIManager *> mManagersMatch;
297   vector<CSynapseAPIManager *> mManagersList;
298   
299 protected:
300   friend class CSynapseServer;
301   /*!
302   use of this is restricted to the server, expecting it knows what it is doing
303   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.
304   */
305   void ForceSetActive() { mbActive = true; }
306   
307 public:
308   CSynapseClient();
309   virtual ~CSynapseClient();
310
311   int GetAPICount(); ///< returns the number of APIs that this module provides
312   APIDescriptor_t* GetAPIDescriptor(int); ///< retrieve specific information about on of the APIs
313
314   /*!
315   Add the API to the CSynapseClient information
316   
317   \param minor
318   minor can be NULL, some APIs don't need to have a 'minor' description  
319   
320   \param type
321   SYN_PROVIDE: means this is an API we provide if anyone needs it
322   SYN_REQUIRE: means this is an API we will require for operation
323   SYN_REQUIRE_ANY: means this is an API we want to load *any* minor found
324      (for instance a list of image fornats, or the plugins)
325      
326   \param pTable 
327   the function table
328   only valid for SYN_REQUIRE APIs
329   
330   \param size
331   the size of the function table
332   if SYN_REQUIRE, you should set the size in pTable and AddAPI will work it out
333   if SYN_PROVIDE, you need to provide this parameter
334   
335   returns a bool:
336   operation may fail, since we have a few safe checks  
337   */
338   bool AddAPI(const char *major, const char *minor = NULL, int size = 0, EAPIType type = SYN_PROVIDE, void *pTable = NULL);
339   
340   /*!
341   Add an API manager to the client
342   this class is designed to handle multiple APIs
343   is not memory managed by CSynapseClient (should it? or ref counted maybe?)
344   writing it with support for multiple managers, that may be a bit overkill right now
345   */
346   void AddManager(CSynapseAPIManager *pManager);
347   
348   int GetManagerMatchCount(); ///< how many API managers
349   CSynapseAPIManager* GetManagerMatch(int); ///< get corresponding API manager
350
351   int GetManagerListCount(); ///< how many API managers
352   CSynapseAPIManager* GetManagerList(int); ///< get corresponding API manager
353   
354   /*!
355   each client has to implement this function itself
356   it will fill in the function table
357   and increment the ref counting in it's own SYN_PROVIDE descriptor
358   returns a bool, false if you ask for an API that's not available
359   */
360   virtual bool RequestAPI(APIDescriptor_t *pAPI) = 0;
361   
362   /*!
363   return the build date, can be overriden by client module
364   */
365   virtual const char* GetInfo();
366   
367   /*!
368   \brief a shirt name to identify the client
369   we use this string to identify individual clients, for instance when some XML configuration nodes are required
370   should be unique, the synapse server should't accept multiple occurences?  
371   */
372   virtual const char* GetName() { return ""; }
373   
374   bool IsActive() { return mbActive; }
375   /*!
376   check wether all interfaces have been filled in
377   in which case we will switch to 'activated' state, that is this client can provide interfaces to others now
378   */
379   bool CheckSetActive();
380   
381   /*!
382   \brief called when the client is being shutdown, before the dlclose happens
383   this is the last call before the dlclose, there's no turning back
384   just do what you have to do before you die.. decref and stuff
385   */
386   void Shutdown();
387   
388   /*!
389   override this one in clients that need to proceed through some init steps when activated
390   if returning false, the init will abort
391   */
392   virtual bool OnActivate() { return true; }
393   
394   /*!
395   \brief walk the XML config and initialize from structures
396   when you use this function, OnActivate will also make sure all the interfaces listed were properly initialized
397   two tables, one for the regular single interface, one for the listings
398   need to store for later and check in OnActivate
399   
400   \param pServer, pass the server to talk to
401   NOTE: might want to store it in the class if that's needed too often
402   
403   \param client_name, the name of the client node to look for. If NULL, use GetName()
404   
405   \return wether all APIs given were successfully found in the config
406   */
407   bool ConfigXML( CSynapseServer *pServer, const char *client_name, const XMLConfigEntry_t entries[] );
408 };
409
410 /*!
411 prototype for the only exported function needed in a synapse client
412 */
413 #define NAME_SYNAPSE_ENUMERATEINTERFACES "Synapse_EnumerateInterfaces"
414
415 class CSynapseServer; // forward declare
416 typedef CSynapseClient* (SYNAPSE_DLL_EXPORT *PFN_SYNAPSE_ENUMERATEINTERFACES)(const char *version, CSynapseServer *server);
417
418 /*!
419 a derived version of CSynapseClient that can be used to provide builtin module without having to go through DLLs
420 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)
421 */
422 class CSynapseBuiltinClient : public CSynapseClient
423 {
424   public:
425   CSynapseBuiltinClient() {}
426   virtual ~CSynapseBuiltinClient() {}
427   
428   virtual void EnumerateInterfaces(CSynapseServer *server) = 0;
429   
430 };
431
432 /*
433 =======================================================================
434 server
435 =======================================================================
436 */
437
438 /*!
439   \enum EClientType
440   \brief we can have clients that are builtin to a server
441 */
442 typedef enum { SYN_SO, SYN_BUILTIN } EClientType;
443
444 /*!
445 server side slot for a synapse client
446 is OS dependant, except for the ISynapseClient part
447 */
448 class CSynapseClientSlot
449 {
450 public:
451   /*!
452   \todo cleanup, make that private with accessors
453   */
454 #if defined(__linux__) || defined(__APPLE__)
455   void *mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
456 #elif defined(_WIN32)
457   HMODULE mpDLL; ///< handle to the shared object (invalid if SYN_BUILTIN)
458 #endif
459   PFN_SYNAPSE_ENUMERATEINTERFACES mpEnumerate; ///< function pointer to the enumeration entry point (invalid if SYN_BUILTIN)
460
461   CSynapseClient *mpClient; ///< the full client API
462   Str mFileName; ///< path to the file
463   
464   EClientType mType;
465
466   /*!
467   \brief release the shared object. NOTE: OS dependent  
468   */
469   void ReleaseSO();
470   
471   CSynapseClientSlot() { mpDLL = NULL; mpEnumerate = NULL; mpClient = NULL; mType = SYN_SO; }
472   /*!
473   NOTE: the slot is stored as static object, and copy constructors used  
474   */
475   virtual ~CSynapseClientSlot() { }
476
477 };
478
479 /*!
480 \class CSynapseServer
481 dynamic modules manager class
482 this class provides the server functionality:
483 initialize, get a list of modules, load them, link them together..
484 */
485 class CSynapseServer : public IRefCounted
486 {
487   list<char *> mSearchPaths;
488   list<CSynapseClientSlot> mClients;
489           
490   /*!
491   used for resolve operations
492   */
493   list<APIDescriptor_t*> mStack;
494   /*!
495   set this when mStack is modified with new stuff to resolve
496   NOTE: if this hack becomes too tricky to use we could just encapsulate mStack
497   */
498   bool mbStackChanged;
499   
500   xmlDocPtr mpDoc;
501   xmlNodePtr mpFocusedNode; ///< currently focused node while we are scanning the config (strictly for GetNextConfig usage)
502   xmlNodePtr mpCurrentClientConfig;
503   /*!
504   stores the allocated strings for each call to GetNextConfig
505   need to be freed if != NULL
506   */
507   xmlChar *m_api_name;
508   gchar *m_content;
509     
510   /*!
511   push required interfaces for this client into the stack of things to be resolved
512   it is possible that several mathing interfaces be in the stack at the same time
513   (for instance several modules that want to get the VFS)
514   but we should never have the same APIDescriptor_t twice
515   NOTE: as this function is called repeatedly during the resolve (because the list of required things is refining),
516     we often have to drop APIDescriptor_t requests that are already there.
517   NOTE CSynapseAPIManager: if there are CSynapseAPIManager objects in the CSynapseClient,
518     we will scan and push all the matching APIs too
519   */
520   void PushRequired(CSynapseClient *pClient);
521   
522   /*!
523   work on resolving this particular APIDescriptor_t
524   returns true if we were able to resolve the interface
525   returns false otherwise
526   if the API was found, but not requested because of more required APIs, we push them in mStack
527   */
528   bool ResolveAPI(APIDescriptor_t* pAPI);
529   
530   /*!
531   push an APIDescriptor_t* into the stack of things to be resolved
532   will check that this is not already present first
533   will update the mbStackChanged flag
534   */
535   void TryPushStack(APIDescriptor_t *);
536         
537   /*!
538   \brief 'client shutdown' (see libs/synapse/docs/unload.txt)
539   performs a 'client shutdown'
540   will free the DLL module
541   before calling here, the client must be in a 'non active' state
542   (i.e. it was not used at all during startup, or we have properly done a 'release' already)
543   we scan the mStack for the SYN_REQUIRE that this client owns, and remove them
544   \param iSlot is an mClients iterator, invalid when the function returns as the item will have been removed from the list
545   \return the iterator afer erase call so that the caller iteration can continue
546   */
547   list<CSynapseClientSlot>::iterator ShutdownClient(list<CSynapseClientSlot>::iterator iSlot);
548   
549   /*!
550   \brief actual implementation of the Resolve function
551   */
552   bool DoResolve(CSynapseClient *pClient);  
553     
554 public:
555   CSynapseServer();
556   virtual ~CSynapseServer();
557
558   void AddSearchPath(char*); ///< add a new directory to the module search path
559   /*!
560   do the big thing, scan for modules, scan their APIs, load up everything
561   providing pf is optional, will set the diagnostics printing
562   \param conf_file is the XML configuration file for the intialization (see docs/runtime.txt)
563   \return false if the init failed (for instance not found/invalid conf file
564   */
565   bool Initialize(const char* conf_file = NULL, PFN_SYN_PRINTF_VA pf = NULL);
566
567   /*!
568   enumerate the interfaces for a given module
569   this will load it, query it's entry point, and request the APIs
570   */
571   void EnumerateInterfaces(Str &);
572   
573   /*!
574   enumerate the interfaces for a module that is builtin to the server
575   */
576   void EnumerateBuiltinModule(CSynapseBuiltinClient *);
577   
578   /*!
579   \brief resolve the function table loading for this client
580   if the client is not listed in the known slots yet, it will be added
581   wraps around internal DoResolve implementation to unload the unused modules
582   \return wether the resolution has been successful  
583   */
584   bool Resolve(CSynapseClient *pClient);
585   
586   /*!
587   \brief shutdown all the clients. Should only be called when the core is about to exit
588   this will force all clients to shutdown. it may destroy refcounted APIs and stuff
589   \todo hafta use the release/refresh code before doing actual shutdown
590   (i.e. when that code is written later on)
591   we need to 'broadcast' to all the clients .. that all the modules are going to be reloaded sorta
592   should clear up as many interfaces as possible to avoid unexpected crashes in the final stages of app exit
593   */
594   void Shutdown();
595   
596   /*!
597   diagnostic print function
598   NOTE:
599     it is essential that those functions should be virtual,
600     otherwise when accessing the g_pPrintf global we could mismatch
601     (happens because the same library is linked into server and client)
602   */
603   virtual PFN_SYN_PRINTF_VA Get_Syn_Printf();
604   
605   /*!
606   \return true if those APIs are matching
607   we provide two APIs for convenience, actual implementation is MatchAPI
608   the minors have to be both NULL, or equal, or one the minors be '*'
609   NOTE: the '*' minor should ONLY be used on an API that will be unique. It is hackish and kinda dangerous
610   */
611   static bool MatchAPI( APIDescriptor_t *p1, APIDescriptor_t *p2 );
612   static bool MatchAPI( const char* major1, const char* minor1, const char* major2, const char* minor2 );
613   
614 #if defined(_WIN32)
615   /*!
616   utility function to retrieve formatted GetLastError message
617   ANSI text, static string
618   */
619   static const char* FormatGetLastError();
620 #endif
621
622   /*!
623   dump the stack of interfaces to be solved
624   this is used when synapse initialization failed to quickly identify the missing/broken pieces
625   */
626   void DumpStack();
627   
628   /*!
629   general purpose information, list what modules are loaded up
630   */
631   void DumpActiveClients();
632   
633   /*!
634   \brief select the config node that has this name
635   call this to locate the right node in XML config
636   this will focus and get ready to walk through the api nodes
637   \return wether the config node was correctly selected
638   */
639   bool SelectClientConfig(const char *client_name);
640   
641   /*!
642   \brief walk through the apis
643   the pointers don't need to be freed
644   you need to copy them over as they are invalidated between each call to GetNextConfig
645   \return false when all apis have been parsed
646   */
647   bool GetNextConfig(char **api_name, char **minor);
648   
649   /*!
650   \brief read the minor for a given api in the current config
651   \return false if this node doesn't exist
652   */
653   bool GetConfigForAPI( const char *api, char **minor );
654
655   /*!
656   returns the filename of the module that the passed on client exists in
657   */
658   const char *GetModuleFilename(CSynapseClient *pClient);
659 };
660
661 #endif