_gcHash = naNil();
_nextGCKey = 0; // Any value will do
_callCount = 0;
- _purgeListeners = false;
}
// Does a naCall() in a new context. Wrapped here to make lock
// drop the lock in every extension function that might call back into
// Nasal, we keep a stack depth counter here and only unlock/lock
// around the naCall if it isn't the first one.
-naRef FGNasalSys::call(naRef code, naRef locals)
+naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals)
{
naContext ctx = naNewContext();
if(_callCount) naModUnlock();
_callCount++;
- naRef result = naCall(ctx, code, 0, 0, naNil(), locals);
+ naRef result = naCall(ctx, code, argc, args, naNil(), locals);
if(naGetError(ctx))
logError(ctx);
_callCount--;
strlen(sourceCode));
if(naIsNil(code))
return false;
- call(code, naNil());
+ call(code, 0, 0, naNil());
return true;
}
->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator"))
->interpolate(node, nPoints, values, deltas);
+ delete[] values;
+ delete[] deltas;
return naNil();
}
naRuntimeError(c, "airportinfo() with invalid function arguments");
return naNil();
}
- if(!apt) {
- naRuntimeError(c, "airportinfo(): no airport found");
- return naNil();
- }
+ if(!apt) return naNil();
string id = apt->getId();
string name = apt->getName();
if(rwy._id != id) break;
if(rwy._type[0] != 'r') continue;
+ naRef rwyid = naStr_fromdata(naNewString(c),
+ const_cast<char *>(rwy._rwy_no.c_str()),
+ rwy._rwy_no.length());
+
naRef rwydata = naNewHash(c);
#define HASHSET(s,l,n) naHash_set(rwydata, naStr_fromdata(naNewString(c),s,l),n)
+ HASHSET("id", 2, rwyid);
HASHSET("lat", 3, naNum(rwy._lat));
HASHSET("lon", 3, naNum(rwy._lon));
HASHSET("heading", 7, naNum(rwy._heading));
HASHSET("stopway1", 8, naNum(rwy._stopway1 * SG_FEET_TO_METER));
HASHSET("stopway2", 8, naNum(rwy._stopway2 * SG_FEET_TO_METER));
#undef HASHSET
-
- naRef no = naStr_fromdata(naNewString(c),
- const_cast<char *>(rwy._rwy_no.c_str()),
- rwy._rwy_no.length());
- naHash_set(rwys, no, rwydata);
+ naHash_set(rwys, rwyid, rwydata);
} while(rwylst->next(&rwy));
}
// Table of extension functions. Terminate with zeros.
-static struct { char* name; naCFunction func; } funcs[] = {
+static struct { const char* name; naCFunction func; } funcs[] = {
{ "getprop", f_getprop },
{ "setprop", f_setprop },
{ "print", f_print },
void FGNasalSys::update(double)
{
- if(_purgeListeners) {
- _purgeListeners = false;
- map<int, FGNasalListener *>::iterator it;
- for(it = _listener.begin(); it != _listener.end();) {
- FGNasalListener *nl = it->second;
- if(nl->_dead) {
- _listener.erase(it++);
- delete nl;
- } else {
- ++it;
- }
- }
+ if(!_dead_listener.empty()) {
+ vector<FGNasalListener *>::iterator it, end = _dead_listener.end();
+ for(it = _dead_listener.begin(); it != end; ++it) delete *it;
+ _dead_listener.clear();
}
+
+ // The global context is a legacy thing. We use dynamically
+ // created contexts for naCall() now, so that we can call them
+ // recursively. But there are still spots that want to use it for
+ // naNew*() calls, which end up leaking memory because the context
+ // only clears out its temporary vector when it's *used*. So just
+ // junk it and fetch a new/reinitialized one every frame. This is
+ // clumsy: the right solution would use the dynamic context in all
+ // cases and eliminate _context entirely. But that's more work,
+ // and this works fine (yes, they say "New" and "Free", but
+ // they're very fast, just trust me). -Andy
+ naFreeContext(_context);
+ _context = naNewContext();
}
// Loads the scripts found under /nasal in the global tree
_cmdArg = (SGPropertyNode*)arg;
- call(code, locals);
+ call(code, 0, 0, locals);
hashset(_globals, moduleName, locals);
}
// code doesn't need it.
_cmdArg = (SGPropertyNode*)arg;
- call(code, locals);
+ call(code, 0, 0, locals);
return true;
}
void FGNasalSys::handleTimer(NasalTimer* t)
{
- call(t->handler, naNil());
+ call(t->handler, 0, 0, naNil());
gcRelease(t->gcKey);
}
int FGNasalSys::_listenerId = 0;
-// setlistener(property, func, bool) extension function. The first argument
-// is either a ghost (SGPropertyNode_ptr*) or a string (global property
-// path), the second is a Nasal function, the optional third one an integer.
-// If it is 1, then the listener is executed initially. If it's 2, then
-// the listener is only triggered when the node value actually changed. The
-// setlistener() function returns a unique id number, that can be used
-// as argument to the removelistener() function.
+// setlistener(<property>, <func> [, <initial=0> [, <persistent=1>]])
+// Attaches a callback function to a property (specified as a global
+// property path string or a SGPropertyNode_ptr* ghost). If the third,
+// optional argument (default=0) is set to 1, then the function is also
+// called initially. If the fourth, optional argument is set to 0, then the
+// function is only called when the property node value actually changes.
+// Otherwise it's called independent of the value whenever the node is
+// written to (default). The setlistener() function returns a unique
+// id number, which is to be used as argument to the removelistener()
+// function.
naRef FGNasalSys::setListener(naContext c, int argc, naRef* args)
{
SGPropertyNode_ptr node;
SG_LOG(SG_NASAL, SG_DEBUG, "Attaching listener to tied property " <<
node->getPath());
- naRef handler = argc > 1 ? args[1] : naNil();
- if(!(naIsCode(handler) || naIsCCode(handler) || naIsFunc(handler))) {
+ naRef code = argc > 1 ? args[1] : naNil();
+ if(!(naIsCode(code) || naIsCCode(code) || naIsFunc(code))) {
naRuntimeError(c, "setlistener() with invalid function argument");
return naNil();
}
- int type = argc > 2 && naIsNum(args[2]) ? args[2].num : 0;
- FGNasalListener *nl = new FGNasalListener(node, handler, this,
- gcSave(handler), _listenerId, type);
- node->addChangeListener(nl, type != 0);
+ int type = argc > 3 && naIsNum(args[3]) ? (int)args[3].num : 1;
+ FGNasalListener *nl = new FGNasalListener(node, code, this,
+ gcSave(code), _listenerId, type);
+
+ bool initial = argc > 2 && naTrue(args[2]);
+ node->addChangeListener(nl, initial);
_listener[_listenerId] = nl;
return naNum(_listenerId++);
return naNil();
}
- FGNasalListener *nl = it->second;
- if(nl->_active) {
- nl->_dead = true;
- _purgeListeners = true;
- return naNum(-1);
- }
-
+ it->second->_dead = true;
+ _dead_listener.push_back(it->second);
_listener.erase(it);
- delete nl;
return naNum(_listener.size());
}
// FGNasalListener class.
-FGNasalListener::FGNasalListener(SGPropertyNode_ptr node, naRef handler,
- FGNasalSys* nasal, int key, int id,
- int type) :
+FGNasalListener::FGNasalListener(SGPropertyNode *node, naRef code,
+ FGNasalSys* nasal, int key, int id, int type) :
_node(node),
- _handler(handler),
+ _code(code),
_gcKey(key),
_id(id),
- _type(type),
_nas(nasal),
+ _type(type),
_active(0),
_dead(false),
_first_call(true),
_last_int(0L),
_last_float(0.0)
{
- unchanged(node);
}
FGNasalListener::~FGNasalListener()
_nas->gcRelease(_gcKey);
}
-void FGNasalListener::valueChanged(SGPropertyNode* node)
+void FGNasalListener::call(SGPropertyNode* which, naRef mode)
{
- // drop recursive listener calls
- if(_active || _dead)
- return;
- if(_type == 2 && !_first_call && unchanged(node))
- return;
-
+ if(_active || _dead) return;
SG_LOG(SG_NASAL, SG_DEBUG, "trigger listener #" << _id);
_active++;
- _nas->_cmdArg = node;
- _nas->call(_handler, naNil());
+ naRef arg[4];
+ arg[0] = _nas->propNodeGhost(which);
+ arg[1] = _nas->propNodeGhost(_node);
+ arg[2] = mode; // value changed, child added/removed
+ arg[3] = naNum(_node != which); // child event?
+ _nas->_cmdArg = _node;
+ _nas->call(_code, 4, arg, naNil());
_active--;
+}
+
+void FGNasalListener::valueChanged(SGPropertyNode* node)
+{
+ if(_type < 2 && node != _node) return; // skip child events
+ if(_type > 0 || changed(_node) || _first_call)
+ call(node, naNum(0));
+
_first_call = false;
}
-bool FGNasalListener::unchanged(SGPropertyNode* node)
+void FGNasalListener::childAdded(SGPropertyNode*, SGPropertyNode* child)
+{
+ if(_type == 2) call(child, naNum(1));
+}
+
+void FGNasalListener::childRemoved(SGPropertyNode*, SGPropertyNode* child)
+{
+ if(_type == 2) call(child, naNum(-1));
+}
+
+bool FGNasalListener::changed(SGPropertyNode* node)
{
SGPropertyNode::Type type = node->getType();
- if(type == SGPropertyNode::NONE) return true;
- if(type == SGPropertyNode::UNSPECIFIED) return false;
+ if(type == SGPropertyNode::NONE) return false;
+ if(type == SGPropertyNode::UNSPECIFIED) return true;
bool result;
switch(type) {
case SGPropertyNode::LONG:
{
long l = node->getLongValue();
- result = l == _last_int;
+ result = l != _last_int;
_last_int = l;
return result;
}
case SGPropertyNode::DOUBLE:
{
double d = node->getDoubleValue();
- result = d == _last_float;
+ result = d != _last_float;
_last_float = d;
return result;
}
default:
{
string s = node->getStringValue();
- result = s == _last_string;
+ result = s != _last_string;
_last_string = s;
return result;
}