diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 81b406770..e38a07337 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -62,7 +62,7 @@ jobs:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
macos:
- runs-on: ${{ (matrix.target == 'x86_64' && 'macos-13') || 'macos-14' }}
+ runs-on: ${{ (matrix.target == 'x86_64' && 'macos-15-intel') || 'macos-15' }}
strategy:
fail-fast: false
matrix:
@@ -103,7 +103,7 @@ jobs:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
macos-universal:
- runs-on: macos-14
+ runs-on: macos-15
needs: macos
env:
OS: macos
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a736eaac..324ddf1d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,6 +48,15 @@ if (APPLE)
else()
# Minimum macOS 13
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4")
+
+ # Catch compiler issue on AppleClang versions below 15.0
+ # TODO: Remove this check when we drop macOS 13 Ventura
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND
+ CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0)
+ message(FATAL_ERROR "AppleClang 15.0 or later is required due to a compiler bug in earlier versions.\n"
+ "Current version: ${CMAKE_CXX_COMPILER_VERSION}\n"
+ "After updating, delete 'CMakeCache.txt' in the build directory.")
+ endif()
endif()
endif()
@@ -299,7 +308,7 @@ find_package(Threads REQUIRED)
if (ENABLE_QT)
if (NOT USE_SYSTEM_QT)
- download_qt(6.7.2)
+ download_qt(6.9.2)
endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)
diff --git a/CMakeModules/DownloadExternals.cmake b/CMakeModules/DownloadExternals.cmake
index fc8ec6089..a76d95ae1 100644
--- a/CMakeModules/DownloadExternals.cmake
+++ b/CMakeModules/DownloadExternals.cmake
@@ -20,9 +20,9 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
set(arch_path "mingw_64")
elseif (MSVC)
if ("arm64" IN_LIST ARCHITECTURE)
- set(arch_path "msvc2019_arm64")
+ set(arch_path "msvc2022_arm64")
elseif ("x86_64" IN_LIST ARCHITECTURE)
- set(arch_path "msvc2019_64")
+ set(arch_path "msvc2022_64")
else()
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
endif()
@@ -30,12 +30,13 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
- set(host_arch_path "msvc2019_64")
+ set(host_arch_path "msvc2022_64")
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
+ # TODO: ^ Is this still true with msvc2022?
# set(host_arch_path "msvc2019_arm64")
- set(host_arch_path "msvc2019_64")
+ set(host_arch_path "msvc2022_64")
endif()
set(host_arch "win64_${host_arch_path}")
else()
@@ -105,7 +106,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
if (NOT EXISTS "${prefix}")
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
- set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.18")
+ set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0")
if (WIN32)
set(aqt_path "${base_path}/aqt.exe")
if (NOT EXISTS "${aqt_path}")
diff --git a/dist/apple/Info.plist.in b/dist/apple/Info.plist.in
index d12a21f8e..2feacd1b6 100644
--- a/dist/apple/Info.plist.in
+++ b/dist/apple/Info.plist.in
@@ -75,6 +75,9 @@
NSHighResolutionCapable
True
+ UIDesignRequiresCompatibility
+
UIFileSharingEnabled
UILaunchStoryboardName
diff --git a/dist/compatibility_list b/dist/compatibility_list
index 4f3904169..a36decbe4 160000
--- a/dist/compatibility_list
+++ b/dist/compatibility_list
@@ -1 +1 @@
-Subproject commit 4f39041699412873d0afcec89a9313148a192647
+Subproject commit a36decbe43d0e5a570ac3d3ba9a0b226dc832a17
diff --git a/dist/languages/ca_ES_valencia.ts b/dist/languages/ca_ES_valencia.ts
index 65ef0ca2a..bc138e02f 100644
--- a/dist/languages/ca_ES_valencia.ts
+++ b/dist/languages/ca_ES_valencia.ts
@@ -27,7 +27,7 @@
<html><head/><body><p><img src=":/icons/default/256x256/azahar.png"/></p></body></html>
-
+ <html><head/><body><p><img src=":/icons/default/256x256/azahar.png"/></p></body></html>
@@ -725,7 +725,7 @@ Desitja ignorar l'error i continuar?
Show log output in console
-
+ Mostrar l'eixida del registre en la consola
@@ -2275,7 +2275,7 @@ Desitja ignorar l'error i continuar?
<a href='https://web.archive.org/web/20240301211230/https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Learn More</span></a>
-
+ <a href='https://web.archive.org/web/20240301211230/https://citra-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input'><span style="text-decoration: underline; color:#039be5;">Més Informació</span></a>
@@ -2494,12 +2494,12 @@ Desitja ignorar l'error i continuar?
Compress installed CIA content
-
+ Comprimir el contingut de CIAs instal·lats
Compresses the content of CIA files when installed to the emulated SD card. Only affects CIA content which is installed while the setting is enabled.
-
+ Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.
@@ -4431,7 +4431,7 @@ Vols reinstal·lar els arxius de totes maneres?
3DS Installation File (*.cia *.zcia)
-
+ Fitxers d'Instalació de 3DS (*.cia *.zcia)
@@ -4510,24 +4510,24 @@ Vols reinstal·lar els arxius de totes maneres?
Error compressing file
-
+ Error al comprimir el fitxer
File compress operation failed, check log for details.
-
+ Operació de compressió fallida, mira el registre per a més detalls.
Error decompressing file
-
+ Error de descompressió del fitxer
File decompress operation failed, check log for details.
-
+ Operació de descompressió fallida, mira el registre per a més detalls.
@@ -4663,62 +4663,62 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.
Load 3DS ROM File
-
+ Carregar ROM de 3DS
3DS ROM Files (*.cia *cci *3dsx *cxi)
-
+ Fitxers ROM 3DS (*.cia *cci *3dsx *cxi)
The selected file is not a compatible 3DS ROM format. Make sure you have chosen the right file, and that it is not encrypted.
-
+ El fitxer seleccionat no és un ROM de 3DS compatible. Assegura't que has triat el fitxer correcte i que no estiga xifrat.
The selected file is already compressed.
-
+ El fitxer seleccionat ja està comprimit.
3DS Compressed ROM File (*.%1)
-
+ Fitxer ROM 3DS comprimit (*.%1)
Save 3DS Compressed ROM File
-
+ Desar fitxer 3DS comprimit
Load 3DS Compressed ROM File
-
+ Carregar fitxer 3DS comprimit
3DS Compressed ROM Files (*.zcia *zcci *z3dsx *zcxi)
-
+ Fitxer ROM 3DS comprimit (*.zcia *zcci *z3dsx *zcxi)
The selected file is not a compatible compressed 3DS ROM format. Make sure you have chosen the right file.
-
+ El fitxer seleccionat no és un format de ROM 3DS comprimit compatible. Assegura't d'haver triat l'arxiu correcte.
The selected file is already decompressed.
-
+ El fitxer seleccionat ja està descomprimit.
3DS ROM File (*.%1)
-
+ Fitxer ROM 3DS (*.%1)
Save 3DS ROM File
-
+ Desar fitxer ROM 3DS
@@ -4818,7 +4818,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.
Frame: %1 ms (GPU: [CMD: %2 ms, SWP: %3 ms], IPC: %4 ms, SVC: %5 ms, Rem: %6 ms)
-
+ Frame: %1 ms (GPU: [CMD: %2 ms, SWP: %3 ms], IPC: %4 ms, SVC: %5 ms, Rem: %6 ms)
@@ -4839,7 +4839,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.
%1 is missing. Please <a href='https://web.archive.org/web/20240304201103/https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>dump your system archives</a>.<br/>Continuing emulation may result in crashes and bugs.
-
+ Falta %1 . Per favor,<a href='https://web.archive.org/web/20240304201103/https://citra-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>bolca els teus arxius de sistema</a>.<br/>Continuar l'emulació pot resultar en penges i errors.
@@ -4869,7 +4869,7 @@ Per a veure una guia sobre com instal·lar FFmpeg, polsa Ajuda.
A fatal error occurred. <a href='https://web.archive.org/web/20240228001712/https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Check the log</a> for details.<br/>Continuing emulation may result in crashes and bugs.
-
+ Error fatal.<a href='https://web.archive.org/web/20240228001712/https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>Mira el log</a>per a més detalls.<br/>Continuar l'emulació pot resultar en penges i errors.
@@ -6387,12 +6387,12 @@ Missatge de depuració:
Compress ROM File...
-
+ Comprimir fitxer ROM...
Decompress ROM File...
-
+ Descomprimir fitxer ROM...
diff --git a/dist/languages/es_ES.ts b/dist/languages/es_ES.ts
index b872896e8..a8126d7c9 100644
--- a/dist/languages/es_ES.ts
+++ b/dist/languages/es_ES.ts
@@ -4517,7 +4517,7 @@ Reinstall the files anyway?
File compress operation failed, check log for details.
- Operación de comprensión fallida, mira el registro para más detalles.
+ Operación de compresión fallida, mira el registro para más detalles.
@@ -4529,7 +4529,7 @@ Reinstall the files anyway?
File decompress operation failed, check log for details.
- Operación de descomprensión fallida, mira el registro para más detalles.
+ Operación de descompresión fallida, mira el registro para más detalles.
diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts
index f5a4a9b3c..1dcb9ad96 100644
--- a/dist/languages/ru_RU.ts
+++ b/dist/languages/ru_RU.ts
@@ -3894,7 +3894,7 @@ Drag points to change position, or double-click table cells to edit values.
Show current application in your Discord status
-
+ Показывать текущее приложение в статусе Discord
@@ -4027,7 +4027,7 @@ Drag points to change position, or double-click table cells to edit values.
Azahar
- Azahar
+ Azahar
@@ -4170,7 +4170,7 @@ Please check your FFmpeg installation used for compilation.
GBA Virtual Console is not supported by Azahar.
-
+ Azahar не поддерживает GBA Virtual Console.
@@ -4315,7 +4315,7 @@ Please check your FFmpeg installation used for compilation.
Azahar
- Azahar
+ Azahar
@@ -4523,7 +4523,7 @@ Reinstall the files anyway?
Error decompressing file
-
+ Ошибра при разжатии файла
@@ -5036,7 +5036,7 @@ Would you like to download it?
Don't show again
-
+ Не показывать снова
@@ -7383,7 +7383,8 @@ They may have left the room.
Azahar has detected user data for Citra.
-
+ Azahar обнаружил файлы пользователя Citra.
+
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 66cbbd52e..aa6e6c84b 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -1,24 +1,37 @@
# Definitions for all external bundled libraries
# Suppress warnings from external libraries
-if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+if (MSVC)
add_compile_options(/W0)
else()
add_compile_options(-w)
endif()
+function(target_disable_warnings target)
+ if (MSVC)
+ target_compile_options(${target} INTERFACE /W0)
+ else()
+ target_compile_options(${target} INTERFACE -w)
+ endif()
+endfunction()
+
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
include(DownloadExternals)
include(ExternalProject)
# Boost
-if (NOT USE_SYSTEM_BOOST)
+if (USE_SYSTEM_BOOST)
+ unset(BOOST_ROOT CACHE)
+ unset(Boost_INCLUDE_DIR CACHE)
+ set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
+else()
message(STATUS "Including vendored Boost library")
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
add_library(boost INTERFACE)
- target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
+ target_include_directories(boost INTERFACE ${Boost_INCLUDE_DIR})
+ target_disable_warnings(boost)
# Boost::serialization
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
@@ -33,11 +46,7 @@ if (NOT USE_SYSTEM_BOOST)
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
)
target_link_libraries(boost_iostreams PUBLIC boost)
-# Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
-else()
- unset(BOOST_ROOT CACHE)
- unset(Boost_INCLUDE_DIR CACHE)
- set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
+ # Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
endif()
# Catch2
@@ -73,6 +82,7 @@ endif()
# dds-ktx
add_library(dds-ktx INTERFACE)
target_include_directories(dds-ktx INTERFACE ./dds-ktx)
+target_disable_warnings(dds-ktx)
# fmt and Xbyak need to be added before dynarmic
# libfmt
@@ -137,7 +147,8 @@ endif()
# MicroProfile
add_library(microprofile INTERFACE)
-target_include_directories(microprofile SYSTEM INTERFACE ./microprofile)
+target_include_directories(microprofile INTERFACE ./microprofile)
+target_disable_warnings(microprofile)
if (ENABLE_MICROPROFILE)
target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1)
else()
@@ -146,10 +157,11 @@ endif()
# Nihstro
add_library(nihstro-headers INTERFACE)
-target_include_directories(nihstro-headers SYSTEM INTERFACE ./nihstro/include)
-if (MSVC)
- # TODO: For some reason MSVC still applies this warning even with /W0 for externals.
- target_compile_options(nihstro-headers INTERFACE /wd4715)
+target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
+target_disable_warnings(nihstro-headers)
+if (NOT MSVC)
+ # TODO: For some reason MSYS2 still applied this warnin even with -w
+ target_compile_options(nihstro-headers INTERFACE -Wno-invalid-specialization)
endif()
# Open Source Archives
@@ -173,7 +185,8 @@ if (USE_SYSTEM_FFMPEG_HEADERS)
endif()
if (NOT FOUND_FFMPEG_HEADERS)
message(STATUS "Using bundled ffmpeg headers.")
- target_include_directories(library-headers SYSTEM INTERFACE ./library-headers/ffmpeg/include)
+ target_include_directories(library-headers INTERFACE ./library-headers/ffmpeg/include)
+ target_disable_warnings(library-headers)
endif()
# SoundTouch
@@ -294,7 +307,8 @@ if (USE_SYSTEM_JSON)
# Citra uses "#include " so we have to add this manually
target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann")
else()
- target_include_directories(json-headers SYSTEM INTERFACE ./json)
+ target_include_directories(json-headers INTERFACE ./json)
+ target_disable_warnings(json-headers)
endif()
# OpenSSL
@@ -310,7 +324,8 @@ if (NOT OPENSSL_FOUND)
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
- target_include_directories(ssl SYSTEM INTERFACE ./libressl/include)
+ target_include_directories(ssl INTERFACE ./libressl/include)
+ target_disable_warnings(ssl)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl
@@ -327,17 +342,20 @@ if(USE_SYSTEM_CPP_HTTPLIB)
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
if(HTTP_LIBS)
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
- target_include_directories(httplib SYSTEM INTERFACE ./httplib)
+ target_include_directories(httplib INTERFACE ./httplib)
+ target_disable_warnings(httplib)
else()
if(CppHttp_FOUND)
target_link_libraries(httplib INTERFACE httplib::httplib)
else()
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
- target_include_directories(httplib SYSTEM INTERFACE ./httplib)
- endif()
+ target_include_directories(httplib INTERFACE ./httplib)
+ target_disable_warnings(httplib)
+ endif()
endif()
else()
- target_include_directories(httplib SYSTEM INTERFACE ./httplib)
+ target_include_directories(httplib INTERFACE ./httplib)
+ target_disable_warnings(httplib)
endif()
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
@@ -354,7 +372,8 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
else()
add_library(cpp-jwt INTERFACE)
- target_include_directories(cpp-jwt SYSTEM INTERFACE ./cpp-jwt/include)
+ target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
+ target_disable_warnings(cpp-jwt)
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
endif()
endif()
@@ -453,7 +472,8 @@ if (ENABLE_VULKAN)
endif()
else()
add_library(vma INTERFACE)
- target_include_directories(vma SYSTEM INTERFACE ./vma/include)
+ target_include_directories(vma INTERFACE ./vma/include)
+ target_disable_warnings(vma)
endif()
# vulkan-headers
@@ -465,7 +485,8 @@ if (ENABLE_VULKAN)
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
endif()
else()
- target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include)
+ target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
+ target_disable_warnings(vulkan-headers)
endif()
# adrenotools
diff --git a/externals/fmt b/externals/fmt
index 123913715..e424e3f2e 160000
--- a/externals/fmt
+++ b/externals/fmt
@@ -1 +1 @@
-Subproject commit 123913715afeb8a437e6388b4473fcc4753e1c9a
+Subproject commit e424e3f2e607da02742f73db84873b8084fc714c
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 84006116f..e4aff1c8b 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -186,7 +186,7 @@ dependencies {
// Download Vulkan Validation Layers from the KhronosGroup GitHub.
val downloadVulkanValidationLayers = tasks.register("downloadVulkanValidationLayers") {
- src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.304.1/android-binaries-1.4.304.1.zip")
+ src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
onlyIfModified(true)
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
index eec388463..e53354dc9 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
@@ -96,6 +96,24 @@ object NativeLibrary {
*/
external fun onTouchMoved(xAxis: Float, yAxis: Float)
+ /**
+ * Handles touch events on the secondary display.
+ *
+ * @param xAxis The value of the x-axis.
+ * @param yAxis The value of the y-axis.
+ * @param pressed To identify if the touch held down or released.
+ * @return true if the pointer is within the touchscreen
+ */
+ external fun onSecondaryTouchEvent(xAxis: Float, yAxis: Float, pressed: Boolean): Boolean
+
+ /**
+ * Handles touch movement on the secondary display.
+ *
+ * @param xAxis The value of the instantaneous x-axis.
+ * @param yAxis The value of the instantaneous y-axis.
+ */
+ external fun onSecondaryTouchMoved(xAxis: Float, yAxis: Float)
+
external fun reloadSettings()
external fun getTitleId(filename: String): Long
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt
index 3ff594ce9..f23147dd8 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/activities/EmulationActivity.kt
@@ -60,7 +60,15 @@ class EmulationActivity : AppCompatActivity() {
private lateinit var binding: ActivityEmulationBinding
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
private lateinit var hotkeyUtility: HotkeyUtility
- private lateinit var secondaryDisplay: SecondaryDisplay;
+ private lateinit var secondaryDisplay: SecondaryDisplay
+
+ private val onShutdown = Runnable {
+ if (intent.getBooleanExtra("launched_from_shortcut", false)) {
+ finishAffinity()
+ } else {
+ this.finish()
+ }
+ }
private val emulationFragment: EmulationFragment
get() {
@@ -77,8 +85,8 @@ class EmulationActivity : AppCompatActivity() {
ThemeUtil.setTheme(this)
settingsViewModel.settings.loadSettings()
super.onCreate(savedInstanceState)
- secondaryDisplay = SecondaryDisplay(this);
- secondaryDisplay.updateDisplay();
+ secondaryDisplay = SecondaryDisplay(this)
+ secondaryDisplay.updateDisplay()
binding = ActivityEmulationBinding.inflate(layoutInflater)
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
@@ -101,13 +109,7 @@ class EmulationActivity : AppCompatActivity() {
windowManager.defaultDisplay.rotation
)
- EmulationLifecycleUtil.addShutdownHook(hook = {
- if (intent.getBooleanExtra("launched_from_shortcut", false)) {
- finishAffinity()
- } else {
- this.finish()
- }
- })
+ EmulationLifecycleUtil.addShutdownHook(onShutdown)
isEmulationRunning = true
instance = this
@@ -165,12 +167,12 @@ class EmulationActivity : AppCompatActivity() {
}
override fun onDestroy() {
- EmulationLifecycleUtil.clear()
+ EmulationLifecycleUtil.removeHook(onShutdown)
NativeLibrary.playTimeManagerStop()
isEmulationRunning = false
instance = null
secondaryDisplay.releasePresentation()
- secondaryDisplay.releaseVD();
+ secondaryDisplay.releaseVD()
super.onDestroy()
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt b/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt
index 4af42792c..b44929c9d 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/display/SecondaryDisplay.kt
@@ -11,29 +11,30 @@ import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Bundle
import android.view.Display
+import android.view.MotionEvent
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
-import org.citra.citra_emu.NativeLibrary
+import android.view.WindowManager
import org.citra.citra_emu.features.settings.model.IntSetting
+import org.citra.citra_emu.display.SecondaryDisplayLayout
+import org.citra.citra_emu.NativeLibrary
-class SecondaryDisplay(val context: Context) {
+class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
private var pres: SecondaryDisplayPresentation? = null
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
private val vd: VirtualDisplay
init {
- val st = SurfaceTexture(0)
- st.setDefaultBufferSize(1920, 1080)
- val vdSurface = Surface(st)
vd = displayManager.createVirtualDisplay(
"HiddenDisplay",
1920,
1080,
320,
- vdSurface,
+ null,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
)
+ displayManager.registerDisplayListener(this, null)
}
fun updateSurface() {
@@ -44,16 +45,23 @@ class SecondaryDisplay(val context: Context) {
NativeLibrary.secondarySurfaceDestroyed()
}
+ private fun getExternalDisplay(context: Context): Display? {
+ val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ val internalId = context.display.displayId ?: Display.DEFAULT_DISPLAY
+ val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
+ return displays.firstOrNull { it.displayId != internalId && it.name != "HiddenDisplay" }
+ }
+
fun updateDisplay() {
// decide if we are going to the external display or the internal one
- var display = getCustomerDisplay()
+ var display = getExternalDisplay(context)
if (display == null ||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
display = vd.display
}
// if our presentation is already on the right display, ignore
- if (pres?.display == display) return;
+ if (pres?.display == display) return
// otherwise, make a new presentation
releasePresentation()
@@ -61,29 +69,41 @@ class SecondaryDisplay(val context: Context) {
pres?.show()
}
- private fun getCustomerDisplay(): Display? {
- val displays = displayManager.displays
- // code taken from MelonDS dual screen - should fix odin 2 detection bug
- return displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
- .firstOrNull { it.displayId != Display.DEFAULT_DISPLAY && it.name != "Built-in Screen" && it.name != "HiddenDisplay"}
- }
-
fun releasePresentation() {
pres?.dismiss()
pres = null
}
fun releaseVD() {
+ displayManager.unregisterDisplayListener(this)
vd.release()
}
+
+ override fun onDisplayAdded(displayId: Int) {
+ updateDisplay()
+ }
+
+ override fun onDisplayRemoved(displayId: Int) {
+ updateDisplay()
+ }
+ override fun onDisplayChanged(displayId: Int) {
+ updateDisplay()
+ }
}
class SecondaryDisplayPresentation(
context: Context, display: Display, val parent: SecondaryDisplay
) : Presentation(context, display) {
private lateinit var surfaceView: SurfaceView
+ private var touchscreenPointerId = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ window?.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ )
// Initialize SurfaceView
surfaceView = SurfaceView(context)
@@ -103,6 +123,42 @@ class SecondaryDisplayPresentation(
}
})
+ this.surfaceView.setOnTouchListener { _, event ->
+
+ val pointerIndex = event.actionIndex
+ val pointerId = event.getPointerId(pointerIndex)
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
+ if (touchscreenPointerId == -1) {
+ touchscreenPointerId = pointerId
+ NativeLibrary.onSecondaryTouchEvent(
+ event.getX(pointerIndex),
+ event.getY(pointerIndex),
+ true
+ )
+ }
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ val index = event.findPointerIndex(touchscreenPointerId)
+ if (index != -1) {
+ NativeLibrary.onSecondaryTouchMoved(
+ event.getX(index),
+ event.getY(index)
+ )
+ }
+ }
+
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
+ if (pointerId == touchscreenPointerId) {
+ NativeLibrary.onSecondaryTouchEvent(0f, 0f, false)
+ touchscreenPointerId = -1
+ }
+ }
+ }
+ true
+ }
+
setContentView(surfaceView) // Set SurfaceView as content
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SliderSetting.kt
index 79406594e..46ed42905 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/SliderSetting.kt
@@ -1,4 +1,4 @@
-// Copyright Citra Emulator Project / Lime3DS Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -10,7 +10,6 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
import org.citra.citra_emu.features.settings.model.FloatSetting
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
import org.citra.citra_emu.utils.Log
-import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
@@ -27,7 +26,8 @@ class SliderSetting(
val selectedFloat: Float
get() {
val setting = setting ?: return defaultValue!!.toFloat()
- return when (setting) {
+
+ val ret = when (setting) {
is AbstractIntSetting -> setting.int.toFloat()
is FloatSetting -> setting.float
is ScaledFloatSetting -> setting.float
@@ -36,8 +36,8 @@ class SliderSetting(
-1f
}
}
+ return ret.coerceIn(min.toFloat(), max.toFloat())
}
-
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
index fbf3e5525..d3625693c 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/fragments/EmulationFragment.kt
@@ -101,6 +101,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val settingsViewModel: SettingsViewModel by viewModels()
+ private val onPause = Runnable{ togglePause() }
+ private val onShutdown = Runnable{ emulationState.stop() }
+
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@@ -156,8 +159,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
emulationState = EmulationState(game.path)
emulationActivity = requireActivity() as EmulationActivity
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings)
- EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
- EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
+ EmulationLifecycleUtil.addPauseResumeHook(onPause)
+ EmulationLifecycleUtil.addShutdownHook(onShutdown)
}
override fun onCreateView(
@@ -507,6 +510,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onDetach()
}
+ override fun onDestroy() {
+ EmulationLifecycleUtil.removeHook(onPause)
+ EmulationLifecycleUtil.removeHook(onShutdown)
+ super.onDestroy()
+ }
+
private fun setupCitraDirectoriesThenStartEmulation() {
val directoryInitializationState = DirectoryInitialization.start()
if (directoryInitializationState ===
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt
index 9828dd046..f7519bb81 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/overlay/InputOverlay.kt
@@ -153,8 +153,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
if (isActionMove) {
NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat())
continue
- }
- else if (isActionUp) {
+ } else if (isActionUp) {
NativeLibrary.onTouchEvent(0f, 0f, false)
break // Up and down actions shouldn't loop
}
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt
index 8f3b5dc07..5fafb7bed 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationLifecycleUtil.kt
@@ -1,4 +1,4 @@
-// Copyright 2023 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -18,15 +18,27 @@ object EmulationLifecycleUtil {
}
fun addShutdownHook(hook: Runnable) {
- shutdownHooks.add(hook)
+ if (shutdownHooks.contains(hook)) {
+ Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
+ } else {
+ shutdownHooks.add(hook)
+ }
}
fun addPauseResumeHook(hook: Runnable) {
- pauseResumeHooks.add(hook)
+ if (pauseResumeHooks.contains(hook)) {
+ Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
+ } else {
+ pauseResumeHooks.add(hook)
+ }
}
- fun clear() {
- pauseResumeHooks.clear()
- shutdownHooks.clear()
+ fun removeHook(hook: Runnable) {
+ if (pauseResumeHooks.contains(hook)) {
+ pauseResumeHooks.remove(hook)
+ }
+ if (shutdownHooks.contains(hook)) {
+ shutdownHooks.remove(hook)
+ }
}
}
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 2cb34cbd0..a2880d6dc 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -657,6 +657,25 @@ void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEn
window->OnTouchMoved((int)x, (int)y);
}
+jboolean Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchEvent([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jobject obj,
+ jfloat x, jfloat y,
+ jboolean pressed) {
+ if (!secondary_window) {
+ return JNI_FALSE;
+ }
+ return static_cast(secondary_window->OnTouchEvent(
+ static_cast(x + 0.5), static_cast(y + 0.5), pressed));
+}
+
+void Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchMoved([[maybe_unused]] JNIEnv* env,
+ [[maybe_unused]] jobject obj,
+ jfloat x, jfloat y) {
+ if (secondary_window) {
+ secondary_window->OnTouchMoved((int)x, (int)y);
+ }
+}
+
jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj,
jstring j_filename) {
std::string filepath = GetJString(env, j_filename);
diff --git a/src/android/app/src/main/res/values-b+ca+ES+valencia/strings.xml b/src/android/app/src/main/res/values-b+ca+ES+valencia/strings.xml
index eb3beaa25..0f0e3e978 100644
--- a/src/android/app/src/main/res/values-b+ca+ES+valencia/strings.xml
+++ b/src/android/app/src/main/res/values-b+ca+ES+valencia/strings.xml
@@ -193,6 +193,10 @@
Advertiment Regió No Vàlida
La configuració del país no és vàlida per a la regió emulada seleccionada.
La configuració del país no és vàlida per a la consola vinculada actual.
+ Emmagatzematge
+ Comprimir el contingut de CIAs instal·lats
+ Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.
+
Càmera interior
Càmera esquerra externa
@@ -397,6 +401,10 @@ S\'esperen errors gràfics temporals quan estigue activat.
Configurar Controls
Editar Estil
Fet
+ Lliscament de botons
+ Mantindre el botó pressionat originalment
+ Mantindre el botó pressionat actualment
+ Mantindre el botó original i actualment pressionat
Activar Controls
Ajustar Escala
Escala Global
@@ -409,6 +417,8 @@ S\'esperen errors gràfics temporals quan estigue activat.
Relació d\'Aspecte
Estil de Pantalla Apaïsada
Estil de Pantalla de Perfil
+ Estil de Pantalla Secundària
+ La disposició de la pantalla secundària connectada, amb cable o sense fil (Chromecast, Miracast)
Pantalla amplia
Vertical
Pantalla Única
@@ -416,6 +426,7 @@ S\'esperen errors gràfics temporals quan estigue activat.
Pantalles híbrides
Original
Per omissió
+ Per defecte del sistema (espill)
Estil Personalitzat
Posició de Pantalla Xicoteta
On hauria d\'aparéixer la pantalla xicoteta en relació amb la gran en Proporció de Pantalla Gran?
@@ -536,6 +547,10 @@ S\'esperen errors gràfics temporals quan estigue activat.
Crear drecera
El nom de la drecera no pot estar buit
Allargar per a ajustar la imatge
+ ID:
+ Fitxer:
+ Tipus:
+
Mostrar informació de rendiment
Informació de rendiment
diff --git a/src/android/app/src/main/res/values-b+es+ES/strings.xml b/src/android/app/src/main/res/values-b+es+ES/strings.xml
index d0c9496bf..f2232f9f9 100644
--- a/src/android/app/src/main/res/values-b+es+ES/strings.xml
+++ b/src/android/app/src/main/res/values-b+es+ES/strings.xml
@@ -194,7 +194,7 @@
La configuración del país no es válida para la región emulada seleccionada.
La configuración del país no es válida para la consola vinculada actual.
Almacenamiento
- Comprimir el contenido CIA instalado
+ Comprimir el contenido de CIAs instalados
Comprime el contenido de archivos CIA cuando son instalados a la SD emulada. Solo afecta contenido CIA instalado con esta opción activada.
@@ -402,9 +402,9 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.
Editar Estilo
Hecho
Deslizamiento de botones
- Mantenga el botón presionado originalmente
- Mantenga el botón presionado actualmente
- Mantenga el botón original y actualmente presionado
+ Mantener el botón presionado originalmente
+ Mantener el botón presionado actualmente
+ Mantener el botón original y actualmente presionado
Activar Controles
Ajustar Escala
Escala Global
@@ -417,6 +417,8 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.
Relación de Aspecto
Estilo de Pantalla Apaisada
Estilo de Pantalla de Perfil
+ Estilo de Pantalla Secundaria
+ El estilo de la pantalla secundaria conectada, con cable o inalámbrica (Chromecast, Miracast)
Pantalla amplia
Retrato
Pantalla Única
@@ -424,6 +426,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.
Pantallas híbridas
Original
Por defecto
+ Por defecto del sistema (espejo)
Estilo personalizado
Posición Pantalla Pequeña
¿Dónde debería aparecer la pantalla pequeña en relación con la grande en Disposicion de Pantalla Grande?
diff --git a/src/android/app/src/main/res/values-b+pl+PL/strings.xml b/src/android/app/src/main/res/values-b+pl+PL/strings.xml
index 29e2aabd2..17a1fbbca 100644
--- a/src/android/app/src/main/res/values-b+pl+PL/strings.xml
+++ b/src/android/app/src/main/res/values-b+pl+PL/strings.xml
@@ -417,6 +417,7 @@
Geometryczny Układ Ekranu
Pionowy Układ Ekranu
Układ ekranu wyświetlacza dodatkowego
+ Układ używany przez podłączony dodatkowy ekran, przewodowy lub bezprzewodowy (Chromecast, Miracast)
Duży Ekran
Ekran
Pojedynczy ekran
@@ -424,6 +425,7 @@
Hybrydowy Ekran
Oryginalny
Domyślny
+ Ustawienia domyślne systemu (mirror)
Niestandardowy Układ
Pozycja małego ekranu
Gdzie powinien być wyświetlany mały ekran względem dużego w układzie dużego ekranu?
diff --git a/src/citra_meta/CMakeLists.txt b/src/citra_meta/CMakeLists.txt
index 9f9dcd574..182423a2b 100644
--- a/src/citra_meta/CMakeLists.txt
+++ b/src/citra_meta/CMakeLists.txt
@@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
endif()
+if (ENABLE_QT AND APPLE)
+ target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
+endif()
+
if (ENABLE_QT AND USE_DISCORD_PRESENCE)
target_link_libraries(citra_meta PRIVATE discord-rpc)
endif()
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index a36d40a4d..4796fc922 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
multiplayer/state.h
multiplayer/validation.h
precompiled_headers.h
+ qt_image_interface.cpp
+ qt_image_interface.h
uisettings.cpp
uisettings.h
user_data_migration.cpp
user_data_migration.h
- qt_image_interface.cpp
- qt_image_interface.h
util/clickable_label.cpp
util/clickable_label.h
util/graphics_device_info.cpp
@@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
util/util.h
)
+if (APPLE)
+ target_sources(citra_qt PUBLIC
+ qt_swizzle.h
+ qt_swizzle.mm
+ )
+endif()
+
file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
@@ -275,6 +282,10 @@ if (NOT WIN32)
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
+if (APPLE)
+ target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
+endif()
+
if (UNIX AND NOT APPLE)
target_link_libraries(citra_qt PRIVATE Qt6::DBus gamemode)
endif()
diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp
index bc9b85661..523c3a3be 100644
--- a/src/citra_qt/citra_qt.cpp
+++ b/src/citra_qt/citra_qt.cpp
@@ -69,6 +69,7 @@
#include "citra_qt/movie/movie_record_dialog.h"
#include "citra_qt/multiplayer/state.h"
#include "citra_qt/qt_image_interface.h"
+#include "citra_qt/qt_swizzle.h"
#include "citra_qt/uisettings.h"
#include "common/play_time_manager.h"
#ifdef ENABLE_QT_UPDATE_CHECKER
@@ -4112,6 +4113,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
}
void LaunchQtFrontend(int argc, char* argv[]) {
+#ifdef __APPLE__
+ // Ensure that the linker doesn't optimize qt_swizzle.mm out of existence.
+ QtSwizzle::Dummy();
+#endif
+
Common::DetachedTasks detached_tasks;
#if MICROPROFILE_ENABLED
diff --git a/src/citra_qt/qt_swizzle.h b/src/citra_qt/qt_swizzle.h
new file mode 100644
index 000000000..25c396b79
--- /dev/null
+++ b/src/citra_qt/qt_swizzle.h
@@ -0,0 +1,9 @@
+// Copyright Citra Emulator Project / Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace QtSwizzle {
+
+void Dummy();
+
+} // namespace QtSwizzle
diff --git a/src/citra_qt/qt_swizzle.mm b/src/citra_qt/qt_swizzle.mm
new file mode 100644
index 000000000..46acdad8f
--- /dev/null
+++ b/src/citra_qt/qt_swizzle.mm
@@ -0,0 +1,48 @@
+// Copyright Citra Emulator Project / Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#import
+#import
+
+namespace QtSwizzle {
+
+void Dummy() {
+ // Call this anywhere to make sure that qt_swizzle.mm is linked.
+ // noop
+}
+
+} // namespace QtSwizzle
+
+@implementation QMetalLayer (AzaharPatch)
+
++ (void)load {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ Class targetClass = [self class];
+
+ // Get the original and swizzled methods
+ Method originalMethod =
+ class_getInstanceMethod(targetClass, @selector(setNeedsDisplayInRect:));
+ Method swizzledMethod =
+ class_getInstanceMethod(targetClass, @selector(swizzled_setNeedsDisplayInRect:));
+
+ // Swap the implementations
+ method_exchangeImplementations(originalMethod, swizzledMethod);
+ });
+}
+
+- (void)swizzled_setNeedsDisplayInRect:(CGRect)rect {
+ constexpr auto tooBig = 1e10; // Arbitrary large number
+
+ // Check for problematic huge rectangles
+ if ((!self.needsDisplay) && (rect.size.width > tooBig || rect.size.height > tooBig ||
+ rect.origin.x < -tooBig || rect.origin.y < -tooBig)) {
+ return;
+ }
+
+ // Call the original implementation
+ [self swizzled_setNeedsDisplayInRect:rect];
+}
+
+@end
\ No newline at end of file
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index c352dbdf0..ee81b177e 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -66,6 +66,10 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
}
#endif
+ if (!layout.bottom_screen_enabled) {
+ return false;
+ }
+
Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue();
if (render_3d_mode == Settings::StereoRenderOption::SideBySide ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7dd42cecc..1b8c97bae 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -864,8 +864,10 @@ bool CIAFile::Close() {
if (!complete) {
LOG_ERROR(Service_AM, "CIAFile closed prematurely, aborting install...");
if (!is_additional_content) {
- FileUtil::DeleteDirRecursively(
- GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()));
+ // Only delete the content folder as there may be user save data in the title folder.
+ const std::string title_content_path =
+ GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()) + "content/";
+ FileUtil::DeleteDirRecursively(title_content_path);
}
return true;
}
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 1ff478984..3a730ab5f 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -556,12 +556,15 @@ bool PipelineCache::EnsureDirectories() const {
};
return create_dir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
- create_dir(GetPipelineCacheDir());
+ create_dir(GetVulkanDir()) && create_dir(GetPipelineCacheDir());
+}
+
+std::string PipelineCache::GetVulkanDir() const {
+ return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP;
}
std::string PipelineCache::GetPipelineCacheDir() const {
- return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP + "pipeline" +
- DIR_SEP;
+ return GetVulkanDir() + "pipeline" + DIR_SEP;
}
void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 650df5680..c4f2ea062 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -108,6 +108,9 @@ private:
/// Create pipeline cache directories. Returns true on success.
bool EnsureDirectories() const;
+ /// Returns the Vulkan shader directory
+ std::string GetVulkanDir() const;
+
/// Returns the pipeline cache storage dir
std::string GetPipelineCacheDir() const;
diff --git a/tools/README.md b/tools/README.md
index c654936af..b4db3f866 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -7,7 +7,7 @@ The scripts in this directory assume that your current working directory is the
## Pre-release checklist
- [ ] Update compatibility list
-- [ ] Update translations
+- [ ] If this is a major release (2123.1 -> major.minor), update translations
### Note:
diff --git a/tools/purge-github-cache.sh b/tools/purge-github-cache.sh
new file mode 100755
index 000000000..01fdb2fed
--- /dev/null
+++ b/tools/purge-github-cache.sh
@@ -0,0 +1,6 @@
+#!/bin/bash -ex
+
+# This script assumes that the Github CLI is installed and that
+# the authenticated user has appropriate authorization.
+
+gh cache delete --all
\ No newline at end of file