From 017f4e485a5c0acfd72f522d4909f19616ff879d Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 25 Dec 2014 19:10:11 +0100 Subject: luaengine: add screen drawing/HUD capabilities This commit allows LUA scripts to implement HUD capabilities, by overlaying elements (lines, boxes, text) to screen. Mostly used to draw custom graphic helpers for trainings and TAS runs development. Signed-off-by: Luca Bruno --- src/emu/luaengine.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/emu/luaengine.h | 6 +++ 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/emu/luaengine.c b/src/emu/luaengine.c index 843c873233e..712da413520 100644 --- a/src/emu/luaengine.c +++ b/src/emu/luaengine.c @@ -17,6 +17,7 @@ #include "emuopts.h" #include "osdepend.h" #include "drivenum.h" +#include "ui/ui.h" #include "web/mongoose.h" //************************************************************************** @@ -336,6 +337,26 @@ int lua_engine::l_emu_hook_output(lua_State *L) return 0; } +//------------------------------------------------- +// machine_get_screens - return table of available screens userdata +// -> manager:machine().screens[":screen"] +//------------------------------------------------- + +luabridge::LuaRef lua_engine::l_machine_get_screens(const running_machine *r) +{ + lua_State *L = luaThis->m_lua_state; + luabridge::LuaRef screens_table = luabridge::LuaRef::newTable(L); + + for (device_t *dev = r->first_screen(); dev != NULL; dev = dev->next()) { + screen_device *sc = dynamic_cast(dev); + if (sc && sc->configured() && sc->started() && sc->type()) { + screens_table[sc->tag()] = sc; + } + } + + return screens_table; +} + //------------------------------------------------- // machine_get_devices - return table of available devices userdata // -> manager:machine().devices[":maincpu"] @@ -438,6 +459,108 @@ int lua_engine::lua_addr_space::l_mem_read(lua_State *L) } +//------------------------------------------------- +// draw_box - draw a box on a screen container +// -> manager:machine().screens[":screen"]:draw_box(x1, y1, x2, y2, bgcolor, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_box(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 6 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "background color (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 7), 7, "outline color (integer) expected"); + + // retrieve all parameters + float x1, y1, x2, y2; + x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->width()) , 1.0f); + y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->height()), 1.0f); + UINT32 bgcolor = lua_tounsigned(L, 6); + UINT32 fgcolor = lua_tounsigned(L, 7); + + // draw the box + render_container &rc = sc->container(); + ui_manager &ui = sc->machine().ui(); + ui.draw_outlined_box(&rc, x1, y1, x2, y2, fgcolor, bgcolor); + + return 0; +} + +//------------------------------------------------- +// draw_line - draw a line on a screen container +// -> manager:machine().screens[":screen"]:draw_line(x1, y1, x2, y2, linecolor) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_line(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got 5 numerical parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 6), 6, "color (integer) expected"); + + // retrieve all parameters + float x1, y1, x2, y2; + x1 = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + y1 = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + x2 = MIN(lua_tounsigned(L, 4) / static_cast(sc->width()) , 1.0f); + y2 = MIN(lua_tounsigned(L, 5) / static_cast(sc->height()), 1.0f); + UINT32 color = lua_tounsigned(L, 6); + + // draw the line + sc->container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA)); + return 0; +} + +//------------------------------------------------- +// draw_text - draw text on a screen container +// -> manager:machine().screens[":screen"]:draw_text(x, y, message) +//------------------------------------------------- + +int lua_engine::lua_screen::l_draw_text(lua_State *L) +{ + screen_device *sc = luabridge::Stack::get(L, 1); + if(!sc) { + return 0; + } + + // ensure that we got proper parameters + luaL_argcheck(L, lua_isnumber(L, 2), 2, "x (integer) expected"); + luaL_argcheck(L, lua_isnumber(L, 3), 3, "y (integer) expected"); + luaL_argcheck(L, lua_isstring(L, 4), 4, "message (string) expected"); + + // retrieve all parameters + float x = MIN(lua_tounsigned(L, 2) / static_cast(sc->width()) , 1.0f); + float y = MIN(lua_tounsigned(L, 3) / static_cast(sc->height()), 1.0f); + const char *msg = luaL_checkstring(L,4); + // TODO: add optional parameters (colors, etc.) + + // draw the text + render_container &rc = sc->container(); + ui_manager &ui = sc->machine().ui(); + ui.draw_text_full(&rc, msg, x, y , (1.0f - x), + JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, + UI_TEXT_BG_COLOR, NULL, NULL); + + return 0; +} + void *lua_engine::checkparam(lua_State *L, int idx, const char *tname) { const char *name; @@ -667,6 +790,7 @@ void lua_engine::initialize() .addFunction ("soft_reset", &running_machine::schedule_soft_reset) .addFunction ("system", &running_machine::system) .addProperty ("devices", &lua_engine::l_machine_get_devices) + .addProperty ("screens", &lua_engine::l_machine_get_screens) .endClass () .beginClass ("game_driver") .addData ("name", &game_driver::name) @@ -691,7 +815,17 @@ void lua_engine::initialize() .deriveClass ("addr_space") .addFunction("name", &address_space::name) .endClass() - .endNamespace (); + .beginClass ("lua_screen_dev") + .addCFunction ("draw_box", &lua_screen::l_draw_box) + .addCFunction ("draw_line", &lua_screen::l_draw_line) + .addCFunction ("draw_text", &lua_screen::l_draw_text) + .endClass() + .deriveClass ("screen_dev") + .addFunction ("name", &screen_device::name) + .addFunction ("height", &screen_device::height) + .addFunction ("width", &screen_device::width) + .endClass() + .endNamespace(); luabridge::push (m_lua_state, machine_manager::instance()); lua_setglobal(m_lua_state, "manager"); diff --git a/src/emu/luaengine.h b/src/emu/luaengine.h index ae1221415ff..d2f7d9323f1 100644 --- a/src/emu/luaengine.h +++ b/src/emu/luaengine.h @@ -105,6 +105,12 @@ private: struct lua_addr_space { template int l_mem_read(lua_State *L); }; + static luabridge::LuaRef l_machine_get_screens(const running_machine *r); + struct lua_screen { + int l_draw_box(lua_State *L); + int l_draw_line(lua_State *L); + int l_draw_text(lua_State *L); + }; void resume(void *L, INT32 param); void report_errors(int status); -- cgit v1.2.3 From be2e7b57ebc3c96103c829aa6371fb70690b9f74 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 25 Dec 2014 19:25:09 +0100 Subject: luaengine: add frame hooking support This commit adds a method to let LUA scripts register a callback to be invoked before rendering each frame. This callback typically makes use of screen drawing methods to draw a custom HUD on top of each frame. Signed-off-by: Luca Bruno --- src/emu/luaengine.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/emu/luaengine.h | 5 +++++ src/emu/video.c | 3 +++ 3 files changed, 51 insertions(+) diff --git a/src/emu/luaengine.c b/src/emu/luaengine.c index 712da413520..9bf22d6db12 100644 --- a/src/emu/luaengine.c +++ b/src/emu/luaengine.c @@ -337,6 +337,31 @@ int lua_engine::l_emu_hook_output(lua_State *L) return 0; } +int lua_engine::l_emu_set_hook(lua_State *L) +{ + luaThis->emu_set_hook(L); + return 0; +} + +void lua_engine::emu_set_hook(lua_State *L) +{ + luaL_argcheck(L, lua_isfunction(L, 1) || lua_isnil(L, 1), 1, "callback function expected"); + luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected"); + const char *hookname = luaL_checkstring(L,2); + + if (strcmp(hookname, "output") == 0) { + hook_output_cb.set(L, 1); + if (!output_notifier_set) { + output_set_notifier(NULL, s_output_notifier, this); + output_notifier_set = true; + } + } else if (strcmp(hookname, "frame") == 0) { + hook_frame_cb.set(L, 1); + } else { + luai_writestringerror("%s", "Unknown hook name, aborting.\n"); + } +} + //------------------------------------------------- // machine_get_screens - return table of available screens userdata // -> manager:machine().screens[":screen"] @@ -773,6 +798,7 @@ void lua_engine::initialize() .addCFunction ("romname", l_emu_romname ) .addCFunction ("keypost", l_emu_keypost ) .addCFunction ("hook_output", l_emu_hook_output ) + .addCFunction ("sethook", l_emu_set_hook ) .addCFunction ("time", l_emu_time ) .addCFunction ("wait", l_emu_wait ) .addCFunction ("after", l_emu_after ) @@ -836,6 +862,23 @@ void lua_engine::start_console() mg_start_thread(::serve_lua, this); } +//------------------------------------------------- +// frame_hook - called at each frame refresh, used to draw a HUD +//------------------------------------------------- +bool lua_engine::frame_hook() +{ + bool is_cb_hooked = false; + if (m_machine != NULL) { + // invoke registered callback (if any) + is_cb_hooked = hook_frame_cb.active(); + if (is_cb_hooked) { + lua_State *L = hook_frame_cb.precall(); + hook_frame_cb.call(this, L, 0); + } + } + return is_cb_hooked; +} + void lua_engine::periodic_check() { osd_lock_acquire(lock); diff --git a/src/emu/luaengine.h b/src/emu/luaengine.h index d2f7d9323f1..0f5fcdda2b0 100644 --- a/src/emu/luaengine.h +++ b/src/emu/luaengine.h @@ -44,6 +44,7 @@ public: void serve_lua(); void periodic_check(); + bool frame_hook(); void resume(lua_State *L, int nparam = 0, lua_State *root = NULL); void set_machine(running_machine *machine) { m_machine = machine; update_machine(); } @@ -68,6 +69,8 @@ private: hook hook_output_cb; bool output_notifier_set; + hook hook_frame_cb; + static lua_engine* luaThis; std::map > thread_registry; @@ -82,6 +85,7 @@ private: int emu_after(lua_State *L); int emu_wait(lua_State *L); void emu_hook_output(lua_State *L); + void emu_set_hook(lua_State *L); static int l_ioport_write(lua_State *L); static int l_emu_after(lua_State *L); @@ -97,6 +101,7 @@ private: static int l_emu_start(lua_State *L); static int l_emu_pause(lua_State *L); static int l_emu_unpause(lua_State *L); + static int l_emu_set_hook(lua_State *L); // "emu.machine" namespace static luabridge::LuaRef l_machine_get_devices(const running_machine *r); diff --git a/src/emu/video.c b/src/emu/video.c index 63c8e6d4b80..2cc4ff225f2 100644 --- a/src/emu/video.c +++ b/src/emu/video.c @@ -655,6 +655,9 @@ bool video_manager::finish_screen_updates() if (screen->update_quads()) anything_changed = true; + // draw HUD from LUA callback (if any) + anything_changed |= machine().manager().lua()->frame_hook(); + // update our movie recording and burn-in state if (!machine().paused()) { -- cgit v1.2.3