diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-11-12 19:14:09 +1100 |
| commit | 8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch) | |
| tree | 02b6cf23f591915747ec2994381854a79979c4a0 /src/lua | |
| parent | 8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff) | |
| download | tangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz | |
Convert the main menu screen to lua lol
Diffstat (limited to 'src/lua')
| -rw-r--r-- | src/lua/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | src/lua/bridge.cpp | 96 | ||||
| -rw-r--r-- | src/lua/include/bridge.hpp | 29 | ||||
| -rw-r--r-- | src/lua/include/lua_thread.hpp | 38 | ||||
| -rw-r--r-- | src/lua/lua_thread.cpp | 98 |
5 files changed, 270 insertions, 0 deletions
diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt new file mode 100644 index 00000000..a2dd8739 --- /dev/null +++ b/src/lua/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright 2023 jacqueline <me@jacqueline.id.au> +# +# SPDX-License-Identifier: GPL-3.0-only + +idf_component_register( + SRCS "lua_thread.cpp" "bridge.cpp" + INCLUDE_DIRS "include" + REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "esp-idf-lua" "luavgl") +target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/lua/bridge.cpp b/src/lua/bridge.cpp new file mode 100644 index 00000000..acc64c31 --- /dev/null +++ b/src/lua/bridge.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "bridge.hpp" + +#include <memory> +#include <string> + +#include "esp_log.h" +#include "event_queue.hpp" +#include "lua.h" +#include "lua.hpp" +#include "lvgl.h" +#include "service_locator.hpp" +#include "ui_events.hpp" + +namespace lua { + +[[maybe_unused]] static constexpr char kTag[] = "lua_bridge"; +static constexpr char kBridgeKey[] = "bridge"; + +static auto open_settings_fn(lua_State* state) -> int { + events::Ui().Dispatch(ui::internal::ShowSettingsPage{ + .page = ui::internal::ShowSettingsPage::Page::kRoot}); + return 0; +} + +static auto open_now_playing_fn(lua_State* state) -> int { + events::Ui().Dispatch(ui::internal::ShowNowPlaying{}); + return 0; +} + +static auto open_browse_fn(lua_State* state) -> int { + int index = luaL_checkinteger(state, 1); + events::Ui().Dispatch(ui::internal::IndexSelected{ + .id = static_cast<uint8_t>(index), + }); + return 0; +} + +static const struct luaL_Reg kLegacyUiFuncs[] = { + {"open_settings", open_settings_fn}, + {"open_now_playing", open_now_playing_fn}, + {"open_browse", open_browse_fn}, + {NULL, NULL}}; + +static auto lua_legacy_ui(lua_State* state) -> int { + luaL_newlib(state, kLegacyUiFuncs); + return 1; +} + +static auto get_indexes(lua_State* state) -> int { + lua_pushstring(state, kBridgeKey); + lua_gettable(state, LUA_REGISTRYINDEX); + Bridge* instance = reinterpret_cast<Bridge*>(lua_touserdata(state, -1)); + + lua_newtable(state); + + auto db = instance->services().database().lock(); + if (!db) { + return 1; + } + + for (const auto& i : db->GetIndexes()) { + lua_pushstring(state, i.name.c_str()); + lua_rawseti(state, -2, i.id); + } + + return 1; +} + +static const struct luaL_Reg kDatabaseFuncs[] = {{"get_indexes", get_indexes}, + {NULL, NULL}}; + +static auto lua_database(lua_State* state) -> int { + luaL_newlib(state, kDatabaseFuncs); + return 1; +} + +Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s) + : services_(services), state_(s) { + lua_pushstring(&s, kBridgeKey); + lua_pushlightuserdata(&s, this); + lua_settable(&s, LUA_REGISTRYINDEX); + + luaL_requiref(&s, "legacy_ui", lua_legacy_ui, true); + lua_pop(&s, 1); + + luaL_requiref(&s, "database", lua_database, true); + lua_pop(&s, 1); +} + +} // namespace lua diff --git a/src/lua/include/bridge.hpp b/src/lua/include/bridge.hpp new file mode 100644 index 00000000..059d0604 --- /dev/null +++ b/src/lua/include/bridge.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <memory> +#include <string> + +#include "lua.hpp" +#include "lvgl.h" +#include "service_locator.hpp" + +namespace lua { + +class Bridge { + public: + Bridge(system_fsm::ServiceLocator&, lua_State& s); + + system_fsm::ServiceLocator& services() { return services_; } + + private: + system_fsm::ServiceLocator& services_; + lua_State& state_; +}; + +} // namespace lua diff --git a/src/lua/include/lua_thread.hpp b/src/lua/include/lua_thread.hpp new file mode 100644 index 00000000..381b1bdb --- /dev/null +++ b/src/lua/include/lua_thread.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <memory> +#include <string> + +#include "lua.hpp" +#include "lvgl.h" + +#include "bridge.hpp" +#include "service_locator.hpp" + +namespace lua { + +class Allocator; + +class LuaThread { + public: + static auto Start(system_fsm::ServiceLocator&, lv_obj_t* lvgl_root = nullptr) + -> LuaThread*; + ~LuaThread(); + + auto RunScript(const std::string& path) -> bool; + + private: + LuaThread(std::unique_ptr<Allocator>&, std::unique_ptr<Bridge>&, lua_State*); + + std::unique_ptr<Allocator> alloc_; + std::unique_ptr<Bridge> bridge_; + lua_State* state_; +}; + +} // namespace lua diff --git a/src/lua/lua_thread.cpp b/src/lua/lua_thread.cpp new file mode 100644 index 00000000..cb7066a5 --- /dev/null +++ b/src/lua/lua_thread.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "lua_thread.hpp" +#include <memory> + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "lua.h" +#include "lua.hpp" +#include "luavgl.h" +#include "service_locator.hpp" + +namespace lua { + +[[maybe_unused]] static constexpr char kTag[] = "lua"; + +class Allocator { + public: + Allocator() : total_allocated_(0) {} + + auto alloc(void* ptr, size_t osize, size_t nsize) -> void* { + total_allocated_ = total_allocated_ - osize + nsize; + // ESP_LOGI(kTag, "lua realloc -> %u KiB", total_allocated_ / 1024); + if (nsize == 0) { + heap_caps_free(ptr); + return NULL; + } else { + return heap_caps_realloc(ptr, nsize, MALLOC_CAP_SPIRAM); + } + } + + private: + size_t total_allocated_; +}; + +static auto lua_alloc(void* ud, void* ptr, size_t osize, size_t nsize) + -> void* { + Allocator* instance = reinterpret_cast<Allocator*>(ud); + return instance->alloc(ptr, osize, nsize); +} + +static int lua_panic(lua_State* L) { + ESP_LOGE(kTag, "!! PANIC !! %s", lua_tostring(L, -1)); + return 0; +} + +auto LuaThread::Start(system_fsm::ServiceLocator& services, lv_obj_t* lvgl_root) + -> LuaThread* { + auto alloc = std::make_unique<Allocator>(); + lua_State* state = lua_newstate(lua_alloc, alloc.get()); + if (!state) { + return nullptr; + } + + 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_root(state, lvgl_root); + luaL_requiref(state, "lvgl", luaopen_lvgl, true); + lua_pop(state, 1); + } + + return new LuaThread(alloc, bridge, 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() { + lua_close(state_); +} + +auto LuaThread::RunScript(const std::string& path) -> bool { + int res = luaL_loadfilex(state_, path.c_str(), NULL); + if (res != LUA_OK) { + return false; + } + res = lua_pcall(state_, 0, 0, 0); + if (res) { + const char* msg = lua_tostring(state_, -1); + lua_writestring(msg, strlen(msg)); + lua_writeline(); + lua_pop(state_, 1); + } + return true; +} + +} // namespace lua |
