diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea57c49..d58e514 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,195 @@ permissions: contents: write packages: write jobs: + build-ios: + name: Build iOS + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.4' + + - name: Configure iOS + run: | + cmake -S . -B build -G Xcode --toolchain cmake/ios-toolchain.cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + + - name: Build iOS + run: cmake --build build --config Release --target vanilla + + - name: Create unsigned IPA + run: | + APP=build/bin/Release/Vanilla.app + test -d "$APP" + mkdir -p Payload + cp -R "$APP" Payload/ + zip -9rX vanilla-ios.ipa Payload + + - name: Upload iOS Artifact + uses: actions/upload-artifact@v4 + with: + name: ios + path: vanilla-ios.ipa + + - name: Upload Continuous Release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'vanilla-wiiu/vanilla' }} + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -fLOSs --retry 2 --retry-delay 60 https://github.com/probonopd/uploadtool/raw/master/upload.sh + bash upload.sh vanilla-ios.ipa + + build-macos: + name: Build macOS + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.4' + + - name: Configure + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \ + -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DVANILLA_BUILD_PIPE=OFF \ + -DVANILLA_BUILD_VENDORED=ON + + - name: Build + run: cmake --build build --target vanilla + + - name: Package + run: | + ditto -c -k --keepParent "$GITHUB_WORKSPACE/build/bin/Vanilla.app" vanilla-macos-universal-unsigned.zip + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: macos universal unsigned + path: vanilla-macos-universal-unsigned.zip + if-no-files-found: error + + sign-macos: + name: Sign and notarize macOS + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'vanilla-wiiu/vanilla' }} + needs: build-macos + runs-on: macos-15 + environment: macos-release + steps: + - uses: actions/checkout@v4 + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.4' + + - name: Download unsigned macOS artifact + uses: actions/download-artifact@v4 + with: + name: macos universal unsigned + path: incoming + + - name: Unpack + run: | + mkdir -p signing + ditto -x -k incoming/vanilla-macos-universal-unsigned.zip signing + + - name: Import signing certificate + env: + MACOS_CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }} + MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }} + MACOS_NOTARY_KEY_BASE64: ${{ secrets.MACOS_NOTARY_KEY_BASE64 }} + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + run: | + certificate_path="$RUNNER_TEMP/developer-id.p12" + keychain_path="$RUNNER_TEMP/vanilla-signing.keychain-db" + notary_key_path="$RUNNER_TEMP/AuthKey_${MACOS_NOTARY_KEY_ID}.p8" + + printf '%s' "$MACOS_CERTIFICATE_BASE64" | base64 --decode -o "$certificate_path" + printf '%s' "$MACOS_NOTARY_KEY_BASE64" | base64 --decode -o "$notary_key_path" + + security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" + security set-keychain-settings -lut 21600 "$keychain_path" + security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" + security import "$certificate_path" -P "$MACOS_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$keychain_path" + security set-key-partition-list -S apple-tool:,apple: -k "$MACOS_KEYCHAIN_PASSWORD" "$keychain_path" + security list-keychains -d user -s "$keychain_path" + + signing_identity="$(security find-identity -v -p codesigning "$keychain_path" | sed -n 's/.*"\(Developer ID Application:.*\)"/\1/p' | head -n 1)" + + echo "MACOS_NOTARY_KEY_PATH=$notary_key_path" >> "$GITHUB_ENV" + echo "MACOS_SIGNING_IDENTITY=$signing_identity" >> "$GITHUB_ENV" + + - name: Sign + run: | + codesign \ + --force \ + --timestamp \ + --options runtime \ + --entitlements "$GITHUB_WORKSPACE/gui/res/Vanilla.entitlements" \ + --sign "$MACOS_SIGNING_IDENTITY" \ + "$GITHUB_WORKSPACE/signing/Vanilla.app" + codesign --verify --deep --strict --verbose=2 "$GITHUB_WORKSPACE/signing/Vanilla.app" + + - name: Notarize and staple app + env: + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} + run: | + APP="$GITHUB_WORKSPACE/signing/Vanilla.app" + ARCHIVE="$RUNNER_TEMP/Vanilla-notary.zip" + + ditto -c -k --keepParent "$APP" "$ARCHIVE" + xcrun notarytool submit "$ARCHIVE" \ + --key "$MACOS_NOTARY_KEY_PATH" \ + --key-id "$MACOS_NOTARY_KEY_ID" \ + --issuer "$MACOS_NOTARY_ISSUER_ID" \ + --wait + + xcrun stapler staple "$APP" + xcrun stapler validate "$APP" + spctl --assess --type execute --verbose=4 "$APP" + + - name: Package + run: ditto -c -k --keepParent signing/Vanilla.app vanilla-macos-universal.zip + + - name: Remove signing credentials + if: ${{ always() }} + env: + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + run: | + security delete-keychain "$RUNNER_TEMP/vanilla-signing.keychain-db" || true + rm -f "$RUNNER_TEMP/developer-id.p12" + if [ -n "$MACOS_NOTARY_KEY_ID" ]; then + rm -f "$RUNNER_TEMP/AuthKey_${MACOS_NOTARY_KEY_ID}.p8" + fi + + - name: Upload signed macOS artifact + uses: actions/upload-artifact@v4 + with: + name: macOS signed and notarized + path: vanilla-macos-universal.zip + if-no-files-found: error + + - name: Upload signed continuous release + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'vanilla-wiiu/vanilla' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -fLOSs --retry 2 --retry-delay 60 https://github.com/probonopd/uploadtool/raw/master/upload.sh + bash upload.sh vanilla-macos-universal.zip + build: strategy: fail-fast: false @@ -27,9 +216,6 @@ jobs: - os: android arch: universal artifact: vanilla-android-universal.apk - - os: macos - arch: universal - artifact: vanilla-macos-universal.zip - os: raspberrypi0w arch: armv6l artifact: vanilla-raspberrypi0w-image.zip @@ -95,8 +281,8 @@ jobs: run: | tar czf ${{ matrix.platform.artifact }} -C "${{ github.workspace }}/build" . - - name: Archive (Windows/macOS/Raspberry Pi/Nintendo Switch) - if: ${{ matrix.platform.os == 'windows' || matrix.platform.os == 'macos' || matrix.platform.os == 'raspberrypi0w' || matrix.platform.os == 'raspberrypi64' || matrix.platform.os == 'nintendoswitch' || matrix.platform.os == 'orangepi_zero3' || matrix.platform.os == 'orangepi_zero2w' }} + - name: Archive (Windows/Raspberry Pi/Nintendo Switch) + if: ${{ matrix.platform.os == 'windows' || matrix.platform.os == 'raspberrypi0w' || matrix.platform.os == 'raspberrypi64' || matrix.platform.os == 'nintendoswitch' || matrix.platform.os == 'orangepi_zero3' || matrix.platform.os == 'orangepi_zero2w' }} run: | cd build zip -r ../${{ matrix.platform.artifact }} . @@ -130,4 +316,3 @@ jobs: run: | curl -fLOSs --retry 2 --retry-delay 60 https://github.com/probonopd/uploadtool/raw/master/upload.sh bash upload.sh ${{ matrix.platform.artifact }} - diff --git a/.gitignore b/.gitignore index cef5039..957ed6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ +CMakeUserPresets.json .vscode/ .directory .cache/ @@ -12,3 +13,6 @@ buildpi/ .idea/ android/local.properties android/app/src/main/assets + +# macOS +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f7b49f..449be7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,13 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) set(LINUX TRUE) endif() -if(ANDROID OR MSVC) +if(APPLE) + # FFmpeg's configure step expects both compiler drivers even though Vanilla + # itself is written in C. + enable_language(CXX) +endif() + +if(ANDROID OR MSVC OR IOS) set(vendored_default TRUE) else() set(vendored_default FALSE) diff --git a/README.md b/README.md index 9272dd3..443c8d5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ An Arch User Repository (AUR) package called `vanilla-wiiu-git` is available for - [Raspberry Pi](https://github.com/vanilla-wiiu/vanilla/wiki/Single%E2%80%90Board-Computers) (requires [external Wi-Fi adapter](https://github.com/vanilla-wiiu/vanilla/wiki/Linux-Setup-Guide#raspberry-pi)) - [Orange Pi](https://github.com/vanilla-wiiu/vanilla/wiki/Single%E2%80%90Board-Computers) (requires [external Wi-Fi adapter](https://github.com/vanilla-wiiu/vanilla/wiki/Linux-Setup-Guide#raspberry-pi)) - macOS (currently frontend only) -- *iOS* - Coming soon +- [iOS](https://github.com/vanilla-wiiu/vanilla/wiki/iOS) (currently frontend only) ## What Wi-Fi adapter should I use? diff --git a/cmake/ios-toolchain.cmake b/cmake/ios-toolchain.cmake new file mode 100644 index 0000000..ef442c4 --- /dev/null +++ b/cmake/ios-toolchain.cmake @@ -0,0 +1,50 @@ +set(CMAKE_SYSTEM_NAME iOS) + +if(NOT CMAKE_GENERATOR STREQUAL "Xcode") + message(FATAL_ERROR "iOS build requires Xcode generator") +endif() + +set(CMAKE_OSX_SYSROOT iphoneos CACHE STRING "iOS SDK") +set(CMAKE_OSX_ARCHITECTURES arm64 CACHE STRING "iOS architectures") +set(CMAKE_XCODE_GENERATE_SCHEME ON CACHE BOOL "Generate an Xcode scheme") + +# Determine Xcode version to determine iOS deployment target +if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + execute_process( + COMMAND xcodebuild -version + RESULT_VARIABLE _vanilla_xcode_version_result + OUTPUT_VARIABLE _vanilla_xcode_version_output + ERROR_VARIABLE _vanilla_xcode_version_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _vanilla_xcode_version_result EQUAL 0) + message(FATAL_ERROR "Could not determine the selected Xcode version: ${_vanilla_xcode_version_error}") + endif() + + string(REGEX MATCH "Xcode ([0-9]+(\\.[0-9]+)*)" _vanilla_xcode_version_match "${_vanilla_xcode_version_output}") + if(NOT CMAKE_MATCH_1) + message(FATAL_ERROR "Could not parse the selected Xcode version from: ${_vanilla_xcode_version_output}") + endif() + + if(CMAKE_MATCH_1 VERSION_GREATER_EQUAL 26.0) + set(_vanilla_ios_deployment_target 15.0) + else() + set(_vanilla_ios_deployment_target 12.0) + endif() + set(CMAKE_OSX_DEPLOYMENT_TARGET "${_vanilla_ios_deployment_target}" CACHE STRING "Minimum iOS deployment version") +endif() + +if(IS_DIRECTORY "${CMAKE_OSX_SYSROOT}") + set(VANILLA_IOS_SDK_PATH "${CMAKE_OSX_SYSROOT}") +else() + execute_process( + COMMAND xcrun --sdk "${CMAKE_OSX_SYSROOT}" --show-sdk-path + RESULT_VARIABLE _vanilla_ios_sdk_result + OUTPUT_VARIABLE VANILLA_IOS_SDK_PATH + ERROR_VARIABLE _vanilla_ios_sdk_error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT _vanilla_ios_sdk_result EQUAL 0 OR NOT IS_DIRECTORY "${VANILLA_IOS_SDK_PATH}") + message(FATAL_ERROR "Could not resolve iOS SDK '${CMAKE_OSX_SYSROOT}': ${_vanilla_ios_sdk_error}") + endif() +endif() diff --git a/docker/macos/Dockerfile b/docker/macos/Dockerfile deleted file mode 100644 index ec97a34..0000000 --- a/docker/macos/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM docker.io/ubuntu:24.04@sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517 - -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=Etc/UTC - -RUN apt -y update && \ - apt -y install build-essential git clang patch python3 cmake patch libssl-dev libxml2-dev liblzma-dev cpio libbz2-dev zlib1g-dev bash wget nasm && \ - apt-get clean && \ - apt-get autoremove --yes && \ - rm -rf /var/lib/{apt,dpkg,cache,log}/ - -RUN git clone https://github.com/tpoechtrager/osxcross - -RUN wget -c https://archive.org/download/xcode-clt/Command_Line_Tools_for_Xcode_16.4.dmg - -RUN /osxcross/tools/gen_sdk_package_tools_dmg.sh /Command_Line_Tools_for_Xcode_*.dmg - -RUN rm /Command_Line_Tools_for_Xcode_*.dmg - -RUN mv /osxcross/*.sdk.tar.xz /osxcross/tarballs/ - -RUN SDK_VERSION=14 UNATTENDED=1 /osxcross/build.sh -RUN /osxcross/build_compiler_rt.sh - -# Fix linker in osxcross cmake (https://github.com/tpoechtrager/osxcross/issues/454) -RUN echo 'set(CMAKE_LINKER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ld" CACHE FILEPATH "ld")' >> /osxcross/target/toolchain.cmake -RUN echo 'set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=${CMAKE_LINKER}")' >> /osxcross/target/toolchain.cmake -RUN echo 'set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=${CMAKE_LINKER}")' >> /osxcross/target/toolchain.cmake -RUN echo 'set(CMAKE_MODULE_LINKER_FLAGS "-fuse-ld=${CMAKE_LINKER}")' >> /osxcross/target/toolchain.cmake - -ENV PATH=/osxcross/target/bin:$PATH - -ENTRYPOINT [ "/vanilla/docker/macos/build.sh" ] diff --git a/docker/macos/build.sh b/docker/macos/build.sh deleted file mode 100755 index 154b346..0000000 --- a/docker/macos/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -e - -cmake \ - -S /vanilla \ - -B /build \ - -DVANILLA_BUILD_VENDORED=ON \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_TOOLCHAIN_FILE=/vanilla/docker/macos/toolchain.cmake - -cmake --build /build --parallel -mv /build/bin/*.app /install/ diff --git a/docker/macos/toolchain.cmake b/docker/macos/toolchain.cmake deleted file mode 100644 index 1bcd371..0000000 --- a/docker/macos/toolchain.cmake +++ /dev/null @@ -1,19 +0,0 @@ -set(CMAKE_CROSSCOMPILING ON) - -set(CMAKE_SYSTEM_NAME "Darwin") - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") -set(CMAKE_OSX_SYSROOT "/osxcross/target/SDK/MacOSX14.sdk") -set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") - -set(CMAKE_INSTALL_NAME_TOOL "xcrun install_name_tool") -set(CMAKE_C_COMPILER clang) -set(CMAKE_CXX_COMPILER clang) -execute_process(COMMAND xcrun -find ar OUTPUT_VARIABLE CMAKE_AR OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND xcrun -find nm OUTPUT_VARIABLE CMAKE_NM OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND xcrun -find ranlib OUTPUT_VARIABLE CMAKE_RANLIB OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND xcrun -find strip OUTPUT_VARIABLE CMAKE_STRIP OUTPUT_STRIP_TRAILING_WHITESPACE) - -execute_process(COMMAND xcrun -find ld OUTPUT_VARIABLE CMAKE_LINKER OUTPUT_STRIP_TRAILING_WHITESPACE) -set(CMAKE_C_FLAGS "--target=darwin -fuse-ld=${CMAKE_LINKER}") -set(CMAKE_CXX_FLAGS "--target=darwin -fuse-ld=${CMAKE_LINKER}") diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 199b844..27fee24 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -69,6 +69,27 @@ function(compile_ffmpeg _target_name _system_name _system_processor) ) elseif(MSVC) set(FFMPEG_TARGET_OS "win32") + elseif(IOS) + set(FFMPEG_TARGET_OS "darwin") + set(_ios_version_flag "-mios-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") + set(FFMPEG_LDFLAGS "${CMAKE_C_FLAGS} -arch ${_system_processor} ${_ios_version_flag} -isysroot ${VANILLA_IOS_SDK_PATH}") + set(FFMPEG_CFLAGS "${CMAKE_C_FLAGS} -arch ${_system_processor} ${_ios_version_flag} -isysroot ${VANILLA_IOS_SDK_PATH} -w") + list(APPEND FFMPEG_CONFIGURE_ARGS + --disable-asm + --disable-avdevice + --disable-indevs + --disable-outdevs + --nm=${CMAKE_NM} + --ar=${CMAKE_AR} + --ranlib=${CMAKE_RANLIB} + --cc=${CMAKE_C_COMPILER} + --cxx=${CMAKE_CXX_COMPILER} + --ld=${CMAKE_C_COMPILER} + --strip=${CMAKE_STRIP} + --host-cc=clang + --host-ld=clang + --enable-videotoolbox + ) elseif(APPLE) set(FFMPEG_TARGET_OS "darwin") set(FFMPEG_LDFLAGS "${FFMPEG_LDFLAGS} -arch ${_system_processor} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -L${CMAKE_OSX_SYSROOT}/usr/lib -lSystem") @@ -220,16 +241,23 @@ if (VANILLA_BUILD_VENDORED) set(LIBXML2_WITH_ICONV OFF) set(LIBXML2_WITH_PROGRAMS OFF) set(LIBXML2_WITH_TESTS OFF) - FetchContent_Declare( - libxml2 - GIT_REPOSITORY https://gitlab.gnome.org/GNOME/libxml2.git - GIT_TAG v2.13.5 - EXCLUDE_FROM_ALL - ) - FetchContent_MakeAvailable(SDL2 SDL2_ttf SDL2_image libxml2) + if (NOT IOS) + FetchContent_Declare( + libxml2 + GIT_REPOSITORY https://gitlab.gnome.org/GNOME/libxml2.git + GIT_TAG v2.13.5 + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(SDL2 SDL2_ttf SDL2_image libxml2) + else() + FetchContent_MakeAvailable(SDL2 SDL2_ttf SDL2_image) + endif() - if (APPLE) + if (IOS) + # Compile FFmpeg for arm64 + compile_ffmpeg("FFmpeg" "${CMAKE_SYSTEM_NAME}" "${CMAKE_OSX_ARCHITECTURES}") + elseif (APPLE) # Compile FFmpeg for both x86_64 and arm64 foreach (_arch ${CMAKE_OSX_ARCHITECTURES}) compile_ffmpeg("FFmpeg_${_arch}" "${CMAKE_SYSTEM_NAME}" "${_arch}") @@ -359,10 +387,105 @@ if (VANILLA_BUILD_USE_POLKIT) ) endif() +if(APPLE) + if(IOS) + set(VANILLA_PLATFORM_INFO_PLIST_ENTRIES [=[ + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + wifi + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIStatusBarHidden + + UIViewControllerBasedStatusBarAppearance + + UIRequiresFullScreen + ]=]) + else() + set(VANILLA_PLATFORM_INFO_PLIST_ENTRIES [=[ + LSMinimumSystemVersion + ${CMAKE_OSX_DEPLOYMENT_TARGET} + NSHighResolutionCapable + ]=]) + endif() + + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/res/Info.plist.in" + "${CMAKE_CURRENT_BINARY_DIR}/Vanilla.Info.plist.in" + @ONLY + ) +endif() + # Declare executable add_library(vanillagui STATIC ${VANILLA_GUI_SRC}) if (ANDROID) add_library(vanilla SHARED main.c) +elseif(IOS) + add_executable(vanilla MACOSX_BUNDLE + main.c + ) + set_target_properties(vanilla PROPERTIES + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.mattkc.vanilla" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" + XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${CMAKE_OSX_DEPLOYMENT_TARGET}" + ) + + set_target_properties(vanilla PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO" + ) + + # Compile storyboard + add_custom_command( + TARGET vanilla POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "$/LaunchScreen.storyboardc" + COMMAND xcrun ibtool --compile "$/LaunchScreen.storyboardc" + "${CMAKE_CURRENT_SOURCE_DIR}/res/ios/LaunchScreen.storyboard" + --target-device iphone --target-device ipad + --minimum-deployment-target "${CMAKE_OSX_DEPLOYMENT_TARGET}" + VERBATIM + ) + + # Compile app icon with actool + if(XCODE_VERSION VERSION_GREATER_EQUAL 26.0 + AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 15.0) + set(_vanilla_icon_input "${CMAKE_CURRENT_SOURCE_DIR}/res/ios/AppIcon.icon") + set(_vanilla_icon_extra_args --include-all-app-icons) + else() + set(_vanilla_icon_input "${CMAKE_CURRENT_SOURCE_DIR}/res/ios/Assets.xcassets") + set(_vanilla_icon_extra_args) + endif() + + add_custom_command( + TARGET vanilla POST_BUILD + COMMAND xcrun actool + --compile "$" + --enable-on-demand-resources NO + --app-icon AppIcon + --platform iphoneos + ${_vanilla_icon_extra_args} + --minimum-deployment-target "${CMAKE_OSX_DEPLOYMENT_TARGET}" + --target-device iphone --target-device ipad + --output-partial-info-plist "${CMAKE_CURRENT_BINARY_DIR}/AssetCatalog-Info.plist" + "${_vanilla_icon_input}" + COMMAND /usr/libexec/PlistBuddy -x + -c "Merge ${CMAKE_CURRENT_BINARY_DIR}/AssetCatalog-Info.plist" + "$/Info.plist" + VERBATIM + ) else() add_executable(vanilla MACOSX_BUNDLE main.c) endif() @@ -377,6 +500,11 @@ if (APPLE) # Set bundle name to be more user-friendly set_target_properties(vanilla PROPERTIES OUTPUT_NAME Vanilla + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/Vanilla.Info.plist.in" + MACOSX_BUNDLE_GUI_IDENTIFIER "com.mattkc.vanilla" + MACOSX_BUNDLE_BUNDLE_NAME "Vanilla" + MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" + MACOSX_BUNDLE_BUNDLE_VERSION "1" ) else() # Ensure executable is installed @@ -391,7 +519,6 @@ if (NOT VANILLA_BUILD_VENDORED) endif() target_link_libraries(vanillagui PRIVATE - SDL2::SDL2main ${_sdl_lib} SDL2_ttf SDL2_image @@ -401,7 +528,6 @@ target_link_libraries(vanillagui PRIVATE if (ANDROID) target_link_libraries(vanillagui PRIVATE - SDL2main android GLESv2 log @@ -409,6 +535,9 @@ if (ANDROID) ) endif() +# SDL owns the platform entry-point shim used by main.c. +target_link_libraries(vanilla PRIVATE SDL2::SDL2main ${_sdl_lib}) + # Find FFmpeg if (NOT VANILLA_BUILD_VENDORED) find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil swscale) @@ -431,7 +560,24 @@ if (LINUX) endif() if (APPLE) - target_link_libraries(vanillagui PRIVATE "-framework VideoToolbox" "-framework CoreMedia" iconv) + target_compile_definitions(vanillagui PRIVATE VANILLA_VIDEOTOOLBOX_AVAILABLE) + target_link_libraries(vanillagui PRIVATE + "-framework CoreFoundation" + "-framework CoreMedia" + "-framework CoreVideo" + "-framework Foundation" + "-framework Metal" + "-framework QuartzCore" + "-framework VideoToolbox" + iconv + ) +endif() + +if (IOS) + target_link_libraries(vanillagui PRIVATE + "-framework ImageIO" + "-framework MobileCoreServices" + ) endif() if (VANILLA_BUILD_PIPE) @@ -484,7 +630,12 @@ endif() # Find libxml2 (used for storing/parsing user config) if (VANILLA_BUILD_VENDORED) - target_link_libraries(vanillagui PRIVATE LibXml2::LibXml2) + if (IOS) + target_link_libraries(vanillagui PRIVATE "-lxml2") + target_include_directories(vanillagui PRIVATE "${VANILLA_IOS_SDK_PATH}/usr/include/libxml2") + else() + target_link_libraries(vanillagui PRIVATE LibXml2::LibXml2) + endif() else() find_package(LibXML2 REQUIRED) target_link_libraries(vanillagui PRIVATE ${LibXML2_LIBRARIES}) @@ -506,8 +657,20 @@ target_include_directories(vanillagui PRIVATE ) # Install assets -if(APPLE) - # Put assets in correct place relative to build +if(ANDROID) + # Put assets in correct place relative to build + add_custom_command( + TARGET vanilla POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${CMAKE_CURRENT_SOURCE_DIR}/../android/app/src/main/assets" + ) +elseif(IOS) + # Put assets inside the iOS app bundle + add_custom_command( + TARGET vanilla POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "$/assets" + ) +elseif(APPLE) + # Put assets in correct place relative to build add_custom_command( TARGET vanilla POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different "${CMAKE_CURRENT_SOURCE_DIR}/assets" "$/../Resources/assets" diff --git a/gui/main.c b/gui/main.c index 83fffbe..1571225 100644 --- a/gui/main.c +++ b/gui/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -9,18 +10,22 @@ #include "ui/ui.h" #include "ui/ui_sdl.h" +#if defined(__APPLE__) +#include +#if TARGET_OS_IOS +// SDL_main will be linked incorrectly on iOS unless we include this +#include +#endif +#endif + #define SCREEN_WIDTH 854 #define SCREEN_HEIGHT 480 -#if !defined(ANDROID) && !defined(_WIN32) -#define SDL_main main -#endif - #include "pipemgmt.h" -void display_cli_help(const char **argv); +void display_cli_help(char **argv); -int SDL_main(int argc, const char **argv) +int main(int argc, char **argv) { int ret = 1; @@ -126,7 +131,7 @@ exit_config: return ret; } -void display_cli_help(const char **argv) { +void display_cli_help(char **argv) { vpilog("Usage: %s [options]\n\n", argv[0]); vpilog("Options:\n"); vpilog(" -w, --window Run Vanilla in a window (overrides config)\n"); diff --git a/gui/platform.c b/gui/platform.c index 1a96945..2c6f483 100644 --- a/gui/platform.c +++ b/gui/platform.c @@ -1,12 +1,7 @@ #include "platform.h" -#if defined(ANDROID) -#include -#elif defined(_WIN32) -#include -#endif - #include +#include #include #include "ui/ui_util.h" @@ -34,15 +29,7 @@ void vpi_get_data_filename(char *out, size_t out_size, const char *filename, con void vpilog_va(const char *fmt, va_list va) { -#if defined(ANDROID) - __android_log_vprint(ANDROID_LOG_ERROR, "VPI", fmt, va); -// #elif defined(_WIN32) -// char buf[4096]; -// vsnprintf(buf, sizeof(buf), fmt, va); -// MessageBoxA(0, buf, 0, 0); -#else - vfprintf(stderr, fmt, va); -#endif + SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, va); } void vpilog(const char *fmt, ...) diff --git a/gui/res/Info.plist.in b/gui/res/Info.plist.in new file mode 100644 index 0000000..31dbb54 --- /dev/null +++ b/gui/res/Info.plist.in @@ -0,0 +1,29 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} +@VANILLA_PLATFORM_INFO_PLIST_ENTRIES@ + NSLocalNetworkUsageDescription + Vanilla needs local network access to connect to your Wii U. + NSMicrophoneUsageDescription + Vanilla needs microphone access to stream audio to your Wii U. + + diff --git a/gui/res/Vanilla.entitlements b/gui/res/Vanilla.entitlements new file mode 100644 index 0000000..d459cb2 --- /dev/null +++ b/gui/res/Vanilla.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.device.audio-input + + + diff --git a/gui/res/ios/AppIcon.icon/Assets/Vanilla.svg b/gui/res/ios/AppIcon.icon/Assets/Vanilla.svg new file mode 100644 index 0000000..d996a31 --- /dev/null +++ b/gui/res/ios/AppIcon.icon/Assets/Vanilla.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/res/ios/AppIcon.icon/icon.json b/gui/res/ios/AppIcon.icon/icon.json new file mode 100644 index 0000000..58b780b --- /dev/null +++ b/gui/res/ios/AppIcon.icon/icon.json @@ -0,0 +1,36 @@ +{ + "fill": { + "automatic-gradient": "srgb:0.13333,0.79608,1.00000,1.00000" + }, + "groups": [ + { + "layers": [ + { + "image-name": "Vanilla.svg", + "name": "Vanilla", + "position": { + "scale": 34.5, + "translation-in-points": [ + 0, + 0 + ] + } + } + ], + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": [ + "watchOS" + ], + "squares": "shared" + } +} diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..089dc45 --- /dev/null +++ b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { "idiom" : "iphone", "size" : "20x20", "scale" : "2x", "filename" : "icon-40.png" }, + { "idiom" : "iphone", "size" : "20x20", "scale" : "3x", "filename" : "icon-60.png" }, + { "idiom" : "iphone", "size" : "29x29", "scale" : "2x", "filename" : "icon-58.png" }, + { "idiom" : "iphone", "size" : "29x29", "scale" : "3x", "filename" : "icon-87.png" }, + { "idiom" : "iphone", "size" : "40x40", "scale" : "2x", "filename" : "icon-80.png" }, + { "idiom" : "iphone", "size" : "40x40", "scale" : "3x", "filename" : "icon-120.png" }, + { "idiom" : "iphone", "size" : "60x60", "scale" : "2x", "filename" : "icon-120.png" }, + { "idiom" : "iphone", "size" : "60x60", "scale" : "3x", "filename" : "icon-180.png" }, + { "idiom" : "ipad", "size" : "20x20", "scale" : "1x", "filename" : "icon-20.png" }, + { "idiom" : "ipad", "size" : "20x20", "scale" : "2x", "filename" : "icon-40.png" }, + { "idiom" : "ipad", "size" : "29x29", "scale" : "1x", "filename" : "icon-29.png" }, + { "idiom" : "ipad", "size" : "29x29", "scale" : "2x", "filename" : "icon-58.png" }, + { "idiom" : "ipad", "size" : "40x40", "scale" : "1x", "filename" : "icon-40.png" }, + { "idiom" : "ipad", "size" : "40x40", "scale" : "2x", "filename" : "icon-80.png" }, + { "idiom" : "ipad", "size" : "76x76", "scale" : "1x", "filename" : "icon-76.png" }, + { "idiom" : "ipad", "size" : "76x76", "scale" : "2x", "filename" : "icon-152.png" }, + { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x", "filename" : "icon-167.png" }, + { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x", "filename" : "icon-1024.png" } + ], + "info" : { "version" : 1, "author" : "xcode" } +} diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-1024.png new file mode 100644 index 0000000..d36f3ee Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-120.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-120.png new file mode 100644 index 0000000..90c3ac6 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-120.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-152.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-152.png new file mode 100644 index 0000000..385f5e7 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-152.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-167.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-167.png new file mode 100644 index 0000000..3c40d76 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-167.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-180.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-180.png new file mode 100644 index 0000000..10cea3a Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-180.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-20.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-20.png new file mode 100644 index 0000000..093137b Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-20.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-29.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-29.png new file mode 100644 index 0000000..97d027e Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-29.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-40.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-40.png new file mode 100644 index 0000000..c626515 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-40.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-58.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-58.png new file mode 100644 index 0000000..7241456 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-58.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-60.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-60.png new file mode 100644 index 0000000..2a34d49 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-60.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-76.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-76.png new file mode 100644 index 0000000..a6be856 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-76.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-80.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-80.png new file mode 100644 index 0000000..2a4fad4 Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-80.png differ diff --git a/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-87.png b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-87.png new file mode 100644 index 0000000..26b2b6c Binary files /dev/null and b/gui/res/ios/Assets.xcassets/AppIcon.appiconset/icon-87.png differ diff --git a/gui/res/ios/Assets.xcassets/Contents.json b/gui/res/ios/Assets.xcassets/Contents.json new file mode 100644 index 0000000..0ce4e31 --- /dev/null +++ b/gui/res/ios/Assets.xcassets/Contents.json @@ -0,0 +1,3 @@ +{ + "info" : { "version" : 1, "author" : "xcode" } +} diff --git a/gui/res/ios/LaunchScreen.storyboard b/gui/res/ios/LaunchScreen.storyboard new file mode 100644 index 0000000..a0abceb --- /dev/null +++ b/gui/res/ios/LaunchScreen.storyboard @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +