diff options
Diffstat (limited to 'src/lua')
| -rw-r--r-- | src/lua/bridge.cpp | 74 | ||||
| -rw-r--r-- | src/lua/include/bridge.hpp | 27 | ||||
| -rw-r--r-- | src/lua/include/lua_thread.hpp | 42 | ||||
| -rw-r--r-- | src/lua/include/property.hpp | 4 | ||||
| -rw-r--r-- | src/lua/lua_thread.cpp | 98 | ||||
| -rw-r--r-- | src/lua/property.cpp | 78 |
6 files changed, 219 insertions, 104 deletions
diff --git a/src/lua/bridge.cpp b/src/lua/bridge.cpp index a680a521..a26f74bb 100644 --- a/src/lua/bridge.cpp +++ b/src/lua/bridge.cpp @@ -22,6 +22,9 @@ #include "lua_version.hpp" #include "lvgl.h" +#include "font/lv_font_loader.h" +#include "luavgl.h" + #include "event_queue.hpp" #include "property.hpp" #include "service_locator.hpp" @@ -32,34 +35,62 @@ int luaopen_linenoise(lua_State* L); int luaopen_term_core(lua_State* L); } +LV_FONT_DECLARE(font_fusion_12); +LV_FONT_DECLARE(font_fusion_10); + namespace lua { [[maybe_unused]] static constexpr char kTag[] = "lua_bridge"; static constexpr char kBridgeKey[] = "bridge"; +static auto make_font_cb(const char* name, int size, int weight) + -> const lv_font_t* { + if (std::string{"fusion"} == name) { + if (size == 12) { + return &font_fusion_12; + } + if (size == 10) { + return &font_fusion_10; + } + } + return NULL; +} + +static auto delete_font_cb(lv_font_t* font) -> void {} + auto Bridge::Get(lua_State* state) -> Bridge* { lua_pushstring(state, kBridgeKey); lua_gettable(state, LUA_REGISTRYINDEX); return reinterpret_cast<Bridge*>(lua_touserdata(state, -1)); } -Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s) - : services_(services), state_(s), bindings_(s) { - lua_pushstring(&s, kBridgeKey); - lua_pushlightuserdata(&s, this); - lua_settable(&s, LUA_REGISTRYINDEX); +Bridge::Bridge(system_fsm::ServiceLocator& services) : services_(services) {} + +auto Bridge::installBaseModules(lua_State* L) -> void { + lua_pushstring(L, kBridgeKey); + lua_pushlightuserdata(L, this); + lua_settable(L, LUA_REGISTRYINDEX); + + bindings_.install(L); - luaL_requiref(&s, "linenoise", luaopen_linenoise, true); - lua_pop(&s, 1); + luaL_requiref(L, "linenoise", luaopen_linenoise, true); + lua_pop(L, 1); - luaL_requiref(&s, "term.core", luaopen_term_core, true); - lua_pop(&s, 1); + luaL_requiref(L, "term.core", luaopen_term_core, true); + lua_pop(L, 1); + + RegisterControlsModule(L); + RegisterDatabaseModule(L); + RegisterQueueModule(L); + RegisterVersionModule(L); +} - RegisterControlsModule(&s); - RegisterDatabaseModule(&s); - RegisterQueueModule(&s); - RegisterVersionModule(&s); +auto Bridge::installLvgl(lua_State* L) -> void { + luavgl_set_pcall(L, CallProtected); + luavgl_set_font_extension(L, make_font_cb, delete_font_cb); + luaL_requiref(L, "lvgl", luaopen_lvgl, true); + lua_pop(L, 1); } static auto new_property_module(lua_State* state) -> int { @@ -76,32 +107,33 @@ static auto new_property_module(lua_State* state) -> int { template <class... Ts> inline constexpr bool always_false_v = false; -auto Bridge::AddPropertyModule( +auto Bridge::installPropertyModule( + lua_State* L, const std::string& name, - std::vector<std::pair<std::string, std::variant<LuaFunction, Property*>>> + std::vector<std::pair<std::string, std::variant<LuaFunction, Property*>>>& props) -> void { // Create the module (or retrieve it if one with this name already exists) - luaL_requiref(&state_, name.c_str(), new_property_module, true); + luaL_requiref(L, name.c_str(), new_property_module, true); for (const auto& prop : props) { - lua_pushstring(&state_, prop.first.c_str()); + lua_pushstring(L, prop.first.c_str()); std::visit( [&](auto&& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, LuaFunction>) { - bindings_.Register(&state_, arg); + bindings_.Register(L, arg); } else if constexpr (std::is_same_v<T, Property*>) { - bindings_.Register(&state_, arg); + bindings_.Register(L, arg); } else { static_assert(always_false_v<T>, "missing case"); } }, prop.second); - lua_settable(&state_, -3); // metatable.propname = property + lua_settable(L, -3); // metatable.propname = property } - lua_pop(&state_, 1); // pop the module off the stack + lua_pop(L, 1); // pop the module off the stack } } // namespace lua diff --git a/src/lua/include/bridge.hpp b/src/lua/include/bridge.hpp index 62fbc340..64f14e0e 100644 --- a/src/lua/include/bridge.hpp +++ b/src/lua/include/bridge.hpp @@ -16,25 +16,38 @@ namespace lua { +/* + * Responsible for adding C/C++ module bindings to Lua threads. This class + * keeps no thread-specific internal state, and instead uses the LUA_REGISTRY + * table of its host threads to store data. + */ class Bridge { public: + /* + * Utility for retrieving the Bridge from a Lua thread in which the Bridge's + * bindings have been installed. Use by Lua's C callbacks to access the rest + * of the system. + */ static auto Get(lua_State* state) -> Bridge*; - Bridge(system_fsm::ServiceLocator&, lua_State& s); + Bridge(system_fsm::ServiceLocator& s); - auto AddPropertyModule( + system_fsm::ServiceLocator& services() { return services_; } + + auto installBaseModules(lua_State* L) -> void; + auto installLvgl(lua_State* L) -> void; + auto installPropertyModule( + lua_State* L, const std::string&, std::vector< - std::pair<std::string, - std::variant<LuaFunction, Property*>>>) + std::pair<std::string, std::variant<LuaFunction, Property*>>>&) -> void; - system_fsm::ServiceLocator& services() { return services_; } - PropertyBindings& bindings() { return bindings_; } + Bridge(const Bridge&) = delete; + Bridge& operator=(const Bridge&) = delete; private: system_fsm::ServiceLocator& services_; - lua_State& state_; PropertyBindings bindings_; }; diff --git a/src/lua/include/lua_thread.hpp b/src/lua/include/lua_thread.hpp index d10dba3a..c12a0bfc 100644 --- a/src/lua/include/lua_thread.hpp +++ b/src/lua/include/lua_thread.hpp @@ -23,8 +23,7 @@ auto CallProtected(lua_State*, int nargs, int nresults) -> int; class LuaThread { public: - static auto Start(system_fsm::ServiceLocator&, lv_obj_t* lvgl_root = nullptr) - -> LuaThread*; + static auto Start(system_fsm::ServiceLocator&) -> LuaThread*; ~LuaThread(); auto RunScript(const std::string& path) -> bool; @@ -32,15 +31,48 @@ class LuaThread { auto DumpStack() -> void; - auto bridge() -> Bridge& { return *bridge_; } auto state() -> lua_State* { return state_; } + LuaThread(const LuaThread&) = delete; + LuaThread& operator=(const LuaThread&) = delete; + private: - LuaThread(std::unique_ptr<Allocator>&, std::unique_ptr<Bridge>&, lua_State*); + LuaThread(std::unique_ptr<Allocator>&, lua_State*); std::unique_ptr<Allocator> alloc_; - std::unique_ptr<Bridge> bridge_; lua_State* state_; }; +class Registry { + public: + static auto instance(system_fsm::ServiceLocator&) -> Registry&; + + auto uiThread() -> std::shared_ptr<LuaThread>; + auto newThread() -> std::shared_ptr<LuaThread>; + + auto AddPropertyModule( + const std::string&, + std::vector< + std::pair<std::string, std::variant<LuaFunction, Property*>>>) + -> void; + + Registry(const Registry&) = delete; + Registry& operator=(const Registry&) = delete; + + private: + Registry(system_fsm::ServiceLocator&); + + system_fsm::ServiceLocator& services_; + std::unique_ptr<Bridge> bridge_; + + std::shared_ptr<LuaThread> ui_thread_; + std::list<std::weak_ptr<LuaThread>> threads_; + + std::vector< + std::pair<std::string, + std::vector<std::pair<std::string, + std::variant<LuaFunction, Property*>>>>> + modules_; +}; + } // namespace lua diff --git a/src/lua/include/property.hpp b/src/lua/include/property.hpp index 03229bc1..7d160fba 100644 --- a/src/lua/include/property.hpp +++ b/src/lua/include/property.hpp @@ -53,7 +53,9 @@ class Property { class PropertyBindings { public: - PropertyBindings(lua_State&); + PropertyBindings(); + + auto install(lua_State*) -> void; auto Register(lua_State*, Property*) -> void; auto Register(lua_State*, LuaFunction) -> void; diff --git a/src/lua/lua_thread.cpp b/src/lua/lua_thread.cpp index b94b70ab..7d64e3c5 100644 --- a/src/lua/lua_thread.cpp +++ b/src/lua/lua_thread.cpp @@ -9,22 +9,16 @@ #include <iostream> #include <memory> -#include "lauxlib.h" -#include "lua.h" -#include "lua.hpp" - -#include "font/lv_font_loader.h" -#include "luavgl.h" - #include "esp_heap_caps.h" #include "esp_log.h" +#include "lua.hpp" + +#include "bridge.hpp" #include "event_queue.hpp" +#include "memory_resource.hpp" #include "service_locator.hpp" #include "ui_events.hpp" -LV_FONT_DECLARE(font_fusion_12); -LV_FONT_DECLARE(font_fusion_10); - namespace lua { [[maybe_unused]] static constexpr char kTag[] = "lua"; @@ -59,23 +53,7 @@ static int lua_panic(lua_State* L) { return 0; } -static auto make_font_cb(const char* name, int size, int weight) - -> const lv_font_t* { - if (std::string{"fusion"} == name) { - if (size == 12) { - return &font_fusion_12; - } - if (size == 10) { - return &font_fusion_10; - } - } - return NULL; -} - -static auto delete_font_cb(lv_font_t* font) -> void {} - -auto LuaThread::Start(system_fsm::ServiceLocator& services, lv_obj_t* lvgl_root) - -> LuaThread* { +auto LuaThread::Start(system_fsm::ServiceLocator& services) -> LuaThread* { auto alloc = std::make_unique<Allocator>(); lua_State* state = lua_newstate(lua_alloc, alloc.get()); if (!state) { @@ -85,24 +63,11 @@ auto LuaThread::Start(system_fsm::ServiceLocator& services, lv_obj_t* lvgl_root) luaL_openlibs(state); lua_atpanic(state, lua_panic); - auto bridge = std::make_unique<Bridge>(services, *state); - - // FIXME: luavgl init should probably be a part of the bridge. - if (lvgl_root) { - luavgl_set_pcall(state, CallProtected); - luavgl_set_font_extension(state, make_font_cb, delete_font_cb); - luavgl_set_root(state, lvgl_root); - luaL_requiref(state, "lvgl", luaopen_lvgl, true); - lua_pop(state, 1); - } - - return new LuaThread(alloc, bridge, state); + return new LuaThread(alloc, state); } -LuaThread::LuaThread(std::unique_ptr<Allocator>& alloc, - std::unique_ptr<Bridge>& bridge, - lua_State* state) - : alloc_(std::move(alloc)), bridge_(std::move(bridge)), state_(state) {} +LuaThread::LuaThread(std::unique_ptr<Allocator>& alloc, lua_State* state) + : alloc_(std::move(alloc)), state_(state) {} LuaThread::~LuaThread() { lua_close(state_); @@ -219,4 +184,51 @@ auto CallProtected(lua_State* s, int nargs, int nresults) -> int { return ret; } +auto Registry::instance(system_fsm::ServiceLocator& s) -> Registry& { + static Registry sRegistry{s}; + return sRegistry; +} + +Registry::Registry(system_fsm::ServiceLocator& services) + : services_(services), bridge_(new Bridge(services)) {} + +auto Registry::uiThread() -> std::shared_ptr<LuaThread> { + if (!ui_thread_) { + ui_thread_ = newThread(); + bridge_->installLvgl(ui_thread_->state()); + } + return ui_thread_; +} + +auto Registry::newThread() -> std::shared_ptr<LuaThread> { + std::shared_ptr<LuaThread> thread{LuaThread::Start(services_)}; + bridge_->installBaseModules(thread->state()); + for (auto& module : modules_) { + bridge_->installPropertyModule(thread->state(), module.first, + module.second); + } + threads_.push_back(thread); + return thread; +} + +auto Registry::AddPropertyModule( + const std::string& name, + std::vector<std::pair<std::string, std::variant<LuaFunction, Property*>>> + properties) -> void { + modules_.push_back(std::make_pair(name, properties)); + + // Any live threads will need to be updated to include the new module. + auto it = threads_.begin(); + while (it != threads_.end()) { + auto thread = it->lock(); + if (!thread) { + // Thread has been destroyed; stop tracking it. + it = threads_.erase(it); + } else { + bridge_->installPropertyModule(thread->state(), name, properties); + it++; + } + } +} + } // namespace lua diff --git a/src/lua/property.cpp b/src/lua/property.cpp index 5357ccc5..f721f9ce 100644 --- a/src/lua/property.cpp +++ b/src/lua/property.cpp @@ -10,10 +10,12 @@ #include <cmath> #include <memory> #include <memory_resource> +#include <sstream> #include <string> #include <variant> #include "bluetooth_types.hpp" +#include "lauxlib.h" #include "lua.h" #include "lua.hpp" #include "lua_thread.hpp" @@ -76,10 +78,30 @@ static auto property_bind(lua_State* state) -> int { return 1; } -static const struct luaL_Reg kPropertyBindingFuncs[] = {{"get", property_get}, - {"set", property_set}, - {"bind", property_bind}, - {NULL, NULL}}; +static auto property_tostring(lua_State* state) -> int { + Property* p = check_property(state); + p->PushValue(*state); + + std::stringstream str{}; + str << "property { " << luaL_tolstring(state, -1, NULL); + if (!p->IsTwoWay()) { + str << ", read-only"; + } + str << " }"; + + lua_settop(state, 0); + + std::string res = str.str(); + lua_pushlstring(state, res.data(), res.size()); + return 1; +} + +static const struct luaL_Reg kPropertyBindingFuncs[] = { + {"get", property_get}, + {"set", property_set}, + {"bind", property_bind}, + {"__tostring", property_tostring}, + {NULL, NULL}}; static auto generic_function_cb(lua_State* state) -> int { lua_pushstring(state, kBinderKey); @@ -98,45 +120,47 @@ static auto generic_function_cb(lua_State* state) -> int { return std::invoke(fn, state); } -PropertyBindings::PropertyBindings(lua_State& s) { - lua_pushstring(&s, kBinderKey); - lua_pushlightuserdata(&s, this); - lua_settable(&s, LUA_REGISTRYINDEX); +PropertyBindings::PropertyBindings() : functions_(&memory::kSpiRamResource) {} + +auto PropertyBindings::install(lua_State* L) -> void { + lua_pushstring(L, kBinderKey); + lua_pushlightuserdata(L, this); + lua_settable(L, LUA_REGISTRYINDEX); // Create the metatable responsible for the Property API. - luaL_newmetatable(&s, kPropertyMetatable); + luaL_newmetatable(L, kPropertyMetatable); - lua_pushliteral(&s, "__index"); - lua_pushvalue(&s, -2); - lua_settable(&s, -3); // metatable.__index = metatable + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); // metatable.__index = metatable // Add our binding funcs (get, set, bind) to the metatable. - luaL_setfuncs(&s, kPropertyBindingFuncs, 0); + luaL_setfuncs(L, kPropertyBindingFuncs, 0); // We've finished setting up the metatable, so pop it. - lua_pop(&s, 1); + lua_pop(L, 1); // Create a weak table in the registry to hold live bindings. - lua_pushstring(&s, kBindingsTable); - lua_newtable(&s); // bindings = {} + lua_pushstring(L, kBindingsTable); + lua_newtable(L); // bindings = {} // Metatable for the weak table. Values are weak. - lua_newtable(&s); // meta = {} - lua_pushliteral(&s, "__mode"); - lua_pushliteral(&s, "v"); - lua_settable(&s, -3); // meta.__mode='v' - lua_setmetatable(&s, -2); // setmetatable(bindings, meta) + lua_newtable(L); // meta = {} + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "v"); + lua_settable(L, -3); // meta.__mode='v' + lua_setmetatable(L, -2); // setmetatable(bindings, meta) - lua_settable(&s, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable] = bindings + lua_settable(L, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable] = bindings // Create the metatable for C++ functions. - luaL_newmetatable(&s, kFunctionMetatable); + luaL_newmetatable(L, kFunctionMetatable); - lua_pushliteral(&s, "__call"); - lua_pushcfunction(&s, generic_function_cb); - lua_settable(&s, -3); // metatable.__call = metatable + lua_pushliteral(L, "__call"); + lua_pushcfunction(L, generic_function_cb); + lua_settable(L, -3); // metatable.__call = metatable - lua_pop(&s, 1); // Clean up the function metatable + lua_pop(L, 1); // Clean up the function metatable } auto PropertyBindings::Register(lua_State* s, Property* prop) -> void { |
