Lua: Clean up callback code
[lsnes.git] / src / lua / lua.cpp
blobe6542db09f8e11a7bf372a4d926570ab3b4f96f9
1 #include "core/command.hpp"
2 #include "library/globalwrap.hpp"
3 #include "lua/internal.hpp"
4 #include "lua/lua.hpp"
5 #include "lua/unsaferewind.hpp"
6 #include "core/mainloop.hpp"
7 #include "core/memorymanip.hpp"
8 #include "core/moviedata.hpp"
9 #include "core/misc.hpp"
11 #include <map>
12 #include <cstring>
13 #include <string>
14 #include <iostream>
15 extern "C" {
16 #include <lualib.h>
19 uint64_t lua_idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
20 uint64_t lua_timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
21 extern const char* lua_sysrc_script;
23 lua_state LS;
25 void push_keygroup_parameters(lua_state& L, const struct keygroup::parameters& p)
27 L.newtable();
28 L.pushstring("last_rawval");
29 L.pushnumber(p.last_rawval);
30 L.settable(-3);
31 L.pushstring("cal_left");
32 L.pushnumber(p.cal_left);
33 L.settable(-3);
34 L.pushstring("cal_center");
35 L.pushnumber(p.cal_center);
36 L.settable(-3);
37 L.pushstring("cal_right");
38 L.pushnumber(p.cal_right);
39 L.settable(-3);
40 L.pushstring("cal_tolerance");
41 L.pushnumber(p.cal_tolerance);
42 L.settable(-3);
43 L.pushstring("ktype");
44 switch(p.ktype) {
45 case keygroup::KT_DISABLED: L.pushstring("disabled"); break;
46 case keygroup::KT_KEY: L.pushstring("key"); break;
47 case keygroup::KT_AXIS_PAIR: L.pushstring("axis"); break;
48 case keygroup::KT_AXIS_PAIR_INVERSE: L.pushstring("axis-inverse"); break;
49 case keygroup::KT_HAT: L.pushstring("hat"); break;
50 case keygroup::KT_MOUSE: L.pushstring("mouse"); break;
51 case keygroup::KT_PRESSURE_PM: L.pushstring("pressure-pm"); break;
52 case keygroup::KT_PRESSURE_P0: L.pushstring("pressure-p0"); break;
53 case keygroup::KT_PRESSURE_0M: L.pushstring("pressure-0m"); break;
54 case keygroup::KT_PRESSURE_0P: L.pushstring("pressure-0p"); break;
55 case keygroup::KT_PRESSURE_M0: L.pushstring("pressure-m0"); break;
56 case keygroup::KT_PRESSURE_MP: L.pushstring("pressure-mp"); break;
58 L.settable(-3);
61 lua_render_context* lua_render_ctx = NULL;
62 controller_frame* lua_input_controllerdata = NULL;
63 bool lua_booted_flag = false;
65 namespace
67 int push_keygroup_parameters2(lua_state& L, const struct keygroup::parameters* p)
69 push_keygroup_parameters(L, *p);
70 return 1;
73 bool recursive_flag = false;
74 const char* luareader_fragment = NULL;
76 const char* read_lua_fragment(lua_State* LS, void* dummy, size_t* size)
78 if(luareader_fragment) {
79 const char* ret = luareader_fragment;
80 *size = strlen(luareader_fragment);
81 luareader_fragment = NULL;
82 return ret;
83 } else {
84 *size = 0;
85 return NULL;
89 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
91 const char* eval_lua_lua = "loadstring(" TEMPORARY ")();";
92 const char* run_lua_lua = "dofile(" TEMPORARY ");";
94 void run_lua_fragment(lua_state& L) throw(std::bad_alloc)
96 if(recursive_flag)
97 return;
98 #if LUA_VERSION_NUM == 501
99 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment");
100 #endif
101 #if LUA_VERSION_NUM == 502
102 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment", "bt");
103 #endif
104 if(t == LUA_ERRSYNTAX) {
105 messages << "Can't run Lua: Internal syntax error: " << L.tostring(-1)
106 << std::endl;
107 L.pop(1);
108 return;
110 if(t == LUA_ERRMEM) {
111 messages << "Can't run Lua: Out of memory" << std::endl;
112 L.pop(1);
113 return;
115 recursive_flag = true;
116 int r = L.pcall(0, 0, 0);
117 recursive_flag = false;
118 if(r == LUA_ERRRUN) {
119 messages << "Error running Lua hunk: " << L.tostring(-1) << std::endl;
120 L.pop(1);
122 if(r == LUA_ERRMEM) {
123 messages << "Error running Lua hunk: Out of memory" << std::endl;
124 L.pop(1);
126 if(r == LUA_ERRERR) {
127 messages << "Error running Lua hunk: Double Fault???" << std::endl;
128 L.pop(1);
130 if(lua_requests_repaint) {
131 lua_requests_repaint = false;
132 lsnes_cmd.invoke("repaint");
136 void do_eval_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
138 L.pushlstring(c.c_str(), c.length());
139 L.setglobal(TEMPORARY);
140 luareader_fragment = eval_lua_lua;
141 run_lua_fragment(L);
144 void do_run_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
146 L.pushlstring(c.c_str(), c.length());
147 L.setglobal(TEMPORARY);
148 luareader_fragment = run_lua_lua;
149 run_lua_fragment(L);
152 template<typename... T> void run_callback(T... args)
154 if(recursive_flag)
155 return;
156 recursive_flag = true;
157 try {
158 LS.callback(args...);
159 } catch(std::exception& e) {
160 messages << e.what() << std::endl;
162 if(lua_requests_repaint) {
163 lua_requests_repaint = false;
164 lsnes_cmd.invoke("repaint");
166 recursive_flag = false;
169 int system_write_error(lua_State* L)
171 lua_pushstring(L, "_SYSTEM is write-protected");
172 lua_error(L);
173 return 0;
176 void copy_system_tables(lua_state& L)
178 #if LUA_VERSION_NUM == 501
179 L.pushvalue(LUA_GLOBALSINDEX);
180 #endif
181 #if LUA_VERSION_NUM == 502
182 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
183 #endif
184 L.newtable();
185 L.pushnil();
186 while(L.next(-3)) {
187 //Stack: _SYSTEM, KEY, VALUE
188 L.pushvalue(-2);
189 L.pushvalue(-2);
190 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
191 L.rawset(-5);
192 //Stack: _SYSTEM, KEY, VALUE
193 L.pop(1);
194 //Stack: _SYSTEM, KEY
196 L.newtable();
197 L.pushcfunction(system_write_error);
198 L.setfield(-2, "__newindex");
199 L.setmetatable(-2);
200 L.setglobal("_SYSTEM");
203 void run_sysrc_lua(lua_state& L)
205 do_eval_lua(L, lua_sysrc_script);
210 void lua_callback_do_paint(struct lua_render_context* ctx, bool non_synthetic) throw()
212 run_callback("on_paint", lua_state::store_tag(lua_render_ctx, ctx), lua_state::boolean_tag(non_synthetic));
215 void lua_callback_do_video(struct lua_render_context* ctx) throw()
217 run_callback("on_video", lua_state::store_tag(lua_render_ctx, ctx));
220 void lua_callback_do_reset() throw()
222 run_callback("on_reset");
225 void lua_callback_do_frame() throw()
227 run_callback("on_frame");
230 void lua_callback_do_rewind() throw()
232 run_callback("on_rewind");
235 void lua_callback_do_idle() throw()
237 lua_idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
238 run_callback("on_idle");
241 void lua_callback_do_timer() throw()
243 lua_timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
244 run_callback("on_timer");
247 void lua_callback_do_frame_emulated() throw()
249 run_callback("on_frame_emulated");
252 void lua_callback_do_readwrite() throw()
254 run_callback("on_readwrite");
257 void lua_callback_startup() throw()
259 lua_booted_flag = true;
260 run_callback("on_startup");
263 void lua_callback_pre_load(const std::string& name) throw()
265 run_callback("on_pre_load", lua_state::string_tag(name));
268 void lua_callback_err_load(const std::string& name) throw()
270 run_callback("on_err_load", lua_state::string_tag(name));
273 void lua_callback_post_load(const std::string& name, bool was_state) throw()
275 run_callback("on_post_load", lua_state::string_tag(name), lua_state::boolean_tag(was_state));
278 void lua_callback_pre_save(const std::string& name, bool is_state) throw()
280 run_callback("on_pre_save", lua_state::string_tag(name), lua_state::boolean_tag(is_state));
283 void lua_callback_err_save(const std::string& name) throw()
285 run_callback("on_err_save", lua_state::string_tag(name));
288 void lua_callback_post_save(const std::string& name, bool is_state) throw()
290 run_callback("on_post_save", lua_state::string_tag(name), lua_state::boolean_tag(is_state));
293 void lua_callback_do_input(controller_frame& data, bool subframe) throw()
295 run_callback("on_input", lua_state::store_tag(lua_input_controllerdata, &data),
296 lua_state::boolean_tag(subframe));
299 void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
301 run_callback("on_snoop", lua_state::numeric_tag(port), lua_state::numeric_tag(controller),
302 lua_state::numeric_tag(index), lua_state::numeric_tag(value));
305 namespace
307 function_ptr_command<const std::string&> evaluate_lua(lsnes_cmd, "evaluate-lua", "Evaluate expression in "
308 "Lua VM", "Syntax: evaluate-lua <expression>\nEvaluates <expression> in Lua VM.\n",
309 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
310 if(args == "")
311 throw std::runtime_error("Expected expression to evaluate");
312 do_eval_lua(LS, args);
315 function_ptr_command<arg_filename> run_lua(lsnes_cmd, "run-lua", "Run Lua script in Lua VM",
316 "Syntax: run-lua <file>\nRuns <file> in Lua VM.\n",
317 [](arg_filename args) throw(std::bad_alloc, std::runtime_error)
319 do_run_lua(LS, args);
322 function_ptr_command<> reset_lua(lsnes_cmd, "reset-lua", "Reset the Lua VM",
323 "Syntax: reset-lua\nReset the Lua VM.\n",
324 []() throw(std::bad_alloc, std::runtime_error)
326 LS.reset();
327 luaL_openlibs(LS.handle());
329 run_sysrc_lua(LS);
330 copy_system_tables(LS);
331 messages << "Lua VM reset" << std::endl;
336 void lua_callback_quit() throw()
338 run_callback("on_quit");
341 void lua_callback_keyhook(const std::string& key, const struct keygroup::parameters& p) throw()
343 run_callback("on_keyhook", lua_state::string_tag(key), lua_state::fnptr_tag(push_keygroup_parameters2, &p));
346 void init_lua(bool soft) throw()
348 char tmpkey;
349 LS.set_oom_handler(OOM_panic);
350 try {
351 LS.reset();
352 } catch(std::exception& e) {
353 messages << "Can't initialize Lua." << std::endl;
354 if(soft)
355 return;
356 fatal_error();
358 LS.getglobal("print");
359 luaL_openlibs(LS.handle());
360 LS.setglobal("print");
361 run_sysrc_lua(LS);
362 copy_system_tables(LS);
365 void quit_lua() throw()
367 LS.deinit();
371 #define LUA_TIMED_HOOK_IDLE 0
372 #define LUA_TIMED_HOOK_TIMER 1
374 uint64_t lua_timed_hook(int timer) throw()
376 switch(timer) {
377 case LUA_TIMED_HOOK_IDLE:
378 return lua_idle_hook_time;
379 case LUA_TIMED_HOOK_TIMER:
380 return lua_timer_hook_time;
384 void lua_callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void* u)
386 if(u) {
387 lua_unsaferewind* u2 = reinterpret_cast<lua_obj_pin<lua_unsaferewind>*>(u)->object();
388 //Load.
389 try {
390 run_callback("on_pre_rewind");
391 mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
392 mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
393 run_callback("on_post_rewind");
394 } catch(...) {
395 return;
397 } else {
398 //Save
399 run_callback("on_set_rewind", lua_state::fn_tag([save, secs, ssecs, &mov](lua_state& L) -> int {
400 lua_unsaferewind* u2 = lua_class<lua_unsaferewind>::create(LS);
401 u2->state = save;
402 u2->secs = secs,
403 u2->ssecs = ssecs;
404 mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
405 return 1;
406 }));
410 bool lua_requests_repaint = false;
411 bool lua_requests_subframe_paint = false;
413 DECLARE_LUACLASS(lua_unsaferewind, "UNSAFEREWIND");