save state

This commit is contained in:
2026-01-16 16:34:32 +01:00
parent fbb6917812
commit a35c222570
15 changed files with 1052 additions and 266 deletions

View File

@@ -1,11 +1,10 @@
// File interface abstraction - extends RmlUi FileInterface for cross-platform
// D:\Dev\Mosis\MosisService\src\main\kernel\include\file_interface.h
#pragma once
#include <RmlUi/Core/FileInterface.h>
#include <string>
#include <vector>
#include <cstdint>
#include <filesystem>
namespace mosis {
@@ -16,17 +15,12 @@ public:
// Additional utility methods
virtual std::vector<uint8_t> ReadAll(const std::string& path) = 0;
virtual bool FileExists(const std::string& path) = 0;
virtual std::vector<std::string> ListDirectory(const std::string& path) = 0;
// Asset path management
virtual void SetAssetsPath(const std::string& path) = 0;
virtual std::string GetAssetsPath() const = 0;
// Resolve a relative path to full path
virtual std::string ResolvePath(const std::string& relative_path) = 0;
// RmlUi FileInterface methods (must implement)
// RmlUi FileInterface methods (must implement in derived classes)
// 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;
@@ -36,33 +30,4 @@ public:
// bool LoadFile(const Rml::String& path, Rml::String& out_data) override;
};
// Desktop file interface implementation
class DesktopFileInterface : public IFileInterface {
std::filesystem::path m_assets_path;
public:
DesktopFileInterface() = default;
explicit DesktopFileInterface(const std::string& assets_path);
// RmlUi FileInterface implementation
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;
bool LoadFile(const Rml::String& path, Rml::String& out_data) override;
// Extended interface
std::vector<uint8_t> ReadAll(const std::string& path) override;
bool FileExists(const std::string& path) override;
std::vector<std::string> ListDirectory(const std::string& path) override;
void SetAssetsPath(const std::string& path) override;
std::string GetAssetsPath() const override;
std::string ResolvePath(const std::string& relative_path) override;
private:
std::filesystem::path ResolveFilePath(const std::string& path);
};
} // namespace mosis

View File

@@ -1,30 +1,17 @@
// Platform abstraction layer for cross-platform Mosis kernel
// D:\Dev\Mosis\MosisService\src\main\kernel\include\platform.h
#pragma once
#include <RmlUi/Core/FileInterface.h>
#include <memory>
#include <string>
#include <functional>
#include <cstdint>
#include <vector>
namespace mosis {
// Forward declarations
class IGraphicsContext;
class IRenderTarget;
class IFileInterface;
// Resolution preset structure
struct Resolution {
uint32_t width;
uint32_t height;
const char* name;
static constexpr Resolution PhoneSD() { return {540, 960, "Phone SD"}; }
static constexpr Resolution PhoneHD() { return {720, 1280, "Phone HD"}; }
static constexpr Resolution PhoneFHD() { return {1080, 1920, "Phone FHD"}; }
static constexpr Resolution Tablet() { return {800, 1280, "Tablet"}; }
};
// Platform abstraction - implemented differently per platform
class IPlatform {
@@ -34,25 +21,19 @@ public:
// Factory methods
virtual std::unique_ptr<IGraphicsContext> CreateGraphicsContext() = 0;
virtual std::unique_ptr<IRenderTarget> CreateRenderTarget(uint32_t width, uint32_t height) = 0;
virtual IFileInterface& GetFileInterface() = 0;
virtual Rml::FileInterface& GetFileInterface() = 0;
// Logging
virtual void Log(const std::string& message) = 0;
virtual void LogError(const std::string& message) = 0;
// Platform-specific windowing (desktop only, no-op on Android)
virtual bool PollEvents() { return true; }
virtual void SwapBuffers() {}
virtual bool ShouldClose() const { return false; }
// Resolution management
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
virtual void SetResolution(uint32_t width, uint32_t height) = 0;
virtual float GetDpiScale() const { return 1.0f; }
// Time
virtual double GetElapsedTime() const = 0;
};
// Graphics context abstraction
@@ -63,10 +44,9 @@ public:
virtual void Destroy() = 0;
virtual void MakeCurrent() = 0;
virtual void SwapBuffers() = 0;
virtual bool IsValid() const = 0;
};
// Render target abstraction (framebuffer)
// Render target abstraction
class IRenderTarget {
public:
virtual ~IRenderTarget() = default;
@@ -78,14 +58,10 @@ public:
virtual uint32_t GetTexture() const = 0;
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
// Read pixels for screenshot capture
virtual std::vector<uint8_t> ReadPixels() const = 0;
};
// Global platform accessor
IPlatform& GetPlatform();
void SetPlatform(std::unique_ptr<IPlatform> platform);
bool HasPlatform();
} // namespace mosis

View File

@@ -1,11 +1,9 @@
// Service interface abstraction - mirrors Android Binder for cross-platform
// D:\Dev\Mosis\MosisService\src\main\kernel\include\service_interface.h
#pragma once
#include <memory>
#include <functional>
#include <string>
#include <cstdint>
#include <vector>
namespace mosis {
@@ -13,14 +11,8 @@ namespace mosis {
class IServiceListener {
public:
virtual ~IServiceListener() = default;
// Called when kernel initialization completes
virtual void OnServiceInitialized(bool success) = 0;
// Called when a new render buffer is available (with optional shared buffer handle)
virtual void OnBufferAvailable(void* buffer_handle) = 0;
// Called when a new frame has been rendered
virtual void OnBufferAvailable() = 0; // Simplified - no HardwareBuffer on desktop
virtual void OnFrameAvailable() = 0;
};
@@ -43,31 +35,19 @@ public:
virtual void OnHomeButton() = 0;
virtual void OnRecentsButton() = 0;
// Hot-reload support (desktop)
// Hot-reload support
virtual void RequestReload() = 0;
// Document loading
virtual void LoadDocument(const std::string& path) = 0;
// Navigation
virtual void NavigateTo(const std::string& screen) = 0;
// Lifecycle
virtual void Start() = 0;
virtual void Stop() = 0;
virtual bool IsRunning() const = 0;
// Single frame update (for non-threaded mode)
virtual void Update() = 0;
virtual void Render() = 0;
};
// Configuration for kernel creation
struct KernelConfig {
uint32_t width = 540;
uint32_t height = 960;
std::string initial_document = "apps/home/home.rml";
bool threaded = true; // false for desktop single-threaded mode
};
// Factory for creating kernel implementation
std::unique_ptr<IKernel> CreateKernel(const KernelConfig& config);
std::unique_ptr<IKernel> CreateKernel();
} // namespace mosis

View File

@@ -0,0 +1,366 @@
// D:\Dev\Mosis\MosisService\src\main\kernel\src\kernel.cpp
// Platform-agnostic kernel implementation
#include "service_interface.h"
#include "platform.h"
#include "file_interface.h"
#include "RmlUi_Renderer_GL3.h"
#include <RmlUi/Core.h>
#include <RmlUi/Lua.h>
#include <RmlUi/Lua/Interpreter.h>
#include <mutex>
#include <vector>
#include <functional>
#include <atomic>
#include <thread>
#include <chrono>
namespace mosis {
// Global state for Lua access
static Rml::Context* g_context = nullptr;
static Rml::ElementDocument* g_document = nullptr;
// System interface using platform abstraction
class PlatformSystemInterface : public Rml::SystemInterface {
public:
double GetElapsedTime() override {
static auto start_time = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now();
return std::chrono::duration<double>(now - start_time).count();
}
bool LogMessage(Rml::Log::Type type, const Rml::String& message) override {
const char* type_str = "";
switch (type) {
case Rml::Log::LT_ERROR: type_str = "[ERROR]"; break;
case Rml::Log::LT_WARNING: type_str = "[WARN]"; break;
case Rml::Log::LT_INFO: type_str = "[INFO]"; break;
default: type_str = "[DEBUG]"; break;
}
GetPlatform().Log(std::string(type_str) + " " + message);
return true;
}
};
static PlatformSystemInterface g_system_interface;
// Lua function to load a screen document
static int LuaLoadScreen(lua_State* L) {
if (!g_context) {
lua_pushboolean(L, false);
return 1;
}
const char* path = luaL_checkstring(L, 1);
GetPlatform().Log(std::string("Loading screen: ") + path);
// Unload current document
if (g_document) {
g_context->UnloadDocument(g_document);
g_document = nullptr;
}
// Load new document
g_document = g_context->LoadDocument(path);
if (g_document) {
g_document->Show();
GetPlatform().Log(std::string("Loaded screen: ") + path);
lua_pushboolean(L, true);
} else {
GetPlatform().Log(std::string("Failed to load screen: ") + path);
lua_pushboolean(L, false);
}
return 1;
}
// Register Lua functions for navigation
static void RegisterLuaFunctions() {
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
lua_pushcfunction(L, LuaLoadScreen);
lua_setglobal(L, "loadScreen");
GetPlatform().Log("Registered Lua loadScreen function");
}
// Kernel implementation
class KernelImpl : public IKernel {
public:
KernelImpl() = default;
~KernelImpl() override {
Stop();
}
void AddListener(std::shared_ptr<IServiceListener> listener) override {
std::lock_guard lock(m_mutex);
m_listeners.push_back(listener);
if (m_initialized) {
listener->OnServiceInitialized(true);
listener->OnBufferAvailable();
}
}
void RemoveListener(std::shared_ptr<IServiceListener> listener) override {
std::lock_guard lock(m_mutex);
m_listeners.erase(
std::remove(m_listeners.begin(), m_listeners.end(), listener),
m_listeners.end()
);
}
void OnTouchDown(float x, float y) override {
std::lock_guard lock(m_mutex);
auto& platform = GetPlatform();
int px = static_cast<int>(x * platform.GetWidth());
int py = static_cast<int>(y * platform.GetHeight());
m_tasks.emplace_back([px, py](Rml::Context* ctx) {
ctx->ProcessMouseMove(px, py, 0);
ctx->ProcessMouseButtonDown(0, 0);
});
}
void OnTouchMove(float x, float y) override {
std::lock_guard lock(m_mutex);
auto& platform = GetPlatform();
int px = static_cast<int>(x * platform.GetWidth());
int py = static_cast<int>(y * platform.GetHeight());
m_tasks.emplace_back([px, py](Rml::Context* ctx) {
ctx->ProcessMouseMove(px, py, 0);
});
}
void OnTouchUp(float x, float y) override {
std::lock_guard lock(m_mutex);
auto& platform = GetPlatform();
int px = static_cast<int>(x * platform.GetWidth());
int py = static_cast<int>(y * platform.GetHeight());
m_tasks.emplace_back([px, py](Rml::Context* ctx) {
ctx->ProcessMouseMove(px, py, 0);
ctx->ProcessMouseButtonUp(0, 0);
});
}
void OnBackButton() override {
std::lock_guard lock(m_mutex);
m_tasks.emplace_back([](Rml::Context* ctx) {
// Execute goBack() Lua function if available
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
lua_getglobal(L, "goBack");
if (lua_isfunction(L, -1)) {
lua_call(L, 0, 0);
} else {
lua_pop(L, 1);
}
});
}
void OnHomeButton() override {
std::lock_guard lock(m_mutex);
m_tasks.emplace_back([](Rml::Context* ctx) {
// Execute goHome() Lua function if available
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
lua_getglobal(L, "goHome");
if (lua_isfunction(L, -1)) {
lua_call(L, 0, 0);
} else {
lua_pop(L, 1);
}
});
}
void OnRecentsButton() override {
// TODO: Implement recents/app switcher
GetPlatform().Log("Recents button pressed (not implemented)");
}
void RequestReload() override {
m_reload_requested = true;
}
void NavigateTo(const std::string& screen) override {
std::lock_guard lock(m_mutex);
std::string path = screen;
m_tasks.emplace_back([path](Rml::Context* ctx) {
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
lua_getglobal(L, "navigateTo");
if (lua_isfunction(L, -1)) {
lua_pushstring(L, path.c_str());
lua_call(L, 1, 0);
} else {
lua_pop(L, 1);
}
});
}
void Start() override {
if (m_running) return;
m_running = true;
m_main_thread = std::thread(&KernelImpl::MainLoop, this);
}
void Stop() override {
m_running = false;
if (m_main_thread.joinable()) {
m_main_thread.join();
}
}
bool IsRunning() const override {
return m_running;
}
private:
void MainLoop() {
auto& platform = GetPlatform();
uint32_t width = platform.GetWidth();
uint32_t height = platform.GetHeight();
// Initialize RmlUi render interface
RenderInterface_GL3 render_interface;
if (!render_interface) {
platform.Log("Failed to create render interface");
return;
}
// Set up RmlUi
Rml::SetRenderInterface(&render_interface);
Rml::SetFileInterface(&platform.GetFileInterface());
Rml::SetSystemInterface(&g_system_interface);
Rml::Initialise();
Rml::Lua::Initialise();
platform.Log("RmlUi initialized with Lua bindings");
// Register navigation functions
RegisterLuaFunctions();
// Create context with platform resolution
g_context = Rml::CreateContext("default", Rml::Vector2i(width, height));
if (!g_context) {
platform.Log("Failed to create RmlUi context");
Rml::Shutdown();
return;
}
// Load fonts
LoadFonts();
// Load initial document
g_document = g_context->LoadDocument("apps/home/home.rml");
if (!g_document) {
platform.Log("Failed to load home.rml document");
Rml::Shutdown();
return;
}
g_document->Show();
// Notify listeners
{
std::lock_guard lock(m_mutex);
m_initialized = true;
for (auto& listener : m_listeners) {
listener->OnServiceInitialized(true);
listener->OnBufferAvailable();
}
}
// Main loop
while (m_running) {
// Handle reload request
if (m_reload_requested.exchange(false)) {
ReloadCurrentDocument();
}
// Process pending tasks
{
std::lock_guard lock(m_mutex);
for (const auto& task : m_tasks) {
task(g_context);
}
m_tasks.clear();
}
// Render
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, width, height);
g_context->Update();
render_interface.SetViewport(width, height);
render_interface.BeginFrame();
g_context->Render();
render_interface.EndFrame(0); // 0 = default framebuffer on desktop
glFinish();
// Notify listeners
{
std::lock_guard lock(m_mutex);
for (auto& listener : m_listeners) {
listener->OnFrameAvailable();
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60 FPS
}
// Cleanup
g_document = nullptr;
g_context = nullptr;
Rml::Shutdown();
}
void LoadFonts() {
Rml::LoadFontFace("fonts/LatoLatin-Bold.ttf");
Rml::LoadFontFace("fonts/LatoLatin-BoldItalic.ttf");
Rml::LoadFontFace("fonts/LatoLatin-Italic.ttf");
Rml::LoadFontFace("fonts/LatoLatin-Regular.ttf");
Rml::LoadFontFace("fonts/NotoEmoji-Regular.ttf", true);
Rml::LoadFontFace("fonts/Roboto/Roboto-VariableFont_wdth,wght.ttf");
Rml::LoadFontFace("fonts/Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf");
}
void ReloadCurrentDocument() {
if (!g_context || !g_document) return;
GetPlatform().Log("Reloading current document...");
// Get current document path
std::string current_path = g_document->GetSourceURL();
// Unload and reload
g_context->UnloadDocument(g_document);
g_document = g_context->LoadDocument(current_path);
if (g_document) {
g_document->Show();
GetPlatform().Log("Document reloaded: " + current_path);
} else {
GetPlatform().Log("Failed to reload document: " + current_path);
}
}
std::mutex m_mutex;
std::vector<std::shared_ptr<IServiceListener>> m_listeners;
std::vector<std::function<void(Rml::Context*)>> m_tasks;
std::thread m_main_thread;
std::atomic<bool> m_running{false};
std::atomic<bool> m_reload_requested{false};
bool m_initialized{false};
};
// Platform singleton
static std::unique_ptr<IPlatform> g_platform;
IPlatform& GetPlatform() {
return *g_platform;
}
void SetPlatform(std::unique_ptr<IPlatform> platform) {
g_platform = std::move(platform);
}
// Factory function
std::unique_ptr<IKernel> CreateKernel() {
return std::make_unique<KernelImpl>();
}
} // namespace mosis