celx.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:128k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // celx.cpp
  2. //
  3. // Copyright (C) 2003-2008, the Celestia Development Team
  4. // Original version by Chris Laurel <claurel@gmail.com>
  5. //
  6. // Lua script extensions for Celestia.
  7. //
  8. // This program is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU General Public License
  10. // as published by the Free Software Foundation; either version 2
  11. // of the License, or (at your option) any later version.
  12. #include <cassert>
  13. #include <cstring>
  14. #include <cstdio>
  15. #include <ctime>
  16. #include <map>
  17. #include <celengine/astro.h>
  18. #include <celengine/celestia.h>
  19. #include <celengine/cmdparser.h>
  20. #include <celengine/execenv.h>
  21. #include <celengine/execution.h>
  22. #include <celmath/vecmath.h>
  23. #include <celengine/timeline.h>
  24. #include <celengine/timelinephase.h>
  25. #include "imagecapture.h"
  26. #include "celx.h"
  27. #include "celx_internal.h"
  28. #include "celx_vector.h"
  29. #include "celx_rotation.h"
  30. #include "celx_position.h"
  31. #include "celx_frame.h"
  32. #include "celx_phase.h"
  33. #include "celx_object.h"
  34. #include "celx_observer.h"
  35. #include "celx_celestia.h"
  36. #include "celx_gl.h"
  37. // Older gcc versions used <strstream> instead of <sstream>.
  38. // This has been corrected in GCC 3.2, but name clashing must
  39. // be avoided
  40. #ifdef __GNUC__
  41. #undef min
  42. #undef max
  43. #endif
  44. #include <sstream>
  45. #include "celx.h"
  46. #include "celestiacore.h"
  47. using namespace std;
  48. const char* CelxLua::ClassNames[] =
  49. {
  50.     "class_celestia",
  51.     "class_observer",
  52.     "class_object",
  53.     "class_vec3",
  54.     "class_matrix",
  55.     "class_rotation",
  56.     "class_position",
  57.     "class_frame",
  58.     "class_celscript",
  59.     "class_font",
  60.     "class_image",
  61.     "class_texture",
  62.     "class_phase",
  63. };
  64. CelxLua::FlagMap CelxLua::RenderFlagMap;
  65. CelxLua::FlagMap CelxLua::LabelFlagMap;
  66. CelxLua::FlagMap CelxLua::LocationFlagMap;
  67. CelxLua::FlagMap CelxLua::BodyTypeMap;
  68. CelxLua::FlagMap CelxLua::OverlayElementMap;
  69. CelxLua::FlagMap CelxLua::OrbitVisibilityMap;
  70. CelxLua::ColorMap CelxLua::LineColorMap;
  71. CelxLua::ColorMap CelxLua::LabelColorMap;
  72. bool CelxLua::mapsInitialized = false;
  73. #define CLASS(i) ClassNames[(i)]
  74. // Maximum timeslice a script may run without
  75. // returning control to celestia
  76. static const double MaxTimeslice = 5.0;
  77. // names of callback-functions in Lua:
  78. static const char* KbdCallback = "celestia_keyboard_callback";
  79. static const char* CleanupCallback = "celestia_cleanup_callback";
  80. static const char* EventHandlers = "celestia_event_handlers";
  81. static const char* KeyHandler        = "key";
  82. static const char* TickHandler       = "tick";
  83. static const char* MouseDownHandler  = "mousedown";
  84. static const char* MouseUpHandler    = "mouseup";
  85. // Initialize various maps from named keywords to numeric flags used within celestia:
  86. void CelxLua::initRenderFlagMap()
  87. {
  88.     RenderFlagMap["orbits"] = Renderer::ShowOrbits;
  89.     RenderFlagMap["cloudmaps"] = Renderer::ShowCloudMaps;
  90.     RenderFlagMap["constellations"] = Renderer::ShowDiagrams;
  91.     RenderFlagMap["galaxies"] = Renderer::ShowGalaxies;
  92. RenderFlagMap["globulars"] = Renderer::ShowGlobulars;     
  93. RenderFlagMap["planets"] = Renderer::ShowPlanets;
  94.     RenderFlagMap["stars"] = Renderer::ShowStars;
  95.     RenderFlagMap["nightmaps"] = Renderer::ShowNightMaps;
  96.     RenderFlagMap["eclipseshadows"] = Renderer::ShowEclipseShadows;
  97.     RenderFlagMap["ringshadows"] = Renderer::ShowRingShadows;
  98.     RenderFlagMap["comettails"] = Renderer::ShowCometTails;
  99.     RenderFlagMap["boundaries"] = Renderer::ShowBoundaries;
  100.     RenderFlagMap["markers"] = Renderer::ShowMarkers;
  101.     RenderFlagMap["automag"] = Renderer::ShowAutoMag;
  102.     RenderFlagMap["atmospheres"] = Renderer::ShowAtmospheres;
  103.     RenderFlagMap["grid"] = Renderer::ShowCelestialSphere;
  104.     //RenderFlagMap["equatorialgrid"] = Renderer::ShowCelestialSphere;
  105.     RenderFlagMap["galacticgrid"] = Renderer::ShowGalacticGrid;
  106.     RenderFlagMap["eclipticgrid"] = Renderer::ShowEclipticGrid;
  107.     RenderFlagMap["horizontalgrid"] = Renderer::ShowHorizonGrid;
  108.     RenderFlagMap["smoothlines"] = Renderer::ShowSmoothLines;
  109.     RenderFlagMap["partialtrajectories"] = Renderer::ShowPartialTrajectories;
  110.     RenderFlagMap["nebulae"] = Renderer::ShowNebulae;
  111.     RenderFlagMap["openclusters"] = Renderer::ShowOpenClusters;
  112.     RenderFlagMap["cloudshadows"] = Renderer::ShowCloudShadows;
  113.     RenderFlagMap["ecliptic"] = Renderer::ShowEcliptic;
  114. }
  115. void CelxLua::initLabelFlagMap()
  116. {
  117.     LabelFlagMap["planets"] = Renderer::PlanetLabels;
  118.     LabelFlagMap["dwarfplanets"] = Renderer::DwarfPlanetLabels;
  119.     LabelFlagMap["moons"] = Renderer::MoonLabels;
  120.     LabelFlagMap["minormoons"] = Renderer::MinorMoonLabels;
  121.     LabelFlagMap["spacecraft"] = Renderer::SpacecraftLabels;
  122.     LabelFlagMap["asteroids"] = Renderer::AsteroidLabels;
  123.     LabelFlagMap["comets"] = Renderer::CometLabels;
  124.     LabelFlagMap["constellations"] = Renderer::ConstellationLabels;
  125.     LabelFlagMap["stars"] = Renderer::StarLabels;
  126.     LabelFlagMap["galaxies"] = Renderer::GalaxyLabels;
  127. LabelFlagMap["globulars"] = Renderer::GlobularLabels;
  128.     LabelFlagMap["locations"] = Renderer::LocationLabels;
  129.     LabelFlagMap["nebulae"] = Renderer::NebulaLabels;
  130.     LabelFlagMap["openclusters"] = Renderer::OpenClusterLabels;
  131.     LabelFlagMap["i18nconstellations"] = Renderer::I18nConstellationLabels;
  132. }
  133. void CelxLua::initBodyTypeMap()
  134. {
  135.     BodyTypeMap["Planet"] = Body::Planet;
  136.     BodyTypeMap["DwarfPlanet"] = Body::DwarfPlanet;
  137.     BodyTypeMap["Moon"] = Body::Moon;
  138.     BodyTypeMap["MinorMoon"] = Body::MinorMoon;
  139.     BodyTypeMap["Asteroid"] = Body::Asteroid;
  140.     BodyTypeMap["Comet"] = Body::Comet;
  141.     BodyTypeMap["Spacecraft"] = Body::Spacecraft;
  142.     BodyTypeMap["Invisible"] = Body::Invisible;
  143.     BodyTypeMap["Star"] = Body::Stellar;
  144.     BodyTypeMap["Unknown"] = Body::Unknown;
  145. }
  146. void CelxLua::initLocationFlagMap()
  147. {
  148.     LocationFlagMap["city"] = Location::City;
  149.     LocationFlagMap["observatory"] = Location::Observatory;
  150.     LocationFlagMap["landingsite"] = Location::LandingSite;
  151.     LocationFlagMap["crater"] = Location::Crater;
  152.     LocationFlagMap["vallis"] = Location::Vallis;
  153.     LocationFlagMap["mons"] = Location::Mons;
  154.     LocationFlagMap["planum"] = Location::Planum;
  155.     LocationFlagMap["chasma"] = Location::Chasma;
  156.     LocationFlagMap["patera"] = Location::Patera;
  157.     LocationFlagMap["mare"] = Location::Mare;
  158.     LocationFlagMap["rupes"] = Location::Rupes;
  159.     LocationFlagMap["tessera"] = Location::Tessera;
  160.     LocationFlagMap["regio"] = Location::Regio;
  161.     LocationFlagMap["chaos"] = Location::Chaos;
  162.     LocationFlagMap["terra"] = Location::Terra;
  163.     LocationFlagMap["volcano"] = Location::EruptiveCenter;
  164.     LocationFlagMap["astrum"] = Location::Astrum;
  165.     LocationFlagMap["corona"] = Location::Corona;
  166.     LocationFlagMap["dorsum"] = Location::Dorsum;
  167.     LocationFlagMap["fossa"] = Location::Fossa;
  168.     LocationFlagMap["catena"] = Location::Catena;
  169.     LocationFlagMap["mensa"] = Location::Mensa;
  170.     LocationFlagMap["rima"] = Location::Rima;
  171.     LocationFlagMap["undae"] = Location::Undae;
  172.     LocationFlagMap["tholus"] = Location::Tholus;
  173.     LocationFlagMap["reticulum"] = Location::Reticulum;
  174.     LocationFlagMap["planitia"] = Location::Planitia;
  175.     LocationFlagMap["linea"] = Location::Linea;
  176.     LocationFlagMap["fluctus"] = Location::Fluctus;
  177.     LocationFlagMap["farrum"] = Location::Farrum;
  178.     LocationFlagMap["insula"] = Location::Insula;
  179.     LocationFlagMap["other"] = Location::Other;
  180. }
  181. void CelxLua::initOverlayElementMap()
  182. {
  183.     OverlayElementMap["Time"] = CelestiaCore::ShowTime;
  184.     OverlayElementMap["Velocity"] = CelestiaCore::ShowVelocity;
  185.     OverlayElementMap["Selection"] = CelestiaCore::ShowSelection;
  186.     OverlayElementMap["Frame"] = CelestiaCore::ShowFrame;
  187. }
  188. void CelxLua::initOrbitVisibilityMap()
  189. {
  190.     OrbitVisibilityMap["never"] = Body::NeverVisible;
  191.     OrbitVisibilityMap["normal"] = Body::UseClassVisibility;
  192.     OrbitVisibilityMap["always"] = Body::AlwaysVisible;
  193. }
  194. void CelxLua::initLabelColorMap()
  195. {
  196.     LabelColorMap["stars"]          = &Renderer::StarLabelColor;
  197.     LabelColorMap["planets"]        = &Renderer::PlanetLabelColor;
  198.     LabelColorMap["dwarfplanets"]   = &Renderer::DwarfPlanetLabelColor;
  199.     LabelColorMap["moons"]          = &Renderer::MoonLabelColor;
  200.     LabelColorMap["minormoons"]     = &Renderer::MinorMoonLabelColor;
  201.     LabelColorMap["asteroids"]      = &Renderer::AsteroidLabelColor;
  202.     LabelColorMap["comets"]         = &Renderer::CometLabelColor;
  203.     LabelColorMap["spacecraft"]     = &Renderer::SpacecraftLabelColor;
  204.     LabelColorMap["locations"]      = &Renderer::LocationLabelColor;
  205.     LabelColorMap["galaxies"]       = &Renderer::GalaxyLabelColor;
  206. LabelColorMap["globulars"]      = &Renderer::GlobularLabelColor;
  207.     LabelColorMap["nebulae"]        = &Renderer::NebulaLabelColor;
  208.     LabelColorMap["openclusters"]   = &Renderer::OpenClusterLabelColor;
  209.     LabelColorMap["constellations"] = &Renderer::ConstellationLabelColor;
  210.     LabelColorMap["equatorialgrid"] = &Renderer::EquatorialGridLabelColor;
  211.     LabelColorMap["galacticgrid"]   = &Renderer::GalacticGridLabelColor;
  212.     LabelColorMap["eclipticgrid"]   = &Renderer::EclipticGridLabelColor;
  213.     LabelColorMap["horizontalgrid"] = &Renderer::HorizonGridLabelColor;
  214.     LabelColorMap["planetographicgrid"] = &Renderer::PlanetographicGridLabelColor;
  215. }
  216. void CelxLua::initLineColorMap()
  217. {
  218.     LineColorMap["starorbits"]       = &Renderer::StarOrbitColor;
  219.     LineColorMap["planetorbits"]     = &Renderer::PlanetOrbitColor;
  220.     LineColorMap["dwarfplanetorbits"]     = &Renderer::DwarfPlanetOrbitColor;
  221.     LineColorMap["moonorbits"]       = &Renderer::MoonOrbitColor;
  222.     LineColorMap["minormoonorbits"]       = &Renderer::MinorMoonOrbitColor;
  223.     LineColorMap["asteroidorbits"]   = &Renderer::AsteroidOrbitColor;
  224.     LineColorMap["cometorbits"]      = &Renderer::CometOrbitColor;
  225.     LineColorMap["spacecraftorbits"] = &Renderer::SpacecraftOrbitColor;
  226.     LineColorMap["constellations"]   = &Renderer::ConstellationColor;
  227.     LineColorMap["boundaries"]       = &Renderer::BoundaryColor;
  228.     LineColorMap["equatorialgrid"]   = &Renderer::EquatorialGridColor;
  229.     LineColorMap["galacticgrid"]     = &Renderer::GalacticGridColor;
  230.     LineColorMap["eclipticgrid"]     = &Renderer::EclipticGridColor;
  231.     LineColorMap["horizontalgrid"]   = &Renderer::HorizonGridColor;
  232.     LineColorMap["planetographicgrid"]   = &Renderer::PlanetographicGridColor;
  233.     LineColorMap["planetequator"]    = &Renderer::PlanetEquatorColor;
  234.     LineColorMap["ecliptic"]         = &Renderer::EclipticColor;
  235.     LineColorMap["selectioncursor"]  = &Renderer::SelectionCursorColor;
  236. }
  237. #if LUA_VER >= 0x050100
  238. // Load a Lua library--in Lua 5.1, the luaopen_* functions cannot be called
  239. // directly. They most be invoked through the Lua state.
  240. static void openLuaLibrary(lua_State* l,
  241.                            const char* name,
  242.                            lua_CFunction func)
  243. {
  244.     lua_pushcfunction(l, func);
  245.     lua_pushstring(l, name);
  246.     lua_call(l, 1, 0);
  247. }
  248. #endif
  249. void CelxLua::initMaps()
  250. {
  251.     if (!mapsInitialized)
  252.     {
  253.         initRenderFlagMap();
  254.         initLabelFlagMap();
  255.         initBodyTypeMap();
  256.         initLocationFlagMap();
  257.         initOverlayElementMap();
  258.         initOrbitVisibilityMap();
  259.         initLabelColorMap();
  260.         initLineColorMap();
  261.     }
  262.     mapsInitialized = true;
  263. }
  264. static void getField(lua_State* l, int index, const char* key)
  265. {
  266.     // When we move to Lua 5.1, this will be replaced by:
  267.     // lua_getfield(l, index, key);
  268.     lua_pushstring(l, key);
  269.     if (index != LUA_GLOBALSINDEX && index != LUA_REGISTRYINDEX)
  270.         lua_gettable(l, index - 1);
  271.     else
  272.         lua_gettable(l, index);
  273. }
  274. // Wrapper for a CEL-script, including the needed Execution Environment
  275. class CelScriptWrapper : public ExecutionEnvironment
  276. {
  277.  public:
  278.     CelScriptWrapper(CelestiaCore& appCore, istream& scriptfile):
  279.         script(NULL),
  280.         core(appCore),
  281.         cmdSequence(NULL),
  282.         tickTime(0.0),
  283.         errorMessage("")
  284.     {
  285.         CommandParser parser(scriptfile);
  286.         cmdSequence = parser.parse();
  287.         if (cmdSequence != NULL)
  288.         {
  289.             script = new Execution(*cmdSequence, *this);
  290.         }
  291.         else
  292.         {
  293.             const vector<string>* errors = parser.getErrors();
  294.             if (errors->size() > 0)
  295.                 errorMessage = "Error while parsing CEL-script: " + (*errors)[0];
  296.             else
  297.                 errorMessage = "Error while parsing CEL-script.";
  298.         }
  299.     }
  300.     virtual ~CelScriptWrapper()
  301.     {
  302.         if (script != NULL)
  303.             delete script;
  304.         if (cmdSequence != NULL)
  305.             delete cmdSequence;
  306.     }
  307.     string getErrorMessage() const
  308.     {
  309.         return errorMessage;
  310.     }
  311.     // tick the CEL-script. t is in seconds and doesn't have to start with zero
  312.     bool tick(double t)
  313.     {
  314.         // use first tick to set the time
  315.         if (tickTime == 0.0)
  316.         {
  317.             tickTime = t;
  318.             return false;
  319.         }
  320.         double dt = t - tickTime;
  321.         tickTime = t;
  322.         return script->tick(dt);
  323.     }
  324.     Simulation* getSimulation() const
  325.     {
  326.         return core.getSimulation();
  327.     }
  328.     Renderer* getRenderer() const
  329.     {
  330.         return core.getRenderer();
  331.     }
  332.     CelestiaCore* getCelestiaCore() const
  333.     {
  334.         return &core;
  335.     }
  336.     void showText(string s, int horig, int vorig, int hoff, int voff,
  337.                   double duration)
  338.     {
  339.         core.showText(s, horig, vorig, hoff, voff, duration);
  340.     }
  341.  private:
  342.     Execution* script;
  343.     CelestiaCore& core;
  344.     CommandSequence* cmdSequence;
  345.     double tickTime;
  346.     string errorMessage;
  347. };
  348. // Push a class name onto the Lua stack
  349. static void PushClass(lua_State* l, int id)
  350. {
  351.     lua_pushlstring(l, CelxLua::ClassNames[id], strlen(CelxLua::ClassNames[id]));
  352. }
  353. // Set the class (metatable) of the object on top of the stack
  354. void Celx_SetClass(lua_State* l, int id)
  355. {
  356.     PushClass(l, id);
  357.     lua_rawget(l, LUA_REGISTRYINDEX);
  358.     if (lua_type(l, -1) != LUA_TTABLE)
  359.         cout << "Metatable for " << CelxLua::ClassNames[id] << " not found!n";
  360.     if (lua_setmetatable(l, -2) == 0)
  361.         cout << "Error setting metatable for " << CelxLua::ClassNames[id] << 'n';
  362. }
  363. // Initialize the metatable for a class; sets the appropriate registry
  364. // entries and __index, leaving the metatable on the stack when done.
  365. void Celx_CreateClassMetatable(lua_State* l, int id)
  366. {
  367.     lua_newtable(l);
  368.     PushClass(l, id);
  369.     lua_pushvalue(l, -2);
  370.     lua_rawset(l, LUA_REGISTRYINDEX); // registry.name = metatable
  371.     lua_pushvalue(l, -1);
  372.     PushClass(l, id);
  373.     lua_rawset(l, LUA_REGISTRYINDEX); // registry.metatable = name
  374.     lua_pushliteral(l, "__index");
  375.     lua_pushvalue(l, -2);
  376.     lua_rawset(l, -3);
  377. }
  378. // Register a class 'method' in the metatable (assumed to be on top of the stack)
  379. void Celx_RegisterMethod(lua_State* l, const char* name, lua_CFunction fn)
  380. {
  381.     lua_pushstring(l, name);
  382.     lua_pushvalue(l, -2);
  383.     lua_pushcclosure(l, fn, 1);
  384.     lua_settable(l, -3);
  385. }
  386. // Verify that an object at location index on the stack is of the
  387. // specified class
  388. static bool Celx_istype(lua_State* l, int index, int id)
  389. {
  390.     // get registry[metatable]
  391.     if (!lua_getmetatable(l, index))
  392.         return false;
  393.     lua_rawget(l, LUA_REGISTRYINDEX);
  394.     if (lua_type(l, -1) != LUA_TSTRING)
  395.     {
  396.         cout << "Celx_istype failed!  Unregistered class.n";
  397.         lua_pop(l, 1);
  398.         return false;
  399.     }
  400.     const char* classname = lua_tostring(l, -1);
  401.     if (classname != NULL && strcmp(classname, CelxLua::ClassNames[id]) == 0)
  402.     {
  403.         lua_pop(l, 1);
  404.         return true;
  405.     }
  406.     lua_pop(l, 1);
  407.     return false;
  408. }
  409. // Verify that an object at location index on the stack is of the
  410. // specified class and return pointer to userdata
  411. void* Celx_CheckUserData(lua_State* l, int index, int id)
  412. {
  413.     if (Celx_istype(l, index, id))
  414.         return lua_touserdata(l, index);
  415.     else
  416.         return NULL;
  417. }
  418. // Return the CelestiaCore object stored in the globals table
  419. static CelestiaCore* getAppCore(lua_State* l, FatalErrors fatalErrors = NoErrors)
  420. {
  421.     lua_pushstring(l, "celestia-appcore");
  422.     lua_gettable(l, LUA_REGISTRYINDEX);
  423.     if (!lua_islightuserdata(l, -1))
  424.     {
  425.         if (fatalErrors == NoErrors)
  426.             return NULL;
  427.         else
  428.         {
  429.             lua_pushstring(l, "internal error: invalid appCore");
  430.             lua_error(l);
  431.         }
  432.     }
  433.     CelestiaCore* appCore = static_cast<CelestiaCore*>(lua_touserdata(l, -1));
  434.     lua_pop(l, 1);
  435.     return appCore;
  436. }
  437. LuaState::LuaState() :
  438.     timeout(MaxTimeslice),
  439.     state(NULL),
  440.     costate(NULL),
  441.     alive(false),
  442.     timer(NULL),
  443.     scriptAwakenTime(0.0),
  444.     ioMode(NoIO),
  445.     eventHandlerEnabled(false)
  446. {
  447.     state = lua_open();
  448.     timer = CreateTimer();
  449.     screenshotCount = 0;
  450. }
  451. LuaState::~LuaState()
  452. {
  453.     delete timer;
  454.     if (state != NULL)
  455.         lua_close(state);
  456. #if 0
  457.     if (costate != NULL)
  458.         lua_close(costate);
  459. #endif
  460. }
  461. lua_State* LuaState::getState() const
  462. {
  463.     return state;
  464. }
  465. double LuaState::getTime() const
  466. {
  467.     return timer->getTime();
  468. }
  469. // Check if the running script has exceeded its allowed timeslice
  470. // and terminate it if it has:
  471. static void checkTimeslice(lua_State* l, lua_Debug* /*ar*/)
  472. {
  473.     lua_pushstring(l, "celestia-luastate");
  474.     lua_gettable(l, LUA_REGISTRYINDEX);
  475.     if (!lua_islightuserdata(l, -1))
  476.     {
  477.         lua_pushstring(l, "Internal Error: Invalid table entry in checkTimeslice");
  478.         lua_error(l);
  479.     }
  480.     LuaState* luastate = static_cast<LuaState*>(lua_touserdata(l, -1));
  481.     if (luastate == NULL)
  482.     {
  483.         lua_pushstring(l, "Internal Error: Invalid value in checkTimeslice");
  484.         lua_error(l);
  485.     }
  486.     if (luastate->timesliceExpired())
  487.     {
  488.         const char* errormsg = "Timeout: script hasn't returned control to celestia (forgot to call wait()?)";
  489.         cerr << errormsg << "n";
  490.         lua_pushstring(l, errormsg);
  491.         lua_error(l);
  492.     }
  493.     return;
  494. }
  495. // allow the script to perform cleanup
  496. void LuaState::cleanup()
  497. {
  498.     if (ioMode == Asking)
  499.     {
  500.         // Restore renderflags:
  501.         CelestiaCore* appCore = getAppCore(costate, NoErrors);
  502.         if (appCore != NULL)
  503.         {
  504.             lua_pushstring(state, "celestia-savedrenderflags");
  505.             lua_gettable(state, LUA_REGISTRYINDEX);
  506.             if (lua_isuserdata(state, -1))
  507.             {
  508.                 int* savedrenderflags = static_cast<int*>(lua_touserdata(state, -1));
  509.                 appCore->getRenderer()->setRenderFlags(*savedrenderflags);
  510.                 // now delete entry:
  511.                 lua_pushstring(state, "celestia-savedrenderflags");
  512.                 lua_pushnil(state);
  513.                 lua_settable(state, LUA_REGISTRYINDEX);
  514.             }
  515.             lua_pop(state,1);
  516.         }
  517.     }
  518.     lua_pushstring(costate, CleanupCallback);
  519.     lua_gettable(costate, LUA_GLOBALSINDEX);
  520.     if (lua_isnil(costate, -1))
  521.     {
  522.         return;
  523.     }
  524.     timeout = getTime() + 1.0;
  525.     if (lua_pcall(costate, 0, 0, 0) != 0)
  526.     {
  527.         cerr << "Error while executing cleanup-callback: " << lua_tostring(costate, -1) << "n";
  528.     }
  529. }
  530. bool LuaState::createThread()
  531. {
  532.     // Initialize the coroutine which wraps the script
  533.     if (!(lua_isfunction(state, -1) && !lua_iscfunction(state, -1)))
  534.     {
  535.         // Should never happen; we manually set up the stack in C++
  536.         assert(0);
  537.         return false;
  538.     }
  539.     else
  540.     {
  541.         costate = lua_newthread(state);
  542.         if (costate == NULL)
  543.             return false;
  544.         lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1000);
  545.         lua_pushvalue(state, -2);
  546.         lua_xmove(state, costate, 1);  /* move function from L to NL */
  547.         alive = true;
  548.         return true;
  549.     }
  550. }
  551. string LuaState::getErrorMessage()
  552. {
  553.     if (lua_gettop(state) > 0)
  554.     {
  555.         if (lua_isstring(state, -1))
  556.             return lua_tostring(state, -1);
  557.     }
  558.     return "";
  559. }
  560. bool LuaState::timesliceExpired()
  561. {
  562.     if (timeout < getTime())
  563.     {
  564.         // timeslice expired, make every instruction (including pcall) fail:
  565.         lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1);
  566.         return true;
  567.     }
  568.     else
  569.     {
  570.         return false;
  571.     }
  572. }
  573. static int resumeLuaThread(lua_State *L, lua_State *co, int narg)
  574. {
  575.     int status;
  576.     //if (!lua_checkstack(co, narg))
  577.     //   luaL_error(L, "too many arguments to resume");
  578.     lua_xmove(L, co, narg);
  579.     status = lua_resume(co, narg);
  580. #if LUA_VER >= 0x050100
  581.     if (status == 0 || status == LUA_YIELD)
  582. #else
  583.     if (status == 0)
  584. #endif
  585.     {
  586.         int nres = lua_gettop(co);
  587.         //if (!lua_checkstack(L, narg))
  588.         //   luaL_error(L, "too many results to resume");
  589.         lua_xmove(co, L, nres);  // move yielded values
  590.         return nres;
  591.     }
  592.     else
  593.     {
  594.         lua_xmove(co, L, 1);  // move error message
  595.         return -1;            // error flag
  596.     }
  597. }
  598. bool LuaState::isAlive() const
  599. {
  600.     return alive;
  601. }
  602. struct ReadChunkInfo
  603. {
  604.     char* buf;
  605.     int bufSize;
  606.     istream* in;
  607. };
  608. static const char* readStreamChunk(lua_State*, void* udata, size_t* size)
  609. {
  610.     assert(udata != NULL);
  611.     if (udata == NULL)
  612.         return NULL;
  613.     ReadChunkInfo* info = reinterpret_cast<ReadChunkInfo*>(udata);
  614.     assert(info->buf != NULL);
  615.     assert(info->in != NULL);
  616.     if (!info->in->good())
  617.     {
  618.         *size = 0;
  619.         return NULL;
  620.     }
  621.     info->in->read(info->buf, info->bufSize);
  622.     streamsize nread = info->in->gcount();
  623.     *size = (size_t) nread;
  624.     if (nread == 0)
  625.         return NULL;
  626.     else
  627.         return info->buf;
  628. }
  629. // Callback for CelestiaCore::charEntered.
  630. // Returns true if keypress has been consumed
  631. bool LuaState::charEntered(const char* c_p)
  632. {
  633.     if (ioMode == Asking && getTime() > timeout)
  634.     {
  635.         int stackTop = lua_gettop(costate);
  636.         if (strcmp(c_p, "y") == 0)
  637.         {
  638. #if LUA_VER >= 0x050100
  639.             openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package);
  640.             openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io);
  641.             openLuaLibrary(costate, LUA_OSLIBNAME, luaopen_os);
  642. #else
  643.             lua_iolibopen(costate);
  644. #endif
  645.             ioMode = IOAllowed;
  646.         }
  647.         else
  648.         {
  649.             ioMode = IODenied;
  650.         }
  651.         CelestiaCore* appCore = getAppCore(costate, NoErrors);
  652.         if (appCore == NULL)
  653.         {
  654.             cerr << "ERROR: appCore not foundn";
  655.             return true;
  656.         }
  657.         appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript);
  658.         appCore->showText("", 0, 0, 0, 0);
  659.         // Restore renderflags:
  660.         lua_pushstring(costate, "celestia-savedrenderflags");
  661.         lua_gettable(costate, LUA_REGISTRYINDEX);
  662.         if (lua_isuserdata(costate, -1))
  663.         {
  664.             int* savedrenderflags = static_cast<int*>(lua_touserdata(costate, -1));
  665.             appCore->getRenderer()->setRenderFlags(*savedrenderflags);
  666.             // now delete entry:
  667.             lua_pushstring(costate, "celestia-savedrenderflags");
  668.             lua_pushnil(costate);
  669.             lua_settable(costate, LUA_REGISTRYINDEX);
  670.         }
  671.         else
  672.         {
  673.             cerr << "Oops, expected savedrenderflags to be userdatan";
  674.         }
  675.         lua_settop(costate,stackTop);
  676.         return true;
  677.     }
  678. #if LUA_VER < 0x050100
  679.     int stack_top = lua_gettop(costate);
  680. #endif
  681.     bool result = true;
  682.     lua_pushstring(costate, KbdCallback);
  683.     lua_gettable(costate, LUA_GLOBALSINDEX);
  684.     lua_pushstring(costate, c_p);
  685.     timeout = getTime() + 1.0;
  686.     if (lua_pcall(costate, 1, 1, 0) != 0)
  687.     {
  688.         cerr << "Error while executing keyboard-callback: " << lua_tostring(costate, -1) << "n";
  689.         result = false;
  690.     }
  691.     else
  692.     {
  693.         if (lua_isboolean(costate, -1))
  694.         {
  695.             result = (lua_toboolean(costate, -1) != 0);
  696.         }
  697. lua_pop(costate, 1);
  698.     }
  699. #if LUA_VER < 0x050100
  700.     // cleanup stack - is this necessary?
  701.     lua_settop(costate, stack_top);
  702. #endif
  703.     return result;
  704. }
  705. // Returns true if a handler is registered for the key
  706. bool LuaState::handleKeyEvent(const char* key)
  707. {
  708.     CelestiaCore* appCore = getAppCore(costate, NoErrors);
  709.     if (appCore == NULL)
  710.     {
  711.         return false;
  712.     }
  713.     // get the registered event table
  714.     getField(costate, LUA_REGISTRYINDEX, EventHandlers);
  715.     if (!lua_istable(costate, -1))
  716.     {
  717.         cerr << "Missing event handler table";
  718.         lua_pop(costate, 1);
  719.         return false;
  720.     }
  721.     bool handled = false;
  722.     getField(costate, -1, KeyHandler);
  723.     if (lua_isfunction(costate, -1))
  724.     {
  725.         lua_remove(costate, -2);        // remove the key event table from the stack
  726.         lua_newtable(costate);
  727.         lua_pushstring(costate, "char");
  728.         lua_pushstring(costate, key);   // the default key handler accepts the key name as an argument
  729.         lua_settable(costate, -3);
  730.         timeout = getTime() + 1.0;
  731.         if (lua_pcall(costate, 1, 1, 0) != 0)
  732.         {
  733.             cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "n";
  734.         }
  735.         else
  736.         {
  737.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  738.         }
  739.         lua_pop(costate, 1);             // pop the return value
  740.     }
  741.     else
  742.     {
  743.         lua_pop(costate, 2);
  744.     }
  745.     return handled;
  746. }
  747. // Returns true if a handler is registered for the button event
  748. bool LuaState::handleMouseButtonEvent(float x, float y, int button, bool down)
  749. {
  750.     CelestiaCore* appCore = getAppCore(costate, NoErrors);
  751.     if (appCore == NULL)
  752.     {
  753.         return false;
  754.     }
  755.     // get the registered event table
  756.     getField(costate, LUA_REGISTRYINDEX, EventHandlers);
  757.     if (!lua_istable(costate, -1))
  758.     {
  759.         cerr << "Missing event handler table";
  760.         lua_pop(costate, 1);
  761.         return false;
  762.     }
  763.     bool handled = false;
  764.     getField(costate, -1, down ? MouseDownHandler : MouseUpHandler);
  765.     if (lua_isfunction(costate, -1))
  766.     {
  767.         lua_remove(costate, -2);        // remove the key event table from the stack
  768.         lua_newtable(costate);
  769.         lua_pushstring(costate, "button");
  770.         lua_pushnumber(costate, button);
  771.         lua_settable(costate, -3);
  772.         lua_pushstring(costate, "x");
  773.         lua_pushnumber(costate, x);
  774.         lua_settable(costate, -3);
  775.         lua_pushstring(costate, "y");
  776.         lua_pushnumber(costate, y);
  777.         lua_settable(costate, -3);
  778.         timeout = getTime() + 1.0;
  779.         if (lua_pcall(costate, 1, 1, 0) != 0)
  780.         {
  781.             cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "n";
  782.         }
  783.         else
  784.         {
  785.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  786.         }
  787.         lua_pop(costate, 1);             // pop the return value
  788.     }
  789.     else
  790.     {
  791.         lua_pop(costate, 2);
  792.     }
  793.     return handled;
  794. }
  795. // Returns true if a handler is registered for the tick event
  796. bool LuaState::handleTickEvent(double dt)
  797. {
  798.     CelestiaCore* appCore = getAppCore(costate, NoErrors);
  799.     if (appCore == NULL)
  800.     {
  801.         return false;
  802.     }
  803.     // get the registered event table
  804.     getField(costate, LUA_REGISTRYINDEX, EventHandlers);
  805.     if (!lua_istable(costate, -1))
  806.     {
  807.         cerr << "Missing event handler table";
  808.         lua_pop(costate, 1);
  809.         return false;
  810.     }
  811.     bool handled = false;
  812.     getField(costate, -1, TickHandler);
  813.     if (lua_isfunction(costate, -1))
  814.     {
  815.         lua_remove(costate, -2);        // remove the key event table from the stack
  816.         lua_newtable(costate);
  817.         lua_pushstring(costate, "dt");
  818.         lua_pushnumber(costate, dt);   // the default key handler accepts the key name as an argument
  819.         lua_settable(costate, -3);
  820.         timeout = getTime() + 1.0;
  821.         if (lua_pcall(costate, 1, 1, 0) != 0)
  822.         {
  823.             cerr << "Error while executing tick callback: " << lua_tostring(costate, -1) << "n";
  824.         }
  825.         else
  826.         {
  827.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  828.         }
  829.         lua_pop(costate, 1);             // pop the return value
  830.     }
  831.     else
  832.     {
  833.         lua_pop(costate, 2);
  834.     }
  835.     return handled;
  836. }
  837. int LuaState::loadScript(istream& in, const string& streamname)
  838. {
  839.     char buf[4096];
  840.     ReadChunkInfo info;
  841.     info.buf = buf;
  842.     info.bufSize = sizeof(buf);
  843.     info.in = &in;
  844.     if (streamname != "string")
  845.     {
  846.         lua_pushstring(state, "celestia-scriptpath");
  847.         lua_pushstring(state, streamname.c_str());
  848.         lua_settable(state, LUA_REGISTRYINDEX);
  849.     }
  850.     int status = lua_load(state, readStreamChunk, &info, streamname.c_str());
  851.     if (status != 0)
  852.         cout << "Error loading script: " << lua_tostring(state, -1) << 'n';
  853.     return status;
  854. }
  855. int LuaState::loadScript(const string& s)
  856. {
  857.     istringstream in(s);
  858.     return loadScript(in, "string");
  859. }
  860. // Resume a thread; if the thread completes, the status is set to !alive
  861. int LuaState::resume()
  862. {
  863.     assert(costate != NULL);
  864.     if (costate == NULL)
  865.         return false;
  866.     lua_State* co = lua_tothread(state, -1);
  867.     //assert(co == costate); // co can be NULL after error (top stack is errorstring)
  868.     if (co != costate)
  869.         return false;
  870.     timeout = getTime() + MaxTimeslice;
  871.     int nArgs = resumeLuaThread(state, co, 0);
  872.     if (nArgs < 0)
  873.     {
  874.         alive = false;
  875.         const char* errorMessage = lua_tostring(state, -1);
  876.         if (errorMessage == NULL)
  877.             errorMessage = "Unknown script error";
  878. #if LUA_VER < 0x050100
  879.         // This is a nasty hack required in Lua 5.0, where there's no
  880.         // way to distinguish between a thread returning because it completed
  881.         // or yielded. Thus, we continue to resume the script until we get
  882.         // an error.  The 'cannot resume dead coroutine' error appears when
  883.         // there were no other errors, and execution terminates normally.
  884.         // In Lua 5.1, we can simply check the thread status to find out
  885.         // if it's done executing.
  886.         if (strcmp(errorMessage, "cannot resume dead coroutine") != 0)
  887. #endif
  888.         {
  889.             cout << "Error: " << errorMessage << 'n';
  890.             CelestiaCore* appCore = getAppCore(co);
  891.             if (appCore != NULL)
  892.             {
  893.                 CelestiaCore::Alerter* alerter = appCore->getAlerter();
  894.                 alerter->fatalError(errorMessage);
  895.             }
  896.         }
  897.         return 1; // just the error string
  898.     }
  899.     else
  900.     {
  901.         if (ioMode == Asking)
  902.         {
  903.             // timeout now is used to first only display warning, and 1s
  904.             // later allow response to avoid accidental activation
  905.             timeout = getTime() + 1.0;
  906.         }
  907. #if LUA_VER >= 0x050100
  908.         // The thread status is zero if it has terminated normally
  909.         if (lua_status(co) == 0)
  910.         {
  911.             alive = false;
  912.         }
  913. #endif
  914.         return nArgs; // arguments from yield
  915.     }
  916. }
  917. // get current linenumber of script and create
  918. // useful error-message
  919. void Celx_DoError(lua_State* l, const char* errorMsg)
  920. {
  921.     lua_Debug debug;
  922.     if (lua_getstack(l, 1, &debug))
  923.     {
  924.         char buf[1024];
  925.         if (lua_getinfo(l, "l", &debug))
  926.         {
  927.             sprintf(buf, "In line %i: %s", debug.currentline, errorMsg);
  928.             lua_pushstring(l, buf);
  929.             lua_error(l);
  930.         }
  931.     }
  932.     lua_pushstring(l, errorMsg);
  933.     lua_error(l);
  934. }
  935. bool LuaState::tick(double dt)
  936. {
  937.     // Due to the way CelestiaCore::tick is called (at least for KDE),
  938.     // this method may be entered a second time when we show the error-alerter
  939.     // Workaround: check if we are alive, return true(!) when we aren't anymore
  940.     // this way the script isn't deleted after the second enter, but only
  941.     // when we return from the first enter. OMG.
  942.     // better Solution: defer showing the error-alterter to CelestiaCore, using
  943.     // getErrorMessage()
  944.     if (!isAlive())
  945.         return false;
  946.     if (ioMode == Asking)
  947.     {
  948.         CelestiaCore* appCore = getAppCore(costate, NoErrors);
  949.         if (appCore == NULL)
  950.         {
  951.             cerr << "ERROR: appCore not foundn";
  952.             return true;
  953.         }
  954.         lua_pushstring(state, "celestia-savedrenderflags");
  955.         lua_gettable(state, LUA_REGISTRYINDEX);
  956.         if (lua_isnil(state, -1))
  957.         {
  958.             lua_pushstring(state, "celestia-savedrenderflags");
  959.             int* savedrenderflags = static_cast<int*>(lua_newuserdata(state, sizeof(int)));
  960.             *savedrenderflags = appCore->getRenderer()->getRenderFlags();
  961.             lua_settable(state, LUA_REGISTRYINDEX);
  962.             appCore->getRenderer()->setRenderFlags(0);
  963.         }
  964.         // now pop result of gettable
  965.         lua_pop(state, 1);
  966.         if (getTime() > timeout)
  967.         {
  968.             appCore->showText("WARNING:nnThis script requests permission to read/write filesn"
  969.                               "and execute external programs. Allowing this can ben"
  970.                               "dangerous.n"
  971.                               "Do you trust the script and want to allow this?nn"
  972.                               "y = yes, ESC = cancel script, any other key = no",
  973.                               0, 0,
  974.                               -15, 5, 5);
  975.             appCore->setTextEnterMode(appCore->getTextEnterMode() | CelestiaCore::KbPassToScript);
  976.         }
  977.         else
  978.         {
  979.             appCore->showText("WARNING:nnThis script requests permission to read/write filesn"
  980.                               "and execute external programs. Allowing this can ben"
  981.                               "dangerous.n"
  982.                               "Do you trust the script and want to allow this?",
  983.                               0, 0,
  984.                               -15, 5, 5);
  985.             appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript);
  986.         }
  987.         return false;
  988.     }
  989.     if (dt == 0 || scriptAwakenTime > getTime())
  990.         return false;
  991.     int nArgs = resume();
  992.     if (!isAlive())
  993.     {
  994.         // The script is complete
  995.         return true;
  996.     }
  997.     else
  998.     {
  999.         // The script has returned control to us, but it is not completed.
  1000.         lua_State* state = getState();
  1001.         // The values on the stack indicate what event will wake up the
  1002.         // script.  For now, we just support wait()
  1003.         double delay;
  1004.         if (nArgs == 1 && lua_isnumber(state, -1))
  1005.             delay = lua_tonumber(state, -1);
  1006.         else
  1007.             delay = 0.0;
  1008.         scriptAwakenTime = getTime() + delay;
  1009.         // Clean up the stack
  1010.         lua_pop(state, nArgs);
  1011.         return false;
  1012.     }
  1013. }
  1014. void LuaState::requestIO()
  1015. {
  1016.     // the script requested IO, set the mode
  1017.     // so we display the warning during tick
  1018.     // and can request keyboard. We can't do this now
  1019.     // because the script is still active and could
  1020.     // disable keyboard again.
  1021.     if (ioMode == NoIO)
  1022.     {
  1023.         CelestiaCore* appCore = getAppCore(state, AllErrors);
  1024.         string policy = appCore->getConfig()->scriptSystemAccessPolicy;
  1025.         if (policy == "allow")
  1026.         {
  1027. #if LUA_VER >= 0x050100
  1028.             openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package);
  1029.             openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io);
  1030.             openLuaLibrary(costate, LUA_OSLIBNAME, luaopen_os);
  1031. #else
  1032.             lua_iolibopen(costate);
  1033. #endif
  1034.             //luaopen_io(costate);
  1035.             ioMode = IOAllowed;
  1036.         }
  1037.         else if (policy == "deny")
  1038.         {
  1039.             ioMode = IODenied;
  1040.         }
  1041.         else
  1042.         {
  1043.             ioMode = Asking;
  1044.         }
  1045.     }
  1046. }
  1047. // Check if the number of arguments on the stack matches
  1048. // the allowed range [minArgs, maxArgs]. Cause an error if not.
  1049. void Celx_CheckArgs(lua_State* l,
  1050.                     int minArgs, int maxArgs, const char* errorMessage)
  1051. {
  1052.     int argc = lua_gettop(l);
  1053.     if (argc < minArgs || argc > maxArgs)
  1054.     {
  1055.         Celx_DoError(l, errorMessage);
  1056.     }
  1057. }
  1058. static ObserverFrame::CoordinateSystem parseCoordSys(const string& name)
  1059. {
  1060. // 'planetographic' is a deprecated name for bodyfixed, but maintained here
  1061. // for compatibility with older scripts.
  1062.     if (compareIgnoringCase(name, "universal") == 0)
  1063.         return ObserverFrame::Universal;
  1064.     else if (compareIgnoringCase(name, "ecliptic") == 0)
  1065.         return ObserverFrame::Ecliptical;
  1066.     else if (compareIgnoringCase(name, "equatorial") == 0)
  1067.         return ObserverFrame::Equatorial;
  1068.     else if (compareIgnoringCase(name, "bodyfixed") == 0)
  1069.         return ObserverFrame::BodyFixed;
  1070.     else if (compareIgnoringCase(name, "planetographic") == 0)
  1071.         return ObserverFrame::BodyFixed;
  1072.     else if (compareIgnoringCase(name, "observer") == 0)
  1073.         return ObserverFrame::ObserverLocal;
  1074.     else if (compareIgnoringCase(name, "lock") == 0)
  1075.         return ObserverFrame::PhaseLock;
  1076.     else if (compareIgnoringCase(name, "chase") == 0)
  1077.         return ObserverFrame::Chase;
  1078.     else
  1079.         return ObserverFrame::Universal;
  1080. }
  1081. // Get a pointer to the LuaState-object from the registry:
  1082. LuaState* getLuaStateObject(lua_State* l)
  1083. {
  1084.     int stackSize = lua_gettop(l);
  1085.     lua_pushstring(l, "celestia-luastate");
  1086.     lua_gettable(l, LUA_REGISTRYINDEX);
  1087.     if (!lua_islightuserdata(l, -1))
  1088.     {
  1089.         Celx_DoError(l, "Internal Error: Invalid table entry for LuaState-pointer");
  1090.     }
  1091.     LuaState* luastate_ptr = static_cast<LuaState*>(lua_touserdata(l, -1));
  1092.     if (luastate_ptr == NULL)
  1093.     {
  1094.         Celx_DoError(l, "Internal Error: Invalid LuaState-pointer");
  1095.     }
  1096.     lua_settop(l, stackSize);
  1097.     return luastate_ptr;
  1098. }
  1099. // Map the observer to its View. Return NULL if no view exists
  1100. // for this observer (anymore).
  1101. View* getViewByObserver(CelestiaCore* appCore, Observer* obs)
  1102. {
  1103.     for (list<View*>::iterator i = appCore->views.begin(); i != appCore->views.end(); i++)
  1104.         if ((*i)->observer == obs)
  1105.             return *i;
  1106.     return NULL;
  1107. }
  1108. // Fill list with all Observers
  1109. void getObservers(CelestiaCore* appCore, vector<Observer*>& observerList)
  1110. {
  1111.     for (list<View*>::iterator i = appCore->views.begin(); i != appCore->views.end(); i++)
  1112.         if ((*i)->type == View::ViewWindow)
  1113.             observerList.push_back((*i)->observer);
  1114. }
  1115. // ==================== Helpers ====================
  1116. // safe wrapper for lua_tostring: fatal errors will terminate script by calling
  1117. // lua_error with errorMsg.
  1118. static const char* Celx_SafeGetString(lua_State* l,
  1119.                                       int index,
  1120.                                       FatalErrors fatalErrors = AllErrors,
  1121.                                       const char* errorMsg = "String argument expected")
  1122. {
  1123.     if (l == NULL)
  1124.     {
  1125.         cerr << "Error: LuaState invalid in Celx_SafeGetStringn";
  1126.         cout << "Error: LuaState invalid in Celx_SafeGetStringn";
  1127.         return NULL;
  1128.     }
  1129.     int argc = lua_gettop(l);
  1130.     if (index < 1 || index > argc)
  1131.     {
  1132.         if (fatalErrors & WrongArgc)
  1133.         {
  1134.             Celx_DoError(l, errorMsg);
  1135.         }
  1136.         else
  1137.             return NULL;
  1138.     }
  1139.     if (!lua_isstring(l, index))
  1140.     {
  1141.         if (fatalErrors & WrongType)
  1142.         {
  1143.             Celx_DoError(l, errorMsg);
  1144.         }
  1145.         else
  1146.             return NULL;
  1147.     }
  1148.     return lua_tostring(l, index);
  1149. }
  1150. // safe wrapper for lua_tonumber, c.f. Celx_SafeGetString
  1151. // Non-fatal errors will return  defaultValue.
  1152. lua_Number Celx_SafeGetNumber(lua_State* l, int index, FatalErrors fatalErrors = AllErrors,
  1153.                               const char* errorMsg = "Numeric argument expected",
  1154.                               lua_Number defaultValue = 0.0)
  1155. {
  1156.     if (l == NULL)
  1157.     {
  1158.         cerr << "Error: LuaState invalid in Celx_SafeGetNumbern";
  1159.         cout << "Error: LuaState invalid in Celx_SafeGetNumbern";
  1160.         return 0.0;
  1161.     }
  1162.     int argc = lua_gettop(l);
  1163.     if (index < 1 || index > argc)
  1164.     {
  1165.         if (fatalErrors & WrongArgc)
  1166.         {
  1167.             Celx_DoError(l, errorMsg);
  1168.         }
  1169.         else
  1170.             return defaultValue;
  1171.     }
  1172.     if (!lua_isnumber(l, index))
  1173.     {
  1174.         if (fatalErrors & WrongType)
  1175.         {
  1176.             Celx_DoError(l, errorMsg);
  1177.         }
  1178.         else
  1179.             return defaultValue;
  1180.     }
  1181.     return lua_tonumber(l, index);
  1182. }
  1183. // Safe wrapper for lua_tobool, c.f. safeGetString
  1184. // Non-fatal errors will return defaultValue
  1185. bool Celx_SafeGetBoolean(lua_State* l, int index, FatalErrors fatalErrors = AllErrors,
  1186.                               const char* errorMsg = "Boolean argument expected",
  1187.                               bool defaultValue = false)
  1188. {
  1189.     if (l == NULL)
  1190.     {
  1191.         cerr << "Error: LuaState invalid in Celx_SafeGetBooleann";
  1192.         cout << "Error: LuaState invalid in Celx_SafeGetBooleann";
  1193.         return 0.0;
  1194.     }
  1195.     int argc = lua_gettop(l);
  1196.     if (index < 1 || index > argc)
  1197.     {
  1198.         if (fatalErrors & WrongArgc)
  1199.         {
  1200.             Celx_DoError(l, errorMsg);
  1201.         }
  1202.         else
  1203.         {
  1204.             return defaultValue;
  1205.         }
  1206.     }
  1207.     
  1208.     if (!lua_isboolean(l, index))
  1209.     {
  1210.         if (fatalErrors & WrongType)
  1211.         {
  1212.             Celx_DoError(l, errorMsg);
  1213.         }
  1214.         else
  1215.         {
  1216.             return defaultValue;
  1217.         }
  1218.     }
  1219.     
  1220.     return lua_toboolean(l, index) != 0;
  1221. }
  1222. // Add a field to the table on top of the stack
  1223. static void setTable(lua_State* l, const char* field, lua_Number value)
  1224. {
  1225.     lua_pushstring(l, field);
  1226.     lua_pushnumber(l, value);
  1227.     lua_settable(l, -3);
  1228. }
  1229. // ==================== Celscript-object ====================
  1230. // create a CelScriptWrapper from a string:
  1231. static int celscript_from_string(lua_State* l, string& script_text)
  1232. {
  1233.     istringstream scriptfile(script_text);
  1234.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  1235.     CelScriptWrapper* celscript = new CelScriptWrapper(*appCore, scriptfile);
  1236.     if (celscript->getErrorMessage() != "")
  1237.     {
  1238.         string error = celscript->getErrorMessage();
  1239.         delete celscript;
  1240.         Celx_DoError(l, error.c_str());
  1241.     }
  1242.     else
  1243.     {
  1244.         CelScriptWrapper** ud = reinterpret_cast<CelScriptWrapper**>(lua_newuserdata(l, sizeof(CelScriptWrapper*)));
  1245.         *ud = celscript;
  1246.         Celx_SetClass(l, Celx_CelScript);
  1247.     }
  1248.     return 1;
  1249. }
  1250. static CelScriptWrapper* this_celscript(lua_State* l)
  1251. {
  1252.     CelScriptWrapper** script = static_cast<CelScriptWrapper**>(Celx_CheckUserData(l, 1, Celx_CelScript));
  1253.     if (script == NULL)
  1254.     {
  1255.         Celx_DoError(l, "Bad CEL-script object!");
  1256.     }
  1257.     return *script;
  1258. }
  1259. static int celscript_tostring(lua_State* l)
  1260. {
  1261.     lua_pushstring(l, "[Celscript]");
  1262.     return 1;
  1263. }
  1264. static int celscript_tick(lua_State* l)
  1265. {
  1266.     CelScriptWrapper* script = this_celscript(l);
  1267.     LuaState* stateObject = getLuaStateObject(l);
  1268.     double t = stateObject->getTime();
  1269.     lua_pushboolean(l, !(script->tick(t)) );
  1270.     return 1;
  1271. }
  1272. static int celscript_gc(lua_State* l)
  1273. {
  1274.     CelScriptWrapper* script = this_celscript(l);
  1275.     delete script;
  1276.     return 0;
  1277. }
  1278. static void CreateCelscriptMetaTable(lua_State* l)
  1279. {
  1280.     Celx_CreateClassMetatable(l, Celx_CelScript);
  1281.     Celx_RegisterMethod(l, "__tostring", celscript_tostring);
  1282.     Celx_RegisterMethod(l, "tick", celscript_tick);
  1283.     Celx_RegisterMethod(l, "__gc", celscript_gc);
  1284.     lua_pop(l, 1); // remove metatable from stack
  1285. }
  1286. // ==================== Celestia-object ====================
  1287. static int celestia_new(lua_State* l, CelestiaCore* appCore)
  1288. {
  1289.     CelestiaCore** ud = reinterpret_cast<CelestiaCore**>(lua_newuserdata(l, sizeof(CelestiaCore*)));
  1290.     *ud = appCore;
  1291.     Celx_SetClass(l, Celx_Celestia);
  1292.     return 1;
  1293. }
  1294. static CelestiaCore* to_celestia(lua_State* l, int index)
  1295. {
  1296.     CelestiaCore** appCore = static_cast<CelestiaCore**>(Celx_CheckUserData(l, index, Celx_Celestia));
  1297.     if (appCore == NULL)
  1298.         return NULL;
  1299.     else
  1300.         return *appCore;
  1301. }
  1302. static CelestiaCore* this_celestia(lua_State* l)
  1303. {
  1304.     CelestiaCore* appCore = to_celestia(l, 1);
  1305.     if (appCore == NULL)
  1306.     {
  1307.         Celx_DoError(l, "Bad celestia object!");
  1308.     }
  1309.     return appCore;
  1310. }
  1311. static int celestia_flash(lua_State* l)
  1312. {
  1313.     Celx_CheckArgs(l, 2, 3, "One or two arguments expected to function celestia:flash");
  1314.     CelestiaCore* appCore = this_celestia(l);
  1315.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "First argument to celestia:flash must be a string");
  1316.     double duration = Celx_SafeGetNumber(l, 3, WrongType, "Second argument to celestia:flash must be a number", 1.5);
  1317.     if (duration < 0.0)
  1318.     {
  1319.         duration = 1.5;
  1320.     }
  1321.     appCore->flash(s, duration);
  1322.     return 0;
  1323. }
  1324. static int celestia_print(lua_State* l)
  1325. {
  1326.     Celx_CheckArgs(l, 2, 7, "One to six arguments expected to function celestia:print");
  1327.     CelestiaCore* appCore = this_celestia(l);
  1328.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "First argument to celestia:print must be a string");
  1329.     double duration = Celx_SafeGetNumber(l, 3, WrongType, "Second argument to celestia:print must be a number", 1.5);
  1330.     int horig = (int)Celx_SafeGetNumber(l, 4, WrongType, "Third argument to celestia:print must be a number", -1.0);
  1331.     int vorig = (int)Celx_SafeGetNumber(l, 5, WrongType, "Fourth argument to celestia:print must be a number", -1.0);
  1332.     int hoff = (int)Celx_SafeGetNumber(l, 6, WrongType, "Fifth argument to celestia:print must be a number", 0.0);
  1333.     int voff = (int)Celx_SafeGetNumber(l, 7, WrongType, "Sixth argument to celestia:print must be a number", 5.0);
  1334.     if (duration < 0.0)
  1335.     {
  1336.         duration = 1.5;
  1337.     }
  1338.     appCore->showText(s, horig, vorig, hoff, voff, duration);
  1339.     return 0;
  1340. }
  1341. static int celestia_gettextwidth(lua_State* l)
  1342. {
  1343.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:gettextwidth");
  1344.     CelestiaCore* appCore = this_celestia(l);
  1345.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "First argument to celestia:gettextwidth must be a string");
  1346.     lua_pushnumber(l, appCore->getTextWidth(s));
  1347.     return 1;
  1348. }
  1349. static int celestia_getaltazimuthmode(lua_State* l)
  1350. {
  1351.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getaltazimuthmode()");
  1352.     CelestiaCore* appCore = this_celestia(l);
  1353.     lua_pushboolean(l, appCore->getAltAzimuthMode());
  1354.     return 1;
  1355. }
  1356. static int celestia_setaltazimuthmode(lua_State* l)
  1357. {
  1358.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:setaltazimuthmode");
  1359.     bool enable = false;
  1360.     if (lua_isboolean(l, -1))
  1361.      {
  1362.         enable = lua_toboolean(l, -1) != 0;
  1363.     }
  1364.     else
  1365.     {
  1366.         Celx_DoError(l, "Argument for celestia:setaltazimuthmode must be a boolean");
  1367.     }
  1368.     CelestiaCore* appCore = this_celestia(l);
  1369.     appCore->setAltAzimuthMode(enable);
  1370.     lua_pop(l, 1);
  1371.     return 0;
  1372. }
  1373. static int celestia_show(lua_State* l)
  1374. {
  1375.     Celx_CheckArgs(l, 1, 1000, "Wrong number of arguments to celestia:show");
  1376.     CelestiaCore* appCore = this_celestia(l);
  1377.     int argc = lua_gettop(l);
  1378.     int flags = 0;
  1379.     for (int i = 2; i <= argc; i++)
  1380.     {
  1381.         string renderFlag = Celx_SafeGetString(l, i, AllErrors, "Arguments to celestia:show() must be strings");
  1382.         if (renderFlag == "lightdelay")
  1383.             appCore->setLightDelayActive(true);
  1384.         else if (CelxLua::RenderFlagMap.count(renderFlag) > 0)
  1385.             flags |= CelxLua::RenderFlagMap[renderFlag];
  1386.     }
  1387.     Renderer* r = appCore->getRenderer();
  1388.     r->setRenderFlags(r->getRenderFlags() | flags);
  1389.     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
  1390.     return 0;
  1391. }
  1392. static int celestia_hide(lua_State* l)
  1393. {
  1394.     Celx_CheckArgs(l, 1, 1000, "Wrong number of arguments to celestia:hide");
  1395.     CelestiaCore* appCore = this_celestia(l);
  1396.     int argc = lua_gettop(l);
  1397.     int flags = 0;
  1398.     for (int i = 2; i <= argc; i++)
  1399.     {
  1400.         string renderFlag = Celx_SafeGetString(l, i, AllErrors, "Arguments to celestia:hide() must be strings");
  1401.         if (renderFlag == "lightdelay")
  1402.             appCore->setLightDelayActive(false);
  1403.         else if (CelxLua::RenderFlagMap.count(renderFlag) > 0)
  1404.             flags |= CelxLua::RenderFlagMap[renderFlag];
  1405.     }
  1406.     Renderer* r = appCore->getRenderer();
  1407.     r->setRenderFlags(r->getRenderFlags() & ~flags);
  1408.     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
  1409.     return 0;
  1410. }
  1411. static int celestia_setrenderflags(lua_State* l)
  1412. {
  1413.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setrenderflags()");
  1414.     CelestiaCore* appCore = this_celestia(l);
  1415.     if (!lua_istable(l, 2))
  1416.     {
  1417.         Celx_DoError(l, "Argument to celestia:setrenderflags() must be a table");
  1418.     }
  1419.     int renderFlags = appCore->getRenderer()->getRenderFlags();
  1420.     lua_pushnil(l);
  1421.     while (lua_next(l, -2) != 0)
  1422.     {
  1423.         string key;
  1424.         bool value = false;
  1425.         if (lua_isstring(l, -2))
  1426.         {
  1427.             key = lua_tostring(l, -2);
  1428.         }
  1429.         else
  1430.         {
  1431.             Celx_DoError(l, "Keys in table-argument to celestia:setrenderflags() must be strings");
  1432.         }
  1433.         if (lua_isboolean(l, -1))
  1434.         {
  1435.             value = lua_toboolean(l, -1) != 0;
  1436.         }
  1437.         else
  1438.         {
  1439.             Celx_DoError(l, "Values in table-argument to celestia:setrenderflags() must be boolean");
  1440.         }
  1441.         if (key == "lightdelay")
  1442.         {
  1443.             appCore->setLightDelayActive(value);
  1444.         }
  1445.         else if (CelxLua::RenderFlagMap.count(key) > 0)
  1446.         {
  1447.             int flag = CelxLua::RenderFlagMap[key];
  1448.             if (value)
  1449.             {
  1450.                 renderFlags |= flag;
  1451.             }
  1452.             else
  1453.             {
  1454.                 renderFlags &= ~flag;
  1455.             }
  1456.         }
  1457.         else
  1458.         {
  1459.             cerr << "Unknown key: " << key << "n";
  1460.         }
  1461.         lua_pop(l,1);
  1462.     }
  1463.     appCore->getRenderer()->setRenderFlags(renderFlags);
  1464.     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
  1465.     return 0;
  1466. }
  1467. static int celestia_getrenderflags(lua_State* l)
  1468. {
  1469.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getrenderflags()");
  1470.     CelestiaCore* appCore = this_celestia(l);
  1471.     lua_newtable(l);
  1472.     CelxLua::FlagMap::const_iterator it = CelxLua::RenderFlagMap.begin();
  1473.     const int renderFlags = appCore->getRenderer()->getRenderFlags();
  1474.     while (it != CelxLua::RenderFlagMap.end())
  1475.     {
  1476.         string key = it->first;
  1477.         lua_pushstring(l, key.c_str());
  1478.         lua_pushboolean(l, (it->second & renderFlags) != 0);
  1479.         lua_settable(l,-3);
  1480.         it++;
  1481.     }
  1482.     lua_pushstring(l, "lightdelay");
  1483.     lua_pushboolean(l, appCore->getLightDelayActive());
  1484.     lua_settable(l, -3);
  1485.     return 1;
  1486. }
  1487. int celestia_getscreendimension(lua_State* l)
  1488. {
  1489.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getscreendimension()");
  1490.     // error checking only:
  1491.     this_celestia(l);
  1492.     // Get the dimensions of the current viewport
  1493.     GLint viewport[4];
  1494.     glGetIntegerv(GL_VIEWPORT, viewport);
  1495.     lua_pushnumber(l, viewport[2]);
  1496.     lua_pushnumber(l, viewport[3]);
  1497.     return 2;
  1498. }
  1499. static int celestia_showlabel(lua_State* l)
  1500. {
  1501.     Celx_CheckArgs(l, 1, 1000, "Bad method call!");
  1502.     CelestiaCore* appCore = this_celestia(l);
  1503.     int argc = lua_gettop(l);
  1504.     int flags = 0;
  1505.     for (int i = 2; i <= argc; i++)
  1506.     {
  1507.         string labelFlag = Celx_SafeGetString(l, i, AllErrors, "Arguments to celestia:showlabel() must be strings");
  1508.         if (CelxLua::LabelFlagMap.count(labelFlag) > 0)
  1509.             flags |= CelxLua::LabelFlagMap[labelFlag];
  1510.     }
  1511.     Renderer* r = appCore->getRenderer();
  1512.     r->setLabelMode(r->getLabelMode() | flags);
  1513.     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
  1514.     return 0;
  1515. }
  1516. static int celestia_hidelabel(lua_State* l)
  1517. {
  1518.     Celx_CheckArgs(l, 1, 1000, "Invalid number of arguments in celestia:hidelabel");
  1519.     CelestiaCore* appCore = this_celestia(l);
  1520.     int argc = lua_gettop(l);
  1521.     int flags = 0;
  1522.     for (int i = 2; i <= argc; i++)
  1523.     {
  1524.         string labelFlag = Celx_SafeGetString(l, i, AllErrors, "Arguments to celestia:hidelabel() must be strings");
  1525.         if (CelxLua::LabelFlagMap.count(labelFlag) > 0)
  1526.             flags |= CelxLua::LabelFlagMap[labelFlag];
  1527.     }
  1528.     Renderer* r = appCore->getRenderer();
  1529.     r->setLabelMode(r->getLabelMode() & ~flags);
  1530.     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
  1531.     return 0;
  1532. }
  1533. static int celestia_setlabelflags(lua_State* l)
  1534. {
  1535.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setlabelflags()");
  1536.     CelestiaCore* appCore = this_celestia(l);
  1537.     if (!lua_istable(l, 2))
  1538.     {
  1539.         Celx_DoError(l, "Argument to celestia:setlabelflags() must be a table");
  1540.     }
  1541.     int labelFlags = appCore->getRenderer()->getLabelMode();
  1542.     lua_pushnil(l);
  1543.     while (lua_next(l, -2) != 0)
  1544.     {
  1545.         string key;
  1546.         bool value = false;
  1547.         if (lua_isstring(l, -2))
  1548.         {
  1549.             key = lua_tostring(l, -2);
  1550.         }
  1551.         else
  1552.         {
  1553.             Celx_DoError(l, "Keys in table-argument to celestia:setlabelflags() must be strings");
  1554.         }
  1555.         if (lua_isboolean(l, -1))
  1556.         {
  1557.             value = lua_toboolean(l, -1) != 0;
  1558.         }
  1559.         else
  1560.         {
  1561.             Celx_DoError(l, "Values in table-argument to celestia:setlabelflags() must be boolean");
  1562.         }
  1563.         if (CelxLua::LabelFlagMap.count(key) == 0)
  1564.         {
  1565.             cerr << "Unknown key: " << key << "n";
  1566.         }
  1567.         else
  1568.         {
  1569.             int flag = CelxLua::LabelFlagMap[key];
  1570.             if (value)
  1571.             {
  1572.                 labelFlags |= flag;
  1573.             }
  1574.             else
  1575.             {
  1576.                 labelFlags &= ~flag;
  1577.             }
  1578.         }
  1579.         lua_pop(l,1);
  1580.     }
  1581.     appCore->getRenderer()->setLabelMode(labelFlags);
  1582.     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
  1583.     return 0;
  1584. }
  1585. static int celestia_getlabelflags(lua_State* l)
  1586. {
  1587.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getlabelflags()");
  1588.     CelestiaCore* appCore = this_celestia(l);
  1589.     lua_newtable(l);
  1590.     CelxLua::FlagMap::const_iterator it = CelxLua::LabelFlagMap.begin();
  1591.     const int labelFlags = appCore->getRenderer()->getLabelMode();
  1592.     while (it != CelxLua::LabelFlagMap.end())
  1593.     {
  1594.         string key = it->first;
  1595.         lua_pushstring(l, key.c_str());
  1596.         lua_pushboolean(l, (it->second & labelFlags) != 0);
  1597.         lua_settable(l,-3);
  1598.         it++;
  1599.     }
  1600.     return 1;
  1601. }
  1602. static int celestia_setorbitflags(lua_State* l)
  1603. {
  1604.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setorbitflags()");
  1605.     CelestiaCore* appCore = this_celestia(l);
  1606.     if (!lua_istable(l, 2))
  1607.     {
  1608.         Celx_DoError(l, "Argument to celestia:setorbitflags() must be a table");
  1609.     }
  1610.     int orbitFlags = appCore->getRenderer()->getOrbitMask();
  1611.     lua_pushnil(l);
  1612.     while (lua_next(l, -2) != 0)
  1613.     {
  1614.         string key;
  1615.         bool value = false;
  1616.         if (lua_isstring(l, -2))
  1617.         {
  1618.             key = lua_tostring(l, -2);
  1619.         }
  1620.         else
  1621.         {
  1622.             Celx_DoError(l, "Keys in table-argument to celestia:setorbitflags() must be strings");
  1623.         }
  1624.         if (lua_isboolean(l, -1))
  1625.         {
  1626.             value = lua_toboolean(l, -1) != 0;
  1627.         }
  1628.         else
  1629.         {
  1630.             Celx_DoError(l, "Values in table-argument to celestia:setorbitflags() must be boolean");
  1631.         }
  1632.         if (CelxLua::BodyTypeMap.count(key) == 0)
  1633.         {
  1634.             cerr << "Unknown key: " << key << "n";
  1635.         }
  1636.         else
  1637.         {
  1638.             int flag = CelxLua::BodyTypeMap[key];
  1639.             if (value)
  1640.             {
  1641.                 orbitFlags |= flag;
  1642.             }
  1643.             else
  1644.             {
  1645.                 orbitFlags &= ~flag;
  1646.             }
  1647.         }
  1648.         lua_pop(l,1);
  1649.     }
  1650.     appCore->getRenderer()->setOrbitMask(orbitFlags);
  1651.     return 0;
  1652. }
  1653. static int celestia_getorbitflags(lua_State* l)
  1654. {
  1655.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getorbitflags()");
  1656.     CelestiaCore* appCore = this_celestia(l);
  1657.     lua_newtable(l);
  1658.     CelxLua::FlagMap::const_iterator it = CelxLua::BodyTypeMap.begin();
  1659.     const int orbitFlags = appCore->getRenderer()->getOrbitMask();
  1660.     while (it != CelxLua::BodyTypeMap.end())
  1661.     {
  1662.         string key = it->first;
  1663.         lua_pushstring(l, key.c_str());
  1664.         lua_pushboolean(l, (it->second & orbitFlags) != 0);
  1665.         lua_settable(l,-3);
  1666.         it++;
  1667.     }
  1668.     return 1;
  1669. }
  1670. static int celestia_showconstellations(lua_State* l)
  1671. {
  1672.     Celx_CheckArgs(l, 1, 2, "Expected no or one argument to celestia:showconstellations()");
  1673.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  1674.     Universe* u = appCore->getSimulation()->getUniverse();
  1675.     AsterismList* asterisms = u->getAsterisms();
  1676.     if (lua_type(l, 2) == LUA_TNONE) // No argument passed
  1677.     {
  1678.         for (AsterismList::const_iterator iter = asterisms->begin();
  1679.              iter != asterisms->end(); iter++)
  1680.         {
  1681.             Asterism* ast = *iter;
  1682.             ast->setActive(true);
  1683.         }
  1684.     }
  1685.     else if (!lua_istable(l, 2))
  1686.     {
  1687.         Celx_DoError(l, "Argument to celestia:showconstellations() must be a table");
  1688.     }
  1689.     else
  1690.     {
  1691.         lua_pushnil(l);
  1692.         while (lua_next(l, -2) != 0)
  1693.         {
  1694.             const char* constellation = "";
  1695.             if (lua_isstring(l, -1))
  1696.             {
  1697.                 constellation = lua_tostring(l, -1);
  1698.             }
  1699.             else
  1700.             {
  1701.                 Celx_DoError(l, "Values in table-argument to celestia:showconstellations() must be strings");
  1702.             }
  1703.             for (AsterismList::const_iterator iter = asterisms->begin();
  1704.                  iter != asterisms->end(); iter++)
  1705.             {
  1706.                 Asterism* ast = *iter;
  1707.              if (compareIgnoringCase(constellation, ast->getName(false)) == 0)
  1708.                     ast->setActive(true);
  1709.             }
  1710.             lua_pop(l,1);
  1711.         }
  1712.     }
  1713.     return 0;
  1714. }
  1715. static int celestia_hideconstellations(lua_State* l)
  1716. {
  1717.     Celx_CheckArgs(l, 1, 2, "Expected no or one argument to celestia:hideconstellations()");
  1718.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  1719.     Universe* u = appCore->getSimulation()->getUniverse();
  1720.     AsterismList* asterisms = u->getAsterisms();
  1721.     if (lua_type(l, 2) == LUA_TNONE) // No argument passed
  1722.     {
  1723.         for (AsterismList::const_iterator iter = asterisms->begin();
  1724.              iter != asterisms->end(); iter++)
  1725.         {
  1726.             Asterism* ast = *iter;
  1727.             ast->setActive(false);
  1728.         }
  1729.     }
  1730.     else if (!lua_istable(l, 2))
  1731.     {
  1732.         Celx_DoError(l, "Argument to celestia:hideconstellations() must be a table");
  1733.     }
  1734.     else
  1735.     {
  1736.         lua_pushnil(l);
  1737.         while (lua_next(l, -2) != 0)
  1738.         {
  1739.             const char* constellation = "";
  1740.             if (lua_isstring(l, -1))
  1741.             {
  1742.                 constellation = lua_tostring(l, -1);
  1743.             }
  1744.             else
  1745.             {
  1746.                 Celx_DoError(l, "Values in table-argument to celestia:hideconstellations() must be strings");
  1747.             }
  1748.             for (AsterismList::const_iterator iter = asterisms->begin();
  1749.                  iter != asterisms->end(); iter++)
  1750.             {
  1751.                 Asterism* ast = *iter;
  1752.              if (compareIgnoringCase(constellation, ast->getName(false)) == 0)
  1753.                     ast->setActive(false);
  1754.             }
  1755.             lua_pop(l,1);
  1756.         }
  1757.     }
  1758.     return 0;
  1759. }
  1760. static int celestia_setconstellationcolor(lua_State* l)
  1761. {
  1762.     Celx_CheckArgs(l, 4, 5, "Expected three or four arguments to celestia:setconstellationcolor()");
  1763.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  1764.     Universe* u = appCore->getSimulation()->getUniverse();
  1765.     AsterismList* asterisms = u->getAsterisms();
  1766.     float r = (float) Celx_SafeGetNumber(l, 2, WrongType, "First argument to celestia:setconstellationcolor() must be a number", 0.0);
  1767.     float g = (float) Celx_SafeGetNumber(l, 3, WrongType, "Second argument to celestia:setconstellationcolor() must be a number", 0.0);
  1768.     float b = (float) Celx_SafeGetNumber(l, 4, WrongType, "Third argument to celestia:setconstellationcolor() must be a number", 0.0);
  1769.     Color constellationColor(r, g, b);
  1770.     if (lua_type(l, 5) == LUA_TNONE) // Fourth argument omited
  1771.     {
  1772.         for (AsterismList::const_iterator iter = asterisms->begin();
  1773.              iter != asterisms->end(); iter++)
  1774.         {
  1775.             Asterism* ast = *iter;
  1776.             ast->setOverrideColor(constellationColor);
  1777.         }
  1778.     }
  1779.     else if (!lua_istable(l, 5))
  1780.     {
  1781.         Celx_DoError(l, "Fourth argument to celestia:setconstellationcolor() must be a table");
  1782.     }
  1783.     else
  1784.     {
  1785.         lua_pushnil(l);
  1786.         while (lua_next(l, -2) != 0)
  1787.         {
  1788.             const char* constellation = NULL;
  1789.             if (lua_isstring(l, -1))
  1790.             {
  1791.                 constellation = lua_tostring(l, -1);
  1792.             }
  1793.             else
  1794.             {
  1795.                 Celx_DoError(l, "Values in table-argument to celestia:setconstellationcolor() must be strings");
  1796.             }
  1797.             for (AsterismList::const_iterator iter = asterisms->begin();
  1798.                  iter != asterisms->end(); iter++)
  1799.             {
  1800.                 Asterism* ast = *iter;
  1801.              if (compareIgnoringCase(constellation, ast->getName(false)) == 0)
  1802.                     ast->setOverrideColor(constellationColor);
  1803.             }
  1804.             lua_pop(l,1);
  1805.         }
  1806.     }
  1807.     return 0;
  1808. }
  1809. static int celestia_setoverlayelements(lua_State* l)
  1810. {
  1811.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setoverlayelements()");
  1812.     CelestiaCore* appCore = this_celestia(l);
  1813.     if (!lua_istable(l, 2))
  1814.     {
  1815.         Celx_DoError(l, "Argument to celestia:setoverlayelements() must be a table");
  1816.     }
  1817.     int overlayElements = appCore->getOverlayElements();
  1818.     lua_pushnil(l);
  1819.     while (lua_next(l, -2) != 0)
  1820.     {
  1821.         string key;
  1822.         bool value = false;
  1823.         if (lua_isstring(l, -2))
  1824.         {
  1825.             key = lua_tostring(l, -2);
  1826.         }
  1827.         else
  1828.         {
  1829.             Celx_DoError(l, "Keys in table-argument to celestia:setoverlayelements() must be strings");
  1830.         }
  1831.         if (lua_isboolean(l, -1))
  1832.         {
  1833.             value = lua_toboolean(l, -1) != 0;
  1834.         }
  1835.         else
  1836.         {
  1837.             Celx_DoError(l, "Values in table-argument to celestia:setoverlayelements() must be boolean");
  1838.         }
  1839.         if (CelxLua::OverlayElementMap.count(key) == 0)
  1840.         {
  1841.             cerr << "Unknown key: " << key << "n";
  1842.         }
  1843.         else
  1844.         {
  1845.             int element = CelxLua::OverlayElementMap[key];
  1846.             if (value)
  1847.             {
  1848.                 overlayElements |= element;
  1849.             }
  1850.             else
  1851.             {
  1852.                 overlayElements &= ~element;
  1853.             }
  1854.         }
  1855.         lua_pop(l,1);
  1856.     }
  1857.     appCore->setOverlayElements(overlayElements);
  1858.     return 0;
  1859. }
  1860. static int celestia_getoverlayelements(lua_State* l)
  1861. {
  1862.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getoverlayelements()");
  1863.     CelestiaCore* appCore = this_celestia(l);
  1864.     lua_newtable(l);
  1865.     CelxLua::FlagMap::const_iterator it = CelxLua::OverlayElementMap.begin();
  1866.     const int overlayElements = appCore->getOverlayElements();
  1867.     while (it != CelxLua::OverlayElementMap.end())
  1868.     {
  1869.         string key = it->first;
  1870.         lua_pushstring(l, key.c_str());
  1871.         lua_pushboolean(l, (it->second & overlayElements) != 0);
  1872.         lua_settable(l,-3);
  1873.         it++;
  1874.     }
  1875.     return 1;
  1876. }
  1877. static int celestia_setlabelcolor(lua_State* l)
  1878. {
  1879.     Celx_CheckArgs(l, 5, 5, "Four arguments expected for celestia:setlabelcolor()");
  1880.     if (!lua_isstring(l, 2))
  1881.     {
  1882.         Celx_DoError(l, "First argument to celestia:setlabelstyle() must be a string");
  1883.     }
  1884.     Color* color = NULL;
  1885.     string key;
  1886.     key = lua_tostring(l, 2);
  1887.     if (CelxLua::LabelColorMap.count(key) == 0)
  1888.     {
  1889.         cerr << "Unknown label style: " << key << "n";
  1890.     }
  1891.     else
  1892.     {
  1893.         color = CelxLua::LabelColorMap[key];
  1894.     }
  1895.     double red     = Celx_SafeGetNumber(l, 3, AllErrors, "setlabelcolor: color values must be numbers");
  1896.     double green   = Celx_SafeGetNumber(l, 4, AllErrors, "setlabelcolor: color values must be numbers");
  1897.     double blue    = Celx_SafeGetNumber(l, 5, AllErrors, "setlabelcolor: color values must be numbers");
  1898.     // opacity currently not settable
  1899.     double opacity = 1.0;
  1900.     if (color != NULL)
  1901.     {
  1902.         *color = Color((float) red, (float) green, (float) blue, (float) opacity);
  1903.     }
  1904.     return 1;
  1905. }
  1906. static int celestia_getlabelcolor(lua_State* l)
  1907. {
  1908.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:getlabelcolor()");
  1909.     string key = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:getlabelcolor() must be a string");
  1910.     Color* labelColor = NULL;
  1911.     if (CelxLua::LabelColorMap.count(key) == 0)
  1912.     {
  1913.         cerr << "Unknown label style: " << key << "n";
  1914.         return 0;
  1915.     }
  1916.     else
  1917.     {
  1918.         labelColor = CelxLua::LabelColorMap[key];
  1919.         lua_pushnumber(l, labelColor->red());
  1920.         lua_pushnumber(l, labelColor->green());
  1921.         lua_pushnumber(l, labelColor->blue());
  1922.         return 3;
  1923.     }
  1924. }
  1925. static int celestia_setlinecolor(lua_State* l)
  1926. {
  1927.     Celx_CheckArgs(l, 5, 5, "Four arguments expected for celestia:setlinecolor()");
  1928.     if (!lua_isstring(l, 2))
  1929.     {
  1930.         Celx_DoError(l, "First argument to celestia:setlinecolor() must be a string");
  1931.     }
  1932.     Color* color = NULL;
  1933.     string key;
  1934.     key = lua_tostring(l, 2);
  1935.     if (CelxLua::LineColorMap.count(key) == 0)
  1936.     {
  1937.         cerr << "Unknown line style: " << key << "n";
  1938.     }
  1939.     else
  1940.     {
  1941.         color = CelxLua::LineColorMap[key];
  1942.     }
  1943.     double red     = Celx_SafeGetNumber(l, 3, AllErrors, "setlinecolor: color values must be numbers");
  1944.     double green   = Celx_SafeGetNumber(l, 4, AllErrors, "setlinecolor: color values must be numbers");
  1945.     double blue    = Celx_SafeGetNumber(l, 5, AllErrors, "setlinecolor: color values must be numbers");
  1946.     // opacity currently not settable
  1947.     double opacity = 1.0;
  1948.     if (color != NULL)
  1949.     {
  1950.         *color = Color((float) red, (float) green, (float) blue, (float) opacity);
  1951.     }
  1952.     return 1;
  1953. }
  1954. static int celestia_getlinecolor(lua_State* l)
  1955. {
  1956.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:getlinecolor()");
  1957.     string key = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:getlinecolor() must be a string");
  1958.     Color* lineColor = NULL;
  1959.     if (CelxLua::LineColorMap.count(key) == 0)
  1960.     {
  1961.         cerr << "Unknown line style: " << key << "n";
  1962.         return 0;
  1963.     }
  1964.     else
  1965.     {
  1966.         lineColor = CelxLua::LineColorMap[key];
  1967.         lua_pushnumber(l, lineColor->red());
  1968.         lua_pushnumber(l, lineColor->green());
  1969.         lua_pushnumber(l, lineColor->blue());
  1970.         return 3;
  1971.     }
  1972. }
  1973. static int celestia_setfaintestvisible(lua_State* l)
  1974. {
  1975.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setfaintestvisible()");
  1976.     CelestiaCore* appCore = this_celestia(l);
  1977.     float faintest = (float)Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setfaintestvisible() must be a number");
  1978.     if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0)
  1979.     {
  1980.         faintest = min(15.0f, max(1.0f, faintest));
  1981.         appCore->setFaintest(faintest);
  1982.         appCore->notifyWatchers(CelestiaCore::FaintestChanged);
  1983.     }
  1984.     else
  1985.     {
  1986.         faintest = min(12.0f, max(6.0f, faintest));
  1987.         appCore->getRenderer()->setFaintestAM45deg(faintest);
  1988.         appCore->setFaintestAutoMag();
  1989.     }
  1990.     return 0;
  1991. }
  1992. static int celestia_getfaintestvisible(lua_State* l)
  1993. {
  1994.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getfaintestvisible()");
  1995.     CelestiaCore* appCore = this_celestia(l);
  1996.     if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0)
  1997.     {
  1998.         lua_pushnumber(l, appCore->getSimulation()->getFaintestVisible());
  1999.     }
  2000.     else
  2001.     {
  2002.         lua_pushnumber(l, appCore->getRenderer()->getFaintestAM45deg());
  2003.     }
  2004.     return 1;
  2005. }
  2006. static int celestia_setgalaxylightgain(lua_State* l)
  2007. {
  2008.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setgalaxylightgain()");
  2009.     float lightgain = (float)Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setgalaxylightgain() must be a number");
  2010.     lightgain = min(1.0f, max(0.0f, lightgain));
  2011.     Galaxy::setLightGain(lightgain);
  2012.     return 0;
  2013. }
  2014. static int celestia_getgalaxylightgain(lua_State* l)
  2015. {
  2016.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getgalaxylightgain()");
  2017.     lua_pushnumber(l, Galaxy::getLightGain());
  2018.     return 1;
  2019. }
  2020. static int celestia_setminfeaturesize(lua_State* l)
  2021. {
  2022.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:setminfeaturesize()");
  2023.     CelestiaCore* appCore = this_celestia(l);
  2024.     float minFeatureSize = (float)Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setminfeaturesize() must be a number");
  2025.     minFeatureSize = max(0.0f, minFeatureSize);
  2026.     appCore->getRenderer()->setMinimumFeatureSize(minFeatureSize);
  2027.     return 0;
  2028. }
  2029. static int celestia_getminfeaturesize(lua_State* l)
  2030. {
  2031.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getminfeaturesize()");
  2032.     CelestiaCore* appCore = this_celestia(l);
  2033.     lua_pushnumber(l, appCore->getRenderer()->getMinimumFeatureSize());
  2034.     return 1;
  2035. }
  2036. static int celestia_getobserver(lua_State* l)
  2037. {
  2038.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getobserver()");
  2039.     CelestiaCore* appCore = this_celestia(l);
  2040.     Observer* o = appCore->getSimulation()->getActiveObserver();
  2041.     if (o == NULL)
  2042.         lua_pushnil(l);
  2043.     else
  2044.         observer_new(l, o);
  2045.     return 1;
  2046. }
  2047. static int celestia_getobservers(lua_State* l)
  2048. {
  2049.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getobservers()");
  2050.     CelestiaCore* appCore = this_celestia(l);
  2051.     vector<Observer*> observer_list;
  2052.     getObservers(appCore, observer_list);
  2053.     lua_newtable(l);
  2054.     for (unsigned int i = 0; i < observer_list.size(); i++)
  2055.     {
  2056.         observer_new(l, observer_list[i]);
  2057.         lua_rawseti(l, -2, i + 1);
  2058.     }
  2059.     return 1;
  2060. }
  2061. static int celestia_getselection(lua_State* l)
  2062. {
  2063.     Celx_CheckArgs(l, 1, 1, "No arguments expected to celestia:getselection()");
  2064.     CelestiaCore* appCore = this_celestia(l);
  2065.     Selection sel = appCore->getSimulation()->getSelection();
  2066.     object_new(l, sel);
  2067.     return 1;
  2068. }
  2069. static int celestia_find(lua_State* l)
  2070. {
  2071.     Celx_CheckArgs(l, 2, 2, "One argument expected for function celestia:find()");
  2072.     if (!lua_isstring(l, 2))
  2073.     {
  2074.         Celx_DoError(l, "Argument to find must be a string");
  2075.     }
  2076.     CelestiaCore* appCore = this_celestia(l);
  2077.     Simulation* sim = appCore->getSimulation();
  2078.     // Should use universe not simulation for finding objects
  2079.     Selection sel = sim->findObjectFromPath(lua_tostring(l, 2));
  2080.     object_new(l, sel);
  2081.     return 1;
  2082. }
  2083. static int celestia_select(lua_State* l)
  2084. {
  2085.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:select()");
  2086.     CelestiaCore* appCore = this_celestia(l);
  2087.     Simulation* sim = appCore->getSimulation();
  2088.     Selection* sel = to_object(l, 2);
  2089.     // If the argument is an object, set the selection; if it's anything else
  2090.     // clear the selection.
  2091.     if (sel != NULL)
  2092.         sim->setSelection(*sel);
  2093.     else
  2094.         sim->setSelection(Selection());
  2095.     return 0;
  2096. }
  2097. static int celestia_mark(lua_State* l)
  2098. {
  2099.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:mark");
  2100.     CelestiaCore* appCore = this_celestia(l);
  2101.     Simulation* sim = appCore->getSimulation();
  2102.     Selection* sel = to_object(l, 2);
  2103.     if (sel != NULL)
  2104.     {
  2105.         MarkerRepresentation markerRep(MarkerRepresentation::Diamond);
  2106.         markerRep.setColor(Color(0.0f, 1.0f, 0.0f));
  2107.         markerRep.setSize(10.0f);
  2108.         
  2109.         sim->getUniverse()->markObject(*sel, markerRep, 1);
  2110.     }
  2111.     else
  2112.     {
  2113.         Celx_DoError(l, "Argument to celestia:mark must be an object");
  2114.     }
  2115.     return 0;
  2116. }
  2117. static int celestia_unmark(lua_State* l)
  2118. {
  2119.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:unmark");
  2120.     CelestiaCore* appCore = this_celestia(l);
  2121.     Simulation* sim = appCore->getSimulation();
  2122.     Selection* sel = to_object(l, 2);
  2123.     if (sel != NULL)
  2124.     {
  2125.         sim->getUniverse()->unmarkObject(*sel, 1);
  2126.     }
  2127.     else
  2128.     {
  2129.         Celx_DoError(l, "Argument to celestia:unmark must be an object");
  2130.     }
  2131.     return 0;
  2132. }
  2133. static int celestia_gettime(lua_State* l)
  2134. {
  2135.     Celx_CheckArgs(l, 1, 1, "No argument expected to function celestia:gettime");
  2136.     CelestiaCore* appCore = this_celestia(l);
  2137.     Simulation* sim = appCore->getSimulation();
  2138.     lua_pushnumber(l, sim->getTime());
  2139.     return 1;
  2140. }
  2141. static int celestia_gettimescale(lua_State* l)
  2142. {
  2143.     Celx_CheckArgs(l, 1, 1, "No argument expected to function celestia:gettimescale");
  2144.     CelestiaCore* appCore = this_celestia(l);
  2145.     lua_pushnumber(l, appCore->getSimulation()->getTimeScale());
  2146.     return 1;
  2147. }
  2148. static int celestia_settime(lua_State* l)
  2149. {
  2150.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:settime");
  2151.     CelestiaCore* appCore = this_celestia(l);
  2152.     double t = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:settime must be a number");
  2153.     appCore->getSimulation()->setTime(t);
  2154.     return 0;
  2155. }
  2156. static int celestia_ispaused(lua_State* l)
  2157. {
  2158.     Celx_CheckArgs(l, 1, 1, "No argument expected to function celestia:ispaused");
  2159.     CelestiaCore* appCore = this_celestia(l);
  2160.     lua_pushboolean(l, appCore->getSimulation()->getPauseState());
  2161.     return 1;
  2162. }
  2163. static int celestia_settimescale(lua_State* l)
  2164. {
  2165.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:settimescale");
  2166.     CelestiaCore* appCore = this_celestia(l);
  2167.     double t = Celx_SafeGetNumber(l, 2, AllErrors, "Second arg to celestia:settimescale must be a number");
  2168.     appCore->getSimulation()->setTimeScale(t);
  2169.     return 0;
  2170. }
  2171. static int celestia_tojulianday(lua_State* l)
  2172. {
  2173.     Celx_CheckArgs(l, 2, 7, "Wrong number of arguments to function celestia:tojulianday");
  2174.     // for error checking only:
  2175.     this_celestia(l);
  2176.     int year = (int)Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:tojulianday must be a number", 0.0);
  2177.     int month = (int)Celx_SafeGetNumber(l, 3, WrongType, "Second arg to celestia:tojulianday must be a number", 1.0);
  2178.     int day = (int)Celx_SafeGetNumber(l, 4, WrongType, "Third arg to celestia:tojulianday must be a number", 1.0);
  2179.     int hour = (int)Celx_SafeGetNumber(l, 5, WrongType, "Fourth arg to celestia:tojulianday must be a number", 0.0);
  2180.     int minute = (int)Celx_SafeGetNumber(l, 6, WrongType, "Fifth arg to celestia:tojulianday must be a number", 0.0);
  2181.     double seconds = Celx_SafeGetNumber(l, 7, WrongType, "Sixth arg to celestia:tojulianday must be a number", 0.0);
  2182.     astro::Date date(year, month, day);
  2183.     date.hour = hour;
  2184.     date.minute = minute;
  2185.     date.seconds = seconds;
  2186.     double jd = (double) date;
  2187.     lua_pushnumber(l, jd);
  2188.     return 1;
  2189. }
  2190. static int celestia_fromjulianday(lua_State* l)
  2191. {
  2192.     Celx_CheckArgs(l, 2, 2, "Wrong number of arguments to function celestia:fromjulianday");
  2193.     // for error checking only:
  2194.     this_celestia(l);
  2195.     double jd = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:fromjulianday must be a number", 0.0);
  2196.     astro::Date date(jd);
  2197.     lua_newtable(l);
  2198.     setTable(l, "year", (double)date.year);
  2199.     setTable(l, "month", (double)date.month);
  2200.     setTable(l, "day", (double)date.day);
  2201.     setTable(l, "hour", (double)date.hour);
  2202.     setTable(l, "minute", (double)date.minute);
  2203.     setTable(l, "seconds", date.seconds);
  2204.     return 1;
  2205. }
  2206. // Convert a UTC Julian date to a TDB Julian day
  2207. // TODO: also support a single table argument of the form output by
  2208. // celestia_tdbtoutc.
  2209. static int celestia_utctotdb(lua_State* l)
  2210. {
  2211.     Celx_CheckArgs(l, 2, 7, "Wrong number of arguments to function celestia:utctotdb");
  2212.     // for error checking only:
  2213.     this_celestia(l);
  2214.     int year = (int) Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:utctotdb must be a number", 0.0);
  2215.     int month = (int) Celx_SafeGetNumber(l, 3, WrongType, "Second arg to celestia:utctotdb must be a number", 1.0);
  2216.     int day = (int) Celx_SafeGetNumber(l, 4, WrongType, "Third arg to celestia:utctotdb must be a number", 1.0);
  2217.     int hour = (int)Celx_SafeGetNumber(l, 5, WrongType, "Fourth arg to celestia:utctotdb must be a number", 0.0);
  2218.     int minute = (int)Celx_SafeGetNumber(l, 6, WrongType, "Fifth arg to celestia:utctotdb must be a number", 0.0);
  2219.     double seconds = Celx_SafeGetNumber(l, 7, WrongType, "Sixth arg to celestia:utctotdb must be a number", 0.0);
  2220.     astro::Date date(year, month, day);
  2221.     date.hour = hour;
  2222.     date.minute = minute;
  2223.     date.seconds = seconds;
  2224.     double jd = astro::UTCtoTDB(date);
  2225.     lua_pushnumber(l, jd);
  2226.     return 1;
  2227. }
  2228. // Convert a TDB Julian day to a UTC Julian date (table format)
  2229. static int celestia_tdbtoutc(lua_State* l)
  2230. {
  2231.     Celx_CheckArgs(l, 2, 2, "Wrong number of arguments to function celestia:tdbtoutc");
  2232.     // for error checking only:
  2233.     this_celestia(l);
  2234.     double jd = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:tdbtoutc must be a number", 0.0);
  2235.     astro::Date date = astro::TDBtoUTC(jd);
  2236.     lua_newtable(l);
  2237.     setTable(l, "year", (double)date.year);
  2238.     setTable(l, "month", (double)date.month);
  2239.     setTable(l, "day", (double)date.day);
  2240.     setTable(l, "hour", (double)date.hour);
  2241.     setTable(l, "minute", (double)date.minute);
  2242.     setTable(l, "seconds", date.seconds);
  2243.     return 1;
  2244. }
  2245. static int celestia_getsystemtime(lua_State* l)
  2246. {
  2247.     Celx_CheckArgs(l, 1, 1, "No argument expected to function celestia:getsystemtime");
  2248.     
  2249.     astro::Date d = astro::Date::systemDate();
  2250.     lua_pushnumber(l, astro::UTCtoTDB(d));
  2251.     return 1;
  2252. }
  2253. static int celestia_unmarkall(lua_State* l)
  2254. {
  2255.     Celx_CheckArgs(l, 1, 1, "No arguments expected to function celestia:unmarkall");
  2256.     CelestiaCore* appCore = this_celestia(l);
  2257.     Simulation* sim = appCore->getSimulation();
  2258.     sim->getUniverse()->unmarkAll();
  2259.     return 0;
  2260. }
  2261. static int celestia_getstarcount(lua_State* l)
  2262. {
  2263.     Celx_CheckArgs(l, 1, 1, "No arguments expected to function celestia:getstarcount");
  2264.     CelestiaCore* appCore = this_celestia(l);
  2265.     Universe* u = appCore->getSimulation()->getUniverse();
  2266.     lua_pushnumber(l, u->getStarCatalog()->size());
  2267.     return 1;
  2268. }
  2269. // Stars iterator function; two upvalues expected
  2270. static int celestia_stars_iter(lua_State* l)
  2271. {
  2272.     CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1));
  2273.     if (appCore == NULL)
  2274.     {
  2275.         Celx_DoError(l, "Bad celestia object!");
  2276.         return 0;
  2277.     }
  2278.     uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2));
  2279.     Universe* u = appCore->getSimulation()->getUniverse();
  2280.     if (i < u->getStarCatalog()->size())
  2281.     {
  2282.         // Increment the counter
  2283.         lua_pushnumber(l, i + 1);
  2284.         lua_replace(l, lua_upvalueindex(2));
  2285.         Star* star = u->getStarCatalog()->getStar(i);
  2286.         if (star == NULL)
  2287.             lua_pushnil(l);
  2288.         else
  2289.             object_new(l, Selection(star));
  2290.         return 1;
  2291.     }
  2292.     else
  2293.     {
  2294.         // Return nil when we've enumerated all the stars
  2295.         return 0;
  2296.     }
  2297. }
  2298. static int celestia_stars(lua_State* l)
  2299. {
  2300.     // Push a closure with two upvalues: the celestia object and a
  2301.     // counter.
  2302.     lua_pushvalue(l, 1);    // Celestia object
  2303.     lua_pushnumber(l, 0);   // counter
  2304.     lua_pushcclosure(l, celestia_stars_iter, 2);
  2305.     return 1;
  2306. }
  2307. static int celestia_getdsocount(lua_State* l)
  2308. {
  2309.     Celx_CheckArgs(l, 1, 1, "No arguments expected to function celestia:getdsocount");
  2310.     CelestiaCore* appCore = this_celestia(l);
  2311.     Universe* u = appCore->getSimulation()->getUniverse();
  2312.     lua_pushnumber(l, u->getDSOCatalog()->size());
  2313.     return 1;
  2314. }
  2315. // DSOs iterator function; two upvalues expected
  2316. static int celestia_dsos_iter(lua_State* l)
  2317. {
  2318.     CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1));
  2319.     if (appCore == NULL)
  2320.     {
  2321.         Celx_DoError(l, "Bad celestia object!");
  2322.         return 0;
  2323.     }
  2324.     uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2));
  2325.     Universe* u = appCore->getSimulation()->getUniverse();
  2326.     if (i < u->getDSOCatalog()->size())
  2327.     {
  2328.         // Increment the counter
  2329.         lua_pushnumber(l, i + 1);
  2330.         lua_replace(l, lua_upvalueindex(2));
  2331.         DeepSkyObject* dso = u->getDSOCatalog()->getDSO(i);
  2332.         if (dso == NULL)
  2333.             lua_pushnil(l);
  2334.         else
  2335.             object_new(l, Selection(dso));
  2336.         return 1;
  2337.     }
  2338.     else
  2339.     {
  2340.         // Return nil when we've enumerated all the DSOs
  2341.         return 0;
  2342.     }
  2343. }
  2344. static int celestia_dsos(lua_State* l)
  2345. {
  2346.     // Push a closure with two upvalues: the celestia object and a
  2347.     // counter.
  2348.     lua_pushvalue(l, 1);    // Celestia object
  2349.     lua_pushnumber(l, 0);   // counter
  2350.     lua_pushcclosure(l, celestia_dsos_iter, 2);
  2351.     return 1;
  2352. }
  2353. static int celestia_setambient(lua_State* l)
  2354. {
  2355.     Celx_CheckArgs(l, 2, 2, "One argument expected in celestia:setambient");
  2356.     CelestiaCore* appCore = this_celestia(l);
  2357.     Renderer* renderer = appCore->getRenderer();
  2358.     double ambientLightLevel = Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setambient must be a number");
  2359.     if (ambientLightLevel > 1.0)
  2360.         ambientLightLevel = 1.0;
  2361.     if (ambientLightLevel < 0.0)
  2362.         ambientLightLevel = 0.0;
  2363.     if (renderer != NULL)
  2364.         renderer->setAmbientLightLevel((float)ambientLightLevel);
  2365.     appCore->notifyWatchers(CelestiaCore::AmbientLightChanged);
  2366.     return 0;
  2367. }
  2368. static int celestia_getambient(lua_State* l)
  2369. {
  2370.     Celx_CheckArgs(l, 1, 1, "No argument expected in celestia:setambient");
  2371.     CelestiaCore* appCore = this_celestia(l);
  2372.     Renderer* renderer = appCore->getRenderer();
  2373.     if (renderer == NULL)
  2374.     {
  2375.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2376.     }
  2377.     else
  2378.     {
  2379.         lua_pushnumber(l, renderer->getAmbientLightLevel());
  2380.     }
  2381.     return 1;
  2382. }
  2383. static int celestia_setminorbitsize(lua_State* l)
  2384. {
  2385.     Celx_CheckArgs(l, 2, 2, "One argument expected in celestia:setminorbitsize");
  2386.     CelestiaCore* appCore = this_celestia(l);
  2387.     double orbitSize = Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setminorbitsize() must be a number");
  2388.     Renderer* renderer = appCore->getRenderer();
  2389.     if (renderer == NULL)
  2390.     {
  2391.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2392.     }
  2393.     else
  2394.     {
  2395.         orbitSize = max(0.0, orbitSize);
  2396.         renderer->setMinimumOrbitSize((float)orbitSize);
  2397.     }
  2398.     return 0;
  2399. }
  2400. static int celestia_getminorbitsize(lua_State* l)
  2401. {
  2402.     Celx_CheckArgs(l, 1, 1, "No argument expected in celestia:getminorbitsize");
  2403.     CelestiaCore* appCore = this_celestia(l);
  2404.     Renderer* renderer = appCore->getRenderer();
  2405.     if (renderer == NULL)
  2406.     {
  2407.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2408.     }
  2409.     else
  2410.     {
  2411.         lua_pushnumber(l, renderer->getMinimumOrbitSize());
  2412.     }
  2413.     return 1;
  2414. }
  2415. static int celestia_setstardistancelimit(lua_State* l)
  2416. {
  2417.     Celx_CheckArgs(l, 2, 2, "One argument expected in celestia:setstardistancelimit");
  2418.     CelestiaCore* appCore = this_celestia(l);
  2419.     double distanceLimit = Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:setstardistancelimit() must be a number");
  2420.     Renderer* renderer = appCore->getRenderer();
  2421.     if (renderer == NULL)
  2422.     {
  2423.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2424.     }
  2425.     else
  2426.     {
  2427.         renderer->setDistanceLimit((float)distanceLimit);
  2428.     }
  2429.     return 0;
  2430. }
  2431. static int celestia_getstardistancelimit(lua_State* l)
  2432. {
  2433.     Celx_CheckArgs(l, 1, 1, "No argument expected in celestia:getstardistancelimit");
  2434.     CelestiaCore* appCore = this_celestia(l);
  2435.     Renderer* renderer = appCore->getRenderer();
  2436.     if (renderer == NULL)
  2437.     {
  2438.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2439.     }
  2440.     else
  2441.     {
  2442.         lua_pushnumber(l, renderer->getDistanceLimit());
  2443.     }
  2444.     return 1;
  2445. }
  2446. static int celestia_getstarstyle(lua_State* l)
  2447. {
  2448.     Celx_CheckArgs(l, 1, 1, "No argument expected in celestia:getstarstyle");
  2449.     CelestiaCore* appCore = this_celestia(l);
  2450.     Renderer* renderer = appCore->getRenderer();
  2451.     if (renderer == NULL)
  2452.     {
  2453.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2454.     }
  2455.     else
  2456.     {
  2457.         Renderer::StarStyle starStyle = renderer->getStarStyle();
  2458.         switch (starStyle)
  2459.         {
  2460.         case Renderer::FuzzyPointStars:
  2461.             lua_pushstring(l, "fuzzy"); break;
  2462.         case Renderer::PointStars:
  2463.             lua_pushstring(l, "point"); break;
  2464.         case Renderer::ScaledDiscStars:
  2465.             lua_pushstring(l, "disc"); break;
  2466.         default:
  2467.             lua_pushstring(l, "invalid starstyle");
  2468.         };
  2469.     }
  2470.     return 1;
  2471. }
  2472. static int celestia_setstarstyle(lua_State* l)
  2473. {
  2474.     Celx_CheckArgs(l, 2, 2, "One argument expected in celestia:setstarstyle");
  2475.     CelestiaCore* appCore = this_celestia(l);
  2476.     string starStyle = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:setstarstyle must be a string");
  2477.     Renderer* renderer = appCore->getRenderer();
  2478.     if (renderer == NULL)
  2479.     {
  2480.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2481.     }
  2482.     else
  2483.     {
  2484.         if (starStyle == "fuzzy")
  2485.         {
  2486.             renderer->setStarStyle(Renderer::FuzzyPointStars);
  2487.         }
  2488.         else if (starStyle == "point")
  2489.         {
  2490.             renderer->setStarStyle(Renderer::PointStars);
  2491.         }
  2492.         else if (starStyle == "disc")
  2493.         {
  2494.             renderer->setStarStyle(Renderer::ScaledDiscStars);
  2495.         }
  2496.         else
  2497.         {
  2498.             Celx_DoError(l, "Invalid starstyle");
  2499.         }
  2500.         appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
  2501.     }
  2502.     return 0;
  2503. }
  2504. static int celestia_gettextureresolution(lua_State* l)
  2505. {
  2506.     Celx_CheckArgs(l, 1, 1, "No argument expected in celestia:gettextureresolution");
  2507.     CelestiaCore* appCore = this_celestia(l);
  2508.     Renderer* renderer = appCore->getRenderer();
  2509.     if (renderer == NULL)
  2510.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2511.     else
  2512.         lua_pushnumber(l, renderer->getResolution());
  2513.     return 1;
  2514. }
  2515. static int celestia_settextureresolution(lua_State* l)
  2516. {
  2517.     Celx_CheckArgs(l, 2, 2, "One argument expected in celestia:settextureresolution");
  2518.     CelestiaCore* appCore = this_celestia(l);
  2519.     unsigned int textureRes = (unsigned int) Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:settextureresolution must be a number");
  2520.     Renderer* renderer = appCore->getRenderer();
  2521.     if (renderer == NULL)
  2522.         Celx_DoError(l, "Internal Error: renderer is NULL!");
  2523.     else
  2524.     {
  2525.         renderer->setResolution(textureRes);
  2526.         appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
  2527.     }
  2528.     return 0;
  2529. }
  2530. static int celestia_getstar(lua_State* l)
  2531. {
  2532.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:getstar");
  2533.     CelestiaCore* appCore = this_celestia(l);
  2534.     double starIndex = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:getstar must be a number");
  2535.     Universe* u = appCore->getSimulation()->getUniverse();
  2536.     Star* star = u->getStarCatalog()->getStar((uint32) starIndex);
  2537.     if (star == NULL)
  2538.         lua_pushnil(l);
  2539.     else
  2540.         object_new(l, Selection(star));
  2541.     return 1;
  2542. }
  2543. static int celestia_getdso(lua_State* l)
  2544. {
  2545.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:getdso");
  2546.     CelestiaCore* appCore = this_celestia(l);
  2547.     double dsoIndex = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:getdso must be a number");
  2548.     Universe* u = appCore->getSimulation()->getUniverse();
  2549.     DeepSkyObject* dso = u->getDSOCatalog()->getDSO((uint32) dsoIndex);
  2550.     if (dso == NULL)
  2551.         lua_pushnil(l);
  2552.     else
  2553.         object_new(l, Selection(dso));
  2554.     return 1;
  2555. }
  2556. static int celestia_newvector(lua_State* l)
  2557. {
  2558.     Celx_CheckArgs(l, 4, 4, "Expected 3 arguments for celestia:newvector");
  2559.     // for error checking only:
  2560.     this_celestia(l);
  2561.     double x = Celx_SafeGetNumber(l, 2, AllErrors, "First arg to celestia:newvector must be a number");
  2562.     double y = Celx_SafeGetNumber(l, 3, AllErrors, "Second arg to celestia:newvector must be a number");
  2563.     double z = Celx_SafeGetNumber(l, 4, AllErrors, "Third arg to celestia:newvector must be a number");
  2564.     vector_new(l, Vec3d(x,y,z));
  2565.     return 1;
  2566. }
  2567. static int celestia_newposition(lua_State* l)
  2568. {
  2569.     Celx_CheckArgs(l, 4, 4, "Expected 3 arguments for celestia:newposition");
  2570.     // for error checking only:
  2571.     this_celestia(l);
  2572.     BigFix components[3];
  2573.     for (int i = 0; i < 3; i++)
  2574.     {
  2575.         if (lua_isnumber(l, i+2))
  2576.         {
  2577.             double v = lua_tonumber(l, i+2);
  2578.             components[i] = BigFix(v);
  2579.         }
  2580.         else if (lua_isstring(l, i+2))
  2581.         {
  2582.             components[i] = BigFix(string(lua_tostring(l, i+2)));
  2583.         }
  2584.         else
  2585.         {
  2586.             Celx_DoError(l, "Arguments to celestia:newposition must be either numbers or strings");
  2587.         }
  2588.     }
  2589.     position_new(l, UniversalCoord(components[0], components[1], components[2]));
  2590.     return 1;
  2591. }
  2592. static int celestia_newrotation(lua_State* l)
  2593. {
  2594.     Celx_CheckArgs(l, 3, 5, "Need 2 or 4 arguments for celestia:newrotation");
  2595.     // for error checking only:
  2596.     this_celestia(l);
  2597.     if (lua_gettop(l) > 3)
  2598.     {
  2599.         // if (lua_gettop == 4), Celx_SafeGetNumber will catch the error
  2600.         double w = Celx_SafeGetNumber(l, 2, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
  2601.         double x = Celx_SafeGetNumber(l, 3, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
  2602.         double y = Celx_SafeGetNumber(l, 4, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
  2603.         double z = Celx_SafeGetNumber(l, 5, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
  2604.         Quatd q(w, x, y, z);
  2605.         rotation_new(l, q);
  2606.     }
  2607.     else
  2608.     {
  2609.         Vec3d* v = to_vector(l, 2);
  2610.         if (v == NULL)
  2611.         {
  2612.             Celx_DoError(l, "newrotation: first argument must be a vector");
  2613.         }
  2614.         double angle = Celx_SafeGetNumber(l, 3, AllErrors, "second argument to celestia:newrotation must be a number");
  2615.         Quatd q;
  2616.         q.setAxisAngle(*v, angle);
  2617.         rotation_new(l, q);
  2618.     }
  2619.     return 1;
  2620. }
  2621. static int celestia_getscripttime(lua_State* l)
  2622. {
  2623.     Celx_CheckArgs(l, 1, 1, "No arguments expected for celestia:getscripttime");
  2624.     // for error checking only:
  2625.     this_celestia(l);
  2626.     LuaState* luastate_ptr = getLuaStateObject(l);
  2627.     lua_pushnumber(l, luastate_ptr->getTime());
  2628.     return 1;
  2629. }
  2630. static int celestia_newframe(lua_State* l)
  2631. {
  2632.     Celx_CheckArgs(l, 2, 4, "One to three arguments expected for function celestia:newframe");
  2633.     int argc = lua_gettop(l);
  2634.     // for error checking only:
  2635.     this_celestia(l);
  2636.     const char* coordsysName = Celx_SafeGetString(l, 2, AllErrors, "newframe: first argument must be a string");
  2637.     ObserverFrame::CoordinateSystem coordSys = parseCoordSys(coordsysName);
  2638.     Selection* ref = NULL;
  2639.     Selection* target = NULL;
  2640.     if (coordSys == ObserverFrame::Universal)
  2641.     {
  2642.         frame_new(l, ObserverFrame());
  2643.     }
  2644.     else if (coordSys == ObserverFrame::PhaseLock)
  2645.     {
  2646.         if (argc >= 4)
  2647.         {
  2648.             ref = to_object(l, 3);
  2649.             target = to_object(l, 4);
  2650.         }
  2651.         if (ref == NULL || target == NULL)
  2652.         {
  2653.             Celx_DoError(l, "newframe: two objects required for lock frame");
  2654.         }
  2655.         frame_new(l, ObserverFrame(coordSys, *ref, *target));
  2656.     }
  2657.     else
  2658.     {
  2659.         if (argc >= 3)
  2660.             ref = to_object(l, 3);
  2661.         if (ref == NULL)
  2662.         {
  2663.             Celx_DoError(l, "newframe: one object argument required for frame");
  2664.         }
  2665.         frame_new(l, ObserverFrame(coordSys, *ref));
  2666.     }
  2667.     return 1;
  2668. }
  2669. static int celestia_requestkeyboard(lua_State* l)
  2670. {
  2671.     Celx_CheckArgs(l, 2, 2, "Need one arguments for celestia:requestkeyboard");
  2672.     CelestiaCore* appCore = this_celestia(l);
  2673.     if (!lua_isboolean(l, 2))
  2674.     {
  2675.         Celx_DoError(l, "First argument for celestia:requestkeyboard must be a boolean");
  2676.     }
  2677.     int mode = appCore->getTextEnterMode();
  2678.     if (lua_toboolean(l, 2))
  2679.     {
  2680.         // Check for existence of charEntered:
  2681.         lua_pushstring(l, KbdCallback);
  2682.         lua_gettable(l, LUA_GLOBALSINDEX);
  2683.         if (lua_isnil(l, -1))
  2684.         {
  2685.             Celx_DoError(l, "script requested keyboard, but did not provide callback");
  2686.         }
  2687.         lua_remove(l, -1);
  2688.         mode = mode | CelestiaCore::KbPassToScript;
  2689.     }
  2690.     else
  2691.     {
  2692.         mode = mode & ~CelestiaCore::KbPassToScript;
  2693.     }
  2694.     appCore->setTextEnterMode(mode);
  2695.     return 0;
  2696. }
  2697. static int celestia_registereventhandler(lua_State* l)
  2698. {
  2699.     Celx_CheckArgs(l, 3, 3, "Two arguments required for celestia:registereventhandler");
  2700.     //CelestiaCore* appCore = this_celestia(l);
  2701.     if (!lua_isstring(l, 2))
  2702.     {
  2703.         Celx_DoError(l, "First argument for celestia:registereventhandler must be a string");
  2704.     }
  2705.     if (!lua_isfunction(l, 3) && !lua_isnil(l, 3))
  2706.     {
  2707.         Celx_DoError(l, "Second argument for celestia:registereventhandler must be a function or nil");
  2708.     }
  2709.     lua_pushstring(l, EventHandlers);
  2710.     lua_gettable(l, LUA_REGISTRYINDEX);
  2711.     if (lua_isnil(l, -1))
  2712.     {
  2713.         // This should never happen--the table should be created when a new Celestia Lua
  2714.         // state is initialized.
  2715.         Celx_DoError(l, "Event handler table not created");
  2716.     }
  2717.     lua_pushvalue(l, 2);
  2718.     lua_pushvalue(l, 3);
  2719.     lua_settable(l, -3);
  2720.     return 0;
  2721. }
  2722. static int celestia_geteventhandler(lua_State* l)
  2723. {
  2724.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:registereventhandler");
  2725.     //CelestiaCore* appCore = this_celestia(l);
  2726.     if (!lua_isstring(l, 2))
  2727.     {
  2728.         Celx_DoError(l, "Argument to celestia:geteventhandler must be a string");
  2729.     }
  2730.     lua_pushstring(l, EventHandlers);
  2731.     lua_gettable(l, LUA_REGISTRYINDEX);
  2732.     if (lua_isnil(l, -1))
  2733.     {
  2734.         // This should never happen--the table should be created when a new Celestia Lua
  2735.         // state is initialized.
  2736.         Celx_DoError(l, "Event handler table not created");
  2737.     }
  2738.     lua_pushvalue(l, 2);
  2739.     lua_gettable(l, -2);
  2740.     return 1;
  2741. }
  2742. static int celestia_takescreenshot(lua_State* l)
  2743. {
  2744.     Celx_CheckArgs(l, 1, 3, "Need 0 to 2 arguments for celestia:takescreenshot");
  2745.     CelestiaCore* appCore = this_celestia(l);
  2746.     LuaState* luastate = getLuaStateObject(l);
  2747.     // make sure we don't timeout because of taking a screenshot:
  2748.     double timeToTimeout = luastate->timeout - luastate->getTime();
  2749.     const char* filetype = Celx_SafeGetString(l, 2, WrongType, "First argument to celestia:takescreenshot must be a string");
  2750.     if (filetype == NULL)
  2751.         filetype = "png";
  2752.     // Let the script safely contribute one part of the filename:
  2753.     const char* fileid_ptr = Celx_SafeGetString(l, 3, WrongType, "Second argument to celestia:takescreenshot must be a string");
  2754.     if (fileid_ptr == NULL)
  2755.         fileid_ptr = "";
  2756.     string fileid(fileid_ptr);
  2757.     // be paranoid about the fileid, make sure it only contains 'A-Za-z0-9_':
  2758.     for (unsigned int i = 0; i < fileid.length(); i++)
  2759.     {
  2760.         char ch = fileid[i];
  2761.         if (!((ch >= 'a' && ch <= 'z') ||
  2762.               (fileid[i] >= 'A' && ch <= 'Z') ||
  2763.               (ch >= '0' && ch <= '9') ) )
  2764.             fileid[i] = '_';
  2765.     }
  2766.     // limit length of string
  2767.     if (fileid.length() > 16)
  2768.         fileid = fileid.substr(0, 16);
  2769.     if (fileid.length() > 0)
  2770.         fileid.append("-");
  2771.     string path = appCore->getConfig()->scriptScreenshotDirectory;
  2772.     if (path.length() > 0 &&
  2773.         path[path.length()-1] != '/' &&
  2774.         path[path.length()-1] != '\')
  2775.         path.append("/");
  2776.     luastate->screenshotCount++;
  2777.     bool success = false;
  2778.     char filenamestem[48];
  2779.     sprintf(filenamestem, "screenshot-%s%06i", fileid.c_str(), luastate->screenshotCount);
  2780.     // Get the dimensions of the current viewport
  2781.     GLint viewport[4];
  2782.     glGetIntegerv(GL_VIEWPORT, viewport);
  2783. #ifndef TARGET_OS_MAC
  2784.     if (strncmp(filetype, "jpg", 3) == 0)
  2785.     {
  2786.         string filepath = path + filenamestem + ".jpg";
  2787.         success = CaptureGLBufferToJPEG(string(filepath),
  2788.                                        viewport[0], viewport[1],
  2789.                                        viewport[2], viewport[3]);
  2790.     }
  2791.     else
  2792.     {
  2793.         string filepath = path + filenamestem + ".png";
  2794.         success = CaptureGLBufferToPNG(string(filepath),
  2795.                                        viewport[0], viewport[1],
  2796.                                        viewport[2], viewport[3]);
  2797.     }
  2798. #endif
  2799.     lua_pushboolean(l, success);
  2800.     // no matter how long it really took, make it look like 0.1s to timeout check:
  2801.     luastate->timeout = luastate->getTime() + timeToTimeout - 0.1;
  2802.     return 1;
  2803. }
  2804. static int celestia_createcelscript(lua_State* l)
  2805. {
  2806.     Celx_CheckArgs(l, 2, 2, "Need one argument for celestia:createcelscript()");
  2807.     string scripttext = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:createcelscript() must be a string");
  2808.     return celscript_from_string(l, scripttext);
  2809. }
  2810. static int celestia_requestsystemaccess(lua_State* l)
  2811. {
  2812.     // ignore possible argument for future extensions
  2813.     Celx_CheckArgs(l, 1, 2, "No argument expected for celestia:requestsystemaccess()");
  2814.     this_celestia(l);
  2815.     LuaState* luastate = getLuaStateObject(l);
  2816.     luastate->requestIO();
  2817.     return 0;
  2818. }
  2819. static int celestia_getscriptpath(lua_State* l)
  2820. {
  2821.     // ignore possible argument for future extensions
  2822.     Celx_CheckArgs(l, 1, 1, "No argument expected for celestia:requestsystemaccess()");
  2823.     this_celestia(l);
  2824.     lua_pushstring(l, "celestia-scriptpath");
  2825.     lua_gettable(l, LUA_REGISTRYINDEX);
  2826.     return 1;
  2827. }
  2828. static int celestia_tostring(lua_State* l)
  2829. {
  2830.     lua_pushstring(l, "[Celestia]");
  2831.     return 1;
  2832. }
  2833. static int celestia_windowbordersvisible(lua_State* l)
  2834. {
  2835.     Celx_CheckArgs(l, 1, 1, "No argument expected for celestia:windowbordersvisible");
  2836.     CelestiaCore* appCore = this_celestia(l);
  2837.     lua_pushboolean(l, appCore->getFramesVisible());
  2838.     return 1;
  2839. }
  2840. static int celestia_setwindowbordersvisible(lua_State* l)
  2841. {
  2842.     Celx_CheckArgs(l, 2, 2, "One argument expected for celestia:windowbordersvisible");
  2843.     CelestiaCore* appCore = this_celestia(l);
  2844.     bool visible = Celx_SafeGetBoolean(l, 2, AllErrors, "Argument to celestia:setwindowbordersvisible must be a boolean", true);
  2845.     appCore->setFramesVisible(visible);
  2846.     return 0;
  2847. }
  2848. static void CreateCelestiaMetaTable(lua_State* l)
  2849. {
  2850.     Celx_CreateClassMetatable(l, Celx_Celestia);
  2851.     Celx_RegisterMethod(l, "__tostring", celestia_tostring);
  2852.     Celx_RegisterMethod(l, "flash", celestia_flash);
  2853.     Celx_RegisterMethod(l, "print", celestia_print);
  2854.     Celx_RegisterMethod(l, "gettextwidth", celestia_gettextwidth);
  2855.     Celx_RegisterMethod(l, "show", celestia_show);
  2856.     Celx_RegisterMethod(l, "setaltazimuthmode", celestia_setaltazimuthmode);
  2857.     Celx_RegisterMethod(l, "getaltazimuthmode", celestia_getaltazimuthmode);
  2858.     Celx_RegisterMethod(l, "hide", celestia_hide);
  2859.     Celx_RegisterMethod(l, "getrenderflags", celestia_getrenderflags);
  2860.     Celx_RegisterMethod(l, "setrenderflags", celestia_setrenderflags);
  2861.     Celx_RegisterMethod(l, "getscreendimension", celestia_getscreendimension);
  2862.     Celx_RegisterMethod(l, "showlabel", celestia_showlabel);
  2863.     Celx_RegisterMethod(l, "hidelabel", celestia_hidelabel);
  2864.     Celx_RegisterMethod(l, "getlabelflags", celestia_getlabelflags);
  2865.     Celx_RegisterMethod(l, "setlabelflags", celestia_setlabelflags);
  2866.     Celx_RegisterMethod(l, "getorbitflags", celestia_getorbitflags);
  2867.     Celx_RegisterMethod(l, "setorbitflags", celestia_setorbitflags);
  2868.     Celx_RegisterMethod(l, "showconstellations", celestia_showconstellations);
  2869.     Celx_RegisterMethod(l, "hideconstellations", celestia_hideconstellations);
  2870.     Celx_RegisterMethod(l, "setconstellationcolor", celestia_setconstellationcolor);
  2871.     Celx_RegisterMethod(l, "setlabelcolor", celestia_setlabelcolor);
  2872.     Celx_RegisterMethod(l, "getlabelcolor", celestia_getlabelcolor);
  2873.     Celx_RegisterMethod(l, "setlinecolor",  celestia_setlinecolor);
  2874.     Celx_RegisterMethod(l, "getlinecolor",  celestia_getlinecolor);
  2875.     Celx_RegisterMethod(l, "getoverlayelements", celestia_getoverlayelements);
  2876.     Celx_RegisterMethod(l, "setoverlayelements", celestia_setoverlayelements);
  2877.     Celx_RegisterMethod(l, "getfaintestvisible", celestia_getfaintestvisible);
  2878.     Celx_RegisterMethod(l, "setfaintestvisible", celestia_setfaintestvisible);
  2879.     Celx_RegisterMethod(l, "getgalaxylightgain", celestia_getgalaxylightgain);
  2880.     Celx_RegisterMethod(l, "setgalaxylightgain", celestia_setgalaxylightgain);
  2881.     Celx_RegisterMethod(l, "setminfeaturesize", celestia_setminfeaturesize);
  2882.     Celx_RegisterMethod(l, "getminfeaturesize", celestia_getminfeaturesize);
  2883.     Celx_RegisterMethod(l, "getobserver", celestia_getobserver);
  2884.     Celx_RegisterMethod(l, "getobservers", celestia_getobservers);
  2885.     Celx_RegisterMethod(l, "getselection", celestia_getselection);
  2886.     Celx_RegisterMethod(l, "find", celestia_find);
  2887.     Celx_RegisterMethod(l, "select", celestia_select);
  2888.     Celx_RegisterMethod(l, "mark", celestia_mark);
  2889.     Celx_RegisterMethod(l, "unmark", celestia_unmark);
  2890.     Celx_RegisterMethod(l, "unmarkall", celestia_unmarkall);
  2891.     Celx_RegisterMethod(l, "gettime", celestia_gettime);
  2892.     Celx_RegisterMethod(l, "settime", celestia_settime);
  2893.     Celx_RegisterMethod(l, "ispaused", celestia_ispaused);
  2894.     Celx_RegisterMethod(l, "gettimescale", celestia_gettimescale);
  2895.     Celx_RegisterMethod(l, "settimescale", celestia_settimescale);
  2896.     Celx_RegisterMethod(l, "getambient", celestia_getambient);
  2897.     Celx_RegisterMethod(l, "setambient", celestia_setambient);
  2898.     Celx_RegisterMethod(l, "getminorbitsize", celestia_getminorbitsize);
  2899.     Celx_RegisterMethod(l, "setminorbitsize", celestia_setminorbitsize);
  2900.     Celx_RegisterMethod(l, "getstardistancelimit", celestia_getstardistancelimit);
  2901.     Celx_RegisterMethod(l, "setstardistancelimit", celestia_setstardistancelimit);
  2902.     Celx_RegisterMethod(l, "getstarstyle", celestia_getstarstyle);
  2903.     Celx_RegisterMethod(l, "setstarstyle", celestia_setstarstyle);
  2904.     Celx_RegisterMethod(l, "gettextureresolution", celestia_gettextureresolution);
  2905.     Celx_RegisterMethod(l, "settextureresolution", celestia_settextureresolution);
  2906.     Celx_RegisterMethod(l, "tojulianday", celestia_tojulianday);
  2907.     Celx_RegisterMethod(l, "fromjulianday", celestia_fromjulianday);
  2908.     Celx_RegisterMethod(l, "utctotdb", celestia_utctotdb);
  2909.     Celx_RegisterMethod(l, "tdbtoutc", celestia_tdbtoutc);
  2910.     Celx_RegisterMethod(l, "getsystemtime", celestia_getsystemtime);
  2911.     Celx_RegisterMethod(l, "getstarcount", celestia_getstarcount);
  2912.     Celx_RegisterMethod(l, "getdsocount", celestia_getdsocount);
  2913.     Celx_RegisterMethod(l, "getstar", celestia_getstar);
  2914.     Celx_RegisterMethod(l, "getdso", celestia_getdso);
  2915.     Celx_RegisterMethod(l, "newframe", celestia_newframe);
  2916.     Celx_RegisterMethod(l, "newvector", celestia_newvector);
  2917.     Celx_RegisterMethod(l, "newposition", celestia_newposition);
  2918.     Celx_RegisterMethod(l, "newrotation", celestia_newrotation);
  2919.     Celx_RegisterMethod(l, "getscripttime", celestia_getscripttime);
  2920.     Celx_RegisterMethod(l, "requestkeyboard", celestia_requestkeyboard);
  2921.     Celx_RegisterMethod(l, "takescreenshot", celestia_takescreenshot);
  2922.     Celx_RegisterMethod(l, "createcelscript", celestia_createcelscript);
  2923.     Celx_RegisterMethod(l, "requestsystemaccess", celestia_requestsystemaccess);
  2924.     Celx_RegisterMethod(l, "getscriptpath", celestia_getscriptpath);
  2925.     Celx_RegisterMethod(l, "registereventhandler", celestia_registereventhandler);
  2926.     Celx_RegisterMethod(l, "geteventhandler", celestia_geteventhandler);
  2927.     Celx_RegisterMethod(l, "stars", celestia_stars);
  2928.     Celx_RegisterMethod(l, "dsos", celestia_dsos);
  2929.     Celx_RegisterMethod(l, "windowbordersvisible", celestia_windowbordersvisible);
  2930.     Celx_RegisterMethod(l, "setwindowbordersvisible", celestia_setwindowbordersvisible);
  2931.     lua_pop(l, 1);
  2932. }
  2933. static void loadLuaLibs(lua_State* state);
  2934. // ==================== Initialization ====================
  2935. bool LuaState::init(CelestiaCore* appCore)
  2936. {
  2937.     CelxLua::initMaps();
  2938.     // Import the base, table, string, and math libraries
  2939. #if LUA_VER >= 0x050100
  2940.     openLuaLibrary(state, "", luaopen_base);
  2941.     openLuaLibrary(state, LUA_MATHLIBNAME, luaopen_math);
  2942.     openLuaLibrary(state, LUA_TABLIBNAME, luaopen_table);
  2943.     openLuaLibrary(state, LUA_STRLIBNAME, luaopen_string);
  2944.     // Make the package library, except the loadlib function, available
  2945.     // for celx regardless of script system access policy.
  2946. allowLuaPackageAccess();
  2947. #else
  2948.     lua_baselibopen(state);
  2949.     lua_mathlibopen(state);
  2950.     lua_tablibopen(state);
  2951.     lua_strlibopen(state);
  2952. #endif
  2953.     // Add an easy to use wait function, so that script writers can
  2954.     // live in ignorance of coroutines.  There will probably be a significant
  2955.     // library of useful functions that can be defined purely in Lua.
  2956.     // At that point, we'll want something a bit more robust than just
  2957.     // parsing the whole text of the library every time a script is launched
  2958.     if (loadScript("wait = function(x) coroutine.yield(x) end") != 0)
  2959.         return false;
  2960.     // Execute the script fragment to define the wait function
  2961.     if (lua_pcall(state, 0, 0, 0) != 0)
  2962.     {
  2963.         cout << "Error running script initialization fragment.n";
  2964.         return false;
  2965.     }
  2966.     lua_pushstring(state, "KM_PER_MICROLY");
  2967.     lua_pushnumber(state, (lua_Number)KM_PER_LY/1e6);
  2968.     lua_settable(state, LUA_GLOBALSINDEX);
  2969.     loadLuaLibs(state);
  2970.     // Create the celestia object
  2971.     lua_pushstring(state, "celestia");
  2972.     celestia_new(state, appCore);
  2973.     lua_settable(state, LUA_GLOBALSINDEX);
  2974.     // add reference to appCore in the registry
  2975.     lua_pushstring(state, "celestia-appcore");
  2976.     lua_pushlightuserdata(state, static_cast<void*>(appCore));
  2977.     lua_settable(state, LUA_REGISTRYINDEX);
  2978.     // add a reference to the LuaState-object in the registry
  2979.     lua_pushstring(state, "celestia-luastate");
  2980.     lua_pushlightuserdata(state, static_cast<void*>(this));
  2981.     lua_settable(state, LUA_REGISTRYINDEX);
  2982.     lua_pushstring(state, EventHandlers);
  2983.     lua_newtable(state);
  2984.     lua_settable(state, LUA_REGISTRYINDEX);
  2985. #if 0
  2986.     lua_pushstring(state, "dofile");
  2987.     lua_gettable(state, LUA_GLOBALSINDEX); // function "dofile" on stack
  2988.     lua_pushstring(state, "luainit.celx"); // parameter
  2989.     if (lua_pcall(state, 1, 0, 0) != 0) // execute it
  2990.     {
  2991.         CelestiaCore::Alerter* alerter = appCore->getAlerter();
  2992.         // copy string?!
  2993.         const char* errorMessage = lua_tostring(state, -1);
  2994.         cout << errorMessage << 'n'; cout.flush();
  2995.         alerter->fatalError(errorMessage);
  2996.         return false;
  2997.     }
  2998. #endif
  2999.     return true;
  3000. }
  3001. void LuaState::setLuaPath(const string& s)
  3002. {
  3003. #if LUA_VER >= 0x050100
  3004.     lua_getfield(state, LUA_GLOBALSINDEX, "package");
  3005.     lua_pushstring(state, s.c_str());
  3006.     lua_setfield(state, -2, "path");
  3007.     lua_pop(state, 1);
  3008. #else
  3009.     lua_pushstring(state, "LUA_PATH");
  3010.     lua_pushstring(state, s.c_str());
  3011.     lua_settable(state, LUA_GLOBALSINDEX);
  3012. #endif
  3013. }
  3014. // ==================== Font Object ====================
  3015. static int font_new(lua_State* l, TextureFont* f)
  3016. {
  3017.     TextureFont** ud = static_cast<TextureFont**>(lua_newuserdata(l, sizeof(TextureFont*)));
  3018.     *ud = f;
  3019.     Celx_SetClass(l, Celx_Font);
  3020.     return 1;
  3021. }
  3022. static TextureFont* to_font(lua_State* l, int index)
  3023. {
  3024.     TextureFont** f = static_cast<TextureFont**>(lua_touserdata(l, index));
  3025.     // Check if pointer is valid
  3026.     if (f != NULL )
  3027.     {
  3028.             return *f;
  3029.     }
  3030.     return NULL;
  3031. }
  3032. static TextureFont* this_font(lua_State* l)
  3033. {
  3034.     TextureFont* f = to_font(l, 1);
  3035.     if (f == NULL)
  3036.     {
  3037.         Celx_DoError(l, "Bad font object!");
  3038.     }
  3039.     return f;
  3040. }
  3041. static int font_bind(lua_State* l)
  3042. {
  3043.     Celx_CheckArgs(l, 1, 1, "No arguments expected for font:bind()");
  3044.     TextureFont* font = this_font(l);
  3045.     font->bind();
  3046.     return 0;
  3047. }
  3048. static int font_render(lua_State* l)
  3049. {
  3050.     Celx_CheckArgs(l, 2, 2, "One argument required for font:render");
  3051.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "First argument to font:render must be a string");
  3052.     TextureFont* font = this_font(l);
  3053. font->render(s);
  3054.     return 0;
  3055. }
  3056. static int font_getwidth(lua_State* l)
  3057. {
  3058.     Celx_CheckArgs(l, 2, 2, "One argument expected for font:getwidth");
  3059.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "Argument to font:getwidth must be a string");
  3060.     TextureFont* font = this_font(l);
  3061.     lua_pushnumber(l, font->getWidth(s));
  3062.     return 1;
  3063. }
  3064. static int font_getheight(lua_State* l)
  3065. {
  3066.     Celx_CheckArgs(l, 1, 1, "No arguments expected for font:getheight()");
  3067.     TextureFont* font = this_font(l);
  3068.     lua_pushnumber(l, font->getHeight());
  3069.     return 1;
  3070. }
  3071. static int font_tostring(lua_State* l)
  3072. {
  3073.     // TODO: print out the actual information about the font
  3074.     lua_pushstring(l, "[Font]");
  3075.     return 1;
  3076. }
  3077. static void CreateFontMetaTable(lua_State* l)
  3078. {
  3079.     Celx_CreateClassMetatable(l, Celx_Font);
  3080.     Celx_RegisterMethod(l, "__tostring", font_tostring);
  3081.     Celx_RegisterMethod(l, "bind", font_bind);
  3082.     Celx_RegisterMethod(l, "render", font_render);
  3083.     Celx_RegisterMethod(l, "getwidth", font_getwidth);
  3084.     Celx_RegisterMethod(l, "getheight", font_getheight);
  3085.     lua_pop(l, 1); // remove metatable from stack
  3086. }
  3087. // ==================== Image =============================================
  3088. #if 0
  3089. static int image_new(lua_State* l, Image* i)
  3090. {
  3091.     Image** ud = static_cast<Image**>(lua_newuserdata(l, sizeof(Image*)));
  3092.     *ud = i;
  3093.     Celx_SetClass(l, Celx_Image);
  3094.     return 1;
  3095. }
  3096. #endif
  3097. static Image* to_image(lua_State* l, int index)
  3098. {
  3099.     Image** image = static_cast<Image**>(lua_touserdata(l, index));
  3100.     // Check if pointer is valid
  3101.     if (image != NULL )
  3102.     {
  3103.             return *image;
  3104.     }
  3105.     return NULL;
  3106. }
  3107. static Image* this_image(lua_State* l)
  3108. {
  3109.     Image* image = to_image(l,1);
  3110.     if (image == NULL)
  3111.     {
  3112.         Celx_DoError(l, "Bad image object!");
  3113.     }
  3114.     return image;
  3115. }
  3116. static int image_getheight(lua_State* l)
  3117. {
  3118.     Celx_CheckArgs(l, 1, 1, "No arguments expected for image:getheight()");
  3119.     Image* image = this_image(l);
  3120.     lua_pushnumber(l, image->getHeight());
  3121.     return 1;
  3122. }
  3123. static int image_getwidth(lua_State* l)
  3124. {
  3125.     Celx_CheckArgs(l, 1, 1, "No arguments expected for image:getwidth()");
  3126.     Image* image = this_image(l);
  3127.     lua_pushnumber(l, image->getWidth());
  3128.     return 1;
  3129. }
  3130. static int image_tostring(lua_State* l)
  3131. {
  3132.     // TODO: print out the actual information about the image
  3133.     lua_pushstring(l, "[Image]");
  3134.     return 1;
  3135. }
  3136. static void CreateImageMetaTable(lua_State* l)
  3137. {
  3138.     Celx_CreateClassMetatable(l, Celx_Image);
  3139.     Celx_RegisterMethod(l, "__tostring", image_tostring);
  3140.     Celx_RegisterMethod(l, "getheight", image_getheight);
  3141.     Celx_RegisterMethod(l, "getwidth", image_getwidth);
  3142.     lua_pop(l, 1); // remove metatable from stack
  3143. }
  3144. // ==================== Texture ============================================
  3145. static int texture_new(lua_State* l, Texture* t)
  3146. {
  3147.     Texture** ud = static_cast<Texture**>(lua_newuserdata(l, sizeof(Texture*)));
  3148.     *ud = t;
  3149.     Celx_SetClass(l, Celx_Texture);
  3150.     return 1;
  3151. }
  3152. static Texture* to_texture(lua_State* l, int index)
  3153. {
  3154.     Texture** texture = static_cast<Texture**>(lua_touserdata(l, index));
  3155.     // Check if pointer is valid
  3156.     if (texture != NULL )
  3157.     {
  3158.             return *texture;
  3159.     }
  3160.     return NULL;
  3161. }
  3162. static Texture* this_texture(lua_State* l)
  3163. {
  3164.     Texture* texture = to_texture(l,1);
  3165.     if (texture == NULL)
  3166.     {
  3167.         Celx_DoError(l, "Bad texture object!");
  3168.     }
  3169.     return texture;
  3170. }
  3171. static int texture_bind(lua_State* l)
  3172. {
  3173.     Celx_CheckArgs(l, 1, 1, "No arguments expected for texture:bind()");
  3174.     Texture* texture = this_texture(l);
  3175.     texture->bind();
  3176.     return 0;
  3177. }
  3178. static int texture_getheight(lua_State* l)
  3179. {
  3180.     Celx_CheckArgs(l, 1, 1, "No arguments expected for texture:getheight()");
  3181.     Texture* texture = this_texture(l);
  3182.     lua_pushnumber(l, texture->getHeight());
  3183.     return 1;
  3184. }
  3185. static int texture_getwidth(lua_State* l)
  3186. {
  3187.     Celx_CheckArgs(l, 1, 1, "No arguments expected for texture:getwidth()");
  3188.     Texture* texture = this_texture(l);
  3189.     lua_pushnumber(l, texture->getWidth());
  3190.     return 1;
  3191. }
  3192. static int texture_tostring(lua_State* l)
  3193. {
  3194.     // TODO: print out the actual information about the texture
  3195.     lua_pushstring(l, "[Texture]");
  3196.     return 1;
  3197. }
  3198. static void CreateTextureMetaTable(lua_State* l)
  3199. {
  3200.     Celx_CreateClassMetatable(l, Celx_Texture);
  3201.     Celx_RegisterMethod(l, "__tostring", texture_tostring);
  3202.     Celx_RegisterMethod(l, "getheight", texture_getheight);
  3203.     Celx_RegisterMethod(l, "getwidth", texture_getwidth);
  3204.     Celx_RegisterMethod(l, "bind", texture_bind);
  3205.     lua_pop(l, 1); // remove metatable from stack
  3206. }
  3207. // ==================== celestia extensions ====================
  3208. static int celestia_log(lua_State* l)
  3209. {
  3210.     Celx_CheckArgs(l, 2, 2, "One argument expected to function celestia:log");
  3211.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "First argument to celestia:log must be a string");
  3212. clog << s << "n"; clog.flush();
  3213. return 0;
  3214. }
  3215. static int celestia_getparamstring(lua_State* l)
  3216. {
  3217.     Celx_CheckArgs(l, 2, 2, "One argument expected to celestia:getparamstring()");
  3218.     CelestiaCore* appCore = this_celestia(l);
  3219.     const char* s = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:getparamstring must be a string");
  3220.     std::string paramString; // HWR
  3221.     CelestiaConfig* config = appCore->getConfig();
  3222.     config->configParams->getString(s, paramString);
  3223.     lua_pushstring(l,paramString.c_str());
  3224.     return 1;
  3225. }
  3226. static int celestia_loadtexture(lua_State* l)
  3227. {
  3228.     Celx_CheckArgs(l, 2, 2, "Need one argument for celestia:loadtexture()");
  3229.     string s = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:loadtexture() must be a string");
  3230.     lua_Debug ar;
  3231.     lua_getstack(l, 1, &ar);
  3232.     lua_getinfo(l, "S", &ar);
  3233.     string base_dir = ar.source; // Lua file from which we are called
  3234.     if (base_dir[0] == '@') base_dir = base_dir.substr(1);
  3235.     base_dir = base_dir.substr(0, base_dir.rfind('/')) + '/';
  3236.     Texture* t = LoadTextureFromFile(base_dir + s);
  3237.     if (t == NULL) return 0;
  3238.     texture_new(l, t);
  3239.     return 1;
  3240. }
  3241. static int celestia_loadfont(lua_State* l)
  3242. {
  3243.     Celx_CheckArgs(l, 2, 2, "Need one argument for celestia:loadtexture()");
  3244.     string s = Celx_SafeGetString(l, 2, AllErrors, "Argument to celestia:loadfont() must be a string");
  3245.     TextureFont* font = LoadTextureFont(s);
  3246.     if (font == NULL) return 0;
  3247. font->buildTexture();
  3248. font_new(l, font);
  3249.     return 1;
  3250. }
  3251. TextureFont* getFont(CelestiaCore* appCore)
  3252. {
  3253.        return appCore->font;
  3254. }
  3255. static int celestia_getfont(lua_State* l)
  3256. {
  3257.     Celx_CheckArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont");
  3258.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  3259.        TextureFont* font = getFont(appCore);
  3260.     if (font == NULL) return 0;
  3261.        font_new(l, font);
  3262.     return 1;
  3263. }
  3264. TextureFont* getTitleFont(CelestiaCore* appCore)
  3265. {
  3266. return appCore->titleFont;
  3267. }
  3268. static int celestia_gettitlefont(lua_State* l)
  3269. {
  3270.     Celx_CheckArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont");
  3271.     CelestiaCore* appCore = getAppCore(l, AllErrors);
  3272. TextureFont* font = getTitleFont(appCore);
  3273.     if (font == NULL) return 0;
  3274. font_new(l, font);
  3275.     return 1;
  3276. }
  3277. static int celestia_settimeslice(lua_State* l)
  3278. {
  3279.     Celx_CheckArgs(l, 2, 2, "One argument required for celestia:settimeslice");
  3280.     //CelestiaCore* appCore = this_celestia(l);
  3281.     if (!lua_isnumber(l, 2) && !lua_isnil(l, 2))
  3282.     {
  3283.         Celx_DoError(l, "Argument for celestia:settimeslice must be a number");
  3284.     }
  3285.     double timeslice = Celx_SafeGetNumber(l, 2, AllErrors, "Argument to celestia:settimeslice must be a number");
  3286.     if (timeslice == 0.0)
  3287.         timeslice = 0.1;
  3288.     LuaState* luastate = getLuaStateObject(l);
  3289.     luastate->timeout = luastate->getTime() + timeslice;
  3290.     return 0;
  3291. }
  3292. static int celestia_setluahook(lua_State* l)
  3293. {
  3294.     Celx_CheckArgs(l, 2, 2, "One argument required for celestia:setluahook");
  3295.     CelestiaCore* appCore = this_celestia(l);
  3296.     if (!lua_istable(l, 2) && !lua_isnil(l, 2))
  3297.     {
  3298.         Celx_DoError(l, "Argument for celestia:setluahook must be a table or nil");
  3299.         return 0;
  3300.     }
  3301.     LuaState* luastate = getLuaStateObject(l);
  3302.     if (luastate != NULL)
  3303.     {
  3304.         luastate->setLuaHookEventHandlerEnabled(lua_istable(l, 2));
  3305.     }
  3306.     lua_pushlightuserdata(l, appCore);
  3307.     lua_pushvalue(l, -2);
  3308.     lua_settable(l, LUA_REGISTRYINDEX);
  3309.     return 0;
  3310. }
  3311. static void ExtendCelestiaMetaTable(lua_State* l)
  3312. {
  3313.     PushClass(l, Celx_Celestia);
  3314.     lua_rawget(l, LUA_REGISTRYINDEX);
  3315.     if (lua_type(l, -1) != LUA_TTABLE)
  3316.         cout << "Metatable for " << CelxLua::ClassNames[Celx_Celestia] << " not found!n";
  3317.     Celx_RegisterMethod(l, "log", celestia_log);
  3318.     Celx_RegisterMethod(l, "settimeslice", celestia_settimeslice);
  3319.     Celx_RegisterMethod(l, "setluahook", celestia_setluahook);
  3320.     Celx_RegisterMethod(l, "getparamstring", celestia_getparamstring);
  3321.     Celx_RegisterMethod(l, "getfont", celestia_getfont);
  3322.     Celx_RegisterMethod(l, "gettitlefont", celestia_gettitlefont);
  3323.     Celx_RegisterMethod(l, "loadtexture", celestia_loadtexture);
  3324.     Celx_RegisterMethod(l, "loadfont", celestia_loadfont);
  3325.     lua_pop(l, 1);
  3326. }
  3327. #if LUA_VER < 0x050100
  3328. // ======================== loadlib ===================================
  3329. /*
  3330. * This is an implementation of loadlib based on the dlfcn interface.
  3331. * The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
  3332. * NetBSD, AIX 4.2, HPUX 11, and  probably most other Unix flavors, at least
  3333. * as an emulation layer on top of native functions.
  3334. */
  3335. #ifndef _WIN32
  3336. extern "C" {
  3337. #include <lualib.h>
  3338. /* #include <lauxlib.h.h> */
  3339. #include <dlfcn.h>
  3340. }
  3341. #if 0
  3342. static int x_loadlib(lua_State *L)
  3343. {
  3344. /* temp -- don't have lauxlib
  3345.  const char *path=luaL_checkstring(L,1);
  3346.  const char *init=luaL_checkstring(L,2);
  3347. */
  3348. cout << "loading lua libn"; cout.flush();
  3349.  const char *path=lua_tostring(L,1);
  3350.  const char *init=lua_tostring(L,2);
  3351.  void *lib=dlopen(path,RTLD_NOW);
  3352.  if (lib!=NULL)
  3353.  {
  3354.   lua_CFunction f=(lua_CFunction) dlsym(lib,init);
  3355.   if (f!=NULL)
  3356.   {
  3357.    lua_pushlightuserdata(L,lib);
  3358.    lua_pushcclosure(L,f,1);
  3359.    return 1;
  3360.   }
  3361.  }
  3362.  /* else return appropriate error messages */
  3363.  lua_pushnil(L);
  3364.  lua_pushstring(L,dlerror());
  3365.  lua_pushstring(L,(lib!=NULL) ? "init" : "open");
  3366.  if (lib!=NULL) dlclose(lib);
  3367.  return 3;
  3368. }
  3369. #endif
  3370. #endif // _WIN32
  3371. #endif // LUA_VER < 0x050100
  3372. // ==================== Load Libraries ================================================
  3373. static void loadLuaLibs(lua_State* state)
  3374. {
  3375. #if LUA_VER >= 0x050100
  3376.     openLuaLibrary(state, LUA_DBLIBNAME, luaopen_debug);
  3377. #else
  3378.     luaopen_debug(state);
  3379. #endif
  3380.     // TODO: Not required with Lua 5.1
  3381. #if 0
  3382. #ifndef _WIN32
  3383.     lua_pushstring(state, "xloadlib");
  3384.     lua_pushcfunction(state, x_loadlib);
  3385.     lua_settable(state, LUA_GLOBALSINDEX);
  3386. #endif
  3387. #endif
  3388.     CreateObjectMetaTable(state);
  3389.     CreateObserverMetaTable(state);
  3390.     CreateCelestiaMetaTable(state);
  3391.     CreatePositionMetaTable(state);
  3392.     CreateVectorMetaTable(state);
  3393.     CreateRotationMetaTable(state);
  3394.     CreateFrameMetaTable(state);
  3395.     CreatePhaseMetaTable(state);
  3396.     CreateCelscriptMetaTable(state);
  3397.     CreateFontMetaTable(state);
  3398.     CreateImageMetaTable(state);
  3399.     CreateTextureMetaTable(state);
  3400.     ExtendCelestiaMetaTable(state);
  3401.     ExtendObjectMetaTable(state);
  3402.     LoadLuaGraphicsLibrary(state);
  3403. }
  3404. void LuaState::allowSystemAccess()
  3405. {
  3406. #if LUA_VER >= 0x050100
  3407.     openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package);
  3408.     openLuaLibrary(state, LUA_IOLIBNAME, luaopen_io);
  3409.     openLuaLibrary(state, LUA_OSLIBNAME, luaopen_os);
  3410. #else
  3411.     luaopen_io(state);
  3412. #endif
  3413.     ioMode = IOAllowed;
  3414. }
  3415. // Permit access to the package library, but prohibit use of the loadlib
  3416. // function.
  3417. void LuaState::allowLuaPackageAccess()
  3418. {
  3419. #if LUA_VER >= 0x050100
  3420.     openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package);
  3421.     // Disallow loadlib
  3422.     lua_getfield(state, LUA_GLOBALSINDEX, "package");
  3423.     lua_pushnil(state);
  3424.     lua_setfield(state, -2, "loadlib");
  3425.     lua_pop(state, 1);
  3426. #endif
  3427. }
  3428. // ==================== Lua Hook Methods ================================================
  3429. void LuaState::setLuaHookEventHandlerEnabled(bool enable)
  3430. {
  3431.     eventHandlerEnabled = enable;
  3432. }
  3433. bool LuaState::callLuaHook(void* obj, const char* method)
  3434. {
  3435.     if (!eventHandlerEnabled)
  3436.         return false;
  3437.     lua_pushlightuserdata(costate, obj);
  3438.     lua_gettable(costate, LUA_REGISTRYINDEX);
  3439.     if (!lua_istable(costate, -1))
  3440.     {
  3441.         lua_pop(costate, 1);
  3442.         return false;
  3443.     }
  3444.     bool handled = false;
  3445.     lua_pushstring(costate, method);
  3446.     lua_gettable(costate, -2);
  3447.     if (lua_isfunction(costate, -1))
  3448.     {
  3449.         lua_pushvalue(costate, -2);          // push the Lua object the stack
  3450.         lua_remove(costate, -3);        // remove the Lua object from the stack
  3451.         timeout = getTime() + 1.0;
  3452.         if (lua_pcall(costate, 1, 1, 0) != 0)
  3453.         {
  3454.             cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "n";
  3455.         }
  3456.         else
  3457.         {
  3458.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  3459.         }
  3460.         lua_pop(costate, 1);             // pop the return value
  3461.     }
  3462.     else
  3463.     {
  3464.         lua_pop(costate, 2);
  3465.     }
  3466.     return handled;
  3467. }
  3468. bool LuaState::callLuaHook(void* obj, const char* method, const char* keyName)
  3469. {
  3470.     if (!eventHandlerEnabled)
  3471.         return false;
  3472.     lua_pushlightuserdata(costate, obj);
  3473.     lua_gettable(costate, LUA_REGISTRYINDEX);
  3474.     if (!lua_istable(costate, -1))
  3475.     {
  3476.         lua_pop(costate, 1);
  3477.         return false;
  3478.     }
  3479.     bool handled = false;
  3480.     lua_pushstring(costate, method);
  3481.     lua_gettable(costate, -2);
  3482.     if (lua_isfunction(costate, -1))
  3483.     {
  3484.         lua_pushvalue(costate, -2);          // push the Lua object onto the stack
  3485.         lua_remove(costate, -3);             // remove the Lua object from the stack
  3486.         lua_pushstring(costate, keyName);    // push the char onto the stack
  3487.         timeout = getTime() + 1.0;
  3488.         if (lua_pcall(costate, 2, 1, 0) != 0)
  3489.         {
  3490.             cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "n";
  3491.         }
  3492.         else
  3493.         {
  3494.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  3495.         }
  3496.         lua_pop(costate, 1);             // pop the return value
  3497.     }
  3498.     else
  3499.     {
  3500.         lua_pop(costate, 2);
  3501.     }
  3502.     return handled;
  3503. }
  3504. bool LuaState::callLuaHook(void* obj, const char* method, float x, float y)
  3505. {
  3506.     if (!eventHandlerEnabled)
  3507.         return false;
  3508.     lua_pushlightuserdata(costate, obj);
  3509. lua_gettable(costate, LUA_REGISTRYINDEX);
  3510.     if (!lua_istable(costate, -1))
  3511.     {
  3512.         lua_pop(costate, 1);
  3513.         return false;
  3514.     }
  3515.     bool handled = false;
  3516.     lua_pushstring(costate, method);
  3517.     lua_gettable(costate, -2);
  3518.     if (lua_isfunction(costate, -1))
  3519.     {
  3520.         lua_pushvalue(costate, -2);          // push the Lua object onto the stack
  3521.         lua_remove(costate, -3);        // remove the Lua object from the stack
  3522.         lua_pushnumber(costate, x);          // push x onto the stack
  3523.         lua_pushnumber(costate, y);          // push y onto the stack
  3524.         timeout = getTime() + 1.0;
  3525.         if (lua_pcall(costate, 3, 1, 0) != 0)
  3526.         {
  3527.             cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "n";
  3528.         }
  3529.         else
  3530.         {
  3531.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  3532.         }
  3533.         lua_pop(costate, 1);             // pop the return value
  3534.     }
  3535.     else
  3536.     {
  3537.         lua_pop(costate, 2);
  3538.     }
  3539.     return handled;
  3540. }
  3541. bool LuaState::callLuaHook(void* obj, const char* method, float x, float y, int b)
  3542. {
  3543.     if (!eventHandlerEnabled)
  3544.         return false;
  3545.     lua_pushlightuserdata(costate, obj);
  3546. lua_gettable(costate, LUA_REGISTRYINDEX);
  3547.     if (!lua_istable(costate, -1))
  3548.     {
  3549.         lua_pop(costate, 1);
  3550.         return false;
  3551.     }
  3552.     bool handled = false;
  3553.     lua_pushstring(costate, method);
  3554. lua_gettable(costate, -2);
  3555.     if (lua_isfunction(costate, -1))
  3556.     {
  3557.         lua_pushvalue(costate, -2);          // push the Lua object onto the stack
  3558.         lua_remove(costate, -3);        // remove the Lua object from the stack
  3559.         lua_pushnumber(costate, x);          // push x onto the stack
  3560.         lua_pushnumber(costate, y);          // push y onto the stack
  3561.         lua_pushnumber(costate, b);          // push b onto the stack
  3562.         timeout = getTime() + 1.0;
  3563.         if (lua_pcall(costate, 4, 1, 0) != 0)
  3564.         {
  3565.             cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "n";
  3566.         }
  3567.         else
  3568.         {
  3569.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  3570.         }
  3571.         lua_pop(costate, 1);             // pop the return value
  3572.     }
  3573.     else
  3574.     {
  3575.         lua_pop(costate, 2);
  3576.     }
  3577.     return handled;
  3578. }
  3579. bool LuaState::callLuaHook(void* obj, const char* method, double dt)
  3580. {
  3581.     if (!eventHandlerEnabled)
  3582.         return false;
  3583.     lua_pushlightuserdata(costate, obj);
  3584.     lua_gettable(costate, LUA_REGISTRYINDEX);
  3585.     if (!lua_istable(costate, -1))
  3586.     {
  3587.         lua_pop(costate, 1);
  3588.         return false;
  3589.     }
  3590.     bool handled = false;
  3591.     lua_pushstring(costate, method);
  3592.     lua_gettable(costate, -2);
  3593.     if (lua_isfunction(costate, -1))
  3594.     {
  3595.         lua_pushvalue(costate, -2);          // push the Lua object onto the stack
  3596.         lua_remove(costate, -3);             // remove the Lua object from the stack
  3597.         lua_pushnumber(costate, dt);
  3598.         timeout = getTime() + 1.0;
  3599.         if (lua_pcall(costate, 2, 1, 0) != 0)
  3600.         {
  3601.             cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "n";
  3602.         }
  3603.         else
  3604.         {
  3605.            handled = lua_toboolean(costate, -1) == 1 ? true : false;
  3606.         }
  3607.         lua_pop(costate, 1);             // pop the return value
  3608.     }
  3609.     else
  3610.     {
  3611.         lua_pop(costate, 2);
  3612.     }
  3613.     return handled;
  3614. }
  3615. /**** Implementation of Celx LuaState wrapper ****/
  3616. CelxLua::CelxLua(lua_State* l) :
  3617. m_lua(l)
  3618. {
  3619. }
  3620. CelxLua::~CelxLua()
  3621. {
  3622. }
  3623. bool CelxLua::isType(int index, int type) const
  3624. {
  3625.     return Celx_istype(m_lua, index, type);
  3626. }
  3627. void CelxLua::setClass(int id)
  3628. {
  3629.     Celx_SetClass(m_lua, id);
  3630. }
  3631. // Push a class name onto the Lua stack
  3632. void CelxLua::pushClassName(int id)
  3633. {
  3634.     lua_pushlstring(m_lua, ClassNames[id], strlen(ClassNames[id]));
  3635. }
  3636. void* CelxLua::checkUserData(int index, int id)
  3637. {
  3638.     return Celx_CheckUserData(m_lua, index, id);
  3639. }
  3640. void CelxLua::doError(const char* errorMessage)
  3641. {
  3642.     Celx_DoError(m_lua, errorMessage);
  3643. }
  3644. void CelxLua::checkArgs(int minArgs, int maxArgs, const char* errorMessage)
  3645. {
  3646.     Celx_CheckArgs(m_lua, minArgs, maxArgs, errorMessage);
  3647. }
  3648. void CelxLua::createClassMetatable(int id)
  3649. {
  3650.     Celx_CreateClassMetatable(m_lua, id);
  3651. }
  3652. void CelxLua::registerMethod(const char* name, lua_CFunction fn)
  3653. {
  3654.     Celx_RegisterMethod(m_lua, name, fn);
  3655. }
  3656. void CelxLua::registerValue(const char* name, float n)
  3657. {
  3658. lua_pushstring(m_lua, name);
  3659. lua_pushnumber(m_lua, n);
  3660. lua_settable(m_lua, -3);
  3661. }
  3662. // Add a field to the table on top of the stack
  3663. void CelxLua::setTable(const char* field, lua_Number value)
  3664. {
  3665.     lua_pushstring(m_lua, field);
  3666.     lua_pushnumber(m_lua, value);
  3667.     lua_settable(m_lua, -3);
  3668. }
  3669. void CelxLua::setTable(const char* field, const char* value)
  3670. {
  3671.     lua_pushstring(m_lua, field);
  3672.     lua_pushstring(m_lua, value);
  3673.     lua_settable(m_lua, -3);
  3674. }
  3675. lua_Number CelxLua::safeGetNumber(int index,
  3676.                                   FatalErrors fatalErrors,
  3677.                                   const char* errorMessage,
  3678.                                   lua_Number defaultValue)
  3679. {
  3680.     return Celx_SafeGetNumber(m_lua, index, fatalErrors, errorMessage, defaultValue);
  3681. }
  3682. const char* CelxLua::safeGetString(int index,
  3683.                                    FatalErrors fatalErrors,
  3684.                                    const char* errorMessage)
  3685. {
  3686.     return Celx_SafeGetString(m_lua, index, fatalErrors, errorMessage);
  3687. }
  3688. bool CelxLua::safeGetBoolean(int index,
  3689.                              FatalErrors fatalErrors,
  3690.                              const char* errorMessage,
  3691.                              bool defaultValue)
  3692. {
  3693.     return Celx_SafeGetBoolean(m_lua, index, fatalErrors, errorMessage, defaultValue);
  3694. }
  3695. void CelxLua::newVector(const Vec3d& v)
  3696. {
  3697.     vector_new(m_lua, v);
  3698. }
  3699. void CelxLua::newPosition(const UniversalCoord& uc)
  3700. {
  3701.     position_new(m_lua, uc);
  3702. }
  3703. void CelxLua::newRotation(const Quatd& q)
  3704. {
  3705.     rotation_new(m_lua, q);
  3706. }
  3707. void CelxLua::newObject(const Selection& sel)
  3708. {
  3709.     object_new(m_lua, sel);
  3710. }
  3711. void CelxLua::newFrame(const ObserverFrame& f)
  3712. {
  3713.     frame_new(m_lua, f);
  3714. }
  3715. void CelxLua::newPhase(const TimelinePhase& phase)
  3716. {
  3717.     phase_new(m_lua, phase);
  3718. }
  3719. Vec3d* CelxLua::toVector(int n)
  3720. {
  3721.     return to_vector(m_lua, n);
  3722. }
  3723. Quatd* CelxLua::toRotation(int n)
  3724. {
  3725.     return to_rotation(m_lua, n);
  3726. }
  3727. UniversalCoord* CelxLua::toPosition(int n)
  3728. {
  3729.     return to_position(m_lua, n);
  3730. }
  3731. Selection* CelxLua::toObject(int n)
  3732. {
  3733.     return to_object(m_lua, n);
  3734. }
  3735. ObserverFrame* CelxLua::toFrame(int n)
  3736. {
  3737.     return to_frame(m_lua, n);
  3738. }
  3739. void CelxLua::push(const CelxValue& v1)
  3740. {
  3741.     v1.push(m_lua);
  3742. }
  3743. void CelxLua::push(const CelxValue& v1, const CelxValue& v2)
  3744. {
  3745.     v1.push(m_lua);
  3746.     v2.push(m_lua);
  3747. }
  3748. CelestiaCore* CelxLua::appCore(FatalErrors fatalErrors)
  3749. {
  3750.     push("celestia-appcore");
  3751.     lua_gettable(m_lua, LUA_REGISTRYINDEX);
  3752.     
  3753.     if (!lua_islightuserdata(m_lua, -1))
  3754.     {
  3755.         if (fatalErrors == NoErrors)
  3756.         {
  3757.             return NULL;
  3758.         }
  3759.         else
  3760.         {
  3761.             lua_pushstring(m_lua, "internal error: invalid appCore");
  3762.             lua_error(m_lua);
  3763.         }
  3764.     }
  3765.     
  3766.     CelestiaCore* appCore = static_cast<CelestiaCore*>(lua_touserdata(m_lua, -1));
  3767.     lua_pop(m_lua, 1);
  3768.     
  3769.     return appCore;
  3770. }
  3771. // Get a pointer to the LuaState-object from the registry:
  3772. LuaState* CelxLua::getLuaStateObject()
  3773. {
  3774.     int stackSize = lua_gettop(m_lua);
  3775.     lua_pushstring(m_lua, "celestia-luastate");
  3776.     lua_gettable(m_lua, LUA_REGISTRYINDEX);
  3777.     
  3778.     if (!lua_islightuserdata(m_lua, -1))
  3779.     {
  3780.         Celx_DoError(m_lua, "Internal Error: Invalid table entry for LuaState-pointer");
  3781.     }
  3782.     LuaState* luastate_ptr = static_cast<LuaState*>(lua_touserdata(m_lua, -1));
  3783.     if (luastate_ptr == NULL)
  3784.     {
  3785.         Celx_DoError(m_lua, "Internal Error: Invalid LuaState-pointer");
  3786.     }
  3787.     lua_settop(m_lua, stackSize);
  3788.     return luastate_ptr;
  3789. }