#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
#include <Scripting/NasalSys.hxx>
+#include <Scripting/NasalDisplay.hxx>
#include <Sound/fg_fx.hxx>
#include <Sound/beacon.hxx>
#include <Sound/morse.hxx>
globals->get_io()->bind();
+ ////////////////////////////////////////////////////////////////////
+ // Initialise Nasal display system
+ ////////////////////////////////////////////////////////////////////
+
+ SG_LOG(SG_GENERAL, SG_INFO, " Nasal Display");
+ globals->set_Nasal_display(new FGNasalDisplay);
+ globals->get_Nasal_display()->init();
+
////////////////////////////////////////////////////////////////////
// Add a new 2D panel.
////////////////////////////////////////////////////////////////////
class FGFlightPlanDispatcher;
class FGIO;
class FGNavList;
+class FGNasalDisplay;
class FGTACANList;
class FGFixList;
class FGLight;
FGNavList *carrierlist;
FGTACANList *channellist;
FGFixList *fixlist;
+
+ // Scripting display
+ FGNasalDisplay * Nasal_display;
#ifdef FG_MPLAYER_AS
inline void set_tile_mgr ( FGTileMgr *t ) { tile_mgr = t; }
inline FGIO* get_io() const { return io; }
+
+ inline FGNasalDisplay *get_Nasal_display() const { return Nasal_display; }
+ inline void set_Nasal_display( FGNasalDisplay *d ) {Nasal_display = d; }
inline FGNavList *get_navlist() const { return navlist; }
inline void set_navlist( FGNavList *n ) { navlist = n; }
#include <Model/acmodel.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
+#include <Scripting/NasalDisplay.hxx>
#include <ATC/ATCdisplay.hxx>
#include <GUI/new_gui.hxx>
if((fgGetBool("/sim/atc/enabled")) || (fgGetBool("/sim/ai-traffic/enabled")))
globals->get_ATC_display()->update(delta_time_sec);
+ // Update any messages from the Nasal System
+ globals->get_Nasal_display()->update(delta_time_sec);
+
// update the panel subsystem
if ( globals->get_current_panel() != NULL ) {
globals->get_current_panel()->update(delta_time_sec);
noinst_LIBRARIES = libScripting.a
-libScripting_a_SOURCES = NasalSys.cxx NasalSys.hxx nasal-props.cxx
-# libScripting_a_SOURCES = scriptmgr.cxx scriptmgr.hxx NasalSys.cxx NasalSys.hxx
+libScripting_a_SOURCES = \
+ NasalSys.cxx NasalSys.hxx \
+ NasalDisplay.cxx NasalDisplay.hxx \
+ nasal-props.cxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
--- /dev/null
+// NasalDisplay.cxx - routines to display Nasal output
+//
+// Written by David Luff, started October 2001.
+//
+// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include SG_GLU_H
+
+#include <simgear/props/props.hxx>
+
+#include <Include/general.hxx>
+#include <Main/fg_props.hxx>
+#include <GUI/gui.h>
+
+#include "NasalDisplay.hxx"
+
+
+// Constructor
+FGNasalDisplay::FGNasalDisplay() {
+ rep_msg = false;
+ change_msg_flag = false;
+ dsp_offset1 = 0.0;
+ dsp_offset2 = 0.0;
+}
+
+
+// Destructor
+FGNasalDisplay::~FGNasalDisplay() {
+}
+
+void FGNasalDisplay::init() {
+}
+
+void FGNasalDisplay::bind() {
+}
+
+void FGNasalDisplay::unbind() {
+}
+
+// update - this actually draws the visuals and should be called from the main Flightgear rendering loop.
+void FGNasalDisplay::update(double dt) {
+
+ // These strings are used for temporary storage of the transmission string in order
+ // that the string we view only changes when the next repetition starts scrolling
+ // even though the master string (rep_msg_str) may change at any time.
+ static string msg1 = "";
+ static string msg2 = "";
+
+ if( rep_msg || msgList.size() ) {
+ SGPropertyNode *xsize_node = fgGetNode("/sim/startup/xsize");
+ SGPropertyNode *ysize_node = fgGetNode("/sim/startup/ysize");
+ int iwidth = xsize_node->getIntValue();
+ int iheight = ysize_node->getIntValue();
+
+ glMatrixMode( GL_PROJECTION );
+ glPushMatrix();
+ glLoadIdentity();
+ gluOrtho2D( 0, iwidth, 0, iheight );
+ glMatrixMode( GL_MODELVIEW );
+ glPushMatrix();
+ glLoadIdentity();
+
+ glDisable( GL_DEPTH_TEST );
+ glDisable( GL_LIGHTING );
+
+ glColor3f( 0.9, 0.4, 0.2 );
+
+ float fps = general.get_frame_rate();
+
+ if(rep_msg) {
+ //cout << "dsp_offset1 = " << dsp_offset1 << " dsp_offset2 = " << dsp_offset2 << endl;
+ if(dsp_offset1 == 0) {
+ msg1 = rep_msg_str;
+ }
+ if(dsp_offset2 == 0) {
+ msg2 = rep_msg_str;
+ }
+ // Check for the situation where one offset is negative and the message is changed
+ if(change_msg_flag) {
+ if(dsp_offset1 < 0) {
+ msg1 = rep_msg_str;
+ } else if(dsp_offset2 < 0) {
+ msg2 = rep_msg_str;
+ }
+ change_msg_flag = false;
+ }
+
+ // guiFnt.drawString( rep_msg_str.c_str(),
+ // int(iwidth - guiFnt.getStringWidth(buf) - 10 - (int)dsp_offset),
+ // (iheight - 20) );
+ guiFnt.drawString( msg1.c_str(),
+ int(iwidth - 10 - dsp_offset1),
+ (iheight - 20) );
+ guiFnt.drawString( msg2.c_str(),
+ int(iwidth - 10 - dsp_offset2),
+ (iheight - 20) );
+
+ // Try to scroll at a frame rate independent speed
+ // 40 pixels/second looks about right for now
+ if(dsp_offset1 >= dsp_offset2) {
+ dsp_offset1+=(40.0/fps);
+ dsp_offset2 = dsp_offset1 - (rep_msg_str.size() * 10) - 100;
+ if(dsp_offset1 > (iwidth + (rep_msg_str.size() * 10)))
+ dsp_offset1 = 0;
+ } else {
+ dsp_offset2+=(40.0/fps);
+ dsp_offset1 = dsp_offset2 - (rep_msg_str.size() * 10) - 100;
+ if(dsp_offset2 > (iwidth + (rep_msg_str.size() * 10)))
+ dsp_offset2 = 0;
+ }
+
+ }
+
+ if(msgList.size()) {
+ //cout << "Attempting to render single message\n";
+ // We have at least one non-repeating message to process
+ if(fgGetBool("/ATC/display/scroll-single-messages")) { // Scroll single messages across the screen.
+ msgList_itr = msgList.begin();
+ int i = 0;
+ while(msgList_itr != msgList.end()) {
+ nasalMessage m = *msgList_itr;
+ //cout << "m.counter = " << m.counter << '\n';
+ if(m.dsp_offset > (iwidth + (m.msg.size() * 10))) {
+ //cout << "Stopping single message\n";
+ msgList_itr = msgList.erase(msgList_itr);
+ } else if(m.counter > m.start_count) {
+ //cout << "Drawing single message\n";
+ guiFnt.drawString( m.msg.c_str(),
+ int(iwidth - 10 - m.dsp_offset),
+ (iheight - 80) );
+ m.counter += dt;
+ m.dsp_offset += (80.0/fps);
+ msgList[i] = m;
+ ++msgList_itr;
+ ++i;
+ } else {
+ //cout << "Not yet started single message\n";
+ m.counter += dt;
+ msgList[i] = m;
+ ++msgList_itr;
+ ++i;
+ }
+ }
+ } else { // Display single messages for a short period of time.
+ msgList_itr = msgList.begin();
+ int i = 0;
+ while(msgList_itr != msgList.end()) {
+ nasalMessage m = *msgList_itr;
+ //cout << "m.counter = " << m.counter << '\n';
+ if(m.counter > m.stop_count) {
+ //cout << "Stopping single message\n";
+ msgList_itr = msgList.erase(msgList_itr);
+ } else if(m.counter > m.start_count) {
+ int pin = (((int)m.msg.size() * 8) >= iwidth ? 5 : (iwidth - (m.msg.size() * 8))/2);
+ //cout << m.msg << '\n';
+ //cout << "pin = " << pin << ", iwidth = " << iwidth << ", msg.size = " << m.msg.size() << '\n';
+ guiFnt.drawString( m.msg.c_str(), pin, (iheight - 80) );
+ m.counter += dt;
+ msgList[i] = m;
+ ++msgList_itr;
+ ++i;
+ } else {
+ m.counter += dt;
+ msgList[i] = m;
+ ++msgList_itr;
+ ++i;
+ }
+ }
+ }
+ }
+ glEnable( GL_DEPTH_TEST );
+ glEnable( GL_LIGHTING );
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+ glPopMatrix();
+ }
+}
+
+void FGNasalDisplay::RegisterSingleMessage(const string& msg, double delay) {
+ //cout << msg << '\n';
+ nasalMessage m;
+ m.msg = msg;
+ m.repeating = false;
+ m.counter = 0.0;
+ m.start_count = delay;
+ m.stop_count = m.start_count + 5.0; // Display for 5ish seconds for now - this might have to change eg. be related to length of message in future
+ //cout << "m.stop_count = " << m.stop_count << '\n';
+ m.id = 0;
+ m.dsp_offset = 0.0;
+
+ msgList.push_back(m);
+ //cout << "Single message registered\n";
+}
+
+void FGNasalDisplay::RegisterRepeatingMessage(const string& msg) {
+ rep_msg = true;
+ rep_msg_str = msg;
+ return;
+}
+
+void FGNasalDisplay::ChangeRepeatingMessage(const string& newmsg) {
+ rep_msg_str = newmsg;
+ change_msg_flag = true;
+ return;
+}
+
+void FGNasalDisplay::CancelRepeatingMessage() {
+ rep_msg = false;
+ rep_msg_str = "";
+ dsp_offset1 = 0;
+ dsp_offset2 = 0;
+ return;
+}
+
--- /dev/null
+// NasalDisplay.hxx - class to manage the graphical display of Nasal messages.
+// - mainly copied from ATCDisplay.hxx
+//
+// Written by David Luff, started October 2001.
+//
+// Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#ifndef _FG_NASAL_DISPLAY_HXX
+#define _FG_NASAL_DISPLAY_HXX
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include <vector>
+#include <string>
+
+SG_USING_STD(vector);
+SG_USING_STD(string);
+
+struct nasalMessage {
+ string msg;
+ bool repeating;
+ double counter; // count of how many seconds since the message was registered
+ double start_count; // value of counter at which display should start (seconds)
+ double stop_count; // value of counter at which display should stop (seconds)
+ int id;
+ double dsp_offset;
+};
+
+// ASSUMPTION - with two radios the list won't be long so we don't need to map the id's
+typedef vector<nasalMessage> nasalMessageList;
+typedef nasalMessageList::iterator nasalMessageListIterator;
+
+class FGNasalDisplay : public SGSubsystem
+{
+
+private:
+ bool rep_msg; // Flag to indicate there is a repeating transmission to display
+ bool change_msg_flag; // Flag to indicate that the repeating message has changed
+ double dsp_offset1; // Used to set the correct position of scrolling display
+ double dsp_offset2;
+ string rep_msg_str; // The repeating transmission to play
+ nasalMessageList msgList;
+ nasalMessageListIterator msgList_itr;
+
+public:
+ FGNasalDisplay();
+ ~FGNasalDisplay();
+
+ void init();
+
+ void bind();
+
+ void unbind();
+
+ // Display any registered messages
+ void update(double dt);
+
+ // Register a single message for display after a delay of delay seconds
+ // Will automatically stop displaying after a suitable interval.
+ void RegisterSingleMessage(const string& msg, double delay = 0.0);
+
+ // For now we will assume only one repeating message at once
+ // This is not really robust
+
+ // Register a continuously repeating message
+ void RegisterRepeatingMessage(const string& msg);
+
+ // Change a repeating message - assume that the message changes after the string has finished for now
+ void ChangeRepeatingMessage(const string& newmsg);
+
+ // Cancel the current repeating message
+ void CancelRepeatingMessage();
+};
+
+#endif // _FG_ATC_DISPLAY_HXX
#include <Main/fg_props.hxx>
#include "NasalSys.hxx"
+#include "NasalDisplay.hxx"
// Read and return file contents in a single buffer. Note use of
// stat() to get the file size. This is a win32 function, believe it
return naNum(sg_random());
}
+// Wrapper function for screenPrint
+static naRef f_screenPrint(naContext c, naRef me, int argc, naRef* args)
+{
+ if(argc != 1 || !naIsString(args[0]))
+ naRuntimeError(c, "bad arguments to screenPrint()");
+ naRef lmsg = args[0];
+ FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal");
+ nasal->screenPrint(naStr_data(lmsg));
+ return naNil();
+}
+
// Table of extension functions. Terminate with zeros.
static struct { char* name; naCFunction func; } funcs[] = {
{ "getprop", f_getprop },
{ "_cmdarg", f_cmdarg },
{ "_interpolate", f_interpolate },
{ "rand", f_rand },
+ { "screenPrint", f_screenPrint },
{ 0, 0 }
};
node->addChangeListener(new FGNasalListener(handler, this, gcSave(handler)));
}
+// functions providing access to the NasalDisplay - used to display text directly on the screen
+void FGNasalSys::screenPrint(const char* src)
+{
+ globals->get_Nasal_display()->RegisterSingleMessage(src, 0);
+}
void createModule(const char* moduleName, const char* fileName,
const char* src, int len);
-
+
+ void screenPrint(const char* src);
+
private:
friend class FGNasalScript;
friend class FGNasalListener;
~FGNasalScript() { _nas->gcRelease(_gcKey); }
bool call() {
- naCall(_nas->_context, _code, 0, 0, naNil(), naNil());
+ naRef n = naNil();
+ naCall(_nas->_context, _code, 0, &n, naNil(), naNil());
return naGetError(_nas->_context) == 0;
}