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