diff --git a/CMakeLists.txt b/CMakeLists.txt index b992d62..f4ad78c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,10 @@ endif () list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") +if (APPLE) + set(VCPKG_OVERLAY_TRIPLETS "${PROJECT_SOURCE_DIR}/triplets") +endif () + include(${PROJECT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake) set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".") @@ -84,12 +88,12 @@ if (APPLE) endif() if (WIN32) - set (SIMPLEGRAPHIC_PLATFORM_SOURCES + list(APPEND SIMPLEGRAPHIC_PLATFORM_SOURCES "engine/system/win/sys_console.cpp" "SimpleGraphic.rc" ) else() - set (SIMPLEGRAPHIC_PLATFORM_SOURCES + list(APPEND SIMPLEGRAPHIC_PLATFORM_SOURCES "engine/system/win/sys_console_unix.cpp" ) endif() @@ -222,6 +226,41 @@ if (APPLE) ${CORE_FOUNDATION_LIBRARY} ${APPLICATION_SERVICES_LIBRARY} ) + set_target_properties(SimpleGraphic PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@loader_path" + ) + file(GLOB ANGLE_DYLIBS + "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/*EGL*.dylib" + "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/*GLESv2*.dylib") + set(ANGLE_EGL_NAME "") + set(ANGLE_GLES_NAME "") + foreach (angle_dylib IN LISTS ANGLE_DYLIBS) + get_filename_component(angle_dylib_name "${angle_dylib}" NAME) + if (angle_dylib_name MATCHES "EGL") + set(ANGLE_EGL_NAME "${angle_dylib_name}") + elseif (angle_dylib_name MATCHES "GLESv2") + set(ANGLE_GLES_NAME "${angle_dylib_name}") + endif () + endforeach () + if (NOT ANGLE_EGL_NAME OR NOT ANGLE_GLES_NAME) + message(FATAL_ERROR "Shared ANGLE dylibs not found in ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib (got: '${ANGLE_DYLIBS}'); expected dynamic ANGLE via triplets/arm64-osx.cmake") + endif () + install(FILES ${ANGLE_DYLIBS} DESTINATION ".") + # GLFW loads EGL at runtime by these canonical names (egl_context.c), so + # alias the ANGLE dylibs, whose install names stay @rpath-based. + install(CODE " + file(REMOVE \"\${CMAKE_INSTALL_PREFIX}/libEGL.dylib\" \"\${CMAKE_INSTALL_PREFIX}/libGLESv2.dylib\") + file(CREATE_LINK ${ANGLE_EGL_NAME} \"\${CMAKE_INSTALL_PREFIX}/libEGL.dylib\" SYMBOLIC) + file(CREATE_LINK ${ANGLE_GLES_NAME} \"\${CMAKE_INSTALL_PREFIX}/libGLESv2.dylib\" SYMBOLIC) + ") +endif () + +# Static triplets (e.g. arm64-osx) only provide the static zstd target +if (TARGET zstd::libzstd_shared) + set(ZSTD_TARGET zstd::libzstd_shared) +else () + set(ZSTD_TARGET zstd::libzstd) endif () target_link_libraries(SimpleGraphic @@ -240,11 +279,11 @@ target_link_libraries(SimpleGraphic Threads::Threads WebP::webpdecoder ZLIB::ZLIB - zstd::libzstd_shared + ${ZSTD_TARGET} ) install(FILES $ DESTINATION ".") -install(TARGETS SimpleGraphic RUNTIME DESTINATION ".") +install(TARGETS SimpleGraphic RUNTIME DESTINATION "." LIBRARY DESTINATION ".") if (WIN32) set(DEPS_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") @@ -283,6 +322,18 @@ set(LCURL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/Lua-cURLv3) file(GLOB LCURL_SOURCES ${LCURL_SOURCE_DIR}/src/**.c) add_library(lcurl SHARED ${LCURL_SOURCES}) +if (NOT WIN32) + # When linking LuaJIT statically, Lua-cURLv3's local Lua 5.2-compat + # luaL_setfuncs shim collides with the one LuaJIT itself exports. + # Rename the local shim (and its internal callers) instead of patching + # the submodule; drop once https://github.com/Lua-cURL/Lua-cURLv3/pull/197 + # is merged and the submodule is bumped. + target_compile_definitions(lcurl + PRIVATE + "luaL_setfuncs=lcurl_compat_setfuncs" + ) +endif () + target_include_directories(lcurl PRIVATE ${LCURL_SOURCE_DIR}/src @@ -294,17 +345,28 @@ target_link_libraries(lcurl LuaJIT::LuaJIT ) -install(TARGETS lcurl RUNTIME DESTINATION ".") +install(TARGETS lcurl RUNTIME DESTINATION "." LIBRARY DESTINATION ".") install(FILES $ DESTINATION ".") # luautf8 module add_library(lua-utf8 SHARED libs/luautf8/lutf8lib.c) -target_compile_definitions(lua-utf8 - PRIVATE - LUA_BUILD_AS_DLL -) +if (WIN32) + target_compile_definitions(lua-utf8 + PRIVATE + LUA_BUILD_AS_DLL + ) +else () + # lutf8lib.c uses INT_MAX without including ; force-include it + # instead of patching the submodule; drop once + # https://github.com/starwing/luautf8/pull/62 is merged and the submodule + # is bumped. + target_compile_options(lua-utf8 + PRIVATE + -include limits.h + ) +endif () target_include_directories(lua-utf8 PRIVATE @@ -315,11 +377,17 @@ target_link_libraries(lua-utf8 LuaJIT::LuaJIT ) -install(TARGETS lua-utf8 RUNTIME DESTINATION ".") +install(TARGETS lua-utf8 RUNTIME DESTINATION "." LIBRARY DESTINATION ".") install(FILES $ DESTINATION ".") # luasocket module +if (WIN32) + set(LUASOCKET_PLATFORM_SOURCES "libs/luasocket/src/wsocket.c") +else () + set(LUASOCKET_PLATFORM_SOURCES "libs/luasocket/src/usocket.c") +endif () + add_library(luasocket SHARED "libs/luasocket/src/auxiliar.c" "libs/luasocket/src/buffer.c" @@ -333,7 +401,7 @@ add_library(luasocket SHARED "libs/luasocket/src/tcp.c" "libs/luasocket/src/timeout.c" "libs/luasocket/src/udp.c" - "libs/luasocket/src/wsocket.c" + ${LUASOCKET_PLATFORM_SOURCES} ) target_include_directories(luasocket @@ -344,12 +412,14 @@ target_include_directories(luasocket target_link_libraries(luasocket PRIVATE LuaJIT::LuaJIT - wsock32 - ws2_32 ) -set_target_properties( luasocket PROPERTIES OUTPUT_NAME "socket" ) -install(TARGETS luasocket RUNTIME DESTINATION ".") +if (WIN32) + target_link_libraries(luasocket PRIVATE wsock32 ws2_32) +endif () + +set_target_properties( luasocket PROPERTIES OUTPUT_NAME "socket" ) +install(TARGETS luasocket RUNTIME DESTINATION "." LIBRARY DESTINATION ".") install(FILES $ DESTINATION ".") # lzip module @@ -366,5 +436,23 @@ target_link_libraries(lzip ZLIB::ZLIB ) -install(TARGETS lzip RUNTIME DESTINATION ".") +install(TARGETS lzip RUNTIME DESTINATION "." LIBRARY DESTINATION ".") install(FILES $ DESTINATION ".") + +if (NOT WIN32) + set_target_properties(lcurl lua-utf8 luasocket lzip PROPERTIES PREFIX "" SUFFIX ".so") +endif () + +# macOS launcher (counterpart of the Windows frontend executable) +if (APPLE) + add_executable(pob macos/main.mm) + target_link_libraries(pob PRIVATE SimpleGraphic "-framework Foundation" "-framework CoreServices") + if (DEFINED POB_DEFAULT_SCRIPT) + target_compile_definitions(pob PRIVATE "POB_DEFAULT_SCRIPT=\"${POB_DEFAULT_SCRIPT}\"") + endif () + set_target_properties(pob PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@executable_path" + ) + install(TARGETS pob RUNTIME DESTINATION ".") +endif () diff --git a/cmake/FindLuaJIT.cmake b/cmake/FindLuaJIT.cmake index a037bf5..f2b28c5 100644 --- a/cmake/FindLuaJIT.cmake +++ b/cmake/FindLuaJIT.cmake @@ -9,15 +9,15 @@ if (DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET) find_path(LuaJIT_INCLUDE_DIR luajit.h PATHS ${LuaJIT_SEARCH_ROOT}/include - PATH_SUFFIXES luajit + PATH_SUFFIXES luajit luajit-2.1 luajit-2.0 NO_DEFAULT_PATH) - find_library(LuaJIT_LIBRARY_RELEASE NAMES lua51 + find_library(LuaJIT_LIBRARY_RELEASE NAMES lua51 luajit-5.1 PATHS ${LuaJIT_SEARCH_ROOT} PATH_SUFFIXES lib NO_DEFAULT_PATH) - find_library(LuaJIT_LIBRARY_DEBUG NAMES lua51 + find_library(LuaJIT_LIBRARY_DEBUG NAMES lua51 luajit-5.1 PATHS ${LuaJIT_SEARCH_ROOT} PATH_SUFFIXES debug/lib NO_DEFAULT_PATH) diff --git a/engine/common/base64.c b/engine/common/base64.c index c02a2b6..9e3ddf3 100644 --- a/engine/common/base64.c +++ b/engine/common/base64.c @@ -29,6 +29,7 @@ #include "base64.h" #include +#include /* ---- Base64 Encoding/Decoding Table --- */ /* Padding character string starts at offset 64. */ diff --git a/engine/common/base64.h b/engine/common/base64.h index 5cb097f..e23e374 100644 --- a/engine/common/base64.h +++ b/engine/common/base64.h @@ -26,6 +26,7 @@ // Modified for standalone inclusion in SimpleGraphic. #include +#include #ifdef __cplusplus extern "C" { diff --git a/engine/common/common.cpp b/engine/common/common.cpp index 48d8df3..92ed54b 100644 --- a/engine/common/common.cpp +++ b/engine/common/common.cpp @@ -6,6 +6,8 @@ #include "common.h" +#include + // =================== // Argument List Class // =================== @@ -437,6 +439,12 @@ char* NarrowUTF8String(const wchar_t* str) return NarrowCodepageString(str, CP_UTF8); } +#endif + +// IndexUTF8ToUTF32 has no platform-specific dependencies (pure UTF-8 decoding); +// it was previously nested inside the #ifdef _WIN32 block above, which left it +// undefined (and unlinkable) on non-Windows platforms despite being called from +// cross-platform code (r_main.cpp, r_font.cpp, sys_console.cpp). IndexedUTF32String IndexUTF8ToUTF32(std::string_view input) { IndexedUTF32String ret{}; @@ -500,5 +508,3 @@ IndexedUTF32String IndexUTF8ToUTF32(std::string_view input) ret.text = std::u32string(codepoints.begin(), codepoints.end()); return ret; } - -#endif diff --git a/engine/render/r_main.cpp b/engine/render/r_main.cpp index 1d46715..c1db369 100644 --- a/engine/render/r_main.cpp +++ b/engine/render/r_main.cpp @@ -1640,7 +1640,7 @@ void r_renderer_c::GetShaderImageSize(r_shaderHnd_c* hnd, int& width, int& heigh if (hnd) { while (hnd->sh->tex->status < r_tex_c::SIZE_KNOWN) { - Sleep(1); + sys->Sleep(1); } width = hnd->sh->tex->fileWidth; height = hnd->sh->tex->fileHeight; diff --git a/engine/render/r_texture.cpp b/engine/render/r_texture.cpp index 07fdda8..a3d7c1c 100644 --- a/engine/render/r_texture.cpp +++ b/engine/render/r_texture.cpp @@ -4,9 +4,11 @@ // Module: Render Texture // +#include #include #include #include +#include #include "r_local.h" #include "cmp_core.h" @@ -504,7 +506,7 @@ static gli::texture2d_array TranscodeTexture(gli::texture2d_array src, gli::form for (size_t blockRow = 0; blockRow < srcBlocksPerRow; ++blockRow) { const size_t rowBase = blockRow * srcBlockSize.y; - const size_t rowsLeft = (std::min)(4ull, dstExtent.y - rowBase); + const size_t rowsLeft = (std::min)(size_t(4), dstExtent.y - rowBase); for (size_t blockCol = 0; blockCol < srcBlocksPerColumn; ++blockCol) { // Read source 4x4 texel block, no branching needed. @@ -524,7 +526,7 @@ static gli::texture2d_array TranscodeTexture(gli::texture2d_array src, gli::form // Here we work off that dstData points at the top left pixel of the block row in the destination. const size_t colBase = blockCol * srcBlockSize.x; - const size_t colsLeft = (std::min)(4ull, dstExtent.x - colBase); + const size_t colsLeft = (std::min)(size_t(4), dstExtent.x - colBase); const size_t colBytesLeft = colsLeft * 4; for (size_t innerRow = 0; innerRow < rowsLeft; ++innerRow) { auto* dstPtr = dstData + dstRowStride * innerRow + colBase * 4; diff --git a/engine/system/win/sys_video.cpp b/engine/system/win/sys_video.cpp index 6f61bff..4c056b7 100644 --- a/engine/system/win/sys_video.cpp +++ b/engine/system/win/sys_video.cpp @@ -115,6 +115,8 @@ sys_video_c::sys_video_c(sys_IMain* sysHnd) platformType = GLFW_ANGLE_PLATFORM_TYPE_D3D11; else // Native Windows platformType = GLFW_ANGLE_PLATFORM_TYPE_D3D11; +#elif defined(__APPLE__) + platformType = GLFW_ANGLE_PLATFORM_TYPE_METAL; #endif glfwInitHint(GLFW_ANGLE_PLATFORM_TYPE, platformType); glfwInit(); @@ -489,7 +491,9 @@ int sys_video_c::Apply(sys_vidSet_s* set) return; } auto video = (sys_video_c*)sys->video; - video->lastCursorPos = CursorPos{ (int)x, (int)y }; + double sx = video->vid.size[0] > 0 ? (double)video->vid.fbSize[0] / video->vid.size[0] : 1.0; + double sy = video->vid.size[1] > 0 ? (double)video->vid.fbSize[1] / video->vid.size[1] : 1.0; + video->lastCursorPos = CursorPos{ (int)(x * sx), (int)(y * sy) }; }); glfwSetWindowCloseCallback(wnd, [](GLFWwindow* wnd) { auto sys = (sys_main_c*)glfwGetWindowUserPointer(wnd); @@ -737,14 +741,18 @@ void sys_video_c::GetRelativeCursor(int& x, int& y) if (!initialised) return; double xpos, ypos; glfwGetCursorPos(wnd, &xpos, &ypos); - x = (int)floor(xpos); - y = (int)floor(ypos); + double sx = vid.size[0] > 0 ? (double)vid.fbSize[0] / vid.size[0] : 1.0; + double sy = vid.size[1] > 0 ? (double)vid.fbSize[1] / vid.size[1] : 1.0; + x = (int)floor(xpos * sx); + y = (int)floor(ypos * sy); } void sys_video_c::SetRelativeCursor(int x, int y) { if (!initialised) return; - glfwSetCursorPos(wnd, (double)x, (double)y); + double sx = vid.fbSize[0] > 0 ? (double)vid.size[0] / vid.fbSize[0] : 1.0; + double sy = vid.fbSize[1] > 0 ? (double)vid.size[1] / vid.fbSize[1] : 1.0; + glfwSetCursorPos(wnd, x * sx, y * sy); } bool sys_video_c::IsCursorOverWindow() diff --git a/macos/main.mm b/macos/main.mm new file mode 100644 index 0000000..1c532d8 --- /dev/null +++ b/macos/main.mm @@ -0,0 +1,103 @@ +// Path of Building launcher for macOS. +// Resolves the Lua entry script, prepares LuaJIT search paths (macOS defaults +// have no exe-relative entries, unlike Windows), captures a pob:// launch URL, +// then hands off to SimpleGraphic's RunLuaFileAsWin. +#import +#import +// kInternetEventClass/kAEGetURL live here, not pulled in transitively by +// CoreServices.h on this SDK. +#import +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" int RunLuaFileAsWin(int argc, char** argv); + +static std::string g_launchUrl; + +@interface PobUrlHandler : NSObject +- (void)handleGetURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)reply; +@end + +@implementation PobUrlHandler +- (void)handleGetURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)reply { + NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + if (url) { + g_launchUrl = url.UTF8String; + } +} +@end + +static std::string ExecutableDir() { + uint32_t size = 0; + _NSGetExecutablePath(nullptr, &size); + std::vector buf(size + 1); + _NSGetExecutablePath(buf.data(), &size); + char resolved[PATH_MAX]; + if (!realpath(buf.data(), resolved)) { + return "."; + } + std::string path = resolved; + return path.substr(0, path.find_last_of('/')); +} + +int main(int argc, char** argv) { + @autoreleasepool { + // Capture the URL if the app was launched via the pob: scheme; the + // event is queued at launch, a short run-loop spin collects it. + PobUrlHandler* handler = [PobUrlHandler new]; + [[NSAppleEventManager sharedAppleEventManager] + setEventHandler:handler + andSelector:@selector(handleGetURLEvent:withReplyEvent:) + forEventClass:kInternetEventClass + andEventID:kAEGetURL]; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.15]]; + + std::string script; + if (argc > 1 && argv[1][0] != '-' && strncmp(argv[1], "pob:", 4) != 0) { + script = argv[1]; + } else if (const char* env = getenv("POB_SCRIPT_PATH")) { + script = env; + } +#ifdef POB_DEFAULT_SCRIPT + if (script.empty()) { + script = POB_DEFAULT_SCRIPT; + } +#endif + if (script.empty()) { + fprintf(stderr, "pob: no Lua entry script; pass a path or set POB_SCRIPT_PATH\n"); + return 1; + } + char resolved[PATH_MAX]; + if (!realpath(script.c_str(), resolved)) { + fprintf(stderr, "pob: script not found: %s\n", script.c_str()); + return 1; + } + + std::string exeDir = ExecutableDir(); + setenv("LUA_PATH", (exeDir + "/lua/?.lua;" + exeDir + "/lua/?/init.lua;;").c_str(), 1); + setenv("LUA_CPATH", (exeDir + "/?.so;;").c_str(), 1); + // Fonts/screenshots resolve relative to the default workdir, which the + // host derives from the executable's directory; make cwd match it too. + chdir(exeDir.c_str()); + + std::vector args; + args.push_back(resolved); + std::string url = g_launchUrl; + if (url.empty() && argc > 1 && strncmp(argv[1], "pob:", 4) == 0) { + url = argv[1]; + } + if (!url.empty()) { + args.push_back(url.data()); + } + for (int i = 2; i < argc; i++) { + args.push_back(argv[i]); + } + return RunLuaFileAsWin((int)args.size(), args.data()); + } +} diff --git a/triplets/arm64-osx.cmake b/triplets/arm64-osx.cmake new file mode 100644 index 0000000..d4aefb4 --- /dev/null +++ b/triplets/arm64-osx.cmake @@ -0,0 +1,11 @@ +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_OSX_ARCHITECTURES arm64) + +# GLFW loads EGL/GLES at runtime via dlopen("libEGL.dylib"), matching the +# libEGL.dll/libGLESv2.dll pair shipped on Windows; ANGLE must be shared. +if(PORT STREQUAL "angle") + set(VCPKG_LIBRARY_LINKAGE dynamic) +endif() diff --git a/vcpkg-ports/ports/luajit/2026-07-20_1/configure b/vcpkg-ports/ports/luajit/2026-07-20_1/configure old mode 100644 new mode 100755 diff --git a/vcpkg-ports/ports/luajit/2026-07-20_1/pob-wide-crt.patch b/vcpkg-ports/ports/luajit/2026-07-20_1/pob-wide-crt.patch index 9a1392a..23856ed 100644 --- a/vcpkg-ports/ports/luajit/2026-07-20_1/pob-wide-crt.patch +++ b/vcpkg-ports/ports/luajit/2026-07-20_1/pob-wide-crt.patch @@ -393,7 +393,7 @@ index 00000000..72e9ccae +char* _lua_tmpnam(); +#else +#define _lua_fopen(path, mode) fopen(path, mode) -+#define _lua_getenvcopy(name) strdup(getenv(name)) ++#define _lua_getenvcopy(name) (getenv(name) ? strdup(getenv(name)) : NULL) +#define _lua_getenvfree(name) free(name) +#define _lua_remove(path) remove(path) +#define _lua_rename(oldpath, newpath) rename(oldpath, newpath) diff --git a/vcpkg-ports/ports/luajit/2026-07-20_1/portfile.cmake b/vcpkg-ports/ports/luajit/2026-07-20_1/portfile.cmake index aff10ba..5fd02ea 100644 --- a/vcpkg-ports/ports/luajit/2026-07-20_1/portfile.cmake +++ b/vcpkg-ports/ports/luajit/2026-07-20_1/portfile.cmake @@ -102,6 +102,15 @@ file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/share/man" ) +if(NOT VCPKG_TARGET_IS_WINDOWS) + # The make install step symlinks bin/luajit to a versioned binary that the + # configure shim never installs, leaving a self-referential link. Replace + # it with the real binary from the build tree so vcpkg_copy_tools finds it. + file(REMOVE "${CURRENT_PACKAGES_DIR}/bin/luajit") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/bin") + file(COPY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/src/luajit" DESTINATION "${CURRENT_PACKAGES_DIR}/bin") +endif() + vcpkg_copy_tools(TOOL_NAMES luajit AUTO_CLEAN) vcpkg_fixup_pkgconfig()