diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 00000000..53a89f1f --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,47 @@ +name: macOS + +on: + pull_request: + push: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: macos-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: macos-26 + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + # act uses the current working tree, including uncommitted changes. + if: ${{ !env.ACT }} + with: + fetch-depth: 0 + submodules: recursive + + - name: Install build tools + run: | + if ! command -v cmake >/dev/null || ! command -v ninja >/dev/null; then + brew install cmake ninja + fi + + - name: Build + working-directory: ${{ env.NXEMU_LOCAL_WORKSPACE || github.workspace }} + run: sh src/script/build_macos.sh Release "${NXEMU_BUILD_DIRECTORY:-build}" + + # The disk image preserves the bundle and includes an Applications shortcut. + - name: Package + working-directory: ${{ env.NXEMU_LOCAL_WORKSPACE || github.workspace }} + run: sh src/script/package_dmg_macos.sh NxEmu-macos-arm64.dmg + + - uses: actions/upload-artifact@v4 + with: + name: NxEmu-macos-arm64 + path: ${{ env.NXEMU_LOCAL_WORKSPACE || github.workspace }}/package/NxEmu-macos-arm64.dmg + if-no-files-found: error diff --git a/.gitignore b/.gitignore index fd0b112f..a1d6a857 100644 --- a/.gitignore +++ b/.gitignore @@ -20,12 +20,14 @@ *.o Thumbs.db +.DS_Store /.vs /bin /build /git.properties /ipch /modules +/package /src/nxemu-core/version.h /src/nxemu-video/version.h /src/nxemu-cpu/version.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ea82bf50..13cbec53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,26 @@ cmake_minimum_required(VERSION 3.22) +# Match the minimum supported by the bundled MoltenVK runtime, instead of +# silently targeting the macOS version of the developer's machine. +if(CMAKE_HOST_APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum macOS version" FORCE) +endif() + project(nxemu) +if(APPLE) + if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "12.0") + message(FATAL_ERROR "The bundled MoltenVK runtime requires macOS 12.0 or newer") + endif() + enable_language(OBJC OBJCXX) +endif() + find_package(Threads REQUIRED) +if(APPLE) + option(ENABLE_SDL2 "Enable SDL game controller input" ON) +endif() + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -11,18 +28,26 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # ============================================================ # Architecture detection # ============================================================ -if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64") +set(NXEMU_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") +if(APPLE AND CMAKE_OSX_ARCHITECTURES) + list(LENGTH CMAKE_OSX_ARCHITECTURES _nxemu_arch_count) + if(_nxemu_arch_count GREATER 1) + message(FATAL_ERROR "Build each macOS architecture separately: the JIT backend is architecture-specific") + endif() + list(GET CMAKE_OSX_ARCHITECTURES 0 NXEMU_TARGET_PROCESSOR) +endif() +if(NXEMU_TARGET_PROCESSOR MATCHES "aarch64|arm64|ARM64") set(ARCHITECTURE_arm64 TRUE) set(ARCHITECTURE "arm64") set(NXEMU_PLATFORM_DIR "arm64") add_compile_definitions(ARCHITECTURE_arm64=1) -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64") +elseif(NXEMU_TARGET_PROCESSOR MATCHES "x86_64|AMD64|amd64") add_compile_definitions(ARCHITECTURE_x86_64=1) set(ARCHITECTURE_x86_64 TRUE) set(ARCHITECTURE "x86_64") set(NXEMU_PLATFORM_DIR "x64") else() - set(NXEMU_PLATFORM_DIR "${CMAKE_SYSTEM_PROCESSOR}") + set(NXEMU_PLATFORM_DIR "${NXEMU_TARGET_PROCESSOR}") endif() # CMake modules diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 81910255..b36e9e9a 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -3,14 +3,46 @@ if (NOT TARGET fmt::fmt) add_subdirectory(fmt) endif() -# AppleClang 21 rejects the consteval path used by the bundled fmt 10.x. -# fmt 10.1.1 controls the constructor qualifier with FMT_CONSTEVAL; the newer -# FMT_USE_CONSTEVAL switch does not exist in this bundled version. Define -# FMT_CONSTEVAL to an empty replacement on the affected toolchain, matching -# the upstream workaround for fmt 10.x on AppleClang 21. -if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND - CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 21) - target_compile_definitions(fmt PUBLIC FMT_CONSTEVAL=) +# macOS controller adapters are bundled against the application deployment target. +if(APPLE) + option(ENABLE_LIBUSB "Enable direct GameCube USB adapter input" ON) + if(ENABLE_LIBUSB AND NOT TARGET libusb::usb) + set(_nxemu_usb "${CMAKE_CURRENT_SOURCE_DIR}/libusb/libusb") + add_library(nxemu_libusb STATIC + ${_nxemu_usb}/core.c + ${_nxemu_usb}/descriptor.c + ${_nxemu_usb}/hotplug.c + ${_nxemu_usb}/io.c + ${_nxemu_usb}/strerror.c + ${_nxemu_usb}/sync.c + ${_nxemu_usb}/os/events_posix.c + ${_nxemu_usb}/os/threads_posix.c + ${_nxemu_usb}/os/darwin_usb.c) + target_include_directories(nxemu_libusb PUBLIC "${_nxemu_usb}" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libusb/Xcode") + target_compile_options(nxemu_libusb PRIVATE -fvisibility=hidden) + target_link_libraries(nxemu_libusb PRIVATE Threads::Threads + "-framework IOKit" "-framework CoreFoundation" "-framework Security") + add_library(libusb::usb ALIAS nxemu_libusb) + unset(_nxemu_usb) + endif() +endif() + +# Keep macOS controller support self-contained in the application bundle. +if(APPLE AND ENABLE_SDL2 AND NOT TARGET SDL2::SDL2) + set(SDL_SHARED OFF CACHE BOOL "Build shared SDL" FORCE) + set(SDL_STATIC ON CACHE BOOL "Build static SDL" FORCE) + set(SDL_TEST OFF CACHE BOOL "Build SDL test library" FORCE) + set(SDL_TESTS OFF CACHE BOOL "Build SDL tests" FORCE) + set(SDL2_DISABLE_INSTALL ON CACHE BOOL "Disable SDL installation" FORCE) + # SDL's bundled checks use an older minimum; CMake 4 requires at least 3.5. + set(_nxemu_saved_policy_minimum "${CMAKE_POLICY_VERSION_MINIMUM}") + if(NOT CMAKE_POLICY_VERSION_MINIMUM OR CMAKE_POLICY_VERSION_MINIMUM VERSION_LESS "3.5") + set(CMAKE_POLICY_VERSION_MINIMUM "3.5") + endif() + add_subdirectory(sdl) + set(CMAKE_POLICY_VERSION_MINIMUM "${_nxemu_saved_policy_minimum}") + unset(_nxemu_saved_policy_minimum) endif() # Boost @@ -184,5 +216,44 @@ if(NOT TARGET tsl::robin_map) target_include_directories(robin_map INTERFACE "${CMAKE_SOURCE_DIR}/external/robin-map/include") endif() +# MoltenVK runtime (macOS) +if(APPLE) + include(ExternalProject) + + find_program(NXEMU_XCODEBUILD xcodebuild REQUIRED) + execute_process(COMMAND "${NXEMU_XCODEBUILD}" -version + RESULT_VARIABLE _nxemu_xcode_result OUTPUT_QUIET ERROR_VARIABLE _nxemu_xcode_error) + if(NOT _nxemu_xcode_result EQUAL 0) + message(FATAL_ERROR + "Building MoltenVK requires full Xcode, not only Command Line Tools. " + "Select Xcode with xcode-select. ${_nxemu_xcode_error}") + endif() + + set(_nxemu_moltenvk_root "${CMAKE_BINARY_DIR}/external/moltenvk") + set(_nxemu_moltenvk_source "${_nxemu_moltenvk_root}/src") + set(_nxemu_moltenvk_runtime + "${_nxemu_moltenvk_source}/MoltenVK/Package/Latest/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib") + + ExternalProject_Add(nxemu_moltenvk + GIT_REPOSITORY "https://github.com/V380-Ori/Ryujinx.MoltenVK.git" + GIT_TAG "45f9b641e45f1d150f3521d74808477add9e975b" + GIT_SHALLOW FALSE + GIT_SUBMODULES_RECURSE TRUE + UPDATE_COMMAND "" + SOURCE_DIR "${_nxemu_moltenvk_source}" + CONFIGURE_COMMAND "" + BUILD_IN_SOURCE TRUE + BUILD_COMMAND /bin/sh -c + "cd MoltenVK && (git apply --reverse --check ../000*.patch || git apply --verbose ../000*.patch) && ./fetchDependencies --macos && make macos" + BUILD_BYPRODUCTS "${_nxemu_moltenvk_runtime}" + INSTALL_COMMAND "" + ) + + set(NXEMU_MOLTENVK_RUNTIME "${_nxemu_moltenvk_runtime}" CACHE INTERNAL "" FORCE) + unset(_nxemu_moltenvk_root) + unset(_nxemu_moltenvk_source) + unset(_nxemu_moltenvk_runtime) +endif() + # sciter -add_subdirectory(sciterui) \ No newline at end of file +add_subdirectory(sciterui) diff --git a/external/ffmpeg b/external/ffmpeg index 772cea1a..7bce512d 160000 --- a/external/ffmpeg +++ b/external/ffmpeg @@ -1 +1 @@ -Subproject commit 772cea1ac12d5cd11ff17df97dc8a9858d9c1c01 +Subproject commit 7bce512dcae670a7349088b564b0e6251c0a487e diff --git a/external/fmt b/external/fmt index f5e54359..1be298e1 160000 --- a/external/fmt +++ b/external/fmt @@ -1 +1 @@ -Subproject commit f5e54359df4c26b6230fc61d38aa294581393084 +Subproject commit 1be298e1bd68957e4cd352e1f676f00e07dcfb57 diff --git a/external/sciterui b/external/sciterui index 303c6855..dcceefb6 160000 --- a/external/sciterui +++ b/external/sciterui @@ -1 +1 @@ -Subproject commit 303c68557c469a76da5ee8b9d07c1594a8798a9f +Subproject commit dcceefb6dfcb92a0ef5d98ec6f368f82c83fe934 diff --git a/lang/english/css/base.css b/lang/english/css/base.css index 47973e95..238b0206 100644 --- a/lang/english/css/base.css +++ b/lang/english/css/base.css @@ -143,3 +143,19 @@ html > header > window-buttons > window-button[role="window-minimize"] { foreground-image: url(icon:window-minimize); } + +/* macOS uses the native AppKit title bar instead of Sciter window chrome. */ +html[data-platform="macos"] +{ + border: none; +} + +html[data-platform="macos"] > header +{ + display: none; +} + +html[data-platform="macos"][window-state="maximized"] +{ + border: none; +} diff --git a/lang/english/css/main_window.css b/lang/english/css/main_window.css index 8c540a01..bff1f46b 100644 --- a/lang/english/css/main_window.css +++ b/lang/english/css/main_window.css @@ -46,7 +46,7 @@ } #StatusBar #volume:hover, -#StatusBar #renderer:hover, +#StatusBar #renderer.interactive:hover, #StatusBar #dockedMode:hover, #StatusBar #gpuCommandSynchronization:hover, #StatusBar #volumePopupBtn:hover diff --git a/lang/english/html/main_window.html b/lang/english/html/main_window.html index cb83d814..2b04a078 100644 --- a/lang/english/html/main_window.html +++ b/lang/english/html/main_window.html @@ -1,94 +1,8 @@ - +