Files
SDL3CPlusPlus/src/services/impl/script_engine_service.cpp

843 lines
28 KiB
C++

#include "script_engine_service.hpp"
#include "lua_helpers.hpp"
#include "services/interfaces/i_logger.hpp"
#include <btBulletDynamicsCommon.h>
#include <lua.hpp>
#include <SDL3/SDL.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <algorithm>
#include <array>
#include <cctype>
#include <stdexcept>
#include <string>
#include <utility>
namespace {
bool TryParseMouseButtonName(const char* name, uint8_t& button) {
if (!name) {
return false;
}
std::string lower(name);
std::transform(lower.begin(), lower.end(), lower.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
if (lower == "left") {
button = SDL_BUTTON_LEFT;
return true;
}
if (lower == "right") {
button = SDL_BUTTON_RIGHT;
return true;
}
if (lower == "middle") {
button = SDL_BUTTON_MIDDLE;
return true;
}
if (lower == "x1" || lower == "extra1") {
button = SDL_BUTTON_X1;
return true;
}
if (lower == "x2" || lower == "extra2") {
button = SDL_BUTTON_X2;
return true;
}
return false;
}
SDL_Keycode ReadKeycodeFromLua(lua_State* L, int index, bool& ok) {
ok = true;
if (lua_isnumber(L, index)) {
return static_cast<SDL_Keycode>(lua_tointeger(L, index));
}
if (lua_isstring(L, index)) {
const char* name = lua_tostring(L, index);
SDL_Keycode key = SDL_GetKeyFromName(name);
if (key == SDLK_UNKNOWN) {
ok = false;
}
return key;
}
ok = false;
return SDLK_UNKNOWN;
}
void PushJsonValue(lua_State* L, const rapidjson::Value& value) {
if (value.IsObject()) {
lua_newtable(L);
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
lua_pushlstring(L, it->name.GetString(), it->name.GetStringLength());
PushJsonValue(L, it->value);
lua_settable(L, -3);
}
return;
}
if (value.IsArray()) {
lua_newtable(L);
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
PushJsonValue(L, value[i]);
lua_rawseti(L, -2, static_cast<lua_Integer>(i + 1));
}
return;
}
if (value.IsString()) {
lua_pushlstring(L, value.GetString(), value.GetStringLength());
return;
}
if (value.IsBool()) {
lua_pushboolean(L, value.GetBool());
return;
}
if (value.IsNumber()) {
lua_pushnumber(L, value.GetDouble());
return;
}
lua_pushnil(L);
}
bool PushConfigTableFromJson(lua_State* L, const std::string& json, std::string& error) {
if (json.empty()) {
error = "Config JSON not available";
return false;
}
rapidjson::Document document;
rapidjson::ParseResult result = document.Parse(json.c_str());
if (!result) {
error = std::string("Config JSON parse failed: ") + rapidjson::GetParseError_En(result.Code());
return false;
}
PushJsonValue(L, document);
return true;
}
} // namespace
namespace sdl3cpp::services::impl {
ScriptEngineService::ScriptEngineService(const std::filesystem::path& scriptPath,
std::shared_ptr<ILogger> logger,
std::shared_ptr<IMeshService> meshService,
std::shared_ptr<IAudioCommandService> audioCommandService,
std::shared_ptr<IPhysicsBridgeService> physicsBridgeService,
std::shared_ptr<IInputService> inputService,
std::shared_ptr<IWindowService> windowService,
std::shared_ptr<IConfigService> configService,
bool debugEnabled)
: logger_(std::move(logger)),
meshService_(std::move(meshService)),
audioCommandService_(std::move(audioCommandService)),
physicsBridgeService_(std::move(physicsBridgeService)),
inputService_(std::move(inputService)),
windowService_(std::move(windowService)),
configService_(std::move(configService)),
scriptPath_(scriptPath),
debugEnabled_(debugEnabled) {
if (logger_) {
logger_->Trace("ScriptEngineService", "ScriptEngineService",
"scriptPath=" + scriptPath_.string() +
", debugEnabled=" + std::string(debugEnabled_ ? "true" : "false") +
", meshService=" + std::string(meshService_ ? "set" : "null") +
", audioCommandService=" + std::string(audioCommandService_ ? "set" : "null") +
", physicsBridgeService=" + std::string(physicsBridgeService_ ? "set" : "null") +
", inputService=" + std::string(inputService_ ? "set" : "null") +
", windowService=" + std::string(windowService_ ? "set" : "null") +
", configService=" + std::string(configService_ ? "set" : "null"));
}
}
ScriptEngineService::~ScriptEngineService() {
if (logger_) {
logger_->Trace("ScriptEngineService", "~ScriptEngineService");
}
if (initialized_) {
Shutdown();
}
}
void ScriptEngineService::Initialize() {
if (initialized_) {
return;
}
logger_->Trace("ScriptEngineService", "Initialize",
"scriptPath=" + scriptPath_.string() +
", debugEnabled=" + std::string(debugEnabled_ ? "true" : "false"));
bindingContext_ = std::make_shared<LuaBindingContext>();
bindingContext_->meshService = meshService_;
bindingContext_->audioCommandService = audioCommandService_;
bindingContext_->physicsBridgeService = physicsBridgeService_;
bindingContext_->inputService = inputService_;
bindingContext_->windowService = windowService_;
bindingContext_->configService = configService_;
bindingContext_->logger = logger_;
luaState_ = luaL_newstate();
if (!luaState_) {
logger_->Error("Failed to create Lua state");
throw std::runtime_error("Failed to create Lua state");
}
luaL_openlibs(luaState_);
RegisterBindings(luaState_);
lua_pushboolean(luaState_, debugEnabled_);
lua_setglobal(luaState_, "lua_debug");
if (configService_) {
std::string error;
if (PushConfigTableFromJson(luaState_, configService_->GetConfigJson(), error)) {
lua_setglobal(luaState_, "config");
} else {
if (logger_) {
logger_->Error("ScriptEngineService: " + error);
}
lua_newtable(luaState_);
lua_setglobal(luaState_, "config");
}
} else {
if (logger_) {
logger_->Error("ScriptEngineService: Config service not available");
}
lua_newtable(luaState_);
lua_setglobal(luaState_, "config");
}
scriptDirectory_ = scriptPath_.parent_path();
if (!scriptDirectory_.empty()) {
lua_getglobal(luaState_, "package");
if (lua_istable(luaState_, -1)) {
lua_getfield(luaState_, -1, "path");
const char* currentPath = lua_tostring(luaState_, -1);
std::string newPath = scriptDirectory_.string() + "/?.lua;";
if (currentPath) {
newPath += currentPath;
}
lua_pop(luaState_, 1);
lua_pushstring(luaState_, newPath.c_str());
lua_setfield(luaState_, -2, "path");
}
lua_pop(luaState_, 1);
}
if (luaL_dofile(luaState_, scriptPath_.string().c_str()) != LUA_OK) {
std::string message = lua::GetLuaError(luaState_);
lua_pop(luaState_, 1);
lua_close(luaState_);
luaState_ = nullptr;
if (logger_) {
logger_->Error("Failed to load Lua script: " + message);
}
throw std::runtime_error("Failed to load Lua script: " + message);
}
initialized_ = true;
logger_->Info("Script engine service initialized");
}
void ScriptEngineService::Shutdown() noexcept {
if (!initialized_) {
return;
}
logger_->Trace("ScriptEngineService", "Shutdown");
if (luaState_) {
lua_close(luaState_);
luaState_ = nullptr;
}
bindingContext_.reset();
initialized_ = false;
logger_->Info("Script engine service shutdown");
}
lua_State* ScriptEngineService::GetLuaState() const {
if (logger_) {
logger_->Trace("ScriptEngineService", "GetLuaState");
}
if (!luaState_) {
throw std::runtime_error("Script engine service not initialized");
}
return luaState_;
}
std::filesystem::path ScriptEngineService::GetScriptDirectory() const {
if (logger_) {
logger_->Trace("ScriptEngineService", "GetScriptDirectory");
}
if (!luaState_) {
throw std::runtime_error("Script engine service not initialized");
}
return scriptDirectory_;
}
void ScriptEngineService::RegisterBindings(lua_State* L) {
if (logger_) {
logger_->Trace("ScriptEngineService", "RegisterBindings");
}
if (!bindingContext_) {
throw std::runtime_error("Lua binding context not initialized");
}
auto bind = [&](const char* name, lua_CFunction fn) {
lua_pushlightuserdata(L, bindingContext_.get());
lua_pushcclosure(L, fn, 1);
lua_setglobal(L, name);
};
bind("load_mesh_from_file", &ScriptEngineService::LoadMeshFromFile);
bind("physics_create_box", &ScriptEngineService::PhysicsCreateBox);
bind("physics_step_simulation", &ScriptEngineService::PhysicsStepSimulation);
bind("physics_get_transform", &ScriptEngineService::PhysicsGetTransform);
bind("glm_matrix_from_transform", &ScriptEngineService::GlmMatrixFromTransform);
bind("audio_play_background", &ScriptEngineService::AudioPlayBackground);
bind("audio_play_sound", &ScriptEngineService::AudioPlaySound);
bind("audio_stop_background", &ScriptEngineService::AudioStopBackground);
bind("input_get_mouse_position", &ScriptEngineService::InputGetMousePosition);
bind("input_get_mouse_delta", &ScriptEngineService::InputGetMouseDelta);
bind("input_get_mouse_wheel", &ScriptEngineService::InputGetMouseWheel);
bind("input_is_key_down", &ScriptEngineService::InputIsKeyDown);
bind("input_is_action_down", &ScriptEngineService::InputIsActionDown);
bind("input_is_mouse_down", &ScriptEngineService::InputIsMouseDown);
bind("input_get_text", &ScriptEngineService::InputGetText);
bind("config_get_json", &ScriptEngineService::ConfigGetJson);
bind("config_get_table", &ScriptEngineService::ConfigGetTable);
bind("window_get_size", &ScriptEngineService::WindowGetSize);
bind("window_set_title", &ScriptEngineService::WindowSetTitle);
bind("window_is_minimized", &ScriptEngineService::WindowIsMinimized);
bind("window_set_mouse_grabbed", &ScriptEngineService::WindowSetMouseGrabbed);
bind("window_get_mouse_grabbed", &ScriptEngineService::WindowGetMouseGrabbed);
bind("window_set_relative_mouse_mode", &ScriptEngineService::WindowSetRelativeMouseMode);
bind("window_get_relative_mouse_mode", &ScriptEngineService::WindowGetRelativeMouseMode);
bind("window_set_cursor_visible", &ScriptEngineService::WindowSetCursorVisible);
bind("window_is_cursor_visible", &ScriptEngineService::WindowIsCursorVisible);
bind("print", &ScriptEngineService::LuaPrint);
}
int ScriptEngineService::LoadMeshFromFile(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->meshService) {
lua_pushnil(L);
lua_pushstring(L, "Mesh service not available");
return 2;
}
const char* path = luaL_checkstring(L, 1);
if (logger) {
logger->Trace("ScriptEngineService", "LoadMeshFromFile",
"path=" + std::string(path));
}
MeshPayload payload;
std::string error;
if (!context->meshService->LoadFromFile(path, payload, error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
context->meshService->PushMeshToLua(L, payload);
lua_pushnil(L);
return 2;
}
int ScriptEngineService::PhysicsCreateBox(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->physicsBridgeService) {
lua_pushnil(L);
lua_pushstring(L, "Physics service not available");
return 2;
}
const char* name = luaL_checkstring(L, 1);
if (logger) {
logger->Trace("ScriptEngineService", "PhysicsCreateBox",
"name=" + std::string(name));
}
if (!lua_istable(L, 2) || !lua_istable(L, 4) || !lua_istable(L, 5)) {
luaL_error(L, "physics_create_box expects vector tables for half extents, origin, and rotation");
}
std::array<float, 3> halfExtents = lua::ReadVector3(L, 2);
float mass = static_cast<float>(luaL_checknumber(L, 3));
std::array<float, 3> origin = lua::ReadVector3(L, 4);
std::array<float, 4> rotation = lua::ReadQuaternion(L, 5);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(origin[0], origin[1], origin[2]));
transform.setRotation(btQuaternion(rotation[0], rotation[1], rotation[2], rotation[3]));
std::string error;
if (!context->physicsBridgeService->AddBoxRigidBody(
name,
btVector3(halfExtents[0], halfExtents[1], halfExtents[2]),
mass,
transform,
error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::PhysicsStepSimulation(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->physicsBridgeService) {
lua_pushinteger(L, 0);
return 1;
}
if (logger) {
logger->Trace("ScriptEngineService", "PhysicsStepSimulation");
}
float deltaTime = static_cast<float>(luaL_checknumber(L, 1));
int steps = context->physicsBridgeService->StepSimulation(deltaTime);
lua_pushinteger(L, steps);
return 1;
}
int ScriptEngineService::PhysicsGetTransform(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->physicsBridgeService) {
lua_pushnil(L);
lua_pushstring(L, "Physics service not available");
return 2;
}
const char* name = luaL_checkstring(L, 1);
if (logger) {
logger->Trace("ScriptEngineService", "PhysicsGetTransform",
"name=" + std::string(name));
}
btTransform transform;
std::string error;
if (!context->physicsBridgeService->GetRigidBodyTransform(name, transform, error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
lua_newtable(L);
lua_newtable(L);
const btVector3& origin = transform.getOrigin();
lua_pushnumber(L, origin.x());
lua_rawseti(L, -2, 1);
lua_pushnumber(L, origin.y());
lua_rawseti(L, -2, 2);
lua_pushnumber(L, origin.z());
lua_rawseti(L, -2, 3);
lua_setfield(L, -2, "position");
lua_newtable(L);
const btQuaternion& orientation = transform.getRotation();
lua_pushnumber(L, orientation.x());
lua_rawseti(L, -2, 1);
lua_pushnumber(L, orientation.y());
lua_rawseti(L, -2, 2);
lua_pushnumber(L, orientation.z());
lua_rawseti(L, -2, 3);
lua_pushnumber(L, orientation.w());
lua_rawseti(L, -2, 4);
lua_setfield(L, -2, "rotation");
return 1;
}
int ScriptEngineService::AudioPlayBackground(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->audioCommandService) {
lua_pushnil(L);
lua_pushstring(L, "Audio service not available");
return 2;
}
const char* path = luaL_checkstring(L, 1);
bool loop = true;
if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) {
loop = lua_toboolean(L, 2);
}
if (logger) {
logger->Trace("ScriptEngineService", "AudioPlayBackground",
"path=" + std::string(path) +
", loop=" + std::string(loop ? "true" : "false"));
}
std::string error;
if (!context->audioCommandService->QueueAudioCommand(
AudioCommandType::Background, path, loop, error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::AudioPlaySound(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->audioCommandService) {
lua_pushnil(L);
lua_pushstring(L, "Audio service not available");
return 2;
}
const char* path = luaL_checkstring(L, 1);
bool loop = false;
if (lua_gettop(L) >= 2 && lua_isboolean(L, 2)) {
loop = lua_toboolean(L, 2);
}
if (logger) {
logger->Trace("ScriptEngineService", "AudioPlaySound",
"path=" + std::string(path) +
", loop=" + std::string(loop ? "true" : "false"));
}
std::string error;
if (!context->audioCommandService->QueueAudioCommand(
AudioCommandType::Effect, path, loop, error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::AudioStopBackground(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (!context || !context->audioCommandService) {
lua_pushnil(L);
lua_pushstring(L, "Audio service not available");
return 2;
}
if (logger) {
logger->Trace("ScriptEngineService", "AudioStopBackground");
}
std::string error;
if (!context->audioCommandService->StopBackground(error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::InputGetMousePosition(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
auto [x, y] = context->inputService->GetMousePosition();
lua_pushnumber(L, x);
lua_pushnumber(L, y);
return 2;
}
int ScriptEngineService::InputGetMouseDelta(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
const auto& state = context->inputService->GetState();
lua_pushnumber(L, state.mouseDeltaX);
lua_pushnumber(L, state.mouseDeltaY);
return 2;
}
int ScriptEngineService::InputGetMouseWheel(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
const auto& state = context->inputService->GetState();
lua_pushnumber(L, state.mouseWheelDeltaX);
lua_pushnumber(L, state.mouseWheelDeltaY);
return 2;
}
int ScriptEngineService::InputIsKeyDown(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
bool ok = false;
SDL_Keycode key = ReadKeycodeFromLua(L, 1, ok);
if (!ok) {
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L, context->inputService->IsKeyPressed(key));
return 1;
}
int ScriptEngineService::InputIsActionDown(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
const char* action = luaL_checkstring(L, 1);
lua_pushboolean(L, context->inputService->IsActionPressed(action));
return 1;
}
int ScriptEngineService::InputIsMouseDown(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
uint8_t button = SDL_BUTTON_LEFT;
if (lua_isnumber(L, 1)) {
button = static_cast<uint8_t>(lua_tointeger(L, 1));
} else if (lua_isstring(L, 1)) {
const char* name = lua_tostring(L, 1);
if (!TryParseMouseButtonName(name, button)) {
lua_pushboolean(L, 0);
return 1;
}
} else {
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L, context->inputService->IsMouseButtonPressed(button));
return 1;
}
int ScriptEngineService::InputGetText(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->inputService) {
lua_pushnil(L);
lua_pushstring(L, "Input service not available");
return 2;
}
const auto& state = context->inputService->GetState();
lua_pushstring(L, state.textInput.c_str());
return 1;
}
int ScriptEngineService::ConfigGetJson(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->configService) {
lua_pushnil(L);
lua_pushstring(L, "Config service not available");
return 2;
}
const std::string& json = context->configService->GetConfigJson();
if (json.empty()) {
lua_pushnil(L);
lua_pushstring(L, "Config JSON not available");
return 2;
}
lua_pushlstring(L, json.c_str(), json.size());
return 1;
}
int ScriptEngineService::ConfigGetTable(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->configService) {
lua_pushnil(L);
lua_pushstring(L, "Config service not available");
return 2;
}
std::string error;
if (!PushConfigTableFromJson(L, context->configService->GetConfigJson(), error)) {
lua_pushnil(L);
lua_pushstring(L, error.c_str());
return 2;
}
return 1;
}
int ScriptEngineService::WindowGetSize(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
auto [width, height] = context->windowService->GetSize();
lua_pushinteger(L, static_cast<lua_Integer>(width));
lua_pushinteger(L, static_cast<lua_Integer>(height));
return 2;
}
int ScriptEngineService::WindowSetTitle(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
const char* title = luaL_checkstring(L, 1);
context->windowService->SetTitle(title);
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::WindowIsMinimized(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
lua_pushboolean(L, context->windowService->IsMinimized());
return 1;
}
int ScriptEngineService::WindowSetMouseGrabbed(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
bool grabbed = lua_toboolean(L, 1);
context->windowService->SetMouseGrabbed(grabbed);
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::WindowGetMouseGrabbed(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
lua_pushboolean(L, context->windowService->IsMouseGrabbed());
return 1;
}
int ScriptEngineService::WindowSetRelativeMouseMode(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
bool enabled = lua_toboolean(L, 1);
context->windowService->SetRelativeMouseMode(enabled);
if (context->inputService) {
context->inputService->SetRelativeMouseMode(enabled);
}
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::WindowGetRelativeMouseMode(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
lua_pushboolean(L, context->windowService->IsRelativeMouseMode());
return 1;
}
int ScriptEngineService::WindowSetCursorVisible(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
bool visible = lua_toboolean(L, 1);
context->windowService->SetCursorVisible(visible);
lua_pushboolean(L, 1);
return 1;
}
int ScriptEngineService::WindowIsCursorVisible(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
if (!context || !context->windowService) {
lua_pushnil(L);
lua_pushstring(L, "Window service not available");
return 2;
}
lua_pushboolean(L, context->windowService->IsCursorVisible());
return 1;
}
int ScriptEngineService::GlmMatrixFromTransform(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
if (logger) {
logger->Trace("ScriptEngineService", "GlmMatrixFromTransform",
"luaStateIsNull=" + std::string(L ? "false" : "true"));
}
return lua::LuaGlmMatrixFromTransform(L);
}
int ScriptEngineService::LuaPrint(lua_State* L) {
auto* context = static_cast<LuaBindingContext*>(lua_touserdata(L, lua_upvalueindex(1)));
auto logger = context ? context->logger : nullptr;
int nargs = lua_gettop(L);
std::string message;
for (int i = 1; i <= nargs; ++i) {
if (i > 1) {
message += "\t";
}
const char* str = nullptr;
if (lua_isstring(L, i)) {
str = lua_tostring(L, i);
} else if (lua_isnil(L, i)) {
str = "nil";
} else if (lua_isboolean(L, i)) {
str = lua_toboolean(L, i) ? "true" : "false";
} else if (lua_isnumber(L, i)) {
lua_pushvalue(L, i);
str = lua_tostring(L, -1);
lua_pop(L, 1);
} else {
lua_pushvalue(L, i);
str = lua_tostring(L, -1);
if (!str) {
str = lua_typename(L, lua_type(L, i));
}
lua_pop(L, 1);
}
if (str) {
message += str;
}
}
if (logger) {
logger->Info("[Lua] " + message);
}
return 0;
}
} // namespace sdl3cpp::services::impl