]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalCanvas.cxx
2e136ff2d81a98800ab0dfb243fdba9ae89c7498
[flightgear.git] / src / Scripting / NasalCanvas.cxx
1 // NasalCanvas.cxx -- expose Canvas classes to Nasal
2 //
3 // Written by James Turner, started 2012.
4 //
5 // Copyright (C) 2012 James Turner
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include <memory>
26 #include <string.h>
27
28 #include "NasalCanvas.hxx"
29 #include <Canvas/canvas_mgr.hxx>
30 #include <Main/globals.hxx>
31
32 //#include <boost/python.hpp>
33 #include <boost/foreach.hpp>
34 #include <boost/function.hpp>
35 #include <boost/utility/enable_if.hpp>
36 #include <boost/algorithm/string/case_conv.hpp>
37 #include <boost/make_shared.hpp>
38 #include <osgGA/GUIEventAdapter>
39
40 #include <simgear/sg_inlines.h>
41
42 #include <simgear/canvas/Canvas.hxx>
43 #include <simgear/canvas/elements/CanvasElement.hxx>
44
45 #include <simgear/nasal/cppbind/to_nasal.hxx>
46 #include <simgear/nasal/cppbind/NasalHash.hxx>
47
48 extern naRef propNodeGhostCreate(naContext c, SGPropertyNode* n);
49
50 //void initCanvasPython()
51 //{
52 //  using namespace boost::python;
53 //  class_<simgear::canvas::Canvas>("Canvas");
54 //}
55
56 namespace sc = simgear::canvas;
57
58 naRef canvasGetNode(naContext c, sc::Canvas* canvas)
59 {
60   return propNodeGhostCreate(c, canvas->getProps());
61 }
62
63 namespace nasal
64 {
65
66   template<class T>
67   struct class_traits
68   {
69     typedef boost::false_type::type is_shared;
70     typedef T raw_type;
71   };
72
73   template<class T>
74   struct class_traits<boost::shared_ptr<T> >
75   {
76     typedef boost::true_type::type is_shared;
77     typedef T raw_type;
78   };
79
80   template<class T>
81   struct class_traits<osg::ref_ptr<T> >
82   {
83     typedef boost::true_type::type is_shared;
84     typedef T raw_type;
85   };
86
87   template<class T>
88   struct class_traits<SGSharedPtr<T> >
89   {
90     typedef boost::true_type::type is_shared;
91     typedef T raw_type;
92   };
93
94   template<class T>
95   struct SharedPointerPolicy
96   {
97     typedef typename class_traits<T>::raw_type raw_type;
98
99     /**
100      * Create a shared pointer on the heap to handle the reference counting for
101      * the passed shared pointer while it is used in Nasal space.
102      */
103     static T* createInstance(const T& ptr)
104     {
105       return new T(ptr);
106     }
107
108     static raw_type* getRawPtr(void* ptr)
109     {
110       return static_cast<T*>(ptr)->get();
111     }
112   };
113
114   template<class T>
115   struct RawPointerPolicy
116   {
117     typedef typename class_traits<T>::raw_type raw_type;
118
119     static T* createInstance()
120     {
121       return new T();
122     }
123
124     static raw_type* getRawPtr(void* ptr)
125     {
126       BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
127       return static_cast<T*>(ptr);
128     }
129   };
130
131   class class_metadata
132   {
133     public:
134       void addNasalBase(const naRef& parent)
135       {
136         assert( naIsHash(parent) );
137         _parents.push_back(parent);
138       }
139
140     protected:
141       const std::string             _name;
142       naGhostType                   _ghost_type;
143       std::vector<class_metadata*>  _base_classes;
144       std::vector<naRef>            _parents;
145
146       explicit class_metadata(const std::string& name):
147         _name(name)
148       {
149
150       }
151
152       void addBaseClass(class_metadata* base)
153       {
154         assert(base);
155         std::cout << _name << ": addBase(" << base->_name << ")" << std::endl;
156         _base_classes.push_back(base);
157       }
158
159       naRef getParents(naContext c)
160       {
161         return nasal::to_nasal(c, _parents);
162       }
163   };
164
165   /**
166    * Class for exposing C++ objects to Nasal
167    */
168   template<class T>
169   class class_:
170     public class_metadata,
171     protected boost::mpl::if_< typename class_traits<T>::is_shared,
172                                SharedPointerPolicy<T>,
173                                RawPointerPolicy<T> >::type
174   {
175     private:
176       typedef typename class_traits<T>::raw_type raw_type;
177
178     public:
179
180       typedef boost::function<naRef(naContext c, raw_type*)> getter_t;
181       typedef std::map<std::string, getter_t> GetterMap;
182
183       static class_& init(const std::string& name)
184       {
185         getSingletonHolder().reset( new class_(name) );
186         return *getSingletonPtr();
187       }
188
189       static naRef f_create(naContext c, naRef me, int argc, naRef* args)
190       {
191         return create(c);
192       }
193
194       static naRef create( naContext c )
195       {
196         return makeGhost(c, class_::createInstance());
197       }
198
199       // TODO use variadic template when supporting C++11
200       template<class A1>
201       static naRef create( naContext c, const A1& a1 )
202       {
203         return makeGhost(c, class_::createInstance(a1));
204       }
205
206       class_& bases(const naRef& parent)
207       {
208         addNasalBase(parent);
209         return *this;
210       }
211
212       template<class Base>
213       typename boost::enable_if
214         < boost::is_convertible<Base, class_metadata>,
215           class_
216         >::type&
217       bases()
218       {
219         Base* base = Base::getSingletonPtr();
220         addBaseClass( base );
221
222         // Replace any getter that is not available in the current class.
223         // TODO check if this is the correct behavior of function overriding
224         const typename Base::GetterMap& base_getter = base->getGetter();
225         for( typename Base::GetterMap::const_iterator getter =
226                base_getter.begin();
227                getter != base_getter.end();
228              ++getter )
229         {
230           if( _getter.find(getter->first) == _getter.end() )
231             _getter.insert( *getter );
232         }
233
234         return *this;
235       }
236
237       template<class Type>
238       typename boost::enable_if_c
239         < !boost::is_convertible< Type, class_metadata>::value,
240           class_
241         >::type&
242       bases()
243       {
244         return bases< class_<Type> >();
245       }
246
247       template<class Var>
248       class_& member( const std::string& field,
249                       Var (raw_type::*getter)() const,
250                       void (raw_type::*setter)(const Var&) = 0 )
251       {
252         naRef (*to_nasal)(naContext, Var) = &nasal::to_nasal;
253         _getter[field] = boost::bind( to_nasal,
254                                       _1,
255                                       boost::bind(getter, _2) );
256         return *this;
257       }
258
259       class_& member( const std::string& field,
260                       const getter_t& getter )
261       {
262         _getter[field] = getter;
263         return *this;
264       }
265
266       static class_* getSingletonPtr()
267       {
268         return getSingletonHolder().get();
269       }
270
271       const GetterMap& getGetter() const
272       {
273         return _getter;
274       }
275
276     private:
277
278       typedef std::auto_ptr<class_> class_ptr;
279       GetterMap _getter;
280
281       explicit class_(const std::string& name):
282         class_metadata( name )
283       {
284         _ghost_type.destroy = &destroyGhost;
285         _ghost_type.name = _name.c_str();
286         _ghost_type.get_member = &getMember;
287         _ghost_type.set_member = 0;
288       }
289
290       static class_ptr& getSingletonHolder()
291       {
292         static class_ptr instance;
293         return instance;
294       }
295
296       static naRef makeGhost(naContext c, void *ptr)
297       {
298         std::cout << "makeGhost    " << ptr << std::endl;
299         return naNewGhost2(c, &getSingletonPtr()->_ghost_type, ptr);
300       }
301
302       static void destroyGhost(void *ptr)
303       {
304         std::cout << "destroyGhost " << ptr << std::endl;
305         delete (T*)ptr;
306       }
307
308       static const char* getMember(naContext c, void* g, naRef field, naRef* out)
309       {
310         const std::string key = naStr_data(field);
311         if( key == "parents" )
312         {
313           if( getSingletonPtr()->_parents.empty() )
314             return 0;
315
316           *out = getSingletonPtr()->getParents(c);
317           return "";
318         }
319
320         typename GetterMap::iterator getter =
321           getSingletonPtr()->_getter.find(key);
322
323         if( getter == getSingletonPtr()->_getter.end() )
324           return 0;
325
326         *out = getter->second(c, class_::getRawPtr(g));
327         return "";
328       }
329   };
330 }
331
332 struct Base
333 {
334   int getInt() const
335   {
336     return 8;
337   }
338 };
339
340 struct Test:
341   public Base
342 {
343   Test(): x(1) {}
344   int x;
345 };
346
347 typedef nasal::class_<sc::CanvasPtr> NasalCanvas;
348
349 void initCanvas()
350 {
351
352   NasalCanvas::init("Canvas")
353     .member("_node_ghost", &canvasGetNode)
354     .member("size_x", &sc::Canvas::getSizeX)
355     .member("size_y", &sc::Canvas::getSizeY);
356   nasal::class_<sc::ElementPtr>::init("canvas.Element");
357   nasal::class_<sc::GroupPtr>::init("canvas.Group")
358     .bases<sc::ElementPtr>();
359
360   nasal::class_<Base>::init("BaseClass")
361     .member("int", &Base::getInt);
362   nasal::class_<Test>::init("TestClass")
363     .bases<Base>();
364 }
365
366 #if 0
367 /**
368  * Class for exposing C++ objects to Nasal
369  */
370 template<class T, class Derived>
371 class NasalObject
372 {
373   public:
374     // TODO use variadic template when supporting C++11
375     template<class A1>
376     static naRef create( naContext c, const A1& a1 )
377     {
378       return makeGhost(c, new T(a1));
379     }
380
381     template<class A1, class A2>
382     static naRef create( naContext c, const A1& a1,
383                                       const A2& a2 )
384     {
385       return makeGhost(c, new T(a1, a2));
386     }
387
388     template<class A1, class A2, class A3>
389     static naRef create( naContext c, const A1& a1,
390                                       const A2& a2,
391                                       const A3& a3 )
392     {
393       return makeGhost(c, new T(a1, a2, a3));
394     }
395
396     template<class A1, class A2, class A3, class A4>
397     static naRef create( naContext c, const A1& a1,
398                                       const A2& a2,
399                                       const A3& a3,
400                                       const A4& a4 )
401     {
402       return makeGhost(c, new T(a1, a2, a3, a4));
403     }
404
405     template<class A1, class A2, class A3, class A4, class A5>
406     static naRef create( naContext c, const A1& a1,
407                                       const A2& a2,
408                                       const A3& a3,
409                                       const A4& a4,
410                                       const A5& a5 )
411     {
412       return makeGhost(c, new T(a1, a2, a3, a4, a5));
413     }
414
415     // TODO If you need more arguments just do some copy&paste :)
416
417     static Derived& getInstance()
418     {
419       static Derived instance;
420       return instance;
421     }
422
423     void setParent(const naRef& parent)
424     {
425       // TODO check if we need to take care of reference counting/gc
426       _parents.resize(1);
427       _parents[0] = parent;
428     }
429
430   protected:
431
432     // TODO switch to boost::/std::function (with C++11 lambdas this can make
433     //      adding setters easier and shorter)
434     typedef naRef (Derived::*getter_t)(naContext, const T&);
435     typedef std::map<std::string, getter_t> GetterMap;
436
437     const std::string   _ghost_name;
438     std::vector<naRef>  _parents;
439     GetterMap           _getter;
440
441     NasalObject(const std::string& ghost_name):
442       _ghost_name( ghost_name )
443     {
444       _ghost_type.destroy = &destroyGhost;
445       _ghost_type.name = _ghost_name.c_str();
446       _ghost_type.get_member = &Derived::getMember;
447       _ghost_type.set_member = 0;
448
449       _getter["parents"] = &NasalObject::getParents;
450     }
451
452     naRef getParents(naContext c, const T&)
453     {
454       naRef parents = naNewVector(c);
455       for(size_t i = 0; i < _parents.size(); ++i)
456         naVec_append(parents, _parents[i]);
457       return parents;
458     }
459
460     static naRef makeGhost(naContext c, void *ptr)
461     {
462       std::cout << "create  " << ptr << std::endl;
463       return naNewGhost2(c, &(getInstance()._ghost_type), ptr);
464     }
465
466     static void destroyGhost(void *ptr)
467     {
468       std::cout << "destroy " << ptr << std::endl;
469       delete (T*)ptr;
470     }
471
472     static const char* getMember(naContext c, void* g, naRef field, naRef* out)
473     {
474       typename GetterMap::iterator getter =
475         getInstance()._getter.find(naStr_data(field));
476
477       if( getter == getInstance()._getter.end() )
478         return 0;
479
480       *out = (getInstance().*getter->second)(c, *static_cast<T*>(g));
481       return "";
482     }
483
484   private:
485
486     naGhostType _ghost_type;
487
488 };
489
490 typedef osg::ref_ptr<osgGA::GUIEventAdapter> GUIEventPtr;
491
492 class NasalCanvasEvent:
493   public NasalObject<GUIEventPtr, NasalCanvasEvent>
494 {
495   public:
496
497     NasalCanvasEvent():
498       NasalObject("CanvasEvent")
499     {
500       _getter["type"] = &NasalCanvasEvent::getEventType;
501     }
502
503     naRef getEventType(naContext c, const GUIEventPtr& event)
504     {
505 #define RET_EVENT_STR(type, str)\
506   case osgGA::GUIEventAdapter::type:\
507     return nasal::to_nasal(c, str);
508
509       switch( event->getEventType() )
510       {
511         RET_EVENT_STR(PUSH,         "push");
512         RET_EVENT_STR(RELEASE,      "release");
513         RET_EVENT_STR(DOUBLECLICK,  "double-click");
514         RET_EVENT_STR(DRAG,         "drag");
515         RET_EVENT_STR(MOVE,         "move");
516         RET_EVENT_STR(SCROLL,       "scroll");
517         RET_EVENT_STR(KEYUP,        "key-up");
518         RET_EVENT_STR(KEYDOWN,      "key-down");
519
520 #undef RET_EVENT_STR
521
522         default:
523           return naNil();
524       }
525     }
526 };
527 #endif
528 #if 0
529 static const char* eventGhostGetMember(naContext c, void* g, naRef field, naRef* out)
530 {
531   const char* fieldName = naStr_data(field);
532   osgGA::GUIEventAdapter* gea = (osgGA::GUIEventAdapter*) g;
533
534   if (!strcmp(fieldName, "windowX")) *out = naNum(gea->getWindowX());
535   else if (!strcmp(fieldName, "windowY")) *out = naNum(gea->getWindowY());
536   else if (!strcmp(fieldName, "time")) *out = naNum(gea->getTime());
537   else if (!strcmp(fieldName, "button")) *out = naNum(gea->getButton());
538   else {
539     return 0;
540   }
541
542   return "";
543 }
544
545 static naRef f_element_addButtonCallback(naContext c, naRef me, int argc, naRef* args)
546 {
547   simgear::canvas::Element* e = elementGhost(me);
548   if (!e) {
549     naRuntimeError(c, "element.addButtonCallback called on non-canvas-element object");
550   }
551   
552   return naNil();
553 }
554
555 static naRef f_element_addDragCallback(naContext c, naRef me, int argc, naRef* args)
556 {
557   simgear::canvas::Element* e = elementGhost(me);
558   if (!e) {
559     naRuntimeError(c, "element.addDragCallback called on non-canvas-element object");
560   }
561   
562   return naNil();
563 }
564
565 static naRef f_element_addMoveCallback(naContext c, naRef me, int argc, naRef* args)
566 {
567   simgear::canvas::Element* e = elementGhost(me);
568   if (!e) {
569     naRuntimeError(c, "element.addMoveCallback called on non-canvas-element object");
570   }
571   
572   return naNil();
573 }
574
575 static naRef f_element_addScrollCallback(naContext c, naRef me, int argc, naRef* args)
576 {
577   simgear::canvas::Element* e = elementGhost(me);
578   if (!e) {
579     naRuntimeError(c, "element.addScrollCallback called on non-canvas-element object");
580   }
581   
582   return naNil();
583 }
584 #endif
585
586 static naRef f_createCanvas(naContext c, naRef me, int argc, naRef* args)
587 {
588   std::cout << "f_createCanvas" << std::endl;
589
590   CanvasMgr* canvas_mgr =
591     static_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
592   if( !canvas_mgr )
593     return naNil();
594
595   return NasalCanvas::create(c, canvas_mgr->createCanvas());
596 }
597
598 naRef initNasalCanvas(naRef globals, naContext c, naRef gcSave)
599 {
600       /*naNewHash(c);
601     hashset(c, gcSave, "canvasProto", canvasPrototype);
602   
603     hashset(c, canvasPrototype, "getElement", naNewFunc(c, naNewCCode(c, f_canvas_getElement)));*/
604     // set any event methods
605   
606 #if 0
607     elementPrototype = naNewHash(c);
608     hashset(c, gcSave, "elementProto", elementPrototype);
609     
610     hashset(c, elementPrototype, "addButtonCallback", naNewFunc(c, naNewCCode(c, f_element_addButtonCallback)));
611     hashset(c, elementPrototype, "addDragCallback", naNewFunc(c, naNewCCode(c, f_element_addDragCallback)));
612     hashset(c, elementPrototype, "addMoveCallback", naNewFunc(c, naNewCCode(c, f_element_addMoveCallback)));
613     hashset(c, elementPrototype, "addScrollCallback", naNewFunc(c, naNewCCode(c, f_element_addScrollCallback)));
614 #endif
615   nasal::Hash globals_module(globals, c),
616               canvas_module = globals_module.createHash("canvas");
617
618   canvas_module.set("_new", f_createCanvas);
619   canvas_module.set("testClass", nasal::class_<Test>::f_create);
620
621   initCanvas();
622
623   return naNil();
624 }