From b14bad5d241362df3640bfda8fc58f90723e8a7b Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 3 Oct 2025 21:22:16 +0300 Subject: [PATCH 1/7] add platform::get_executable_path (#638) --- src/io/engine_paths.cpp | 2 ++ src/objects/rigging.cpp | 2 +- src/util/platform.cpp | 60 +++++++++++++++++++++++++++++++--- src/util/platform.hpp | 8 +++-- src/util/platform_internal.cpp | 24 ++++++++++++++ 5 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/util/platform_internal.cpp diff --git a/src/io/engine_paths.cpp b/src/io/engine_paths.cpp index 3526f387..98363d9b 100644 --- a/src/io/engine_paths.cpp +++ b/src/io/engine_paths.cpp @@ -6,6 +6,7 @@ #include #include "typedefs.hpp" #include "util/stringutil.hpp" +#include "util/platform.hpp" #include #include "io/devices/StdfsDevice.hpp" @@ -44,6 +45,7 @@ void EnginePaths::prepare() { resourcesFolder.string() + " is not a directory" ); } + logger.info() << "executable path: " << platform::get_executable_path().string(); logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); logger.info() << "project folder: " << fs::canonical(projectFolder).u8string(); diff --git a/src/objects/rigging.cpp b/src/objects/rigging.cpp index 3f50e905..1c0e9218 100644 --- a/src/objects/rigging.cpp +++ b/src/objects/rigging.cpp @@ -141,7 +141,7 @@ void SkeletonConfig::update( build_matrix(rotation, interpolation.getCurrent()) ); } else { - update(0, skeleton, root.get(), rotation); + update(0, skeleton, root.get(), build_matrix(rotation, position)); } } diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 2cdd0b89..7bb7f7f4 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -10,13 +10,22 @@ #include "stringutil.hpp" #include "typedefs.hpp" #include "debug/Logger.hpp" - -static debug::Logger logger("platform"); +#include "frontend/locale.hpp" #ifdef _WIN32 #include #pragma comment(lib, "winmm.lib") +#else +#include +#endif +namespace platform::internal { + std::filesystem::path get_executable_path(); +} + +static debug::Logger logger("platform"); + +#ifdef _WIN32 void platform::configure_encoding() { // set utf-8 encoding to console output SetConsoleOutputCP(CP_UTF8); @@ -83,9 +92,6 @@ bool platform::open_url(const std::string& url) { #else // _WIN32 -#include -#include "frontend/locale.hpp" - void platform::configure_encoding() { } @@ -158,3 +164,47 @@ void platform::open_folder(const std::filesystem::path& folder) { #endif } + +std::filesystem::path platform::get_executable_path() { +#ifdef _WIN32 + wchar_t buffer[MAX_PATH]; + DWORD result = GetModuleFileNameW(NULL, buffer, MAX_PATH); + if (result == 0) { + DWORD error = GetLastError(); + throw std::runtime_error("GetModuleFileName failed with code: " + std::to_string(error)); + } + + int size = WideCharToMultiByte( + CP_UTF8, 0, buffer, -1, nullptr, 0, nullptr, nullptr + ); + if (size == 0) { + throw std::runtime_error("could not get executable path"); + } + std::string str(size, 0); + size = WideCharToMultiByte( + CP_UTF8, 0, buffer, -1, str.data(), size, nullptr, nullptr + ); + if (size == 0) { + DWORD error = GetLastError(); + throw std::runtime_error("WideCharToMultiByte failed with code: " + std::to_string(error)); + } + str.resize(size - 1); + return std::filesystem::path(str); + +#elif defined(__APPLE__) + auto path = platform::internal::get_executable_path(); + if (path.empty()) { + throw std::runtime_error("could not get executable path"); + } + return path; +#else + char buffer[1024]; + ssize_t count = readlink("/proc/self/exe", buffer, sizeof(buffer)); + if (count != -1) { + return std::filesystem::canonical(std::filesystem::path( + std::string(buffer, static_cast(count)) + )); + } + throw std::runtime_error("could not get executable path"); +#endif +} diff --git a/src/util/platform.hpp b/src/util/platform.hpp index a638ee92..c4b45650 100644 --- a/src/util/platform.hpp +++ b/src/util/platform.hpp @@ -5,13 +5,17 @@ namespace platform { void configure_encoding(); - /// @return environment locale in ISO format ll_CC + /// @brief Get Environment locale in ISO format ll_CC std::string detect_locale(); /// @brief Open folder using system file manager asynchronously /// @param folder target folder void open_folder(const std::filesystem::path& folder); - /// Makes the current thread sleep for the specified amount of milliseconds. + /// @brief Makes the current thread sleep for the specified amount of milliseconds. void sleep(size_t millis); + /// @brief Get current process id int get_process_id(); + /// @brief Get current process running executable path + std::filesystem::path get_executable_path(); + /// @brief Open URL in web browser bool open_url(const std::string& url); } diff --git a/src/util/platform_internal.cpp b/src/util/platform_internal.cpp new file mode 100644 index 00000000..90168219 --- /dev/null +++ b/src/util/platform_internal.cpp @@ -0,0 +1,24 @@ +#include + +#ifndef _WIN32 +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +namespace platform::internal { + std::filesystem::path get_executable_path() { +#ifdef __APPLE__ + char buffer[1024]; + uint32_t size = sizeof(buffer); + if (_NSGetExecutablePath(buffer, &size) == 0) { + return std::filesystem::canonical(std::filesystem::path(buffer)); + } else { + return std::filesystem::path(); + } +#endif + return std::filesystem::path(); + } +} From 5a5bd5cb2ee2606d99aa904b34105c62d2f56091 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 3 Oct 2025 22:02:56 +0300 Subject: [PATCH 2/7] add platform::new_engine_instance --- src/util/platform.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++ src/util/platform.hpp | 3 ++ 2 files changed, 70 insertions(+) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index 7bb7f7f4..b36d5eb7 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -115,6 +115,73 @@ int platform::get_process_id() { return getpid(); } +void platform::new_engine_instance(const std::vector& args) { + auto executable = get_executable_path(); + +#ifdef _WIN32 + std::stringstream ss; + for (int i = 0; i < args.size(); i++) { + ss << " " << util::quote(args[i]); + } + + auto toWString = [](const std::string& src) { + if (src.empty()) + return L""; + int size = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, nullptr, 0); + if (size == 0) { + throw std::runtime_error( + "MultiByteToWideChar failed with code: " + + std::to_string(GetLastError()) + ) + } + std::vector buffer(size); + MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, buffer.data(), size); + return std::wstring(buffer.data()); + }; + + auto executableString = toWString(executable.u8string()); + auto argsString = toWString(ss.str()); + + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi = { 0 }; + DWORD flags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS; + // | CREATE_NO_WINDOW; + BOOL success = CreateProcessW( + executable.u8string().c_str(), + argsString.c_str(), + nullptr, + nullptr, + FALSE, + flags, + nullptr, + "", + &si, + &pi + ); + if (!success) { + throw std::runtime_error( + "starting an engine instance failed with code: " + + std::to_string(GetLastError()) + ); + } +#else + std::stringstream ss; + ss << executable; + for (int i = 0; i < args.size(); i++) { + ss << " " << util::quote(args[i]); + } + ss << " >/dev/null &"; + + auto command = ss.str(); + if (int res = system(command.c_str())) { + throw std::runtime_error( + "starting an engine instance failed with code: " + + std::to_string(res) + ); + } +#endif +} + bool platform::open_url(const std::string& url) { if (url.empty()) return false; diff --git a/src/util/platform.hpp b/src/util/platform.hpp index c4b45650..148c64c4 100644 --- a/src/util/platform.hpp +++ b/src/util/platform.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace platform { @@ -16,6 +17,8 @@ namespace platform { int get_process_id(); /// @brief Get current process running executable path std::filesystem::path get_executable_path(); + /// @brief Run a separate engine instance with specified arguments + void new_engine_instance(const std::vector& args); /// @brief Open URL in web browser bool open_url(const std::string& url); } From 5e542225c79ceb6eac06466ffc164c19b8df0138 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 3 Oct 2025 22:03:27 +0300 Subject: [PATCH 3/7] update workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 5a01a618..447e1280 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -46,7 +46,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always - name: Build AppImage uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 env: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f8fe3dfa..ac9f8979 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,7 +39,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always - name: Create DMG run: | mkdir VoxelEngineDmgContent diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index cdf354dd..5c26937e 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -66,4 +66,4 @@ jobs: shell: msys2 {0} working-directory: ${{ github.workspace }} run: | - packaged/vctest.exe -e packaged/VoxelCore.exe -d dev/tests -u build + packaged/vctest.exe -e packaged/VoxelCore.exe -d dev/tests -u build --output-always From 317aa710c4d40a1fd59ae76b0c740eeea0893871 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Fri, 3 Oct 2025 23:41:53 +0300 Subject: [PATCH 4/7] test --- src/graphics/ui/GUI.cpp | 8 +++ src/util/platform.cpp | 135 ++++++++++++++++++++-------------------- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 3ab438b1..9faed8f7 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -19,6 +19,7 @@ #include "graphics/core/DrawContext.hpp" #include "graphics/core/Shader.hpp" #include "gui_util.hpp" +#include "util/platform.hpp" #include "window/Camera.hpp" #include "window/Window.hpp" #include "window/input.hpp" @@ -204,6 +205,13 @@ void GUI::act(float delta, const glm::uvec2& vp) { container->act(delta); auto prevfocus = focus; + if (input.jpressed(Keycode::BACKSPACE)) { + platform::new_engine_instance({ + "--res", engine.getPaths().getResourcesFolder().u8string(), + "--dir", engine.getPaths().getUserFilesFolder().u8string() + }); + } + updateTooltip(delta); const auto& cursor = input.getCursor(); diff --git a/src/util/platform.cpp b/src/util/platform.cpp index b36d5eb7..b4d71db4 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -115,73 +115,6 @@ int platform::get_process_id() { return getpid(); } -void platform::new_engine_instance(const std::vector& args) { - auto executable = get_executable_path(); - -#ifdef _WIN32 - std::stringstream ss; - for (int i = 0; i < args.size(); i++) { - ss << " " << util::quote(args[i]); - } - - auto toWString = [](const std::string& src) { - if (src.empty()) - return L""; - int size = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, nullptr, 0); - if (size == 0) { - throw std::runtime_error( - "MultiByteToWideChar failed with code: " + - std::to_string(GetLastError()) - ) - } - std::vector buffer(size); - MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, buffer.data(), size); - return std::wstring(buffer.data()); - }; - - auto executableString = toWString(executable.u8string()); - auto argsString = toWString(ss.str()); - - STARTUPINFOW si = { sizeof(si) }; - PROCESS_INFORMATION pi = { 0 }; - DWORD flags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS; - // | CREATE_NO_WINDOW; - BOOL success = CreateProcessW( - executable.u8string().c_str(), - argsString.c_str(), - nullptr, - nullptr, - FALSE, - flags, - nullptr, - "", - &si, - &pi - ); - if (!success) { - throw std::runtime_error( - "starting an engine instance failed with code: " + - std::to_string(GetLastError()) - ); - } -#else - std::stringstream ss; - ss << executable; - for (int i = 0; i < args.size(); i++) { - ss << " " << util::quote(args[i]); - } - ss << " >/dev/null &"; - - auto command = ss.str(); - if (int res = system(command.c_str())) { - throw std::runtime_error( - "starting an engine instance failed with code: " + - std::to_string(res) - ); - } -#endif -} - bool platform::open_url(const std::string& url) { if (url.empty()) return false; @@ -275,3 +208,71 @@ std::filesystem::path platform::get_executable_path() { throw std::runtime_error("could not get executable path"); #endif } + +void platform::new_engine_instance(const std::vector& args) { + auto executable = get_executable_path(); + +#ifdef _WIN32 + std::stringstream ss; + for (int i = 0; i < args.size(); i++) { + ss << " " << util::quote(args[i]); + } + + auto toWString = [](const std::string& src) -> std::wstring { + if (src.empty()) + return L""; + int size = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, nullptr, 0); + if (size == 0) { + throw std::runtime_error( + "MultiByteToWideChar failed with code: " + + std::to_string(GetLastError()) + ); + } + std::vector buffer(size + 1); + buffer[size] = 0; + MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, buffer.data(), size); + return std::wstring(buffer.data(), size); + }; + + auto executableString = toWString(executable.u8string()); + auto argsString = toWString(ss.str()); + + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi = { 0 }; + DWORD flags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS; + // | CREATE_NO_WINDOW; + BOOL success = CreateProcessW( + executableString.c_str(), + argsString.data(), + nullptr, + nullptr, + FALSE, + flags, + nullptr, + L"", + &si, + &pi + ); + if (!success) { + throw std::runtime_error( + "starting an engine instance failed with code: " + + std::to_string(GetLastError()) + ); + } +#else + std::stringstream ss; + ss << executable; + for (int i = 0; i < args.size(); i++) { + ss << " " << util::quote(args[i]); + } + ss << " >/dev/null &"; + + auto command = ss.str(); + if (int res = system(command.c_str())) { + throw std::runtime_error( + "starting an engine instance failed with code: " + + std::to_string(res) + ); + } +#endif +} From 68bdcf07b28577a4b27c02eba97ce5bcc190d051 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 4 Oct 2025 01:51:47 +0300 Subject: [PATCH 5/7] fix CreateProcessW use --- src/util/platform.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/util/platform.cpp b/src/util/platform.cpp index b4d71db4..55ac1630 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -214,6 +214,7 @@ void platform::new_engine_instance(const std::vector& args) { #ifdef _WIN32 std::stringstream ss; + ss << util::quote(executable.u8string()); for (int i = 0; i < args.size(); i++) { ss << " " << util::quote(args[i]); } @@ -230,30 +231,38 @@ void platform::new_engine_instance(const std::vector& args) { } std::vector buffer(size + 1); buffer[size] = 0; - MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, buffer.data(), size); - return std::wstring(buffer.data(), size); + size = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, buffer.data(), size); + if (size == 0) { + throw std::runtime_error( + "MultiByteToWideChar failed with code: " + + std::to_string(GetLastError()) + ); + } + return std::wstring(buffer.data(), size + 1); }; - auto executableString = toWString(executable.u8string()); - auto argsString = toWString(ss.str()); + auto commandString = toWString(ss.str()); STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi = { 0 }; DWORD flags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS; // | CREATE_NO_WINDOW; BOOL success = CreateProcessW( - executableString.c_str(), - argsString.data(), + nullptr, + commandString.data(), nullptr, nullptr, FALSE, flags, nullptr, - L"", + nullptr, &si, &pi ); - if (!success) { + if (success) { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } else { throw std::runtime_error( "starting an engine instance failed with code: " + std::to_string(GetLastError()) From 8ef1ccbb30a056318f48c44ebf70d49089b82568 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 4 Oct 2025 01:54:49 +0300 Subject: [PATCH 6/7] revert test --- src/graphics/ui/GUI.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 9faed8f7..3ab438b1 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -19,7 +19,6 @@ #include "graphics/core/DrawContext.hpp" #include "graphics/core/Shader.hpp" #include "gui_util.hpp" -#include "util/platform.hpp" #include "window/Camera.hpp" #include "window/Window.hpp" #include "window/input.hpp" @@ -205,13 +204,6 @@ void GUI::act(float delta, const glm::uvec2& vp) { container->act(delta); auto prevfocus = focus; - if (input.jpressed(Keycode::BACKSPACE)) { - platform::new_engine_instance({ - "--res", engine.getPaths().getResourcesFolder().u8string(), - "--dir", engine.getPaths().getUserFilesFolder().u8string() - }); - } - updateTooltip(delta); const auto& cursor = input.getCursor(); From 72e98bf8cd1c1808ba76aef14afc721304a51e85 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 4 Oct 2025 01:56:55 +0300 Subject: [PATCH 7/7] revert workflows --- .github/workflows/appimage.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows-clang.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 447e1280..5a01a618 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -46,7 +46,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build - name: Build AppImage uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910 env: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ac9f8979..f8fe3dfa 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -39,7 +39,7 @@ jobs: run: | chmod +x build/VoxelEngine chmod +x AppDir/usr/bin/vctest - AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always + AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build - name: Create DMG run: | mkdir VoxelEngineDmgContent diff --git a/.github/workflows/windows-clang.yml b/.github/workflows/windows-clang.yml index 5c26937e..cdf354dd 100644 --- a/.github/workflows/windows-clang.yml +++ b/.github/workflows/windows-clang.yml @@ -66,4 +66,4 @@ jobs: shell: msys2 {0} working-directory: ${{ github.workspace }} run: | - packaged/vctest.exe -e packaged/VoxelCore.exe -d dev/tests -u build --output-always + packaged/vctest.exe -e packaged/VoxelCore.exe -d dev/tests -u build