diff --git a/core/src/sandbox/timer_manager.cpp b/core/src/sandbox/timer_manager.cpp index a581bc4..c252913 100644 --- a/core/src/sandbox/timer_manager.cpp +++ b/core/src/sandbox/timer_manager.cpp @@ -2,6 +2,13 @@ #include #include +#ifdef __ANDROID__ +#include +#define TIMER_LOG(...) __android_log_print(ANDROID_LOG_INFO, "MosisOS", __VA_ARGS__) +#else +#include +#define TIMER_LOG(...) printf(__VA_ARGS__) +#endif namespace mosis { @@ -64,8 +71,12 @@ TimerId TimerManager::SetInterval(lua_State* L, const std::string& app_id, int callback_ref, int interval_ms) { std::lock_guard lock(m_mutex); + TIMER_LOG("[Timer] SetInterval called: app=%s, interval=%dms, ref=%d", + app_id.c_str(), interval_ms, callback_ref); + // Check per-app limit if (m_app_timer_counts[app_id] >= MAX_TIMERS_PER_APP) { + TIMER_LOG("[Timer] REJECTED: timer limit exceeded for %s", app_id.c_str()); luaL_unref(L, LUA_REGISTRYINDEX, callback_ref); return 0; } @@ -89,6 +100,9 @@ TimerId TimerManager::SetInterval(lua_State* L, const std::string& app_id, m_app_timer_counts[app_id]++; m_app_timer_ids[app_id].insert(timer.id); + TIMER_LOG("[Timer] Created interval timer id=%llu for %s (total: %zu)", + timer.id, app_id.c_str(), m_timers.size()); + return timer.id; } @@ -158,9 +172,13 @@ void TimerManager::ClearAppTimers(const std::string& app_id) { void TimerManager::FireTimer(Timer& timer) { if (timer.cancelled || timer.callback_ref == LUA_NOREF || !timer.L) { + TIMER_LOG("[Timer] FireTimer skipped: cancelled=%d, ref=%d, L=%p", + timer.cancelled, timer.callback_ref, timer.L); return; } + TIMER_LOG("[Timer] Firing timer id=%llu for %s", timer.id, timer.app_id.c_str()); + lua_State* L = timer.L; // Get the callback from registry @@ -170,10 +188,15 @@ void TimerManager::FireTimer(Timer& timer) { // Call the callback with protected call int result = lua_pcall(L, 0, 0, 0); if (result != LUA_OK) { - // Log error but don't propagate + // Log the error + const char* err = lua_tostring(L, -1); + TIMER_LOG("[Timer] ERROR in callback: %s", err ? err : "(unknown)"); lua_pop(L, 1); + } else { + TIMER_LOG("[Timer] Callback completed successfully"); } } else { + TIMER_LOG("[Timer] ERROR: callback ref is not a function!"); lua_pop(L, 1); } } @@ -209,6 +232,12 @@ int TimerManager::ProcessTimers() { } } } + + // Log only when we have timers to fire (to avoid spam) + if (!to_fire.empty()) { + TIMER_LOG("[Timer] ProcessTimers: %zu ready to fire out of %zu total", + to_fire.size(), m_timers.size()); + } } // Fire timers outside the lock to allow callbacks to create new timers diff --git a/src/main/cpp/kernel.cpp b/src/main/cpp/kernel.cpp index 5e065c0..eb83126 100644 --- a/src/main/cpp/kernel.cpp +++ b/src/main/cpp/kernel.cpp @@ -62,6 +62,9 @@ struct FileHandleWrapper { class AssetFilesInterface : public Rml::FileInterface { + // Track base path of currently loading document for relative path resolution + std::string m_current_document_base; + public: static AssetFilesInterface& Instance() { @@ -69,6 +72,22 @@ public: return instance; } + // Set the base path for relative resource resolution (called before LoadDocument) + void SetDocumentBasePath(const std::string& doc_path) { + // Extract directory from full document path + size_t last_slash = doc_path.find_last_of("/\\"); + if (last_slash != std::string::npos) { + m_current_document_base = doc_path.substr(0, last_slash + 1); + } else { + m_current_document_base = ""; + } + Logger::Log(std::format("Document base path set to: {}", m_current_document_base)); + } + + void ClearDocumentBasePath() { + m_current_document_base = ""; + } + // Check if path is a filesystem path (starts with / or file://) static bool IsFilesystemPath(const Rml::String& path) { return !path.empty() && (path[0] == '/' || path.rfind("file://", 0) == 0); @@ -82,15 +101,37 @@ public: return path; } + // Resolve a path - if it's relative and we have a filesystem base, resolve against it + Rml::String ResolvePath(const Rml::String& path) const { + // If already absolute or explicitly a filesystem/asset path, return as-is + if (IsFilesystemPath(path)) { + return path; + } + + // If we have a filesystem base path and this is a relative path, resolve it + if (!m_current_document_base.empty() && IsFilesystemPath(m_current_document_base)) { + Rml::String resolved = m_current_document_base + path; + Logger::Log(std::format("Resolved relative path '{}' to '{}'", path, resolved)); + return resolved; + } + + // Otherwise, treat as asset path + return path; + } + Rml::FileHandle Open(const Rml::String &path) override { + // Resolve relative paths against current document base + Rml::String resolved_path = ResolvePath(path); + auto* wrapper = new FileHandleWrapper(); - if (IsFilesystemPath(path)) { + if (IsFilesystemPath(resolved_path)) { // Filesystem path - Rml::String fs_path = GetFilesystemPath(path); + Rml::String fs_path = GetFilesystemPath(resolved_path); auto* file = new std::ifstream(fs_path, std::ios::binary); if (!file->is_open()) { + Logger::Log(std::format("Open failed for filesystem: {}", fs_path)); delete file; delete wrapper; return 0; @@ -103,8 +144,9 @@ public: } else { // Asset path AAssetManager* am = AssetsManager::Native(); - AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER); + AAsset* asset = AAssetManager_open(am, resolved_path.c_str(), AASSET_MODE_BUFFER); if (!asset) { + Logger::Log(std::format("Open failed for asset: {}", resolved_path)); delete wrapper; return 0; } @@ -185,28 +227,43 @@ public: bool LoadFile(const Rml::String &path, Rml::String &out_data) override { - if (IsFilesystemPath(path)) { + Logger::Log(std::format("LoadFile requested: {}", path)); + + // Resolve relative paths against current document base + Rml::String resolved_path = ResolvePath(path); + + if (IsFilesystemPath(resolved_path)) { // Load from filesystem - Rml::String fs_path = GetFilesystemPath(path); + Rml::String fs_path = GetFilesystemPath(resolved_path); + Logger::Log(std::format(" -> Loading from filesystem: {}", fs_path)); std::ifstream file(fs_path, std::ios::binary | std::ios::ate); - if (!file.is_open()) return false; + if (!file.is_open()) { + Logger::Log(std::format(" -> FAILED to open file: {}", fs_path)); + return false; + } size_t size = file.tellg(); file.seekg(0, std::ios::beg); out_data.resize(size); file.read(out_data.data(), size); + Logger::Log(std::format(" -> Loaded {} bytes from filesystem", size)); return true; } else { // Load from assets + Logger::Log(std::format(" -> Loading from assets: {}", resolved_path)); AAssetManager* am = AssetsManager::Native(); - AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER); - if (!asset) return false; + AAsset* asset = AAssetManager_open(am, resolved_path.c_str(), AASSET_MODE_BUFFER); + if (!asset) { + Logger::Log(std::format(" -> FAILED to load from assets: {}", resolved_path)); + return false; + } out_data.resize(AAsset_getLength(asset)); auto data_ptr = static_cast(AAsset_getBuffer(asset)); std::span data = std::span(data_ptr, out_data.size()); std::ranges::copy(data, out_data.begin()); AAsset_close(asset); + Logger::Log(std::format(" -> Loaded {} bytes from assets", out_data.size())); return true; } } @@ -261,6 +318,10 @@ static int LuaLoadScreen(lua_State* L) return 1; } + // Set document base path for relative resource resolution (scripts, stylesheets) + // This is crucial for filesystem documents to load their scripts correctly + AssetFilesInterface::Instance().SetDocumentBasePath(path); + // Unload current document if (g_document) { @@ -282,6 +343,9 @@ static int LuaLoadScreen(lua_State* L) lua_pushboolean(L, false); } + // Clear base path after document is fully loaded + AssetFilesInterface::Instance().ClearDocumentBasePath(); + return 1; } diff --git a/test-apps/com.mosis.sandbox-test/app.lua b/test-apps/com.mosis.sandbox-test/app.lua index 264a92a..98a3a82 100644 --- a/test-apps/com.mosis.sandbox-test/app.lua +++ b/test-apps/com.mosis.sandbox-test/app.lua @@ -16,11 +16,12 @@ local function getDocument() currentDocument = document return document end - -- Try to get from RmlUi context - if rmlui and rmlui.contexts and rmlui.contexts.main then - local ctx = rmlui.contexts.main - -- documents is a proxy, iterate to get first doc - if ctx.documents then + -- Try to get from RmlUi context (our context is named "default") + if rmlui and rmlui.contexts then + -- Try both "default" and "main" context names + local ctx = rmlui.contexts["default"] or rmlui.contexts.main + if ctx and ctx.documents then + -- documents is a proxy, iterate to get first doc for i, doc in ipairs(ctx.documents) do if doc then currentDocument = doc @@ -155,7 +156,7 @@ function testCrypto() -- Test SHA256 if success then - local hash = crypto.sha256("hello world") + local hash = crypto.hash("sha256", "hello world") if hash then log("SHA256: " .. hash:sub(1, 32) .. "...") else