On-demand loading of Nasal modules.
Load a module whenever its /nasal/foo/enabled flag is set, even if it was disabled at start-up. Also expose a property if it was successfully loaded.
This commit is contained in:
parent
731e803223
commit
93180eb004
2 changed files with 99 additions and 53 deletions
|
@ -38,6 +38,31 @@
|
||||||
|
|
||||||
static FGNasalSys* nasalSys = 0;
|
static FGNasalSys* nasalSys = 0;
|
||||||
|
|
||||||
|
// Listener class for loading Nasal modules on demand
|
||||||
|
class FGNasalModuleListener : public SGPropertyChangeListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FGNasalModuleListener(SGPropertyNode* node);
|
||||||
|
|
||||||
|
virtual void valueChanged(SGPropertyNode* node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SGPropertyNode_ptr _node;
|
||||||
|
};
|
||||||
|
|
||||||
|
FGNasalModuleListener::FGNasalModuleListener(SGPropertyNode* node) : _node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGNasalModuleListener::valueChanged(SGPropertyNode*)
|
||||||
|
{
|
||||||
|
if (_node->getBoolValue("enabled",false)&&
|
||||||
|
!_node->getBoolValue("loaded",true))
|
||||||
|
{
|
||||||
|
nasalSys->loadPropertyScripts(_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Read and return file contents in a single buffer. Note use of
|
// Read and return file contents in a single buffer. Note use of
|
||||||
// stat() to get the file size. This is a win32 function, believe it
|
// stat() to get the file size. This is a win32 function, believe it
|
||||||
|
@ -817,56 +842,73 @@ void FGNasalSys::loadPropertyScripts()
|
||||||
SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
|
SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
|
||||||
if(!nasal) return;
|
if(!nasal) return;
|
||||||
|
|
||||||
for(int i=0; i<nasal->nChildren(); i++) {
|
for(int i=0; i<nasal->nChildren(); i++)
|
||||||
|
{
|
||||||
SGPropertyNode* n = nasal->getChild(i);
|
SGPropertyNode* n = nasal->getChild(i);
|
||||||
bool is_loaded = false;
|
loadPropertyScripts(n);
|
||||||
|
|
||||||
const char* module = n->getName();
|
|
||||||
if(n->hasChild("module"))
|
|
||||||
module = n->getStringValue("module");
|
|
||||||
if (n->getBoolValue("enabled",true))
|
|
||||||
{
|
|
||||||
// allow multiple files to be specified within a single
|
|
||||||
// Nasal module tag
|
|
||||||
int j = 0;
|
|
||||||
SGPropertyNode *fn;
|
|
||||||
bool file_specified = false;
|
|
||||||
while((fn = n->getChild("file", j)) != NULL) {
|
|
||||||
file_specified = true;
|
|
||||||
const char* file = fn->getStringValue();
|
|
||||||
SGPath p(file);
|
|
||||||
if (!p.isAbsolute() || !p.exists())
|
|
||||||
{
|
|
||||||
p = globals->resolve_maybe_aircraft_path(file);
|
|
||||||
}
|
|
||||||
loadModule(p, module);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* src = n->getStringValue("script");
|
|
||||||
if(!n->hasChild("script")) src = 0; // Hrm...
|
|
||||||
if(src)
|
|
||||||
createModule(module, n->getPath().c_str(), src, strlen(src));
|
|
||||||
|
|
||||||
if(!file_specified && !src)
|
|
||||||
{
|
|
||||||
// module no longer exists - clear the archived "enable" flag
|
|
||||||
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
|
||||||
SGPropertyNode* node = n->getChild("enabled",0,false);
|
|
||||||
if (node)
|
|
||||||
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
|
||||||
|
|
||||||
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
|
|
||||||
"no <file> or <script> defined in " <<
|
|
||||||
"/nasal/" << module);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
is_loaded = true;
|
|
||||||
}
|
|
||||||
n->setBoolValue("loaded",is_loaded);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads the scripts found under /nasal in the global tree
|
||||||
|
void FGNasalSys::loadPropertyScripts(SGPropertyNode* n)
|
||||||
|
{
|
||||||
|
bool is_loaded = false;
|
||||||
|
|
||||||
|
const char* module = n->getName();
|
||||||
|
if(n->hasChild("module"))
|
||||||
|
module = n->getStringValue("module");
|
||||||
|
if (n->getBoolValue("enabled",true))
|
||||||
|
{
|
||||||
|
// allow multiple files to be specified within a single
|
||||||
|
// Nasal module tag
|
||||||
|
int j = 0;
|
||||||
|
SGPropertyNode *fn;
|
||||||
|
bool file_specified = false;
|
||||||
|
bool ok=true;
|
||||||
|
while((fn = n->getChild("file", j)) != NULL) {
|
||||||
|
file_specified = true;
|
||||||
|
const char* file = fn->getStringValue();
|
||||||
|
SGPath p(file);
|
||||||
|
if (!p.isAbsolute() || !p.exists())
|
||||||
|
{
|
||||||
|
p = globals->resolve_maybe_aircraft_path(file);
|
||||||
|
}
|
||||||
|
ok &= loadModule(p, module);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* src = n->getStringValue("script");
|
||||||
|
if(!n->hasChild("script")) src = 0; // Hrm...
|
||||||
|
if(src)
|
||||||
|
createModule(module, n->getPath().c_str(), src, strlen(src));
|
||||||
|
|
||||||
|
if(!file_specified && !src)
|
||||||
|
{
|
||||||
|
// module no longer exists - clear the archived "enable" flag
|
||||||
|
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||||
|
SGPropertyNode* node = n->getChild("enabled",0,false);
|
||||||
|
if (node)
|
||||||
|
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||||
|
|
||||||
|
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
|
||||||
|
"no <file> or <script> defined in " <<
|
||||||
|
"/nasal/" << module);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
is_loaded = ok;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SGPropertyNode* enable = n->getChild("enabled");
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
FGNasalModuleListener* listener = new FGNasalModuleListener(n);
|
||||||
|
enable->addChangeListener(listener, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n->setBoolValue("loaded",is_loaded);
|
||||||
|
}
|
||||||
|
|
||||||
// Logs a runtime error, with stack trace, to the FlightGear log stream
|
// Logs a runtime error, with stack trace, to the FlightGear log stream
|
||||||
void FGNasalSys::logError(naContext context)
|
void FGNasalSys::logError(naContext context)
|
||||||
{
|
{
|
||||||
|
@ -884,7 +926,7 @@ void FGNasalSys::logError(naContext context)
|
||||||
// Reads a script file, executes it, and places the resulting
|
// Reads a script file, executes it, and places the resulting
|
||||||
// namespace into the global namespace under the specified module
|
// namespace into the global namespace under the specified module
|
||||||
// name.
|
// name.
|
||||||
void FGNasalSys::loadModule(SGPath file, const char* module)
|
bool FGNasalSys::loadModule(SGPath file, const char* module)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
char* buf = readfile(file.c_str(), &len);
|
char* buf = readfile(file.c_str(), &len);
|
||||||
|
@ -892,25 +934,26 @@ void FGNasalSys::loadModule(SGPath file, const char* module)
|
||||||
SG_LOG(SG_NASAL, SG_ALERT,
|
SG_LOG(SG_NASAL, SG_ALERT,
|
||||||
"Nasal error: could not read script file " << file.c_str()
|
"Nasal error: could not read script file " << file.c_str()
|
||||||
<< " into module " << module);
|
<< " into module " << module);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
createModule(module, file.c_str(), buf, len);
|
bool ok = createModule(module, file.c_str(), buf, len);
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and run. Save the local variables namespace, as it will
|
// Parse and run. Save the local variables namespace, as it will
|
||||||
// become a sub-object of globals. The optional "arg" argument can be
|
// become a sub-object of globals. The optional "arg" argument can be
|
||||||
// used to pass an associated property node to the module, which can then
|
// used to pass an associated property node to the module, which can then
|
||||||
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
|
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
|
||||||
void FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
||||||
const char* src, int len,
|
const char* src, int len,
|
||||||
const SGPropertyNode* cmdarg,
|
const SGPropertyNode* cmdarg,
|
||||||
int argc, naRef* args)
|
int argc, naRef* args)
|
||||||
{
|
{
|
||||||
naRef code = parse(fileName, src, len);
|
naRef code = parse(fileName, src, len);
|
||||||
if(naIsNil(code))
|
if(naIsNil(code))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// See if we already have a module hash to use. This allows the
|
// See if we already have a module hash to use. This allows the
|
||||||
// user to, for example, add functions to the built-in math
|
// user to, for example, add functions to the built-in math
|
||||||
|
@ -925,6 +968,7 @@ void FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
||||||
|
|
||||||
call(code, argc, args, locals);
|
call(code, argc, args, locals);
|
||||||
hashset(_globals, moduleName, locals);
|
hashset(_globals, moduleName, locals);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGNasalSys::deleteModule(const char* moduleName)
|
void FGNasalSys::deleteModule(const char* moduleName)
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
|
|
||||||
// Loads a nasal script from an external file and inserts it as a
|
// Loads a nasal script from an external file and inserts it as a
|
||||||
// global module of the specified name.
|
// global module of the specified name.
|
||||||
void loadModule(SGPath file, const char* moduleName);
|
bool loadModule(SGPath file, const char* moduleName);
|
||||||
|
|
||||||
// Simple hook to run arbitrary source code. Returns a bool to
|
// Simple hook to run arbitrary source code. Returns a bool to
|
||||||
// indicate successful execution. Does *not* return any Nasal
|
// indicate successful execution. Does *not* return any Nasal
|
||||||
|
@ -54,7 +54,7 @@ public:
|
||||||
// Callbacks for command and timer bindings
|
// Callbacks for command and timer bindings
|
||||||
virtual bool handleCommand(const SGPropertyNode* arg);
|
virtual bool handleCommand(const SGPropertyNode* arg);
|
||||||
|
|
||||||
void createModule(const char* moduleName, const char* fileName,
|
bool createModule(const char* moduleName, const char* fileName,
|
||||||
const char* src, int len, const SGPropertyNode* cmdarg=0,
|
const char* src, int len, const SGPropertyNode* cmdarg=0,
|
||||||
int argc=0, naRef*args=0);
|
int argc=0, naRef*args=0);
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class FGNasalScript;
|
friend class FGNasalScript;
|
||||||
friend class FGNasalListener;
|
friend class FGNasalListener;
|
||||||
|
friend class FGNasalModuleListener;
|
||||||
|
|
||||||
//
|
//
|
||||||
// FGTimer subclass for handling Nasal timer callbacks.
|
// FGTimer subclass for handling Nasal timer callbacks.
|
||||||
|
@ -86,6 +87,7 @@ private:
|
||||||
static int _listenerId;
|
static int _listenerId;
|
||||||
|
|
||||||
void loadPropertyScripts();
|
void loadPropertyScripts();
|
||||||
|
void loadPropertyScripts(SGPropertyNode* n);
|
||||||
void loadScriptDirectory(simgear::Dir nasalDir);
|
void loadScriptDirectory(simgear::Dir nasalDir);
|
||||||
void addModule(string moduleName, simgear::PathList scripts);
|
void addModule(string moduleName, simgear::PathList scripts);
|
||||||
void hashset(naRef hash, const char* key, naRef val);
|
void hashset(naRef hash, const char* key, naRef val);
|
||||||
|
|
Loading…
Reference in a new issue