summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/luaengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/luaengine.cpp')
-rw-r--r--src/frontend/mame/luaengine.cpp1194
1 files changed, 744 insertions, 450 deletions
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 2cc98b971fe..6aa2d3840ec 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -21,14 +21,19 @@
#include "debugger.h"
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "inputdev.h"
#include "natkeyboard.h"
+#include "screen.h"
#include "softlist.h"
#include "uiinput.h"
#include "corestr.h"
+#include <algorithm>
+#include <condition_variable>
#include <cstring>
+#include <mutex>
#include <thread>
@@ -36,15 +41,11 @@
// LUA ENGINE
//**************************************************************************
-extern "C" {
-
-int luaopen_zlib(lua_State *L);
-int luaopen_lfs(lua_State *L);
+int luaopen_zlib(lua_State *const L);
+extern "C" int luaopen_lfs(lua_State *L);
int luaopen_linenoise(lua_State *L);
int luaopen_lsqlite3(lua_State *L);
-} // extern "C"
-
template <typename T>
struct lua_engine::devenum
@@ -59,6 +60,128 @@ struct lua_engine::devenum
namespace {
+struct save_item
+{
+ void *base;
+ unsigned int size;
+ unsigned int count;
+ unsigned int valcount;
+ unsigned int blockcount;
+ unsigned int stride;
+};
+
+struct thread_context
+{
+private:
+ sol::state m_state;
+ std::string m_result;
+ std::mutex m_guard;
+ std::condition_variable m_sync;
+ bool m_busy = false;
+
+public:
+ bool m_yield = false;
+
+ thread_context()
+ {
+ m_state.open_libraries();
+ m_state["package"]["preload"]["zlib"] = &luaopen_zlib;
+ m_state["package"]["preload"]["lfs"] = &luaopen_lfs;
+ m_state["package"]["preload"]["linenoise"] = &luaopen_linenoise;
+ m_state.set_function("yield",
+ [this] ()
+ {
+ std::unique_lock<std::mutex> yield_lock(m_guard);
+ m_result = m_state["status"];
+ m_yield = true;
+ m_sync.wait(yield_lock);
+ m_yield = false;
+ });
+ }
+
+ bool start(sol::this_state s, char const *scr)
+ {
+ std::unique_lock<std::mutex> caller_lock(m_guard);
+ if (m_busy)
+ return false;
+
+ sol::load_result res = m_state.load(scr);
+ if (!res.valid())
+ {
+ sol::error err = res;
+ luaL_error(s, err.what());
+ return false; // unreachable - luaL_error throws
+ }
+
+ std::thread th(
+ [this, func = res.get<sol::protected_function>()] ()
+ {
+ auto ret = func();
+ std::unique_lock<std::mutex> result_lock(m_guard);
+ if (ret.valid())
+ {
+ auto result = ret.get<std::optional<char const *> >();
+ if (!result)
+ osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
+ else if (!*result)
+ m_result.clear();
+ else
+ m_result = *result;
+ }
+ else
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
+ }
+ m_busy = false;
+ });
+ m_busy = true;
+ m_yield = false;
+ th.detach(); // FIXME: this is unsafe as the thread function modifies members of the object
+ return true;
+ }
+
+ void resume(char const *val)
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_yield)
+ {
+ if (val)
+ m_state["status"] = val;
+ else
+ m_state["status"] = sol::lua_nil;
+ m_sync.notify_all();
+ }
+ }
+
+ char const *result()
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_busy && !m_yield)
+ return "";
+ else
+ return m_result.c_str();
+ }
+
+ bool busy() const
+ {
+ return m_busy;
+ }
+};
+
+
+struct device_state_entries
+{
+ device_state_entries(device_state_interface const &s) : state(s) { }
+ device_state_interface::entrylist_type const &items() { return state.state_entries(); }
+
+ static device_state_entry const &unwrap(device_state_interface::entrylist_type::const_iterator const &it) { return **it; }
+ static int push_key(lua_State *L, device_state_interface::entrylist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->symbol()); }
+
+ device_state_interface const &state;
+};
+
+
struct image_interface_formats
{
image_interface_formats(device_image_interface &i) : image(i) { }
@@ -82,26 +205,39 @@ struct plugin_options_plugins
plugin_options &options;
};
-} // anonymous namespace
-
-namespace sol
+template <typename T>
+void resume_tasks(lua_State *L, T &&tasks, bool status)
{
+ for (int ref : tasks)
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_State *const thread = lua_tothread(L, -1);
+ lua_pop(L, 1);
+ lua_pushboolean(thread, status ? 1 : 0);
+ int nresults = 0;
+ int const stat = lua_resume(thread, nullptr, 1, &nresults);
+ if ((stat != LUA_OK) && (stat != LUA_YIELD))
+ {
+ osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(thread, -1));
+ lua_pop(thread, 1);
+ }
+ else
+ {
+ lua_pop(thread, nresults);
+ }
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ }
+}
-template <> struct is_container<image_interface_formats> : std::true_type { };
-template <> struct is_container<plugin_options_plugins> : std::true_type { };
+} // anonymous namespace
-sol::buffer *sol_lua_get(sol::types<buffer *>, lua_State *L, int index, sol::stack::record &tracking)
-{
- return new sol::buffer(stack::get<int>(L, index), L);
-}
+namespace sol {
-int sol_lua_push(sol::types<buffer *>, lua_State *L, buffer *value)
-{
- delete value;
- return 1;
-}
+template <> struct is_container<device_state_entries> : std::true_type { };
+template <> struct is_container<image_interface_formats> : std::true_type { };
+template <> struct is_container<plugin_options_plugins> : std::true_type { };
template <typename T>
@@ -208,6 +344,34 @@ public:
template <>
+struct usertype_container<device_state_entries> : lua_engine::immutable_sequence_helper<device_state_entries, device_state_interface::entrylist_type const, device_state_interface::entrylist_type::const_iterator>
+{
+private:
+ using entrylist_type = device_state_interface::entrylist_type;
+
+public:
+ static int get(lua_State *L)
+ {
+ device_state_entries &self(get_self(L));
+ char const *const symbol(stack::unqualified_get<char const *>(L));
+ auto const found(std::find_if(
+ self.state.state_entries().begin(),
+ self.state.state_entries().end(),
+ [&symbol] (std::unique_ptr<device_state_entry> const &v) { return !std::strcmp(v->symbol(), symbol); }));
+ if (self.state.state_entries().end() != found)
+ return stack::push_reference(L, std::cref(**found));
+ else
+ return stack::push(L, lua_nil);
+ }
+
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+};
+
+
+template <>
struct usertype_container<image_interface_formats> : lua_engine::immutable_sequence_helper<image_interface_formats, device_image_interface::formatlist_type const, device_image_interface::formatlist_type::const_iterator>
{
private:
@@ -223,7 +387,7 @@ public:
self.image.formatlist().end(),
[&name] (std::unique_ptr<image_device_format> const &v) { return v->name() == name; }));
if (self.image.formatlist().end() != found)
- return stack::push_reference(L, **found);
+ return stack::push_reference(L, std::cref(**found));
else
return stack::push(L, lua_nil);
}
@@ -251,7 +415,7 @@ public:
self.options.plugins().end(),
[&name] (plugin_options::plugin const &p) { return p.m_name == name; }));
if (self.options.plugins().end() != found)
- return stack::push_reference(L, *found);
+ return stack::push_reference(L, std::cref(*found));
else
return stack::push(L, lua_nil);
}
@@ -287,26 +451,6 @@ int sol_lua_push(sol::types<screen_type_enum>, lua_State *L, screen_type_enum &&
return sol::stack::push(L, "unknown");
}
-int sol_lua_push(sol::types<image_init_result>, lua_State *L, image_init_result &&value)
-{
- switch (value)
- {
- case image_init_result::PASS: return sol::stack::push(L, "pass");
- case image_init_result::FAIL: return sol::stack::push(L, "fail");
- }
- return sol::stack::push(L, "invalid");
-}
-
-int sol_lua_push(sol::types<image_verify_result>, lua_State *L, image_verify_result &&value)
-{
- switch (value)
- {
- case image_verify_result::PASS: return sol::stack::push(L, "pass");
- case image_verify_result::FAIL: return sol::stack::push(L, "fail");
- }
- return sol::stack::push(L, "invalid");
-}
-
//-------------------------------------------------
// process_snapshot_filename - processes a snapshot
@@ -330,13 +474,16 @@ static std::string process_snapshot_filename(running_machine &machine, const cha
//-------------------------------------------------
lua_engine::lua_engine()
+ : m_lua_state(nullptr)
+ , m_machine(nullptr)
+ , m_timer(nullptr)
{
- m_machine = nullptr;
- m_lua_state = luaL_newstate(); /* create state */
+ m_lua_state = luaL_newstate(); // create state
m_sol_state = std::make_unique<sol::state_view>(m_lua_state); // create sol view
+ m_notifiers.emplace();
luaL_checkversion(m_lua_state);
- lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */
+ lua_gc(m_lua_state, LUA_GCSTOP, 0); // stop collector during initialization
sol().open_libraries();
// Get package.preload so we can store builtins in it.
@@ -361,16 +508,18 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
{
std::string field = "cb_" + name;
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>(), in);
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
}
else
+ {
return res.get<sol::object>();
+ }
}
return sol::lua_nil;
}
@@ -389,7 +538,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
}
else
{
- std::tuple<sol::table, std::optional<long>, std::string> table = res;
+ std::tuple<sol::table, std::optional<long>, std::optional<std::string> > table = res;
for (auto &entry : std::get<0>(table))
{
if (entry.second.is<sol::table>())
@@ -398,7 +547,10 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
}
}
- flags = std::get<2>(table);
+ if (std::get<2>(table))
+ flags = *std::get<2>(table);
+ else
+ flags.clear();
return std::get<1>(table);
}
}
@@ -409,7 +561,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
{
std::string field = "menu_cb_" + menu;
- std::pair<bool, std::optional<long> > ret(false, std::nullopt);
+ std::pair<std::optional<bool>, std::optional<long> > ret(false, std::nullopt);
sol::object obj = sol().registry()[field];
if (obj.is<sol::protected_function>())
{
@@ -424,7 +576,7 @@ std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::strin
ret = res;
}
}
- return ret;
+ return std::make_pair(std::get<0>(ret) && *std::get<0>(ret), std::get<1>(ret));
}
void lua_engine::set_machine(running_machine *machine)
@@ -432,9 +584,10 @@ void lua_engine::set_machine(running_machine *machine)
m_machine = machine;
}
-int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
+template <typename T>
+size_t lua_engine::enumerate_functions(const char *id, T &&callback)
{
- int count = 0;
+ size_t count = 0;
sol::object functable = sol().registry()[id];
if (functable.is<sol::table>())
{
@@ -448,23 +601,24 @@ int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol
break;
}
}
- return true;
}
return count;
}
bool lua_engine::execute_function(const char *id)
{
- int count = enumerate_functions(id, [this](const sol::protected_function &func)
- {
- auto ret = invoke(func);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
- }
- return true;
- });
+ size_t count = enumerate_functions(
+ id,
+ [this] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func);
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
+ }
+ return true;
+ });
return count > 0;
}
@@ -482,13 +636,25 @@ void lua_engine::on_machine_prestart()
execute_function("LUA_ON_PRESTART");
}
-void lua_engine::on_machine_start()
+void lua_engine::on_machine_reset()
{
+ m_notifiers->on_reset();
execute_function("LUA_ON_START");
}
void lua_engine::on_machine_stop()
{
+ // clear waiting tasks
+ m_timer = nullptr;
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_stop();
execute_function("LUA_ON_STOP");
}
@@ -499,22 +665,45 @@ void lua_engine::on_machine_before_load_settings()
void lua_engine::on_machine_pause()
{
+ m_notifiers->on_pause();
execute_function("LUA_ON_PAUSE");
}
void lua_engine::on_machine_resume()
{
+ m_notifiers->on_resume();
execute_function("LUA_ON_RESUME");
}
void lua_engine::on_machine_frame()
{
+ std::vector<int> tasks = std::move(m_frame_tasks);
+ m_frame_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
+ m_notifiers->on_frame();
+
execute_function("LUA_ON_FRAME");
}
-void lua_engine::on_frame_done()
+void lua_engine::on_machine_presave()
+{
+ m_notifiers->on_presave();
+}
+
+void lua_engine::on_machine_postload()
{
- execute_function("LUA_ON_FRAME_DONE");
+ // clear waiting tasks
+ m_timer->reset();
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_postload();
}
void lua_engine::on_sound_update()
@@ -530,32 +719,38 @@ void lua_engine::on_periodic()
bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
{
bool handled = false;
- enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func)
- {
- auto ret = invoke(func, instance_name);
+ enumerate_functions(
+ "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE",
+ [this, &instance_name, &handled] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func, instance_name);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
- }
- else if (ret.get<bool>())
- {
- handled = true;
- }
- return !handled;
- });
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
+ }
+ else if (ret.get<bool>())
+ {
+ handled = true;
+ }
+ return !handled;
+ });
return handled;
}
void lua_engine::attach_notifiers()
{
machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true);
- machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_start, this));
+ machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_reset, this));
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&lua_engine::on_machine_stop, this));
machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&lua_engine::on_machine_pause, this));
machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&lua_engine::on_machine_resume, this));
machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&lua_engine::on_machine_frame, this));
+ machine().save().register_presave(save_prepost_delegate(FUNC(lua_engine::on_machine_presave), this));
+ machine().save().register_postload(save_prepost_delegate(FUNC(lua_engine::on_machine_postload), this));
+
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lua_engine::resume), this));
}
//-------------------------------------------------
@@ -596,9 +791,6 @@ void lua_engine::initialize()
* emu.unpause() - unpause emulation
* emu.step() - advance one frame
* emu.keypost(keys) - post keys to natural keyboard
- * emu.wait(len) - wait for len within coroutine
- * emu.lang_translate(str) - get translation for str if available
- * emu.subst_env(str) - substitute environment variables with values for str (semantics are OS-specific)
*
* emu.register_prestart(callback) - register callback before reset
* emu.register_start(callback) - register callback after reset
@@ -615,11 +807,6 @@ void lua_engine::initialize()
* emu.register_before_load_settings(callback) - register callback to be run before settings are loaded
* emu.show_menu(menu_name) - show menu by name and pause the machine
*
- * emu.print_verbose(str) - output to stderr at verbose level
- * emu.print_error(str) - output to stderr at error level
- * emu.print_info(str) - output to stderr at info level
- * emu.print_debug(str) - output to stderr at debug level
- *
* emu.device_enumerator(dev) - get device enumerator starting at arbitrary point in tree
* emu.screen_enumerator(dev) - get screen device enumerator starting at arbitrary point in tree
* emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree
@@ -627,6 +814,79 @@ void lua_engine::initialize()
*/
sol::table emu = sol().create_named_table("emu");
+ emu["wait"] = sol::yielding(
+ [this] (sol::this_state s, sol::object duration, sol::variadic_args args)
+ {
+ attotime delay;
+ if (!duration)
+ {
+ luaL_error(s, "waiting duration expected");
+ }
+ else if (duration.is<attotime>())
+ {
+ delay = duration.as<attotime>();
+ }
+ else
+ {
+ auto seconds = duration.as<std::optional<double> >();
+ if (!seconds)
+ luaL_error(s, "waiting duration must be attotime or number");
+ delay = attotime::from_double(*seconds);
+ }
+ attotime const expiry = machine().time() + delay;
+
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ int const ref = luaL_ref(s, LUA_REGISTRYINDEX);
+
+ auto const pos = std::upper_bound(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ expiry,
+ [] (auto const &a, auto const &b) { return a < b.first; });
+ if (m_waiting_tasks.begin() == pos)
+ m_timer->reset(delay);
+ m_waiting_tasks.emplace(pos, expiry, ref);
+
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_update"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_update_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_frame"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_frame_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu.set_function("add_machine_reset_notifier", make_notifier_adder(m_notifiers->on_reset, "machine reset"));
+ emu.set_function("add_machine_stop_notifier", make_notifier_adder(m_notifiers->on_stop, "machine stop"));
+ emu.set_function("add_machine_pause_notifier", make_notifier_adder(m_notifiers->on_pause, "machine pause"));
+ emu.set_function("add_machine_resume_notifier", make_notifier_adder(m_notifiers->on_resume, "machine resume"));
+ emu.set_function("add_machine_frame_notifier", make_notifier_adder(m_notifiers->on_frame, "machine frame"));
+ emu.set_function("add_machine_pre_save_notifier", make_notifier_adder(m_notifiers->on_presave, "machine pre-save"));
+ emu.set_function("add_machine_post_load_notifier", make_notifier_adder(m_notifiers->on_postload, "machine post-load"));
+ emu.set_function("print_error", [] (const char *str) { osd_printf_error("%s\n", str); });
+ emu.set_function("print_warning", [] (const char *str) { osd_printf_warning("%s\n", str); });
+ emu.set_function("print_info", [] (const char *str) { osd_printf_info("%s\n", str); });
+ emu.set_function("print_verbose", [] (const char *str) { osd_printf_verbose("%s\n", str); });
+ emu.set_function("print_debug", [] (const char *str) { osd_printf_debug("%s\n", str); });
+ emu["lang_translate"] = sol::overload(
+ static_cast<char const *(*)(char const *)>(&lang_translate),
+ static_cast<char const *(*)(char const *, char const *)>(&lang_translate));
+ emu.set_function("subst_env", &osd_subst_env);
+
+ // TODO: stuff below here needs to be rationalised
emu["app_name"] = &emulator_info::get_appname_lower;
emu["app_version"] = &emulator_info::get_bare_build_version;
emu["gamename"] = [this] () { return machine().system().type.fullname(); };
@@ -654,11 +914,11 @@ void lua_engine::initialize()
machine().resume();
};
emu["register_prestart"] = [this] (sol::function func) { register_function(func, "LUA_ON_PRESTART"); };
- emu["register_start"] = [this] (sol::function func) { register_function(func, "LUA_ON_START"); };
- emu["register_stop"] = [this] (sol::function func) { register_function(func, "LUA_ON_STOP"); };
- emu["register_pause"] = [this] (sol::function func) { register_function(func, "LUA_ON_PAUSE"); };
- 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_start"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_start is deprecated - please use emu.add_machine_reset_notifier\n"); register_function(func, "LUA_ON_START"); };
+ emu["register_stop"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_stop is deprecated - please use emu.add_machine_stop_notifier\n"); register_function(func, "LUA_ON_STOP"); };
+ emu["register_pause"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_pause is deprecated - please use emu.add_machine_pause_notifier\n"); register_function(func, "LUA_ON_PAUSE"); };
+ emu["register_resume"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_resume is deprecated - please use emu.add_machine_resume_notifier\n"); register_function(func, "LUA_ON_RESUME"); };
+ emu["register_frame"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_frame is deprecated - please use emu.add_machine_frame_notifier\n"); register_function(func, "LUA_ON_FRAME"); };
emu["register_frame_done"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME_DONE"); };
emu["register_sound_update"] = [this] (sol::function func) { register_function(func, "LUA_ON_SOUND_UPDATE"); };
emu["register_periodic"] = [this] (sol::function func) { register_function(func, "LUA_ON_PERIODIC"); };
@@ -678,7 +938,7 @@ void lua_engine::initialize()
{
mame_ui_manager &mui = mame_machine_manager::instance()->ui();
render_container &container = machine().render().ui_container();
- ui::menu_plugin::show_menu(mui, container, (char *)name);
+ ui::menu_plugin::show_menu(mui, container, name);
};
emu["register_callback"] =
[this] (sol::function cb, const std::string &name)
@@ -686,10 +946,6 @@ void lua_engine::initialize()
std::string field = "cb_" + name;
sol().registry()[field] = cb;
};
- emu["print_verbose"] = [] (const char *str) { osd_printf_verbose("%s\n", str); };
- emu["print_error"] = [] (const char *str) { osd_printf_error("%s\n", str); };
- emu["print_info"] = [] (const char *str) { osd_printf_info("%s\n", str); };
- emu["print_debug"] = [] (const char *str) { osd_printf_debug("%s\n", str); };
emu["osd_ticks"] = &osd_ticks;
emu["osd_ticks_per_second"] = &osd_ticks_per_second;
emu["driver_find"] =
@@ -700,32 +956,13 @@ void lua_engine::initialize()
return sol::lua_nil;
return sol::make_object(s, driver_list::driver(i));
};
- emu["wait"] = lua_CFunction(
- [] (lua_State *L)
- {
- lua_engine *engine = mame_machine_manager::instance()->lua();
- luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
- int ret = lua_pushthread(L);
- if (ret == 1)
- return luaL_error(L, "cannot wait from outside coroutine");
- int ref = luaL_ref(L, LUA_REGISTRYINDEX);
- engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr);
- return lua_yield(L, 0);
- });
- emu["lang_translate"] = sol::overload(
- static_cast<char const *(*)(char const *)>(&lang_translate),
- static_cast<char const *(*)(char const *, char const *)>(&lang_translate));
emu["pid"] = &osd_getpid;
- emu["subst_env"] =
- [] (const std::string &str)
- {
- std::string result;
- osd_subst_env(result, str);
- return result;
- };
emu["device_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<device_enumerator>(dev, maxdepth); });
+ emu["palette_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<palette_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<palette_interface_enumerator>(dev, maxdepth); });
emu["screen_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<screen_device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<screen_device_enumerator>(dev, maxdepth); });
@@ -740,6 +977,10 @@ void lua_engine::initialize()
[] (device_t &dev, int maxdepth) { return devenum<slot_interface_enumerator>(dev, maxdepth); });
+ auto notifier_subscription_type = sol().registry().new_usertype<util::notifier_subscription>("notifier_subscription", sol::no_constructor);
+ notifier_subscription_type["unsubscribe"] = &util::notifier_subscription::reset;
+ notifier_subscription_type["is_active"] = sol::property(&util::notifier_subscription::operator bool);
+
auto attotime_type = emu.new_usertype<attotime>(
"attotime",
sol::call_constructor, sol::constructors<attotime(), attotime(seconds_t, attoseconds_t), attotime(attotime const &)>());
@@ -827,7 +1068,15 @@ void lua_engine::initialize()
}
new (&file) emu_file(path, flags);
}));
- file_type.set("read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; });
+ file_type.set("read",
+ [] (emu_file &file, sol::this_state s, size_t len)
+ {
+ buffer_helper buf(s);
+ auto space = buf.prepare(len);
+ space.add(file.read(space.get(), len));
+ buf.push();
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ });
file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
file_type.set("puts", &emu_file::puts);
file_type.set("open", static_cast<std::error_condition (emu_file::*)(std::string_view)>(&emu_file::open));
@@ -871,75 +1120,17 @@ void lua_engine::initialize()
* thread runs until yield() and/or terminates on return.
* thread:continue(val) - resume thread that has yielded and pass val to it
*
- * thread.result - get result of a terminated thread as string
- * thread.busy - check if thread is running
- * thread.yield - check if thread is yielded
+ * thread.result - get result of a terminated or yielding thread as string
+ * thread.busy - check if thread is running or yielding
+ * thread.yield - check if thread is yielding
*/
- auto thread_type = emu.new_usertype<context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
- thread_type.set("start", [](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<sol::protected_function>();
- thstate["yield"] = [&ctx, &thstate]() {
- std::mutex m;
- std::unique_lock<std::mutex> 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())
- {
- const char *tmp = ret.get<const char *>();
- if (tmp != nullptr)
- ctx.result = tmp;
- else
- osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
- }
- else
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
- }
- }
- else
- {
- sol::error err = res;
- osd_printf_error("[LUA ERROR] when loading script for thread: %s\n", err.what());
- }
- ctx.busy = false;
- });
- ctx.busy = true;
- ctx.yield = false;
- th.detach();
- return true;
- });
- thread_type.set("continue", [](context &ctx, const char *val) {
- if (!ctx.yield)
- return;
- ctx.result = val;
- ctx.sync.notify_all();
- });
- thread_type.set("result", sol::property([](context &ctx) -> std::string {
- if (ctx.busy && !ctx.yield)
- return "";
- return ctx.result;
- }));
- thread_type.set("busy", sol::readonly(&context::busy));
- thread_type.set("yield", sol::readonly(&context::yield));
+ auto thread_type = emu.new_usertype<thread_context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
+ thread_type.set_function("start", &thread_context::start);
+ thread_type.set_function("continue", &thread_context::resume);
+ thread_type["result"] = sol::property(&thread_context::result);
+ thread_type["busy"] = sol::property(&thread_context::busy);
+ thread_type["yield"] = sol::readonly(&thread_context::m_yield);
/* save_item library
@@ -971,12 +1162,14 @@ void lua_engine::initialize()
}));
item_type.set("size", sol::readonly(&save_item::size));
item_type.set("count", sol::readonly(&save_item::count));
- item_type.set("read", [this](save_item &item, int offset) -> sol::object {
- if(!item.base || (offset >= item.count))
+ item_type.set("read",
+ [this] (save_item &item, int offset) -> sol::object
+ {
+ if (!item.base || (offset >= item.count))
return sol::lua_nil;
const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
uint64_t ret = 0;
- switch(item.size)
+ switch (item.size)
{
case 1:
default:
@@ -994,29 +1187,38 @@ void lua_engine::initialize()
}
return sol::make_object(sol(), ret);
});
- item_type.set("read_block", [](save_item &item, int offset, sol::buffer *buff) {
- if(!item.base || ((offset + buff->get_len()) > (item.size * item.count)))
+ item_type.set("read_block",
+ [] (save_item &item, sol::this_state s, uint32_t offset, size_t len) -> sol::object
+ {
+ if (!item.base)
{
- buff->set_len(0);
+ luaL_error(s, "Invalid save item");
+ return sol::lua_nil;
}
- else
+
+ if ((offset + len) > (item.size * item.count))
{
- const uint32_t blocksize = item.size * item.valcount;
- uint32_t remaining = buff->get_len();
- uint8_t *dest = reinterpret_cast<uint8_t *>(buff->get_ptr());
- while(remaining)
- {
- const uint32_t blockno = offset / blocksize;
- const uint32_t available = blocksize - (offset % blocksize);
- const uint32_t chunk = (available < remaining) ? available : remaining;
- const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
- std::memcpy(dest, source, chunk);
- offset += chunk;
- remaining -= chunk;
- dest += chunk;
- }
+ luaL_error(s, "Range extends beyond end of save item");
+ return sol::lua_nil;
}
- return buff;
+
+ luaL_Buffer buff;
+ uint8_t *dest = reinterpret_cast<uint8_t *>(luaL_buffinitsize(s, &buff, len));
+ const uint32_t blocksize = item.size * item.valcount;
+ size_t remaining = len;
+ while (remaining)
+ {
+ const uint32_t blockno = offset / blocksize;
+ const uint32_t available = blocksize - (offset % blocksize);
+ const uint32_t chunk = (available < remaining) ? available : remaining;
+ const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
+ std::memcpy(dest, source, chunk);
+ offset += chunk;
+ remaining -= chunk;
+ dest += chunk;
+ }
+ luaL_pushresultsize(&buff, len);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
});
item_type.set("write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset >= item.count))
@@ -1134,25 +1336,25 @@ void lua_engine::initialize()
auto core_options_entry_type = sol().registry().new_usertype<core_options::entry>("core_options_entry", "new", sol::no_constructor);
core_options_entry_type.set("value", sol::overload(
[this](core_options::entry &e, bool val) {
- if(e.type() != OPTION_BOOLEAN)
+ if(e.type() != core_options::option_type::BOOLEAN)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val ? "1" : "0", OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, float val) {
- if(e.type() != OPTION_FLOAT)
+ if(e.type() != core_options::option_type::FLOAT)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%f", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, int val) {
- if(e.type() != OPTION_INTEGER)
+ if(e.type() != core_options::option_type::INTEGER)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, const char *val) {
- if(e.type() != OPTION_STRING)
+ if(e.type() != core_options::option_type::STRING && e.type() != core_options::option_type::PATH && e.type() != core_options::option_type::MULTIPATH)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val, OPTION_PRIORITY_CMDLINE);
@@ -1180,54 +1382,54 @@ void lua_engine::initialize()
auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor);
- machine_type["exit"] = &running_machine::schedule_exit;
- machine_type["hard_reset"] = &running_machine::schedule_hard_reset;
- machine_type["soft_reset"] = &running_machine::schedule_soft_reset;
- machine_type["save"] = &running_machine::schedule_save; // TODO: some kind of completion notification?
- machine_type["load"] = &running_machine::schedule_load; // TODO: some kind of completion notification?
- machine_type["buffer_save"] =
- [] (running_machine &m, sol::this_state s)
- {
- // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- lua_State *L = s;
- luaL_Buffer buff;
- int size = ram_state::get_size(m.save());
- u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
- save_error error = m.save().write_buffer(ptr, size);
- if (error == STATERR_NONE)
+ machine_type.set_function("exit", &running_machine::schedule_exit);
+ machine_type.set_function("hard_reset", &running_machine::schedule_hard_reset);
+ machine_type.set_function("soft_reset", &running_machine::schedule_soft_reset);
+ machine_type.set_function("save", &running_machine::schedule_save); // TODO: some kind of completion notification?
+ machine_type.set_function("load", &running_machine::schedule_load); // TODO: some kind of completion notification?
+ machine_type.set_function("buffer_save",
+ [] (running_machine &m, sol::this_state s)
{
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- }
- luaL_error(L, "State save error.");
- return sol::make_reference(L, nullptr);
- };
- machine_type["buffer_load"] =
- [] (running_machine &m, sol::this_state s, std::string str)
- {
- // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
- if (error == STATERR_NONE)
+ // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ lua_State *L = s;
+ luaL_Buffer buff;
+ int size = ram_state::get_size(m.save());
+ u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
+ save_error error = m.save().write_buffer(ptr, size);
+ if (error == STATERR_NONE)
+ {
+ luaL_pushresultsize(&buff, size);
+ return sol::make_reference(L, sol::stack_reference(L, -1));
+ }
+ luaL_error(L, "State save error.");
+ return sol::make_reference(L, nullptr);
+ });
+ machine_type.set_function("buffer_load",
+ [] (running_machine &m, sol::this_state s, std::string str)
{
- return true;
- }
- else
+ // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
+ if (error == STATERR_NONE)
+ {
+ return true;
+ }
+ else
+ {
+ luaL_error(s,"State load error.");
+ return false;
+ }
+ });
+ machine_type.set_function("popmessage",
+ [] (running_machine &m, std::optional<const char *> str)
{
- luaL_error(s,"State load error.");
- return false;
- }
- };
- machine_type["popmessage"] =
- [] (running_machine &m, const char *str)
- {
- if (str)
- m.popmessage("%s", str);
- else
- m.popmessage();
- };
- machine_type["logerror"] = [] (running_machine &m, std::string const *str) { m.logerror("[luaengine] %s\n", str); };
+ if (str)
+ m.popmessage("%s", *str);
+ else
+ m.popmessage();
+ });
+ machine_type.set_function("logerror", [] (running_machine &m, char const *str) { m.logerror("[luaengine] %s\n", str); });
machine_type["time"] = sol::property(&running_machine::time);
machine_type["system"] = sol::property(&running_machine::system);
machine_type["parameters"] = sol::property(&running_machine::parameters);
@@ -1254,6 +1456,7 @@ void lua_engine::initialize()
machine_type["exit_pending"] = sol::property(&running_machine::exit_pending);
machine_type["hard_reset_pending"] = sol::property(&running_machine::hard_reset_pending);
machine_type["devices"] = sol::property([] (running_machine &m) { return devenum<device_enumerator>(m.root_device()); });
+ machine_type["palettes"] = sol::property([] (running_machine &m) { return devenum<palette_interface_enumerator>(m.root_device()); });
machine_type["screens"] = sol::property([] (running_machine &m) { return devenum<screen_device_enumerator>(m.root_device()); });
machine_type["cassettes"] = sol::property([] (running_machine &m) { return devenum<cassette_device_enumerator>(m.root_device()); });
machine_type["images"] = sol::property([] (running_machine &m) { return devenum<image_interface_enumerator>(m.root_device()); });
@@ -1267,7 +1470,7 @@ void lua_engine::initialize()
game_driver_type["manufacturer"] = sol::readonly(&game_driver::manufacturer);
game_driver_type["parent"] = sol::readonly(&game_driver::parent);
game_driver_type["compatible_with"] = sol::property([] (game_driver const &driver) { return strcmp(driver.compatible_with, "0") ? driver.compatible_with : nullptr; });
- game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; });
+ game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return driver.type.source(); });
game_driver_type["orientation"] = sol::property(
[] (game_driver const &driver)
{
@@ -1294,35 +1497,11 @@ void lua_engine::initialize()
}
return rot;
});
- game_driver_type["type"] = sol::property(
- [] (game_driver const &driver)
- {
- // FIXME: this shouldn't be called type - there's potendial for confusion with the device type
- // also, this should eventually go away in favour of richer flags
- std::string type;
- switch (driver.flags & machine_flags::MASK_TYPE)
- {
- case machine_flags::TYPE_ARCADE:
- type = "arcade";
- break;
- case machine_flags::TYPE_CONSOLE:
- type = "console";
- break;
- case machine_flags::TYPE_COMPUTER:
- type = "computer";
- break;
- default:
- type = "other";
- break;
- }
- return type;
- });
game_driver_type["not_working"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) != 0; });
game_driver_type["supports_save"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) != 0; });
game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; });
game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; });
game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; });
- game_driver_type["clickable_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) != 0; });
game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; });
game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; });
game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; });
@@ -1330,15 +1509,16 @@ void lua_engine::initialize()
auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
- device_type["subtag"] = &device_t::subtag;
- device_type["siblingtag"] = &device_t::siblingtag;
- device_type["memregion"] = &device_t::memregion;
- device_type["memshare"] = &device_t::memshare;
- device_type["membank"] = &device_t::membank;
- device_type["ioport"] = &device_t::ioport;
- device_type["subdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice);
- device_type["siblingdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice);
- device_type["parameter"] = &device_t::parameter;
+ device_type.set_function(sol::meta_function::to_string, [] (device_t &d) { return util::string_format("%s(%s)", d.shortname(), d.tag()); });
+ device_type.set_function("subtag", &device_t::subtag);
+ device_type.set_function("siblingtag", &device_t::siblingtag);
+ device_type.set_function("memregion", &device_t::memregion);
+ device_type.set_function("memshare", &device_t::memshare);
+ device_type.set_function("membank", &device_t::membank);
+ device_type.set_function("ioport", &device_t::ioport);
+ device_type.set_function("subdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice));
+ device_type.set_function("siblingdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice));
+ device_type.set_function("parameter", &device_t::parameter);
device_type["tag"] = sol::property(&device_t::tag);
device_type["basetag"] = sol::property(&device_t::basetag);
device_type["name"] = sol::property(&device_t::name);
@@ -1367,18 +1547,13 @@ void lua_engine::initialize()
}
return sp_table;
});
- // FIXME: improve this
device_type["state"] = sol::property(
- [this] (device_t &dev)
+ [] (device_t &dev, sol::this_state s) -> sol::object
{
- sol::table st_table = sol().create_table();
- const device_state_interface *state;
- if(!dev.interface(state))
- return st_table;
- // XXX: refrain from exporting non-visible entries?
- for(auto &s : state->state_entries())
- st_table[s->symbol()] = s.get();
- return st_table;
+ device_state_interface const *state;
+ if (!dev.interface(state))
+ return sol::lua_nil;
+ return sol::make_object(s, device_state_entries(*state));
});
// FIXME: turn into a wrapper - it's stupid slow to walk on every property access
// also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts
@@ -1414,11 +1589,67 @@ void lua_engine::initialize()
});
+ auto dipalette_type = sol().registry().new_usertype<device_palette_interface>("dipalette", sol::no_constructor);
+ dipalette_type.set_function("pen", &device_palette_interface::pen);
+ dipalette_type.set_function(
+ "pen_color",
+ [] (device_palette_interface const &pal, pen_t pen)
+ {
+ return uint32_t(pal.pen_color(pen));
+ });
+ dipalette_type.set_function("pen_contrast", &device_palette_interface::pen_contrast);
+ dipalette_type.set_function("pen_indirect", &device_palette_interface::pen_indirect);
+ dipalette_type.set_function(
+ "indirect_color",
+ [] (device_palette_interface const &pal, int index)
+ {
+ return uint32_t(pal.indirect_color(index));
+ });
+ dipalette_type["set_pen_color"] = sol::overload(
+ [] (device_palette_interface &pal, pen_t pen, uint32_t color)
+ {
+ pal.set_pen_color(pen, rgb_t(color));
+ },
+ static_cast<void (device_palette_interface::*)(pen_t, uint8_t, uint8_t, uint8_t)>(&device_palette_interface::set_pen_color));
+ dipalette_type.set_function("set_pen_red_level", &device_palette_interface::set_pen_red_level);
+ dipalette_type.set_function("set_pen_green_level", &device_palette_interface::set_pen_green_level);
+ dipalette_type.set_function("set_pen_blue_level", &device_palette_interface::set_pen_blue_level);
+ dipalette_type.set_function("set_pen_contrast", &device_palette_interface::set_pen_contrast);
+ dipalette_type.set_function("set_pen_indirect", &device_palette_interface::set_pen_indirect);
+ dipalette_type["set_indirect_color"] = sol::overload(
+ [] (device_palette_interface &pal, int index, uint32_t color)
+ {
+ pal.set_indirect_color(index, rgb_t(color));
+ },
+ [] (device_palette_interface &pal, int index, uint8_t r, uint8_t g, uint8_t b)
+ {
+ pal.set_indirect_color(index, rgb_t(r, g, b));
+ });
+ dipalette_type.set_function("set_shadow_factor", &device_palette_interface::set_shadow_factor);
+ dipalette_type.set_function("set_highlight_factor", &device_palette_interface::set_highlight_factor);
+ dipalette_type.set_function("set_shadow_mode", &device_palette_interface::set_shadow_mode);
+ dipalette_type["palette"] = sol::property(
+ [] (device_palette_interface &pal)
+ {
+ return pal.palette()
+ ? std::optional<palette_wrapper>(std::in_place, *pal.palette())
+ : std::optional<palette_wrapper>();
+ });
+ dipalette_type["entries"] = sol::property(&device_palette_interface::entries);
+ dipalette_type["indirect_entries"] = sol::property(&device_palette_interface::indirect_entries);
+ dipalette_type["black_pen"] = sol::property(&device_palette_interface::black_pen);
+ dipalette_type["white_pen"] = sol::property(&device_palette_interface::white_pen);
+ dipalette_type["shadows_enabled"] = sol::property(&device_palette_interface::shadows_enabled);
+ dipalette_type["highlights_enabled"] = sol::property(&device_palette_interface::hilights_enabled);
+ dipalette_type["device"] = sol::property(static_cast<device_t & (device_palette_interface::*)()>(&device_palette_interface::device));
+
+
auto screen_dev_type = sol().registry().new_usertype<screen_device>(
"screen_dev",
sol::no_constructor,
sol::base_classes, sol::bases<device_t>());
- screen_dev_type["draw_box"] =
+ screen_dev_type.set_function(
+ "draw_box",
[] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
float const sc_width(sdev.visible_area().width());
@@ -1433,8 +1664,9 @@ void lua_engine::initialize()
if (!bgcolor)
bgcolor = ui.colors().background_color();
ui.draw_outlined_box(sdev.container(), x1, y1, x2, y2, *fgcolor, *bgcolor);
- };
- screen_dev_type["draw_line"] =
+ });
+ screen_dev_type.set_function(
+ "draw_line",
[] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
{
float const sc_width(sdev.visible_area().width());
@@ -1446,8 +1678,9 @@ void lua_engine::initialize()
if (!color)
color = mame_machine_manager::instance()->ui().colors().text_color();
sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- };
- screen_dev_type["draw_text"] =
+ });
+ screen_dev_type.set_function(
+ "draw_text",
[this] (screen_device &sdev, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
float const sc_width(sdev.visible_area().width());
@@ -1485,85 +1718,87 @@ void lua_engine::initialize()
x, y, (1.0f - x),
justify, ui::text_layout::word_wrapping::WORD,
mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor);
- };
- screen_dev_type["orientation"] =
- [] (screen_device &sdev)
- {
- uint32_t flags = sdev.orientation();
- int rotation_angle = 0;
- switch (flags)
+ });
+ screen_dev_type.set_function(
+ "orientation",
+ [] (screen_device &sdev)
{
- case ORIENTATION_SWAP_XY:
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
- rotation_angle = 90;
- flags ^= ORIENTATION_FLIP_X;
- break;
- case ORIENTATION_FLIP_Y:
- case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
- rotation_angle = 180;
- flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y;
- break;
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
- rotation_angle = 270;
- flags ^= ORIENTATION_FLIP_Y;
- break;
- }
- return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
- };
+ uint32_t flags = sdev.orientation();
+ int rotation_angle = 0;
+ switch (flags)
+ {
+ case ORIENTATION_SWAP_XY:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
+ rotation_angle = 90;
+ flags ^= ORIENTATION_FLIP_X;
+ break;
+ case ORIENTATION_FLIP_Y:
+ case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 180;
+ flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y;
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 270;
+ flags ^= ORIENTATION_FLIP_Y;
+ break;
+ }
+ return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
+ });
screen_dev_type["time_until_pos"] = sol::overload(
[] (screen_device &sdev, int vpos) { return sdev.time_until_pos(vpos).as_double(); },
[] (screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); });
- screen_dev_type["time_until_vblank_start"] = &screen_device::time_until_vblank_start;
- screen_dev_type["time_until_vblank_end"] = &screen_device::time_until_vblank_end;
- screen_dev_type["snapshot"] =
- [this] (screen_device &sdev, char const *filename) -> sol::object
- {
- // FIXME: this shouldn't be a member of the screen device
- // the screen is only used as a hint when configured for native snapshots and may be ignored
- std::string snapstr;
- bool is_absolute_path = false;
- if (filename)
+ screen_dev_type.set_function("time_until_vblank_start", &screen_device::time_until_vblank_start);
+ screen_dev_type.set_function("time_until_vblank_end", &screen_device::time_until_vblank_end);
+ screen_dev_type.set_function(
+ "snapshot",
+ [this] (screen_device &sdev, char const *filename) -> sol::object
{
- // a filename was specified; if it isn't absolute post-process it
- snapstr = process_snapshot_filename(machine(), filename);
- is_absolute_path = osd_is_absolute_path(snapstr);
- }
+ // FIXME: this shouldn't be a member of the screen device
+ // the screen is only used as a hint when configured for native snapshots and may be ignored
+ std::string snapstr;
+ bool is_absolute_path = false;
+ if (filename)
+ {
+ // a filename was specified; if it isn't absolute post-process it
+ snapstr = process_snapshot_filename(machine(), filename);
+ is_absolute_path = osd_is_absolute_path(snapstr);
+ }
- // open the file
- emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- std::error_condition filerr;
- if (!snapstr.empty())
- filerr = file.open(snapstr);
- else
- filerr = machine().video().open_next(file, "png");
- if (filerr)
- return sol::make_object(sol(), filerr);
+ // open the file
+ emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ std::error_condition filerr;
+ if (!snapstr.empty())
+ filerr = file.open(snapstr);
+ else
+ filerr = machine().video().open_next(file, "png");
+ if (filerr)
+ return sol::make_object(sol(), filerr);
- // and save the snapshot
- machine().video().save_snapshot(&sdev, file);
- return sol::lua_nil;
- };
- screen_dev_type["pixel"] = [] (screen_device &sdev, s32 x, s32 y) { return sdev.pixel(x, y); };
- screen_dev_type["pixels"] =
- [] (screen_device &sdev, sol::this_state s)
- {
- // TODO: would be better if this could return a tuple of (buffer, width, height)
- const rectangle &visarea = sdev.visible_area();
- luaL_Buffer buff;
- int size = visarea.height() * visarea.width() * 4;
- u32 *ptr = (u32 *)luaL_buffinitsize(s, &buff, size);
- sdev.pixels(ptr);
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(s, sol::stack_reference(s, -1));
- };
+ // and save the snapshot
+ machine().video().save_snapshot(&sdev, file);
+ return sol::lua_nil;
+ });
+ screen_dev_type.set_function("pixel", &screen_device::pixel);
+ screen_dev_type.set_function(
+ "pixels",
+ [] (screen_device &sdev, sol::this_state s)
+ {
+ const rectangle &visarea = sdev.visible_area();
+ luaL_Buffer buff;
+ int size = visarea.height() * visarea.width() * 4;
+ u32 *const ptr = reinterpret_cast<u32 *>(luaL_buffinitsize(s, &buff, size));
+ sdev.pixels(ptr);
+ luaL_pushresultsize(&buff, size);
+ return std::make_tuple(sol::make_reference(s, sol::stack_reference(s, -1)), visarea.width(), visarea.height());
+ });
screen_dev_type["screen_type"] = sol::property(&screen_device::screen_type);
screen_dev_type["width"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().width(); });
screen_dev_type["height"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().height(); });
screen_dev_type["refresh"] = sol::property([] (screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); });
screen_dev_type["refresh_attoseconds"] = sol::property([] (screen_device &sdev) { return sdev.refresh_attoseconds(); });
- screen_dev_type["xofffset"] = sol::property(&screen_device::xoffset);
- screen_dev_type["yofffset"] = sol::property(&screen_device::yoffset);
+ screen_dev_type["xoffset"] = sol::property(&screen_device::xoffset);
+ screen_dev_type["yoffset"] = sol::property(&screen_device::yoffset);
screen_dev_type["xscale"] = sol::property(&screen_device::xscale);
screen_dev_type["yscale"] = sol::property(&screen_device::yscale);
screen_dev_type["pixel_period"] = sol::property([] (screen_device &sdev) { return sdev.pixel_period().as_double(); });
@@ -1571,6 +1806,7 @@ void lua_engine::initialize()
screen_dev_type["frame_period"] = sol::property([] (screen_device &sdev) { return sdev.frame_period().as_double(); });
screen_dev_type["frame_number"] = &screen_device::frame_number;
screen_dev_type["container"] = sol::property(&screen_device::container);
+ screen_dev_type["palette"] = sol::property([] (screen_device const &sdev) { return sdev.has_palette() ? &sdev.palette() : nullptr; });
auto cass_type = sol().registry().new_usertype<cassette_image_device>(
@@ -1593,11 +1829,65 @@ void lua_engine::initialize()
auto image_type = sol().registry().new_usertype<device_image_interface>("image", sol::no_constructor);
- image_type["load"] = &device_image_interface::load;
- image_type["load_software"] = static_cast<image_init_result (device_image_interface::*)(std::string_view)>(&device_image_interface::load_software);
- image_type["unload"] = &device_image_interface::unload;
- image_type["create"] = static_cast<image_init_result (device_image_interface::*)(std::string_view)>(&device_image_interface::create);
- image_type["display"] = &device_image_interface::call_display;
+ image_type.set_function("load",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
+ {
+ auto [err, message] = di.load(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("load_software",
+ [] (device_image_interface &di, sol::this_state s, std::string_view identifier) -> sol::object
+ {
+ auto [err, message] = di.load_software(identifier);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("unload", &device_image_interface::unload);
+ image_type.set_function("create",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
+ {
+ auto [err, message] = di.create(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("display", &device_image_interface::call_display);
+ image_type.set_function("add_media_change_notifier",
+ [this] (device_image_interface &di, sol::protected_function cb)
+ {
+ return di.add_media_change_notifier(
+ [this, cbfunc = sol::protected_function(m_lua_state, cb)] (device_image_interface::media_change_event ev)
+ {
+ char const *evstr(nullptr);
+ switch (ev)
+ {
+ case device_image_interface::media_change_event::LOADED:
+ evstr = "loaded";
+ break;
+ case device_image_interface::media_change_event::UNLOADED:
+ evstr = "unloaded";
+ break;
+ }
+ auto status(invoke(cbfunc, evstr));
+ if (!status.valid())
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in media change callback: %s\n", err.what());
+ }
+ });
+ });
image_type["is_readable"] = sol::property(&device_image_interface::is_readable);
image_type["is_writeable"] = sol::property(&device_image_interface::is_writeable);
image_type["is_creatable"] = sol::property(&device_image_interface::is_creatable);
@@ -1640,6 +1930,38 @@ void lua_engine::initialize()
image_type["device"] = sol::property(static_cast<device_t & (device_image_interface::*)()>(&device_image_interface::device));
+ auto state_entry_type = sol().registry().new_usertype<device_state_entry>("state_entry", sol::no_constructor);
+ state_entry_type["value"] = sol::property(
+ [] (device_state_entry const &entry, sol::this_state s) -> sol::object
+ {
+ if (entry.is_float())
+ return sol::make_object(s, entry.dvalue());
+ else
+ return sol::make_object(s, entry.value());
+ },
+ [] (device_state_entry const &entry, sol::this_state s, sol::object value)
+ {
+ if (!entry.writeable())
+ luaL_error(s, "cannot set value of read-only device state entry");
+ else if (entry.is_float())
+ entry.set_dvalue(value.as<double>());
+ else
+ entry.set_value(value.as<u64>());
+ });
+ state_entry_type["symbol"] = sol::property(&device_state_entry::symbol);
+ state_entry_type["visible"] = sol::property(&device_state_entry::visible);
+ state_entry_type["writeable"] = sol::property(&device_state_entry::writeable);
+ state_entry_type["is_float"] = sol::property(&device_state_entry::is_float);
+ state_entry_type["datamask"] = sol::property(
+ [] (device_state_entry const &entry)
+ {
+ return entry.is_float() ? std::optional<u64>() : std::optional<u64>(entry.datamask());
+ });
+ state_entry_type["datasize"] = sol::property(&device_state_entry::datasize);
+ state_entry_type["max_length"] = sol::property(&device_state_entry::max_length);
+ state_entry_type[sol::meta_function::to_string] = &device_state_entry::to_string;
+
+
auto format_type = sol().registry().new_usertype<image_device_format>("image_format", sol::no_constructor);
format_type["name"] = sol::property(&image_device_format::name);
format_type["description"] = sol::property(&image_device_format::description);
@@ -1760,54 +2082,21 @@ void lua_engine::initialize()
auto ui_type = sol().registry().new_usertype<mame_ui_manager>("ui", sol::no_constructor);
// sol converts char32_t to a string
- ui_type["get_char_width"] = [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); };
- ui_type["get_string_width"] = &mame_ui_manager::get_string_width;
- ui_type["set_aggressive_input_focus"] = [](mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); };
+ ui_type.set_function("get_char_width", [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); });
+ ui_type.set_function("get_string_width", static_cast<float (mame_ui_manager::*)(std::string_view)>(&mame_ui_manager::get_string_width));
+ ui_type.set_function("set_aggressive_input_focus", [] (mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); });
ui_type["get_general_input_setting"] = sol::overload(
// TODO: overload with sequence type string - parser isn't available here
[] (mame_ui_manager &ui, ioport_type type, int player) { return ui.get_general_input_setting(type, player, SEQ_TYPE_STANDARD); },
[] (mame_ui_manager &ui, ioport_type type) { return ui.get_general_input_setting(type, 0, SEQ_TYPE_STANDARD); });
ui_type["options"] = sol::property([] (mame_ui_manager &m) { return static_cast<core_options *>(&m.options()); });
- ui_type["line_height"] = sol::property(&mame_ui_manager::get_line_height);
+ ui_type["line_height"] = sol::property([] (mame_ui_manager &m) { return m.get_line_height(); });
ui_type["menu_active"] = sol::property(&mame_ui_manager::is_menu_active);
+ ui_type["ui_active"] = sol::property(&mame_ui_manager::ui_active, &mame_ui_manager::set_ui_active);
ui_type["single_step"] = sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step);
ui_type["show_fps"] = sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps);
ui_type["show_profiler"] = sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler);
-
-
-/* device_state_entry library
- *
- * manager:machine().devices[device_tag].state[state_name]
- *
- * state:name() - get device state name
- * state:is_visible() - is state visible in debugger
- * state:is_divider() - is state a divider
- *
- * state.value - get device state value
- */
-
- auto dev_state_type = sol().registry().new_usertype<device_state_entry>("dev_state", "new", sol::no_constructor);
- dev_state_type.set("name", &device_state_entry::symbol);
- dev_state_type.set("value", sol::property(
- [this](device_state_entry &entry) -> uint64_t {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- machine().save().dispatch_presave();
- return state->state_int(entry.index());
- }
- return 0;
- },
- [this](device_state_entry &entry, uint64_t val) {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- state->set_state_int(entry.index(), val);
- machine().save().dispatch_presave();
- }
- }));
- dev_state_type.set("is_visible", &device_state_entry::visible);
- dev_state_type.set("is_divider", &device_state_entry::divider);
+ ui_type["image_display_enabled"] = sol::property(&mame_ui_manager::image_display_enabled, &mame_ui_manager::set_image_display_enabled);
/* rom_entry library
@@ -1875,6 +2164,10 @@ void lua_engine::initialize()
//-------------------------------------------------
bool lua_engine::frame_hook()
{
+ std::vector<int> tasks = std::move(m_update_tasks);
+ m_update_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
return execute_function("LUA_ON_FRAME_DONE");
}
@@ -1884,6 +2177,10 @@ bool lua_engine::frame_hook()
void lua_engine::close()
{
+ m_notifiers.reset();
+ m_menu.clear();
+ m_update_tasks.clear();
+ m_frame_tasks.clear();
m_sol_state.reset();
if (m_lua_state)
{
@@ -1893,49 +2190,46 @@ void lua_engine::close()
}
}
-void lua_engine::resume(void *ptr, int nparam)
+void lua_engine::resume(s32 param)
{
- lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, nparam);
- lua_State *L = lua_tothread(m_lua_state, -1);
- lua_pop(m_lua_state, 1);
- int stat = lua_resume(L, nullptr, 0);
- if((stat != LUA_OK) && (stat != LUA_YIELD))
- {
- osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(L, -1));
- lua_pop(L, 1);
- }
- luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
+ attotime const now = machine().time();
+ auto const pos = std::find_if(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ [&now] (auto const &x) { return now < x.first; });
+ std::vector<int> expired;
+ expired.reserve(std::distance(m_waiting_tasks.begin(), pos));
+ for (auto it = m_waiting_tasks.begin(); pos != it; ++it)
+ expired.emplace_back(it->second);
+ m_waiting_tasks.erase(m_waiting_tasks.begin(), pos);
+ if (!m_waiting_tasks.empty())
+ m_timer->reset(m_waiting_tasks.begin()->first - now);
+ resume_tasks(m_lua_state, expired, true);
}
-void lua_engine::run(sol::load_result res)
+//-------------------------------------------------
+// load_script - load script from file path
+//-------------------------------------------------
+
+sol::load_result lua_engine::load_script(std::string const &filename)
{
- if(res.valid())
- {
- auto ret = invoke(res.get<sol::protected_function>());
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
- }
- }
- else
- osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
+ return sol().load_file(filename);
}
//-------------------------------------------------
-// execute - load and execute script
+// load_string - load script from string
//-------------------------------------------------
-void lua_engine::load_script(const char *filename)
+sol::load_result lua_engine::load_string(std::string const &value)
{
- run(sol().load_file(filename));
+ return sol().load(value);
}
//-------------------------------------------------
-// execute_string - execute script from string
+// make_environment - make a sandbox
//-------------------------------------------------
-void lua_engine::load_string(const char *value)
+sol::environment lua_engine::make_environment()
{
- run(sol().load(value));
+ return sol::environment(sol(), sol::create, sol().globals());
}