- Add file:// URL handling to AssetFilesInterface in kernel.cpp - Update home.lua to use file:// prefix for absolute filesystem paths - Add file:// URL handling to desktop file interface for consistency This fixes RmlUi stripping the leading slash from absolute paths when resolving img src URLs relative to the document base. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
656 lines
20 KiB
C++
656 lines
20 KiB
C++
#include "kernel.h"
|
|
#include "egl_context.h"
|
|
#include "render_target.h"
|
|
#include <mosis/util/logger.h>
|
|
#include "assets_manager.h"
|
|
#include "apps/app_manager.h"
|
|
#include "apps/update_service.h"
|
|
#include "apps/app_api.h"
|
|
#include "sandbox/sandbox_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>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
|
|
// Sandbox API includes for RmlUi integration
|
|
#include <mosis/sandbox/timer_manager.h>
|
|
#include <mosis/sandbox/json_api.h>
|
|
#include <mosis/sandbox/crypto_api.h>
|
|
#include <mosis/sandbox/virtual_fs.h>
|
|
|
|
// Global state for Lua access
|
|
static Rml::Context* g_context = nullptr;
|
|
static Rml::ElementDocument* g_document = nullptr;
|
|
static mosis::AppManager* g_app_manager = nullptr;
|
|
static mosis::UpdateService* g_update_service = nullptr;
|
|
static mosis::LuaSandboxManager* g_sandbox_manager = nullptr;
|
|
|
|
// RmlUi sandbox state - APIs registered into RmlUi's Lua state
|
|
static std::unique_ptr<mosis::TimerManager> g_rmlui_timer_manager;
|
|
static std::unique_ptr<mosis::VirtualFS> g_rmlui_vfs;
|
|
static std::string g_current_app_id = "com.mosis.home";
|
|
static std::string g_data_root;
|
|
|
|
// Forward declarations
|
|
static void SwitchAppSandbox(const std::string& app_id, const std::string& app_data_path);
|
|
|
|
using namespace aidl::com::omixlab::mosis;
|
|
using namespace aidl::android::hardware;
|
|
|
|
// File handle wrapper to support both assets and filesystem files
|
|
struct FileHandleWrapper {
|
|
enum class Type { Asset, File };
|
|
Type type;
|
|
union {
|
|
AAsset* asset;
|
|
std::ifstream* file;
|
|
};
|
|
size_t file_size = 0; // For filesystem files
|
|
};
|
|
|
|
class AssetFilesInterface : public Rml::FileInterface
|
|
{
|
|
public:
|
|
static AssetFilesInterface& Instance()
|
|
{
|
|
static AssetFilesInterface instance;
|
|
return instance;
|
|
}
|
|
|
|
// Check if path is a filesystem path (starts with / or file://)
|
|
static bool IsFilesystemPath(const Rml::String& path) {
|
|
return !path.empty() && (path[0] == '/' || path.rfind("file://", 0) == 0);
|
|
}
|
|
|
|
// Get the actual filesystem path, stripping file:// prefix if present
|
|
static Rml::String GetFilesystemPath(const Rml::String& path) {
|
|
if (path.rfind("file://", 0) == 0) {
|
|
return path.substr(7); // Strip "file://"
|
|
}
|
|
return path;
|
|
}
|
|
|
|
Rml::FileHandle Open(const Rml::String &path) override
|
|
{
|
|
auto* wrapper = new FileHandleWrapper();
|
|
|
|
if (IsFilesystemPath(path)) {
|
|
// Filesystem path
|
|
Rml::String fs_path = GetFilesystemPath(path);
|
|
auto* file = new std::ifstream(fs_path, std::ios::binary);
|
|
if (!file->is_open()) {
|
|
delete file;
|
|
delete wrapper;
|
|
return 0;
|
|
}
|
|
file->seekg(0, std::ios::end);
|
|
wrapper->file_size = file->tellg();
|
|
file->seekg(0, std::ios::beg);
|
|
wrapper->type = FileHandleWrapper::Type::File;
|
|
wrapper->file = file;
|
|
} else {
|
|
// Asset path
|
|
AAssetManager* am = AssetsManager::Native();
|
|
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
|
if (!asset) {
|
|
delete wrapper;
|
|
return 0;
|
|
}
|
|
wrapper->type = FileHandleWrapper::Type::Asset;
|
|
wrapper->asset = asset;
|
|
}
|
|
|
|
return reinterpret_cast<Rml::FileHandle>(wrapper);
|
|
}
|
|
|
|
void Close(Rml::FileHandle file) override
|
|
{
|
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
|
if (!wrapper) return;
|
|
|
|
if (wrapper->type == FileHandleWrapper::Type::Asset) {
|
|
AAsset_close(wrapper->asset);
|
|
} else {
|
|
wrapper->file->close();
|
|
delete wrapper->file;
|
|
}
|
|
delete wrapper;
|
|
}
|
|
|
|
size_t Read(void *buffer, size_t size, Rml::FileHandle file) override
|
|
{
|
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
|
if (!wrapper) return 0;
|
|
|
|
if (wrapper->type == FileHandleWrapper::Type::Asset) {
|
|
return AAsset_read(wrapper->asset, buffer, size);
|
|
} else {
|
|
wrapper->file->read(static_cast<char*>(buffer), size);
|
|
return wrapper->file->gcount();
|
|
}
|
|
}
|
|
|
|
bool Seek(Rml::FileHandle file, long offset, int origin) override
|
|
{
|
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
|
if (!wrapper) return false;
|
|
|
|
if (wrapper->type == FileHandleWrapper::Type::Asset) {
|
|
off_t new_cursor = AAsset_seek(wrapper->asset, offset, origin);
|
|
return new_cursor != -1;
|
|
} else {
|
|
std::ios_base::seekdir dir = std::ios::beg;
|
|
if (origin == SEEK_CUR) dir = std::ios::cur;
|
|
else if (origin == SEEK_END) dir = std::ios::end;
|
|
wrapper->file->seekg(offset, dir);
|
|
return !wrapper->file->fail();
|
|
}
|
|
}
|
|
|
|
size_t Tell(Rml::FileHandle file) override
|
|
{
|
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
|
if (!wrapper) return 0;
|
|
|
|
if (wrapper->type == FileHandleWrapper::Type::Asset) {
|
|
return AAsset_seek(wrapper->asset, 0, SEEK_CUR);
|
|
} else {
|
|
return wrapper->file->tellg();
|
|
}
|
|
}
|
|
|
|
size_t Length(Rml::FileHandle file) override
|
|
{
|
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
|
if (!wrapper) return 0;
|
|
|
|
if (wrapper->type == FileHandleWrapper::Type::Asset) {
|
|
return AAsset_getLength(wrapper->asset);
|
|
} else {
|
|
return wrapper->file_size;
|
|
}
|
|
}
|
|
|
|
bool LoadFile(const Rml::String &path, Rml::String &out_data) override
|
|
{
|
|
if (IsFilesystemPath(path)) {
|
|
// Load from filesystem
|
|
Rml::String fs_path = GetFilesystemPath(path);
|
|
std::ifstream file(fs_path, std::ios::binary | std::ios::ate);
|
|
if (!file.is_open()) return false;
|
|
|
|
size_t size = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
out_data.resize(size);
|
|
file.read(out_data.data(), size);
|
|
return true;
|
|
} else {
|
|
// Load from assets
|
|
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());
|
|
AAsset_close(asset);
|
|
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 file exists - support both asset and filesystem paths
|
|
bool exists = false;
|
|
if (AssetFilesInterface::IsFilesystemPath(path)) {
|
|
// Filesystem path
|
|
exists = std::filesystem::exists(path);
|
|
} else {
|
|
// Asset path
|
|
AAssetManager* am = AssetsManager::Native();
|
|
AAsset* asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
|
|
if (asset) {
|
|
exists = true;
|
|
AAsset_close(asset);
|
|
}
|
|
}
|
|
|
|
if (!exists)
|
|
{
|
|
Logger::Log(std::format("Screen not found: {}", path));
|
|
lua_pushboolean(L, false);
|
|
return 1;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Lua function to go back to home screen
|
|
static int LuaGoHome(lua_State* L)
|
|
{
|
|
if (!g_context)
|
|
{
|
|
lua_pushboolean(L, false);
|
|
return 1;
|
|
}
|
|
|
|
Logger::Log("goHome called - returning to home screen");
|
|
|
|
// Stop any running third-party app in the legacy sandbox manager
|
|
if (g_sandbox_manager) {
|
|
auto running_apps = g_sandbox_manager->GetRunningApps();
|
|
for (const auto& app_id : running_apps) {
|
|
// Don't stop system apps
|
|
if (app_id.find("com.mosis.") == 0 && app_id != "com.mosis.home") {
|
|
Logger::Log(std::format("Stopping app: {}", app_id));
|
|
g_sandbox_manager->StopApp(app_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Switch sandbox context back to home screen
|
|
SwitchAppSandbox("com.mosis.home", g_data_root);
|
|
|
|
// Load home screen
|
|
const char* home_path = "apps/home/home.rml";
|
|
|
|
// Unload current document
|
|
if (g_document)
|
|
{
|
|
g_context->UnloadDocument(g_document);
|
|
g_document = nullptr;
|
|
}
|
|
|
|
// Load home document
|
|
g_document = g_context->LoadDocument(home_path);
|
|
if (g_document)
|
|
{
|
|
g_document->Show();
|
|
Logger::Log("Returned to home screen");
|
|
lua_pushboolean(L, true);
|
|
}
|
|
else
|
|
{
|
|
Logger::Log("Failed to load home screen");
|
|
lua_pushboolean(L, false);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Register sandbox APIs (timer, json, crypto, fs) into RmlUi's Lua state
|
|
static void RegisterSandboxAPIs(lua_State* L)
|
|
{
|
|
Logger::Log("Registering sandbox APIs...");
|
|
|
|
// Register timer API
|
|
if (g_rmlui_timer_manager) {
|
|
mosis::RegisterTimerAPI(L, g_rmlui_timer_manager.get(), g_current_app_id);
|
|
Logger::Log(" Timer API registered");
|
|
}
|
|
|
|
// Register JSON API
|
|
mosis::RegisterJsonAPI(L, mosis::JsonLimits{});
|
|
Logger::Log(" JSON API registered");
|
|
|
|
// Register crypto API
|
|
mosis::RegisterCryptoAPI(L);
|
|
Logger::Log(" Crypto API registered");
|
|
|
|
// Register virtual filesystem API
|
|
if (g_rmlui_vfs) {
|
|
mosis::RegisterVirtualFS(L, g_rmlui_vfs.get());
|
|
Logger::Log(" VirtualFS API registered");
|
|
}
|
|
|
|
Logger::Log("All sandbox APIs registered");
|
|
}
|
|
|
|
// Unregister sandbox APIs (for clean switch between apps)
|
|
static void UnregisterSandboxAPIs(lua_State* L)
|
|
{
|
|
// Clear timer globals
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "setTimeout");
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "clearTimeout");
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "setInterval");
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "clearInterval");
|
|
|
|
// Clear json global
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "json");
|
|
|
|
// Clear crypto global
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "crypto");
|
|
|
|
// Clear fs global
|
|
lua_pushnil(L);
|
|
lua_setglobal(L, "fs");
|
|
|
|
Logger::Log("Sandbox APIs unregistered");
|
|
}
|
|
|
|
// Switch sandbox context to a different app
|
|
static void SwitchAppSandbox(const std::string& app_id, const std::string& app_data_path)
|
|
{
|
|
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
|
|
|
|
// Clear timers for current app
|
|
if (g_rmlui_timer_manager) {
|
|
g_rmlui_timer_manager->ClearAppTimers(g_current_app_id);
|
|
Logger::Log(std::format("Timers cleared for {}", g_current_app_id));
|
|
}
|
|
|
|
// Unregister current APIs
|
|
UnregisterSandboxAPIs(L);
|
|
|
|
// Update current app ID
|
|
g_current_app_id = app_id;
|
|
|
|
// Create new VirtualFS for the app
|
|
std::string vfs_path = app_data_path.empty() ?
|
|
(g_data_root + "/sandbox_data") :
|
|
(app_data_path + "/sandbox_data");
|
|
|
|
std::filesystem::create_directories(vfs_path);
|
|
|
|
g_rmlui_vfs = std::make_unique<mosis::VirtualFS>(
|
|
app_id,
|
|
vfs_path,
|
|
mosis::VirtualFSLimits{}
|
|
);
|
|
|
|
Logger::Log(std::format("VirtualFS created for {} at {}", app_id, vfs_path));
|
|
|
|
// Register APIs for new app
|
|
RegisterSandboxAPIs(L);
|
|
}
|
|
|
|
// Lua function to switch sandbox context for a third-party app
|
|
// Called before loading an app's UI: switchAppSandbox(app_id, install_path)
|
|
static int LuaSwitchAppSandbox(lua_State* L)
|
|
{
|
|
const char* app_id = luaL_checkstring(L, 1);
|
|
const char* install_path = luaL_checkstring(L, 2);
|
|
|
|
Logger::Log(std::format("Switching sandbox to app: {} at {}", app_id, install_path));
|
|
|
|
SwitchAppSandbox(app_id, install_path);
|
|
|
|
lua_pushboolean(L, true);
|
|
return 1;
|
|
}
|
|
|
|
// Register Lua functions for navigation
|
|
static void RegisterLuaFunctions(const std::string& current_app_id, bool is_system_app)
|
|
{
|
|
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
|
|
lua_pushcfunction(L, LuaLoadScreen);
|
|
lua_setglobal(L, "loadScreen");
|
|
lua_pushcfunction(L, LuaGoHome);
|
|
lua_setglobal(L, "goHome");
|
|
lua_pushcfunction(L, LuaSwitchAppSandbox);
|
|
lua_setglobal(L, "switchAppSandbox");
|
|
Logger::Log("Registered Lua loadScreen, goHome, and switchAppSandbox functions");
|
|
|
|
// Register app management APIs
|
|
if (g_app_manager && g_update_service) {
|
|
mosis::RegisterAppAPIs(L, g_app_manager, g_update_service, current_app_id, is_system_app);
|
|
Logger::Log("Registered Lua app management APIs");
|
|
}
|
|
|
|
// Initialize and register sandbox APIs
|
|
RegisterSandboxAPIs(L);
|
|
}
|
|
|
|
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");
|
|
|
|
// Initialize app management system
|
|
// TODO: Get data root from Android context (for now use a placeholder)
|
|
std::string data_root = "/data/data/com.omixlab.mosis/files";
|
|
g_data_root = data_root; // Store for sandbox switching
|
|
m_app_manager = std::make_unique<mosis::AppManager>(data_root);
|
|
m_update_service = std::make_unique<mosis::UpdateService>(
|
|
m_app_manager.get(), "https://portal.mosis.dev/api/v1");
|
|
m_sandbox_manager = std::make_unique<mosis::LuaSandboxManager>(data_root);
|
|
m_app_manager->SetSandboxManager(m_sandbox_manager.get());
|
|
g_app_manager = m_app_manager.get();
|
|
g_update_service = m_update_service.get();
|
|
g_sandbox_manager = m_sandbox_manager.get();
|
|
Logger::Log("App management system initialized");
|
|
|
|
// Initialize RmlUi sandbox state (timer manager and VFS for home screen)
|
|
g_rmlui_timer_manager = std::make_unique<mosis::TimerManager>();
|
|
std::string home_sandbox_path = data_root + "/sandbox_data";
|
|
std::filesystem::create_directories(home_sandbox_path);
|
|
g_rmlui_vfs = std::make_unique<mosis::VirtualFS>(
|
|
"com.mosis.home", home_sandbox_path, mosis::VirtualFSLimits{});
|
|
Logger::Log("RmlUi sandbox initialized");
|
|
|
|
// Start background update checks (every 24 hours)
|
|
m_update_service->Start(std::chrono::hours(24));
|
|
|
|
// Register navigation functions with Lua
|
|
// Home screen is a system app with full access
|
|
RegisterLuaFunctions("com.mosis.home", true);
|
|
|
|
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();
|
|
}
|
|
|
|
// Update sandbox timers for running apps (legacy sandbox manager)
|
|
if (m_sandbox_manager) {
|
|
m_sandbox_manager->UpdateTimers();
|
|
}
|
|
|
|
// Update RmlUi sandbox timers (for UI-based apps)
|
|
if (g_rmlui_timer_manager) {
|
|
g_rmlui_timer_manager->ProcessTimers();
|
|
}
|
|
|
|
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){
|
|
int px = static_cast<int>(x * 540);
|
|
int py = static_cast<int>(y * 960);
|
|
bool move_handled = context->ProcessMouseMove(px, py, 0);
|
|
bool down_handled = context->ProcessMouseButtonDown(0, 0);
|
|
Logger::Log(std::format("Touch at ({}, {}): move={}, down={}", px, py, move_handled, down_handled));
|
|
|
|
// Debug: Check if there's a hover element
|
|
auto* hover_elem = context->GetHoverElement();
|
|
if (hover_elem) {
|
|
Logger::Log(std::format(" Hover element: {} (id={})",
|
|
hover_elem->GetTagName().c_str(),
|
|
hover_elem->GetId().c_str()));
|
|
} else {
|
|
Logger::Log(" No hover element at this position");
|
|
}
|
|
});
|
|
}
|
|
|
|
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;
|