qt: Add support for building for iOS. (#6594)

This commit is contained in:
Steveice10 2023-06-07 20:40:53 -07:00 committed by GitHub
parent d9bf4fd8a2
commit 238a574645
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 277 additions and 122 deletions

View File

@ -15,24 +15,35 @@ project(citra LANGUAGES C CXX ASM)
if (APPLE) if (APPLE)
enable_language(OBJC) enable_language(OBJC)
if (IOS)
# Enable searching CMAKE_PREFIX_PATH for bundled dependencies.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
endif()
endif() endif()
option(ENABLE_LTO "Enable link time optimization" OFF) option(ENABLE_LTO "Enable link time optimization" OFF)
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) option(ENABLE_SDL2 "Enable using SDL2" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_SDL2_FRONTEND "Enable the SDL2 frontend" ON "ENABLE_SDL2;NOT ANDROID AND NOT IOS" OFF)
option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OFF) option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OFF)
# Set bundled qt as dependent options. # Set bundled qt as dependent options.
option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_QT_UPDATER "Enable built-in updater for the Qt frontend" ON "NOT IOS" OFF)
CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC OR APPLE" OFF) CMAKE_DEPENDENT_OPTION(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC OR APPLE" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_TESTS "Enable generating tests executable" ON "NOT IOS" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_DEDICATED_ROOM "Enable generating dedicated room executable" ON "NOT ANDROID AND NOT IOS" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
if (MSVC) if (MSVC)
set(OPENSSL_DLL_DIR "" CACHE PATH "Location of the Openssl dlls") set(OPENSSL_DLL_DIR "" CACHE PATH "Location of the Openssl dlls")
endif() endif()
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON) option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support" ON "NOT IOS" OFF) CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support" ON "NOT IOS" OFF)
@ -205,8 +216,7 @@ find_package(Threads REQUIRED)
if (ENABLE_QT) if (ENABLE_QT)
if (CITRA_USE_BUNDLED_QT) if (CITRA_USE_BUNDLED_QT)
download_qt_external(6.5.0 QT_PREFIX) download_qt_external(6.5.0)
list(APPEND CMAKE_PREFIX_PATH ${QT_PREFIX})
endif() endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
@ -290,10 +300,14 @@ if (APPLE)
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED) find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.") message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
# Umbrella framework for everything GUI-related if (NOT IOS)
find_library(COCOA_LIBRARY Cocoa REQUIRED) # Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa REQUIRED)
endif()
find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED) find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${MOLTENVK_LIBRARY}) find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY} ${MOLTENVK_LIBRARY})
elseif (WIN32) elseif (WIN32)
set(PLATFORM_LIBRARIES winmm ws2_32) set(PLATFORM_LIBRARIES winmm ws2_32)
if (MINGW) if (MINGW)

View File

@ -36,10 +36,11 @@ endfunction()
# Params: # Params:
# target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool. # target: Qt dependency to install. Specify a version number to download Qt, or "tools_(name)" for a specific build tool.
# prefix_var: Name of a variable which will be set with the path to the extracted contents. # prefix_var: Name of a variable which will be set with the path to the extracted contents.
function(download_qt_external target prefix_var) function(download_qt_external target)
# Determine installation parameters for OS, architecture, and compiler # Determine installation parameters for OS, architecture, and compiler
if (WIN32) if (WIN32)
set(host "windows") set(host "windows")
set(type "desktop")
if (MINGW) if (MINGW)
set(arch_path "mingw81") set(arch_path "mingw81")
elseif ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND "x86_64" IN_LIST ARCHITECTURE) elseif ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND "x86_64" IN_LIST ARCHITECTURE)
@ -56,10 +57,19 @@ function(download_qt_external target prefix_var)
set(arch "win64_${arch_path}") set(arch "win64_${arch_path}")
elseif (APPLE) elseif (APPLE)
set(host "mac") set(host "mac")
set(arch "clang_64") if (IOS)
set(arch_path "macos") set(type "ios")
set(arch "ios")
set(arch_path "ios")
set(host_arch_path "macos")
else()
set(type "desktop")
set(arch "clang_64")
set(arch_path "macos")
endif()
else() else()
set(host "linux") set(host "linux")
set(type "desktop")
set(arch "gcc_64") set(arch "gcc_64")
set(arch_path "linux") set(arch_path "linux")
endif() endif()
@ -72,7 +82,11 @@ function(download_qt_external target prefix_var)
set(install_args install-tool --outputdir ${base_path} ${host} desktop ${target}) set(install_args install-tool --outputdir ${base_path} ${host} desktop ${target})
else() else()
set(prefix "${base_path}/${target}/${arch_path}") set(prefix "${base_path}/${target}/${arch_path}")
set(install_args install-qt --outputdir ${base_path} ${host} desktop ${target} ${arch} -m qtmultimedia) if (host_arch_path)
set(host_flag "--autodesktop")
set(host_prefix "${base_path}/${target}/${host_arch_path}")
endif()
set(install_args install-qt --outputdir ${base_path} ${host} ${type} ${target} ${arch} ${host_flag} -m qtmultimedia)
endif() endif()
if (NOT EXISTS "${prefix}") if (NOT EXISTS "${prefix}")
@ -97,7 +111,15 @@ function(download_qt_external target prefix_var)
endif() endif()
message(STATUS "Using downloaded Qt binaries at ${prefix}") message(STATUS "Using downloaded Qt binaries at ${prefix}")
set(${prefix_var} "${prefix}" PARENT_SCOPE)
# Add the Qt prefix path so CMake can locate it.
list(APPEND CMAKE_PREFIX_PATH "${prefix}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
if (DEFINED host_prefix)
message(STATUS "Using downloaded host Qt binaries at ${host_prefix}")
set(QT_HOST_PATH "${host_prefix}" CACHE STRING "")
endif()
endfunction() endfunction()
function(download_moltenvk) function(download_moltenvk)

View File

@ -0,0 +1,54 @@
# Gets a UTC timstamp and sets the provided variable to it
function(get_timestamp _var)
string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction()
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
# Find the package here with the known path so that the GetGit commands can find it as well
find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
# generate git/build information
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
string(SUBSTRING "${GIT_REV}" 0 7 GIT_SHORT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE)
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
set(BUILD_FULLNAME "${GIT_SHORT_REV}")
if (DEFINED ENV{CI})
if (DEFINED ENV{GITHUB_ACTIONS})
set(BUILD_REPOSITORY $ENV{GITHUB_REPOSITORY})
set(BUILD_TAG $ENV{GITHUB_REF_NAME})
endif()
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "citra-emu/citra-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if ("${CMAKE_MATCH_COUNT}" GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
endforeach()
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()

View File

@ -1,55 +1,5 @@
# Gets a UTC timstamp and sets the provided variable to it list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/CMakeModules")
function(get_timestamp _var) include(GenerateBuildInfo)
string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE)
endfunction()
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
# Find the package here with the known path so that the GetGit commands can find it as well
find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
# generate git/build information
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
get_timestamp(BUILD_DATE)
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
if (DEFINED ENV{CI})
if (DEFINED ENV{GITHUB_ACTIONS})
set(BUILD_REPOSITORY $ENV{GITHUB_REPOSITORY})
set(BUILD_TAG $ENV{GITHUB_REF_NAME})
endif()
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "citra-emu/citra-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if ("${CMAKE_MATCH_COUNT}" GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
endforeach()
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR) # The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
set(VIDEO_CORE "${SRC_DIR}/src/video_core") set(VIDEO_CORE "${SRC_DIR}/src/video_core")

View File

@ -2,45 +2,49 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <!-- Templated data -->
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>citra.icns</string>
<key>CFBundleIdentifier</key>
<string>com.citra-emu.citra</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string></string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Citra</string> <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key> <key>CFBundleIdentifier</key>
<string>APPL</string> <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleShortVersionString</key>
<string></string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string></string> <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key> <key>CFBundleShortVersionString</key>
<true/> <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSRequiresCarbon</key> <key>CFBundleLongVersionString</key>
<true/> <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string></string> <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSPrincipalClass</key> <!-- Fixed -->
<string>NSApplication</string> <key>LSApplicationCategoryType</key>
<key>NSHighResolutionCapable</key> <string>public.app-category.games</string>
<string>True</string>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>This app requires camera access to emulate the 3DS&apos;s cameras.</string> <string>This app requires camera access to emulate the 3DS&apos;s cameras.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to emulate the 3DS&apos;s microphone.</string> <string>This app requires microphone access to emulate the 3DS&apos;s microphone.</string>
<key>LSApplicationCategoryType</key> <key>CFBundleInfoDictionaryVersion</key>
<string>public.app-category.games</string> <string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
</dict> </dict>
</plist> </plist>

42
dist/apple/LaunchScreen.storyboard vendored Normal file
View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="Y6W-OH-hqX">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="yrZ-hu-Uge">
<rect key="frame" x="-59.666666666666657" y="306" width="512.33333333333337" height="240"/>
<constraints>
<constraint firstAttribute="height" constant="240" id="VhL-hA-Bwr"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="yrZ-hu-Uge" firstAttribute="centerX" secondItem="5EZ-qb-Rvc" secondAttribute="centerX" id="1y3-Mx-a65"/>
<constraint firstItem="yrZ-hu-Uge" firstAttribute="centerY" secondItem="5EZ-qb-Rvc" secondAttribute="centerY" id="vNO-xV-EPD"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="219" y="18"/>
</scene>
</scenes>
<resources>
<image name="launch_logo.png" width="512" height="512"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

BIN
dist/apple/launch_logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -148,6 +148,7 @@ target_include_directories(enet INTERFACE ./enet/include)
# Cubeb # Cubeb
if (ENABLE_CUBEB) if (ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "") set(BUILD_TESTS OFF CACHE BOOL "")
set(BUILD_TOOLS OFF CACHE BOOL "")
add_subdirectory(cubeb EXCLUDE_FROM_ALL) add_subdirectory(cubeb EXCLUDE_FROM_ALL)
endif() endif()

2
externals/libressl vendored

@ -1 +1 @@
Subproject commit 8929f818fd748fd31a34fec7c04558399e13014a Subproject commit dcf9a84aba598f827f65d946d31c3c93af62790a

View File

@ -143,9 +143,12 @@ add_subdirectory(video_core)
add_subdirectory(audio_core) add_subdirectory(audio_core)
add_subdirectory(network) add_subdirectory(network)
add_subdirectory(input_common) add_subdirectory(input_common)
add_subdirectory(tests)
if (ENABLE_SDL2) if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
if (ENABLE_SDL2 AND ENABLE_SDL2_FRONTEND)
add_subdirectory(citra) add_subdirectory(citra)
endif() endif()
@ -153,11 +156,13 @@ if (ENABLE_QT)
add_subdirectory(citra_qt) add_subdirectory(citra_qt)
endif() endif()
if (ENABLE_DEDICATED_ROOM)
add_subdirectory(dedicated_room)
endif()
if (ANDROID) if (ANDROID)
add_subdirectory(android/app/src/main/jni) add_subdirectory(android/app/src/main/jni)
target_include_directories(citra-android PRIVATE android/app/src/main) target_include_directories(citra-android PRIVATE android/app/src/main)
else()
add_subdirectory(dedicated_room)
endif() endif()
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)

View File

@ -8,7 +8,6 @@ if (POLICY CMP0071)
endif() endif()
add_executable(citra-qt add_executable(citra-qt
Info.plist
aboutdialog.cpp aboutdialog.cpp
aboutdialog.h aboutdialog.h
aboutdialog.ui aboutdialog.ui
@ -169,9 +168,6 @@ add_executable(citra-qt
uisettings.h uisettings.h
qt_image_interface.cpp qt_image_interface.cpp
qt_image_interface.h qt_image_interface.h
updater/updater.cpp
updater/updater.h
updater/updater_p.h
util/clickable_label.cpp util/clickable_label.cpp
util/clickable_label.h util/clickable_label.h
util/sequence_dialog/sequence_dialog.cpp util/sequence_dialog/sequence_dialog.cpp
@ -202,6 +198,15 @@ file(GLOB COMPAT_LIST
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
if (ENABLE_QT_UPDATER)
target_sources(citra-qt PRIVATE
updater/updater.cpp
updater/updater.h
updater/updater_p.h
)
target_compile_definitions(citra-qt PUBLIC ENABLE_QT_UPDATER)
endif()
if (ENABLE_QT_TRANSLATION) if (ENABLE_QT_TRANSLATION)
set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") set(CITRA_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
@ -248,17 +253,43 @@ target_sources(citra-qt
) )
if (APPLE) if (APPLE)
set(MACOSX_ICON "../../dist/citra.icns") set(DIST_DIR "../../dist/apple")
set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(APPLE_RESOURCES
"${DIST_DIR}/citra.icns"
"${DIST_DIR}/LaunchScreen.storyboard"
"${DIST_DIR}/launch_logo.png"
)
target_sources(citra-qt PRIVATE target_sources(citra-qt PRIVATE
${MACOSX_ICON} ${APPLE_RESOURCES}
macos_authorization.h macos_authorization.h
macos_authorization.mm macos_authorization.mm
) )
# Define app bundle metadata.
include(GenerateBuildInfo)
set_target_properties(citra-qt PROPERTIES set_target_properties(citra-qt PROPERTIES
MACOSX_BUNDLE TRUE MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist MACOSX_BUNDLE_INFO_PLIST "${DIST_DIR}/Info.plist.in"
MACOSX_BUNDLE_BUNDLE_NAME "Citra"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.citra-emu.citra"
MACOSX_BUNDLE_BUNDLE_VERSION "${BUILD_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${BUILD_FULLNAME}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${BUILD_FULLNAME}"
MACOSX_BUNDLE_ICON_FILE "citra.icns"
RESOURCE "${APPLE_RESOURCES}"
) )
if (IOS)
set_target_properties(citra-qt PROPERTIES
# Have Xcode copy and sign MoltenVK into app bundle.
XCODE_EMBED_FRAMEWORKS "${MOLTENVK_LIBRARY}"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
# Support iPhone and iPad.
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
)
endif()
elseif(WIN32) elseif(WIN32)
# compile as a win32 gui application instead of a console application # compile as a win32 gui application instead of a console application
target_link_libraries(citra-qt PRIVATE Qt6::EntryPointImplementation) target_link_libraries(citra-qt PRIVATE Qt6::EntryPointImplementation)

View File

@ -364,7 +364,7 @@ static Frontend::WindowSystemType GetWindowSystemType() {
return Frontend::WindowSystemType::X11; return Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland")) else if (platform_name == QStringLiteral("wayland"))
return Frontend::WindowSystemType::Wayland; return Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa")) else if (platform_name == QStringLiteral("cocoa") || platform_name == QStringLiteral("ios"))
return Frontend::WindowSystemType::MacOS; return Frontend::WindowSystemType::MacOS;
LOG_CRITICAL(Frontend, "Unknown Qt platform!"); LOG_CRITICAL(Frontend, "Unknown Qt platform!");

View File

@ -223,7 +223,12 @@ GMainWindow::GMainWindow(Core::System& system_)
InitializeRecentFileMenuActions(); InitializeRecentFileMenuActions();
InitializeSaveStateMenuActions(); InitializeSaveStateMenuActions();
InitializeHotkeys(); InitializeHotkeys();
#if ENABLE_QT_UPDATER
ShowUpdaterWidgets(); ShowUpdaterWidgets();
#else
ui->action_Check_For_Updates->setVisible(false);
ui->action_Open_Maintenance_Tool->setVisible(false);
#endif
SetDefaultUIGeometry(); SetDefaultUIGeometry();
RestoreUIState(); RestoreUIState();
@ -268,9 +273,11 @@ GMainWindow::GMainWindow(Core::System& system_)
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor); connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity); connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::OnMouseActivity);
#if ENABLE_QT_UPDATER
if (UISettings::values.check_for_update_on_start) { if (UISettings::values.check_for_update_on_start) {
CheckForUpdates(); CheckForUpdates();
} }
#endif
QStringList args = QApplication::arguments(); QStringList args = QApplication::arguments();
if (args.length() >= 2) { if (args.length() >= 2) {
@ -322,9 +329,11 @@ void GMainWindow::InitializeWidgets() {
ui->action_Show_Room); ui->action_Show_Room);
multiplayer_state->setVisible(false); multiplayer_state->setVisible(false);
#if ENABLE_QT_UPDATER
// Setup updater // Setup updater
updater = new Updater(this); updater = new Updater(this);
UISettings::values.updater_found = updater->HasUpdater(); UISettings::values.updater_found = updater->HasUpdater();
#endif
UpdateBootHomeMenuState(); UpdateBootHomeMenuState();
@ -655,13 +664,6 @@ void GMainWindow::InitializeHotkeys() {
}); });
} }
void GMainWindow::ShowUpdaterWidgets() {
ui->action_Check_For_Updates->setVisible(UISettings::values.updater_found);
ui->action_Open_Maintenance_Tool->setVisible(UISettings::values.updater_found);
connect(updater, &Updater::CheckUpdatesDone, this, &GMainWindow::OnUpdateFound);
}
void GMainWindow::SetDefaultUIGeometry() { void GMainWindow::SetDefaultUIGeometry() {
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
const QRect screenRect = screen()->geometry(); const QRect screenRect = screen()->geometry();
@ -851,8 +853,11 @@ void GMainWindow::ConnectMenuEvents() {
QDesktopServices::openUrl(QUrl(QStringLiteral("https://citra-emu.org/wiki/faq/"))); QDesktopServices::openUrl(QUrl(QStringLiteral("https://citra-emu.org/wiki/faq/")));
}); });
connect_menu(ui->action_About, &GMainWindow::OnMenuAboutCitra); connect_menu(ui->action_About, &GMainWindow::OnMenuAboutCitra);
#if ENABLE_QT_UPDATER
connect_menu(ui->action_Check_For_Updates, &GMainWindow::OnCheckForUpdates); connect_menu(ui->action_Check_For_Updates, &GMainWindow::OnCheckForUpdates);
connect_menu(ui->action_Open_Maintenance_Tool, &GMainWindow::OnOpenUpdater); connect_menu(ui->action_Open_Maintenance_Tool, &GMainWindow::OnOpenUpdater);
#endif
} }
void GMainWindow::UpdateMenuState() { void GMainWindow::UpdateMenuState() {
@ -904,6 +909,7 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
} }
} }
#if ENABLE_QT_UPDATER
void GMainWindow::OnCheckForUpdates() { void GMainWindow::OnCheckForUpdates() {
explicit_update_check = true; explicit_update_check = true;
CheckForUpdates(); CheckForUpdates();
@ -970,6 +976,14 @@ void GMainWindow::OnOpenUpdater() {
updater->LaunchUI(); updater->LaunchUI();
} }
void GMainWindow::ShowUpdaterWidgets() {
ui->action_Check_For_Updates->setVisible(UISettings::values.updater_found);
ui->action_Open_Maintenance_Tool->setVisible(UISettings::values.updater_found);
connect(updater, &Updater::CheckUpdatesDone, this, &GMainWindow::OnUpdateFound);
}
#endif
#if defined(__unix__) && !defined(__APPLE__) #if defined(__unix__) && !defined(__APPLE__)
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) { static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
if (!QDBusConnection::sessionBus().isConnected()) { if (!QDBusConnection::sessionBus().isConnected()) {
@ -1327,9 +1341,11 @@ void GMainWindow::ShutdownGame() {
emulation_running = false; emulation_running = false;
#if ENABLE_QT_UDPATER
if (defer_update_prompt) { if (defer_update_prompt) {
ShowUpdatePrompt(); ShowUpdatePrompt();
} }
#endif
game_title.clear(); game_title.clear();
UpdateWindowTitle(); UpdateWindowTitle();

View File

@ -46,7 +46,9 @@ class QProgressBar;
class QPushButton; class QPushButton;
class QSlider; class QSlider;
class RegistersWidget; class RegistersWidget;
#if ENABLE_QT_UPDATER
class Updater; class Updater;
#endif
class WaitTreeWidget; class WaitTreeWidget;
namespace Camera { namespace Camera {
@ -146,12 +148,15 @@ private:
void ShutdownGame(); void ShutdownGame();
void ShowTelemetryCallout(); void ShowTelemetryCallout();
void SetDiscordEnabled(bool state);
void LoadAmiibo(const QString& filename);
#if ENABLE_QT_UPDATER
void ShowUpdaterWidgets(); void ShowUpdaterWidgets();
void ShowUpdatePrompt(); void ShowUpdatePrompt();
void ShowNoUpdatePrompt(); void ShowNoUpdatePrompt();
void CheckForUpdates(); void CheckForUpdates();
void SetDiscordEnabled(bool state); #endif
void LoadAmiibo(const QString& filename);
/** /**
* Stores the filename in the recently loaded files list. * Stores the filename in the recently loaded files list.
@ -244,9 +249,13 @@ private slots:
void OnCoreError(Core::System::ResultStatus, std::string); void OnCoreError(Core::System::ResultStatus, std::string);
/// Called whenever a user selects Help->About Citra /// Called whenever a user selects Help->About Citra
void OnMenuAboutCitra(); void OnMenuAboutCitra();
#if ENABLE_QT_UPDATER
void OnUpdateFound(bool found, bool error); void OnUpdateFound(bool found, bool error);
void OnCheckForUpdates(); void OnCheckForUpdates();
void OnOpenUpdater(); void OnOpenUpdater();
#endif
void OnLanguageChanged(const QString& locale); void OnLanguageChanged(const QString& locale);
void OnMouseActivity(); void OnMouseActivity();
@ -326,7 +335,9 @@ private:
IPCRecorderWidget* ipcRecorderWidget; IPCRecorderWidget* ipcRecorderWidget;
LLEServiceModulesWidget* lleServiceModulesWidget; LLEServiceModulesWidget* lleServiceModulesWidget;
WaitTreeWidget* waitTreeWidget; WaitTreeWidget* waitTreeWidget;
#if ENABLE_QT_UPDATER
Updater* updater; Updater* updater;
#endif
bool explicit_update_check = false; bool explicit_update_check = false;
bool defer_update_prompt = false; bool defer_update_prompt = false;

View File

@ -21,7 +21,12 @@
#ifdef _WIN32 #ifdef _WIN32
#define EMU_DATA_DIR "Citra" #define EMU_DATA_DIR "Citra"
#elif defined(__APPLE__) #elif defined(__APPLE__)
#define MACOS_EMU_DATA_DIR "Library" DIR_SEP "Application Support" DIR_SEP "Citra" #include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define APPLE_EMU_DATA_DIR "Documents" DIR_SEP "Citra"
#else
#define APPLE_EMU_DATA_DIR "Library" DIR_SEP "Application Support" DIR_SEP "Citra"
#endif
// For compatibility with XDG paths. // For compatibility with XDG paths.
#define EMU_DATA_DIR "citra-emu" #define EMU_DATA_DIR "citra-emu"
#else #else

View File

@ -786,7 +786,7 @@ void SetUserPath(const std::string& path) {
// paths. // paths.
if (!FileUtil::Exists(data_dir) && !FileUtil::Exists(config_dir) && if (!FileUtil::Exists(data_dir) && !FileUtil::Exists(config_dir) &&
!FileUtil::Exists(cache_dir)) { !FileUtil::Exists(cache_dir)) {
data_dir = GetHomeDirectory() + DIR_SEP MACOS_EMU_DATA_DIR DIR_SEP; data_dir = GetHomeDirectory() + DIR_SEP APPLE_EMU_DATA_DIR DIR_SEP;
config_dir = data_dir + CONFIG_DIR DIR_SEP; config_dir = data_dir + CONFIG_DIR DIR_SEP;
cache_dir = data_dir + CACHE_DIR DIR_SEP; cache_dir = data_dir + CACHE_DIR DIR_SEP;
} }