292 lines
8.6 KiB
C++
292 lines
8.6 KiB
C++
#include "kernel.h"
|
|
#include "egl_context.h"
|
|
#include "render_target.h"
|
|
#include "logger.h"
|
|
#include "assets_manager.h"
|
|
#include "aidl/com/omixlab/mosis/IMosisListener.h"
|
|
#include <android/hardware_buffer.h>
|
|
#include <android/asset_manager.h>
|
|
#include <RmlUi/Core.h>
|
|
#include <RmlUi/Lua.h>
|
|
#include <RmlUi/Lua/Interpreter.h>
|
|
#include "RmlUi_Renderer_GL3.h"
|
|
#include <span>
|
|
#include <ranges>
|
|
#include <chrono>
|
|
#include <format>
|
|
|
|
// Global state for Lua access
|
|
static Rml::Context* g_context = nullptr;
|
|
static Rml::ElementDocument* g_document = nullptr;
|
|
|
|
using namespace aidl::com::omixlab::mosis;
|
|
using namespace aidl::android::hardware;
|
|
|
|
class AssetFilesInterface : public Rml::FileInterface
|
|
{
|
|
public:
|
|
static AssetFilesInterface& Instance()
|
|
{
|
|
static AssetFilesInterface instance;
|
|
return instance;
|
|
}
|
|
Rml::FileHandle Open(const Rml::String &path) override
|
|
{
|
|
AAssetManager* am = AssetsManager::Native();
|
|
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
|
return reinterpret_cast<Rml::FileHandle>(asset);
|
|
}
|
|
void Close(Rml::FileHandle file) override
|
|
{
|
|
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
|
AAsset_close(asset);
|
|
}
|
|
size_t Read(void *buffer, size_t size, Rml::FileHandle file) override
|
|
{
|
|
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
|
return AAsset_read(asset, buffer, size);
|
|
}
|
|
bool Seek(Rml::FileHandle file, long offset, int origin) override
|
|
{
|
|
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
|
off_t new_cursor = AAsset_seek(asset, offset, origin);
|
|
return new_cursor != -1;
|
|
}
|
|
size_t Tell(Rml::FileHandle file) override
|
|
{
|
|
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
|
return AAsset_seek(asset, 0, SEEK_CUR);
|
|
}
|
|
|
|
size_t Length(Rml::FileHandle file) override
|
|
{
|
|
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
|
return AAsset_getLength(asset);
|
|
}
|
|
|
|
bool LoadFile(const Rml::String &path, Rml::String &out_data) override
|
|
{
|
|
AAssetManager* am = AssetsManager::Native();
|
|
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
|
if (!asset)
|
|
return false;
|
|
out_data.resize(AAsset_getLength(asset));
|
|
auto data_ptr = static_cast<const char*>(AAsset_getBuffer(asset));
|
|
std::span data = std::span(data_ptr, out_data.size());
|
|
std::ranges::copy(data, out_data.begin());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class SystemInterface : public Rml::SystemInterface
|
|
{
|
|
public:
|
|
static SystemInterface& Instance()
|
|
{
|
|
static SystemInterface instance;
|
|
return instance;
|
|
}
|
|
bool LogMessage(Rml::Log::Type type, const Rml::String &message) override
|
|
{
|
|
Logger::Log(std::format("RMLUI: {}", message));
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// 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);
|
|
Logger::Log(std::format("Loading screen: {}", path));
|
|
|
|
// Check if asset exists
|
|
AAssetManager* am = AssetsManager::Native();
|
|
AAsset* asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
|
|
if (!asset)
|
|
{
|
|
Logger::Log(std::format("Screen not found: {}", path));
|
|
lua_pushboolean(L, false);
|
|
return 1;
|
|
}
|
|
AAsset_close(asset);
|
|
|
|
// 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();
|
|
Logger::Log(std::format("Loaded screen: {}", path));
|
|
lua_pushboolean(L, true);
|
|
}
|
|
else
|
|
{
|
|
Logger::Log(std::format("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");
|
|
Logger::Log("Registered Lua loadScreen function");
|
|
}
|
|
|
|
void Kernel::main_loop()
|
|
{
|
|
m_egl_context = std::make_unique<egl::Context>();
|
|
if (!m_egl_context->create())
|
|
{
|
|
Logger::Log("failed to create EGL context");
|
|
return;
|
|
}
|
|
m_render_target = std::make_unique<RenderTarget>();
|
|
if (!m_render_target->create_exported(540, 960))
|
|
{
|
|
Logger::Log("failed to create render target");
|
|
return;
|
|
}
|
|
m_render_target->bind();
|
|
for (const auto& [pid, l] : m_listeners)
|
|
l->onServiceInitialized(true);
|
|
m_aidl_buffer = std::make_unique<HardwareBuffer>();
|
|
m_aidl_buffer->reset(m_render_target->hardware_buffer());
|
|
for (const auto& [pid, l] : m_listeners)
|
|
l->onBufferAvailable(*m_aidl_buffer);
|
|
|
|
RenderInterface_GL3 rmlui_render_interface;
|
|
if (!rmlui_render_interface)
|
|
{
|
|
Logger::Log("failed to create render interface");
|
|
return;
|
|
}
|
|
Rml::SetRenderInterface(&rmlui_render_interface);
|
|
Rml::SetFileInterface(&AssetFilesInterface::Instance());
|
|
Rml::SetSystemInterface(&SystemInterface::Instance());
|
|
Rml::Initialise();
|
|
Rml::Lua::Initialise();
|
|
Logger::Log("RmlUi Lua bindings initialized");
|
|
|
|
// Register navigation functions with Lua
|
|
RegisterLuaFunctions();
|
|
|
|
g_context = Rml::CreateContext("default", Rml::Vector2i(540, 960));
|
|
if (!g_context)
|
|
{
|
|
Logger::Log("RMLUI failed to create a context");
|
|
Rml::Shutdown();
|
|
return;
|
|
}
|
|
// Load fonts from assets/fonts/
|
|
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");
|
|
|
|
// Load home screen document
|
|
g_document = g_context->LoadDocument("apps/home/home.rml");
|
|
if (!g_document)
|
|
{
|
|
Logger::Log("Failed to load home.rml document");
|
|
Rml::Shutdown();
|
|
return;
|
|
}
|
|
g_document->Show();
|
|
|
|
while (true)
|
|
{
|
|
if (!m_tasks.empty())
|
|
{
|
|
std::lock_guard _lock(m_mutex);
|
|
for (const auto &task: m_tasks)
|
|
task(g_context);
|
|
m_tasks.clear();
|
|
}
|
|
|
|
m_render_target->bind();
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glViewport(0, 0, 540, 960);
|
|
|
|
g_context->Update();
|
|
rmlui_render_interface.SetViewport(540, 960);
|
|
rmlui_render_interface.BeginFrame();
|
|
g_context->Render();
|
|
rmlui_render_interface.EndFrame(m_render_target->framebuffer());
|
|
|
|
glFinish();
|
|
{
|
|
std::lock_guard _lock(m_mutex);
|
|
for (const auto& [pid, l] : m_listeners)
|
|
l->onFrameAvailable();
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60 FPS
|
|
}
|
|
Rml::Shutdown();
|
|
}
|
|
|
|
Kernel::Kernel(const std::shared_ptr<IMosisListener> &listener)
|
|
{
|
|
m_listeners.emplace(AIBinder_getCallingPid(), listener);
|
|
m_main_loop_thread = std::thread(&Kernel::main_loop, this);
|
|
}
|
|
|
|
void Kernel::add_listener(const std::shared_ptr<IMosisListener> &listener)
|
|
{
|
|
std::lock_guard _lock(m_mutex);
|
|
m_listeners.emplace(AIBinder_getCallingPid(), listener);
|
|
listener->onServiceInitialized(true);
|
|
listener->onBufferAvailable(*m_aidl_buffer);
|
|
}
|
|
|
|
void Kernel::on_touch_down(float x, float y)
|
|
{
|
|
Logger::Log(std::format("on_touch_down {} - {}", x, y));
|
|
std::lock_guard _lock(m_mutex);
|
|
m_tasks.emplace_back([x, y](Rml::Context* context){
|
|
context->ProcessMouseMove(static_cast<int>(x * 540), static_cast<int>(y * 960), 0);
|
|
context->ProcessMouseButtonDown(0, 0);
|
|
});
|
|
}
|
|
|
|
void Kernel::on_touch_move(float x, float y)
|
|
{
|
|
Logger::Log(std::format("on_touch_move {} - {}", x, y));
|
|
std::lock_guard _lock(m_mutex);
|
|
m_tasks.emplace_back([x, y](Rml::Context* context){
|
|
context->ProcessMouseMove(static_cast<int>(x * 540), static_cast<int>(y * 960), 0);
|
|
});
|
|
}
|
|
|
|
void Kernel::on_touch_up(float x, float y)
|
|
{
|
|
Logger::Log(std::format("on_touch_up {} - {}", x, y));
|
|
std::lock_guard _lock(m_mutex);
|
|
m_tasks.emplace_back([x, y](Rml::Context* context){
|
|
context->ProcessMouseMove(static_cast<int>(x * 540), static_cast<int>(y * 960), 0);
|
|
context->ProcessMouseButtonUp(0, 0);
|
|
});
|
|
}
|
|
|
|
Kernel::~Kernel() = default;
|