_gcHash = naNil();
_nextGCKey = 0; // Any value will do
_callCount = 0;
- _purgeListeners = false;
}
// Does a naCall() in a new context. Wrapped here to make lock
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();
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
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 > 3 && naIsNum(args[3]) ? args[3].num : 1;
- FGNasalListener *nl = new FGNasalListener(node, handler, this,
- gcSave(handler), _listenerId, type);
+ 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);
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,
+FGNasalListener::FGNasalListener(SGPropertyNode *node, naRef code,
FGNasalSys* nasal, int key, int id, int type) :
_node(node),
- _handler(handler),
+ _code(code),
_gcKey(key),
_id(id),
_nas(nasal),
_nas->gcRelease(_gcKey);
}
-void FGNasalListener::call(SGPropertyNode* cmdarg, int argc, naRef* args)
+void FGNasalListener::call(SGPropertyNode* which, naRef mode)
{
- if(_active || _dead)
- return;
+ if(_active || _dead) return;
SG_LOG(SG_NASAL, SG_DEBUG, "trigger listener #" << _id);
_active++;
- _nas->_cmdArg = cmdarg;
- _nas->call(_handler, argc, args, 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;
+ if(_type < 2 && node != _node) return; // skip child events
+ if(_type > 0 || changed(_node) || _first_call)
+ call(node, naNum(0));
- if(_type > 0 || changed(_node) || _first_call) {
- naRef arg[3];
- arg[0] = _nas->propNodeGhost(_node);
- arg[1] = _nas->propNodeGhost(node);
- arg[2] = naNil();
- call(_node, 3, arg);
- }
_first_call = false;
}
void FGNasalListener::childAdded(SGPropertyNode*, SGPropertyNode* child)
{
- naRef arg[3];
- arg[0] = _nas->propNodeGhost(_node);
- arg[1] = _nas->propNodeGhost(child);
- arg[2] = naNum(1);
- call(_node, 3, arg);
+ if(_type == 2) call(child, naNum(1));
}
void FGNasalListener::childRemoved(SGPropertyNode*, SGPropertyNode* child)
{
- naRef arg[3];
- arg[0] = _nas->propNodeGhost(_node);
- arg[1] = _nas->propNodeGhost(child);
- arg[2] = naNum(0);
- call(_node, 3, arg);
+ if(_type == 2) call(child, naNum(-1));
}
bool FGNasalListener::changed(SGPropertyNode* node)