summaryrefslogtreecommitdiff
path: root/src/lua
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-12 19:14:09 +1100
commit8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de (patch)
tree02b6cf23f591915747ec2994381854a79979c4a0 /src/lua
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'src/lua')
-rw-r--r--src/lua/CMakeLists.txt9
-rw-r--r--src/lua/bridge.cpp96
-rw-r--r--src/lua/include/bridge.hpp29
-rw-r--r--src/lua/include/lua_thread.hpp38
-rw-r--r--src/lua/lua_thread.cpp98
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