Lua: gui.synchronous_repaint()
[lsnes.git] / src / lua / lua.cpp
blob23d72ac394c7c9cae7d572e0088cb035085bb442
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 bool* lua_veto_flag = NULL;
22 bool* lua_kill_frame = NULL;
23 extern const char* lua_sysrc_script;
24 void* synchronous_paint_ctx;
26 lua_state lsnes_lua_state;
27 lua_function_group lua_func_bit;
28 lua_function_group lua_func_misc;
29 lua_function_group lua_func_callback;
30 lua_function_group lua_func_load;
32 namespace
34 void pushpair(lua_state& L, std::string key, double value)
36 L.pushstring(key.c_str());
37 L.pushnumber(value);
38 L.settable(-3);
41 void pushpair(lua_state& L, std::string key, std::string value)
43 L.pushstring(key.c_str());
44 L.pushstring(value.c_str());
45 L.settable(-3);
48 std::string get_mode_str(int mode)
50 if(mode < 0)
51 return "disabled";
52 else if(mode > 0)
53 return "axis";
54 return "pressure0+";
58 void push_keygroup_parameters(lua_state& L, keyboard_key& p)
60 keyboard_mouse_calibration p2;
61 keyboard_axis_calibration p3;
62 int mode;
63 L.newtable();
64 switch(p.get_type()) {
65 case KBD_KEYTYPE_KEY:
66 pushpair(L, "value", p.get_state());
67 pushpair(L, "type", "key");
68 break;
69 case KBD_KEYTYPE_HAT:
70 pushpair(L, "value", p.get_state());
71 pushpair(L, "type", "hat");
72 break;
73 case KBD_KEYTYPE_MOUSE:
74 p2 = p.cast_mouse()->get_calibration();
75 pushpair(L, "value", p.get_state());
76 pushpair(L, "type", "mouse");
77 break;
78 case KBD_KEYTYPE_AXIS:
79 mode = p.cast_axis()->get_mode();
80 pushpair(L, "value", p.get_state());
81 pushpair(L, "type", get_mode_str(mode));
82 break;
86 lua_render_context* lua_render_ctx = NULL;
87 controller_frame* lua_input_controllerdata = NULL;
88 bool lua_booted_flag = false;
90 namespace
92 int push_keygroup_parameters2(lua_state& L, keyboard_key* p)
94 push_keygroup_parameters(L, *p);
95 return 1;
98 bool recursive_flag = false;
99 const char* luareader_fragment = NULL;
101 const char* read_lua_fragment(lua_State* L, void* dummy, size_t* size)
103 if(luareader_fragment) {
104 const char* ret = luareader_fragment;
105 *size = strlen(luareader_fragment);
106 luareader_fragment = NULL;
107 return ret;
108 } else {
109 *size = 0;
110 return NULL;
114 #define TEMPORARY "LUAINTERP_INTERNAL_COMMAND_TEMPORARY"
116 const char* eval_lua_lua = "loadstring(" TEMPORARY ")();";
117 const char* run_lua_lua = "dofile(" TEMPORARY ");";
119 void run_lua_fragment(lua_state& L) throw(std::bad_alloc)
121 if(recursive_flag)
122 return;
123 #if LUA_VERSION_NUM == 501
124 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment");
125 #endif
126 #if LUA_VERSION_NUM == 502
127 int t = L.load(read_lua_fragment, NULL, "run_lua_fragment", "t");
128 #endif
129 if(t == LUA_ERRSYNTAX) {
130 messages << "Can't run Lua: Internal syntax error: " << L.tostring(-1)
131 << std::endl;
132 L.pop(1);
133 return;
135 if(t == LUA_ERRMEM) {
136 messages << "Can't run Lua: Out of memory" << std::endl;
137 L.pop(1);
138 return;
140 recursive_flag = true;
141 int r = L.pcall(0, 0, 0);
142 recursive_flag = false;
143 if(r == LUA_ERRRUN) {
144 messages << "Error running Lua hunk: " << L.tostring(-1) << std::endl;
145 L.pop(1);
147 if(r == LUA_ERRMEM) {
148 messages << "Error running Lua hunk: Out of memory" << std::endl;
149 L.pop(1);
151 if(r == LUA_ERRERR) {
152 messages << "Error running Lua hunk: Double Fault???" << std::endl;
153 L.pop(1);
155 lua_render_ctx = NULL;
156 if(lua_requests_repaint) {
157 lua_requests_repaint = false;
158 lsnes_cmd.invoke("repaint");
162 void do_eval_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
164 L.pushlstring(c.c_str(), c.length());
165 L.setglobal(TEMPORARY);
166 luareader_fragment = eval_lua_lua;
167 run_lua_fragment(L);
170 void do_run_lua(lua_state& L, const std::string& c) throw(std::bad_alloc)
172 L.pushlstring(c.c_str(), c.length());
173 L.setglobal(TEMPORARY);
174 luareader_fragment = run_lua_lua;
175 run_lua_fragment(L);
178 template<typename... T> bool run_callback(lua_state::lua_callback_list& list, T... args)
180 if(recursive_flag)
181 return true;
182 recursive_flag = true;
183 try {
184 if(!list.callback(args...)) {
185 recursive_flag = false;
186 return false;
188 } catch(std::exception& e) {
189 messages << e.what() << std::endl;
191 lua_render_ctx = NULL;
192 if(lua_requests_repaint) {
193 lua_requests_repaint = false;
194 lsnes_cmd.invoke("repaint");
196 recursive_flag = false;
197 return true;
200 int system_write_error(lua_State* L)
202 lua_pushstring(L, "_SYSTEM is write-protected");
203 lua_error(L);
204 return 0;
207 void copy_system_tables(lua_state& L)
209 #if LUA_VERSION_NUM == 501
210 L.pushvalue(LUA_GLOBALSINDEX);
211 #endif
212 #if LUA_VERSION_NUM == 502
213 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
214 #endif
215 L.newtable();
216 L.pushnil();
217 while(L.next(-3)) {
218 //Stack: _SYSTEM, KEY, VALUE
219 L.pushvalue(-2);
220 L.pushvalue(-2);
221 //Stack: _SYSTEM, KEY, VALUE, KEY, VALUE
222 L.rawset(-5);
223 //Stack: _SYSTEM, KEY, VALUE
224 L.pop(1);
225 //Stack: _SYSTEM, KEY
227 L.newtable();
228 L.pushcfunction(system_write_error);
229 L.setfield(-2, "__newindex");
230 L.setmetatable(-2);
231 L.setglobal("_SYSTEM");
234 void run_sysrc_lua(lua_state& L)
236 do_eval_lua(L, lua_sysrc_script);
239 void run_synchronous_paint(struct lua_render_context* ctx)
241 if(!synchronous_paint_ctx)
242 return;
243 lua_renderq_run(ctx, synchronous_paint_ctx);
246 #define DEFINE_CB(X) lua_state::lua_callback_list on_##X (lsnes_lua_state, #X , "on_" #X )
248 DEFINE_CB(paint);
249 DEFINE_CB(video);
250 DEFINE_CB(reset);
251 DEFINE_CB(frame);
252 DEFINE_CB(rewind);
253 DEFINE_CB(idle);
254 DEFINE_CB(timer);
255 DEFINE_CB(frame_emulated);
256 DEFINE_CB(readwrite);
257 DEFINE_CB(startup);
258 DEFINE_CB(pre_load);
259 DEFINE_CB(post_load);
260 DEFINE_CB(err_load);
261 DEFINE_CB(pre_save);
262 DEFINE_CB(post_save);
263 DEFINE_CB(err_save);
264 DEFINE_CB(input);
265 DEFINE_CB(snoop);
266 DEFINE_CB(snoop2);
267 DEFINE_CB(button);
268 DEFINE_CB(quit);
269 DEFINE_CB(keyhook);
270 DEFINE_CB(movie_lost);
271 DEFINE_CB(pre_rewind);
272 DEFINE_CB(post_rewind);
273 DEFINE_CB(set_rewind);
276 void lua_callback_do_paint(struct lua_render_context* ctx, bool non_synthetic) throw()
278 run_synchronous_paint(ctx);
279 run_callback(on_paint, lua_state::store_tag(lua_render_ctx, ctx), lua_state::boolean_tag(non_synthetic));
282 void lua_callback_do_video(struct lua_render_context* ctx, bool& kill_frame) throw()
284 run_callback(on_video, lua_state::store_tag(lua_render_ctx, ctx), lua_state::store_tag(lua_kill_frame,
285 &kill_frame));
288 void lua_callback_do_reset() throw()
290 run_callback(on_reset);
293 void lua_callback_do_frame() throw()
295 run_callback(on_frame);
298 void lua_callback_do_rewind() throw()
300 run_callback(on_rewind);
303 void lua_callback_do_idle() throw()
305 lua_idle_hook_time = 0x7EFFFFFFFFFFFFFFULL;
306 run_callback(on_idle);
309 void lua_callback_do_timer() throw()
311 lua_timer_hook_time = 0x7EFFFFFFFFFFFFFFULL;
312 run_callback(on_timer);
315 void lua_callback_do_frame_emulated() throw()
317 run_callback(on_frame_emulated);
320 void lua_callback_do_readwrite() throw()
322 run_callback(on_readwrite);
325 void lua_callback_startup() throw()
327 lua_booted_flag = true;
328 run_callback(on_startup);
331 void lua_callback_pre_load(const std::string& name) throw()
333 run_callback(on_pre_load, lua_state::string_tag(name));
336 void lua_callback_err_load(const std::string& name) throw()
338 run_callback(on_err_load, lua_state::string_tag(name));
341 void lua_callback_post_load(const std::string& name, bool was_state) throw()
343 run_callback(on_post_load, lua_state::string_tag(name), lua_state::boolean_tag(was_state));
346 void lua_callback_pre_save(const std::string& name, bool is_state) throw()
348 run_callback(on_pre_save, lua_state::string_tag(name), lua_state::boolean_tag(is_state));
351 void lua_callback_err_save(const std::string& name) throw()
353 run_callback(on_err_save, lua_state::string_tag(name));
356 void lua_callback_post_save(const std::string& name, bool is_state) throw()
358 run_callback(on_post_save, lua_state::string_tag(name), lua_state::boolean_tag(is_state));
361 void lua_callback_do_input(controller_frame& data, bool subframe) throw()
363 run_callback(on_input, lua_state::store_tag(lua_input_controllerdata, &data),
364 lua_state::boolean_tag(subframe));
367 void lua_callback_snoop_input(uint32_t port, uint32_t controller, uint32_t index, short value) throw()
369 if(run_callback(on_snoop2, lua_state::numeric_tag(port), lua_state::numeric_tag(controller),
370 lua_state::numeric_tag(index), lua_state::numeric_tag(value)))
371 return;
372 run_callback(on_snoop, lua_state::numeric_tag(port), lua_state::numeric_tag(controller),
373 lua_state::numeric_tag(index), lua_state::numeric_tag(value));
376 bool lua_callback_do_button(uint32_t port, uint32_t controller, uint32_t index, const char* type)
378 bool flag = false;
379 run_callback(on_button, lua_state::store_tag(lua_veto_flag, &flag), lua_state::numeric_tag(port),
380 lua_state::numeric_tag(controller), lua_state::numeric_tag(index), lua_state::string_tag(type));
381 return flag;
384 namespace
386 function_ptr_command<const std::string&> evaluate_lua(lsnes_cmd, "evaluate-lua", "Evaluate expression in "
387 "Lua VM", "Syntax: evaluate-lua <expression>\nEvaluates <expression> in Lua VM.\n",
388 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
389 if(args == "")
390 throw std::runtime_error("Expected expression to evaluate");
391 do_eval_lua(lsnes_lua_state, args);
394 function_ptr_command<const std::string&> evaluate_lua2(lsnes_cmd, "L", "Evaluate expression in "
395 "Lua VM", "Syntax: evaluate-lua <expression>\nEvaluates <expression> in Lua VM.\n",
396 [](const std::string& args) throw(std::bad_alloc, std::runtime_error) {
397 if(args == "")
398 throw std::runtime_error("Expected expression to evaluate");
399 do_eval_lua(lsnes_lua_state, args);
402 function_ptr_command<arg_filename> run_lua(lsnes_cmd, "run-lua", "Run Lua script in Lua VM",
403 "Syntax: run-lua <file>\nRuns <file> in Lua VM.\n",
404 [](arg_filename args) throw(std::bad_alloc, std::runtime_error)
406 do_run_lua(lsnes_lua_state, args);
409 function_ptr_command<> reset_lua(lsnes_cmd, "reset-lua", "Reset the Lua VM",
410 "Syntax: reset-lua\nReset the Lua VM.\n",
411 []() throw(std::bad_alloc, std::runtime_error)
413 lsnes_lua_state.reset();
414 luaL_openlibs(lsnes_lua_state.handle());
416 run_sysrc_lua(lsnes_lua_state);
417 copy_system_tables(lsnes_lua_state);
418 messages << "Lua VM reset" << std::endl;
423 void lua_callback_quit() throw()
425 run_callback(on_quit);
428 void lua_callback_keyhook(const std::string& key, keyboard_key& p) throw()
430 run_callback(on_keyhook, lua_state::string_tag(key), lua_state::fnptr_tag(push_keygroup_parameters2, &p));
433 void init_lua() throw()
435 lsnes_lua_state.set_oom_handler(OOM_panic);
436 try {
437 lsnes_lua_state.reset();
438 lsnes_lua_state.add_function_group(lua_func_bit);
439 lsnes_lua_state.add_function_group(lua_func_load);
440 lsnes_lua_state.add_function_group(lua_func_callback);
441 lsnes_lua_state.add_function_group(lua_func_misc);
442 } catch(std::exception& e) {
443 messages << "Can't initialize Lua." << std::endl;
444 fatal_error();
446 luaL_openlibs(lsnes_lua_state.handle());
447 run_sysrc_lua(lsnes_lua_state);
448 copy_system_tables(lsnes_lua_state);
451 void quit_lua() throw()
453 lsnes_lua_state.deinit();
457 #define LUA_TIMED_HOOK_IDLE 0
458 #define LUA_TIMED_HOOK_TIMER 1
460 uint64_t lua_timed_hook(int timer) throw()
462 switch(timer) {
463 case LUA_TIMED_HOOK_IDLE:
464 return lua_idle_hook_time;
465 case LUA_TIMED_HOOK_TIMER:
466 return lua_timer_hook_time;
468 return 0;
471 void lua_callback_do_unsafe_rewind(const std::vector<char>& save, uint64_t secs, uint64_t ssecs, movie& mov, void* u)
473 if(u) {
474 lua_unsaferewind* u2 = reinterpret_cast<lua_obj_pin<lua_unsaferewind>*>(u)->object();
475 //Load.
476 try {
477 run_callback(on_pre_rewind);
478 run_callback(on_movie_lost, "unsaferewind");
479 mainloop_restore_state(u2->state, u2->secs, u2->ssecs);
480 mov.fast_load(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
481 try { get_host_memory() = u2->hostmemory; } catch(...) {}
482 run_callback(on_post_rewind);
483 } catch(...) {
484 return;
486 } else {
487 //Save
488 run_callback(on_set_rewind, lua_state::fn_tag([save, secs, ssecs, &mov](lua_state& L) -> int {
489 lua_unsaferewind* u2 = lua_class<lua_unsaferewind>::create(lsnes_lua_state);
490 u2->state = save;
491 u2->secs = secs,
492 u2->ssecs = ssecs;
493 u2->hostmemory = get_host_memory();
494 mov.fast_save(u2->frame, u2->ptr, u2->lag, u2->pollcounters);
495 return 1;
496 }));
500 void lua_callback_movie_lost(const char* what)
502 run_callback(on_movie_lost, std::string(what));
505 bool lua_requests_repaint = false;
506 bool lua_requests_subframe_paint = false;
508 DECLARE_LUACLASS(lua_unsaferewind, "UNSAFEREWIND");