#include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include } #include "src/data_models.h" #include "src/system_interface.h" #include "src/utils.h" // Global app state struct AppState { Rml::Context* context = nullptr; Rml::ElementDocument* document = nullptr; std::filesystem::path assets_path; } g_app; static LoggingSystemInterface* g_logging_interface = nullptr; static WindowsFileInterface* g_file_interface = nullptr; // Lua function: loadScreen(path) - loads a new RML document int lua_loadScreen(lua_State* L) { std::println("lua_loadScreen called!"); const char* path = luaL_checkstring(L, 1); std::println("Loading: {}", path); std::filesystem::path full_path = g_app.assets_path / path; if (!std::filesystem::exists(full_path)) { std::println("Screen not found: {}", full_path.generic_string()); lua_pushboolean(L, false); return 1; } // Unload current document if (g_app.document) { g_app.context->UnloadDocument(g_app.document); g_app.document = nullptr; } // Load new document with absolute path std::string full_path_str = full_path.generic_string(); std::println("Full path: {}", full_path_str); g_app.document = g_app.context->LoadDocument(full_path_str); if (g_app.document) { g_app.document->Show(); std::println("Loaded screen: {}", path); lua_pushboolean(L, true); } else { std::println("Failed to load screen: {}", path); lua_pushboolean(L, false); } return 1; } // Register custom Lua functions void registerLuaFunctions() { lua_State* L = Rml::Lua::Interpreter::GetLuaState(); lua_pushcfunction(L, lua_loadScreen); lua_setglobal(L, "loadScreen"); std::println("Registered Lua functions"); } int main(const int argc, const char* argv[]) { constexpr int window_width = 540; constexpr int window_height = 960; if (argc < 2) { std::println("Usage: mosis-designer file.rml [--dump]"); return EXIT_FAILURE; } const std::filesystem::path file = argv[1]; const bool dump_mode = (argc >= 3 && std::string(argv[2]) == "--dump"); if (!std::filesystem::exists(file)) { std::println("File does not exist"); return EXIT_FAILURE; } if (!Backend::Initialize("Mosis Designer", window_width, window_height, true)) { return EXIT_FAILURE; } // Use custom interfaces g_logging_interface = new LoggingSystemInterface(Backend::GetSystemInterface()); g_file_interface = new WindowsFileInterface(); Rml::SetSystemInterface(g_logging_interface); Rml::SetFileInterface(g_file_interface); Rml::SetRenderInterface(Backend::GetRenderInterface()); Rml::Initialise(); Rml::Lua::Initialise(); std::println("RmlUi and Lua initialized"); g_app.context = Rml::CreateContext("main", Rml::Vector2i(window_width, window_height)); if (!g_app.context) { Rml::Shutdown(); Backend::Shutdown(); return EXIT_FAILURE; } // Initialize sample data and data models initializeSampleData(); setupDataModels(g_app.context); // Register custom Lua functions registerLuaFunctions(); // Find the assets folder by checking for fonts/ subdirectory g_app.assets_path = std::filesystem::absolute(file.parent_path()); std::filesystem::path fonts_path; while (!g_app.assets_path.empty() && g_app.assets_path.has_parent_path()) { fonts_path = g_app.assets_path / "fonts"; if (std::filesystem::exists(fonts_path) && std::filesystem::is_directory(fonts_path)) { bool has_fonts = false; for (const auto& entry : std::filesystem::directory_iterator(fonts_path)) { if (entry.path().extension() == ".ttf") { has_fonts = true; break; } } if (has_fonts) break; } g_app.assets_path = g_app.assets_path.parent_path(); } load_fonts(fonts_path); // Load document with absolute path std::filesystem::path abs_file = std::filesystem::absolute(file); std::string abs_file_str = abs_file.generic_string(); std::println("Loading document: {}", abs_file_str); std::println("Assets path: {}", g_app.assets_path.generic_string()); g_app.document = g_app.context->LoadDocument(abs_file_str); if (g_app.document) { g_app.document->Show(); std::println("Document loaded successfully"); } else { std::println("Failed to load document!"); } // Dump mode: render and capture screenshot if (dump_mode) { OffscreenFBO fbo; if (!fbo.create(window_width, window_height)) { std::println("Failed to create FBO"); Rml::Shutdown(); Backend::Shutdown(); return EXIT_FAILURE; } // First render to default framebuffer to initialize everything Backend::ProcessEvents(g_app.context); g_app.context->Update(); Backend::BeginFrame(); g_app.context->Render(); Backend::PresentFrame(); // Create dump folder if it doesn't exist auto dump_folder = std::filesystem::absolute("dump"); std::filesystem::create_directories(dump_folder); // Write element dump std::ofstream dump_file((dump_folder / "element_dump.txt").string()); if (dump_file) { dump_file << "Window: " << window_width << "x" << window_height << "\n"; dump_file << "\n=== Element Layout ===\n"; DumpElementTree(g_app.document, dump_file); dump_file.close(); } // Bind FBO and render fbo.bind(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); g_app.context->Update(); g_app.context->Render(); auto pixels = fbo.readPixels(); fbo.destroy(); SavePNG(dump_folder / "screenshot.png", pixels, window_width, window_height); glBindFramebuffer(GL_FRAMEBUFFER, 0); Rml::Shutdown(); Backend::Shutdown(); return EXIT_SUCCESS; } // Watch for file changes const HANDLE hNotif = FindFirstChangeNotification(g_app.assets_path.c_str(), TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE); bool running = true; while (running) { if (const DWORD wait_result = WaitForSingleObject(hNotif, 100); wait_result == WAIT_OBJECT_0) { if (g_app.document) g_app.context->UnloadDocument(g_app.document); g_app.context->Update(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); g_app.document = g_app.context->LoadDocument(abs_file_str); if (g_app.document) { g_app.document->ReloadStyleSheet(); g_app.document->Show(); } FindNextChangeNotification(hNotif); } running = Backend::ProcessEvents(g_app.context); g_app.context->Update(); Backend::BeginFrame(); g_app.context->Render(); Backend::PresentFrame(); } delete g_logging_interface; delete g_file_interface; Rml::Shutdown(); Backend::Shutdown(); return EXIT_SUCCESS; }