From 96c0a7bbc16e044524f6aab5d5b74203864640ef Mon Sep 17 00:00:00 2001 From: mfranz Date: Fri, 16 Dec 2005 19:11:03 +0000 Subject: [PATCH] add _setlistener() function, so that slow property polling loops can be replaced with efficient listener callbacks. One use is the new FPS display. This is reviewed and OK'ed by Andy, relatively trivial and separated from the rest of Nasal, so problems are quite unlikely and confined to users of this function. The callback is executed whenever the property is written to -- even if the value didn't change. The triggering Node is available via cmdarg(). Examples: _setlistener("/sim/crashed", func {print("haha!")}); _setlistener("/foo/bar", func { print(cmdarg().getPath() ~ " changed")}) --- src/Scripting/NasalSys.cxx | 29 +++++++++++++++++++++++++++++ src/Scripting/NasalSys.hxx | 23 +++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 870d6def1..53b9f8357 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -234,6 +234,15 @@ static naRef f_settimer(naContext c, naRef me, int argc, naRef* args) return naNil(); } +// setlistener(func, property) extension function. Falls through to +// FGNasalSys::setListener(). See there for docs. +static naRef f_setlistener(naContext c, naRef me, int argc, naRef* args) +{ + FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal"); + nasal->setListener(argc, args); + return naNil(); +} + // Returns a ghost handle to the argument to the currently executing // command static naRef f_cmdarg(naContext c, naRef me, int argc, naRef* args) @@ -285,6 +294,7 @@ static struct { char* name; naCFunction func; } funcs[] = { { "print", f_print }, { "_fgcommand", f_fgcommand }, { "settimer", f_settimer }, + { "_setlistener", f_setlistener }, { "_cmdarg", f_cmdarg }, { "_interpolate", f_interpolate }, { "rand", f_rand }, @@ -553,3 +563,22 @@ void FGNasalSys::NasalTimer::timerExpired() nasal->handleTimer(this); delete this; } + +// setlistener(property, func) extension function. The first argument +// is either a ghost (SGPropertyNode_ptr*) or a string (global property +// path), the second is a Nasal function. +void FGNasalSys::setListener(int argc, naRef* args) +{ + SGPropertyNode* node; + naRef prop = argc > 0 ? args[0] : naNil(); + if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true); + else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop); + else return; + + naRef handler = argc > 1 ? args[1] : naNil(); + if(!(naIsCode(handler) || naIsCCode(handler) || naIsFunc(handler))) + return; + + node->addChangeListener(new FGNasalListener(handler, this, gcSave(handler))); +} + diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx index 4d59b8b2c..f3e756b59 100644 --- a/src/Scripting/NasalSys.hxx +++ b/src/Scripting/NasalSys.hxx @@ -36,6 +36,9 @@ public: // Implementation of the settimer extension function void setTimer(int argc, naRef* args); + // Implementation of the setlistener extension function + void setListener(int argc, naRef* args); + // Returns a ghost wrapper for the current _cmdArg naRef cmdArgGhost(); @@ -47,6 +50,7 @@ public: private: friend class FGNasalScript; + friend class FGNasalListener; // // FGTimer subclass for handling Nasal timer callbacks. @@ -103,4 +107,23 @@ private: FGNasalSys* _nas; }; +class FGNasalListener : public SGPropertyChangeListener { +public: + FGNasalListener(naRef handler, FGNasalSys* nasal, int gcKey) + : _handler(handler), _gcKey(gcKey), _nas(nasal) {} + + void valueChanged(SGPropertyNode* node) { + _nas->_cmdArg = node; + naCall(_nas->_context, _handler, 0, 0, naNil(), naNil()); + if(naGetError(_nas->_context)) + _nas->logError(); + } + +private: + friend class FGNasalSys; + naRef _handler; + int _gcKey; + FGNasalSys* _nas; +}; + #endif // __NASALSYS_HXX -- 2.39.5