save state
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
366
src/main/kernel/src/kernel.cpp
Normal file
366
src/main/kernel/src/kernel.cpp
Normal 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
|
||||
Reference in New Issue
Block a user