diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..989a022 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +patches/* -text diff --git a/.gitignore b/.gitignore index f7bd29a..914a30d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /build/ +/build-*/ /build-SimpleGraphic -/.idea \ No newline at end of file +/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index b992d62..9a711bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,18 +78,18 @@ set(SIMPLEGRAPHIC_SOURCES set (SIMPLEGRAPHIC_PLATFORM_SOURCES) if (APPLE) - set (SIMPLEGRAPHIC_PLATFORM_SOURCES + list (APPEND SIMPLEGRAPHIC_PLATFORM_SOURCES "engine/system/win/sys_macos.mm" ) 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() @@ -131,6 +131,12 @@ find_package(zstd REQUIRED) find_package(ZLIB REQUIRED) find_package(WebP) +if (TARGET zstd::libzstd_shared) + set(ZSTD_LIBRARY zstd::libzstd_shared) +else () + set(ZSTD_LIBRARY zstd::libzstd_static) +endif () + add_library(cmp_core STATIC dep/compressonator/cmp_core/source/cmp_core.cpp dep/compressonator/cmp_core/source/cmp_core.h @@ -240,9 +246,17 @@ target_link_libraries(SimpleGraphic Threads::Threads WebP::webpdecoder ZLIB::ZLIB - zstd::libzstd_shared + ${ZSTD_LIBRARY} ) +if (APPLE) + set_target_properties(SimpleGraphic PROPERTIES + BUILD_WITH_INSTALL_NAME_DIR TRUE + INSTALL_NAME_DIR "@rpath" + MACOSX_RPATH TRUE + ) +endif () + install(FILES $ DESTINATION ".") install(TARGETS SimpleGraphic RUNTIME DESTINATION ".") @@ -276,10 +290,25 @@ if (WIN32) ) endif () +if (APPLE) + find_program(PATCH_EXECUTABLE patch REQUIRED) + set(LCURL_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/Lua-cURLv3) + file(REMOVE_RECURSE ${LCURL_SOURCE_DIR}) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/libs/Lua-cURLv3/src + DESTINATION ${LCURL_SOURCE_DIR}) + execute_process( + COMMAND ${PATCH_EXECUTABLE} -l -p1 -i + ${CMAKE_CURRENT_SOURCE_DIR}/patches/Lua-cURLv3-skip-luaL_setfuncs-on-luajit.patch + WORKING_DIRECTORY ${LCURL_SOURCE_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) +else () + set(LCURL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/Lua-cURLv3) +endif () + # lcurl module -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}) @@ -301,10 +330,14 @@ install(FILES $ DESTINATION ".") 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 + ) +elseif (APPLE) + target_compile_options(lua-utf8 PRIVATE -include limits.h) +endif () target_include_directories(lua-utf8 PRIVATE @@ -333,9 +366,14 @@ add_library(luasocket SHARED "libs/luasocket/src/tcp.c" "libs/luasocket/src/timeout.c" "libs/luasocket/src/udp.c" - "libs/luasocket/src/wsocket.c" ) +if (WIN32) + target_sources(luasocket PRIVATE "libs/luasocket/src/wsocket.c") +else () + target_sources(luasocket PRIVATE "libs/luasocket/src/usocket.c") +endif () + target_include_directories(luasocket PRIVATE ${LSOCKET_SOURCE_DIR}/src @@ -344,10 +382,12 @@ target_include_directories(luasocket target_link_libraries(luasocket PRIVATE LuaJIT::LuaJIT - wsock32 - ws2_32 ) +if (WIN32) + target_link_libraries(luasocket PRIVATE wsock32 ws2_32) +endif () + set_target_properties( luasocket PROPERTIES OUTPUT_NAME "socket" ) install(TARGETS luasocket RUNTIME DESTINATION ".") install(FILES $ DESTINATION ".") @@ -368,3 +408,71 @@ target_link_libraries(lzip install(TARGETS lzip RUNTIME DESTINATION ".") install(FILES $ DESTINATION ".") + +if (APPLE) + set_target_properties(lcurl lua-utf8 luasocket lzip PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@loader_path/../Frameworks" + PREFIX "" + SUFFIX ".so" + ) + + set_source_files_properties(macos/smoke/Launch.lua PROPERTIES + MACOSX_PACKAGE_LOCATION Resources + ) + add_executable(SimpleGraphicSmoke MACOSX_BUNDLE + macos/launcher.c + macos/smoke/Launch.lua + ) + target_link_libraries(SimpleGraphicSmoke PRIVATE SimpleGraphic) + add_dependencies(SimpleGraphicSmoke lcurl lua-utf8 luasocket lzip) + set_property(TARGET SimpleGraphicSmoke APPEND PROPERTY LINK_DEPENDS + "$" + "$" + "$" + "$" + ) + set_target_properties(SimpleGraphicSmoke PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@executable_path/../Frameworks" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in" + MACOSX_BUNDLE_BUNDLE_NAME "SimpleGraphicSmoke" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.pathofbuilding.simplegraphic.smoke" + ) + + add_custom_command(TARGET SimpleGraphicSmoke POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/../Frameworks" + "$/../Resources/lcurl" + "$/../Resources/socket" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + "$" + "$/../Frameworks" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Frameworks/libEGL.dylib" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Frameworks/libGLESv2.dylib" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Resources/lcurl.so" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Resources/lcurl/safe.so" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Resources/lua-utf8.so" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Resources/socket/core.so" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/../Resources/lzip.so" + COMMAND /usr/bin/codesign --force --deep --sign - + "$" + VERBATIM + ) +endif () diff --git a/cmake/FindLuaJIT.cmake b/cmake/FindLuaJIT.cmake index a037bf5..3638100 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.0 luajit-2.1 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..ba84a65 100644 --- a/engine/common/common.cpp +++ b/engine/common/common.cpp @@ -437,6 +437,8 @@ char* NarrowUTF8String(const wchar_t* str) return NarrowCodepageString(str, CP_UTF8); } +#endif + IndexedUTF32String IndexUTF8ToUTF32(std::string_view input) { IndexedUTF32String ret{}; @@ -500,5 +502,3 @@ IndexedUTF32String IndexUTF8ToUTF32(std::string_view input) ret.text = std::u32string(codepoints.begin(), codepoints.end()); return ret; } - -#endif diff --git a/engine/render/r_font.cpp b/engine/render/r_font.cpp index 90375b3..71fa186 100644 --- a/engine/render/r_font.cpp +++ b/engine/render/r_font.cpp @@ -321,7 +321,7 @@ r_font_c::FontHeightEntry r_font_c::FindFontHeight(int height) { void r_font_c::DrawTextLine(scp_t pos, int align, int height, col4_t col, std::u32string_view str) { // Check if the line is visible - if (pos[Y] >= renderer->sys->video->vid.size[1] || pos[Y] <= -height) { + if (pos[Y] >= renderer->VirtualScreenHeight() || pos[Y] <= -height) { // Just process the colour codes while (!str.empty()) { // Check for escape character diff --git a/engine/render/r_main.cpp b/engine/render/r_main.cpp index 1d46715..9b8c04f 100644 --- a/engine/render/r_main.cpp +++ b/engine/render/r_main.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -1640,7 +1642,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); + std::this_thread::sleep_for(std::chrono::milliseconds(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..0b049ef 100644 --- a/engine/render/r_texture.cpp +++ b/engine/render/r_texture.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "r_local.h" #include "cmp_core.h" @@ -504,7 +505,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 +525,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_main.cpp b/engine/system/win/sys_main.cpp index 3078e0d..7291adb 100644 --- a/engine/system/win/sys_main.cpp +++ b/engine/system/win/sys_main.cpp @@ -576,7 +576,13 @@ std::filesystem::path FindBasePath() progPath = basePath; #endif progPath = weakly_canonical(progPath); - return progPath.parent_path(); + auto dirPath = progPath.parent_path(); +#if __APPLE__ && __MACH__ + if (dirPath.filename() == "MacOS" && dirPath.parent_path().filename() == "Contents") { + dirPath = dirPath.parent_path() / "Resources"; + } +#endif + return dirPath; } std::tuple, std::optional> FindUserPath() @@ -609,11 +615,7 @@ std::tuple, std::optional> Fin sys_main_c::sys_main_c() : heldKeyState(KEY_SCROLL + 1, (uint8_t)0) { -#ifdef _WIN64 - x64 = true; -#else - x64 = false; -#endif + x64 = sizeof(void*) == 8; #ifdef _DEBUG debug = true; #else @@ -650,7 +652,12 @@ bool sys_main_c::Run(int argc, char** argv) core = core_IMain::GetHandle(this); // Print some handy information - con->Printf(CFG_VERSION" %s %s, built " __DATE__ "\n", x64? "x64":"x86", debug? "Debug":"Release"); +#if defined(__aarch64__) || defined(_M_ARM64) + const char* architecture = "arm64"; +#else + const char* architecture = x64 ? "x64" : "x86"; +#endif + con->Printf(CFG_VERSION" %s %s, built " __DATE__ "\n", architecture, debug? "Debug":"Release"); if (debuggerRunning) { con->Printf("Debugger is present.\n"); } @@ -715,7 +722,9 @@ bool sys_main_c::Run(int argc, char** argv) #endif if (exitMsg) { +#ifdef _WIN32 exitFlag = false; +#endif video->SetVisible(false); conWin->SetVisible(true); if (exitMsg) { @@ -723,9 +732,11 @@ bool sys_main_c::Run(int argc, char** argv) FreeString(exitMsg); exitMsg = NULL; } +#ifdef _WIN32 while (exitFlag == false) { Sleep(50); } +#endif } initialised = false; diff --git a/engine/system/win/sys_video.cpp b/engine/system/win/sys_video.cpp index 6f61bff..827fff9 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(); @@ -141,6 +143,17 @@ std::optional> PlatformGetCursorPos() { #endif } +std::pair CursorScale(const sys_vidSave_s& vid) { +#ifdef __APPLE__ + return { + vid.size[0] > 0 ? (double)vid.fbSize[0] / vid.size[0] : 1.0, + vid.size[1] > 0 ? (double)vid.fbSize[1] / vid.size[1] : 1.0 + }; +#else + return { 1.0, 1.0 }; +#endif +} + struct sys_programIcons_c { #if _WIN32 sys_programIcons_c() @@ -489,7 +502,8 @@ int sys_video_c::Apply(sys_vidSet_s* set) return; } auto video = (sys_video_c*)sys->video; - video->lastCursorPos = CursorPos{ (int)x, (int)y }; + auto [sx, sy] = CursorScale(video->vid); + video->lastCursorPos = CursorPos{ (int)(x * sx), (int)(y * sy) }; }); glfwSetWindowCloseCallback(wnd, [](GLFWwindow* wnd) { auto sys = (sys_main_c*)glfwGetWindowUserPointer(wnd); @@ -737,14 +751,16 @@ 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); + auto [sx, sy] = CursorScale(vid); + 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); + auto [sx, sy] = CursorScale(vid); + glfwSetCursorPos(wnd, x / sx, y / sy); } bool sys_video_c::IsCursorOverWindow() diff --git a/macos/Info.plist.in b/macos/Info.plist.in new file mode 100644 index 0000000..b2455a7 --- /dev/null +++ b/macos/Info.plist.in @@ -0,0 +1,22 @@ + + + + + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 13.0 + NSHighResolutionCapable + + + diff --git a/macos/README.md b/macos/README.md new file mode 100644 index 0000000..64af835 --- /dev/null +++ b/macos/README.md @@ -0,0 +1,53 @@ +# Apple Silicon smoke app + +`SimpleGraphicSmoke.app` is the first native Apple Silicon SimpleGraphic +artifact. It uses the same LuaJIT host, Lua modules, GLFW Cocoa window, and +ANGLE Metal renderer intended for Path of Building integration. + +## Build + +The build requires Xcode command-line tools, CMake, Git, and an Apple Silicon +Mac. Dependencies are built by the pinned vcpkg submodule. + +```sh +git submodule update --init --recursive + +MACOSX_DEPLOYMENT_TARGET=13.0 cmake -S . -B build-macos13 \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake \ + -DVCPKG_OVERLAY_TRIPLETS="$PWD/vcpkg-ports/triplets" \ + -DVCPKG_TARGET_TRIPLET=arm64-osx \ + -DCMAKE_OSX_ARCHITECTURES=arm64 \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 + +cmake --build build-macos13 --target SimpleGraphicSmoke --parallel +``` + +The artifact is written to `build-macos13/SimpleGraphicSmoke.app`. Run its +executable from a terminal to retain the smoke log: + +```sh +build-macos13/SimpleGraphicSmoke.app/Contents/MacOS/SimpleGraphicSmoke +``` + +A passing run reports LuaJIT and native module loading, compression, paths, +localhost sockets, ANGLE rendering, and the host restart loop before exiting. + +## Verify + +```sh +macos/verify-bundle.sh build-macos13/SimpleGraphicSmoke.app +``` + +The verifier rejects non-arm64 Mach-O files, deployment targets newer than +macOS 13, absolute non-system dependencies, and invalid ad-hoc signatures. + +## Current scope + +The smoke app proves the native host and packaging seam. It does not yet prove +bitmap fonts, PNG/WebP and DDS/BC fixture rendering, screenshots, clipboard, +URL opening, HTTPS trust, Retina 1x/2x cursor hit-testing, or full Path of +Building startup. It also still needs a run on physical Apple Silicon macOS 13 +and a green Windows CI build before it is ready to hand off to Path of Building +integration. diff --git a/macos/launcher.c b/macos/launcher.c new file mode 100644 index 0000000..3736221 --- /dev/null +++ b/macos/launcher.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +extern int RunLuaFileAsWin(int argc, char **argv); + +int main(int argc, char **argv) +{ + char restartMarker[128]; + char failureMarker[144]; + char successMarker[144]; + snprintf(restartMarker, sizeof(restartMarker), + "/tmp/org.pathofbuilding.simplegraphic-smoke-restart-%d", getpid()); + snprintf(failureMarker, sizeof(failureMarker), "%s-failed", restartMarker); + snprintf(successMarker, sizeof(successMarker), "%s-passed", restartMarker); + unlink(restartMarker); + unlink(failureMarker); + unlink(successMarker); + setenv("SIMPLEGRAPHIC_SMOKE_RESTART_MARKER", restartMarker, 1); + + char executablePath[4096]; + uint32_t size = sizeof(executablePath); + if (_NSGetExecutablePath(executablePath, &size) == 0) { + char resourcesPath[4096]; + snprintf(resourcesPath, sizeof(resourcesPath), "%s/../Resources", dirname(executablePath)); + if (chdir(resourcesPath) != 0) { + perror("Could not enter app resources directory"); + return 1; + } + } + + if (argc > 1) { + return RunLuaFileAsWin(argc - 1, argv + 1); + } + char *defaultArgs[] = { "Launch.lua" }; + int result = RunLuaFileAsWin(1, defaultArgs); + int passed = access(successMarker, F_OK) == 0; + unlink(failureMarker); + unlink(successMarker); + return result == 0 && passed ? 0 : 1; +} diff --git a/macos/smoke/Launch.lua b/macos/smoke/Launch.lua new file mode 100644 index 0000000..e68bbf1 --- /dev/null +++ b/macos/smoke/Launch.lua @@ -0,0 +1,93 @@ +#@ SimpleGraphic + +local restartMarker = assert(os.getenv("SIMPLEGRAPHIC_SMOKE_RESTART_MARKER")) +local marker = io.open(restartMarker, "r") +local restarted = marker ~= nil +if marker then + marker:close() + os.remove(restartMarker) +end + +local smoke = { frames = 0, restarted = restarted, subscriptPassed = false } +SetMainObject(smoke) +SetWindowTitle("SimpleGraphic macOS smoke") +ConExecute("set vid_mode 8") +ConExecute("set vid_resizable 3") + +function smoke:Fail(message) + local failureMarker = assert(io.open(restartMarker .. "-failed", "w")) + failureMarker:write(message) + failureMarker:close() + ConPrintf("SMOKE FAILED: %s\n", message) + Exit() +end + +function smoke:RunChecks() + RenderInit("DPI_AWARE") + SetClearColor(0.04, 0.08, 0.12, 1) + + local payload = "SimpleGraphic macOS arm64 smoke" + assert(Inflate(Deflate(payload)) == payload) + assert(type(GetScriptPath()) == "string") + assert(type(GetRuntimePath()) == "string") + assert(type(GetUserPath()) == "string") + assert(require("lcurl.safe")) + assert(require("lua-utf8")) + assert(require("lzip")) + local socket = assert(require("socket.core")) + local listener = assert(socket.tcp()) + assert(listener:bind("127.0.0.1", 0)) + listener:close() + + self.subscript = assert(LaunchSubScript([[ + assert(require("lcurl.safe")) + assert(require("lua-utf8")) + assert(require("lzip")) + assert(require("socket.core")) + return true + ]], "", "")) + ConPrintf("SMOKE: main-state modules, compression, paths, and localhost socket passed.\n") +end + +function smoke:OnInit() + local passed, errorMessage = pcall(self.RunChecks, self) + if not passed then + self:Fail(errorMessage) + end +end + +function smoke:OnSubError(_, errorMessage) + self:Fail("subscript error: " .. errorMessage) +end + +function smoke:OnSubFinished(_, passed) + if passed ~= true then + self:Fail("subscript returned without success") + return + end + self.subscriptPassed = true + ConPrintf("SMOKE: subscript-state native modules passed.\n") +end + +function smoke:OnFrame() + local width, height = GetScreenSize() + SetDrawColor(0.12, 0.55, 0.82, 1) + DrawImage(nil, width / 4, height / 4, width / 2, height / 2) + self.frames = self.frames + 1 + if self.frames >= 120 and self.subscriptPassed then + ConPrintf("SMOKE: ANGLE frame loop passed at %dx%d scale %.2f.\n", width, height, GetScreenScale()) + if self.restarted then + local successMarker = assert(io.open(restartMarker .. "-passed", "w")) + successMarker:write("passed") + successMarker:close() + ConPrintf("SMOKE: restart loop passed; exiting cleanly.\n") + Exit() + else + local markerFile = assert(io.open(restartMarker, "w")) + markerFile:write("restart") + markerFile:close() + ConPrintf("SMOKE: restarting Lua host.\n") + Restart() + end + end +end diff --git a/macos/verify-bundle.sh b/macos/verify-bundle.sh new file mode 100755 index 0000000..24d39fe --- /dev/null +++ b/macos/verify-bundle.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +set -eu + +bundle=${1:-} +if [ -z "$bundle" ] || [ ! -d "$bundle/Contents" ]; then + echo "usage: $0 SimpleGraphicSmoke.app" >&2 + exit 2 +fi + +status=0 +while IFS= read -r file_path; do + if ! file "$file_path" | grep -q "Mach-O"; then + continue + fi + + echo "== $file_path" + file "$file_path" + archs=$(lipo -archs "$file_path") + if [ "$archs" != "arm64" ]; then + echo "unexpected architectures: $archs" >&2 + status=1 + fi + + minos=$(vtool -show-build "$file_path" | awk '/minos/ { print $2; exit }') + if [ -z "$minos" ] || ! awk -v minos="$minos" 'BEGIN { exit !(minos + 0 <= 13) }'; then + echo "unexpected deployment target: $minos" >&2 + status=1 + fi + + install_name=$(otool -D "$file_path" 2>/dev/null | awk 'NR == 2 { print $1 }') + while IFS= read -r dependency; do + if [ "$dependency" = "$install_name" ]; then + continue + fi + case "$dependency" in + @rpath/*) + if [ ! -e "$bundle/Contents/Frameworks/${dependency#@rpath/}" ]; then + echo "unresolved dependency: $dependency" >&2 + status=1 + fi + ;; + /System/*|/usr/lib/*) ;; + *) echo "non-relocatable dependency: $dependency" >&2; status=1 ;; + esac + done < 1 { print $1 }') +EOF +done < /* for memset */ + #include + +-#if LUA_VERSION_NUM >= 502 ++/* LuaJIT (LUA_VERSION_NUM == 501) ships luaL_setfuncs as an extension; pull in ++ luajit.h so LUAJIT_VERSION is visible and the < 502 definition below can be ++ skipped, avoiding a duplicate-symbol link error against the LuaJIT library. */ ++#if defined(__has_include) ++# if __has_include() ++# include ++# endif ++#endif ++ ++#if LUA_VERSION_NUM >= 502 + + int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, +@@ -33,6 +42,7 @@ void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){ + + #else + ++#if !defined(LUAJIT_VERSION) /* LuaJIT already provides luaL_setfuncs */ + void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){ + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ +@@ -44,6 +54,7 @@ void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){ + } + lua_pop(L, nup); /* remove upvalues */ + } ++#endif + + void lua_rawgetp(lua_State *L, int index, const void *p){ + index = lua_absindex(L, index); diff --git a/ui_api.cpp b/ui_api.cpp index 6008350..3b7bc98 100644 --- a/ui_api.cpp +++ b/ui_api.cpp @@ -2172,18 +2172,7 @@ int ui_main_c::InitAPI(lua_State* L) sol::state_view lua(L); luaL_openlibs(L); - // Add "lua/" subdir for non-JIT Lua - { - lua_getglobal(L, "package"); - char const* tn = lua_typename(L, -1); - lua_getfield(L, -1, "path"); - std::string old_path = lua_tostring(L, -1); - lua_pop(L, 1); - old_path += ";lua/?.lua"; - lua_pushstring(L, old_path.c_str()); - lua_setfield(L, -2, "path"); - lua_pop(L, 1); - } + AppendLocalLuaSubdir(L); // Callbacks lua_newtable(L); // Callbacks table diff --git a/ui_main.cpp b/ui_main.cpp index 09e59be..4a099c2 100644 --- a/ui_main.cpp +++ b/ui_main.cpp @@ -184,6 +184,18 @@ void ui_main_c::DoError(const char* msg, const char* error) didExit = true; } +void ui_main_c::AppendLocalLuaSubdir(lua_State* L) +{ + lua_getglobal(L, "package"); + lua_getfield(L, -1, "path"); + std::string oldPath = lua_tostring(L, -1); + lua_pop(L, 1); + oldPath += ";lua/?.lua;lua/?/init.lua"; + lua_pushstring(L, oldPath.c_str()); + lua_setfield(L, -2, "path"); + lua_pop(L, 1); +} + // From lua.c static int traceback (lua_State *L) { if (!lua_isstring(L, 1)) /* 'message' not a string? */ diff --git a/ui_main.h b/ui_main.h index 02ec907..eb344e0 100644 --- a/ui_main.h +++ b/ui_main.h @@ -55,6 +55,7 @@ class ui_main_c: public ui_IMain { float lastColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static int InitAPI(lua_State* L); + static void AppendLocalLuaSubdir(lua_State* L); void RenderInit(r_featureFlag_e features); void ScriptInit(); diff --git a/ui_subscript.cpp b/ui_subscript.cpp index befba5c..c03c2d6 100644 --- a/ui_subscript.cpp +++ b/ui_subscript.cpp @@ -322,6 +322,7 @@ bool ui_subscript_c::Start() // Add libraries and APIs lua_gc(L, LUA_GCSTOP, 0); luaL_openlibs(L); + ui_main_c::AppendLocalLuaSubdir(L); lua_getglobal(L, "os"); lua_pushcfunction(L, l_os_exit); lua_setfield(L, -2, "exit"); diff --git a/vcpkg-ports/ports/luajit/2026-07-20_1/configure b/vcpkg-ports/ports/luajit/2026-07-20_1/configure index 0384100..df9b19e 100644 --- a/vcpkg-ports/ports/luajit/2026-07-20_1/configure +++ b/vcpkg-ports/ports/luajit/2026-07-20_1/configure @@ -32,7 +32,6 @@ cat > Makefile.vcpkg <