first commit
This commit is contained in:
55
src/Scripting/CMakeLists.txt
Normal file
55
src/Scripting/CMakeLists.txt
Normal file
@@ -0,0 +1,55 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
nasal-props.cxx
|
||||
NasalAddons.cxx
|
||||
NasalAircraft.cxx
|
||||
NasalPositioned.cxx
|
||||
NasalPositioned_cppbind.cxx
|
||||
NasalCanvas.cxx
|
||||
NasalClipboard.cxx
|
||||
NasalCondition.cxx
|
||||
NasalHTTP.cxx
|
||||
NasalString.cxx
|
||||
NasalModelData.cxx
|
||||
NasalSGPath.cxx
|
||||
NasalFlightPlan.cxx
|
||||
# we don't add this here becuase we need to exclude it the testSuite
|
||||
# so it can't go nto fgfsObjects library
|
||||
# NasalUnitTesting.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
NasalSys.hxx
|
||||
NasalSys_private.hxx
|
||||
NasalAddons.hxx
|
||||
NasalAircraft.hxx
|
||||
NasalPositioned.hxx
|
||||
NasalCanvas.hxx
|
||||
NasalClipboard.hxx
|
||||
NasalCondition.hxx
|
||||
NasalHTTP.hxx
|
||||
NasalString.hxx
|
||||
NasalModelData.hxx
|
||||
NasalSGPath.hxx
|
||||
NasalFlightPlan.hxx
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES ClipboardWindows.cxx)
|
||||
elseif(APPLE)
|
||||
list(APPEND SOURCES ClipboardCocoa.mm)
|
||||
else()
|
||||
find_package(X11)
|
||||
if(X11_FOUND)
|
||||
list(APPEND SOURCES ClipboardX11.cxx)
|
||||
else()
|
||||
list(APPEND SOURCES ClipboardFallback.cxx)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# NasalSys.cxx is passed as TEST_SOURCES since we need to compile it
|
||||
# seperately whrn building the test_suite
|
||||
|
||||
flightgear_component(Scripting "${SOURCES}" "${HEADERS}" NasalSys.cxx)
|
||||
|
||||
93
src/Scripting/ClipboardCocoa.mm
Normal file
93
src/Scripting/ClipboardCocoa.mm
Normal file
@@ -0,0 +1,93 @@
|
||||
// Cocoa implementation of clipboard access for Nasal
|
||||
//
|
||||
// Copyright (C) 2012 James Turner <zakalawe@mac.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
|
||||
#include "NasalClipboard.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <AppKit/NSPasteboard.h>
|
||||
#include <Foundation/NSArray.h>
|
||||
|
||||
#include <GUI/CocoaHelpers_private.h>
|
||||
|
||||
/**
|
||||
*/
|
||||
class ClipboardCocoa: public NasalClipboard
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Get clipboard contents as text
|
||||
*/
|
||||
virtual std::string getText(Type type)
|
||||
{
|
||||
CocoaAutoreleasePool pool;
|
||||
|
||||
if( type == CLIPBOARD )
|
||||
{
|
||||
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
||||
NSString* nstext = [pboard stringForType:NSStringPboardType];
|
||||
#else // > 10.5
|
||||
NSString* nstext = [pboard stringForType:NSPasteboardTypeString];
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
return stdStringFromCocoa(nstext);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clipboard contents as text
|
||||
*/
|
||||
virtual bool setText(const std::string& text, Type type)
|
||||
{
|
||||
CocoaAutoreleasePool pool;
|
||||
|
||||
if( type == CLIPBOARD )
|
||||
{
|
||||
NSPasteboard* pboard = [NSPasteboard generalPasteboard];
|
||||
NSString* nstext = stdStringToCocoa(text);
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
|
||||
NSString* type = NSStringPboardType;
|
||||
NSArray* types = [NSArray arrayWithObjects: type, nil];
|
||||
[pboard declareTypes:types owner:nil];
|
||||
[pboard setString:nstext forType: NSStringPboardType];
|
||||
#else // > 10.5
|
||||
NSString* type = NSPasteboardTypeString;
|
||||
[pboard clearContents];
|
||||
[pboard setString:nstext forType:NSPasteboardTypeString];
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::string _selection;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::create()
|
||||
{
|
||||
return NasalClipboard::Ptr(new ClipboardCocoa);
|
||||
}
|
||||
64
src/Scripting/ClipboardFallback.cxx
Normal file
64
src/Scripting/ClipboardFallback.cxx
Normal file
@@ -0,0 +1,64 @@
|
||||
// Fallback implementation of clipboard access for Nasal. Copy and edit for
|
||||
// implementing support of other platforms
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "NasalClipboard.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
/**
|
||||
* Provide a basic clipboard whose contents are only available to FlightGear
|
||||
* itself
|
||||
*/
|
||||
class ClipboardFallback:
|
||||
public NasalClipboard
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Get clipboard contents as text
|
||||
*/
|
||||
virtual std::string getText(Type type)
|
||||
{
|
||||
return type == CLIPBOARD ? _clipboard : _selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clipboard contents as text
|
||||
*/
|
||||
virtual bool setText(const std::string& text, Type type)
|
||||
{
|
||||
if( type == CLIPBOARD )
|
||||
_clipboard = text;
|
||||
else
|
||||
_selection = text;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::string _clipboard,
|
||||
_selection;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::create()
|
||||
{
|
||||
return NasalClipboard::Ptr(new ClipboardFallback);
|
||||
}
|
||||
108
src/Scripting/ClipboardWindows.cxx
Normal file
108
src/Scripting/ClipboardWindows.cxx
Normal file
@@ -0,0 +1,108 @@
|
||||
// Windows implementation of clipboard access for Nasal
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "NasalClipboard.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <windows.h>
|
||||
|
||||
/**
|
||||
* Windows does only support on clipboard and no selection. We fake also the X11
|
||||
* selection buffer - at least inside FlightGear
|
||||
*/
|
||||
class ClipboardWindows:
|
||||
public NasalClipboard
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Get clipboard contents as text
|
||||
*/
|
||||
virtual std::string getText(Type type)
|
||||
{
|
||||
if( type == CLIPBOARD )
|
||||
{
|
||||
std::string data;
|
||||
|
||||
if( !OpenClipboard(NULL) )
|
||||
return data;
|
||||
|
||||
HANDLE hData = GetClipboardData( CF_TEXT );
|
||||
char* buff = (char*)GlobalLock( hData );
|
||||
if (buff)
|
||||
data = buff;
|
||||
GlobalUnlock( hData );
|
||||
CloseClipboard();
|
||||
|
||||
return data;
|
||||
}
|
||||
else
|
||||
return _selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clipboard contents as text
|
||||
*/
|
||||
virtual bool setText(const std::string& text, Type type)
|
||||
{
|
||||
if( type == CLIPBOARD )
|
||||
{
|
||||
if( !OpenClipboard(NULL) )
|
||||
return false;
|
||||
|
||||
bool ret = true;
|
||||
if( !EmptyClipboard() )
|
||||
ret = false;
|
||||
else if( !text.empty() )
|
||||
{
|
||||
HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
|
||||
if( !hGlob )
|
||||
ret = false;
|
||||
else
|
||||
{
|
||||
memcpy(GlobalLock(hGlob), (char*)&text[0], text.size() + 1);
|
||||
GlobalUnlock(hGlob);
|
||||
|
||||
if( !SetClipboardData(CF_TEXT, hGlob) )
|
||||
{
|
||||
GlobalFree(hGlob);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selection = text;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::string _selection;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::create()
|
||||
{
|
||||
return NasalClipboard::Ptr(new ClipboardWindows);
|
||||
}
|
||||
367
src/Scripting/ClipboardX11.cxx
Normal file
367
src/Scripting/ClipboardX11.cxx
Normal file
@@ -0,0 +1,367 @@
|
||||
// X11 implementation of clipboard access for Nasal
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
/*
|
||||
* See the following links for more information on X11 clipboard:
|
||||
*
|
||||
* http://www.jwz.org/doc/x-cut-and-paste.html
|
||||
* http://michael.toren.net/mirrors/doc/X-copy+paste.txt
|
||||
* https://github.com/kfish/xsel/blob/master/xsel.c
|
||||
*/
|
||||
|
||||
#include "NasalClipboard.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
class ClipboardX11:
|
||||
public NasalClipboard
|
||||
{
|
||||
public:
|
||||
ClipboardX11():
|
||||
// REVIEW: Memory Leak - 55,397 (104 direct, 55,293 indirect) bytes in 1 blocks are definitely lost
|
||||
_display( XOpenDisplay(NULL) ),
|
||||
_window( XCreateSimpleWindow(
|
||||
_display,
|
||||
DefaultRootWindow(_display),
|
||||
0, 0, 1, 1, // dummy dimensions -> window will never be mapped
|
||||
0,
|
||||
0, 0
|
||||
) ),
|
||||
_atom_targets( XInternAtom(_display, "TARGETS", False) ),
|
||||
_atom_text( XInternAtom(_display, "TEXT", False) ),
|
||||
_atom_utf8( XInternAtom(_display, "UTF8_STRING", False) ),
|
||||
_atom_primary( XInternAtom(_display, "PRIMARY", False) ),
|
||||
_atom_clipboard( XInternAtom(_display, "CLIPBOARD", False) )
|
||||
{
|
||||
assert(_display);
|
||||
}
|
||||
|
||||
virtual ~ClipboardX11()
|
||||
{
|
||||
// Ensure we get rid of any selection ownership
|
||||
if( XGetSelectionOwner(_display, _atom_primary) )
|
||||
XSetSelectionOwner(_display, _atom_primary, None, CurrentTime);
|
||||
if( XGetSelectionOwner(_display, _atom_clipboard) )
|
||||
XSetSelectionOwner(_display, _atom_clipboard, None, CurrentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to run an event queue to check for selection request
|
||||
*/
|
||||
virtual void update()
|
||||
{
|
||||
while( XPending(_display) )
|
||||
{
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clipboard contents as text
|
||||
*/
|
||||
virtual std::string getText(Type type)
|
||||
{
|
||||
Atom atom_type = typeToAtom(type);
|
||||
|
||||
//Request a list of possible conversions
|
||||
XConvertSelection( _display, atom_type, _atom_targets, atom_type,
|
||||
_window, CurrentTime );
|
||||
|
||||
Atom requested_type = None;
|
||||
bool sent_request = false;
|
||||
|
||||
for(int cnt = 0; cnt < 5;)
|
||||
{
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
|
||||
if( event.type != SelectionNotify )
|
||||
{
|
||||
handleEvent(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
Atom target = event.xselection.target;
|
||||
if(event.xselection.property == None)
|
||||
{
|
||||
if( target == _atom_targets )
|
||||
// If TARGETS can not be converted no selection is available
|
||||
break;
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_WARN,
|
||||
"ClipboardX11::getText: Conversion failed: "
|
||||
"target=" << getAtomName(target)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're being given a list of targets (possible conversions)
|
||||
if(target == _atom_targets && !sent_request)
|
||||
{
|
||||
sent_request = true;
|
||||
requested_type = XA_STRING; // TODO select other type
|
||||
XConvertSelection( _display, atom_type, requested_type, atom_type,
|
||||
_window, CurrentTime );
|
||||
}
|
||||
else if(target == requested_type)
|
||||
{
|
||||
Property prop = readProperty(_window, atom_type);
|
||||
if( prop.format != 8 )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_WARN,
|
||||
"ClipboardX11::getText: can only handle 8-bit data (is "
|
||||
<< prop.format << "-bit) -> retry "
|
||||
<< cnt++
|
||||
);
|
||||
XFree(prop.data);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string result((const char*)prop.data, prop.num_items);
|
||||
XFree(prop.data);
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_WARN,
|
||||
"ClipboardX11::getText: wrong target: " << getAtomName(target)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clipboard contents as text
|
||||
*/
|
||||
virtual bool setText(const std::string& text, Type type)
|
||||
{
|
||||
Atom atom_type = typeToAtom(type);
|
||||
XSetSelectionOwner(_display, atom_type, _window, CurrentTime);
|
||||
if( XGetSelectionOwner(_display, atom_type) != _window )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_ALERT,
|
||||
"ClipboardX11::setText: failed to get selection owner!"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to store the text for sending it to another application upon
|
||||
// request
|
||||
if( type == CLIPBOARD )
|
||||
_clipboard = text;
|
||||
else
|
||||
_selection = text;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
Display *_display;
|
||||
Window _window;
|
||||
Atom _atom_targets,
|
||||
_atom_text,
|
||||
_atom_utf8,
|
||||
_atom_primary,
|
||||
_atom_clipboard;
|
||||
|
||||
std::string _clipboard,
|
||||
_selection;
|
||||
|
||||
void handleEvent(const XEvent& event)
|
||||
{
|
||||
switch( event.type )
|
||||
{
|
||||
case SelectionRequest:
|
||||
handleSelectionRequest(event.xselectionrequest);
|
||||
break;
|
||||
case SelectionClear:
|
||||
if( event.xselectionclear.selection == _atom_clipboard )
|
||||
_clipboard.clear();
|
||||
else
|
||||
_selection.clear();
|
||||
break;
|
||||
default:
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_WARN,
|
||||
"ClipboardX11: unexpected XEvent: " << event.type
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSelectionRequest(const XSelectionRequestEvent& sel_req)
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_DEBUG,
|
||||
"ClipboardX11: handle selection request: "
|
||||
"selection=" << getAtomName(sel_req.selection) << ", "
|
||||
"target=" << getAtomName(sel_req.target)
|
||||
);
|
||||
|
||||
const std::string& buf = sel_req.selection == _atom_clipboard
|
||||
? _clipboard
|
||||
: _selection;
|
||||
|
||||
// Prepare response to notify whether we have written to the property or
|
||||
// are unable to do the conversion
|
||||
XSelectionEvent response;
|
||||
response.type = SelectionNotify;
|
||||
response.display = sel_req.display;
|
||||
response.requestor = sel_req.requestor;
|
||||
response.selection = sel_req.selection;
|
||||
response.target = sel_req.target;
|
||||
response.property = sel_req.property;
|
||||
response.time = sel_req.time;
|
||||
|
||||
if( sel_req.target == _atom_targets )
|
||||
{
|
||||
static Atom supported[] = {
|
||||
XA_STRING,
|
||||
_atom_text,
|
||||
_atom_utf8
|
||||
};
|
||||
|
||||
changeProperty
|
||||
(
|
||||
sel_req.requestor,
|
||||
sel_req.property,
|
||||
sel_req.target,
|
||||
&supported,
|
||||
sizeof(supported)
|
||||
);
|
||||
}
|
||||
else if( sel_req.target == XA_STRING
|
||||
|| sel_req.target == _atom_text
|
||||
|| sel_req.target == _atom_utf8 )
|
||||
{
|
||||
changeProperty
|
||||
(
|
||||
sel_req.requestor,
|
||||
sel_req.property,
|
||||
sel_req.target,
|
||||
buf.data(),
|
||||
buf.size()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Notify requestor that we are unable to perform requested conversion
|
||||
response.property = None;
|
||||
}
|
||||
|
||||
XSendEvent(_display, sel_req.requestor, False, 0, (XEvent*)&response);
|
||||
}
|
||||
|
||||
void changeProperty( Window w,
|
||||
Atom prop,
|
||||
Atom type,
|
||||
const void *data,
|
||||
size_t size )
|
||||
{
|
||||
XChangeProperty
|
||||
(
|
||||
_display, w, prop, type, 8, PropModeReplace,
|
||||
static_cast<const unsigned char*>(data), size
|
||||
);
|
||||
}
|
||||
|
||||
struct Property
|
||||
{
|
||||
unsigned char *data;
|
||||
int format;
|
||||
unsigned long num_items;
|
||||
Atom type;
|
||||
};
|
||||
|
||||
// Get all data from a property
|
||||
Property readProperty(Window w, Atom property)
|
||||
{
|
||||
Atom actual_type;
|
||||
int actual_format;
|
||||
unsigned long nitems;
|
||||
unsigned long bytes_after;
|
||||
unsigned char *ret = 0;
|
||||
|
||||
int read_bytes = 1024;
|
||||
|
||||
//Keep trying to read the property until there are no
|
||||
//bytes unread.
|
||||
do
|
||||
{
|
||||
if( ret )
|
||||
XFree(ret);
|
||||
|
||||
XGetWindowProperty
|
||||
(
|
||||
_display, w, property, 0, read_bytes, False, AnyPropertyType,
|
||||
&actual_type, &actual_format, &nitems, &bytes_after,
|
||||
&ret
|
||||
);
|
||||
|
||||
read_bytes *= 2;
|
||||
} while( bytes_after );
|
||||
|
||||
Property p = {ret, actual_format, nitems, actual_type};
|
||||
return p;
|
||||
}
|
||||
|
||||
std::string getAtomName(Atom atom) const
|
||||
{
|
||||
return atom == None ? "None" : XGetAtomName(_display, atom);
|
||||
}
|
||||
|
||||
Atom typeToAtom(Type type) const
|
||||
{
|
||||
return type == CLIPBOARD ? _atom_clipboard : _atom_primary;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::create()
|
||||
{
|
||||
return NasalClipboard::Ptr(new ClipboardX11);
|
||||
}
|
||||
212
src/Scripting/NasalAddons.cxx
Normal file
212
src/Scripting/NasalAddons.cxx
Normal file
@@ -0,0 +1,212 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// NasalAddons.cxx --- Expose add-on classes to Nasal
|
||||
// Copyright (C) 2017, 2018 Florent Rougon
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalCallContext.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <Add-ons/addon_fwd.hxx>
|
||||
#include <Add-ons/Addon.hxx>
|
||||
#include <Add-ons/AddonManager.hxx>
|
||||
#include <Add-ons/AddonVersion.hxx>
|
||||
#include <Add-ons/contacts.hxx>
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
namespace addons
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * AddonManager *
|
||||
// ***************************************************************************
|
||||
|
||||
static const std::unique_ptr<AddonManager>& getAddonMgrWithCheck()
|
||||
{
|
||||
const auto& addonMgr = AddonManager::instance();
|
||||
|
||||
if (!addonMgr) {
|
||||
throw sg_exception("trying to access the AddonManager from Nasal, "
|
||||
"however it is not initialized");
|
||||
}
|
||||
|
||||
return addonMgr;
|
||||
}
|
||||
|
||||
static naRef f_registeredAddons(const nasal::CallContext& ctx)
|
||||
{
|
||||
const auto& addonMgr = getAddonMgrWithCheck();
|
||||
return ctx.to_nasal(addonMgr->registeredAddons());
|
||||
}
|
||||
|
||||
static naRef f_isAddonRegistered(const nasal::CallContext& ctx)
|
||||
{
|
||||
const auto& addonMgr = getAddonMgrWithCheck();
|
||||
std::string addonId = ctx.requireArg<std::string>(0);
|
||||
return ctx.to_nasal(addonMgr->isAddonRegistered(addonId));
|
||||
}
|
||||
|
||||
static naRef f_loadedAddons(const nasal::CallContext& ctx)
|
||||
{
|
||||
const auto& addonMgr = getAddonMgrWithCheck();
|
||||
return ctx.to_nasal(addonMgr->loadedAddons());
|
||||
}
|
||||
|
||||
static naRef f_isAddonLoaded(const nasal::CallContext& ctx)
|
||||
{
|
||||
const auto& addonMgr = getAddonMgrWithCheck();
|
||||
std::string addonId = ctx.requireArg<std::string>(0);
|
||||
return ctx.to_nasal(addonMgr->isAddonLoaded(addonId));
|
||||
}
|
||||
|
||||
static naRef f_getAddon(const nasal::CallContext& ctx)
|
||||
{
|
||||
const auto& addonMgr = getAddonMgrWithCheck();
|
||||
return ctx.to_nasal(addonMgr->getAddon(ctx.requireArg<std::string>(0)));
|
||||
}
|
||||
|
||||
static void wrapAddonManagerMethods(nasal::Hash& addonsModule)
|
||||
{
|
||||
addonsModule.set("registeredAddons", &f_registeredAddons);
|
||||
addonsModule.set("isAddonRegistered", &f_isAddonRegistered);
|
||||
addonsModule.set("loadedAddons", &f_loadedAddons);
|
||||
addonsModule.set("isAddonLoaded", &f_isAddonLoaded);
|
||||
addonsModule.set("getAddon", &f_getAddon);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * AddonVersion *
|
||||
// ***************************************************************************
|
||||
|
||||
// Create a new AddonVersion instance.
|
||||
//
|
||||
// addons.AddonVersion.new(versionString)
|
||||
// addons.AddonVersion.new(major[, minor[, patchLevel[, suffix]]])
|
||||
//
|
||||
// where:
|
||||
// - 'major', 'minor' and 'patchLevel' are integers;
|
||||
// - 'suffix' is a string such as "", "a3", "b12" or "rc4" (resp. meaning:
|
||||
// release, alpha 3, beta 12, release candidate 4) Suffixes for
|
||||
// development releases are also allowed, such as ".dev4" (sorts before
|
||||
// the release as well as any alphas, betas and rcs for the release) and
|
||||
// "a3.dev10" (sorts before "a3.dev11", "a3.dev12", "a3", etc.).
|
||||
//
|
||||
// For details, see <https://www.python.org/dev/peps/pep-0440/> which is a
|
||||
// proper superset of what is allowed here.
|
||||
naRef f_createAddonVersion(const nasal::CallContext& ctx)
|
||||
{
|
||||
int major = 0, minor = 0, patchLevel = 0;
|
||||
std::string suffix;
|
||||
|
||||
if (ctx.argc == 0 || ctx.argc > 4) {
|
||||
ctx.runtimeError(
|
||||
"AddonVersion.new(versionString) or "
|
||||
"AddonVersion.new(major[, minor[, patchLevel[, suffix]]])"
|
||||
);
|
||||
}
|
||||
|
||||
if (ctx.argc == 1) {
|
||||
naRef arg1 = ctx.args[0];
|
||||
|
||||
if (naIsString(arg1)) {
|
||||
return ctx.to_nasal(AddonVersionRef(new AddonVersion(naStr_data(arg1))));
|
||||
} else if (naIsNum(arg1)) {
|
||||
AddonVersionRef ref{new AddonVersion(arg1.num, minor, patchLevel,
|
||||
AddonVersionSuffix(suffix))};
|
||||
return ctx.to_nasal(std::move(ref));
|
||||
} else {
|
||||
ctx.runtimeError(
|
||||
"AddonVersion.new(versionString) or "
|
||||
"AddonVersion.new(major[, minor[, patchLevel[, suffix]]])"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assert(ctx.argc > 0);
|
||||
if (!ctx.isNumeric(0)) {
|
||||
ctx.runtimeError(
|
||||
"addons.AddonVersion.new() requires major number as an integer"
|
||||
);
|
||||
}
|
||||
|
||||
major = ctx.requireArg<int>(0);
|
||||
|
||||
if (ctx.argc > 1) {
|
||||
if (!ctx.isNumeric(1)) {
|
||||
ctx.runtimeError(
|
||||
"addons.AddonVersion.new() requires minor number as an integer"
|
||||
);
|
||||
}
|
||||
|
||||
minor = ctx.requireArg<int>(1);
|
||||
}
|
||||
|
||||
if (ctx.argc > 2) {
|
||||
if (!ctx.isNumeric(2)) {
|
||||
ctx.runtimeError(
|
||||
"addons.AddonVersion.new() requires patch level as an integer"
|
||||
);
|
||||
}
|
||||
|
||||
patchLevel = ctx.requireArg<int>(2);
|
||||
}
|
||||
|
||||
if (ctx.argc > 3) {
|
||||
if (!ctx.isString(3)) {
|
||||
ctx.runtimeError(
|
||||
"addons.AddonVersion.new() requires suffix as a string"
|
||||
);
|
||||
}
|
||||
|
||||
suffix = ctx.requireArg<std::string>(3);
|
||||
}
|
||||
|
||||
assert(ctx.argc <= 4);
|
||||
|
||||
return ctx.to_nasal(
|
||||
AddonVersionRef(new AddonVersion(major, minor, patchLevel,
|
||||
AddonVersionSuffix(suffix))));
|
||||
}
|
||||
|
||||
void initAddonClassesForNasal(naRef globals, naContext c)
|
||||
{
|
||||
nasal::Hash globalsModule(globals, c);
|
||||
nasal::Hash addonsModule = globalsModule.createHash("addons");
|
||||
|
||||
wrapAddonManagerMethods(addonsModule);
|
||||
|
||||
Addon::setupGhost(addonsModule);
|
||||
Contact::setupGhost(addonsModule);
|
||||
Author::setupGhost(addonsModule);
|
||||
Maintainer::setupGhost(addonsModule);
|
||||
|
||||
AddonVersion::setupGhost(addonsModule);
|
||||
addonsModule.createHash("AddonVersion").set("new", &f_createAddonVersion);
|
||||
}
|
||||
|
||||
} // of namespace addons
|
||||
|
||||
} // of namespace flightgear
|
||||
37
src/Scripting/NasalAddons.hxx
Normal file
37
src/Scripting/NasalAddons.hxx
Normal file
@@ -0,0 +1,37 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// NasalAddons.hxx --- Expose add-on classes to Nasal
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_ADDON_NASAL_INTERFACE_HXX
|
||||
#define FG_ADDON_NASAL_INTERFACE_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
namespace addons
|
||||
{
|
||||
|
||||
void initAddonClassesForNasal(naRef globals, naContext c);
|
||||
|
||||
} // of namespace addons
|
||||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // of FG_ADDON_NASAL_INTERFACE_HXX
|
||||
49
src/Scripting/NasalAircraft.cxx
Normal file
49
src/Scripting/NasalAircraft.cxx
Normal file
@@ -0,0 +1,49 @@
|
||||
// Expose aircraft related data to Nasal
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalAircraft.hxx"
|
||||
#include <Aircraft/FlightHistory.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_getHistory(const nasal::CallContext& ctx)
|
||||
{
|
||||
FGFlightHistory* history =
|
||||
static_cast<FGFlightHistory*>(globals->get_subsystem("history"));
|
||||
if( !history )
|
||||
ctx.runtimeError("Failed to get 'history' subsystem");
|
||||
|
||||
return ctx.to_nasal(history);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void initNasalAircraft(naRef globals, naContext c)
|
||||
{
|
||||
nasal::Ghost<SGSharedPtr<FGFlightHistory> >::init("FGFlightHistory")
|
||||
.method("pathForHistory", &FGFlightHistory::pathForHistory);
|
||||
|
||||
nasal::Hash aircraft_module = nasal::Hash(globals, c).createHash("aircraft");
|
||||
aircraft_module.set("history", &f_getHistory);
|
||||
}
|
||||
27
src/Scripting/NasalAircraft.hxx
Normal file
27
src/Scripting/NasalAircraft.hxx
Normal file
@@ -0,0 +1,27 @@
|
||||
//@file Expose aircraft related data to Nasal
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_AIRCRAFT_HXX
|
||||
#define SCRIPTING_NASAL_AIRCRAFT_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
void initNasalAircraft(naRef globals, naContext c);
|
||||
|
||||
#endif // SCRIPTING_NASAL_AIRCRAFT_HXX
|
||||
|
||||
658
src/Scripting/NasalCanvas.cxx
Normal file
658
src/Scripting/NasalCanvas.cxx
Normal file
@@ -0,0 +1,658 @@
|
||||
// NasalCanvas.cxx -- expose Canvas classes to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "NasalCanvas.hxx"
|
||||
#include <Canvas/canvas_mgr.hxx>
|
||||
#include <Canvas/gui_mgr.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasWindow.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include <simgear/canvas/elements/CanvasText.hxx>
|
||||
#include <simgear/canvas/events/CustomEvent.hxx>
|
||||
#include <simgear/canvas/events/KeyboardEvent.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/layout/BoxLayout.hxx>
|
||||
#include <simgear/canvas/layout/GridLayout.hxx>
|
||||
#include <simgear/canvas/layout/NasalWidget.hxx>
|
||||
#include <simgear/canvas/layout/SpacerItem.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
extern naRef propNodeGhostCreate(naContext c, SGPropertyNode* n);
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
template<class Element>
|
||||
naRef elementGetNode(Element& element, naContext c)
|
||||
{
|
||||
return propNodeGhostCreate(c, element.getProps());
|
||||
}
|
||||
|
||||
typedef nasal::Ghost<sc::EventPtr> NasalEvent;
|
||||
typedef nasal::Ghost<sc::CustomEventPtr> NasalCustomEvent;
|
||||
typedef nasal::Ghost<sc::DeviceEventPtr> NasalDeviceEvent;
|
||||
typedef nasal::Ghost<sc::KeyboardEventPtr> NasalKeyboardEvent;
|
||||
typedef nasal::Ghost<sc::MouseEventPtr> NasalMouseEvent;
|
||||
|
||||
struct CustomEventDetailWrapper;
|
||||
typedef SGSharedPtr<CustomEventDetailWrapper> CustomEventDetailPtr;
|
||||
typedef nasal::Ghost<CustomEventDetailPtr> NasalCustomEventDetail;
|
||||
|
||||
typedef nasal::Ghost<simgear::PropertyBasedElementPtr> NasalPropertyBasedElement;
|
||||
typedef nasal::Ghost<sc::CanvasPtr> NasalCanvas;
|
||||
typedef nasal::Ghost<sc::ElementPtr> NasalElement;
|
||||
typedef nasal::Ghost<sc::GroupPtr> NasalGroup;
|
||||
typedef nasal::Ghost<sc::TextPtr> NasalText;
|
||||
typedef nasal::Ghost<sc::ImagePtr> NasalImage;
|
||||
|
||||
typedef nasal::Ghost<sc::LayoutItemRef> NasalLayoutItem;
|
||||
typedef nasal::Ghost<sc::LayoutRef> NasalLayout;
|
||||
typedef nasal::Ghost<sc::BoxLayoutRef> NasalBoxLayout;
|
||||
typedef nasal::Ghost<sc::GridLayoutRef> NasalGridLayout;
|
||||
using NasalSpacerItem = nasal::Ghost<sc::SpacerItemRef>;
|
||||
|
||||
typedef nasal::Ghost<sc::WindowPtr> NasalWindow;
|
||||
|
||||
naRef to_nasal_helper(naContext c, const osg::BoundingBox& bb)
|
||||
{
|
||||
std::vector<float> bb_vec(4);
|
||||
bb_vec[0] = bb._min.x();
|
||||
bb_vec[1] = bb._min.y();
|
||||
bb_vec[2] = bb._max.x();
|
||||
bb_vec[3] = bb._max.y();
|
||||
|
||||
return nasal::to_nasal(c, bb_vec);
|
||||
}
|
||||
|
||||
SGPropertyNode* from_nasal_helper(naContext c, naRef ref, SGPropertyNode**)
|
||||
{
|
||||
SGPropertyNode* props = ghostToPropNode(ref);
|
||||
if( !props )
|
||||
naRuntimeError(c, "Not a SGPropertyNode ghost.");
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
CanvasMgr& requireCanvasMgr(const nasal::ContextWrapper& ctx)
|
||||
{
|
||||
CanvasMgr* canvas_mgr =
|
||||
static_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
|
||||
if( !canvas_mgr )
|
||||
ctx.runtimeError("Failed to get Canvas subsystem");
|
||||
|
||||
return *canvas_mgr;
|
||||
}
|
||||
|
||||
GUIMgr& requireGUIMgr(const nasal::ContextWrapper& ctx)
|
||||
{
|
||||
GUIMgr* mgr =
|
||||
static_cast<GUIMgr*>(globals->get_subsystem("CanvasGUI"));
|
||||
if( !mgr )
|
||||
ctx.runtimeError("Failed to get CanvasGUI subsystem");
|
||||
|
||||
return *mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new Canvas and get ghost for it.
|
||||
*/
|
||||
static naRef f_createCanvas(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(requireCanvasMgr(ctx).createCanvas());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new Window and get ghost for it.
|
||||
*/
|
||||
static naRef f_createWindow(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal<sc::WindowWeakPtr>
|
||||
(
|
||||
requireGUIMgr(ctx).createWindow( ctx.getArg<std::string>(0) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ghost for existing Canvas.
|
||||
*/
|
||||
static naRef f_getCanvas(const nasal::CallContext& ctx)
|
||||
{
|
||||
SGPropertyNode& props = *ctx.requireArg<SGPropertyNode*>(0);
|
||||
CanvasMgr& canvas_mgr = requireCanvasMgr(ctx);
|
||||
|
||||
sc::CanvasPtr canvas;
|
||||
if( canvas_mgr.getPropertyRoot() == props.getParent() )
|
||||
{
|
||||
// get a canvas specified by its root node
|
||||
canvas = canvas_mgr.getCanvas( props.getIndex() );
|
||||
if( !canvas || canvas->getProps() != &props )
|
||||
return naNil();
|
||||
}
|
||||
else
|
||||
{
|
||||
// get a canvas by name
|
||||
if( props.hasValue("name") )
|
||||
canvas = canvas_mgr.getCanvas( props.getStringValue("name") );
|
||||
else if( props.hasValue("index") )
|
||||
canvas = canvas_mgr.getCanvas( props.getIntValue("index") );
|
||||
}
|
||||
|
||||
return ctx.to_nasal(canvas);
|
||||
}
|
||||
|
||||
naRef f_canvasCreateGroup(sc::Canvas& canvas, const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal( canvas.createGroup(ctx.getArg<std::string>(0)) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group containing all gui windows
|
||||
*/
|
||||
naRef f_getDesktop(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(requireGUIMgr(ctx).getDesktop());
|
||||
}
|
||||
|
||||
naRef f_setInputFocus(const nasal::CallContext& ctx)
|
||||
{
|
||||
requireGUIMgr(ctx).setInputFocus(ctx.requireArg<sc::WindowPtr>(0));
|
||||
return naNil();
|
||||
}
|
||||
|
||||
naRef f_grabPointer(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(
|
||||
requireGUIMgr(ctx).grabPointer(ctx.requireArg<sc::WindowPtr>(0))
|
||||
);
|
||||
}
|
||||
|
||||
naRef f_ungrabPointer(const nasal::CallContext& ctx)
|
||||
{
|
||||
requireGUIMgr(ctx).ungrabPointer(ctx.requireArg<sc::WindowPtr>(0));
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_groupCreateChild(sc::Group& group, const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal( group.createChild( ctx.requireArg<std::string>(0),
|
||||
ctx.getArg<std::string>(1) ) );
|
||||
}
|
||||
|
||||
static sc::ElementPtr f_groupGetChild(sc::Group& group, SGPropertyNode* node)
|
||||
{
|
||||
return group.getChild(node);
|
||||
}
|
||||
|
||||
static void propElementSetData( simgear::PropertyBasedElement& el,
|
||||
const std::string& name,
|
||||
const nasal::ContextWrapper& ctx,
|
||||
naRef ref )
|
||||
{
|
||||
if( naIsNil(ref) )
|
||||
return el.removeDataProp(name);
|
||||
|
||||
std::string val = ctx.from_nasal<std::string>(ref);
|
||||
|
||||
char* end = NULL;
|
||||
|
||||
long val_long = strtol(val.c_str(), &end, 10);
|
||||
if( !*end )
|
||||
return el.setDataProp(name, val_long);
|
||||
|
||||
double val_double = strtod(val.c_str(), &end);
|
||||
if( !*end )
|
||||
return el.setDataProp(name, val_double);
|
||||
|
||||
el.setDataProp(name, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor for HTML5 data properties.
|
||||
*
|
||||
* # set single property:
|
||||
* el.data("myKey", 5);
|
||||
*
|
||||
* # set multiple properties
|
||||
* el.data({myProp1: 12, myProp2: "test"});
|
||||
*
|
||||
* # get value of properties
|
||||
* el.data("myKey"); # 5
|
||||
* el.data("myProp2"); # "test"
|
||||
*
|
||||
* # remove a single property
|
||||
* el.data("myKey", nil);
|
||||
*
|
||||
* # remove multiple properties
|
||||
* el.data({myProp1: nil, myProp2: nil});
|
||||
*
|
||||
* # set and remove multiple properties
|
||||
* el.data({newProp: "some text...", removeProp: nil});
|
||||
*
|
||||
*
|
||||
* @see http://api.jquery.com/data/
|
||||
*/
|
||||
static naRef f_propElementData( simgear::PropertyBasedElement& el,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
if( ctx.isHash(0) )
|
||||
{
|
||||
// Add/delete properties given as hash
|
||||
nasal::Hash obj = ctx.requireArg<nasal::Hash>(0);
|
||||
for(nasal::Hash::iterator it = obj.begin(); it != obj.end(); ++it)
|
||||
propElementSetData(el, it->getKey(), ctx, it->getValue<naRef>());
|
||||
|
||||
return ctx.to_nasal(&el);
|
||||
}
|
||||
|
||||
std::string name = ctx.getArg<std::string>(0);
|
||||
if( !name.empty() )
|
||||
{
|
||||
if( ctx.argc == 1 )
|
||||
{
|
||||
// name + additional argument -> add/delete property
|
||||
SGPropertyNode* node = el.getDataProp<SGPropertyNode*>(name);
|
||||
if( !node )
|
||||
return naNil();
|
||||
|
||||
return ctx.to_nasal( node->getStringValue() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// only name -> get property
|
||||
propElementSetData(el, name, ctx, ctx.requireArg<naRef>(1));
|
||||
return ctx.to_nasal(&el);
|
||||
}
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_createCustomEvent(const nasal::CallContext& ctx)
|
||||
{
|
||||
std::string const& type = ctx.requireArg<std::string>(0);
|
||||
if( type.empty() )
|
||||
return naNil();
|
||||
|
||||
bool bubbles = false;
|
||||
simgear::StringMap detail;
|
||||
if( ctx.isHash(1) )
|
||||
{
|
||||
nasal::Hash const& cfg = ctx.requireArg<nasal::Hash>(1);
|
||||
naRef na_detail = cfg.get("detail");
|
||||
if( naIsHash(na_detail) )
|
||||
detail = ctx.from_nasal<simgear::StringMap>(na_detail);
|
||||
bubbles = cfg.get<bool>("bubbles");
|
||||
}
|
||||
|
||||
return ctx.to_nasal(
|
||||
sc::CustomEventPtr(new sc::CustomEvent(type, bubbles, detail))
|
||||
);
|
||||
}
|
||||
|
||||
struct CustomEventDetailWrapper:
|
||||
public SGReferenced
|
||||
{
|
||||
sc::CustomEventPtr _event;
|
||||
|
||||
CustomEventDetailWrapper(const sc::CustomEventPtr& event):
|
||||
_event(event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool _get( const std::string& key,
|
||||
std::string& value_out ) const
|
||||
{
|
||||
if( !_event )
|
||||
return false;
|
||||
|
||||
simgear::StringMap::const_iterator it = _event->detail.find(key);
|
||||
if( it == _event->detail.end() )
|
||||
return false;
|
||||
|
||||
value_out = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _set( const std::string& key,
|
||||
const std::string& value )
|
||||
{
|
||||
if( !_event )
|
||||
return false;
|
||||
|
||||
_event->detail[ key ] = value;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static naRef f_customEventGetDetail( sc::CustomEvent& event,
|
||||
naContext c )
|
||||
{
|
||||
return nasal::to_nasal(
|
||||
c,
|
||||
CustomEventDetailPtr(new CustomEventDetailWrapper(&event))
|
||||
);
|
||||
}
|
||||
|
||||
static naRef f_boxLayoutAddItem( sc::BoxLayout& box,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
box.addItem( ctx.requireArg<sc::LayoutItemRef>(0),
|
||||
ctx.getArg<int>(1),
|
||||
ctx.getArg<int>(2, sc::AlignFill) );
|
||||
return naNil();
|
||||
}
|
||||
static naRef f_boxLayoutInsertItem( sc::BoxLayout& box,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
box.insertItem( ctx.requireArg<int>(0),
|
||||
ctx.requireArg<sc::LayoutItemRef>(1),
|
||||
ctx.getArg<int>(2),
|
||||
ctx.getArg<int>(3, sc::AlignFill) );
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_boxLayoutAddStretch( sc::BoxLayout& box,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
box.addStretch( ctx.getArg<int>(0) );
|
||||
return naNil();
|
||||
}
|
||||
static naRef f_boxLayoutInsertStretch( sc::BoxLayout& box,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
box.insertStretch( ctx.requireArg<int>(0),
|
||||
ctx.getArg<int>(1) );
|
||||
return naNil();
|
||||
}
|
||||
|
||||
template<class Type, class Base>
|
||||
static naRef f_newAsBase(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal<Base*>(new Type());
|
||||
}
|
||||
|
||||
static naRef f_imageFillRect(sc::Image& img, const nasal::CallContext& ctx)
|
||||
{
|
||||
const SGRecti r = ctx.requireArg<SGRecti>(0);
|
||||
if (ctx.isString(1)) {
|
||||
img.fillRect(r, ctx.getArg<std::string>(1));
|
||||
} else {
|
||||
img.fillRect(r, ctx.requireArg<osg::Vec4>(1));
|
||||
}
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_imageSetPixel(sc::Image& img, const nasal::CallContext& ctx)
|
||||
{
|
||||
const int s = ctx.requireArg<int>(0);
|
||||
const int t = ctx.requireArg<int>(1);
|
||||
if (ctx.isString(2)) {
|
||||
img.setPixel(s, t, ctx.getArg<std::string>(2));
|
||||
} else {
|
||||
img.setPixel(s, t, ctx.requireArg<osg::Vec4>(2));
|
||||
}
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_gridLayoutAddItem(sc::GridLayout& grid,
|
||||
const nasal::CallContext& ctx)
|
||||
{
|
||||
grid.addItem(ctx.requireArg<sc::LayoutItemRef>(0),
|
||||
ctx.requireArg<int>(1),
|
||||
ctx.requireArg<int>(2),
|
||||
ctx.getArg<int>(3, 1),
|
||||
ctx.getArg<int>(4, 1));
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_newGridLayout(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(new sc::GridLayout);
|
||||
}
|
||||
|
||||
static naRef f_newSpacerItem(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(new sc::SpacerItem);
|
||||
}
|
||||
|
||||
naRef initNasalCanvas(naRef globals, naContext c)
|
||||
{
|
||||
nasal::Hash globals_module(globals, c),
|
||||
canvas_module = globals_module.createHash("canvas");
|
||||
|
||||
nasal::Object::setupGhost();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Events
|
||||
|
||||
using osgGA::GUIEventAdapter;
|
||||
NasalEvent::init("canvas.Event")
|
||||
.member("type", &sc::Event::getTypeString)
|
||||
.member("target", &sc::Event::getTarget)
|
||||
.member("currentTarget", &sc::Event::getCurrentTarget)
|
||||
.member("defaultPrevented", &sc::Event::defaultPrevented)
|
||||
.method("stopPropagation", &sc::Event::stopPropagation)
|
||||
.method("preventDefault", &sc::Event::preventDefault);
|
||||
|
||||
NasalCustomEvent::init("canvas.CustomEvent")
|
||||
.bases<NasalEvent>()
|
||||
.member("detail", &f_customEventGetDetail, &sc::CustomEvent::setDetail);
|
||||
NasalCustomEventDetail::init("canvas.CustomEventDetail")
|
||||
._get(&CustomEventDetailWrapper::_get)
|
||||
._set(&CustomEventDetailWrapper::_set);
|
||||
|
||||
canvas_module.createHash("CustomEvent")
|
||||
.set("new", &f_createCustomEvent);
|
||||
|
||||
NasalDeviceEvent::init("canvas.DeviceEvent")
|
||||
.bases<NasalEvent>()
|
||||
.member("modifiers", &sc::DeviceEvent::getModifiers)
|
||||
.member("ctrlKey", &sc::DeviceEvent::ctrlKey)
|
||||
.member("shiftKey", &sc::DeviceEvent::shiftKey)
|
||||
.member("altKey", &sc::DeviceEvent::altKey)
|
||||
.member("metaKey", &sc::DeviceEvent::metaKey);
|
||||
|
||||
NasalKeyboardEvent::init("canvas.KeyboardEvent")
|
||||
.bases<NasalDeviceEvent>()
|
||||
.member("key", &sc::KeyboardEvent::key)
|
||||
.member("location", &sc::KeyboardEvent::location)
|
||||
.member("repeat", &sc::KeyboardEvent::repeat)
|
||||
.member("charCode", &sc::KeyboardEvent::charCode)
|
||||
.member("keyCode", &sc::KeyboardEvent::keyCode);
|
||||
|
||||
NasalMouseEvent::init("canvas.MouseEvent")
|
||||
.bases<NasalDeviceEvent>()
|
||||
.member("screenX", &sc::MouseEvent::getScreenX)
|
||||
.member("screenY", &sc::MouseEvent::getScreenY)
|
||||
.member("clientX", &sc::MouseEvent::getClientX)
|
||||
.member("clientY", &sc::MouseEvent::getClientY)
|
||||
.member("localX", &sc::MouseEvent::getLocalX)
|
||||
.member("localY", &sc::MouseEvent::getLocalY)
|
||||
.member("deltaX", &sc::MouseEvent::getDeltaX)
|
||||
.member("deltaY", &sc::MouseEvent::getDeltaY)
|
||||
.member("button", &sc::MouseEvent::getButton)
|
||||
.member("buttons", &sc::MouseEvent::getButtonMask)
|
||||
.member("click_count", &sc::MouseEvent::getCurrentClickCount);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Canvas & elements
|
||||
|
||||
NasalPropertyBasedElement::init("PropertyBasedElement")
|
||||
.method("data", &f_propElementData);
|
||||
NasalCanvas::init("Canvas")
|
||||
.bases<NasalPropertyBasedElement>()
|
||||
.bases<nasal::ObjectRef>()
|
||||
.member("_node_ghost", &elementGetNode<sc::Canvas>)
|
||||
.member("size_x", &sc::Canvas::getSizeX)
|
||||
.member("size_y", &sc::Canvas::getSizeY)
|
||||
.method("_createGroup", &f_canvasCreateGroup)
|
||||
.method("_getGroup", &sc::Canvas::getGroup)
|
||||
.method( "addEventListener",
|
||||
static_cast<bool (sc::Canvas::*)( const std::string&,
|
||||
const sc::EventListener& )>
|
||||
(&sc::Canvas::addEventListener) )
|
||||
.method("dispatchEvent", &sc::Canvas::dispatchEvent)
|
||||
.method("setLayout", &sc::Canvas::setLayout)
|
||||
.method("setFocusElement", &sc::Canvas::setFocusElement)
|
||||
.method("clearFocusElement", &sc::Canvas::clearFocusElement);
|
||||
|
||||
canvas_module.set("_newCanvasGhost", f_createCanvas);
|
||||
canvas_module.set("_getCanvasGhost", f_getCanvas);
|
||||
|
||||
NasalElement::init("canvas.Element")
|
||||
.bases<NasalPropertyBasedElement>()
|
||||
.member("_node_ghost", &elementGetNode<sc::Element>)
|
||||
.method("_getParent", &sc::Element::getParent)
|
||||
.method("_getCanvas", &sc::Element::getCanvas)
|
||||
.method("addEventListener", &sc::Element::addEventListener)
|
||||
.method("setFocus", &sc::Element::setFocus)
|
||||
.method("dispatchEvent", &sc::Element::dispatchEvent)
|
||||
.method("getBoundingBox", &sc::Element::getBoundingBox)
|
||||
.method("getTightBoundingBox", &sc::Element::getTightBoundingBox);
|
||||
|
||||
NasalGroup::init("canvas.Group")
|
||||
.bases<NasalElement>()
|
||||
.method("_createChild", &f_groupCreateChild)
|
||||
.method( "_getChild", &f_groupGetChild)
|
||||
.method("_getElementById", &sc::Group::getElementById);
|
||||
NasalText::init("canvas.Text")
|
||||
.bases<NasalElement>()
|
||||
.method("heightForWidth", &sc::Text::heightForWidth)
|
||||
.method("maxWidth", &sc::Text::maxWidth)
|
||||
.method("lineCount", &sc::Text::lineCount)
|
||||
.method("lineLength", &sc::Text::lineLength)
|
||||
.method("getNearestCursor", &sc::Text::getNearestCursor)
|
||||
.method("getCursorPos", &sc::Text::getCursorPos);
|
||||
|
||||
NasalImage::init("canvas.Image")
|
||||
.bases<NasalElement>()
|
||||
.method("fillRect", &f_imageFillRect)
|
||||
.method("setPixel", &f_imageSetPixel)
|
||||
.method("dirtyPixels", &sc::Image::dirtyPixels);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Layouting
|
||||
|
||||
#define ALIGN_ENUM_MAPPING(key, val, comment) canvas_module.set(#key, sc::key);
|
||||
# include <simgear/canvas/layout/AlignFlag_values.hxx>
|
||||
#undef ALIGN_ENUM_MAPPING
|
||||
|
||||
void (sc::LayoutItem::*f_layoutItemSetContentsMargins)(int, int, int, int)
|
||||
= &sc::LayoutItem::setContentsMargins;
|
||||
|
||||
NasalLayoutItem::init("canvas.LayoutItem")
|
||||
.method("getCanvas", &sc::LayoutItem::getCanvas)
|
||||
.method("setCanvas", &sc::LayoutItem::setCanvas)
|
||||
.method("getParent", &sc::LayoutItem::getParent)
|
||||
.method("setParent", &sc::LayoutItem::setParent)
|
||||
.method("setContentsMargins", f_layoutItemSetContentsMargins)
|
||||
.method("setContentsMargin", &sc::LayoutItem::setContentsMargin)
|
||||
.method("sizeHint", &sc::LayoutItem::sizeHint)
|
||||
.method("minimumSize", &sc::LayoutItem::minimumSize)
|
||||
.method("maximumSize", &sc::LayoutItem::maximumSize)
|
||||
.method("hasHeightForWidth", &sc::LayoutItem::hasHeightForWidth)
|
||||
.method("heightForWidth", &sc::LayoutItem::heightForWidth)
|
||||
.method("minimumHeightForWidth", &sc::LayoutItem::minimumHeightForWidth)
|
||||
.method("setAlignment", &sc::LayoutItem::setAlignment)
|
||||
.method("alignment", &sc::LayoutItem::alignment)
|
||||
.method("setVisible", &sc::LayoutItem::setVisible)
|
||||
.method("isVisible", &sc::LayoutItem::isVisible)
|
||||
.method("isExplicitlyHidden", &sc::LayoutItem::isExplicitlyHidden)
|
||||
.method("show", &sc::LayoutItem::show)
|
||||
.method("hide", &sc::LayoutItem::hide)
|
||||
.method("setGeometry", &sc::LayoutItem::setGeometry)
|
||||
.method("geometry", &sc::LayoutItem::geometry)
|
||||
.method("setGridLocation", &sc::LayoutItem::setGridLocation)
|
||||
.method("setGridSpan", &sc::LayoutItem::setGridSpan);
|
||||
|
||||
sc::NasalWidget::setupGhost(canvas_module);
|
||||
|
||||
NasalLayout::init("canvas.Layout")
|
||||
.bases<NasalLayoutItem>()
|
||||
.method("addItem", &sc::Layout::addItem)
|
||||
.method("setSpacing", &sc::Layout::setSpacing)
|
||||
.method("spacing", &sc::Layout::spacing)
|
||||
.method("count", &sc::Layout::count)
|
||||
.method("itemAt", &sc::Layout::itemAt)
|
||||
.method("takeAt", &sc::Layout::takeAt)
|
||||
.method("removeItem", &sc::Layout::removeItem)
|
||||
.method("clear", &sc::Layout::clear);
|
||||
|
||||
NasalBoxLayout::init("canvas.BoxLayout")
|
||||
.bases<NasalLayout>()
|
||||
.method("addItem", &f_boxLayoutAddItem)
|
||||
.method("addSpacing", &sc::BoxLayout::addSpacing)
|
||||
.method("addStretch", &f_boxLayoutAddStretch)
|
||||
.method("insertItem", &f_boxLayoutInsertItem)
|
||||
.method("insertSpacing", &sc::BoxLayout::insertSpacing)
|
||||
.method("insertStretch", &f_boxLayoutInsertStretch)
|
||||
.method("setStretch", &sc::BoxLayout::setStretch)
|
||||
.method("setStretchFactor", &sc::BoxLayout::setStretchFactor)
|
||||
.method("stretch", &sc::BoxLayout::stretch);
|
||||
|
||||
NasalGridLayout::init("canvas.GridLayout")
|
||||
.bases<NasalLayout>()
|
||||
.method("addItem", &f_gridLayoutAddItem)
|
||||
.method("setRowStretch", &sc::GridLayout::setRowStretch)
|
||||
.method("setColumnStretch", &sc::GridLayout::setColumnStretch);
|
||||
|
||||
NasalSpacerItem::init("canvas.SpacerItem")
|
||||
.bases<NasalLayoutItem>();
|
||||
|
||||
canvas_module.createHash("HBoxLayout")
|
||||
.set("new", &f_newAsBase<sc::HBoxLayout, sc::BoxLayout>);
|
||||
canvas_module.createHash("VBoxLayout")
|
||||
.set("new", &f_newAsBase<sc::VBoxLayout, sc::BoxLayout>);
|
||||
canvas_module.createHash("GridLayout")
|
||||
.set("new", &f_newGridLayout);
|
||||
canvas_module.createHash("Spacer")
|
||||
.set("new", &f_newSpacerItem);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Window
|
||||
|
||||
NasalWindow::init("canvas.Window")
|
||||
.bases<NasalElement>()
|
||||
.bases<NasalLayoutItem>()
|
||||
.member("_node_ghost", &elementGetNode<sc::Window>)
|
||||
.method("_getCanvasDecoration", &sc::Window::getCanvasDecoration)
|
||||
.method("setLayout", &sc::Window::setLayout);
|
||||
|
||||
canvas_module.set("_newWindowGhost", f_createWindow);
|
||||
canvas_module.set("_getDesktopGhost", f_getDesktop);
|
||||
canvas_module.set("setInputFocus", f_setInputFocus);
|
||||
canvas_module.set("grabPointer", f_grabPointer);
|
||||
canvas_module.set("ungrabPointer", f_ungrabPointer);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
29
src/Scripting/NasalCanvas.hxx
Normal file
29
src/Scripting/NasalCanvas.hxx
Normal file
@@ -0,0 +1,29 @@
|
||||
// NasalCanvas.hxx -- expose Canvas classes to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_CANVAS_HXX
|
||||
#define SCRIPTING_NASAL_CANVAS_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
naRef initNasalCanvas(naRef globals, naContext c);
|
||||
|
||||
#endif // of SCRIPTING_NASAL_CANVAS_HXX
|
||||
|
||||
106
src/Scripting/NasalClipboard.cxx
Normal file
106
src/Scripting/NasalClipboard.cxx
Normal file
@@ -0,0 +1,106 @@
|
||||
// X11 implementation of clipboard access for Nasal
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalClipboard.hxx"
|
||||
#include "NasalSys.hxx"
|
||||
#include <simgear/nasal/cppbind/NasalCallContext.hxx>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
/*
|
||||
* Nasal wrappers for setting/getting clipboard text
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
static NasalClipboard::Type parseType(const nasal::CallContext& ctx, size_t i)
|
||||
{
|
||||
if( ctx.argc > i )
|
||||
{
|
||||
if( ctx.isNumeric(i) )
|
||||
{
|
||||
if( ctx.requireArg<int>(i) == NasalClipboard::CLIPBOARD )
|
||||
return NasalClipboard::CLIPBOARD;
|
||||
if( ctx.requireArg<int>(i) == NasalClipboard::PRIMARY )
|
||||
return NasalClipboard::PRIMARY;
|
||||
}
|
||||
|
||||
ctx.runtimeError("clipboard: invalid arg "
|
||||
"(expected clipboard.CLIPBOARD or clipboard.SELECTION)");
|
||||
}
|
||||
|
||||
return NasalClipboard::CLIPBOARD;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_setClipboardText(const nasal::CallContext& ctx)
|
||||
{
|
||||
if( ctx.argc < 1 || ctx.argc > 2 )
|
||||
ctx.runtimeError("clipboard.setText() expects 1 or 2 arguments: "
|
||||
"text, [, type = clipboard.CLIPBOARD]");
|
||||
|
||||
return
|
||||
naNum
|
||||
(
|
||||
NasalClipboard::getInstance()->setText( ctx.requireArg<std::string>(0),
|
||||
parseType(ctx, 1) )
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_getClipboardText(const nasal::CallContext& ctx)
|
||||
{
|
||||
if( ctx.argc > 1 )
|
||||
ctx.runtimeError("clipboard.getText() accepts max 1 arg: "
|
||||
"[type = clipboard.CLIPBOARD]");
|
||||
|
||||
return ctx.to_nasal
|
||||
(
|
||||
NasalClipboard::getInstance()->getText(parseType(ctx, 0))
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::_clipboard;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::~NasalClipboard()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void NasalClipboard::init(FGNasalSys *nasal)
|
||||
{
|
||||
_clipboard = create();
|
||||
|
||||
nasal::Hash clipboard = nasal->getGlobals().createHash("clipboard");
|
||||
|
||||
clipboard.set("setText", f_setClipboardText);
|
||||
clipboard.set("getText", f_getClipboardText);
|
||||
clipboard.set("CLIPBOARD", NasalClipboard::CLIPBOARD);
|
||||
clipboard.set("SELECTION", NasalClipboard::PRIMARY);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NasalClipboard::Ptr NasalClipboard::getInstance()
|
||||
{
|
||||
return _clipboard;
|
||||
}
|
||||
72
src/Scripting/NasalClipboard.hxx
Normal file
72
src/Scripting/NasalClipboard.hxx
Normal file
@@ -0,0 +1,72 @@
|
||||
// Clipboard access for Nasal
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef NASAL_CLIPOARD_HXX_
|
||||
#define NASAL_CLIPOARD_HXX_
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class FGNasalSys;
|
||||
class NasalClipboard
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
/// Standard clipboard as supported by nearly all operating systems
|
||||
CLIPBOARD,
|
||||
|
||||
/// X11 platforms support also a mode called PRIMARY selection which
|
||||
/// contains the current (mouse) selection and can typically be inserted
|
||||
/// via a press on the middle mouse button
|
||||
PRIMARY
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<NasalClipboard> Ptr;
|
||||
|
||||
virtual void update() {}
|
||||
virtual std::string getText(Type type = CLIPBOARD) = 0;
|
||||
virtual bool setText( const std::string& text,
|
||||
Type type = CLIPBOARD ) = 0;
|
||||
|
||||
/**
|
||||
* Sets up the clipboard and puts all the extension functions into a new
|
||||
* "clipboard" namespace.
|
||||
*/
|
||||
static void init(FGNasalSys *nasal);
|
||||
|
||||
/**
|
||||
* Get clipboard platform specific instance
|
||||
*/
|
||||
static Ptr getInstance();
|
||||
|
||||
protected:
|
||||
|
||||
static Ptr _clipboard;
|
||||
|
||||
/**
|
||||
* Implementation supplied by actual platform implementation
|
||||
*/
|
||||
static Ptr create();
|
||||
|
||||
virtual ~NasalClipboard() = 0;
|
||||
};
|
||||
|
||||
#endif /* NASAL_CLIPOARD_HXX_ */
|
||||
69
src/Scripting/NasalCondition.cxx
Normal file
69
src/Scripting/NasalCondition.cxx
Normal file
@@ -0,0 +1,69 @@
|
||||
// NasalCondition -- expose SGCondition to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalCondition.hxx"
|
||||
#include "NasalSys.hxx"
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
typedef nasal::Ghost<SGConditionRef> NasalCondition;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_createCondition(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
SGPropertyNode* node = argc > 0
|
||||
? ghostToPropNode(args[0])
|
||||
: NULL;
|
||||
SGPropertyNode* root = argc > 1
|
||||
? ghostToPropNode(args[1])
|
||||
: globals->get_props();
|
||||
|
||||
if( !node || !root )
|
||||
naRuntimeError(c, "createCondition: invalid argument(s)");
|
||||
|
||||
try
|
||||
{
|
||||
return nasal::to_nasal(c, sgReadCondition(root, node));
|
||||
}
|
||||
catch(std::exception& ex)
|
||||
{
|
||||
naRuntimeError(c, "createCondition: %s", ex.what());
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalCondition(naRef globals, naContext c)
|
||||
{
|
||||
nasal::Ghost<SGConditionRef>::init("Condition")
|
||||
.method("test", &SGCondition::test);
|
||||
|
||||
nasal::Hash(globals, c).set("_createCondition", f_createCondition);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
28
src/Scripting/NasalCondition.hxx
Normal file
28
src/Scripting/NasalCondition.hxx
Normal file
@@ -0,0 +1,28 @@
|
||||
// NasalCondition.hxx -- expose SGCondition to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_CONDITION_HXX
|
||||
#define SCRIPTING_NASAL_CONDITION_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
naRef initNasalCondition(naRef globals, naContext c);
|
||||
|
||||
#endif // of SCRIPTING_NASAL_CONDITION_HXX
|
||||
2323
src/Scripting/NasalFlightPlan.cxx
Normal file
2323
src/Scripting/NasalFlightPlan.cxx
Normal file
File diff suppressed because it is too large
Load Diff
37
src/Scripting/NasalFlightPlan.hxx
Normal file
37
src/Scripting/NasalFlightPlan.hxx
Normal file
@@ -0,0 +1,37 @@
|
||||
// NasalFlightPlan.hxx -- expose FlightPlan classes to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2020.
|
||||
//
|
||||
// Copyright (C) 2020 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
#include <Navaids/procedure.hxx>
|
||||
|
||||
flightgear::Waypt* wayptGhost(naRef r);
|
||||
flightgear::FlightPlan::Leg* fpLegGhost(naRef r);
|
||||
flightgear::Procedure* procedureGhost(naRef r);
|
||||
|
||||
naRef ghostForWaypt(naContext c, const flightgear::Waypt* wpt);
|
||||
naRef ghostForLeg(naContext c, const flightgear::FlightPlan::Leg* leg);
|
||||
naRef ghostForProcedure(naContext c, const flightgear::Procedure* proc);
|
||||
|
||||
naRef initNasalFlightPlan(naRef globals, naContext c);
|
||||
void shutdownNasalFlightPlan();
|
||||
124
src/Scripting/NasalHTTP.cxx
Normal file
124
src/Scripting/NasalHTTP.cxx
Normal file
@@ -0,0 +1,124 @@
|
||||
// Expose HTTP module to Nasal
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalHTTP.hxx"
|
||||
#include <Main/globals.hxx>
|
||||
#include <Network/HTTPClient.hxx>
|
||||
|
||||
#include <simgear/io/HTTPFileRequest.hxx>
|
||||
#include <simgear/io/HTTPMemoryRequest.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
typedef nasal::Ghost<simgear::HTTP::Request_ptr> NasalRequest;
|
||||
typedef nasal::Ghost<simgear::HTTP::FileRequestRef> NasalFileRequest;
|
||||
typedef nasal::Ghost<simgear::HTTP::MemoryRequestRef> NasalMemoryRequest;
|
||||
|
||||
FGHTTPClient& requireHTTPClient(const nasal::ContextWrapper& ctx)
|
||||
{
|
||||
FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
|
||||
if( !http )
|
||||
ctx.runtimeError("Failed to get HTTP subsystem");
|
||||
|
||||
return *http;
|
||||
}
|
||||
|
||||
/**
|
||||
* http.save(url, filename)
|
||||
*/
|
||||
static naRef f_http_save(const nasal::CallContext& ctx)
|
||||
{
|
||||
const std::string url = ctx.requireArg<std::string>(0);
|
||||
|
||||
// Check for write access to target file
|
||||
const std::string filename = ctx.requireArg<std::string>(1);
|
||||
const SGPath validated_path = SGPath(filename).validate(true);
|
||||
|
||||
if( validated_path.isNull() )
|
||||
ctx.runtimeError("Access denied: can not write to %s", filename.c_str());
|
||||
|
||||
return ctx.to_nasal
|
||||
(
|
||||
requireHTTPClient(ctx).client()->save(url, validated_path.utf8Str())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* http.load(url)
|
||||
*/
|
||||
static naRef f_http_load(const nasal::CallContext& ctx)
|
||||
{
|
||||
const std::string url = ctx.requireArg<std::string>(0);
|
||||
return ctx.to_nasal( requireHTTPClient(ctx).client()->load(url) );
|
||||
}
|
||||
|
||||
static naRef f_request_abort( simgear::HTTP::Request&,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
// we need a request_ptr for cancel, not a reference. So extract
|
||||
// the me object from the context directly.
|
||||
simgear::HTTP::Request_ptr req = ctx.from_nasal<simgear::HTTP::Request_ptr>(ctx.me);
|
||||
requireHTTPClient(ctx).client()->cancelRequest(req);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalHTTP(naRef globals, naContext c)
|
||||
{
|
||||
using simgear::HTTP::Request;
|
||||
typedef Request* (Request::*HTTPCallback)(const Request::Callback&);
|
||||
NasalRequest::init("http.Request")
|
||||
.member("url", &Request::url)
|
||||
.member("method", &Request::method)
|
||||
.member("scheme", &Request::scheme)
|
||||
.member("path", &Request::path)
|
||||
.member("host", &Request::host)
|
||||
.member("port", &Request::port)
|
||||
.member("query", &Request::query)
|
||||
.member("status", &Request::responseCode)
|
||||
.member("reason", &Request::responseReason)
|
||||
.member("readyState", &Request::readyState)
|
||||
.method("abort", f_request_abort)
|
||||
.method("done", static_cast<HTTPCallback>(&Request::done))
|
||||
.method("fail", static_cast<HTTPCallback>(&Request::fail))
|
||||
.method("always", static_cast<HTTPCallback>(&Request::always));
|
||||
|
||||
using simgear::HTTP::FileRequest;
|
||||
NasalFileRequest::init("http.FileRequest")
|
||||
.bases<NasalRequest>();
|
||||
|
||||
using simgear::HTTP::MemoryRequest;
|
||||
NasalMemoryRequest::init("http.MemoryRequest")
|
||||
.bases<NasalRequest>()
|
||||
.member("response", &MemoryRequest::responseBody);
|
||||
|
||||
nasal::Hash globals_module(globals, c),
|
||||
http = globals_module.createHash("http");
|
||||
|
||||
http.set("save", f_http_save);
|
||||
http.set("load", f_http_load);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
26
src/Scripting/NasalHTTP.hxx
Normal file
26
src/Scripting/NasalHTTP.hxx
Normal file
@@ -0,0 +1,26 @@
|
||||
//@file Expose HTTP module to Nasal
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_HTTP_HXX
|
||||
#define SCRIPTING_NASAL_HTTP_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
naRef initNasalHTTP(naRef globals, naContext c);
|
||||
|
||||
#endif // of SCRIPTING_NASAL_HTTP_HXX
|
||||
229
src/Scripting/NasalModelData.cxx
Normal file
229
src/Scripting/NasalModelData.cxx
Normal file
@@ -0,0 +1,229 @@
|
||||
|
||||
#include "NasalModelData.hxx"
|
||||
#include "NasalSys.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include <osg/Transform>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/scene/util/OsgDebug.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring> // for strlen
|
||||
|
||||
// FGNasalModelData class. If sgLoad3DModel() is called with a pointer to
|
||||
// such a class, then it lets modelLoaded() run the <load> script, and the
|
||||
// destructor the <unload> script. The latter happens when the model branch
|
||||
// is removed from the scene graph.
|
||||
|
||||
unsigned int FGNasalModelData::_max_module_id = 0;
|
||||
FGNasalModelDataList FGNasalModelData::_loaded_models;
|
||||
|
||||
typedef osg::ref_ptr<osg::Node> NodeRef;
|
||||
typedef nasal::Ghost<NodeRef> NasalNode;
|
||||
|
||||
/**
|
||||
* Get position (lat, lon, elevation) and orientation (heading, pitch, roll) of
|
||||
* model.
|
||||
*/
|
||||
static naRef f_node_getPose( const osg::Node& node,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
osg::NodePathList parent_paths = node.getParentalNodePaths();
|
||||
for( osg::NodePathList::const_iterator path = parent_paths.begin();
|
||||
path != parent_paths.end();
|
||||
++path )
|
||||
{
|
||||
osg::Matrix local_to_world = osg::computeLocalToWorld(*path);
|
||||
if( !local_to_world.valid() )
|
||||
continue;
|
||||
|
||||
SGGeod coord = SGGeod::fromCart( toSG(local_to_world.getTrans()) );
|
||||
if( !coord.isValid() )
|
||||
continue;
|
||||
|
||||
osg::Matrix local_frame = makeZUpFrameRelative(coord),
|
||||
inv_local;
|
||||
inv_local.invert_4x3(local_frame);
|
||||
local_to_world.postMult(inv_local);
|
||||
|
||||
SGQuatd rotate = toSG(local_to_world.getRotate());
|
||||
double hdg, pitch, roll;
|
||||
rotate.getEulerDeg(hdg, pitch, roll);
|
||||
|
||||
nasal::Hash pose(ctx.to_nasal(coord), ctx.c_ctx());
|
||||
pose.set("heading", hdg);
|
||||
pose.set("pitch", pitch);
|
||||
pose.set("roll", roll);
|
||||
return pose.get_naRef();
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGNasalModelData::FGNasalModelData( SGPropertyNode *root,
|
||||
const std::string& path,
|
||||
SGPropertyNode *prop,
|
||||
SGPropertyNode* load,
|
||||
SGPropertyNode* unload,
|
||||
osg::Node* branch ):
|
||||
_path(path),
|
||||
_root(root), _prop(prop),
|
||||
_load(load), _unload(unload),
|
||||
_branch(branch),
|
||||
_module_id( _max_module_id++ )
|
||||
{
|
||||
_loaded_models.push_back(this);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_INFO,
|
||||
"New model with attached script(s) "
|
||||
"(branch = " << branch << ","
|
||||
" path = " << simgear::getNodePathString(branch) << ")"
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGNasalModelData::~FGNasalModelData()
|
||||
{
|
||||
_loaded_models.remove(this);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_INFO,
|
||||
"Removed model with script(s) (branch = " << _branch.get() << ")"
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGNasalModelData::load()
|
||||
{
|
||||
std::stringstream m;
|
||||
m << "__model" << _module_id;
|
||||
_module = m.str();
|
||||
|
||||
SG_LOG(SG_NASAL, SG_DEBUG, "Loading nasal module " << _module.c_str());
|
||||
|
||||
const string s = _load ? _load->getStringValue() : "";
|
||||
FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
|
||||
// Add _module_id to script local hash to allow placing canvasses on objects
|
||||
// inside the model.
|
||||
nasal::Hash module = nasalSys->getGlobals().createHash(_module);
|
||||
module.set("_module_id", _module_id);
|
||||
|
||||
NasalNode::init("osg.Node")
|
||||
.method("getPose", &f_node_getPose);
|
||||
module.set("_model", _branch);
|
||||
|
||||
naRef arg[2];
|
||||
arg[0] = nasalSys->propNodeGhost(_root);
|
||||
arg[1] = nasalSys->propNodeGhost(_prop);
|
||||
nasalSys->createModule(_module.c_str(), _path.c_str(), s.c_str(), s.length(),
|
||||
_root, 2, arg);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGNasalModelData::unload()
|
||||
{
|
||||
if (_module.empty())
|
||||
return;
|
||||
|
||||
FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
if(!nasalSys) {
|
||||
SG_LOG(SG_NASAL, SG_WARN, "Trying to run an <unload> script "
|
||||
"without Nasal subsystem present.");
|
||||
return;
|
||||
}
|
||||
|
||||
SG_LOG(SG_NASAL, SG_DEBUG, "Unloading nasal module " << _module.c_str());
|
||||
|
||||
if (_unload)
|
||||
{
|
||||
const string s = _unload->getStringValue();
|
||||
nasalSys->createModule(_module.c_str(), _module.c_str(), s.c_str(), s.length(), _root);
|
||||
}
|
||||
|
||||
nasalSys->deleteModule(_module.c_str());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
osg::Node* FGNasalModelData::getNode()
|
||||
{
|
||||
return _branch.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGNasalModelData* FGNasalModelData::getByModuleId(unsigned int id)
|
||||
{
|
||||
FGNasalModelDataList::iterator it = std::find_if
|
||||
(
|
||||
_loaded_models.begin(),
|
||||
_loaded_models.end(),
|
||||
[id] (const FGNasalModelData* const data) {
|
||||
return data->_module_id == id; });
|
||||
|
||||
if( it != _loaded_models.end() )
|
||||
return *it;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGNasalModelDataProxy::~FGNasalModelDataProxy()
|
||||
{
|
||||
auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
|
||||
// when necessary, register Nasal module to be destroyed/unloaded
|
||||
// in the main thread.
|
||||
if ((_data.valid())&&(nasalSys))
|
||||
nasalSys->registerToUnload(_data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGNasalModelDataProxy::modelLoaded( const std::string& path,
|
||||
SGPropertyNode *prop,
|
||||
osg::Node *branch )
|
||||
{
|
||||
if( fgGetBool("/sim/disable-embedded-nasal") )
|
||||
return;
|
||||
|
||||
if(!prop)
|
||||
return;
|
||||
|
||||
SGPropertyNode *nasal = prop->getNode("nasal");
|
||||
if(!nasal)
|
||||
return;
|
||||
|
||||
FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
if(!nasalSys)
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_WARN,
|
||||
"Can not load model script(s) (Nasal subsystem not available)."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
SGPropertyNode* load = nasal->getNode("load");
|
||||
SGPropertyNode* unload = nasal->getNode("unload");
|
||||
|
||||
if ((!load) && (!unload))
|
||||
return;
|
||||
|
||||
_data = new FGNasalModelData(_root, path, prop, load, unload, branch);
|
||||
|
||||
// register Nasal module to be created and loaded in the main thread.
|
||||
nasalSys->registerToLoad(_data);
|
||||
}
|
||||
104
src/Scripting/NasalModelData.hxx
Normal file
104
src/Scripting/NasalModelData.hxx
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2013 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef NASAL_MODEL_DATA_HXX
|
||||
#define NASAL_MODEL_DATA_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/scene/model/modellib.hxx>
|
||||
|
||||
class FGNasalModelData;
|
||||
typedef SGSharedPtr<FGNasalModelData> FGNasalModelDataRef;
|
||||
typedef std::list<FGNasalModelData*> FGNasalModelDataList;
|
||||
|
||||
/** Nasal model data container.
|
||||
* load and unload methods must be run in main thread (not thread-safe). */
|
||||
class FGNasalModelData : public SGReferenced
|
||||
{
|
||||
public:
|
||||
/** Constructor to be run in an arbitrary thread. */
|
||||
FGNasalModelData( SGPropertyNode *root,
|
||||
const std::string& path,
|
||||
SGPropertyNode *prop,
|
||||
SGPropertyNode* load,
|
||||
SGPropertyNode* unload,
|
||||
osg::Node* branch );
|
||||
|
||||
~FGNasalModelData();
|
||||
|
||||
/** Load hook. Always call from inside the main loop. */
|
||||
void load();
|
||||
|
||||
/** Unload hook. Always call from inside the main loop. */
|
||||
void unload();
|
||||
|
||||
/**
|
||||
* Get osg scenegraph node of model
|
||||
*/
|
||||
osg::Node* getNode();
|
||||
|
||||
/**
|
||||
* Get FGNasalModelData for model with the given module id. Every scenery
|
||||
* model containing a nasal load or unload tag gets assigned a module id
|
||||
* automatically.
|
||||
*
|
||||
* @param id Module id
|
||||
* @return model data or NULL if does not exists
|
||||
*/
|
||||
static FGNasalModelData* getByModuleId(unsigned int id);
|
||||
|
||||
private:
|
||||
static unsigned int _max_module_id;
|
||||
static FGNasalModelDataList _loaded_models;
|
||||
|
||||
std::string _module, _path;
|
||||
SGPropertyNode_ptr _root, _prop;
|
||||
SGConstPropertyNode_ptr _load, _unload;
|
||||
osg::ref_ptr<osg::Node> _branch;
|
||||
unsigned int _module_id;
|
||||
};
|
||||
|
||||
/** Thread-safe proxy for FGNasalModelData.
|
||||
* modelLoaded/destroy methods only register the requested
|
||||
* operation. Actual (un)loading of Nasal module is deferred
|
||||
* and done in the main loop. */
|
||||
class FGNasalModelDataProxy : public simgear::SGModelData
|
||||
{
|
||||
public:
|
||||
FGNasalModelDataProxy(SGPropertyNode *root = 0) :
|
||||
_root(root), _data(0)
|
||||
{
|
||||
}
|
||||
|
||||
~FGNasalModelDataProxy();
|
||||
|
||||
void modelLoaded( const std::string& path,
|
||||
SGPropertyNode *prop,
|
||||
osg::Node *branch );
|
||||
|
||||
virtual FGNasalModelDataProxy* clone() const { return new FGNasalModelDataProxy(_root); }
|
||||
|
||||
ErrorContext getErrorContext() const override
|
||||
{
|
||||
return {}; // return nothing for now, not yet clear if this proxy needs it
|
||||
}
|
||||
|
||||
protected:
|
||||
SGPropertyNode_ptr _root;
|
||||
FGNasalModelDataRef _data;
|
||||
};
|
||||
|
||||
#endif // of NASAL_MODEL_DATA_HXX
|
||||
1662
src/Scripting/NasalPositioned.cxx
Normal file
1662
src/Scripting/NasalPositioned.cxx
Normal file
File diff suppressed because it is too large
Load Diff
52
src/Scripting/NasalPositioned.hxx
Normal file
52
src/Scripting/NasalPositioned.hxx
Normal file
@@ -0,0 +1,52 @@
|
||||
// NasalPositioned.hxx -- expose FGPositioned classes to Nasal
|
||||
//
|
||||
// Written by James Turner, started 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_POSITIONED_HXX
|
||||
#define SCRIPTING_NASAL_POSITIONED_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
#include <Navaids/positioned.hxx>
|
||||
|
||||
|
||||
// forward decls
|
||||
class SGGeod;
|
||||
class FGRunway;
|
||||
class FGAirport;
|
||||
|
||||
bool geodFromHash(naRef ref, SGGeod& result);
|
||||
|
||||
FGAirport* airportGhost(naRef r);
|
||||
FGRunway* runwayGhost(naRef r);
|
||||
|
||||
naRef ghostForPositioned(naContext c, FGPositionedRef pos);
|
||||
naRef ghostForRunway(naContext c, const FGRunway* r);
|
||||
naRef ghostForAirport(naContext c, const FGAirport* apt);
|
||||
|
||||
FGPositioned* positionedGhost(naRef r);
|
||||
FGPositionedRef positionedFromArg(naRef ref);
|
||||
int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result);
|
||||
|
||||
naRef initNasalPositioned(naRef globals, naContext c);
|
||||
naRef initNasalPositioned_cppbind(naRef globals, naContext c);
|
||||
void postinitNasalPositioned(naRef globals, naContext c);
|
||||
void shutdownNasalPositioned();
|
||||
|
||||
#endif // of SCRIPTING_NASAL_POSITIONED_HXX
|
||||
536
src/Scripting/NasalPositioned_cppbind.cxx
Normal file
536
src/Scripting/NasalPositioned_cppbind.cxx
Normal file
@@ -0,0 +1,536 @@
|
||||
// NasalPositioned_cppbind.cxx -- expose FGPositioned classes to Nasal
|
||||
//
|
||||
// Port of NasalPositioned.cpp to the new nasal/cppbind helpers. Will replace
|
||||
// old NasalPositioned.cpp once finished.
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalPositioned.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <simgear/misc/ListDiff.hxx>
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Airports/dynamics.hxx>
|
||||
#include <Airports/pavement.hxx>
|
||||
#include <ATC/CommStation.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/fix.hxx>
|
||||
|
||||
typedef nasal::Ghost<FGPositionedRef> NasalPositioned;
|
||||
typedef nasal::Ghost<FGRunwayRef> NasalRunway;
|
||||
typedef nasal::Ghost<FGParkingRef> NasalParking;
|
||||
typedef nasal::Ghost<FGAirportRef> NasalAirport;
|
||||
typedef nasal::Ghost<flightgear::CommStationRef> NasalCommStation;
|
||||
typedef nasal::Ghost<FGNavRecordRef> NasalNavRecord;
|
||||
typedef nasal::Ghost<FGRunwayRef> NasalRunway;
|
||||
typedef nasal::Ghost<FGFixRef> NasalFix;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef to_nasal_helper(naContext c, flightgear::SID* sid)
|
||||
{
|
||||
// TODO SID ghost
|
||||
return nasal::to_nasal(c, sid->ident());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef to_nasal_helper(naContext c, flightgear::STAR* star)
|
||||
{
|
||||
// TODO STAR ghost
|
||||
return nasal::to_nasal(c, star->ident());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef to_nasal_helper(naContext c, flightgear::Approach* iap)
|
||||
{
|
||||
// TODO Approach ghost
|
||||
return nasal::to_nasal(c, iap->ident());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_navaid_course(FGNavRecord& nav, naContext)
|
||||
{
|
||||
if( !( nav.type() == FGPositioned::ILS
|
||||
|| nav.type() == FGPositioned::LOC
|
||||
) )
|
||||
return naNil();
|
||||
|
||||
double radial = nav.get_multiuse();
|
||||
return naNum(SGMiscd::normalizePeriodic(0.5, 360.5, radial));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static FGRunwayBaseRef f_airport_runway(FGAirport& apt, const std::string& ident)
|
||||
{
|
||||
const std::string Id = simgear::strutils::uppercase (ident);
|
||||
|
||||
if( apt.hasRunwayWithIdent(Id) )
|
||||
return apt.getRunwayByIdent(Id);
|
||||
else if( apt.hasHelipadWithIdent(Id) )
|
||||
return apt.getHelipadByIdent(Id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
template<class T, class C1, class C2>
|
||||
std::vector<T> extract( const std::vector<C1>& in,
|
||||
T (C2::*getter)() const )
|
||||
{
|
||||
std::vector<T> ret(in.size());
|
||||
std::transform(in.begin(), in.end(), ret.begin(), [getter](const C1& c)
|
||||
{ return (c->*getter)(); }
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_airport_comms(FGAirport& apt, const nasal::CallContext& ctx)
|
||||
{
|
||||
FGPositioned::Type comm_type =
|
||||
FGPositioned::typeFromName( ctx.getArg<std::string>(0) );
|
||||
|
||||
// if we have an explicit type, return a simple vector of frequencies
|
||||
if( comm_type != FGPositioned::INVALID )
|
||||
return ctx.to_nasal
|
||||
(
|
||||
extract( apt.commStationsOfType(comm_type),
|
||||
&flightgear::CommStation::freqMHz )
|
||||
);
|
||||
else
|
||||
// otherwise return a vector of ghosts, one for each comm station.
|
||||
return ctx.to_nasal(apt.commStations());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGRunwayRef runwayFromNasalArg( const FGAirport& apt,
|
||||
const nasal::CallContext& ctx,
|
||||
size_t index = 0 )
|
||||
{
|
||||
if( index >= ctx.argc )
|
||||
return FGRunwayRef();
|
||||
|
||||
try
|
||||
{
|
||||
std::string ident = ctx.getArg<std::string>(index);
|
||||
if( !ident.empty() )
|
||||
{
|
||||
if( !apt.hasRunwayWithIdent(ident) )
|
||||
// TODO warning/exception?
|
||||
return FGRunwayRef();
|
||||
|
||||
return apt.getRunwayByIdent(ident);
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{}
|
||||
|
||||
// TODO warn/error if no runway?
|
||||
return ctx.from_nasal<FGRunwayRef>(ctx.args[index]);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_airport_sids(FGAirport& apt, const nasal::CallContext& ctx)
|
||||
{
|
||||
FGRunway* rwy = runwayFromNasalArg(apt, ctx);
|
||||
return ctx.to_nasal
|
||||
(
|
||||
extract(rwy ? rwy->getSIDs() : apt.getSIDs(), &flightgear::SID::ident)
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_airport_stars(FGAirport& apt, const nasal::CallContext& ctx)
|
||||
{
|
||||
FGRunway* rwy = runwayFromNasalArg(apt, ctx);
|
||||
return ctx.to_nasal
|
||||
(
|
||||
extract(rwy ? rwy->getSTARs() : apt.getSTARs(), &flightgear::STAR::ident)
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_airport_approaches(FGAirport& apt, const nasal::CallContext& ctx)
|
||||
{
|
||||
FGRunway* rwy = runwayFromNasalArg(apt, ctx);
|
||||
|
||||
flightgear::ProcedureType type = flightgear::PROCEDURE_INVALID;
|
||||
const std::string type_str = simgear::strutils::uppercase (ctx.getArg<std::string>(1));
|
||||
if( !type_str.empty() )
|
||||
{
|
||||
if( type_str == "NDB" ) type = flightgear::PROCEDURE_APPROACH_NDB;
|
||||
else if( type_str == "VOR" ) type = flightgear::PROCEDURE_APPROACH_VOR;
|
||||
else if( type_str == "ILS" ) type = flightgear::PROCEDURE_APPROACH_ILS;
|
||||
else if( type_str == "RNAV") type = flightgear::PROCEDURE_APPROACH_RNAV;
|
||||
}
|
||||
|
||||
return ctx.to_nasal
|
||||
(
|
||||
extract( rwy ? rwy->getApproaches(type)
|
||||
// no runway specified, report them all
|
||||
: apt.getApproaches(type),
|
||||
&flightgear::Approach::ident )
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static FGParkingList
|
||||
f_airport_parking(FGAirport& apt, nasal::CallContext ctx)
|
||||
{
|
||||
std::string type = ctx.getArg<std::string>(0);
|
||||
bool only_available = ctx.getArg<bool>(1);
|
||||
FGAirportDynamicsRef dynamics = apt.getDynamics();
|
||||
return dynamics->getParkings(only_available, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a SGGeod from a nasal function argument list.
|
||||
*
|
||||
* <lat>, <lon>
|
||||
* {"lat": <lat-deg>, "lon": <lon-deg>}
|
||||
* geo.Coord.new() (aka. {"_lat": <lat-rad>, "_lon": <lon-rad>})
|
||||
*/
|
||||
static bool extractGeod(nasal::CallContext& ctx, SGGeod& result)
|
||||
{
|
||||
if( !ctx.argc )
|
||||
return false;
|
||||
|
||||
if( ctx.isGhost(0) )
|
||||
{
|
||||
FGPositionedRef pos =
|
||||
ctx.from_nasal<FGPositionedRef>(ctx.requireArg<naRef>(0));
|
||||
|
||||
if( pos )
|
||||
{
|
||||
result = pos->geod();
|
||||
ctx.popFront();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if( ctx.isHash(0) )
|
||||
{
|
||||
nasal::Hash pos_hash = ctx.requireArg<nasal::Hash>(0);
|
||||
|
||||
// check for manual latitude / longitude names
|
||||
naRef lat = pos_hash.get("lat"),
|
||||
lon = pos_hash.get("lon");
|
||||
if( naIsNum(lat) && naIsNum(lon) )
|
||||
{
|
||||
result = SGGeod::fromDeg( ctx.from_nasal<double>(lon),
|
||||
ctx.from_nasal<double>(lat) );
|
||||
ctx.popFront();
|
||||
return true;
|
||||
}
|
||||
|
||||
// geo.Coord uses _lat/_lon in radians
|
||||
// TODO should we check if its really a geo.Coord?
|
||||
lat = pos_hash.get("_lat");
|
||||
lon = pos_hash.get("_lon");
|
||||
if( naIsNum(lat) && naIsNum(lon) )
|
||||
{
|
||||
result = SGGeod::fromRad( ctx.from_nasal<double>(lon),
|
||||
ctx.from_nasal<double>(lat) );
|
||||
ctx.popFront();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if( ctx.isNumeric(0) && ctx.isNumeric(1) )
|
||||
{
|
||||
// lat, lon
|
||||
result = SGGeod::fromDeg( ctx.requireArg<double>(1),
|
||||
ctx.requireArg<double>(0) );
|
||||
ctx.popFront(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract position from ctx or return current aircraft position if not given.
|
||||
*/
|
||||
static SGGeod getPosition(nasal::CallContext& ctx)
|
||||
{
|
||||
SGGeod pos;
|
||||
if( !extractGeod(ctx, pos) )
|
||||
pos = globals->get_aircraft_position();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Returns Nasal ghost for particular or nearest airport of a <type>, or nil
|
||||
// on error.
|
||||
//
|
||||
// airportinfo(<id>); e.g. "KSFO"
|
||||
// airportinfo(<type>); type := ("airport"|"seaport"|"heliport")
|
||||
// airportinfo() same as airportinfo("airport")
|
||||
// airportinfo(<lat>, <lon> [, <type>]);
|
||||
static naRef f_airportinfo(nasal::CallContext ctx)
|
||||
{
|
||||
SGGeod pos = getPosition(ctx);
|
||||
|
||||
if( ctx.argc > 1 )
|
||||
ctx.runtimeError("airportinfo() with invalid function arguments");
|
||||
|
||||
// optional type/ident
|
||||
std::string ident("airport");
|
||||
if( ctx.isString(0) )
|
||||
ident = ctx.requireArg<std::string>(0);
|
||||
|
||||
FGAirport::TypeRunwayFilter filter;
|
||||
if( !filter.fromTypeString(ident) )
|
||||
// user provided an <id>, hopefully
|
||||
return ctx.to_nasal(FGAirport::findByIdent(ident));
|
||||
|
||||
double maxRange = 10000.0; // expose this? or pick a smaller value?
|
||||
return ctx.to_nasal( FGAirport::findClosest(pos, maxRange, &filter) );
|
||||
}
|
||||
|
||||
/**
|
||||
* findAirportsWithinRange([<position>,] <range-nm> [, type])
|
||||
*/
|
||||
static naRef f_findAirportsWithinRange(nasal::CallContext ctx)
|
||||
{
|
||||
SGGeod pos = getPosition(ctx);
|
||||
double range_nm = ctx.requireArg<double>(0);
|
||||
|
||||
FGAirport::TypeRunwayFilter filter; // defaults to airports only
|
||||
filter.fromTypeString( ctx.getArg<std::string>(1) );
|
||||
|
||||
FGPositionedList apts = FGPositioned::findWithinRange(pos, range_nm, &filter);
|
||||
FGPositioned::sortByRange(apts, pos);
|
||||
|
||||
return ctx.to_nasal(apts);
|
||||
}
|
||||
|
||||
/**
|
||||
* findAirportsByICAO(<ident/prefix> [, type])
|
||||
*/
|
||||
static naRef f_findAirportsByICAO(nasal::CallContext ctx)
|
||||
{
|
||||
std::string prefix = ctx.requireArg<std::string>(0);
|
||||
|
||||
FGAirport::TypeRunwayFilter filter; // defaults to airports only
|
||||
filter.fromTypeString( ctx.getArg<std::string>(1) );
|
||||
|
||||
return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, false) );
|
||||
}
|
||||
|
||||
// Returns vector of data hash for navaid of a <type>, nil on error
|
||||
// navaids sorted by ascending distance
|
||||
// navinfo([<lat>,<lon>],[<type>],[<id>])
|
||||
// lat/lon (numeric): use latitude/longitude instead of ac position
|
||||
// type: ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
|
||||
// id: (partial) id of the fix
|
||||
// examples:
|
||||
// navinfo("vor") returns all vors
|
||||
// navinfo("HAM") return all navaids who's name start with "HAM"
|
||||
// navinfo("vor", "HAM") return all vor who's name start with "HAM"
|
||||
//navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM"
|
||||
// sorted by distance relative to lat=34, lon=48
|
||||
static naRef f_navinfo(nasal::CallContext ctx)
|
||||
{
|
||||
SGGeod pos = getPosition(ctx);
|
||||
std::string id = ctx.getArg<std::string>(0);
|
||||
|
||||
FGNavList::TypeFilter filter;
|
||||
if( filter.fromTypeString(id) )
|
||||
id = ctx.getArg<std::string>(1);
|
||||
else if( ctx.argc > 1 )
|
||||
ctx.runtimeError("navinfo() already got an ident");
|
||||
|
||||
return ctx.to_nasal( FGNavList::findByIdentAndFreq(pos, id, 0.0, &filter) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static naRef f_findWithinRange(nasal::CallContext ctx)
|
||||
{
|
||||
SGGeod pos = getPosition(ctx);
|
||||
double range_nm = ctx.requireArg<double>(0);
|
||||
|
||||
std::string typeSpec = ctx.getArg<std::string>(1);
|
||||
FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
|
||||
|
||||
FGPositionedList items = FGPositioned::findWithinRange(pos, range_nm, &filter);
|
||||
FGPositioned::sortByRange(items, pos);
|
||||
return ctx.to_nasal(items);
|
||||
}
|
||||
|
||||
static naRef f_findByIdent(nasal::CallContext ctx)
|
||||
{
|
||||
std::string prefix = ctx.requireArg<std::string>(0);
|
||||
std::string typeSpec = ctx.getArg<std::string>(1);
|
||||
FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
|
||||
bool exact = ctx.getArg<bool>(2, false);
|
||||
|
||||
return ctx.to_nasal( FGPositioned::findAllWithIdent(prefix, &filter, exact) );
|
||||
}
|
||||
|
||||
static naRef f_findByName(nasal::CallContext ctx)
|
||||
{
|
||||
std::string prefix = ctx.requireArg<std::string>(0);
|
||||
std::string typeSpec = ctx.getArg<std::string>(1);
|
||||
FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec));
|
||||
|
||||
return ctx.to_nasal( FGPositioned::findAllWithName(prefix, &filter, false) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static naRef f_courseAndDistance(nasal::CallContext ctx)
|
||||
{
|
||||
SGGeod from = globals->get_aircraft_position(), to, pos;
|
||||
bool ok = extractGeod(ctx, pos);
|
||||
if (!ok) {
|
||||
ctx.runtimeError("invalid arguments to courseAndDistance");
|
||||
}
|
||||
|
||||
if (extractGeod(ctx, to)) {
|
||||
from = pos; // we parsed both FROM and TO args, so first was FROM
|
||||
} else {
|
||||
to = pos; // only parsed one arg, so FROM is current
|
||||
}
|
||||
|
||||
double course, course2, d;
|
||||
SGGeodesy::inverse(from, to, course, course2, d);
|
||||
|
||||
return ctx.to_nasal_vec(course, d * SG_METER_TO_NM);
|
||||
}
|
||||
|
||||
static naRef f_sortByRange(nasal::CallContext ctx)
|
||||
{
|
||||
FGPositionedList items = ctx.requireArg<FGPositionedList>(0);
|
||||
ctx.popFront();
|
||||
FGPositioned::sortByRange(items, getPosition(ctx));
|
||||
return ctx.to_nasal(items);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Get difference between two lists of positioned objects.
|
||||
//
|
||||
// For every element in old_list not in new_list the callback cb_remove is
|
||||
// called with the removed element as single argument. For every element in
|
||||
// new_list not in old_list cb_add is called.
|
||||
//
|
||||
// diff(old_list, new_list, cb_add[, cb_remove])
|
||||
//
|
||||
// example:
|
||||
// # Print all fixes within a distance of 320 to 640 miles
|
||||
// diff( findWithinRange(320, "fix"),
|
||||
// findWithinRange(640, "fix"),
|
||||
// func(p) print('found fix: ', p.id) );
|
||||
static naRef f_diff(nasal::CallContext ctx)
|
||||
{
|
||||
typedef simgear::ListDiff<FGPositionedRef> Diff;
|
||||
Diff::List old_list = ctx.requireArg<FGPositionedList>(0),
|
||||
new_list = ctx.requireArg<FGPositionedList>(1);
|
||||
Diff::Callback cb_add = ctx.requireArg<Diff::Callback>(2),
|
||||
cb_rm = ctx.getArg<Diff::Callback>(3);
|
||||
|
||||
// Note that FGPositionedRef instances are only compared for pointer equality.
|
||||
// As the NavCache caches every queried positioned instance it is guaranteed
|
||||
// that only one instance of every positioned object can exist. Therefore we
|
||||
// can make the comparison faster by just comparing pointers and not also the
|
||||
// guid.
|
||||
// (On my machine the difference is 0.27s vs 0.17s)
|
||||
Diff::inplace(old_list, new_list, cb_add, cb_rm);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalPositioned_cppbind(naRef globalsRef, naContext c)
|
||||
{
|
||||
NasalPositioned::init("Positioned")
|
||||
.member("id", &FGPositioned::ident)
|
||||
.member("ident", &FGPositioned::ident) // TODO to we really need id and ident?
|
||||
.member("name", &FGPositioned::name)
|
||||
.member("type", &FGPositioned::typeString)
|
||||
.member("lat", &FGPositioned::latitude)
|
||||
.member("lon", &FGPositioned::longitude)
|
||||
.member("elevation", &FGPositioned::elevationM);
|
||||
NasalRunway::init("Runway")
|
||||
.bases<NasalPositioned>();
|
||||
NasalParking::init("Parking")
|
||||
.bases<NasalPositioned>();
|
||||
NasalCommStation::init("CommStation")
|
||||
.bases<NasalPositioned>()
|
||||
.member("frequency", &flightgear::CommStation::freqMHz);
|
||||
NasalNavRecord::init("Navaid")
|
||||
.bases<NasalPositioned>()
|
||||
.member("frequency", &FGNavRecord::get_freq)
|
||||
.member("range_nm", &FGNavRecord::get_range)
|
||||
.member("course", &f_navaid_course)
|
||||
.member("magvar", &FGNavRecord::get_multiuse)
|
||||
.member("dme", &FGNavRecord::hasDME)
|
||||
.member("vorac", &FGNavRecord::isVORTAC);
|
||||
|
||||
NasalFix::init("Fix")
|
||||
.bases<NasalPositioned>();
|
||||
|
||||
NasalAirport::init("FGAirport")
|
||||
.bases<NasalPositioned>()
|
||||
.member("has_metar", &FGAirport::getMetar)
|
||||
.member("runways", &FGAirport::getRunwayMap)
|
||||
.member("helipads", &FGAirport::getHelipadMap)
|
||||
.member("taxiways", &FGAirport::getTaxiways)
|
||||
.member("pavements", &FGAirport::getPavements)
|
||||
.method("runway", &f_airport_runway)
|
||||
.method("helipad", &f_airport_runway)
|
||||
.method("tower", &FGAirport::getTowerLocation)
|
||||
.method("comms", &f_airport_comms)
|
||||
.method("sids", &f_airport_sids)
|
||||
.method("stars", &f_airport_stars)
|
||||
.method("getApproachList", f_airport_approaches)
|
||||
.method("parking", &f_airport_parking)
|
||||
.method("getSid", &FGAirport::findSIDWithIdent)
|
||||
.method("getStar", &FGAirport::findSTARWithIdent)
|
||||
.method("getIAP", &FGAirport::findApproachWithIdent)
|
||||
.method("tostring", &FGAirport::toString);
|
||||
|
||||
nasal::Hash globals(globalsRef, c),
|
||||
positioned( globals.createHash("positioned") );
|
||||
|
||||
positioned.set("airportinfo", &f_airportinfo);
|
||||
positioned.set("findAirportsWithinRange", f_findAirportsWithinRange);
|
||||
positioned.set("findAirportsByICAO", &f_findAirportsByICAO);
|
||||
positioned.set("navinfo", &f_navinfo);
|
||||
|
||||
positioned.set("findWithinRange", &f_findWithinRange);
|
||||
positioned.set("findByIdent", &f_findByIdent);
|
||||
positioned.set("findByName", &f_findByName);
|
||||
positioned.set("courseAndDistance", &f_courseAndDistance);
|
||||
positioned.set("sortByRange", &f_sortByRange);
|
||||
|
||||
positioned.set("diff", &f_diff);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
152
src/Scripting/NasalSGPath.cxx
Normal file
152
src/Scripting/NasalSGPath.cxx
Normal file
@@ -0,0 +1,152 @@
|
||||
// Expose SGPath module to Nasal
|
||||
//
|
||||
// Copyright (C) 2013 The FlightGear Community
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalSGPath.hxx"
|
||||
#include <Main/globals.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
typedef std::shared_ptr<SGPath> SGPathRef;
|
||||
typedef nasal::Ghost<SGPathRef> NasalSGPath;
|
||||
|
||||
SGPath::Permissions checkIORules(const SGPath& path)
|
||||
{
|
||||
SGPath::Permissions perm;
|
||||
if (!path.isAbsolute()) {
|
||||
// SGPath caches permissions, which breaks for relative paths
|
||||
// if the current directory changes
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "os.path: file operation on '" <<
|
||||
path<< "' access denied (relative paths not accepted; use "
|
||||
"realpath() to make a path absolute)");
|
||||
}
|
||||
|
||||
perm.read = path.isAbsolute() && !SGPath(path).validate(false).isNull();
|
||||
perm.write = path.isAbsolute() && !SGPath(path).validate(true).isNull();
|
||||
|
||||
return perm;
|
||||
}
|
||||
|
||||
// TODO make exposing such function easier...
|
||||
static naRef validatedPathToNasal( const nasal::CallContext& ctx,
|
||||
const SGPath& p )
|
||||
{
|
||||
return ctx.to_nasal( SGPathRef(new SGPath(p.utf8Str(), &checkIORules)) );
|
||||
}
|
||||
|
||||
/**
|
||||
* os.path.new()
|
||||
*/
|
||||
static naRef f_new_path(const nasal::CallContext& ctx)
|
||||
{
|
||||
return validatedPathToNasal(ctx, SGPath(ctx.getArg<std::string>(0)));
|
||||
}
|
||||
|
||||
static int f_path_create_dir(SGPath& p, const nasal::CallContext& ctx)
|
||||
{
|
||||
// limit setable access rights for Nasal
|
||||
return p.create_dir(ctx.getArg<mode_t>(0, 0755) & 0775);
|
||||
}
|
||||
|
||||
static void f_path_set(SGPath& p, const nasal::CallContext& ctx)
|
||||
{
|
||||
p = SGPath::fromUtf8(ctx.getArg<std::string>(0), p.getPermissionChecker());
|
||||
}
|
||||
|
||||
/**
|
||||
* os.path.desktop()
|
||||
*/
|
||||
static naRef f_desktop(const nasal::CallContext& ctx)
|
||||
{
|
||||
return validatedPathToNasal(ctx, SGPath::desktop(SGPath(&checkIORules)));
|
||||
}
|
||||
|
||||
/**
|
||||
* os.path.standardLocation(type)
|
||||
*/
|
||||
static naRef f_standardLocation(const nasal::CallContext& ctx)
|
||||
{
|
||||
const std::string type_str = ctx.requireArg<std::string>(0);
|
||||
SGPath::StandardLocation type = SGPath::HOME;
|
||||
if( type_str == "DESKTOP" )
|
||||
type = SGPath::DESKTOP;
|
||||
else if( type_str == "DOWNLOADS" )
|
||||
type = SGPath::DOWNLOADS;
|
||||
else if( type_str == "DOCUMENTS" )
|
||||
type = SGPath::DOCUMENTS;
|
||||
else if( type_str == "PICTURES" )
|
||||
type = SGPath::PICTURES;
|
||||
else if( type_str != "HOME" )
|
||||
ctx.runtimeError
|
||||
(
|
||||
"os.path.standardLocation: unknown type %s", type_str.c_str()
|
||||
);
|
||||
|
||||
return validatedPathToNasal(ctx, SGPath::standardLocation(type));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalSGPath(naRef globals, naContext c)
|
||||
{
|
||||
// This wraps most of the SGPath APIs for use by Nasal
|
||||
// See: http://docs.freeflightsim.org/simgear/classSGPath.html
|
||||
|
||||
NasalSGPath::init("os.path")
|
||||
.method("set", &f_path_set)
|
||||
.method("append", &SGPath::append)
|
||||
.method("concat", &SGPath::concat)
|
||||
|
||||
.member("realpath", &SGPath::realpath)
|
||||
.member("file", &SGPath::file)
|
||||
.member("dir", &SGPath::dir)
|
||||
.member("base", &SGPath::base)
|
||||
.member("file_base", &SGPath::file_base)
|
||||
.member("extension", &SGPath::extension)
|
||||
.member("lower_extension", &SGPath::lower_extension)
|
||||
.member("complete_lower_extension", &SGPath::complete_lower_extension)
|
||||
.member("str", &SGPath::utf8Str)
|
||||
.member("mtime", &SGPath::modTime)
|
||||
|
||||
.method("exists", &SGPath::exists)
|
||||
.method("canRead", &SGPath::canRead)
|
||||
.method("canWrite", &SGPath::canWrite)
|
||||
.method("isFile", &SGPath::isFile)
|
||||
.method("isDir", &SGPath::isDir)
|
||||
.method("isRelative", &SGPath::isRelative)
|
||||
.method("isAbsolute", &SGPath::isAbsolute)
|
||||
.method("isNull", &SGPath::isNull)
|
||||
|
||||
.method("create_dir", &f_path_create_dir)
|
||||
.method("remove", &SGPath::remove)
|
||||
.method("rename", &SGPath::rename);
|
||||
|
||||
nasal::Hash globals_module(globals, c),
|
||||
path = globals_module.createHash("os")
|
||||
.createHash("path");
|
||||
|
||||
path.set("new", f_new_path);
|
||||
path.set("desktop", &f_desktop);
|
||||
path.set("standardLocation", &f_standardLocation);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
26
src/Scripting/NasalSGPath.hxx
Normal file
26
src/Scripting/NasalSGPath.hxx
Normal file
@@ -0,0 +1,26 @@
|
||||
//@file Expose SGPath module to Nasal
|
||||
//
|
||||
// Copyright (C) 2013 The FlightGear Community
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_SGPATH_HXX
|
||||
#define SCRIPTING_NASAL_SGPATH_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
naRef initNasalSGPath(naRef globals, naContext c);
|
||||
|
||||
#endif // of SCRIPTING_NASAL_SGPATH_HXX
|
||||
140
src/Scripting/NasalString.cxx
Normal file
140
src/Scripting/NasalString.cxx
Normal file
@@ -0,0 +1,140 @@
|
||||
// Add (std::string) like methods to Nasal strings
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "NasalString.hxx"
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalString.hxx>
|
||||
|
||||
/**
|
||||
* Compare (sub)string with other string
|
||||
*
|
||||
* compare(s)
|
||||
* compare(pos, len, s)
|
||||
*/
|
||||
static naRef f_compare(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
rhs = ctx.requireArg<nasal::String>(ctx.argc > 1 ? 2 : 0);
|
||||
size_t pos = ctx.argc > 1 ? ctx.requireArg<int>(1) : 0;
|
||||
size_t len = ctx.argc > 1 ? ctx.requireArg<int>(2) : 0;
|
||||
|
||||
if( len == 0 )
|
||||
len = nasal::String::npos;
|
||||
|
||||
return naNum( str.compare(pos, len, rhs) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether string starts with other string
|
||||
*/
|
||||
static naRef f_starts_with(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
rhs = ctx.requireArg<nasal::String>(0);
|
||||
|
||||
return naNum( str.starts_with(rhs) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether string ends with other string
|
||||
*/
|
||||
static naRef f_ends_with(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
rhs = ctx.requireArg<nasal::String>(0);
|
||||
|
||||
return naNum( str.ends_with(rhs) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to convert size_t position/npos to Nasal conventions (-1 == npos)
|
||||
*/
|
||||
naRef pos_to_nasal(size_t pos)
|
||||
{
|
||||
if( pos == nasal::String::npos )
|
||||
return naNum(-1);
|
||||
else
|
||||
return naNum(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first occurrence of single character
|
||||
*
|
||||
* find(c, pos = 0)
|
||||
*/
|
||||
static naRef f_find(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
find = ctx.requireArg<nasal::String>(0);
|
||||
size_t pos = ctx.getArg<int>(1, 0);
|
||||
|
||||
if( find.size() != 1 )
|
||||
ctx.runtimeError("string::find: single character expected");
|
||||
|
||||
return pos_to_nasal( str.find(*find.c_str(), pos) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first character of a string occurring in this string
|
||||
*
|
||||
* find_first_of(search, pos = 0)
|
||||
*/
|
||||
static naRef f_find_first_of(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
find = ctx.requireArg<nasal::String>(0);
|
||||
size_t pos = ctx.getArg<int>(1, 0);
|
||||
|
||||
return pos_to_nasal( str.find_first_of(find, pos) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first character of this string not occurring in the other string
|
||||
*
|
||||
* find_first_not_of(search, pos = 0)
|
||||
*/
|
||||
static naRef f_find_first_not_of(const nasal::CallContext& ctx)
|
||||
{
|
||||
nasal::String str = ctx.from_nasal<nasal::String>(ctx.me),
|
||||
find = ctx.requireArg<nasal::String>(0);
|
||||
size_t pos = ctx.getArg<int>(1, 0);
|
||||
|
||||
return pos_to_nasal( str.find_first_not_of(find, pos) );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalString(naRef globals, naRef string, naContext c)
|
||||
{
|
||||
nasal::Hash string_module(string, c);
|
||||
|
||||
string_module.set("compare", f_compare);
|
||||
string_module.set("starts_with", f_starts_with);
|
||||
string_module.set("ends_with", f_ends_with);
|
||||
string_module.set("find", f_find);
|
||||
string_module.set("find_first_of", f_find_first_of);
|
||||
string_module.set("find_first_not_of", f_find_first_not_of);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
27
src/Scripting/NasalString.hxx
Normal file
27
src/Scripting/NasalString.hxx
Normal file
@@ -0,0 +1,27 @@
|
||||
// Add (std::string) like methods to Nasal strings
|
||||
//
|
||||
// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_STRING_HXX
|
||||
#define SCRIPTING_NASAL_STRING_HXX
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
naRef initNasalString(naRef globals, naRef string, naContext c);
|
||||
|
||||
#endif // of SCRIPTING_NASAL_STRING_HXX
|
||||
|
||||
2075
src/Scripting/NasalSys.cxx
Normal file
2075
src/Scripting/NasalSys.cxx
Normal file
File diff suppressed because it is too large
Load Diff
256
src/Scripting/NasalSys.hxx
Normal file
256
src/Scripting/NasalSys.hxx
Normal file
@@ -0,0 +1,256 @@
|
||||
#ifndef __NASALSYS_HXX
|
||||
#define __NASALSYS_HXX
|
||||
|
||||
#include <simgear/math/SGMath.hxx> // keep before any cppbind include to enable
|
||||
// SGVec2<T> conversion.
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
|
||||
// Required only for MSVC
|
||||
#ifdef _MSC_VER
|
||||
# include <Scripting/NasalModelData.hxx>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class FGNasalScript;
|
||||
class FGNasalListener;
|
||||
class SGCondition;
|
||||
class FGNasalModelData;
|
||||
class NasalCommand;
|
||||
class FGNasalModuleListener;
|
||||
struct NasalTimer; ///< timer created by settimer
|
||||
class TimerObj; ///< persistent timer created by maketimer
|
||||
|
||||
namespace simgear { class BufferedLogCallback; }
|
||||
|
||||
SGPropertyNode* ghostToPropNode(naRef ref);
|
||||
|
||||
class FGNasalSys : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
FGNasalSys();
|
||||
virtual ~FGNasalSys();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void shutdown() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "nasal"; }
|
||||
|
||||
// Loads a nasal script from an external file and inserts it as a
|
||||
// global module of the specified name.
|
||||
bool loadModule(SGPath file, const char* moduleName);
|
||||
|
||||
// Simple hook to run arbitrary source code. Returns a bool to
|
||||
// indicate successful execution. Does *not* return any Nasal
|
||||
// values, because handling garbage-collected objects from C space
|
||||
// is deep voodoo and violates the "simple hook" idea.
|
||||
bool parseAndRun(const std::string& source);
|
||||
|
||||
bool parseAndRunWithOutput(const std::string& source,
|
||||
std::string& output,
|
||||
std::string& errors);
|
||||
|
||||
// Implementation of the settimer extension function
|
||||
void setTimer(naContext c, int argc, naRef* args);
|
||||
|
||||
// Implementation of the setlistener extension function
|
||||
naRef setListener(naContext c, int argc, naRef* args);
|
||||
naRef removeListener(naContext c, int argc, naRef* args);
|
||||
|
||||
// Returns a ghost wrapper for the current _cmdArg
|
||||
naRef cmdArgGhost();
|
||||
|
||||
void setCmdArg(SGPropertyNode* aNode);
|
||||
|
||||
/**
|
||||
* create Nasal props.Node for an SGPropertyNode*
|
||||
* This is the actual ghost, wrapped in a Nasal sugar class.
|
||||
*/
|
||||
naRef wrappedPropsNode(SGPropertyNode* aProps);
|
||||
|
||||
// Callbacks for command and timer bindings
|
||||
virtual bool handleCommand( const char* moduleName,
|
||||
const char* fileName,
|
||||
const char* src,
|
||||
const SGPropertyNode* arg = 0,
|
||||
SGPropertyNode* root = 0);
|
||||
virtual bool handleCommand(const SGPropertyNode* arg, SGPropertyNode *root);
|
||||
|
||||
bool createModule(const char* moduleName, const char* fileName,
|
||||
const char* src, int len, const SGPropertyNode* cmdarg=0,
|
||||
int argc=0, naRef*args=0);
|
||||
|
||||
void deleteModule(const char* moduleName);
|
||||
|
||||
naRef getModule(const std::string& moduleName) const;
|
||||
naRef getModule(const char* moduleName);
|
||||
|
||||
bool addCommand(naRef func, const std::string& name);
|
||||
bool removeCommand(const std::string& name);
|
||||
|
||||
/**
|
||||
* Set member of specified hash to given value
|
||||
*/
|
||||
void hashset(naRef hash, const char* key, naRef val);
|
||||
|
||||
/**
|
||||
* Set member of globals hash to given value
|
||||
*/
|
||||
void globalsSet(const char* key, naRef val);
|
||||
|
||||
naRef call(naRef code, int argc, naRef* args, naRef locals);
|
||||
naRef callWithContext(naContext ctx, naRef code, int argc, naRef* args, naRef locals);
|
||||
|
||||
naRef callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals);
|
||||
naRef callMethodWithContext(naContext ctx, naRef code, naRef self, int argc, naRef* args, naRef locals);
|
||||
|
||||
naRef propNodeGhost(SGPropertyNode* handle);
|
||||
|
||||
void registerToLoad(FGNasalModelData* data);
|
||||
void registerToUnload(FGNasalModelData* data);
|
||||
|
||||
// can't call this 'globals' due to naming clash
|
||||
naRef nasalGlobals() const
|
||||
{ return _globals; }
|
||||
|
||||
nasal::Hash getGlobals() const
|
||||
{ return nasal::Hash(_globals, _context); }
|
||||
|
||||
// This mechanism is here to allow naRefs to be passed to
|
||||
// locations "outside" the interpreter. Normally, such a
|
||||
// reference would be garbage collected unexpectedly. By passing
|
||||
// it to gcSave and getting a key/handle, it can be cached in a
|
||||
// globals.__gcsave hash. Be sure to release it with gcRelease
|
||||
// when done.
|
||||
int gcSave(naRef r);
|
||||
void gcRelease(int key);
|
||||
|
||||
/**
|
||||
* Check if IOrules correctly work to limit access from Nasal scripts to the
|
||||
* file system.
|
||||
*
|
||||
* @note Just a simple test is performed to check if access to a path is
|
||||
* possible which should never be possible (The actual path refers to
|
||||
* a file/folder named 'do-not-access' in the file system root).
|
||||
*
|
||||
* @see http://wiki.flightgear.org/IOrules
|
||||
*
|
||||
* @return Whether the check was successful.
|
||||
*/
|
||||
bool checkIOrules();
|
||||
|
||||
/// retrive the associated log object, for displaying log
|
||||
/// output somewhere (a UI, presumably)
|
||||
simgear::BufferedLogCallback* log() const
|
||||
{ return _log.get(); }
|
||||
|
||||
string_list getAndClearErrorList();
|
||||
|
||||
/**
|
||||
@brief Convert the value of an SGPropertyNode to its Nasal representation. Used by
|
||||
props.Node.getValue internally, but exposed here for other use cases which don't want to create
|
||||
a props.Node wrapper each time.
|
||||
*/
|
||||
static naRef getPropertyValue(naContext c, SGPropertyNode* node);
|
||||
|
||||
private:
|
||||
void initLogLevelConstants();
|
||||
|
||||
void loadPropertyScripts();
|
||||
void loadPropertyScripts(SGPropertyNode* n);
|
||||
void loadScriptDirectory(simgear::Dir nasalDir, SGPropertyNode* loadorder,
|
||||
bool excludeUnspecifiedInLoadOrder);
|
||||
void addModule(std::string moduleName, simgear::PathList scripts);
|
||||
static void logError(naContext);
|
||||
naRef parse(naContext ctx, const char* filename, const char* buf, int len,
|
||||
std::string& errors);
|
||||
naRef genPropsModule();
|
||||
|
||||
private:
|
||||
//friend class FGNasalScript;
|
||||
friend class FGNasalListener;
|
||||
friend class FGNasalModuleListener;
|
||||
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _loadList;
|
||||
SGLockedQueue<SGSharedPtr<FGNasalModelData> > _unloadList;
|
||||
// Delay removing items of the _loadList to ensure the are already attached
|
||||
// to the scene graph (eg. enables to retrieve world position in load
|
||||
// callback).
|
||||
bool _delay_load;
|
||||
|
||||
// Listener
|
||||
std::map<int, FGNasalListener *> _listener;
|
||||
std::vector<FGNasalListener *> _dead_listener;
|
||||
|
||||
std::vector<FGNasalModuleListener*> _moduleListeners;
|
||||
|
||||
static int _listenerId;
|
||||
|
||||
bool _inited;
|
||||
naContext _context;
|
||||
naRef _globals,
|
||||
_string;
|
||||
|
||||
SGPropertyNode_ptr _cmdArg;
|
||||
|
||||
std::unique_ptr<simgear::BufferedLogCallback> _log;
|
||||
|
||||
typedef std::map<std::string, NasalCommand*> NasalCommandDict;
|
||||
NasalCommandDict _commands;
|
||||
|
||||
naRef _wrappedNodeFunc;
|
||||
|
||||
// track NasalTimer instances (created via settimer() call) -
|
||||
// this allows us to clean these up on shutdown
|
||||
std::vector<NasalTimer*> _nasalTimers;
|
||||
|
||||
// NasalTimer is a friend to invoke handleTimer and do the actual
|
||||
// dispatch of the settimer-d callback
|
||||
friend NasalTimer;
|
||||
|
||||
void handleTimer(NasalTimer* t);
|
||||
|
||||
// track persistent timers. These are owned from the Nasal side, so we
|
||||
// only track a non-owning reference here.
|
||||
std::vector<TimerObj*> _persistentTimers;
|
||||
|
||||
friend TimerObj;
|
||||
|
||||
void addPersistentTimer(TimerObj* pto);
|
||||
void removePersistentTimer(TimerObj* obj);
|
||||
|
||||
static void logNasalStack(naContext context, string_list& stack);
|
||||
};
|
||||
|
||||
#if 0
|
||||
class FGNasalScript
|
||||
{
|
||||
public:
|
||||
~FGNasalScript() { _nas->gcRelease(_gcKey); }
|
||||
|
||||
bool call() {
|
||||
naRef n = naNil();
|
||||
naCall(_nas->_context, _code, 0, &n, naNil(), naNil());
|
||||
return naGetError(_nas->_context) == 0;
|
||||
}
|
||||
|
||||
FGNasalSys* sys() const { return _nas; }
|
||||
|
||||
private:
|
||||
friend class FGNasalSys;
|
||||
naRef _code;
|
||||
int _gcKey;
|
||||
FGNasalSys* _nas;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // __NASALSYS_HXX
|
||||
97
src/Scripting/NasalSys_private.hxx
Normal file
97
src/Scripting/NasalSys_private.hxx
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 2013 James Turner//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __NASALSYS_PRIVATE_HXX
|
||||
#define __NASALSYS_PRIVATE_HXX
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/xml/easyxml.hxx>
|
||||
|
||||
/**
|
||||
@breif wrapper for naEqual which recursively checks vec/hash equality
|
||||
Probably not very performant.
|
||||
*/
|
||||
int nasalStructEqual(naContext ctx, naRef a, naRef b);
|
||||
|
||||
class FGNasalListener : public SGPropertyChangeListener {
|
||||
public:
|
||||
FGNasalListener(SGPropertyNode* node, naRef code, FGNasalSys* nasal,
|
||||
int key, int id, int init, int type);
|
||||
|
||||
virtual ~FGNasalListener();
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
virtual void childAdded(SGPropertyNode* parent, SGPropertyNode* child);
|
||||
virtual void childRemoved(SGPropertyNode* parent, SGPropertyNode* child);
|
||||
|
||||
private:
|
||||
bool changed(SGPropertyNode* node);
|
||||
void call(SGPropertyNode* which, naRef mode);
|
||||
|
||||
friend class FGNasalSys;
|
||||
SGPropertyNode_ptr _node;
|
||||
naRef _code;
|
||||
int _gcKey;
|
||||
int _id;
|
||||
FGNasalSys* _nas;
|
||||
int _init;
|
||||
int _type;
|
||||
unsigned int _active;
|
||||
bool _dead;
|
||||
long _last_int;
|
||||
double _last_float;
|
||||
std::string _last_string;
|
||||
};
|
||||
|
||||
|
||||
class NasalXMLVisitor : public XMLVisitor {
|
||||
public:
|
||||
NasalXMLVisitor(naContext c, int argc, naRef* args);
|
||||
virtual ~NasalXMLVisitor() { naFreeContext(_c); }
|
||||
|
||||
virtual void startElement(const char* tag, const XMLAttributes& a);
|
||||
virtual void endElement(const char* tag);
|
||||
virtual void data(const char* str, int len);
|
||||
virtual void pi(const char* target, const char* data);
|
||||
|
||||
private:
|
||||
void call(naRef func, int num, naRef a = naNil(), naRef b = naNil());
|
||||
naRef make_string(const char* s, int n = -1);
|
||||
|
||||
naContext _c;
|
||||
naRef _start_element, _end_element, _data, _pi;
|
||||
};
|
||||
|
||||
//
|
||||
// See the implementation of the settimer() extension function for
|
||||
// more notes.
|
||||
//
|
||||
struct NasalTimer
|
||||
{
|
||||
NasalTimer(naRef handler, FGNasalSys* sys);
|
||||
|
||||
void timerExpired();
|
||||
~NasalTimer();
|
||||
|
||||
naRef handler;
|
||||
int gcKey = 0;
|
||||
FGNasalSys* nasal = nullptr;
|
||||
};
|
||||
|
||||
// declare the interface to the unit-testing module
|
||||
naRef initNasalUnitTestCppUnit(naRef globals, naContext c);
|
||||
naRef initNasalUnitTestInSim(naRef globals, naContext c);
|
||||
|
||||
#endif // of __NASALSYS_PRIVATE_HXX
|
||||
310
src/Scripting/NasalUnitTesting.cxx
Normal file
310
src/Scripting/NasalUnitTesting.cxx
Normal file
@@ -0,0 +1,310 @@
|
||||
// Unit-test API for nasal
|
||||
//
|
||||
// There are two versions of this module, and we load one or the other
|
||||
// depending on if we're running the test_suite (using CppUnit) or
|
||||
// the normal simulator. The logic is that aircraft-developers and
|
||||
// people hacking Nasal likely don't have a way to run the test-suite,
|
||||
// whereas core-developers and Jenksin want a way to run all tests
|
||||
// through the standard CppUnit mechanim. So we have a consistent
|
||||
// Nasal API, but different implement in fgfs_test_suite vs
|
||||
// normal fgfs executable.
|
||||
//
|
||||
// Copyright (C) 2020 James Turner
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <Scripting/NasalUnitTesting.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/util.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <Scripting/NasalSys_private.hxx>
|
||||
|
||||
#include <Main/fg_commands.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
struct ActiveTest
|
||||
{
|
||||
bool failure = false;
|
||||
std::string failureMessage;
|
||||
std::string failureFileName;
|
||||
int failLineNumber;
|
||||
};
|
||||
|
||||
static std::unique_ptr<ActiveTest> static_activeTest;
|
||||
|
||||
static naRef f_assert(const nasal::CallContext& ctx )
|
||||
{
|
||||
bool pass = ctx.requireArg<bool>(0);
|
||||
auto msg = ctx.getArg<string>(1);
|
||||
|
||||
if (!pass) {
|
||||
if (!static_activeTest) {
|
||||
ctx.runtimeError("No active test in progress");
|
||||
}
|
||||
|
||||
if (static_activeTest->failure) {
|
||||
ctx.runtimeError("Active test already failed");
|
||||
}
|
||||
|
||||
static_activeTest->failure = true;
|
||||
static_activeTest->failureMessage = msg;
|
||||
static_activeTest->failureFileName = ctx.from_nasal<string>(naGetSourceFile(ctx.c_ctx(), 0));
|
||||
static_activeTest->failLineNumber = naGetLine(ctx.c_ctx(), 0);
|
||||
|
||||
ctx.runtimeError("Test assert failed");
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_fail(const nasal::CallContext& ctx )
|
||||
{
|
||||
auto msg = ctx.getArg<string>(0);
|
||||
|
||||
if (!static_activeTest) {
|
||||
ctx.runtimeError("No active test in progress");
|
||||
}
|
||||
|
||||
if (static_activeTest->failure) {
|
||||
ctx.runtimeError("Active test already failed");
|
||||
}
|
||||
|
||||
static_activeTest->failure = true;
|
||||
static_activeTest->failureMessage = msg;
|
||||
static_activeTest->failureFileName = ctx.from_nasal<string>(naGetSourceFile(ctx.c_ctx(), 0));
|
||||
static_activeTest->failLineNumber = naGetLine(ctx.c_ctx(), 0);
|
||||
|
||||
ctx.runtimeError("Test failed");
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_assert_equal(const nasal::CallContext& ctx )
|
||||
{
|
||||
naRef argA = ctx.requireArg<naRef>(0);
|
||||
naRef argB = ctx.requireArg<naRef>(1);
|
||||
auto msg = ctx.getArg<string>(2, "assert_equal failed");
|
||||
|
||||
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
|
||||
if (!same) {
|
||||
string aStr = ctx.from_nasal<string>(argA);
|
||||
string bStr = ctx.from_nasal<string>(argB);
|
||||
msg += "; expected:" + aStr + ", actual:" + bStr;
|
||||
static_activeTest->failure = true;
|
||||
static_activeTest->failureMessage = msg;
|
||||
static_activeTest->failureFileName = ctx.from_nasal<string>(naGetSourceFile(ctx.c_ctx(), 0));
|
||||
static_activeTest->failLineNumber = naGetLine(ctx.c_ctx(), 0);
|
||||
ctx.runtimeError(msg.c_str());
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_assert_doubles_equal(const nasal::CallContext& ctx )
|
||||
{
|
||||
double argA = ctx.requireArg<double>(0);
|
||||
double argB = ctx.requireArg<double>(1);
|
||||
double tolerance = ctx.requireArg<double>(2);
|
||||
|
||||
auto msg = ctx.getArg<string>(3, "assert_doubles_equal failed");
|
||||
|
||||
const bool same = fabs(argA - argB) < tolerance;
|
||||
if (!same) {
|
||||
msg += "; expected:" + std::to_string(argA) + ", actual:" + std::to_string(argB);
|
||||
static_activeTest->failure = true;
|
||||
static_activeTest->failureMessage = msg;
|
||||
static_activeTest->failureFileName = ctx.from_nasal<string>(naGetSourceFile(ctx.c_ctx(), 0));
|
||||
static_activeTest->failLineNumber = naGetLine(ctx.c_ctx(), 0);
|
||||
ctx.runtimeError(msg.c_str());
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_equal(const nasal::CallContext& ctx)
|
||||
{
|
||||
naRef argA = ctx.requireArg<naRef>(0);
|
||||
naRef argB = ctx.requireArg<naRef>(1);
|
||||
|
||||
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
|
||||
return naNum(same);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// commands
|
||||
|
||||
bool command_executeNasalTest(const SGPropertyNode *arg, SGPropertyNode * root)
|
||||
{
|
||||
SGPath p = SGPath::fromUtf8(arg->getStringValue("path"));
|
||||
if (p.isRelative()) {
|
||||
for (auto dp : globals->get_data_paths("Nasal")) {
|
||||
SGPath absPath = dp / p.utf8Str();
|
||||
if (absPath.exists()) {
|
||||
p = absPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!p.exists() || !p.isFile() || (p.lower_extension() != "nut")) {
|
||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, "not a Nasal test file:" << p);
|
||||
return false;
|
||||
}
|
||||
|
||||
return executeNasalTest(p);
|
||||
}
|
||||
|
||||
bool command_executeNasalTestDir(const SGPropertyNode *arg, SGPropertyNode * root)
|
||||
{
|
||||
SGPath p = SGPath::fromUtf8(arg->getStringValue("path"));
|
||||
if (!p.exists() || !p.isDir()) {
|
||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, "no such directory:" << p);
|
||||
return false;
|
||||
}
|
||||
|
||||
executeNasalTestsInDir(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
naRef initNasalUnitTestInSim(naRef nasalGlobals, naContext c)
|
||||
{
|
||||
nasal::Hash globals_module(nasalGlobals, c),
|
||||
unitTest = globals_module.createHash("unitTest");
|
||||
|
||||
unitTest.set("assert", f_assert);
|
||||
unitTest.set("fail", f_fail);
|
||||
unitTest.set("assert_equal", f_assert_equal);
|
||||
unitTest.set("assert_doubles_equal", f_assert_doubles_equal);
|
||||
unitTest.set("equal", f_equal);
|
||||
|
||||
globals->get_commands()->addCommand("nasal-test", &command_executeNasalTest);
|
||||
globals->get_commands()->addCommand("nasal-test-dir", &command_executeNasalTestDir);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
void executeNasalTestsInDir(const SGPath& path)
|
||||
{
|
||||
simgear::Dir d(path);
|
||||
|
||||
for (const auto& testFile : d.children(simgear::Dir::TYPE_FILE, "*.nut")) {
|
||||
SG_LOG(SG_NASAL, SG_INFO, "Processing test file " << testFile);
|
||||
|
||||
} // of test files iteration
|
||||
}
|
||||
|
||||
// variant on FGNasalSys parse,
|
||||
static naRef parseTestFile(naContext ctx, const char* filename,
|
||||
const char* buf, int len,
|
||||
std::string& errors)
|
||||
{
|
||||
int errLine = -1;
|
||||
naRef srcfile = naNewString(ctx);
|
||||
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
|
||||
naRef code = naParseCode(ctx, srcfile, 1, (char*)buf, len, &errLine);
|
||||
if(naIsNil(code)) {
|
||||
std::ostringstream errorMessageStream;
|
||||
errorMessageStream << "Nasal Test parse error: " << naGetError(ctx) <<
|
||||
" in "<< filename <<", line " << errLine;
|
||||
errors = errorMessageStream.str();
|
||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, errors);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
const auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
return naBindFunction(ctx, code, nasalSys->nasalGlobals());
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool executeNasalTest(const SGPath& path)
|
||||
{
|
||||
naContext ctx = naNewContext();
|
||||
const auto nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
sg_ifstream file_in(path);
|
||||
const auto source = file_in.read_all();
|
||||
|
||||
string errors;
|
||||
string fileName = path.utf8Str();
|
||||
naRef code = parseTestFile(ctx, fileName.c_str(),
|
||||
source.c_str(),
|
||||
source.size(), errors);
|
||||
if(naIsNil(code)) {
|
||||
naFreeContext(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create test context
|
||||
|
||||
auto localNS = nasalSys->getGlobals().createHash("_test_" + path.utf8Str());
|
||||
nasalSys->callWithContext(ctx, code, 0, 0, localNS.get_naRef());
|
||||
|
||||
|
||||
auto setUpFunc = localNS.get("setUp");
|
||||
auto tearDown = localNS.get("tearDown");
|
||||
|
||||
for (const auto value : localNS) {
|
||||
if (value.getKey().find("test_") == 0) {
|
||||
static_activeTest.reset(new ActiveTest);
|
||||
|
||||
if (naIsFunc(setUpFunc)) {
|
||||
nasalSys->callWithContext(ctx, setUpFunc, 0, nullptr ,localNS.get_naRef());
|
||||
}
|
||||
|
||||
const auto testName = value.getKey();
|
||||
auto testFunc = value.getValue<naRef>();
|
||||
if (!naIsFunc(testFunc)) {
|
||||
SG_LOG(SG_NAVAID, SG_DEV_WARN, "Skipping non-function test member:" << testName);
|
||||
continue;
|
||||
}
|
||||
|
||||
nasalSys->callWithContext(ctx, testFunc, 0, nullptr, localNS.get_naRef());
|
||||
if (static_activeTest->failure) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, testName << ": Test failure:" << static_activeTest->failureMessage << "\n\tat: " << static_activeTest->failureFileName << ": " << static_activeTest->failLineNumber);
|
||||
} else {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, testName << ": Test passed");
|
||||
}
|
||||
|
||||
if (naIsFunc(tearDown)) {
|
||||
nasalSys->callWithContext(ctx, tearDown, 0, nullptr ,localNS.get_naRef());
|
||||
}
|
||||
|
||||
static_activeTest.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// remvoe test hash/namespace
|
||||
|
||||
naFreeContext(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void shutdownNasalUnitTestInSim()
|
||||
{
|
||||
globals->get_commands()->removeCommand("nasal-test");
|
||||
globals->get_commands()->removeCommand("nasal-test-dir");
|
||||
}
|
||||
12
src/Scripting/NasalUnitTesting.hxx
Normal file
12
src/Scripting/NasalUnitTesting.hxx
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
class SGPath;
|
||||
|
||||
naRef initNasalUnitTestInSim(naRef globals, naContext c);
|
||||
|
||||
void shutdownNasalUnitTestInSim();
|
||||
|
||||
void executeNasalTestsInDir(const SGPath& path);
|
||||
bool executeNasalTest(const SGPath& path);
|
||||
952
src/Scripting/nasal-props.cxx
Normal file
952
src/Scripting/nasal-props.cxx
Normal file
@@ -0,0 +1,952 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/vectorPropTemplates.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "NasalSys.hxx"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Implementation of a Nasal wrapper for the SGPropertyNode class,
|
||||
// using the Nasal "ghost" (er... Garbage collection Handle for
|
||||
// OutSide Thingy) facility.
|
||||
//
|
||||
// Note that these functions appear in Nasal with prepended
|
||||
// underscores. They work on the low-level "ghost" objects and aren't
|
||||
// intended for use from user code, but from Nasal code you will find
|
||||
// in props.nas. That is where the Nasal props.Node class is defined,
|
||||
// which provides a saner interface along the lines of SGPropertyNode.
|
||||
|
||||
static void propNodeGhostDestroy(void* ghost)
|
||||
{
|
||||
SGPropertyNode* prop = static_cast<SGPropertyNode*>(ghost);
|
||||
if (!SGPropertyNode::put(prop)) delete prop;
|
||||
}
|
||||
|
||||
naGhostType PropNodeGhostType = { propNodeGhostDestroy, "prop", nullptr, nullptr };
|
||||
|
||||
naRef propNodeGhostCreate(naContext c, SGPropertyNode* ghost)
|
||||
{
|
||||
if(!ghost) return naNil();
|
||||
SGPropertyNode::get(ghost);
|
||||
return naNewGhost(c, &PropNodeGhostType, ghost);
|
||||
}
|
||||
|
||||
naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
|
||||
{
|
||||
return propNodeGhostCreate(_context, handle);
|
||||
}
|
||||
|
||||
SGPropertyNode* ghostToPropNode(naRef ref)
|
||||
{
|
||||
if (!naIsGhost(ref) || (naGhost_type(ref) != &PropNodeGhostType))
|
||||
return NULL;
|
||||
|
||||
return static_cast<SGPropertyNode*>(naGhost_ptr(ref));
|
||||
}
|
||||
|
||||
#define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
|
||||
|
||||
//
|
||||
// Standard header for the extension functions. It turns the "ghost"
|
||||
// found in arg[0] into a SGPropertyNode_ptr, and then "unwraps" the
|
||||
// vector found in the second argument into a normal-looking args
|
||||
// array. This allows the Nasal handlers to do things like:
|
||||
// Node.getChild = func { _getChild(me.ghost, arg) }
|
||||
//
|
||||
#define NODENOARG() \
|
||||
if(argc < 2 || !naIsGhost(args[0]) || \
|
||||
naGhost_type(args[0]) != &PropNodeGhostType) \
|
||||
naRuntimeError(c, "bad argument to props function"); \
|
||||
SGPropertyNode_ptr node = static_cast<SGPropertyNode*>(naGhost_ptr(args[0]));
|
||||
|
||||
#define NODEARG() \
|
||||
NODENOARG(); \
|
||||
naRef argv = args[1]
|
||||
|
||||
//
|
||||
// Pops the first argument as a relative path if the first condition
|
||||
// is true (e.g. argc > 1 for getAttribute) and if it is a string.
|
||||
// If the second confition is true, then another is popped to specify
|
||||
// if the node should be created (i.e. like the second argument to
|
||||
// getNode())
|
||||
//
|
||||
// Note that this makes the function return nil if the node doesn't
|
||||
// exist, so all functions with a relative_path parameter will
|
||||
// return nil if the specified node does not exist.
|
||||
//
|
||||
#define MOVETARGET(cond1, create) \
|
||||
if(cond1) { \
|
||||
naRef name = naVec_get(argv, 0); \
|
||||
if(naIsString(name)) { \
|
||||
try { \
|
||||
node = node->getNode(naStr_data(name), create); \
|
||||
} catch(const string& err) { \
|
||||
naRuntimeError(c, (char *)err.c_str()); \
|
||||
return naNil(); \
|
||||
} \
|
||||
if(!node) return naNil(); \
|
||||
naVec_removefirst(argv); /* pop only if we were successful */ \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
// Get the type of a property (returns a string).
|
||||
// Forms:
|
||||
// props.Node.getType(string relative_path);
|
||||
// props.Node.getType();
|
||||
static naRef f_getType(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 0, false);
|
||||
const char* t = "unknown";
|
||||
switch(node->getType()) {
|
||||
case props::NONE: t = "NONE"; break;
|
||||
case props::ALIAS: t = "ALIAS"; break;
|
||||
case props::BOOL: t = "BOOL"; break;
|
||||
case props::INT: t = "INT"; break;
|
||||
case props::LONG: t = "LONG"; break;
|
||||
case props::FLOAT: t = "FLOAT"; break;
|
||||
case props::DOUBLE: t = "DOUBLE"; break;
|
||||
case props::STRING: t = "STRING"; break;
|
||||
case props::UNSPECIFIED: t = "UNSPECIFIED"; break;
|
||||
case props::VEC3D: t = "VEC3D"; break;
|
||||
case props::VEC4D: t = "VEC4D"; break;
|
||||
case props::EXTENDED: t = "EXTENDED"; break; // shouldn't happen
|
||||
}
|
||||
return NASTR(t);
|
||||
}
|
||||
|
||||
// Check if type of a property is numeric (returns 0 or 1).
|
||||
// Forms:
|
||||
// props.Node.isNumeric(string relative_path);
|
||||
// props.Node.isNumeric();
|
||||
static naRef f_isNumeric(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 0, false);
|
||||
switch(node->getType()) {
|
||||
case props::INT:
|
||||
case props::LONG:
|
||||
case props::FLOAT:
|
||||
case props::DOUBLE: return naNum(true);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return naNum(false);
|
||||
}
|
||||
|
||||
// Check if type of a property is integer (returns 0 or 1).
|
||||
// Forms:
|
||||
// props.Node.isInt(string relative_path);
|
||||
// props.Node.isInt();
|
||||
static naRef f_isInt(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 0, false);
|
||||
if ((node->getType() == props::INT) || (node->getType() == props::LONG)) {
|
||||
return naNum(true);
|
||||
}
|
||||
return naNum(false);
|
||||
}
|
||||
|
||||
|
||||
// Get an attribute of a property by name (returns true/false).
|
||||
// Forms:
|
||||
// props.Node.getType(string relative_path,
|
||||
// string attribute_name);
|
||||
// props.Node.getType(string attribute_name);
|
||||
static naRef f_getAttribute(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
if(naVec_size(argv) == 0) return naNum(unsigned(node->getAttributes()));
|
||||
MOVETARGET(naVec_size(argv) > 1, false);
|
||||
naRef val = naVec_get(argv, 0);
|
||||
const char *a = naStr_data(val);
|
||||
SGPropertyNode::Attribute attr;
|
||||
if(!a) a = "";
|
||||
if(!strcmp(a, "last")) return naNum(SGPropertyNode::LAST_USED_ATTRIBUTE);
|
||||
else if(!strcmp(a, "children")) return naNum(node->nChildren());
|
||||
else if(!strcmp(a, "listeners")) return naNum(node->nListeners());
|
||||
// Number of references without instance used in this function
|
||||
else if(!strcmp(a, "references")) return naNum(node.getNumRefs() - 1);
|
||||
else if(!strcmp(a, "tied")) return naNum(node->isTied());
|
||||
else if(!strcmp(a, "alias")) return naNum(node->isAlias());
|
||||
else if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
|
||||
else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
|
||||
else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
|
||||
else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
|
||||
else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
|
||||
else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
|
||||
else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
|
||||
else if(!strcmp(a, "protected")) attr = SGPropertyNode::PROTECTED;
|
||||
else if(!strcmp(a, "listener-safe")) attr = SGPropertyNode::LISTENER_SAFE;
|
||||
else if(!strcmp(a, "value-changed-up")) attr = SGPropertyNode::VALUE_CHANGED_UP;
|
||||
else if(!strcmp(a, "value-changed-down")) attr = SGPropertyNode::VALUE_CHANGED_DOWN;
|
||||
|
||||
else {
|
||||
naRuntimeError(c, "props.getAttribute() with invalid attribute");
|
||||
return naNil();
|
||||
}
|
||||
return naNum(node->getAttribute(attr));
|
||||
}
|
||||
|
||||
|
||||
// Set an attribute by name and boolean value or raw (bitmasked) number.
|
||||
// Forms:
|
||||
// props.Node.setAttribute(string relative_path,
|
||||
// string attribute_name,
|
||||
// bool value);
|
||||
// props.Node.setAttribute(string attribute_name,
|
||||
// bool value);
|
||||
// props.Node.setArtribute(int attributes);
|
||||
static naRef f_setAttribute(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
if (node->getAttribute(SGPropertyNode::PROTECTED)) {
|
||||
naRuntimeError(c, "props.setAttribute() called on protected property %s",
|
||||
node->getPath().c_str());
|
||||
return naNil();
|
||||
}
|
||||
|
||||
MOVETARGET(naVec_size(argv) > 2, false);
|
||||
naRef val = naVec_get(argv, 0);
|
||||
if(naVec_size(argv) == 1 && naIsNum(val)) {
|
||||
naRef ret = naNum(node->getAttributes());
|
||||
// prevent Nasal modifying PROTECTED
|
||||
int attrs = static_cast<int>(val.num) & (~SGPropertyNode::PROTECTED);
|
||||
node->setAttributes(attrs);
|
||||
return ret;
|
||||
}
|
||||
SGPropertyNode::Attribute attr;
|
||||
const char *a = naStr_data(val);
|
||||
if(!a) a = "";
|
||||
if(!strcmp(a, "readable")) attr = SGPropertyNode::READ;
|
||||
else if(!strcmp(a, "writable")) attr = SGPropertyNode::WRITE;
|
||||
else if(!strcmp(a, "archive")) attr = SGPropertyNode::ARCHIVE;
|
||||
else if(!strcmp(a, "trace-read")) attr = SGPropertyNode::TRACE_READ;
|
||||
else if(!strcmp(a, "trace-write")) attr = SGPropertyNode::TRACE_WRITE;
|
||||
else if(!strcmp(a, "userarchive")) attr = SGPropertyNode::USERARCHIVE;
|
||||
else if(!strcmp(a, "preserve")) attr = SGPropertyNode::PRESERVE;
|
||||
// explicitly don't allow "protected" to be modified here
|
||||
else {
|
||||
naRuntimeError(c, "props.setAttribute() with invalid attribute");
|
||||
return naNil();
|
||||
}
|
||||
naRef ret = naNum(node->getAttribute(attr));
|
||||
node->setAttribute(attr, naTrue(naVec_get(argv, 1)) ? true : false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// Get the simple name of this node.
|
||||
// Forms:
|
||||
// props.Node.getName();
|
||||
static naRef f_getName(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
return NASTR(node->getNameString().c_str());
|
||||
}
|
||||
|
||||
|
||||
// Get the index of this node.
|
||||
// Forms:
|
||||
// props.Node.getIndex();
|
||||
static naRef f_getIndex(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
return naNum(node->getIndex());
|
||||
}
|
||||
|
||||
// Check if other_node refers to the same as this node.
|
||||
// Forms:
|
||||
// props.Node.equals(other_node);
|
||||
static naRef f_equals(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
|
||||
naRef rhs = naVec_get(argv, 0);
|
||||
if( !naIsGhost(rhs) || naGhost_type(rhs) != &PropNodeGhostType )
|
||||
return naNum(false);
|
||||
|
||||
SGPropertyNode* node_rhs = static_cast<SGPropertyNode*>(naGhost_ptr(rhs));
|
||||
return naNum(node.ptr() == node_rhs);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
naRef makeVectorFromVec(naContext c, const T& vec)
|
||||
{
|
||||
const int num_components
|
||||
= sizeof(vec.data()) / sizeof(typename T::value_type);
|
||||
naRef vector = naNewVector(c);
|
||||
naVec_setsize(c, vector, num_components);
|
||||
for (int i = 0; i < num_components; ++i)
|
||||
naVec_set(vector, i, naNum(vec[i]));
|
||||
return vector;
|
||||
}
|
||||
|
||||
|
||||
// Get the value of a node, with or without a relative path.
|
||||
// Forms:
|
||||
// props.Node.getValue(string relative_path);
|
||||
// props.Node.getValue();
|
||||
static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 0, false);
|
||||
return FGNasalSys::getPropertyValue(c, node);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T makeVecFromVector(naRef vector)
|
||||
{
|
||||
T vec;
|
||||
const int num_components
|
||||
= sizeof(vec.data()) / sizeof(typename T::value_type);
|
||||
int size = naVec_size(vector);
|
||||
|
||||
for (int i = 0; i < num_components && i < size; ++i) {
|
||||
naRef element = naVec_get(vector, i);
|
||||
naRef n = naNumValue(element);
|
||||
if (!naIsNil(n))
|
||||
vec[i] = n.num;
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
static std::string s_val_description(naRef val)
|
||||
{
|
||||
std::ostringstream message;
|
||||
if (naIsNil(val)) message << "nil";
|
||||
else if (naIsNum(val)) message << "num:" << naNumValue(val).num;
|
||||
else if (naIsString(val)) message << "string:" << naStr_data(val);
|
||||
else if (naIsScalar(val)) message << "scalar";
|
||||
else if (naIsVector(val)) message << "vector";
|
||||
else if (naIsHash(val)) message << "hash";
|
||||
else if (naIsFunc(val)) message << "func";
|
||||
else if (naIsCode(val)) message << "code";
|
||||
else if (naIsCCode(val)) message << "ccode";
|
||||
else if (naIsGhost(val)) message << "ghost";
|
||||
else message << "?";
|
||||
return message.str();
|
||||
}
|
||||
|
||||
// Helper function to set the value of a node; returns true if it succeeded or
|
||||
// false if it failed. <val> can be a string, number, or a
|
||||
// vector or numbers (for SGVec3D/4D types).
|
||||
static naRef f_setValueHelper(naContext c, SGPropertyNode_ptr node, naRef val) {
|
||||
bool result = false;
|
||||
if(naIsString(val)) {
|
||||
result = node->setStringValue(naStr_data(val));
|
||||
} else if(naIsVector(val)) {
|
||||
if(naVec_size(val) == 3)
|
||||
result = node->setValue(makeVecFromVector<SGVec3d>(val));
|
||||
else if(naVec_size(val) == 4)
|
||||
result = node->setValue(makeVecFromVector<SGVec4d>(val));
|
||||
else
|
||||
naRuntimeError(c, "props.setValue() vector value has wrong size");
|
||||
} else if(naIsNum(val)) {
|
||||
double d = naNumValue(val).num;
|
||||
if (SGMisc<double>::isNaN(d)) {
|
||||
naRuntimeError(c, "props.setValue() passed a NaN");
|
||||
}
|
||||
|
||||
result = node->setDoubleValue(d);
|
||||
} else {
|
||||
naRuntimeError(c, "props.setValue() called with unsupported value %s", s_val_description(val).c_str());
|
||||
}
|
||||
return naNum(result);
|
||||
}
|
||||
|
||||
|
||||
// Set the value of a node; returns true if it succeeded or
|
||||
// false if it failed. <val> can be a string, number, or a
|
||||
// vector or numbers (for SGVec3D/4D types).
|
||||
// Forms:
|
||||
// props.Node.setValue(string relative_path,
|
||||
// val);
|
||||
// props.Node.setValue(val);
|
||||
static naRef f_setValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, true);
|
||||
naRef val = naVec_get(argv, 0);
|
||||
return f_setValueHelper(c, node, val);
|
||||
}
|
||||
|
||||
static naRef f_setIntValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, true);
|
||||
// Original code:
|
||||
// int iv = (int)naNumValue(naVec_get(argv, 0)).num;
|
||||
|
||||
// Junk to pacify the gcc-2.95.3 optimizer:
|
||||
naRef tmp0 = naVec_get(argv, 0);
|
||||
naRef tmp1 = naNumValue(tmp0);
|
||||
if(naIsNil(tmp1))
|
||||
naRuntimeError(c, "props.setIntValue() with non-number");
|
||||
double tmp2 = tmp1.num;
|
||||
int iv = (int)tmp2;
|
||||
|
||||
return naNum(node->setIntValue(iv));
|
||||
}
|
||||
|
||||
static naRef f_setBoolValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, true);
|
||||
naRef val = naVec_get(argv, 0);
|
||||
return naNum(node->setBoolValue(naTrue(val) ? true : false));
|
||||
}
|
||||
|
||||
static naRef f_toggleBoolValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 0, false);
|
||||
if (node->getType() != props::BOOL) {
|
||||
naRuntimeError(c, "props.toggleBoolValue() on non-bool prop");
|
||||
}
|
||||
|
||||
const auto val = node->getBoolValue();
|
||||
return naNum(node->setBoolValue(val ? false : true));
|
||||
}
|
||||
|
||||
static naRef f_setDoubleValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, true);
|
||||
naRef r = naNumValue(naVec_get(argv, 0));
|
||||
if (naIsNil(r))
|
||||
naRuntimeError(c, "props.setDoubleValue() with non-number");
|
||||
|
||||
if (SGMisc<double>::isNaN(r.num)) {
|
||||
naRuntimeError(c, "props.setDoubleValue() passed a NaN");
|
||||
}
|
||||
|
||||
return naNum(node->setDoubleValue(r.num));
|
||||
}
|
||||
|
||||
static naRef f_adjustValue(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
using namespace simgear;
|
||||
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, false);
|
||||
naRef r = naNumValue(naVec_get(argv, 0));
|
||||
if (naIsNil(r))
|
||||
naRuntimeError(c, "props.adjustValue() with non-number");
|
||||
|
||||
if (SGMisc<double>::isNaN(r.num)) {
|
||||
naRuntimeError(c, "props.adjustValue() passed a NaN");
|
||||
}
|
||||
|
||||
switch(node->getType()) {
|
||||
case props::BOOL:
|
||||
case props::INT:
|
||||
case props::LONG:
|
||||
case props::FLOAT:
|
||||
case props::DOUBLE:
|
||||
// fall through
|
||||
break;
|
||||
|
||||
default:
|
||||
naRuntimeError(c, "props.adjustValue() called on non-numeric type");
|
||||
return naNil();
|
||||
}
|
||||
|
||||
const auto dv = node->getDoubleValue();
|
||||
return naNum(node->setDoubleValue(dv + r.num));
|
||||
}
|
||||
|
||||
// Forward declaration
|
||||
static naRef f_setChildrenHelper(naContext c, SGPropertyNode_ptr node, char* name, naRef val);
|
||||
|
||||
static naRef f_setValuesHelper(naContext c, SGPropertyNode_ptr node, naRef hash)
|
||||
{
|
||||
if (!naIsHash(hash)) {
|
||||
naRuntimeError(c, "props.setValues() with non-hash");
|
||||
}
|
||||
|
||||
naRef keyvec = naNewVector(c);
|
||||
naHash_keys(keyvec, hash);
|
||||
naRef ret;
|
||||
|
||||
for (int i = 0; i < naVec_size(keyvec); i++) {
|
||||
naRef key = naVec_get(keyvec, i);
|
||||
if (! naIsScalar(key)) {
|
||||
naRuntimeError(c, "props.setValues() with non-scalar key value");
|
||||
}
|
||||
char* keystr = naStr_data(naStringValue(c, key));
|
||||
ret = f_setChildrenHelper(c, node, keystr, naHash_cget(hash, keystr));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static naRef f_setValues(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
MOVETARGET(naVec_size(argv) > 1, true);
|
||||
naRef val = naVec_get(argv, 0);
|
||||
return f_setValuesHelper(c, node, val);
|
||||
}
|
||||
|
||||
static naRef f_setChildrenHelper(naContext c, SGPropertyNode_ptr node, char* name, naRef val)
|
||||
{
|
||||
naRef ret;
|
||||
try {
|
||||
SGPropertyNode_ptr subnode = node->getNode(name, true);
|
||||
|
||||
if (naIsScalar(val)) {
|
||||
ret = f_setValueHelper(c, subnode, val);
|
||||
} else if (naIsHash(val)) {
|
||||
ret = f_setValuesHelper(c, subnode, val);
|
||||
} else if (naIsVector(val)) {
|
||||
char nameBuf[1024];
|
||||
for (int i = 0; i < naVec_size(val); i++) {
|
||||
const auto len = ::snprintf(nameBuf, sizeof(nameBuf), "%s[%i]", name, i);
|
||||
if ((len < 0) || (len >= (int) sizeof(nameBuf))) {
|
||||
naRuntimeError(c, "Failed to create buffer for property name in setChildren");
|
||||
}
|
||||
ret = f_setChildrenHelper(c, node, nameBuf, naVec_get(val, i));
|
||||
}
|
||||
} else if (naIsNil(val)) {
|
||||
// Nil value OK - no-op
|
||||
} else {
|
||||
// We have an error, but throwing a runtime error will prevent certain things from
|
||||
// working (such as the pilot list)
|
||||
// The nasal version would fail silently with invalid data - a runtime error will dump the stack and
|
||||
// stop execution.
|
||||
// Overall to be safer the new method should be functionally equivalent to keep compatibility.
|
||||
//
|
||||
//REMOVED: naRuntimeError(c, "props.setChildren() with unknown type");
|
||||
}
|
||||
} catch(const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static naRef f_setChildren(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
if(! naIsString(naVec_get(argv, 0))) {
|
||||
naRuntimeError(c, "props.setChildren() with non-string first argument");
|
||||
}
|
||||
|
||||
char* name = naStr_data(naVec_get(argv, 0));
|
||||
naRef val = naVec_get(argv, 1);
|
||||
return f_setChildrenHelper(c, node, name, val);
|
||||
}
|
||||
|
||||
// Get the parent of this node as a ghost.
|
||||
// Forms:
|
||||
// props.Node.getParent();
|
||||
static naRef f_getParent(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
SGPropertyNode* n = node->getParent();
|
||||
if(!n) return naNil();
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
|
||||
// Get a child by name and optional index=0, creating if specified (by default it
|
||||
// does not create it). If the node does not exist and create is false, then it
|
||||
// returns nil, else it returns a (possibly new) property ghost.
|
||||
// Forms:
|
||||
// props.Node.getChild(string relative_path,
|
||||
// int index=0,
|
||||
// bool create=false);
|
||||
static naRef f_getChild(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(argv, 0);
|
||||
if(!naIsString(child)) return naNil();
|
||||
naRef idx = naNumValue(naVec_get(argv, 1));
|
||||
bool create = naTrue(naVec_get(argv, 2)) != 0;
|
||||
SGPropertyNode* n;
|
||||
try {
|
||||
if(naIsNil(idx)) {
|
||||
n = node->getChild(naStr_data(child), create);
|
||||
} else {
|
||||
n = node->getChild(naStr_data(child), (int)idx.num, create);
|
||||
}
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
if(!n) return naNil();
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
|
||||
// Get all children with a specified name as a vector of ghosts.
|
||||
// Forms:
|
||||
// props.Node.getChildren(string relative_path);
|
||||
// props.Node.getChildren(); #get all children
|
||||
static naRef f_getChildren(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef result = naNewVector(c);
|
||||
if(naIsNil(argv) || naVec_size(argv) == 0) {
|
||||
// Get all children
|
||||
for(int i=0; i<node->nChildren(); i++)
|
||||
naVec_append(result, propNodeGhostCreate(c, node->getChild(i)));
|
||||
} else {
|
||||
// Get all children of a specified name
|
||||
naRef name = naVec_get(argv, 0);
|
||||
if(!naIsString(name)) return naNil();
|
||||
try {
|
||||
vector<SGPropertyNode_ptr> children
|
||||
= node->getChildren(naStr_data(name));
|
||||
for(unsigned int i=0; i<children.size(); i++)
|
||||
naVec_append(result, propNodeGhostCreate(c, children[i]));
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Append a named child at the first unused index...
|
||||
// Forms:
|
||||
// props.Node.addChild(string name,
|
||||
// int min_index=0,
|
||||
// bool append=true);
|
||||
static naRef f_addChild(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(argv, 0);
|
||||
if(!naIsString(child)) return naNil();
|
||||
naRef ref_min_index = naNumValue(naVec_get(argv, 1));
|
||||
naRef ref_append = naVec_get(argv, 2);
|
||||
SGPropertyNode* n;
|
||||
try
|
||||
{
|
||||
int min_index = 0;
|
||||
if(!naIsNil(ref_min_index))
|
||||
min_index = ref_min_index.num;
|
||||
|
||||
bool append = true;
|
||||
if(!naIsNil(ref_append))
|
||||
append = naTrue(ref_append) != 0;
|
||||
|
||||
n = node->addChild(naStr_data(child), min_index, append);
|
||||
}
|
||||
catch (const string& err)
|
||||
{
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
static naRef f_addChildren(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(argv, 0);
|
||||
if(!naIsString(child)) return naNil();
|
||||
naRef ref_count = naNumValue(naVec_get(argv, 1));
|
||||
naRef ref_min_index = naNumValue(naVec_get(argv, 2));
|
||||
naRef ref_append = naVec_get(argv, 3);
|
||||
try
|
||||
{
|
||||
size_t count = 0;
|
||||
if( !naIsNum(ref_count) )
|
||||
throw string("props.addChildren() missing number of children");
|
||||
count = ref_count.num;
|
||||
|
||||
int min_index = 0;
|
||||
if(!naIsNil(ref_min_index))
|
||||
min_index = ref_min_index.num;
|
||||
|
||||
bool append = true;
|
||||
if(!naIsNil(ref_append))
|
||||
append = naTrue(ref_append) != 0;
|
||||
|
||||
const simgear::PropertyList& nodes =
|
||||
node->addChildren(naStr_data(child), count, min_index, append);
|
||||
|
||||
naRef result = naNewVector(c);
|
||||
for( size_t i = 0; i < nodes.size(); ++i )
|
||||
naVec_append(result, propNodeGhostCreate(c, nodes[i]));
|
||||
return result;
|
||||
}
|
||||
catch (const string& err)
|
||||
{
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
|
||||
// Remove a child by name and index. Returns it as a ghost.
|
||||
// Forms:
|
||||
// props.Node.removeChild(string relative_path,
|
||||
// int index);
|
||||
static naRef f_removeChild(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(argv, 0);
|
||||
naRef index = naVec_get(argv, 1);
|
||||
if(!naIsString(child) || !naIsNum(index)) return naNil();
|
||||
SGPropertyNode_ptr n;
|
||||
try {
|
||||
n = node->getChild(naStr_data(child), (int)index.num);
|
||||
if (n && n->getAttribute(SGPropertyNode::PROTECTED)) {
|
||||
naRuntimeError(c, "props.Node.removeChild() called on protected child %s of %s",
|
||||
naStr_data(child), node->getPath().c_str());
|
||||
return naNil();
|
||||
}
|
||||
n = node->removeChild(naStr_data(child), (int)index.num);
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
}
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
|
||||
// Remove all children with specified name. Returns a vector of all the nodes
|
||||
// removed as ghosts.
|
||||
// Forms:
|
||||
// props.Node.removeChildren(string relative_path);
|
||||
// props.Node.removeChildren(); #remove all children
|
||||
static naRef f_removeChildren(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef result = naNewVector(c);
|
||||
if(naIsNil(argv) || naVec_size(argv) == 0) {
|
||||
// Remove all children
|
||||
for(int i = node->nChildren() - 1; i >=0; i--) {
|
||||
SGPropertyNode_ptr n = node->getChild(i);
|
||||
if (n->getAttribute(SGPropertyNode::PROTECTED)) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "props.Node.removeChildren: node " <<
|
||||
n->getPath() << " is protected");
|
||||
continue;
|
||||
}
|
||||
|
||||
node->removeChild(i);
|
||||
naVec_append(result, propNodeGhostCreate(c, n));
|
||||
}
|
||||
} else {
|
||||
// Remove all children of a specified name
|
||||
naRef name = naVec_get(argv, 0);
|
||||
if(!naIsString(name)) return naNil();
|
||||
try {
|
||||
auto children = node->getChildren(naStr_data(name));
|
||||
for (auto cn : children) {
|
||||
if (cn->getAttribute(SGPropertyNode::PROTECTED)) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "props.Node.removeChildren: node " <<
|
||||
cn->getPath() << " is protected");
|
||||
continue;
|
||||
}
|
||||
node->removeChild(cn);
|
||||
naVec_append(result, propNodeGhostCreate(c, cn));
|
||||
}
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove all children of a property node.
|
||||
// Forms:
|
||||
// props.Node.removeAllChildren();
|
||||
static naRef f_removeAllChildren(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
node->removeAllChildren();
|
||||
return propNodeGhostCreate(c, node);
|
||||
}
|
||||
|
||||
// Alias this property to another one; returns 1 on success or 0 on failure
|
||||
// (only applicable to tied properties).
|
||||
// Forms:
|
||||
// props.Node.alias(string global_path);
|
||||
// props.Node.alias(prop_ghost node);
|
||||
// props.Node.alias(props.Node node); #added by props.nas
|
||||
static naRef f_alias(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
if (node->getAttribute(SGPropertyNode::PROTECTED)) {
|
||||
naRuntimeError(c, "props.Node.alias() called on protected property %s",
|
||||
node->getPath().c_str());
|
||||
return naNil();
|
||||
}
|
||||
SGPropertyNode* al;
|
||||
naRef prop = naVec_get(argv, 0);
|
||||
try {
|
||||
if(naIsString(prop)) al = globals->get_props()->getNode(naStr_data(prop), true);
|
||||
else if(naIsGhost(prop)) al = static_cast<SGPropertyNode*>(naGhost_ptr(prop));
|
||||
else throw string("props.alias() with bad argument");
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
return naNum(node->alias(al));
|
||||
}
|
||||
|
||||
|
||||
// Un-alias this property. Returns 1 on success or 0 on failure (only
|
||||
// applicable to tied properties).
|
||||
// Forms:
|
||||
// props.Node.unalias();
|
||||
static naRef f_unalias(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
return naNum(node->unalias());
|
||||
}
|
||||
|
||||
|
||||
// Get the alias of this node as a ghost.
|
||||
// Forms:
|
||||
// props.Node.getAliasTarget();
|
||||
static naRef f_getAliasTarget(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODENOARG();
|
||||
return propNodeGhostCreate(c, node->getAliasTarget());
|
||||
}
|
||||
|
||||
|
||||
// Get a relative node. Returns nil if it does not exist and create is false,
|
||||
// or a ghost object otherwise (wrapped into a props.Node object by props.nas).
|
||||
// Forms:
|
||||
// props.Node.getNode(string relative_path,
|
||||
// bool create=false);
|
||||
static naRef f_getNode(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef path = naVec_get(argv, 0);
|
||||
bool create = naTrue(naVec_get(argv, 1)) != 0;
|
||||
if(!naIsString(path)) return naNil();
|
||||
SGPropertyNode* n;
|
||||
try {
|
||||
n = node->getNode(naStr_data(path), create);
|
||||
} catch (const string& err) {
|
||||
naRuntimeError(c, (char *)err.c_str());
|
||||
return naNil();
|
||||
}
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
|
||||
// Create a new property node.
|
||||
// Forms:
|
||||
// props.Node.new();
|
||||
static naRef f_new(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
return propNodeGhostCreate(c, new SGPropertyNode());
|
||||
}
|
||||
|
||||
|
||||
// Get the global root node (cached by props.nas so that it does
|
||||
// not require a function call).
|
||||
// Forms:
|
||||
// props._globals()
|
||||
// props.globals
|
||||
static naRef f_globals(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
return propNodeGhostCreate(c, globals->get_props());
|
||||
}
|
||||
|
||||
static struct {
|
||||
naCFunction func;
|
||||
const char* name;
|
||||
} propfuncs[] = {
|
||||
{ f_getType, "_getType" },
|
||||
{ f_getAttribute, "_getAttribute" },
|
||||
{ f_setAttribute, "_setAttribute" },
|
||||
{ f_getName, "_getName" },
|
||||
{ f_getIndex, "_getIndex" },
|
||||
{ f_equals, "_equals" },
|
||||
{ f_getValue, "_getValue" },
|
||||
{ f_setValue, "_setValue" },
|
||||
{ f_setValues, "_setValues" },
|
||||
{ f_setIntValue, "_setIntValue" },
|
||||
{ f_setBoolValue, "_setBoolValue" },
|
||||
{ f_toggleBoolValue, "_toggleBoolValue" },
|
||||
{ f_setDoubleValue, "_setDoubleValue" },
|
||||
{ f_getParent, "_getParent" },
|
||||
{ f_getChild, "_getChild" },
|
||||
{ f_getChildren, "_getChildren" },
|
||||
{ f_addChild, "_addChild" },
|
||||
{ f_addChildren, "_addChildren" },
|
||||
{ f_removeChild, "_removeChild" },
|
||||
{ f_removeChildren, "_removeChildren" },
|
||||
{ f_removeAllChildren, "_removeAllChildren" },
|
||||
{ f_setChildren, "_setChildren" },
|
||||
{ f_alias, "_alias" },
|
||||
{ f_unalias, "_unalias" },
|
||||
{ f_getAliasTarget, "_getAliasTarget" },
|
||||
{ f_getNode, "_getNode" },
|
||||
{ f_new, "_new" },
|
||||
{ f_globals, "_globals" },
|
||||
{ f_isNumeric, "_isNumeric" },
|
||||
{ f_isInt, "_isInt" },
|
||||
{ f_adjustValue, "_adjustValue" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
naRef FGNasalSys::genPropsModule()
|
||||
{
|
||||
naRef namespc = naNewHash(_context);
|
||||
for(int i=0; propfuncs[i].name; i++)
|
||||
hashset(namespc, propfuncs[i].name,
|
||||
naNewFunc(_context, naNewCCode(_context, propfuncs[i].func)));
|
||||
return namespc;
|
||||
}
|
||||
|
||||
naRef FGNasalSys::getPropertyValue(naContext c, SGPropertyNode* node)
|
||||
{
|
||||
using namespace simgear;
|
||||
if (!node)
|
||||
return naNil();
|
||||
|
||||
switch(node->getType()) {
|
||||
case props::BOOL: case props::INT:
|
||||
case props::LONG: case props::FLOAT:
|
||||
case props::DOUBLE:
|
||||
{
|
||||
double dv = node->getDoubleValue();
|
||||
if (SGMisc<double>::isNaN(dv)) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
|
||||
return naNil();
|
||||
}
|
||||
|
||||
return naNum(dv);
|
||||
}
|
||||
|
||||
case props::STRING:
|
||||
case props::UNSPECIFIED:
|
||||
return NASTR(node->getStringValue().c_str());
|
||||
case props::VEC3D:
|
||||
return makeVectorFromVec(c, node->getValue<SGVec3d>());
|
||||
case props::VEC4D:
|
||||
return makeVectorFromVec(c, node->getValue<SGVec4d>());
|
||||
default:
|
||||
return naNil();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user