save state
This commit is contained in:
66
designer/src/desktop_file_interface.cpp
Normal file
66
designer/src/desktop_file_interface.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// D:\Dev\Mosis\MosisService\designer\src\desktop_file_interface.cpp
|
||||
#include "desktop_file_interface.h"
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace mosis {
|
||||
|
||||
void DesktopFileInterface::SetAssetsPath(const std::string& path) {
|
||||
m_assets_path = path;
|
||||
// Ensure trailing separator
|
||||
if (!m_assets_path.empty() && m_assets_path.back() != '/' && m_assets_path.back() != '\\') {
|
||||
m_assets_path += '/';
|
||||
}
|
||||
}
|
||||
|
||||
std::string DesktopFileInterface::ResolvePath(const std::string& path) const {
|
||||
// If path is absolute, use it directly
|
||||
if (fs::path(path).is_absolute()) {
|
||||
return path;
|
||||
}
|
||||
// Otherwise, prepend assets path
|
||||
return m_assets_path + path;
|
||||
}
|
||||
|
||||
Rml::FileHandle DesktopFileInterface::Open(const Rml::String& path) {
|
||||
std::string resolved = ResolvePath(path);
|
||||
FILE* file = fopen(resolved.c_str(), "rb");
|
||||
return reinterpret_cast<Rml::FileHandle>(file);
|
||||
}
|
||||
|
||||
void DesktopFileInterface::Close(Rml::FileHandle file) {
|
||||
if (file) {
|
||||
fclose(reinterpret_cast<FILE*>(file));
|
||||
}
|
||||
}
|
||||
|
||||
size_t DesktopFileInterface::Read(void* buffer, size_t size, Rml::FileHandle file) {
|
||||
if (!file) return 0;
|
||||
return fread(buffer, 1, size, reinterpret_cast<FILE*>(file));
|
||||
}
|
||||
|
||||
bool DesktopFileInterface::Seek(Rml::FileHandle file, long offset, int origin) {
|
||||
if (!file) return false;
|
||||
return fseek(reinterpret_cast<FILE*>(file), offset, origin) == 0;
|
||||
}
|
||||
|
||||
size_t DesktopFileInterface::Tell(Rml::FileHandle file) {
|
||||
if (!file) return 0;
|
||||
return static_cast<size_t>(ftell(reinterpret_cast<FILE*>(file)));
|
||||
}
|
||||
|
||||
size_t DesktopFileInterface::Length(Rml::FileHandle file) {
|
||||
if (!file) return 0;
|
||||
|
||||
FILE* f = reinterpret_cast<FILE*>(file);
|
||||
long current = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
long length = ftell(f);
|
||||
fseek(f, current, SEEK_SET);
|
||||
|
||||
return static_cast<size_t>(length);
|
||||
}
|
||||
|
||||
} // namespace mosis
|
||||
34
designer/src/desktop_file_interface.h
Normal file
34
designer/src/desktop_file_interface.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// D:\Dev\Mosis\MosisService\designer\src\desktop_file_interface.h
|
||||
#pragma once
|
||||
|
||||
#include <RmlUi/Core/FileInterface.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mosis {
|
||||
|
||||
class DesktopFileInterface : public Rml::FileInterface {
|
||||
public:
|
||||
DesktopFileInterface() = default;
|
||||
~DesktopFileInterface() override = default;
|
||||
|
||||
// Asset path management
|
||||
void SetAssetsPath(const std::string& path);
|
||||
std::string GetAssetsPath() const { return m_assets_path; }
|
||||
|
||||
// RmlUi FileInterface
|
||||
Rml::FileHandle Open(const Rml::String& path) override;
|
||||
void Close(Rml::FileHandle file) override;
|
||||
size_t Read(void* buffer, size_t size, Rml::FileHandle file) override;
|
||||
bool Seek(Rml::FileHandle file, long offset, int origin) override;
|
||||
size_t Tell(Rml::FileHandle file) override;
|
||||
size_t Length(Rml::FileHandle file) override;
|
||||
|
||||
private:
|
||||
std::string ResolvePath(const std::string& path) const;
|
||||
|
||||
std::string m_assets_path;
|
||||
};
|
||||
|
||||
} // namespace mosis
|
||||
@@ -1,89 +1,122 @@
|
||||
// Desktop platform implementation (simplified - uses RmlUi backend for graphics)
|
||||
// D:\Dev\Mosis\MosisService\designer\src\desktop_platform.cpp
|
||||
// Include glad BEFORE GLFW
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "desktop_platform.h"
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
// Note: Graphics context and rendering is handled by RmlUi's backend.
|
||||
// This platform implementation provides additional utilities and state management.
|
||||
namespace mosis {
|
||||
|
||||
namespace mosis::desktop {
|
||||
// DesktopPlatform implementation
|
||||
|
||||
DesktopPlatform::DesktopPlatform()
|
||||
: m_file_interface(std::make_unique<DesktopFileInterface>())
|
||||
DesktopPlatform::DesktopPlatform(GLFWwindow* window, uint32_t width, uint32_t height)
|
||||
: m_window(window)
|
||||
, m_width(width)
|
||||
, m_height(height)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
m_start_time = std::chrono::duration<double>(now.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
DesktopPlatform::~DesktopPlatform() = default;
|
||||
|
||||
bool DesktopPlatform::Initialize(uint32_t width, uint32_t height, const char* title) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
// Graphics initialization is done by RmlUi Backend
|
||||
return true;
|
||||
}
|
||||
|
||||
void DesktopPlatform::Shutdown() {
|
||||
// Graphics shutdown is done by RmlUi Backend
|
||||
}
|
||||
|
||||
std::unique_ptr<IGraphicsContext> DesktopPlatform::CreateGraphicsContext() {
|
||||
// Graphics context is managed by RmlUi Backend
|
||||
// On desktop, GLFW manages the context, so we don't need a separate wrapper
|
||||
// Return nullptr to indicate context is already managed
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IRenderTarget> DesktopPlatform::CreateRenderTarget(uint32_t width, uint32_t height) {
|
||||
// Render targets are managed by RmlUi Backend
|
||||
auto target = std::make_unique<DesktopRenderTarget>();
|
||||
if (target->Create(width, height)) {
|
||||
return target;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IFileInterface& DesktopPlatform::GetFileInterface() {
|
||||
return *m_file_interface;
|
||||
Rml::FileInterface& DesktopPlatform::GetFileInterface() {
|
||||
return m_file_interface;
|
||||
}
|
||||
|
||||
void DesktopPlatform::Log(const std::string& message) {
|
||||
std::cout << "[INFO] " << message << std::endl;
|
||||
}
|
||||
|
||||
void DesktopPlatform::LogError(const std::string& message) {
|
||||
std::cerr << "[ERROR] " << message << std::endl;
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
|
||||
bool DesktopPlatform::PollEvents() {
|
||||
// Events are handled by RmlUi Backend
|
||||
return true;
|
||||
glfwPollEvents();
|
||||
return !glfwWindowShouldClose(m_window);
|
||||
}
|
||||
|
||||
void DesktopPlatform::SwapBuffers() {
|
||||
// Swap is handled by RmlUi Backend
|
||||
}
|
||||
|
||||
bool DesktopPlatform::ShouldClose() const {
|
||||
return false; // Determined by RmlUi Backend
|
||||
glfwSwapBuffers(m_window);
|
||||
}
|
||||
|
||||
void DesktopPlatform::SetResolution(uint32_t width, uint32_t height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
glfwSetWindowSize(m_window, width, height);
|
||||
}
|
||||
|
||||
float DesktopPlatform::GetDpiScale() const {
|
||||
return 1.0f;
|
||||
void DesktopPlatform::SetAssetsPath(const std::string& path) {
|
||||
m_file_interface.SetAssetsPath(path);
|
||||
}
|
||||
|
||||
double DesktopPlatform::GetElapsedTime() const {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
double current = std::chrono::duration<double>(now.time_since_epoch()).count();
|
||||
return current - m_start_time;
|
||||
// DesktopRenderTarget implementation
|
||||
|
||||
DesktopRenderTarget::~DesktopRenderTarget() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool DesktopPlatform::IsMouseButtonDown() const {
|
||||
return false; // Input is handled through RmlUi
|
||||
bool DesktopRenderTarget::Create(uint32_t width, uint32_t height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
// Create framebuffer
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
|
||||
|
||||
// Create color texture
|
||||
glGenTextures(1, &m_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0);
|
||||
|
||||
// Create depth/stencil renderbuffer
|
||||
glGenRenderbuffers(1, &m_depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_depth_buffer);
|
||||
|
||||
// Check completeness
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DesktopPlatform::GetMousePosition(double& x, double& y) const {
|
||||
x = y = 0; // Input is handled through RmlUi
|
||||
void DesktopRenderTarget::Bind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
|
||||
glViewport(0, 0, m_width, m_height);
|
||||
}
|
||||
|
||||
} // namespace mosis::desktop
|
||||
void DesktopRenderTarget::Unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void DesktopRenderTarget::Destroy() {
|
||||
if (m_depth_buffer) {
|
||||
glDeleteRenderbuffers(1, &m_depth_buffer);
|
||||
m_depth_buffer = 0;
|
||||
}
|
||||
if (m_texture) {
|
||||
glDeleteTextures(1, &m_texture);
|
||||
m_texture = 0;
|
||||
}
|
||||
if (m_framebuffer) {
|
||||
glDeleteFramebuffers(1, &m_framebuffer);
|
||||
m_framebuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mosis
|
||||
|
||||
@@ -1,50 +1,62 @@
|
||||
// Desktop platform implementation using GLFW + OpenGL 3.3
|
||||
// Note: Graphics context and rendering is handled by RmlUi's backend.
|
||||
// This platform implementation provides file interface and utilities.
|
||||
// D:\Dev\Mosis\MosisService\designer\src\desktop_platform.h
|
||||
#pragma once
|
||||
|
||||
#include "platform.h"
|
||||
#include "file_interface.h"
|
||||
#include <memory>
|
||||
#include "desktop_file_interface.h"
|
||||
|
||||
namespace mosis::desktop {
|
||||
// Forward declare GLFW types to avoid including glfw3.h here
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace mosis {
|
||||
|
||||
class DesktopPlatform : public IPlatform {
|
||||
uint32_t m_width = 540;
|
||||
uint32_t m_height = 960;
|
||||
std::unique_ptr<DesktopFileInterface> m_file_interface;
|
||||
double m_start_time = 0.0;
|
||||
|
||||
public:
|
||||
DesktopPlatform();
|
||||
~DesktopPlatform() override;
|
||||
DesktopPlatform(GLFWwindow* window, uint32_t width, uint32_t height);
|
||||
~DesktopPlatform() override = default;
|
||||
|
||||
// Initialize the platform
|
||||
bool Initialize(uint32_t width, uint32_t height, const char* title);
|
||||
void Shutdown();
|
||||
|
||||
// IPlatform implementation
|
||||
// IPlatform interface
|
||||
std::unique_ptr<IGraphicsContext> CreateGraphicsContext() override;
|
||||
std::unique_ptr<IRenderTarget> CreateRenderTarget(uint32_t width, uint32_t height) override;
|
||||
IFileInterface& GetFileInterface() override;
|
||||
|
||||
Rml::FileInterface& GetFileInterface() override;
|
||||
void Log(const std::string& message) override;
|
||||
void LogError(const std::string& message) override;
|
||||
|
||||
bool PollEvents() override;
|
||||
void SwapBuffers() override;
|
||||
bool ShouldClose() const override;
|
||||
|
||||
uint32_t GetWidth() const override { return m_width; }
|
||||
uint32_t GetHeight() const override { return m_height; }
|
||||
void SetResolution(uint32_t width, uint32_t height) override;
|
||||
float GetDpiScale() const override;
|
||||
|
||||
double GetElapsedTime() const override;
|
||||
// Desktop-specific
|
||||
void SetAssetsPath(const std::string& path);
|
||||
GLFWwindow* GetWindow() const { return m_window; }
|
||||
|
||||
// Input state (delegated to RmlUi backend)
|
||||
bool IsMouseButtonDown() const;
|
||||
void GetMousePosition(double& x, double& y) const;
|
||||
private:
|
||||
GLFWwindow* m_window;
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
DesktopFileInterface m_file_interface;
|
||||
};
|
||||
|
||||
} // namespace mosis::desktop
|
||||
// Desktop render target using FBO
|
||||
class DesktopRenderTarget : public IRenderTarget {
|
||||
public:
|
||||
DesktopRenderTarget() = default;
|
||||
~DesktopRenderTarget() override;
|
||||
|
||||
bool Create(uint32_t width, uint32_t height) override;
|
||||
void Bind() override;
|
||||
void Unbind() override;
|
||||
void Destroy() override;
|
||||
uint32_t GetFramebuffer() const override { return m_framebuffer; }
|
||||
uint32_t GetTexture() const override { return m_texture; }
|
||||
uint32_t GetWidth() const override { return m_width; }
|
||||
uint32_t GetHeight() const override { return m_height; }
|
||||
|
||||
private:
|
||||
uint32_t m_framebuffer = 0;
|
||||
uint32_t m_texture = 0;
|
||||
uint32_t m_depth_buffer = 0;
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
};
|
||||
|
||||
} // namespace mosis
|
||||
|
||||
11
designer/src/glad_loader.h
Normal file
11
designer/src/glad_loader.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// D:\Dev\Mosis\MosisService\designer\src\glad_loader.h
|
||||
// Wrapper header for glad that sets up correct shader version for desktop OpenGL
|
||||
|
||||
#pragma once
|
||||
|
||||
// Define the shader version for desktop OpenGL 3.3 core profile
|
||||
// This must be defined BEFORE the renderer includes glad via RMLUI_GL3_CUSTOM_LOADER
|
||||
#define RMLUI_SHADER_HEADER_VERSION "#version 330\n"
|
||||
|
||||
// Now include the actual glad header
|
||||
#include <glad/glad.h>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Hot-reload file watcher implementation
|
||||
// D:\Dev\Mosis\MosisService\designer\src\hot_reload.cpp
|
||||
#include "hot_reload.h"
|
||||
#include <iostream>
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace mosis::desktop {
|
||||
namespace mosis {
|
||||
|
||||
HotReload::HotReload(const std::filesystem::path& watch_path, ReloadCallback callback)
|
||||
: m_watch_path(watch_path)
|
||||
, m_callback(std::move(callback))
|
||||
, m_last_change(std::chrono::steady_clock::now())
|
||||
{}
|
||||
|
||||
HotReload::~HotReload() {
|
||||
@@ -21,31 +20,28 @@ HotReload::~HotReload() {
|
||||
void HotReload::Start() {
|
||||
if (m_running) return;
|
||||
|
||||
m_running = true;
|
||||
#ifdef _WIN32
|
||||
m_notification_handle = FindFirstChangeNotificationW(
|
||||
m_watch_path.c_str(),
|
||||
TRUE, // Watch subtree
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE
|
||||
);
|
||||
|
||||
|
||||
if (m_notification_handle == INVALID_HANDLE_VALUE) {
|
||||
std::cerr << "Failed to create file change notification for: "
|
||||
<< m_watch_path << std::endl;
|
||||
std::cerr << "Failed to set up file watching" << std::endl;
|
||||
m_running = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_running = true;
|
||||
m_watch_thread = std::thread(&HotReload::WatchThread, this);
|
||||
}
|
||||
|
||||
void HotReload::Stop() {
|
||||
m_running = false;
|
||||
|
||||
if (m_watch_thread.joinable()) {
|
||||
m_watch_thread.join();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_notification_handle && m_notification_handle != INVALID_HANDLE_VALUE) {
|
||||
FindCloseChangeNotification(m_notification_handle);
|
||||
@@ -54,40 +50,23 @@ void HotReload::Stop() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HotReload::CheckForChanges() {
|
||||
if (m_change_detected) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - m_last_change >= m_debounce_delay) {
|
||||
m_change_detected = false;
|
||||
if (m_callback) {
|
||||
m_callback();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HotReload::WatchThread() {
|
||||
while (m_running) {
|
||||
#ifdef _WIN32
|
||||
DWORD result = WaitForSingleObject(m_notification_handle, 100); // 100ms timeout
|
||||
if (result == WAIT_OBJECT_0) {
|
||||
m_last_change = std::chrono::steady_clock::now();
|
||||
m_change_detected = true;
|
||||
|
||||
// Reset the notification
|
||||
if (!FindNextChangeNotification(m_notification_handle)) {
|
||||
std::cerr << "FindNextChangeNotification failed" << std::endl;
|
||||
break;
|
||||
// Debounce - wait a bit for file writes to complete
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (m_callback) {
|
||||
m_callback();
|
||||
}
|
||||
FindNextChangeNotification(m_notification_handle);
|
||||
}
|
||||
#else
|
||||
// TODO: Linux inotify / macOS FSEvents implementation
|
||||
// For now, just sleep to avoid busy loop
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mosis::desktop
|
||||
} // namespace mosis
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Hot-reload file watcher for development
|
||||
// D:\Dev\Mosis\MosisService\designer\src\hot_reload.h
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
namespace mosis::desktop {
|
||||
namespace mosis {
|
||||
|
||||
class HotReload {
|
||||
public:
|
||||
@@ -20,9 +19,6 @@ public:
|
||||
void Stop();
|
||||
bool IsRunning() const { return m_running; }
|
||||
|
||||
// Check for changes (call from main thread if not using threaded mode)
|
||||
bool CheckForChanges();
|
||||
|
||||
private:
|
||||
void WatchThread();
|
||||
|
||||
@@ -30,15 +26,10 @@ private:
|
||||
ReloadCallback m_callback;
|
||||
std::thread m_watch_thread;
|
||||
std::atomic<bool> m_running{false};
|
||||
std::atomic<bool> m_change_detected{false};
|
||||
|
||||
#ifdef _WIN32
|
||||
void* m_notification_handle = nullptr; // HANDLE
|
||||
#endif
|
||||
|
||||
// Debounce settings
|
||||
std::chrono::milliseconds m_debounce_delay{100};
|
||||
std::chrono::steady_clock::time_point m_last_change;
|
||||
};
|
||||
|
||||
} // namespace mosis::desktop
|
||||
} // namespace mosis
|
||||
|
||||
23
designer/src/platform_singleton.cpp
Normal file
23
designer/src/platform_singleton.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// D:\Dev\Mosis\MosisService\designer\src\platform_singleton.cpp
|
||||
// Platform singleton implementation for desktop
|
||||
|
||||
#include "platform.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace mosis {
|
||||
|
||||
// Platform singleton
|
||||
static std::unique_ptr<IPlatform> g_platform;
|
||||
|
||||
IPlatform& GetPlatform() {
|
||||
if (!g_platform) {
|
||||
throw std::runtime_error("Platform not initialized. Call SetPlatform first.");
|
||||
}
|
||||
return *g_platform;
|
||||
}
|
||||
|
||||
void SetPlatform(std::unique_ptr<IPlatform> platform) {
|
||||
g_platform = std::move(platform);
|
||||
}
|
||||
|
||||
} // namespace mosis
|
||||
Reference in New Issue
Block a user