1 // NasalCanvas.cxx -- expose Canvas classes to Nasal
3 // Written by James Turner, started 2012.
5 // Copyright (C) 2012 James Turner
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.
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.
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.
28 #include "NasalCanvas.hxx"
29 #include <Canvas/canvas_mgr.hxx>
30 #include <Main/globals.hxx>
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>
40 #include <simgear/sg_inlines.h>
42 #include <simgear/canvas/Canvas.hxx>
43 #include <simgear/canvas/elements/CanvasElement.hxx>
45 static naRef elementPrototype;
46 static naRef propsNodePrototype;
48 extern naRef propNodeGhostCreate(naContext c, SGPropertyNode* n);
50 static void hashset(naContext c, naRef hash, const char* key, naRef val)
52 naRef s = naNewString(c);
53 naStr_fromdata(s, (char*)key, strlen(key));
54 naHash_set(hash, s, val);
58 typename boost::enable_if<boost::is_arithmetic<T>, naRef>::type
64 //void initCanvasPython()
66 // using namespace boost::python;
67 // class_<simgear::canvas::Canvas>("Canvas");
70 namespace sc = simgear::canvas;
72 naRef canvasGetTexture(naContext c, sc::Canvas* canvas)
74 //{ parents : [Node], _g : node }
75 naRef parents = naNewVector(c);
76 naVec_append(parents, propsNodePrototype);
78 naRef node = naNewHash(c);
79 hashset(c, node, "parents", parents);
80 hashset(c, node, "_g", propNodeGhostCreate(c, canvas->getProps()));
91 typedef boost::false_type::type is_shared;
96 struct class_traits<boost::shared_ptr<T> >
98 typedef boost::true_type::type is_shared;
103 struct class_traits<osg::ref_ptr<T> >
105 typedef boost::true_type::type is_shared;
110 struct class_traits<SGSharedPtr<T> >
112 typedef boost::true_type::type is_shared;
117 struct SharedPointerPolicy
119 typedef typename class_traits<T>::raw_type raw_type;
122 * Create a shared pointer on the heap to handle the reference counting for
123 * the passed shared pointer while it is used in Nasal space.
125 static T* createInstance(const T& ptr)
130 static raw_type* getRawPtr(void* ptr)
132 return static_cast<T*>(ptr)->get();
137 struct RawPointerPolicy
139 typedef typename class_traits<T>::raw_type raw_type;
141 static T* createInstance()
146 static raw_type* getRawPtr(void* ptr)
148 BOOST_STATIC_ASSERT((boost::is_same<raw_type, T>::value));
149 return static_cast<T*>(ptr);
156 void addNasalBase(const naRef& parent)
158 assert( naIsHash(parent) );
159 _parents.push_back(parent);
163 const std::string _name;
164 naGhostType _ghost_type;
165 std::vector<class_metadata*> _base_classes;
166 std::vector<naRef> _parents;
168 explicit class_metadata(const std::string& name):
174 void addBaseClass(class_metadata* base)
177 std::cout << _name << ": addBase(" << base->_name << ")" << std::endl;
178 _base_classes.push_back(base);
181 naRef getParents(naContext c)
183 naRef parents = naNewVector(c);
184 for(size_t i = 0; i < _parents.size(); ++i)
185 naVec_append(parents, _parents[i]);
191 * Class for exposing C++ objects to Nasal
195 public class_metadata,
196 protected boost::mpl::if_< typename class_traits<T>::is_shared,
197 SharedPointerPolicy<T>,
198 RawPointerPolicy<T> >::type
201 typedef typename class_traits<T>::raw_type raw_type;
205 typedef boost::function<naRef(naContext c, raw_type*)> getter_t;
206 typedef std::map<std::string, getter_t> GetterMap;
208 static class_& init(const std::string& name)
210 getSingletonHolder().reset( new class_(name) );
211 return *getSingletonPtr();
214 static naRef f_create(naContext c, naRef me, int argc, naRef* args)
219 static naRef create( naContext c )
221 return makeGhost(c, class_::createInstance());
224 // TODO use variadic template when supporting C++11
226 static naRef create( naContext c, const A1& a1 )
228 return makeGhost(c, class_::createInstance(a1));
231 class_& bases(const naRef& parent)
233 addNasalBase(parent);
238 typename boost::enable_if
239 < boost::is_convertible<Base, class_metadata>,
244 Base* base = Base::getSingletonPtr();
245 addBaseClass( base );
247 // Replace any getter that is not available in the current class.
248 // TODO check if this is the correct behavior of function overriding
249 const typename Base::GetterMap& base_getter = base->getGetter();
250 for( typename Base::GetterMap::const_iterator getter =
252 getter != base_getter.end();
255 if( _getter.find(getter->first) == _getter.end() )
256 _getter.insert( *getter );
263 typename boost::enable_if_c
264 < !boost::is_convertible< Type, class_metadata>::value,
269 return bases< class_<Type> >();
273 class_& def( const std::string& field,
274 Var (raw_type::*getter)() const )
276 _getter[field] = boost::bind( &convertToNasal<Var>,
277 boost::bind(getter, _2) );
281 class_& def_readonly( const std::string& field,
284 _getter[field] = getter;
292 class_& def_readonly( const std::string& field,
295 _getter[field] = boost::bind( &convertToNasal<Var>,
296 boost::bind(var, _2) );
300 static class_* getSingletonPtr()
302 return getSingletonHolder().get();
305 const GetterMap& getGetter() const
312 typedef std::auto_ptr<class_> class_ptr;
315 explicit class_(const std::string& name):
316 class_metadata( name )
318 _ghost_type.destroy = &destroyGhost;
319 _ghost_type.name = _name.c_str();
320 _ghost_type.get_member = &getMember;
321 _ghost_type.set_member = 0;
324 static class_ptr& getSingletonHolder()
326 static class_ptr instance;
330 static naRef makeGhost(naContext c, void *ptr)
332 std::cout << "makeGhost " << ptr << std::endl;
333 return naNewGhost2(c, &getSingletonPtr()->_ghost_type, ptr);
336 static void destroyGhost(void *ptr)
338 std::cout << "destroyGhost " << ptr << std::endl;
342 static const char* getMember(naContext c, void* g, naRef field, naRef* out)
344 const std::string key = naStr_data(field);
345 if( key == "parents" )
347 *out = getSingletonPtr()->getParents(c);
351 typename GetterMap::iterator getter =
352 getSingletonPtr()->_getter.find(key);
354 if( getter == getSingletonPtr()->_getter.end() )
357 *out = getter->second(c, class_::getRawPtr(g));
378 typedef nasal::class_<sc::CanvasPtr> NasalCanvas;
383 NasalCanvas::init("Canvas")
384 .def_readonly("texture", &canvasGetTexture)
385 .def("size_x", &sc::Canvas::getSizeX)
386 .def("size_y", &sc::Canvas::getSizeY);
387 nasal::class_<sc::ElementPtr>::init("canvas.Element");
388 nasal::class_<sc::GroupPtr>::init("canvas.Group")
389 .bases<sc::ElementPtr>();
391 nasal::class_<Base>::init("BaseClass")
392 .def("int", &Base::getInt);
393 nasal::class_<Test>::init("TestClass")
395 .def_readonly("x", &Test::x);
406 * Initialize from an existing Nasal Hash
408 * @param hash Existing Nasal Hash
409 * @param c Nasal context
411 Hash(const naRef& hash, naContext c):
415 assert( naIsHash(_hash) );
418 void set(const std::string& name, naRef val)
420 naHash_set(_hash, stringToNasal(name), val);
423 void set(const std::string& name, naCFunction func)
425 set(name, naNewFunc(_context, naNewCCode(_context, func)));
428 void set(const std::string& name, const std::string& str)
430 set(name, stringToNasal(str));
433 void set(const std::string& name, double num)
435 set(name, naNum(num));
439 * Create a new child hash (module)
441 * @param name Name of the new hash inside this hash
443 Hash createHash(const std::string& name)
445 naRef hash = naNewHash(_context);
447 return Hash(hash, _context);
454 naRef stringToNasal(const std::string& str)
456 naRef s = naNewString(_context);
457 naStr_fromdata(s, str.c_str(), str.size());
463 * Class for exposing C++ objects to Nasal
465 template<class T, class Derived>
469 // TODO use variadic template when supporting C++11
471 static naRef create( naContext c, const A1& a1 )
473 return makeGhost(c, new T(a1));
476 template<class A1, class A2>
477 static naRef create( naContext c, const A1& a1,
480 return makeGhost(c, new T(a1, a2));
483 template<class A1, class A2, class A3>
484 static naRef create( naContext c, const A1& a1,
488 return makeGhost(c, new T(a1, a2, a3));
491 template<class A1, class A2, class A3, class A4>
492 static naRef create( naContext c, const A1& a1,
497 return makeGhost(c, new T(a1, a2, a3, a4));
500 template<class A1, class A2, class A3, class A4, class A5>
501 static naRef create( naContext c, const A1& a1,
507 return makeGhost(c, new T(a1, a2, a3, a4, a5));
510 // TODO If you need more arguments just do some copy&paste :)
512 static Derived& getInstance()
514 static Derived instance;
518 void setParent(const naRef& parent)
520 // TODO check if we need to take care of reference counting/gc
522 _parents[0] = parent;
527 // TODO switch to boost::/std::function (with C++11 lambdas this can make
528 // adding setters easier and shorter)
529 typedef naRef (Derived::*getter_t)(naContext, const T&);
530 typedef std::map<std::string, getter_t> GetterMap;
532 const std::string _ghost_name;
533 std::vector<naRef> _parents;
536 NasalObject(const std::string& ghost_name):
537 _ghost_name( ghost_name )
539 _ghost_type.destroy = &destroyGhost;
540 _ghost_type.name = _ghost_name.c_str();
541 _ghost_type.get_member = &Derived::getMember;
542 _ghost_type.set_member = 0;
544 _getter["parents"] = &NasalObject::getParents;
547 naRef getParents(naContext c, const T&)
549 naRef parents = naNewVector(c);
550 for(size_t i = 0; i < _parents.size(); ++i)
551 naVec_append(parents, _parents[i]);
555 static naRef makeGhost(naContext c, void *ptr)
557 std::cout << "create " << ptr << std::endl;
558 return naNewGhost2(c, &(getInstance()._ghost_type), ptr);
561 static void destroyGhost(void *ptr)
563 std::cout << "destroy " << ptr << std::endl;
567 static const char* getMember(naContext c, void* g, naRef field, naRef* out)
569 typename GetterMap::iterator getter =
570 getInstance()._getter.find(naStr_data(field));
572 if( getter == getInstance()._getter.end() )
575 *out = (getInstance().*getter->second)(c, *static_cast<T*>(g));
581 naGhostType _ghost_type;
585 static naRef stringToNasal(naContext c, const std::string& s)
587 return naStr_fromdata(naNewString(c),
588 const_cast<char *>(s.c_str()),
592 typedef osg::ref_ptr<osgGA::GUIEventAdapter> GUIEventPtr;
594 class NasalCanvasEvent:
595 public NasalObject<GUIEventPtr, NasalCanvasEvent>
600 NasalObject("CanvasEvent")
602 _getter["type"] = &NasalCanvasEvent::getEventType;
605 naRef getEventType(naContext c, const GUIEventPtr& event)
607 #define RET_EVENT_STR(type, str)\
608 case osgGA::GUIEventAdapter::type:\
609 return stringToNasal(c, str);
611 switch( event->getEventType() )
613 RET_EVENT_STR(PUSH, "push");
614 RET_EVENT_STR(RELEASE, "release");
615 RET_EVENT_STR(DOUBLECLICK, "double-click");
616 RET_EVENT_STR(DRAG, "drag");
617 RET_EVENT_STR(MOVE, "move");
618 RET_EVENT_STR(SCROLL, "scroll");
619 RET_EVENT_STR(KEYUP, "key-up");
620 RET_EVENT_STR(KEYDOWN, "key-down");
630 //using simgear::canvas::CanvasPtr;
633 // * Expose Canvas to Nasal
636 // public NasalObject<CanvasPtr, NasalCanvas>
641 // NasalObject("Canvas")
643 // _getter["texture"] = &NasalCanvas::getTexture;
644 // _getter["size_x"] = &NasalCanvas::getSizeX;
645 // _getter["size_y"] = &NasalCanvas::getSizeY;
648 // static naRef f_create(naContext c, naRef me, int argc, naRef* args)
650 // std::cout << "NasalCanvas::create" << std::endl;
652 // CanvasMgr* canvas_mgr =
653 // static_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
657 // return create(c, canvas_mgr->createCanvas());
660 // static naRef f_setPrototype(naContext c, naRef me, int argc, naRef* args)
662 // if( argc != 1 || !naIsHash(args[0]) )
663 // naRuntimeError(c, "Invalid argument(s)");
665 // getInstance().setParent(args[0]);
670 // naRef getTexture(naContext c, const CanvasPtr& canvas)
672 // //{ parents : [Node], _g : node }
673 // naRef parents = naNewVector(c);
674 // naVec_append(parents, propsNodePrototype);
676 // naRef node = naNewHash(c);
677 // hashset(c, node, "parents", parents);
678 // hashset(c, node, "_g", propNodeGhostCreate(c, canvas->getProps()));
683 // naRef getSizeX(naContext c, const CanvasPtr& canvas)
685 // return naNum(canvas->getSizeX());
688 // naRef getSizeY(naContext c, const CanvasPtr& canvas)
690 // return naNum(canvas->getSizeY());
694 static void elementGhostDestroy(void* g);
696 static const char* elementGhostGetMember(naContext c, void* g, naRef field, naRef* out);
697 static void elementGhostSetMember(naContext c, void* g, naRef field, naRef value);
698 naGhostType ElementGhostType = { elementGhostDestroy, "canvas.element",
699 elementGhostGetMember, elementGhostSetMember };
701 static simgear::canvas::Element* elementGhost(naRef r)
703 if (naGhost_type(r) == &ElementGhostType)
704 return (simgear::canvas::Element*) naGhost_ptr(r);
708 static void elementGhostDestroy(void* g)
712 static const char* eventGhostGetMember(naContext c, void* g, naRef field, naRef* out)
714 const char* fieldName = naStr_data(field);
715 osgGA::GUIEventAdapter* gea = (osgGA::GUIEventAdapter*) g;
717 if (!strcmp(fieldName, "windowX")) *out = naNum(gea->getWindowX());
718 else if (!strcmp(fieldName, "windowY")) *out = naNum(gea->getWindowY());
719 else if (!strcmp(fieldName, "time")) *out = naNum(gea->getTime());
720 else if (!strcmp(fieldName, "button")) *out = naNum(gea->getButton());
728 static const char* elementGhostGetMember(naContext c, void* g, naRef field, naRef* out)
730 const char* fieldName = naStr_data(field);
731 simgear::canvas::Element* e = (simgear::canvas::Element*) g;
734 if (!strcmp(fieldName, "parents")) {
735 *out = naNewVector(c);
736 naVec_append(*out, elementPrototype);
744 static void elementGhostSetMember(naContext c, void* g, naRef field, naRef value)
746 const char* fieldName = naStr_data(field);
747 simgear::canvas::Element* e = (simgear::canvas::Element*) g;
748 SG_UNUSED(fieldName);
753 static naRef f_canvas_getElement(naContext c, naRef me, int argc, naRef* args)
755 // simgear::canvas::Canvas* cvs = canvasGhost(me);
757 // naRuntimeError(c, "canvas.getElement called on non-canvas object");
763 static naRef f_element_addButtonCallback(naContext c, naRef me, int argc, naRef* args)
765 simgear::canvas::Element* e = elementGhost(me);
767 naRuntimeError(c, "element.addButtonCallback called on non-canvas-element object");
773 static naRef f_element_addDragCallback(naContext c, naRef me, int argc, naRef* args)
775 simgear::canvas::Element* e = elementGhost(me);
777 naRuntimeError(c, "element.addDragCallback called on non-canvas-element object");
783 static naRef f_element_addMoveCallback(naContext c, naRef me, int argc, naRef* args)
785 simgear::canvas::Element* e = elementGhost(me);
787 naRuntimeError(c, "element.addMoveCallback called on non-canvas-element object");
793 static naRef f_element_addScrollCallback(naContext c, naRef me, int argc, naRef* args)
795 simgear::canvas::Element* e = elementGhost(me);
797 naRuntimeError(c, "element.addScrollCallback called on non-canvas-element object");
803 static naRef f_createCanvas(naContext c, naRef me, int argc, naRef* args)
805 std::cout << "f_createCanvas" << std::endl;
807 CanvasMgr* canvas_mgr =
808 static_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
812 return NasalCanvas::create(c, canvas_mgr->createCanvas());
815 static naRef f_setCanvasPrototype(naContext c, naRef me, int argc, naRef* args)
817 if( argc != 1 || !naIsHash(args[0]) )
818 naRuntimeError(c, "Invalid argument(s)");
820 NasalCanvas::getSingletonPtr()->addNasalBase(args[0]);
825 naRef initNasalCanvas(naRef globals, naContext c, naRef gcSave)
827 naRef props_module = naHash_cget(globals, (char*)"props");
828 if( naIsNil(props_module) )
829 std::cerr << "No props module" << std::endl;
831 propsNodePrototype = naHash_cget(props_module, (char*)"Node");
832 if( naIsNil(propsNodePrototype) )
833 std::cerr << "Failed to get props.Node" << std::endl;
836 hashset(c, gcSave, "canvasProto", canvasPrototype);
838 hashset(c, canvasPrototype, "getElement", naNewFunc(c, naNewCCode(c, f_canvas_getElement)));*/
839 // set any event methods
841 elementPrototype = naNewHash(c);
842 hashset(c, gcSave, "elementProto", elementPrototype);
844 hashset(c, elementPrototype, "addButtonCallback", naNewFunc(c, naNewCCode(c, f_element_addButtonCallback)));
845 hashset(c, elementPrototype, "addDragCallback", naNewFunc(c, naNewCCode(c, f_element_addDragCallback)));
846 hashset(c, elementPrototype, "addMoveCallback", naNewFunc(c, naNewCCode(c, f_element_addMoveCallback)));
847 hashset(c, elementPrototype, "addScrollCallback", naNewFunc(c, naNewCCode(c, f_element_addScrollCallback)));
849 Hash globals_module(globals, c),
850 canvas_module = globals_module.createHash("canvas");
852 canvas_module.set("_new", f_createCanvas);
853 canvas_module.set("_setPrototype", f_setCanvasPrototype);
854 canvas_module.set("testClass", nasal::class_<Test>::f_create);