summaryrefslogtreecommitdiff
path: root/src
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
parent8471046a95ab9e00f7d42b56dbbc9ce3e5b424b9 (diff)
downloadtangara-fw-8a0a167adbf3d9b6f8b6f16aaf20ca39ad5549de.tar.gz
Convert the main menu screen to lua lol
Diffstat (limited to 'src')
-rw-r--r--src/app_console/app_console.cpp5
-rw-r--r--src/database/database.cpp4
-rw-r--r--src/database/include/database.hpp2
-rw-r--r--src/drivers/CMakeLists.txt4
-rw-r--r--src/drivers/include/spiffs.hpp15
-rw-r--r--src/drivers/spiffs.cpp35
-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
-rw-r--r--src/system_fsm/booting.cpp2
-rw-r--r--src/ui/CMakeLists.txt6
-rw-r--r--src/ui/include/screen_lua.hpp22
-rw-r--r--src/ui/include/screen_menu.hpp34
-rw-r--r--src/ui/include/ui_events.hpp2
-rw-r--r--src/ui/include/ui_fsm.hpp24
-rw-r--r--src/ui/lvgl_task.cpp1
-rw-r--r--src/ui/screen_lua.cpp19
-rw-r--r--src/ui/screen_menu.cpp75
-rw-r--r--src/ui/screen_track_browser.cpp1
-rw-r--r--src/ui/ui_fsm.cpp114
22 files changed, 459 insertions, 176 deletions
diff --git a/src/app_console/app_console.cpp b/src/app_console/app_console.cpp
index 28fac182..4a57853c 100644
--- a/src/app_console/app_console.cpp
+++ b/src/app_console/app_console.cpp
@@ -245,7 +245,7 @@ int CmdDbIndex(int argc, char** argv) {
}
std::shared_ptr<database::Result<database::IndexRecord>> res(
- db->GetTracksByIndex(*index, 20).get());
+ db->GetTracksByIndex(index->id, 20).get());
int choice_index = 2;
if (res->values().empty()) {
@@ -708,7 +708,8 @@ void RegisterHapticEffect() {
esp_console_cmd_t cmd{
.command = "haptic_effect",
.help =
- "Plays one, a range of, or all effects from a DRV2624 effect library; run 'haptic_effect help' for more.",
+ "Plays one, a range of, or all effects from a DRV2624 effect "
+ "library; run 'haptic_effect help' for more.",
.hint = NULL,
.func = &CmdHaptics,
.argtable = NULL};
diff --git a/src/database/database.cpp b/src/database/database.cpp
index e6cb85ed..142735d8 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -453,12 +453,12 @@ auto Database::GetIndexes() -> std::vector<IndexInfo> {
};
}
-auto Database::GetTracksByIndex(const IndexInfo& index, std::size_t page_size)
+auto Database::GetTracksByIndex(IndexId index, std::size_t page_size)
-> std::future<Result<IndexRecord>*> {
return worker_task_->Dispatch<Result<IndexRecord>*>(
[=, this]() -> Result<IndexRecord>* {
IndexKey::Header header{
- .id = index.id,
+ .id = index,
.depth = 0,
.components_hash = 0,
};
diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp
index fb58f3e7..544e4a62 100644
--- a/src/database/include/database.hpp
+++ b/src/database/include/database.hpp
@@ -117,7 +117,7 @@ class Database {
-> std::future<std::vector<std::shared_ptr<Track>>>;
auto GetIndexes() -> std::vector<IndexInfo>;
- auto GetTracksByIndex(const IndexInfo& index, std::size_t page_size)
+ auto GetTracksByIndex(IndexId index, std::size_t page_size)
-> std::future<Result<IndexRecord>*>;
auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>;
auto GetDump(std::size_t page_size) -> std::future<Result<std::pmr::string>*>;
diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt
index b2b5bfdd..7d1e048d 100644
--- a/src/drivers/CMakeLists.txt
+++ b/src/drivers/CMakeLists.txt
@@ -5,7 +5,7 @@
idf_component_register(
SRCS "touchwheel.cpp" "i2s_dac.cpp" "gpios.cpp" "adc.cpp" "storage.cpp" "i2c.cpp"
"spi.cpp" "display.cpp" "display_init.cpp" "samd.cpp" "relative_wheel.cpp" "wm8523.cpp"
- "nvs.cpp" "bluetooth.cpp" "haptics.cpp"
+ "nvs.cpp" "bluetooth.cpp" "haptics.cpp" "spiffs.cpp"
INCLUDE_DIRS "include"
- REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks" "nvs_flash" "bt" "tinyfsm")
+ REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span" "tasks" "nvs_flash" "bt" "tinyfsm" "spiffs")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/drivers/include/spiffs.hpp b/src/drivers/include/spiffs.hpp
new file mode 100644
index 00000000..04478590
--- /dev/null
+++ b/src/drivers/include/spiffs.hpp
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include "esp_err.h"
+
+namespace drivers {
+
+esp_err_t spiffs_mount();
+
+} // namespace drivers
diff --git a/src/drivers/spiffs.cpp b/src/drivers/spiffs.cpp
new file mode 100644
index 00000000..9a85c0d3
--- /dev/null
+++ b/src/drivers/spiffs.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "spiffs.hpp"
+
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_spiffs.h"
+
+namespace drivers {
+
+[[maybe_unused]] static constexpr char kTag[] = "spiffs";
+
+esp_err_t spiffs_mount() {
+ esp_vfs_spiffs_conf_t config{
+ .base_path = "/lua",
+ .partition_label = "lua",
+ .max_files = 5,
+ .format_if_mount_failed = false,
+ };
+
+ esp_err_t res = esp_vfs_spiffs_register(&config);
+ if (res == ESP_OK) {
+ size_t total, used;
+ esp_spiffs_info("lua", &total, &used);
+ ESP_LOGI(kTag, "spiffs mounted okay. %d / %d ", used / 1024, total / 1024);
+ }
+
+ return res;
+}
+
+} // namespace drivers
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
diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp
index 53288dbd..e7080e9b 100644
--- a/src/system_fsm/booting.cpp
+++ b/src/system_fsm/booting.cpp
@@ -6,6 +6,7 @@
#include "collation.hpp"
#include "haptics.hpp"
+#include "spiffs.hpp"
#include "system_fsm.hpp"
#include <stdint.h>
@@ -71,6 +72,7 @@ auto Booting::entry() -> void {
tasks::Worker::Start<tasks::Type::kBackgroundWorker>()});
ESP_LOGI(kTag, "installing remaining drivers");
+ drivers::spiffs_mount();
sServices->samd(std::unique_ptr<drivers::Samd>(drivers::Samd::Create()));
sServices->nvs(
std::unique_ptr<drivers::NvsStorage>(drivers::NvsStorage::OpenSync()));
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 8d3640e6..00b1f1f5 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -3,15 +3,15 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp" "screen_menu.cpp"
+ SRCS "lvgl_task.cpp" "ui_fsm.cpp" "screen_splash.cpp"
"encoder_input.cpp" "screen_track_browser.cpp" "screen_playing.cpp"
"themes.cpp" "widget_top_bar.cpp" "screen.cpp" "screen_onboarding.cpp"
"modal_progress.cpp" "modal.cpp" "modal_confirm.cpp" "screen_settings.cpp"
- "event_binding.cpp" "modal_add_to_queue.cpp"
+ "event_binding.cpp" "modal_add_to_queue.cpp" "screen_lua.cpp"
"splash.c" "font_fusion.c" "font_symbols.c"
"icons/battery_empty.c" "icons/battery_full.c" "icons/battery_20.c"
"icons/battery_40.c" "icons/battery_60.c" "icons/battery_80.c" "icons/play.c"
"icons/pause.c" "icons/bluetooth.c"
INCLUDE_DIRS "include"
- REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "bindey")
+ REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "bindey" "lua" "luavgl")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/ui/include/screen_lua.hpp b/src/ui/include/screen_lua.hpp
new file mode 100644
index 00000000..df83ea8b
--- /dev/null
+++ b/src/ui/include/screen_lua.hpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include "lua.hpp"
+
+#include "screen.hpp"
+
+namespace ui {
+namespace screens {
+
+class Lua : public Screen {
+ public:
+ explicit Lua(lua_State* l);
+};
+
+} // namespace screens
+} // namespace ui
diff --git a/src/ui/include/screen_menu.hpp b/src/ui/include/screen_menu.hpp
deleted file mode 100644
index a83346f6..00000000
--- a/src/ui/include/screen_menu.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "index.hpp"
-#include "lvgl.h"
-
-#include "model_top_bar.hpp"
-#include "screen.hpp"
-#include "screen_settings.hpp"
-
-namespace ui {
-namespace screens {
-
-class Menu : public MenuScreen {
- public:
- explicit Menu(models::TopBar&, std::vector<database::IndexInfo> indexes);
- ~Menu();
-
- private:
- std::vector<database::IndexInfo> indexes_;
- lv_obj_t* container_;
- lv_obj_t* label_;
-};
-
-} // namespace screens
-} // namespace ui
diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp
index 2bee6222..b8dd459c 100644
--- a/src/ui/include/ui_events.hpp
+++ b/src/ui/include/ui_events.hpp
@@ -35,7 +35,7 @@ struct RecordSelected : tinyfsm::Event {
};
struct IndexSelected : tinyfsm::Event {
- database::IndexInfo index;
+ database::IndexId id;
};
struct ControlSchemeChanged : tinyfsm::Event {};
diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp
index da63b77f..7d1d62d6 100644
--- a/src/ui/include/ui_fsm.hpp
+++ b/src/ui/include/ui_fsm.hpp
@@ -16,6 +16,7 @@
#include "bindey/property.h"
#include "db_events.hpp"
#include "gpios.hpp"
+#include "lua_thread.hpp"
#include "lvgl_task.hpp"
#include "model_playback.hpp"
#include "model_top_bar.hpp"
@@ -89,7 +90,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
protected:
void PushScreen(std::shared_ptr<Screen>);
- void PopScreen();
+ int PopScreen();
static std::unique_ptr<UiTask> sTask;
static std::shared_ptr<system_fsm::ServiceLocator> sServices;
@@ -99,6 +100,9 @@ class UiState : public tinyfsm::Fsm<UiState> {
static std::stack<std::shared_ptr<Screen>> sScreens;
static std::shared_ptr<Screen> sCurrentScreen;
static std::shared_ptr<Modal> sCurrentModal;
+ static std::shared_ptr<lua::LuaThread> sLua;
+
+ static std::weak_ptr<screens::Bluetooth> bluetooth_screen_;
static models::Playback sPlaybackModel;
static models::TopBar sTopBarModel;
@@ -110,6 +114,19 @@ class Splash : public UiState {
public:
void exit() override;
void react(const system_fsm::BootComplete&) override;
+ void react(const system_fsm::StorageMounted&) override;
+ using UiState::react;
+};
+
+class Lua : public UiState {
+ public:
+ void entry() override;
+ void exit() override;
+
+ void react(const internal::IndexSelected&) override;
+ void react(const internal::ShowNowPlaying&) override;
+ void react(const internal::ShowSettingsPage&) override;
+
using UiState::react;
};
@@ -131,20 +148,15 @@ class Browse : public UiState {
void entry() override;
void react(const internal::RecordSelected&) override;
- void react(const internal::IndexSelected&) override;
void react(const internal::BackPressed&) override;
void react(const internal::ShowNowPlaying&) override;
void react(const internal::ShowSettingsPage&) override;
void react(const internal::ReindexDatabase&) override;
- void react(const system_fsm::StorageMounted&) override;
void react(const system_fsm::BluetoothDevicesChanged&) override;
using UiState::react;
-
- private:
- std::weak_ptr<screens::Bluetooth> bluetooth_screen_;
};
class Playing : public UiState {
diff --git a/src/ui/lvgl_task.cpp b/src/ui/lvgl_task.cpp
index 817ed185..96f9bdf5 100644
--- a/src/ui/lvgl_task.cpp
+++ b/src/ui/lvgl_task.cpp
@@ -30,6 +30,7 @@
#include "hal/gpio_types.h"
#include "hal/lv_hal_indev.h"
#include "hal/spi_types.h"
+#include "lua.h"
#include "lv_api_map.h"
#include "lvgl/lvgl.h"
#include "misc/lv_color.h"
diff --git a/src/ui/screen_lua.cpp b/src/ui/screen_lua.cpp
new file mode 100644
index 00000000..cee5186e
--- /dev/null
+++ b/src/ui/screen_lua.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "screen_lua.hpp"
+
+#include "lua.hpp"
+#include "luavgl.h"
+
+namespace ui {
+namespace screens {
+
+Lua::Lua(lua_State* l) {
+}
+
+} // namespace screens
+} // namespace ui
diff --git a/src/ui/screen_menu.cpp b/src/ui/screen_menu.cpp
deleted file mode 100644
index 037e1156..00000000
--- a/src/ui/screen_menu.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "screen_menu.hpp"
-
-#include "core/lv_event.h"
-#include "esp_log.h"
-
-#include "core/lv_group.h"
-#include "core/lv_obj_pos.h"
-#include "event_queue.hpp"
-#include "extra/widgets/list/lv_list.h"
-#include "extra/widgets/menu/lv_menu.h"
-#include "extra/widgets/spinner/lv_spinner.h"
-#include "hal/lv_hal_disp.h"
-#include "index.hpp"
-#include "lv_api_map.h"
-#include "misc/lv_area.h"
-#include "model_top_bar.hpp"
-#include "ui_events.hpp"
-#include "ui_fsm.hpp"
-#include "widget_top_bar.hpp"
-#include "widgets/lv_label.h"
-
-namespace ui {
-namespace screens {
-
-static void now_playing_click_cb(lv_event_t* ev) {
- events::Ui().Dispatch(internal::ShowNowPlaying{});
-}
-
-static void settings_click_callback(lv_event_t* ev) {
- events::Ui().Dispatch(internal::ShowSettingsPage{
- .page = internal::ShowSettingsPage::Page::kRoot});
-}
-
-static void index_click_cb(lv_event_t* ev) {
- if (ev->user_data == NULL) {
- return;
- }
- database::IndexInfo* index =
- reinterpret_cast<database::IndexInfo*>(ev->user_data);
-
- events::Ui().Dispatch(internal::IndexSelected{.index = *index});
-}
-
-Menu::Menu(models::TopBar& top_bar, std::vector<database::IndexInfo> indexes)
- : MenuScreen(top_bar, " ", false), indexes_(indexes) {
- lv_obj_t* list = lv_list_create(content_);
- lv_obj_set_size(list, lv_pct(100), lv_pct(100));
-
- lv_obj_t* now_playing = lv_list_add_btn(list, NULL, "Now Playing");
- lv_obj_add_event_cb(now_playing, now_playing_click_cb, LV_EVENT_CLICKED,
- NULL);
- lv_group_add_obj(group_, now_playing);
-
- for (database::IndexInfo& index : indexes_) {
- lv_obj_t* item = lv_list_add_btn(list, NULL, index.name.c_str());
- lv_obj_add_event_cb(item, index_click_cb, LV_EVENT_CLICKED, &index);
- lv_group_add_obj(group_, item);
- }
-
- lv_obj_t* settings = lv_list_add_btn(list, NULL, "Settings");
- lv_obj_add_event_cb(settings, settings_click_callback, LV_EVENT_CLICKED,
- NULL);
- lv_group_add_obj(group_, settings);
-}
-
-Menu::~Menu() {}
-
-} // namespace screens
-} // namespace ui
diff --git a/src/ui/screen_track_browser.cpp b/src/ui/screen_track_browser.cpp
index 34abf17e..c7b035ad 100644
--- a/src/ui/screen_track_browser.cpp
+++ b/src/ui/screen_track_browser.cpp
@@ -18,7 +18,6 @@
#include "misc/lv_anim.h"
#include "misc/lv_color.h"
#include "model_top_bar.hpp"
-#include "screen_menu.hpp"
#include "core/lv_event.h"
#include "esp_log.h"
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index 8d3fa20d..748e08f9 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -10,9 +10,16 @@
#include "audio_fsm.hpp"
#include "battery.hpp"
+#include "core/lv_group.h"
#include "core/lv_obj.h"
+#include "core/lv_obj_tree.h"
#include "database.hpp"
+#include "esp_heap_caps.h"
#include "haptics.hpp"
+#include "lauxlib.h"
+#include "lua.hpp"
+#include "lua_thread.hpp"
+#include "luavgl.h"
#include "misc/lv_gc.h"
#include "audio_events.hpp"
@@ -28,19 +35,21 @@
#include "nvs.hpp"
#include "relative_wheel.hpp"
#include "screen.hpp"
-#include "screen_menu.hpp"
+#include "screen_lua.hpp"
#include "screen_onboarding.hpp"
#include "screen_playing.hpp"
#include "screen_settings.hpp"
#include "screen_splash.hpp"
#include "screen_track_browser.hpp"
#include "source.hpp"
+#include "spiffs.hpp"
#include "storage.hpp"
#include "system_events.hpp"
#include "touchwheel.hpp"
#include "track_queue.hpp"
#include "ui_events.hpp"
#include "widget_top_bar.hpp"
+#include "widgets/lv_label.h"
namespace ui {
@@ -56,6 +65,9 @@ std::shared_ptr<EncoderInput> UiState::sInput;
std::stack<std::shared_ptr<Screen>> UiState::sScreens;
std::shared_ptr<Screen> UiState::sCurrentScreen;
std::shared_ptr<Modal> UiState::sCurrentModal;
+std::shared_ptr<lua::LuaThread> UiState::sLua;
+
+std::weak_ptr<screens::Bluetooth> UiState::bluetooth_screen_;
models::Playback UiState::sPlaybackModel;
models::TopBar UiState::sTopBarModel{{},
@@ -83,12 +95,13 @@ void UiState::PushScreen(std::shared_ptr<Screen> screen) {
sCurrentScreen = screen;
}
-void UiState::PopScreen() {
+int UiState::PopScreen() {
if (sScreens.empty()) {
- return;
+ return 0;
}
sCurrentScreen = sScreens.top();
sScreens.pop();
+ return sScreens.size();
}
void UiState::react(const system_fsm::KeyLockChanged& ev) {
@@ -159,12 +172,48 @@ void Splash::react(const system_fsm::BootComplete& ev) {
} else {
ESP_LOGE(kTag, "no input devices initialised!");
}
+}
- if (sServices->nvs().HasShownOnboarding()) {
- transit<Browse>();
- } else {
- transit<Onboarding>();
+void Splash::react(const system_fsm::StorageMounted&) {
+ transit<Lua>();
+}
+
+void Lua::entry() {
+ if (!sLua) {
+ sCurrentScreen.reset(new Screen());
+ lv_group_set_default(sCurrentScreen->group());
+
+ sLua.reset(lua::LuaThread::Start(*sServices, sCurrentScreen->content()));
+ sLua->RunScript("/lua/main.lua");
+
+ lv_group_set_default(NULL);
+ }
+}
+
+void Lua::exit() {}
+
+void Lua::react(const internal::IndexSelected& ev) {
+ auto db = sServices->database().lock();
+ if (!db) {
+ return;
}
+
+ ESP_LOGI(kTag, "selected index %u", ev.id);
+ auto query = db->GetTracksByIndex(ev.id, kRecordsPerPage);
+ std::pmr::vector<std::pmr::string> crumbs = {""};
+ PushScreen(std::make_shared<screens::TrackBrowser>(
+ sTopBarModel, sServices->track_queue(), sServices->database(), crumbs,
+ std::move(query)));
+ transit<Browse>();
+}
+
+void Lua::react(const internal::ShowNowPlaying&) {
+ transit<Playing>();
+}
+
+void Lua::react(const internal::ShowSettingsPage& ev) {
+ PushScreen(std::shared_ptr<Screen>(new screens::Settings(sTopBarModel)));
+ transit<Browse>();
}
void Onboarding::entry() {
@@ -218,31 +267,7 @@ void Onboarding::react(const internal::OnboardingNavigate& ev) {
}
}
-static bool sBrowseFirstEntry = true;
-
-void Browse::entry() {
- if (sBrowseFirstEntry) {
- auto db = sServices->database().lock();
- if (!db) {
- return;
- }
- sCurrentScreen =
- std::make_shared<screens::Menu>(sTopBarModel, db->GetIndexes());
- sBrowseFirstEntry = false;
- }
-}
-
-void Browse::react(const system_fsm::StorageMounted& ev) {
- if (sBrowseFirstEntry) {
- auto db = sServices->database().lock();
- if (!db) {
- return;
- }
- sCurrentScreen =
- std::make_shared<screens::Menu>(sTopBarModel, db->GetIndexes());
- sBrowseFirstEntry = false;
- }
-}
+void Browse::entry() {}
void Browse::react(const internal::ShowNowPlaying& ev) {
transit<Playing>();
@@ -327,22 +352,10 @@ void Browse::react(const internal::RecordSelected& ev) {
}
}
-void Browse::react(const internal::IndexSelected& ev) {
- auto db = sServices->database().lock();
- if (!db) {
- return;
- }
-
- ESP_LOGI(kTag, "selected index %s", ev.index.name.c_str());
- auto query = db->GetTracksByIndex(ev.index, kRecordsPerPage);
- std::pmr::vector<std::pmr::string> crumbs = {ev.index.name};
- PushScreen(std::make_shared<screens::TrackBrowser>(
- sTopBarModel, sServices->track_queue(), sServices->database(), crumbs,
- std::move(query)));
-}
-
void Browse::react(const internal::BackPressed& ev) {
- PopScreen();
+ if (PopScreen() == 0) {
+ transit<Lua>();
+ }
}
void Browse::react(const system_fsm::BluetoothDevicesChanged&) {
@@ -368,11 +381,14 @@ void Playing::entry() {
void Playing::exit() {
sPlayingScreen.reset();
- PopScreen();
}
void Playing::react(const internal::BackPressed& ev) {
- transit<Browse>();
+ if (PopScreen() == 0) {
+ transit<Lua>();
+ } else {
+ transit<Browse>();
+ }
}
static std::shared_ptr<modals::Progress> sIndexProgress;