270 lines
7.7 KiB
C++
270 lines
7.7 KiB
C++
#include <print>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
|
|
#include <RmlUi/Core.h>
|
|
#include <RmlUi/Debugger.h>
|
|
#include <RmlUi/Lua.h>
|
|
#include <RmlUi/Lua/Interpreter.h>
|
|
#include <RmlUi_Backend.h>
|
|
#include <RmlUi_Include_Windows.h>
|
|
#include <RmlUi_Include_GL3.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
extern "C" {
|
|
#include <lua.h>
|
|
#include <lauxlib.h>
|
|
#include <lualib.h>
|
|
}
|
|
|
|
#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;
|
|
}
|