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