summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-22 14:38:52 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-22 14:38:52 +1100
commit06aca259cbb84c41a002e5a93735b289cc2aa93a (patch)
tree3c3a6c09a7362ab95b0019f4bd4da56faf167187 /src
parentcd46d7bd203b69e6d163fd19e38600d9feae6e56 (diff)
downloadtangara-fw-06aca259cbb84c41a002e5a93735b289cc2aa93a.tar.gz
Add basic lua browser screen
Diffstat (limited to 'src')
-rw-r--r--src/database/database.cpp12
-rw-r--r--src/database/include/database.hpp1
-rw-r--r--src/lua/lua_database.cpp77
-rw-r--r--src/ui/ui_fsm.cpp15
4 files changed, 72 insertions, 33 deletions
diff --git a/src/database/database.cpp b/src/database/database.cpp
index 88ae7bbe..dad983d0 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -846,8 +846,7 @@ auto IndexRecord::Expand(std::size_t page_size) const
if (track_) {
return {};
}
- IndexKey::Header new_header = ExpandHeader(key_.header, key_.item);
- std::string new_prefix = EncodeIndexPrefix(new_header);
+ std::string new_prefix = EncodeIndexPrefix(ExpandHeader());
return Continuation{
.prefix = {new_prefix.data(), new_prefix.size()},
.start_key = {new_prefix.data(), new_prefix.size()},
@@ -857,6 +856,10 @@ auto IndexRecord::Expand(std::size_t page_size) const
};
}
+auto IndexRecord::ExpandHeader() const -> IndexKey::Header {
+ return ::database::ExpandHeader(key_.header, key_.item);
+}
+
Iterator::Iterator(std::weak_ptr<Database> db, const IndexInfo& idx)
: db_(db), pos_mutex_(), current_pos_(), prev_pos_() {
std::string prefix = EncodeIndexPrefix(
@@ -887,6 +890,11 @@ auto Iterator::Next(Callback cb) -> void {
db->dbGetPage<IndexRecord>(*current_pos_)};
prev_pos_ = current_pos_;
current_pos_ = res->next_page();
+ if (!res || res->values().empty() || !res->values()[0]) {
+ ESP_LOGI(kTag, "dropping empty result");
+ InvokeNull(cb);
+ return;
+ }
std::invoke(cb, *res->values()[0]);
});
}
diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp
index 63014bed..e18701eb 100644
--- a/src/database/include/database.hpp
+++ b/src/database/include/database.hpp
@@ -80,6 +80,7 @@ class IndexRecord {
auto track() const -> std::optional<TrackId>;
auto Expand(std::size_t) const -> std::optional<Continuation>;
+ auto ExpandHeader() const -> IndexKey::Header;
private:
IndexKey key_;
diff --git a/src/lua/lua_database.cpp b/src/lua/lua_database.cpp
index d8ae86f6..79916115 100644
--- a/src/lua/lua_database.cpp
+++ b/src/lua/lua_database.cpp
@@ -20,7 +20,9 @@
#include "event_queue.hpp"
#include "index.hpp"
#include "property.hpp"
+#include "records.hpp"
#include "service_locator.hpp"
+#include "track.hpp"
#include "ui_events.hpp"
namespace lua {
@@ -55,6 +57,42 @@ static auto indexes(lua_State* state) -> int {
static const struct luaL_Reg kDatabaseFuncs[] = {{"indexes", indexes},
{NULL, NULL}};
+/*
+ * Struct to be used as userdata for the Lua representation of database records.
+ * In order to push these large values into PSRAM as much as possible, memory
+ * for these is allocated and managed by Lua. This struct must therefore be
+ * trivially copyable.
+ */
+struct LuaRecord {
+ database::TrackId id_or_zero;
+ database::IndexKey::Header header_at_next_depth;
+ size_t text_size;
+ char text[];
+};
+
+static_assert(std::is_trivially_copyable_v<LuaRecord> == true);
+
+static auto push_lua_record(lua_State* L, const database::IndexRecord& r)
+ -> void {
+ // Bake out the text into something concrete.
+ auto text = r.text().value_or("");
+
+ // Create and init the userdata.
+ LuaRecord* record = reinterpret_cast<LuaRecord*>(
+ lua_newuserdata(L, sizeof(LuaRecord) + text.size()));
+ luaL_setmetatable(L, kDbRecordMetatable);
+
+ // Init all the fields
+ *record = {
+ .id_or_zero = r.track().value_or(0),
+ .header_at_next_depth = r.ExpandHeader(),
+ .text_size = text.size(),
+ };
+
+ // Copy the string data across.
+ std::memcpy(record->text, text.data(), text.size());
+}
+
static auto db_iterate(lua_State* state) -> int {
luaL_checktype(state, 1, LUA_TFUNCTION);
int callback_ref = luaL_ref(state, LUA_REGISTRYINDEX);
@@ -66,11 +104,7 @@ static auto db_iterate(lua_State* state) -> int {
events::Ui().RunOnTask([=]() {
lua_rawgeti(state, LUA_REGISTRYINDEX, callback_ref);
if (res) {
- database::IndexRecord** record =
- reinterpret_cast<database::IndexRecord**>(
- lua_newuserdata(state, sizeof(uintptr_t)));
- *record = new database::IndexRecord(*res);
- luaL_setmetatable(state, kDbRecordMetatable);
+ push_lua_record(state, *res);
} else {
lua_pushnil(state);
}
@@ -105,40 +139,37 @@ static auto push_iterator(
lua_pushcclosure(state, db_iterate, 1);
}
+
static auto record_text(lua_State* state) -> int {
- database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>(
+ LuaRecord* data = reinterpret_cast<LuaRecord*>(
luaL_checkudata(state, 1, kDbRecordMetatable));
- lua_pushstring(state,
- data->text().value_or("[tell jacqueline u saw this]").c_str());
+ lua_pushlstring(state, data->text, data->text_size);
return 1;
}
static auto record_contents(lua_State* state) -> int {
- database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>(
+ LuaRecord* data = reinterpret_cast<LuaRecord*>(
luaL_checkudata(state, 1, kDbRecordMetatable));
- if (data->track()) {
- lua_pushinteger(state, *data->track());
+ if (data->id_or_zero) {
+ lua_pushinteger(state, data->id_or_zero);
} else {
- push_iterator(state, data->Expand(1).value());
+ std::string p = database::EncodeIndexPrefix(data->header_at_next_depth);
+ push_iterator(state, database::Continuation{
+ .prefix = {p.data(), p.size()},
+ .start_key = {p.data(), p.size()},
+ .forward = true,
+ .was_prev_forward = true,
+ .page_size = 1,
+ });
}
return 1;
}
-static auto record_gc(lua_State* state) -> int {
- database::IndexRecord** data = reinterpret_cast<database::IndexRecord**>(
- luaL_checkudata(state, 1, kDbRecordMetatable));
- if (data != NULL) {
- delete *data;
- }
- return 0;
-}
-
static const struct luaL_Reg kDbRecordFuncs[] = {{"title", record_text},
{"contents", record_contents},
{"__tostring", record_text},
- {"__gc", record_gc},
{NULL, NULL}};
static auto index_name(lua_State* state) -> int {
@@ -207,4 +238,4 @@ auto RegisterDatabaseModule(lua_State* s) -> void {
lua_pop(s, 1);
}
-} // namespace lua \ No newline at end of file
+} // namespace lua
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index d5de53f0..ed0624df 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -219,6 +219,7 @@ void Lua::entry() {
{"pop", [&](lua_State* s) { return PopLuaScreen(s); }},
});
+ sCurrentScreen.reset();
sLua->RunScript("/lua/main.lua");
}
}
@@ -243,12 +244,6 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
// Store the reference for the table the constructor returned.
new_screen->SetObjRef(s);
- // Ensure that we don't pollute the new screen's group. We leave the luavgl
- // root alone.
- // FIXME: maybe we should set the luavgl root to some catch-all that throws
- // when anything is added to it? this may help catch bugs!
- lv_group_set_default(NULL);
-
// Finally, push the now-initialised screen as if it were a regular C++
// screen.
PushScreen(new_screen);
@@ -256,12 +251,16 @@ auto Lua::PushLuaScreen(lua_State* s) -> int {
return 0;
}
-auto Lua::PopLuaScreen(lua_State*) -> int {
+auto Lua::PopLuaScreen(lua_State* s) -> int {
PopScreen();
+ luavgl_set_root(s, sCurrentScreen->root());
+ lv_group_set_default(sCurrentScreen->group());
return 0;
}
-void Lua::exit() {}
+void Lua::exit() {
+ lv_group_set_default(NULL);
+}
void Lua::react(const internal::IndexSelected& ev) {
auto db = sServices->database().lock();