From 2de04414ed0fdff027eb99c83faca2074a944bc6 Mon Sep 17 00:00:00 2001 From: cracyc Date: Sat, 5 Nov 2016 17:45:35 -0500 Subject: [PATCH 1/5] plugins/console: add lua console [Carl] --- plugins/console/init.lua | 84 +++++++++++++++++++ plugins/console/plugin.json | 10 +++ scripts/src/mame/frontend.lua | 2 - src/frontend/mame/clifront.cpp | 7 -- src/frontend/mame/console.cpp | 138 -------------------------------- src/frontend/mame/console.h | 41 ---------- src/frontend/mame/luaengine.cpp | 67 ++++++++++++++++ src/frontend/mame/luaengine.h | 12 ++- src/frontend/mame/mame.cpp | 1 + 9 files changed, 172 insertions(+), 190 deletions(-) create mode 100644 plugins/console/init.lua create mode 100644 plugins/console/plugin.json delete mode 100644 src/frontend/mame/console.cpp delete mode 100644 src/frontend/mame/console.h diff --git a/plugins/console/init.lua b/plugins/console/init.lua new file mode 100644 index 00000000000..fea0f930d42 --- /dev/null +++ b/plugins/console/init.lua @@ -0,0 +1,84 @@ +-- license:BSD-3-Clause +-- copyright-holders:Carl +local exports = {} +exports.name = "console" +exports.version = "0.0.1" +exports.description = "Console plugin" +exports.license = "The BSD 3-Clause License" +exports.author = { name = "Carl" } + +local console = exports + +function console.startplugin() + local conth = emu.thread() + local started = false + local ln = require("linenoise") + print(" _/ _/ _/_/ _/ _/ _/_/_/_/"); + print(" _/_/ _/_/ _/ _/ _/_/ _/_/ _/ "); + print(" _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ "); + print(" _/ _/ _/ _/ _/ _/ _/ "); + print("_/ _/ _/ _/ _/ _/ _/_/_/_/ \n"); + print(emu.app_name() .. " " .. emu.app_version(), "\nCopyright (C) Nicola Salmoria and the MAME team\n"); + print(_VERSION, "\nCopyright (C) Lua.org, PUC-Rio\n"); + -- linenoise isn't thread safe but that means history can handled here + -- that also means that bad things will happen if anything outside lua tries to use it + -- especially the completion callback + ln.historysetmaxlen(10) + local scr = "local ln = require('linenoise')\n" + scr = scr .. "ln.setcompletion(function(c, str) status = str\n" + scr = scr .. " yield()\n" -- coroutines can't yield in the middle of a callback so this is a real thread + scr = scr .. " status:gsub('[^,]*', function(s) if s ~= '' then ln.addcompletion(c, s) end end)\n" + scr = scr .. "end)\n" + scr = scr .. "return ln.linenoise('\x1b[1;36m[MAME]\x1b[0m> ')" + + function get_completions(str) + local function is_pair_iterable(t) + local mt = getmetatable(t) + return type(t) == 'table' or (mt and mt.__pairs) + end + local comps = "," + local table, last = str:match("([(]?[%w.:()]-)[:.]?([%w]*)$") + if last == "" then + return comps + end + local err + err, tablef = pcall(load("return " .. table)) + if (not err) or (not tablef) then + return comps + end + if is_pair_iterable(tablef) then + for k, v in pairs(tablef) do + if k:match("^" .. last) then + comps = comps .. "," .. table + end + end + end + return comps + end + + emu.register_periodic(function() + if conth.yield then + conth:continue(get_completions(conth.result)) + return + elseif conth.busy then + return + elseif started then + local cmd = conth.result + ln.historyadd(cmd) + local func, err = load(cmd) + if not func then + print("error: ", err) + else + local status + status, err = pcall(func) + if not status then + print("error: ", err) + end + end + end + conth:start(scr) + started = true + end) +end + +return exports diff --git a/plugins/console/plugin.json b/plugins/console/plugin.json new file mode 100644 index 00000000000..182bb887785 --- /dev/null +++ b/plugins/console/plugin.json @@ -0,0 +1,10 @@ +{ + "plugin": { + "name": "console", + "description": "Console plugin", + "version": "0.0.1", + "author": "Carl", + "type": "plugin", + "start": "false" + } +} diff --git a/scripts/src/mame/frontend.lua b/scripts/src/mame/frontend.lua index bb89b1b3350..be764f32a0e 100644 --- a/scripts/src/mame/frontend.lua +++ b/scripts/src/mame/frontend.lua @@ -66,8 +66,6 @@ files { MAME_DIR .. "src/frontend/mame/cheat.h", MAME_DIR .. "src/frontend/mame/clifront.cpp", MAME_DIR .. "src/frontend/mame/clifront.h", - MAME_DIR .. "src/frontend/mame/console.cpp", - MAME_DIR .. "src/frontend/mame/console.h", MAME_DIR .. "src/frontend/mame/info.cpp", MAME_DIR .. "src/frontend/mame/info.h", MAME_DIR .. "src/frontend/mame/language.cpp", diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp index 96281849884..40775af09c9 100644 --- a/src/frontend/mame/clifront.cpp +++ b/src/frontend/mame/clifront.cpp @@ -29,7 +29,6 @@ #include "ui/moptions.h" #include "language.h" #include "pluginopts.h" -#include "console.h" #include #include @@ -312,13 +311,7 @@ int cli_frontend::execute(int argc, char **argv) manager->start_luaengine(); - if (m_options.console()) { - //manager->lua()->start_console(); - console_frontend console(m_options, m_osd); - console.start_console(); - } else { start_execution(manager, argc, argv, option_errors); - } } // handle exceptions of various types catch (emu_fatalerror &fatal) diff --git a/src/frontend/mame/console.cpp b/src/frontend/mame/console.cpp deleted file mode 100644 index 2bc834f5ad5..00000000000 --- a/src/frontend/mame/console.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Miodrag Milanovic -/*************************************************************************** - - console.c - - Console interface frontend for MAME. - -***************************************************************************/ - -#include "emu.h" -#include "luaengine.h" -#include "console.h" -#include "linenoise-ng/include/linenoise.h" -#include "mame.h" - -#include -#include - -static console_frontend *gConsole = nullptr; -//------------------------------------------------- -// console_frontend - constructor -//------------------------------------------------- - -console_frontend::console_frontend(emu_options &options, osd_interface &osd) - : //m_options(options), - //m_osd(osd), - m_run(true), - m_wait(false), - m_prompt("\x1b[1;36m[MAME]\x1b[0m> ") -{ - mame_machine_manager::instance()->lua()->sol()["quit"] = [this]() { cmd_quit(); }; - m_commands.push_back("quit()"); - mame_machine_manager::instance()->lua()->sol()["exit"] = [this]() { cmd_quit(); }; - m_commands.push_back("exit()"); - gConsole = this; -} - - -//------------------------------------------------- -// ~console_frontend - destructor -//------------------------------------------------- - -console_frontend::~console_frontend() -{ -} - -void console_frontend::completion(char const* prefix, linenoiseCompletions* lc) -{ - for (auto cmd : m_commands) - { - if (strncmp(prefix, cmd.c_str(), strlen(prefix)) == 0) - { - linenoiseAddCompletion(lc, cmd.c_str()); - } - } -} - -void console_frontend::cmd_quit() -{ - printf("Exiting application\n"); - m_run.store(false); - m_wait.store(false); -} - -void console_frontend::read_console(std::string &cmdLine) -{ - while (m_run.load()) - { - while (m_wait.load()) - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(100ms); - } - if (!m_run.load()) break; - char* result = linenoise(m_prompt.c_str()); - if (result == NULL) - { - continue; - } - else if (*result == '\0') { - free(result); - continue; - } - cmdLine = std::string(result); - linenoiseHistoryAdd(result); - //m_prompt = "\x1b[1;36m[MAME]\x1b[0m \x1b[1;32m[test]\x1b[0m> "; - - free(result); - m_wait.store(true); - } -} - -static void completionHook(char const* prefix, linenoiseCompletions* lc) -{ - gConsole->completion(prefix, lc); -} - -void console_frontend::start_console() -{ - linenoiseInstallWindowChangeHandler(); - std::string cmdLine = ""; - const char* file = "./history"; - - linenoiseHistoryLoad(file); - linenoiseSetCompletionCallback(completionHook); - - // Display app info - printf(" _/ _/ _/_/ _/ _/ _/_/_/_/\n"); - printf(" _/_/ _/_/ _/ _/ _/_/ _/_/ _/ \n"); - printf(" _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ \n"); - printf(" _/ _/ _/ _/ _/ _/ _/ \n"); - printf("_/ _/ _/ _/ _/ _/ _/_/_/_/ \n"); - printf("\n"); - printf("%s v%s\n%s\n\n", emulator_info::get_appname(), build_version, emulator_info::get_copyright_info()); - - - std::thread cinThread(&console_frontend::read_console, this, std::ref(cmdLine)); - - while (m_run.load()) - { - if (m_wait.load()) - { - mame_machine_manager::instance()->lua()->load_string(cmdLine.c_str()); - cmdLine.clear(); - m_wait.store(false); - } else { - using namespace std::chrono_literals; - std::this_thread::sleep_for(100ms); - } - } - - m_run.store(false); - cinThread.join(); - - linenoiseHistorySave(file); - linenoiseHistoryFree(); -} diff --git a/src/frontend/mame/console.h b/src/frontend/mame/console.h deleted file mode 100644 index 94405c7dc60..00000000000 --- a/src/frontend/mame/console.h +++ /dev/null @@ -1,41 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Miodrag Milanovic -/*************************************************************************** - - console.c - - Console interface frontend for MAME. - -***************************************************************************/ -#ifndef MAME_FRONTEND_CONSOLE_H -#define MAME_FRONTEND_CONSOLE_H - -#pragma once - -#include "emu.h" - -struct linenoiseCompletions; - -class console_frontend -{ -public: - // construction/destruction - console_frontend(emu_options &options, osd_interface &osd); - ~console_frontend(); - - void start_console(); - void completion(char const* prefix, linenoiseCompletions* lc); - -private: - void read_console(std::string &cmdLine); - - void cmd_quit(); - // internal state - std::atomic m_run; - std::atomic m_wait; - std::string m_prompt; - - std::vector m_commands; -}; - -#endif /* MAME_FRONTEND_CONSOLE_H */ diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index 616375fc2e9..e5fb9930db4 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -8,6 +8,7 @@ ***************************************************************************/ +#include #include #include "emu.h" #include "mame.h" @@ -668,6 +669,11 @@ void lua_engine::on_frame_done() execute_function("LUA_ON_FRAME_DONE"); } +void lua_engine::on_periodic() +{ + execute_function("LUA_ON_PERIODIC"); +} + void lua_engine::attach_notifiers() { machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true); @@ -702,6 +708,7 @@ void lua_engine::initialize() * emu.register_resume(callback) - callback at resume * emu.register_frame(callback) - callback at end of frame * emu.register_frame_done(callback) - callback after frame is drawn to screen (for overlays) + * emu.register_periodic(callback) - periodic callback while program is running * emu.register_menu(event_callback, populate_callback, name) - callbacks for plugin menu * emu.print_verbose(str) -- output to stderr at verbose level * emu.print_error(str) -- output to stderr at error level @@ -735,6 +742,7 @@ void lua_engine::initialize() emu["register_resume"] = [this](sol::function func){ register_function(func, "LUA_ON_RESUME"); }; emu["register_frame"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME"); }; emu["register_frame_done"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME_DONE"); }; + emu["register_periodic"] = [this](sol::function func){ register_function(func, "LUA_ON_PERIODIC"); }; emu["register_menu"] = [this](sol::function cb, sol::function pop, const std::string &name) { std::string cbfield = "menu_cb_" + name; std::string popfield = "menu_pop_" + name; @@ -767,6 +775,7 @@ void lua_engine::initialize() } return item; }; + emu["thread"] = []() { context *ctx = new context; ctx->busy = false; ctx->yield = false; return ctx; }; emu.new_usertype("file", sol::call_constructor, sol::constructors>(), "read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; }, @@ -777,6 +786,64 @@ void lua_engine::initialize() "filename", &emu_file::filename, "fullpath", &emu_file::fullpath); +/* + * thread.start(scr) - run scr (string not function) in a seperate thread in a new empty (other then modules) lua context + * thread.continue(val) - resume thread and pass val to it + * thread.result() - get thread result as string + * thread.busy - check if thread is running + * thread.yield - check if thread is yielded +*/ + + sol().new_usertype("thread", + sol::meta_function::garbage_collect, sol::destructor([](context *ctx) { delete ctx; }), + "start", [this](context &ctx, const char *scr) { + std::string script(scr); + if(ctx.busy) + return false; + std::thread th([&ctx, script]() { + sol::state thstate; + thstate.open_libraries(); + thstate["package"]["preload"]["zlib"] = &luaopen_zlib; + thstate["package"]["preload"]["lfs"] = &luaopen_lfs; + thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise; + sol::load_result res = thstate.load(script); + if(res.valid()) + { + sol::protected_function func = res.get(); + thstate["yield"] = [&ctx, &thstate]() { + std::mutex m; + std::unique_lock lock(m); + ctx.result = thstate["status"]; + ctx.yield = true; + ctx.sync.wait(lock); + ctx.yield = false; + thstate["status"] = ctx.result; + }; + auto ret = func(); + if(ret.valid()) + ctx.result = ret.get(); + } + ctx.busy = false; + }); + ctx.busy = true; + ctx.yield = false; + th.detach(); + return true; + }, + "continue", [this](context &ctx, const char *val) { + if(!ctx.yield) + return; + ctx.result = val; + ctx.sync.notify_all(); + }, + "result", sol::property([this](context &ctx) -> std::string { + if(ctx.busy && !ctx.yield) + return ""; + return ctx.result; + }), + "busy", sol::readonly(&context::busy), + "yield", sol::readonly(&context::yield)); + sol().new_usertype("item", sol::meta_function::garbage_collect, sol::destructor([](save_item *item) { delete item; }), "size", sol::readonly(&save_item::size), diff --git a/src/frontend/mame/luaengine.h b/src/frontend/mame/luaengine.h index 570008d808b..1da55b416f8 100644 --- a/src/frontend/mame/luaengine.h +++ b/src/frontend/mame/luaengine.h @@ -18,12 +18,11 @@ #define __LUA_ENGINE_H__ #include +#include #define SOL_SAFE_USERTYPE //#define SOL_CHECK_ARGUMENTS #include "sol2/sol.hpp" -class cheat_manager; - struct lua_State; class lua_engine @@ -46,6 +45,7 @@ public: std::vector &get_menu() { return m_menu; } void attach_notifiers(); void on_frame_done(); + void on_periodic(); template bool call_plugin(const std::string &name, const T in, U &out) @@ -153,6 +153,14 @@ private: void close(); void run(sol::load_result res); + + struct context + { + std::string result; + std::condition_variable sync; + bool busy; + bool yield; + }; }; #endif /* __LUA_ENGINE_H__ */ diff --git a/src/frontend/mame/mame.cpp b/src/frontend/mame/mame.cpp index fa5cc2999e6..5bb5486b7f7 100644 --- a/src/frontend/mame/mame.cpp +++ b/src/frontend/mame/mame.cpp @@ -326,6 +326,7 @@ void emulator_info::draw_user_interface(running_machine& machine) void emulator_info::periodic_check() { + return mame_machine_manager::instance()->lua()->on_periodic(); } bool emulator_info::frame_hook() From fad9a96cb0c49d693ab59504091cb9c522910f1a Mon Sep 17 00:00:00 2001 From: cracyc Date: Sat, 5 Nov 2016 18:32:03 -0500 Subject: [PATCH 2/5] plugins/console: better completions (nw) --- plugins/console/init.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/console/init.lua b/plugins/console/init.lua index fea0f930d42..b4f42acf48e 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -37,11 +37,12 @@ function console.startplugin() return type(t) == 'table' or (mt and mt.__pairs) end local comps = "," - local table, last = str:match("([(]?[%w.:()]-)[:.]?([%w]*)$") - if last == "" then - return comps - end + local table = str:match("([(]?[%w.:()]-)[:.]?[%w_]*$") + local rest, last = str:match("(.-[:.]?)([%w_]*)$") local err + if table == "" then + table = "_G" + end err, tablef = pcall(load("return " .. table)) if (not err) or (not tablef) then return comps @@ -49,7 +50,15 @@ function console.startplugin() if is_pair_iterable(tablef) then for k, v in pairs(tablef) do if k:match("^" .. last) then - comps = comps .. "," .. table + comps = comps .. "," .. rest .. k + end + end + end + local tablef = getmetatable(tablef) + if is_pair_iterable(tablef) then + for k, v in pairs(tablef) do + if k:match("^" .. last) then + comps = comps .. "," .. rest .. k end end end From 68ff36882cc9555dde14bba5f50af8d2d552fbd5 Mon Sep 17 00:00:00 2001 From: cracyc Date: Sat, 5 Nov 2016 18:38:22 -0500 Subject: [PATCH 3/5] plugins/console: (nw) --- plugins/console/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/console/init.lua b/plugins/console/init.lua index b4f42acf48e..3e5487c7b60 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -31,7 +31,7 @@ function console.startplugin() scr = scr .. "end)\n" scr = scr .. "return ln.linenoise('\x1b[1;36m[MAME]\x1b[0m> ')" - function get_completions(str) + local function get_completions(str) local function is_pair_iterable(t) local mt = getmetatable(t) return type(t) == 'table' or (mt and mt.__pairs) From a6aaa59d19c5183ba16da11ab963646ffbb66aa3 Mon Sep 17 00:00:00 2001 From: cracyc Date: Sat, 5 Nov 2016 19:33:17 -0500 Subject: [PATCH 4/5] lua-linenoise: add preload (nw) --- 3rdparty/lua-linenoise/linenoise.c | 9 +++++++++ plugins/console/init.lua | 14 +++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/3rdparty/lua-linenoise/linenoise.c b/3rdparty/lua-linenoise/linenoise.c index 670a71c2b22..fc8ae19e079 100644 --- a/3rdparty/lua-linenoise/linenoise.c +++ b/3rdparty/lua-linenoise/linenoise.c @@ -101,6 +101,14 @@ static int l_historyadd(lua_State *L) return handle_ln_ok(L); } +static int l_preloadbuffer(lua_State *L) +{ + const char *line = luaL_checkstring(L, 1); + linenoisePreloadBuffer(line); + + return handle_ln_ok(L); +} + static int l_historysetmaxlen(lua_State *L) { int len = luaL_checkinteger(L, 1); @@ -168,6 +176,7 @@ luaL_Reg linenoise_funcs[] = { { "clearscreen", l_clearscreen }, { "setcompletion", l_setcompletion}, { "addcompletion", l_addcompletion }, + { "preload", l_preloadbuffer }, /* Aliases for more consistent function names */ { "addhistory", l_historyadd }, diff --git a/plugins/console/init.lua b/plugins/console/init.lua index 3e5487c7b60..151af204400 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -13,6 +13,7 @@ function console.startplugin() local conth = emu.thread() local started = false local ln = require("linenoise") + local preload = false print(" _/ _/ _/_/ _/ _/ _/_/_/_/"); print(" _/_/ _/_/ _/ _/ _/_/ _/_/ _/ "); print(" _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ "); @@ -76,14 +77,25 @@ function console.startplugin() ln.historyadd(cmd) local func, err = load(cmd) if not func then - print("error: ", err) + if err:match("") then + print("incomplete command") + ln.preload(cmd) + preload = true + else + print("error: ", err) + preload = false + end else + preload = false local status status, err = pcall(func) if not status then print("error: ", err) end end + if not preload then + ln.historyadd(cmd) + end end conth:start(scr) started = true From cbf588359ee1fdb60cea32f653112144f35ca62d Mon Sep 17 00:00:00 2001 From: cracyc Date: Sat, 5 Nov 2016 20:00:10 -0500 Subject: [PATCH 5/5] plugins/console: (nw) --- plugins/console/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/console/init.lua b/plugins/console/init.lua index 151af204400..3034b7c2cab 100644 --- a/plugins/console/init.lua +++ b/plugins/console/init.lua @@ -74,7 +74,6 @@ function console.startplugin() return elseif started then local cmd = conth.result - ln.historyadd(cmd) local func, err = load(cmd) if not func then if err:match("") then