Files
MosisService/src/main/cpp/kernel.cpp
2026-01-16 12:43:06 +01:00

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;