summaryrefslogtreecommitdiff
path: root/src/tangara/lua
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-09-09 15:15:00 +1000
committerjacqueline <me@jacqueline.id.au>2024-09-09 15:15:00 +1000
commit2b1a01705d62d08cefd6816ba108c5cae48a50ac (patch)
tree20ba16a6259ffc335dbcded84fa6bcbe327e9d84 /src/tangara/lua
parent9475d10d1000c7e21a7ea311b0c8ee6a72ef46c4 (diff)
parentacdc9789c90ed6f083d054cd07930e020123457f (diff)
downloadtangara-fw-2b1a01705d62d08cefd6816ba108c5cae48a50ac.tar.gz
Merge branch 'main' into jqln/tts
Diffstat (limited to 'src/tangara/lua')
-rw-r--r--src/tangara/lua/file_iterator.cpp40
-rw-r--r--src/tangara/lua/file_iterator.hpp7
-rw-r--r--src/tangara/lua/lua_filesystem.cpp65
-rw-r--r--src/tangara/lua/lua_queue.cpp30
-rw-r--r--src/tangara/lua/lua_screen.cpp6
-rw-r--r--src/tangara/lua/lua_theme.cpp33
-rw-r--r--src/tangara/lua/property.cpp81
-rw-r--r--src/tangara/lua/property.hpp4
8 files changed, 162 insertions, 104 deletions
diff --git a/src/tangara/lua/file_iterator.cpp b/src/tangara/lua/file_iterator.cpp
index c3d63a16..71daf2d8 100644
--- a/src/tangara/lua/file_iterator.cpp
+++ b/src/tangara/lua/file_iterator.cpp
@@ -15,8 +15,8 @@ namespace lua {
[[maybe_unused]] static const char* kTag = "FileIterator";
-FileIterator::FileIterator(std::string filepath)
- : original_path_(filepath), current_(), offset_(-1) {
+FileIterator::FileIterator(std::string filepath, bool showHidden)
+ : original_path_(filepath), show_hidden_(showHidden), current_(), offset_(-1) {
const TCHAR* path = static_cast<const TCHAR*>(filepath.c_str());
FRESULT res = f_opendir(&dir_, path);
if (res != FR_OK) {
@@ -33,7 +33,16 @@ auto FileIterator::value() const -> const std::optional<FileEntry>& {
}
auto FileIterator::next() -> void {
- iterate(false);
+ size_t prev_index = -1;
+ if (current_) {
+ prev_index = current_->index;
+ }
+ do {
+ bool res = iterate(show_hidden_);
+ if (!res) {
+ break;
+ }
+ } while (!current_ || current_->index == prev_index);
}
auto FileIterator::prev() -> void {
@@ -45,11 +54,11 @@ auto FileIterator::prev() -> void {
auto new_offset = offset_ - 1;
offset_ = -1;
for (int i = 0; i <= new_offset; i++) {
- iterate(false);
+ iterate(show_hidden_);
}
}
-auto FileIterator::iterate(bool reverse) -> bool {
+auto FileIterator::iterate(bool show_hidden) -> bool {
FILINFO info;
auto res = f_readdir(&dir_, &info);
if (res != FR_OK) {
@@ -60,18 +69,21 @@ auto FileIterator::iterate(bool reverse) -> bool {
// End of directory
// Set value to nil
current_.reset();
+ return false;
} else {
// Update current value
offset_++;
- current_ = FileEntry{
- .index = offset_,
- .isHidden = (info.fattrib & AM_HID) > 0,
- .isDirectory = (info.fattrib & AM_DIR) > 0,
- .isTrack = false, // TODO
- .filepath = original_path_ + (original_path_.size() > 0 ? "/" : "") +
- info.fname,
-
- };
+ bool hidden = (info.fattrib & AM_HID) > 0 || info.fname[0] == '.';
+ if (!hidden || show_hidden) {
+ current_ = FileEntry{
+ .index = offset_,
+ .isHidden = hidden,
+ .isDirectory = (info.fattrib & AM_DIR) > 0,
+ .filepath = original_path_ + (original_path_.size() > 0 ? "/" : "") +
+ info.fname,
+ .name = info.fname,
+ };
+ }
}
return true;
}
diff --git a/src/tangara/lua/file_iterator.hpp b/src/tangara/lua/file_iterator.hpp
index b803062c..2d5c2d7d 100644
--- a/src/tangara/lua/file_iterator.hpp
+++ b/src/tangara/lua/file_iterator.hpp
@@ -19,13 +19,13 @@ struct FileEntry {
int index;
bool isHidden;
bool isDirectory;
- bool isTrack;
std::string filepath;
+ std::string name;
};
class FileIterator {
public:
- FileIterator(std::string filepath);
+ FileIterator(std::string filepath, bool showHidden);
~FileIterator();
auto value() const -> const std::optional<FileEntry>&;
@@ -35,6 +35,7 @@ class FileIterator {
private:
FF_DIR dir_;
std::string original_path_;
+ bool show_hidden_;
std::optional<FileEntry> current_;
int offset_;
@@ -42,4 +43,4 @@ class FileIterator {
auto iterate(bool reverse = false) -> bool;
};
-} // namespace lua \ No newline at end of file
+} // namespace lua
diff --git a/src/tangara/lua/lua_filesystem.cpp b/src/tangara/lua/lua_filesystem.cpp
index de51f555..9c2ea880 100644
--- a/src/tangara/lua/lua_filesystem.cpp
+++ b/src/tangara/lua/lua_filesystem.cpp
@@ -21,29 +21,21 @@ struct LuaFileEntry {
bool isHidden;
bool isDirectory;
bool isTrack;
- size_t path_size;
- char path[];
+ std::string path;
+ std::string name;
};
-static_assert(std::is_trivially_destructible<LuaFileEntry>());
-static_assert(std::is_trivially_copy_assignable<LuaFileEntry>());
-
static auto push_lua_file_entry(lua_State* L, const lua::FileEntry& r) -> void {
- // Create and init the userdata.
- LuaFileEntry* file_entry = reinterpret_cast<LuaFileEntry*>(
- lua_newuserdata(L, sizeof(LuaFileEntry) + r.filepath.size()));
+ lua::FileEntry** entry = reinterpret_cast<lua::FileEntry**>(
+ lua_newuserdata(L, sizeof(uintptr_t)));
+ *entry = new lua::FileEntry(r);
luaL_setmetatable(L, kFileEntryMetatable);
+}
- // Init all the fields
- *file_entry = {
- .isHidden = r.isHidden,
- .isDirectory = r.isDirectory,
- .isTrack = r.isTrack,
- .path_size = r.filepath.size(),
- };
-
- // Copy the string data across.
- std::memcpy(file_entry->path, r.filepath.data(), r.filepath.size());
+auto check_file_entry(lua_State* L, int stack_pos) -> lua::FileEntry* {
+ lua::FileEntry* entry = *reinterpret_cast<lua::FileEntry**>(
+ luaL_checkudata(L, stack_pos, kFileEntryMetatable));
+ return entry;
}
auto check_file_iterator(lua_State* L, int stack_pos) -> lua::FileIterator* {
@@ -56,7 +48,7 @@ static auto push_iterator(lua_State* state, const lua::FileIterator& it)
-> void {
lua::FileIterator** data = reinterpret_cast<lua::FileIterator**>(
lua_newuserdata(state, sizeof(uintptr_t)));
- *data = new lua::FileIterator(it); // TODO...
+ *data = new lua::FileIterator(it);
luaL_setmetatable(state, kFileIteratorMetatable);
}
@@ -108,45 +100,48 @@ static const struct luaL_Reg kFileIteratorFuncs[] = {{"next", fs_iterate},
{NULL, NULL}};
static auto file_entry_path(lua_State* state) -> int {
- LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>(
- luaL_checkudata(state, 1, kFileEntryMetatable));
- lua_pushlstring(state, data->path, data->path_size);
+ lua::FileEntry* entry = check_file_entry(state, 1);
+ lua_pushlstring(state, entry->filepath.c_str(), entry->filepath.size());
return 1;
}
static auto file_entry_is_dir(lua_State* state) -> int {
- LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>(
- luaL_checkudata(state, 1, kFileEntryMetatable));
- lua_pushboolean(state, data->isDirectory);
+ lua::FileEntry* entry = check_file_entry(state, 1);
+ lua_pushboolean(state, entry->isDirectory);
return 1;
}
static auto file_entry_is_hidden(lua_State* state) -> int {
- LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>(
- luaL_checkudata(state, 1, kFileEntryMetatable));
- lua_pushboolean(state, data->isHidden);
+ lua::FileEntry* entry = check_file_entry(state, 1);
+ lua_pushboolean(state, entry->isHidden);
+ return 1;
+}
+
+static auto file_entry_name(lua_State* state) -> int {
+ lua::FileEntry* entry = check_file_entry(state, 1);
+ lua_pushlstring(state, entry->name.c_str(), entry->name.size());
return 1;
}
-static auto file_entry_is_track(lua_State* state) -> int {
- LuaFileEntry* data = reinterpret_cast<LuaFileEntry*>(
- luaL_checkudata(state, 1, kFileEntryMetatable));
- lua_pushboolean(state, data->isTrack);
+static auto file_entry_gc(lua_State* state) -> int {
+ lua::FileEntry* entry = check_file_entry(state, 1);
+ delete entry;
return 1;
}
static const struct luaL_Reg kFileEntryFuncs[] = {{"filepath", file_entry_path},
+ {"name", file_entry_name},
{"is_directory", file_entry_is_dir},
{"is_hidden", file_entry_is_hidden},
- {"is_track", file_entry_is_track},
- {"__tostring", file_entry_path},
+ {"__tostring", file_entry_name},
+ {"__gc", file_entry_gc},
{NULL, NULL}};
static auto fs_new_iterator(lua_State* state) -> int {
// Takes a filepath as a string and returns a new FileIterator
// on that directory
std::string filepath = luaL_checkstring(state, -1);
- lua::FileIterator iter(filepath);
+ lua::FileIterator iter(filepath, false);
push_iterator(state, iter);
return 1;
}
diff --git a/src/tangara/lua/lua_queue.cpp b/src/tangara/lua/lua_queue.cpp
index bc393aa5..7eb32c62 100644
--- a/src/tangara/lua/lua_queue.cpp
+++ b/src/tangara/lua/lua_queue.cpp
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
+#include "audio/audio_events.hpp"
#include "lua/lua_database.hpp"
#include <memory>
@@ -39,6 +40,14 @@ static auto queue_add(lua_State* state) -> int {
audio::TrackQueue& queue = instance->services().track_queue();
queue.append(id);
});
+ } else if (lua_isstring(state, 1)) {
+ size_t len;
+ const char* str = luaL_checklstring(state, 1, &len);
+ std::string path{str, len};
+ instance->services().bg_worker().Dispatch<void>([=]() {
+ audio::TrackQueue& queue = instance->services().track_queue();
+ queue.append(path);
+ });
} else {
database::Iterator* it = db_check_iterator(state, 1);
instance->services().bg_worker().Dispatch<void>([=]() {
@@ -57,9 +66,24 @@ static auto queue_clear(lua_State* state) -> int {
return 0;
}
-static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add},
- {"clear", queue_clear},
- {NULL, NULL}};
+static auto queue_open_playlist(lua_State* state) -> int {
+ Bridge* instance = Bridge::Get(state);
+ audio::TrackQueue& queue = instance->services().track_queue();
+ size_t len = 0;
+ const char* str = luaL_checklstring(state, 1, &len);
+ if (!str) {
+ return 0;
+ }
+ queue.clear();
+ queue.openPlaylist(str);
+ return 0;
+}
+
+static const struct luaL_Reg kQueueFuncs[] = {
+ {"add", queue_add},
+ {"clear", queue_clear},
+ {"open_playlist", queue_open_playlist},
+ {NULL, NULL}};
static auto lua_queue(lua_State* state) -> int {
luaL_newlib(state, kQueueFuncs);
diff --git a/src/tangara/lua/lua_screen.cpp b/src/tangara/lua/lua_screen.cpp
index 8d87eebd..6bb26ec1 100644
--- a/src/tangara/lua/lua_screen.cpp
+++ b/src/tangara/lua/lua_screen.cpp
@@ -56,9 +56,9 @@ static auto screen_true(lua_State* state) -> int {
}
static const struct luaL_Reg kScreenFuncs[] = {
- {"new", screen_new}, {"createUi", screen_noop},
- {"onShown", screen_noop}, {"onHidden", screen_noop},
- {"canPop", screen_true}, {NULL, NULL}};
+ {"new", screen_new}, {"create_ui", screen_noop},
+ {"on_show", screen_noop}, {"on_hide", screen_noop},
+ {"can_pop", screen_true}, {NULL, NULL}};
static auto lua_screen(lua_State* state) -> int {
luaL_newlib(state, kScreenFuncs);
diff --git a/src/tangara/lua/lua_theme.cpp b/src/tangara/lua/lua_theme.cpp
index 5edde104..03578778 100644
--- a/src/tangara/lua/lua_theme.cpp
+++ b/src/tangara/lua/lua_theme.cpp
@@ -75,8 +75,41 @@ static auto set_theme(lua_State* L) -> int {
return 0;
}
+
+static auto load_theme(lua_State* L) -> int {
+ std::string filename = luaL_checkstring(L, -1);
+ // Set the theme filename in non-volatile storage
+ Bridge* instance = Bridge::Get(L);
+ // Load the theme using lua
+ auto status = luaL_loadfile(L, filename.c_str());
+ if (status != LUA_OK) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+ status = lua::CallProtected(L, 0, 1);
+ if (status == LUA_OK) {
+ ui::themes::Theme::instance()->Reset();
+ set_theme(L);
+ instance->services().nvs().InterfaceTheme(filename);
+ lua_pushboolean(L, true);
+ } else {
+ lua_pushboolean(L, false);
+ }
+
+ return 1;
+}
+
+static auto theme_filename(lua_State* L) -> int {
+ Bridge* instance = Bridge::Get(L);
+ auto file = instance->services().nvs().InterfaceTheme().value_or("/lua/theme_light.lua");
+ lua_pushstring(L, file.c_str());
+ return 1;
+}
+
static const struct luaL_Reg kThemeFuncs[] = {{"set", set_theme},
{"set_style", set_style},
+ {"load_theme", load_theme},
+ {"theme_filename", theme_filename},
{NULL, NULL}};
static auto lua_theme(lua_State* L) -> int {
diff --git a/src/tangara/lua/property.cpp b/src/tangara/lua/property.cpp
index 2b93809d..1be1fd2d 100644
--- a/src/tangara/lua/property.cpp
+++ b/src/tangara/lua/property.cpp
@@ -289,13 +289,14 @@ static void pushTrack(lua_State* L, const audio::TrackInfo& track) {
lua_settable(L, -3);
}
-static void pushDevice(lua_State* L, const drivers::bluetooth::Device& dev) {
+static void pushDevice(lua_State* L,
+ const drivers::bluetooth::MacAndName& dev) {
lua_createtable(L, 0, 4);
lua_pushliteral(L, "address");
auto* mac = reinterpret_cast<drivers::bluetooth::mac_addr_t*>(
lua_newuserdata(L, sizeof(drivers::bluetooth::mac_addr_t)));
- *mac = dev.address;
+ *mac = dev.mac;
lua_rawset(L, -3);
// What I just did there was perfectly safe. Look, I can prove it:
@@ -308,14 +309,8 @@ static void pushDevice(lua_State* L, const drivers::bluetooth::Device& dev) {
lua_pushlstring(L, dev.name.data(), dev.name.size());
lua_rawset(L, -3);
- // FIXME: This field deserves a little more structure.
- lua_pushliteral(L, "class");
- lua_pushinteger(L, dev.class_of_device);
- lua_rawset(L, -3);
-
- lua_pushliteral(L, "signal_strength");
- lua_pushinteger(L, dev.signal_strength);
- lua_rawset(L, -3);
+ // FIXME: Plumbing through device classes to here could be useful if we ever
+ // want to show cute little icons.
}
auto Property::pushValue(lua_State& s) -> int {
@@ -332,10 +327,12 @@ auto Property::pushValue(lua_State& s) -> int {
lua_pushstring(&s, arg.c_str());
} else if constexpr (std::is_same_v<T, audio::TrackInfo>) {
pushTrack(&s, arg);
- } else if constexpr (std::is_same_v<T, drivers::bluetooth::Device>) {
+ } else if constexpr (std::is_same_v<T,
+ drivers::bluetooth::MacAndName>) {
pushDevice(&s, arg);
} else if constexpr (std::is_same_v<
- T, std::vector<drivers::bluetooth::Device>>) {
+ T,
+ std::vector<drivers::bluetooth::MacAndName>>) {
lua_createtable(&s, arg.size(), 0);
size_t i = 1;
for (const auto& dev : arg) {
@@ -364,48 +361,44 @@ auto popRichType(lua_State* L) -> LuaValue {
lua_pushliteral(L, "name");
lua_gettable(L, -2);
- std::pmr::string name = lua_tostring(L, -1);
+ std::string name = lua_tostring(L, -1);
lua_pop(L, 1);
- return drivers::bluetooth::Device{
- .address = mac,
- .name = name,
- .class_of_device = 0,
- .signal_strength = 0,
- };
+ return drivers::bluetooth::MacAndName{.mac = mac, .name = name};
}
return std::monostate{};
}
auto Property::popValue(lua_State& s) -> bool {
- LuaValue new_val;
- switch (lua_type(&s, 2)) {
- case LUA_TNIL:
- new_val = std::monostate{};
- break;
- case LUA_TNUMBER:
- if (lua_isinteger(&s, 2)) {
- new_val = lua_tointeger(&s, 2);
- } else {
- new_val = static_cast<lua_Integer>(std::round(lua_tonumber(&s, 2)));
- }
- break;
- case LUA_TBOOLEAN:
- new_val = static_cast<bool>(lua_toboolean(&s, 2));
- break;
- case LUA_TSTRING:
- new_val = lua_tostring(&s, 2);
- break;
- default:
- if (lua_istable(&s, 2)) {
- new_val = popRichType(&s);
- if (std::holds_alternative<std::monostate>(new_val)) {
+ LuaValue new_val{std::monostate{}};
+ if (lua_gettop(&s) >= 2) {
+ switch (lua_type(&s, 2)) {
+ case LUA_TNIL:
+ break;
+ case LUA_TNUMBER:
+ if (lua_isinteger(&s, 2)) {
+ new_val = lua_tointeger(&s, 2);
+ } else {
+ new_val = static_cast<lua_Integer>(std::round(lua_tonumber(&s, 2)));
+ }
+ break;
+ case LUA_TBOOLEAN:
+ new_val = static_cast<bool>(lua_toboolean(&s, 2));
+ break;
+ case LUA_TSTRING:
+ new_val = lua_tostring(&s, 2);
+ break;
+ default:
+ if (lua_istable(&s, 2)) {
+ new_val = popRichType(&s);
+ if (std::holds_alternative<std::monostate>(new_val)) {
+ return false;
+ }
+ } else {
return false;
}
- } else {
- return false;
- }
+ }
}
return set(new_val);
diff --git a/src/tangara/lua/property.hpp b/src/tangara/lua/property.hpp
index 9f925766..d45821bd 100644
--- a/src/tangara/lua/property.hpp
+++ b/src/tangara/lua/property.hpp
@@ -24,8 +24,8 @@ using LuaValue = std::variant<std::monostate,
bool,
std::string,
audio::TrackInfo,
- drivers::bluetooth::Device,
- std::vector<drivers::bluetooth::Device>>;
+ drivers::bluetooth::MacAndName,
+ std::vector<drivers::bluetooth::MacAndName>>;
using LuaFunction = std::function<int(lua_State*)>;