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 = ∈
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