diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 81b406770..0d02dd2f0 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-26' }}
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-26
needs: macos
env:
OS: macos
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a736eaac..9c5e5c14e 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()
@@ -112,6 +121,8 @@ option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
+option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
+
# Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
@@ -299,7 +310,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/CMakeLists.txt b/src/CMakeLists.txt
index 43ab20d46..69d17bac2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -174,6 +174,9 @@ endif()
if(ENABLE_VULKAN)
add_compile_definitions(ENABLE_VULKAN)
endif()
+if(ENABLE_DEVELOPER_OPTIONS)
+ add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
+endif()
add_subdirectory(common)
add_subdirectory(core)
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 84006116f..10e766459 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -64,7 +64,7 @@ android {
// The application ID refers to Lime3DS to allow for
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
applicationId = "io.github.lime3ds.android"
- minSdk = 28
+ minSdk = 29
targetSdk = 35
versionCode = autoVersion
versionName = getGitVersion()
@@ -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/BooleanSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt
index 925e17805..11f55d184 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/BooleanSetting.kt
@@ -19,13 +19,14 @@ enum class BooleanSetting(
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false),
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
- OVERLAY_SHOW_FPS("overlay_show_fps", Settings.SECTION_LAYOUT, true),
- OVERLAY_SHOW_FRAMETIME("overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
- OVERLAY_SHOW_SPEED("overlay_show_speed", Settings.SECTION_LAYOUT, false),
- OVERLAY_SHOW_APP_RAM_USAGE("overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
- OVERLAY_SHOW_AVAILABLE_RAM("overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
- OVERLAY_SHOW_BATTERY_TEMP("overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
- OVERLAY_BACKGROUND("overlay_background", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true),
+ PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_SHOW_SPEED("performance_overlay_show_speed", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_SHOW_APP_RAM_USAGE("performance_overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_SHOW_AVAILABLE_RAM("performance_overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_SHOW_BATTERY_TEMP("performance_overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
+ PERF_OVERLAY_BACKGROUND("performance_overlay_background", Settings.SECTION_LAYOUT, false),
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
@@ -49,7 +50,8 @@ enum class BooleanSetting(
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false),
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false),
UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false),
- COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false);
+ COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false),
+ ANDROID_HIDE_IMAGES("android_hide_images", Settings.SECTION_CORE, false);
override var boolean: Boolean = defaultValue
@@ -83,6 +85,8 @@ enum class BooleanSetting(
SHADERS_ACCURATE_MUL,
USE_ARTIC_BASE_CONTROLLER,
COMPRESS_INSTALLED_CIA_CONTENT,
+ ANDROID_HIDE_IMAGES,
+ PERF_OVERLAY_ENABLE // Works in overlay options, but not from the settings menu
)
fun from(key: String): BooleanSetting? =
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/FloatSetting.kt
index 94ddb295d..69bc16ca7 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/FloatSetting.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,6 +10,10 @@ enum class FloatSetting(
override val defaultValue: Float
) : AbstractFloatSetting {
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
+ SECOND_SCREEN_OPACITY("custom_second_layer_opacity", Settings.SECTION_RENDERER, 100f),
+ BACKGROUND_RED("bg_red", Settings.SECTION_RENDERER, 0f),
+ BACKGROUND_BLUE("bg_blue", Settings.SECTION_RENDERER, 0f),
+ BACKGROUND_GREEN("bg_green", Settings.SECTION_RENDERER, 0f),
EMPTY_SETTING("", "", 0.0f);
override var float: Float = defaultValue
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/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt
index 06fcb19e4..33aea46f9 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsActivityPresenter.kt
@@ -4,14 +4,19 @@
package org.citra.citra_emu.features.settings.ui
+import android.net.Uri
import android.os.Bundle
import android.text.TextUtils
+import androidx.documentfile.provider.DocumentFile
+import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
-import org.citra.citra_emu.features.settings.model.IntSetting
+import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.utils.SystemSaveGame
import org.citra.citra_emu.utils.DirectoryInitialization
+import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.Log
+import org.citra.citra_emu.utils.PermissionsHandler
import org.citra.citra_emu.utils.TurboHelper
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
@@ -60,6 +65,32 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
loadSettingsUI()
}
+ private fun updateAndroidImageVisibility() {
+ val dataDirTreeUri: Uri
+ val dataDirDocument: DocumentFile
+ val nomediaFileDocument: DocumentFile?
+ val nomediaFileExists: Boolean
+ try {
+ dataDirTreeUri = PermissionsHandler.citraDirectory
+ dataDirDocument = DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
+ nomediaFileDocument = dataDirDocument.findFile(".nomedia")
+ nomediaFileExists = (nomediaFileDocument != null)
+ } catch (e: Exception) {
+ Log.error("[SettingsActivity]: Error occurred while trying to find .nomedia, error: " + e.message)
+ return
+ }
+
+ if (BooleanSetting.ANDROID_HIDE_IMAGES.boolean) {
+ if (!nomediaFileExists) {
+ Log.info("[SettingsActivity]: Attempting to create .nomedia in user data directory")
+ FileUtil.createFile(dataDirTreeUri.toString(), ".nomedia")
+ }
+ } else if (nomediaFileExists) {
+ Log.info("[SettingsActivity]: Attempting to delete .nomedia in user data directory")
+ nomediaFileDocument!!.delete()
+ }
+ }
+
fun onStop(finishing: Boolean) {
if (finishing && shouldSave) {
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
@@ -67,6 +98,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
//added to ensure that layout changes take effect as soon as settings window closes
NativeLibrary.reloadSettings()
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
+ updateAndroidImageVisibility()
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
}
NativeLibrary.reloadSettings()
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 28fb82e84..d4baf6166 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -248,6 +248,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.TURBO_LIMIT.defaultValue.toFloat()
)
)
+ add(
+ SwitchSetting(
+ BooleanSetting.ANDROID_HIDE_IMAGES,
+ R.string.android_hide_images,
+ R.string.android_hide_images_description,
+ BooleanSetting.ANDROID_HIDE_IMAGES.key,
+ BooleanSetting.ANDROID_HIDE_IMAGES.defaultValue
+ )
+ )
}
}
@@ -1166,6 +1175,89 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
)
)
+ add(
+ SliderSetting(
+ FloatSetting.SECOND_SCREEN_OPACITY,
+ R.string.second_screen_opacity,
+ R.string.second_screen_opacity_description,
+ 0,
+ 100,
+ "%",
+ FloatSetting.SECOND_SCREEN_OPACITY.key,
+ FloatSetting.SECOND_SCREEN_OPACITY.defaultValue,
+ isEnabled = IntSetting.SCREEN_LAYOUT.int == 5
+ )
+ )
+ add(HeaderSetting(R.string.bg_color, R.string.bg_color_description))
+ val bgRedSetting = object : AbstractIntSetting {
+ override var int: Int
+ get() = (FloatSetting.BACKGROUND_RED.float * 255).toInt()
+ set(value) {
+ FloatSetting.BACKGROUND_RED.float = value.toFloat() / 255
+ settings.saveSetting(FloatSetting.BACKGROUND_RED, SettingsFile.FILE_NAME_CONFIG)
+ }
+ override val key = null
+ override val section = null
+ override val isRuntimeEditable = false
+ override val valueAsString = int.toString()
+ override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
+ }
+ add(
+ SliderSetting(
+ bgRedSetting,
+ R.string.bg_red,
+ 0,
+ 0,
+ 255,
+ ""
+ )
+ )
+ val bgGreenSetting = object : AbstractIntSetting {
+ override var int: Int
+ get() = (FloatSetting.BACKGROUND_GREEN.float * 255).toInt()
+ set(value) {
+ FloatSetting.BACKGROUND_GREEN.float = value.toFloat() / 255
+ settings.saveSetting(FloatSetting.BACKGROUND_GREEN, SettingsFile.FILE_NAME_CONFIG)
+ }
+ override val key = null
+ override val section = null
+ override val isRuntimeEditable = false
+ override val valueAsString = int.toString()
+ override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
+ }
+ add(
+ SliderSetting(
+ bgGreenSetting,
+ R.string.bg_green,
+ 0,
+ 0,
+ 255,
+ ""
+ )
+ )
+ val bgBlueSetting = object : AbstractIntSetting {
+ override var int: Int
+ get() = (FloatSetting.BACKGROUND_BLUE.float * 255).toInt()
+ set(value) {
+ FloatSetting.BACKGROUND_BLUE.float = value.toFloat() / 255
+ settings.saveSetting(FloatSetting.BACKGROUND_BLUE, SettingsFile.FILE_NAME_CONFIG)
+ }
+ override val key = null
+ override val section = null
+ override val isRuntimeEditable = false
+ override val valueAsString = int.toString()
+ override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
+ }
+ add(
+ SliderSetting(
+ bgBlueSetting,
+ R.string.bg_blue,
+ 0,
+ 0,
+ 255,
+ ""
+ )
+ )
add(
SubmenuSetting(
R.string.performance_overlay_options,
@@ -1201,38 +1293,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add(
SwitchSetting(
- object : AbstractBooleanSetting {
- override val key = "EmulationMenuSettings_showPerfPerformanceOverlay"
- override val section = Settings.SECTION_LAYOUT
- override val defaultValue = false
- override var boolean: Boolean
- get() = EmulationMenuSettings.showPerformanceOverlay
- set(value) { EmulationMenuSettings.showPerformanceOverlay = value }
- override val isRuntimeEditable = true
- override val valueAsString: String get() = boolean.toString()
- },
+ BooleanSetting.PERF_OVERLAY_ENABLE,
R.string.performance_overlay_enable,
0,
- "EmulationMenuSettings_showPerfPerformanceOverlay",
- false
+ BooleanSetting.PERF_OVERLAY_ENABLE.key,
+ BooleanSetting.PERF_OVERLAY_ENABLE.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_BACKGROUND,
- R.string.overlay_background,
- R.string.overlay_background_description,
- "overlay_background",
- false
+ BooleanSetting.PERF_OVERLAY_BACKGROUND,
+ R.string.performance_overlay_background,
+ R.string.performance_overlay_background_description,
+ BooleanSetting.PERF_OVERLAY_BACKGROUND.key,
+ BooleanSetting.PERF_OVERLAY_BACKGROUND.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.PERFORMANCE_OVERLAY_POSITION,
- R.string.overlay_position,
- R.string.overlay_position_description,
+ R.string.performance_overlay_position,
+ R.string.performance_overlay_position_description,
R.array.statsPosition,
R.array.statsPositionValues,
)
@@ -1243,61 +1326,61 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_FPS,
- R.string.overlay_show_fps,
- R.string.overlay_show_fps_description,
- "overlay_show_fps",
- true
+ BooleanSetting.PERF_OVERLAY_SHOW_FPS,
+ R.string.performance_overlay_show_fps,
+ R.string.performance_overlay_show_fps_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_FPS.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_FPS.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_FRAMETIME,
- R.string.overlay_show_frametime,
- R.string.overlay_show_frametime_description,
- "overlay_show_frame_time",
- true
+ BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME,
+ R.string.performance_overlay_show_frametime,
+ R.string.performance_overlay_show_frametime_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_SPEED,
- R.string.overlay_show_speed,
- R.string.overlay_show_speed_description,
- "overlay_show_speed",
- false
+ BooleanSetting.PERF_OVERLAY_SHOW_SPEED,
+ R.string.performance_overlay_show_speed,
+ R.string.performance_overlay_show_speed_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_SPEED.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_SPEED.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE,
- R.string.overlay_show_app_ram_usage,
- R.string.overlay_show_app_ram_usage_description,
- "overlay_show_app_ram_usage",
- false
+ BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE,
+ R.string.performance_overlay_show_app_ram_usage,
+ R.string.performance_overlay_show_app_ram_usage_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM,
- R.string.overlay_show_available_ram,
- R.string.overlay_show_available_ram_description,
- "overlay_show_available_ram",
- false
+ BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM,
+ R.string.performance_overlay_show_available_ram,
+ R.string.performance_overlay_show_available_ram_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.defaultValue
)
)
add(
SwitchSetting(
- BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP,
- R.string.overlay_show_battery_temp,
- R.string.overlay_show_battery_temp_description,
- "overlay_show_battery_temp",
- false
+ BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP,
+ R.string.performance_overlay_show_battery_temp,
+ R.string.performance_overlay_show_battery_temp_description,
+ BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.key,
+ BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.defaultValue
)
)
}
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..419919527 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
@@ -66,6 +66,7 @@ import org.citra.citra_emu.display.ScreenAdjustmentUtil
import org.citra.citra_emu.display.ScreenLayout
import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.IntSetting
+import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile
@@ -100,6 +101,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val settingsViewModel: SettingsViewModel by viewModels()
+ private val settings get() = settingsViewModel.settings
+
+ private val onPause = Runnable{ togglePause() }
+ private val onShutdown = Runnable{ emulationState.stop() }
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -155,9 +160,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
retainInstance = true
emulationState = EmulationState(game.path)
emulationActivity = requireActivity() as EmulationActivity
- screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings)
- EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
- EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
+ screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settings)
+ EmulationLifecycleUtil.addPauseResumeHook(onPause)
+ EmulationLifecycleUtil.addShutdownHook(onShutdown)
}
override fun onCreateView(
@@ -507,6 +512,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 ===
@@ -662,7 +673,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.menu.apply {
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_performance_overlay_show).isChecked =
- EmulationMenuSettings.showPerformanceOverlay
+ BooleanSetting.PERF_OVERLAY_ENABLE.boolean
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
EmulationMenuSettings.joystickRelCenter
@@ -679,7 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
R.id.menu_performance_overlay_show -> {
- EmulationMenuSettings.showPerformanceOverlay = !EmulationMenuSettings.showPerformanceOverlay
+ BooleanSetting.PERF_OVERLAY_ENABLE.boolean = !BooleanSetting.PERF_OVERLAY_ENABLE.boolean
+ settings.saveSetting(BooleanSetting.PERF_OVERLAY_ENABLE, SettingsFile.FILE_NAME_CONFIG)
updateShowPerformanceOverlay()
true
}
@@ -1202,7 +1214,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
- if (EmulationMenuSettings.showPerformanceOverlay) {
+ if (BooleanSetting.PERF_OVERLAY_ENABLE.boolean) {
val SYSTEM_FPS = 0
val FPS = 1
val SPEED = 2
@@ -1217,11 +1229,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val perfStats = NativeLibrary.getPerfStats()
val dividerString = "\u00A0\u2502 "
if (perfStats[FPS] > 0) {
- if (BooleanSetting.OVERLAY_SHOW_FPS.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_FPS.boolean) {
sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
}
- if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
@@ -1236,7 +1248,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
)
}
- if (BooleanSetting.OVERLAY_SHOW_SPEED.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_SPEED.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
sb.append(
String.format(
@@ -1246,14 +1258,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
)
}
- if (BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
val appRamUsage =
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
}
- if (BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
context?.let { ctx ->
val activityManager =
@@ -1266,14 +1278,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
}
- if (BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString)
val batteryTemp = getBatteryTemperature()
val tempF = celsiusToFahrenheit(batteryTemp)
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
}
- if (BooleanSetting.OVERLAY_BACKGROUND.boolean) {
+ if (BooleanSetting.PERF_OVERLAY_BACKGROUND.boolean) {
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
} else {
binding.performanceOverlayShowText.setBackgroundResource(0)
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/java/org/citra/citra_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.kt
index 184964549..6ff08fa37 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.kt
@@ -35,13 +35,6 @@ object EmulationMenuSettings {
.apply()
}
- var showPerformanceOverlay: Boolean
- get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
- set(value) {
- preferences.edit()
- .putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
- .apply()
- }
var hapticFeedback: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
set(value) {
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 1f2e8708b..9ac9f3737 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -172,6 +172,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue);
+ ReadSetting("Renderer", Settings::values.custom_second_layer_opacity);
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
index 46083be50..5bedfa319 100644
--- a/src/android/app/src/main/jni/default_ini.h
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -170,6 +170,9 @@ bg_red =
bg_blue =
bg_green =
+# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer.
+custom_second_layer_opacity =
+
# Whether and how Stereoscopic 3D should be rendered
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
render_3d =
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/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index e29871840..17bda5e66 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -148,12 +148,12 @@
- - @string/overlay_position_top_left
- - @string/overlay_position_center_top
- - @string/overlay_position_top_right
- - @string/overlay_position_bottom_left
- - @string/overlay_position_center_bottom
- - @string/overlay_position_bottom_right
+ - @string/performance_overlay_position_top_left
+ - @string/performance_overlay_position_center_top
+ - @string/performance_overlay_position_top_right
+ - @string/performance_overlay_position_bottom_left
+ - @string/performance_overlay_position_center_bottom
+ - @string/performance_overlay_position_bottom_right
- 0
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index c79be56c8..078113f12 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -238,25 +238,27 @@
Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL
Disable SPIR-V Optimizer
Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.
- Enable asynchronous shader compilation
+ Enable Asynchronous Shader Compilation
Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches
Linear Filtering
Enables linear filtering, which causes game visuals to appear smoother.
Texture Filter
Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.
- Delay game render thread
+ Delay Game Render Thread
Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.
Advanced
Texture Sampling
Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure, set this to Game Controlled.
Accurate Multiplication
Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.
- Enable asynchronous GPU emulation
+ Enable Asynchronous GPU Emulation
Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.
Limit Speed
When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.
Limit Speed Percent
Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.
+ Hide 3DS Images from Android
+ Prevent 3DS camera, screenshot, and custom texture images from being indexed by Android and displayed in the gallery. Your device may need to be rebooted after changing this setting to take effect.
Turbo Speed Limit
Emulation speed limit used while the turbo hotkey is active.
Expand to Cutout Area
@@ -306,7 +308,7 @@
Volume
Audio Stretching
Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.
- Enable realtime audio
+ Enable Realtime Audio
Scales audio playback speed to account for drops in emulation framerate. This means that audio will play at full speed even while the game framerate is low. May cause audio desync issues.
Audio Input Device
Sound Output Mode
@@ -321,9 +323,9 @@
Synchronizes the game frame rate to the refresh rate of your device.
Debug Renderer
Log additional graphics related debug information. When enabled, game performance will be significantly reduced.
- Flush log output on every message
+ Flush Log Output on Every Message
Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.
- Delay start with LLE modules
+ Delay Start With LLE Modules
Delays the start of the app when LLE modules are enabled.
Deterministic Async Operations
Makes async operations deterministic for debugging. Enabling this may cause freezes.
@@ -448,6 +450,13 @@
Default
System Default (mirror)
Custom Layout
+ Background Color
+ The color which appears behind the screens during emulation, represented as an RGB value.
+ Red
+ Green
+ Blue
+ Custom Layout Second Screen Opacity
+ The opacity of the second 3DS screen when using a custom screen layout. Useful if the second screen is to be positioned on top of the first screen.
Small Screen Position
Where should the small screen appear relative to the large one in Large Screen Layout?
Top Right
@@ -576,28 +585,28 @@
Performance Overlay
Enable Performance Overlay
Configure whether the performance overlay is shown and what information is displayed.
- Show FPS
- Display current frames per second.
- Show Frametime
- Display current frametime.
- Show Speed
- Display current emulation speed percentage.
- Show App Memory Usage
- Display the amount of RAM getting used by the emulator.
- Show Available Memory
- Display the amount of RAM which is available.
- Show Battery Temperature
- Display current Battery temperature in Celsius and Fahrenheit.
- Overlay Position
- Choose where the performance overlay is displayed on the screen.
- Top Left
- Top Right
- Bottom Left
- Bottom Right
- Center Top
- Center Bottom
- Overlay Background
- Adds a background behind the overlay for easier reading.
+ Show FPS
+ Display current frames per second.
+ Show Frametime
+ Display current frametime.
+ Show Speed
+ Display current emulation speed percentage.
+ Show App Memory Usage
+ Display the amount of RAM getting used by the emulator.
+ Show Available Memory
+ Display the amount of RAM which is available.
+ Show Battery Temperature
+ Display current Battery temperature in Celsius and Fahrenheit.
+ Overlay Position
+ Choose where the performance overlay is displayed on the screen.
+ Top Left
+ Top Right
+ Bottom Left
+ Bottom Right
+ Center Top
+ Center Bottom
+ Overlay Background
+ Adds a background behind the overlay for easier reading.
Cheats
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index 65b5a820c..73a10f567 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -9,7 +9,7 @@
namespace AudioCore {
-constexpr char auto_device_name[] = "auto";
+constexpr char auto_device_name[] = "Auto";
/**
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
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..7dc2b777b 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)
@@ -272,7 +279,12 @@ endif()
if (NOT WIN32)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
- target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
+ if(Qt6_VERSION VERSION_GREATER_EQUAL "6.10.0")
+ find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
+ target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
+ else()
+ target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
+ endif()
endif()
if (UNIX AND NOT APPLE)
diff --git a/src/citra_qt/citra_qt.cpp b/src/citra_qt/citra_qt.cpp
index bc9b85661..ea28f0f2c 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
@@ -979,6 +980,10 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
connect(game_list, &GameList::PopulatingCompleted, this,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
+#ifdef ENABLE_DEVELOPER_OPTIONS
+ connect(game_list, &GameList::StartingLaunchStressTest, this,
+ &GMainWindow::StartLaunchStressTest);
+#endif
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
@@ -1575,6 +1580,16 @@ void GMainWindow::ShutdownGame() {
secondary_window->ReleaseRenderTarget();
}
+void GMainWindow::StartLaunchStressTest(const QString& game_path) {
+ QThreadPool::globalInstance()->start([this, game_path] {
+ do {
+ ui->action_Stop->trigger();
+ emit game_list->GameChosen(game_path);
+ QThread::sleep(2);
+ } while (emulation_running);
+ });
+}
+
void GMainWindow::StoreRecentFile(const QString& filename) {
UISettings::values.recent_files.prepend(filename);
UISettings::values.recent_files.removeDuplicates();
@@ -4112,6 +4127,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/citra_qt.h b/src/citra_qt/citra_qt.h
index 70e0493f3..746689cb5 100644
--- a/src/citra_qt/citra_qt.h
+++ b/src/citra_qt/citra_qt.h
@@ -306,6 +306,7 @@ private slots:
#endif
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total);
+ void StartLaunchStressTest(const QString& game_path);
private:
Q_INVOKABLE void OnMoviePlaybackCompleted();
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 041085eec..0e7777911 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -371,7 +371,7 @@ void QtConfig::ReadControlValues() {
const auto append_profile = [this, num_touch_from_button_maps] {
Settings::InputProfile profile;
profile.name =
- ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString();
+ ReadSetting(QStringLiteral("name"), QStringLiteral("Default")).toString().toStdString();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
diff --git a/src/citra_qt/configuration/configure_audio.ui b/src/citra_qt/configuration/configure_audio.ui
index 5bf8e3b0b..2a34765be 100644
--- a/src/citra_qt/configuration/configure_audio.ui
+++ b/src/citra_qt/configuration/configure_audio.ui
@@ -35,7 +35,7 @@
-
- Emulation:
+ Emulation
diff --git a/src/citra_qt/configuration/configure_camera.ui b/src/citra_qt/configuration/configure_camera.ui
index 07977edd4..ce39fc9d1 100644
--- a/src/citra_qt/configuration/configure_camera.ui
+++ b/src/citra_qt/configuration/configure_camera.ui
@@ -28,7 +28,7 @@
Select the camera to configure
- Camera to configure:
+ Camera to Configure
@@ -59,7 +59,7 @@
Select the camera mode (single or double)
- Camera mode:
+ Camera mode
@@ -90,7 +90,7 @@
Select the position of camera to configure
- Camera position:
+ Camera position
@@ -130,7 +130,7 @@
Select where the image of the emulated camera comes from. It may be an image or a real camera.
- Camera Image Source:
+ Camera Image Source
@@ -166,7 +166,7 @@
QFrame::NoFrame
- File:
+ File
@@ -193,7 +193,7 @@
QFrame::NoFrame
- Camera:
+ Camera
@@ -234,7 +234,7 @@
QFrame::NoFrame
- Flip:
+ Flip
diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui
index b69676d02..f26943444 100644
--- a/src/citra_qt/configuration/configure_enhancements.ui
+++ b/src/citra_qt/configuration/configure_enhancements.ui
@@ -113,7 +113,7 @@
-
- Enable Linear Filtering
+ Enable linear filtering
@@ -320,7 +320,7 @@
-
- Disable Right Eye Rendering
+ Disable right eye rendering
<html><head/><body><p>Disable Right Eye Rendering</p><p>Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.</p></body></html>
@@ -342,7 +342,7 @@
<html><head/><body><p>Replace textures with PNG files.</p><p>Textures are loaded from load/textures/[Title ID]/.</p></body></html>
- Use Custom Textures
+ Use custom textures
@@ -352,7 +352,7 @@
<html><head/><body><p>Dump textures to PNG files.</p><p>Textures are dumped to dump/textures/[Title ID]/.</p></body></html>
- Dump Textures
+ Dump textures
@@ -362,7 +362,7 @@
<html><head/><body><p>Load all custom textures into memory on boot, instead of loading them when the application requires them.</p></body></html>
- Preload Custom Textures
+ Preload custom textures
@@ -372,7 +372,7 @@
<html><head/><body><p>Load custom textures asynchronously with background threads to reduce loading stutter</p></body></html>
- Async Custom Texture Loading
+ Async custom texture loading
diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui
index 741b898c3..a4fee730a 100644
--- a/src/citra_qt/configuration/configure_general.ui
+++ b/src/citra_qt/configuration/configure_general.ui
@@ -97,7 +97,7 @@
-
- Set emulation speed:
+ Set emulation speed
@@ -105,7 +105,7 @@
-
- Emulation Speed:
+ Emulation Speed
diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui
index fea3c523d..ec6a5392c 100644
--- a/src/citra_qt/configuration/configure_graphics.ui
+++ b/src/citra_qt/configuration/configure_graphics.ui
@@ -132,14 +132,14 @@
-
- SPIR-V Shader Generation
+ SPIR-V shader generation
-
- Disable GLSL -> SPIR-V Optimizer
+ Disable GLSL -> SPIR-V optimizer
<html><head/><body><p>Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</p></body></html>
@@ -176,7 +176,7 @@
<html><head/><body><p>Use the selected graphics API to accelerate shader emulation.</p><p>Requires a relatively powerful GPU for better performance.</p></body></html>
- Enable Hardware Shader
+ Enable hardware shader
@@ -201,7 +201,7 @@
<html><head/><body><p>Correctly handle all edge cases in multiplication operation in shaders. </p><p>Some applications requires this to be enabled for the hardware shader to render properly.</p><p>However this would reduce performance in most applications.</p></body></html>
- Accurate Multiplication
+ Accurate multiplication
@@ -217,7 +217,7 @@
<html><head/><body><p>Use the JIT engine instead of the interpreter for software shader emulation. </p><p>Enable this for better performance.</p></body></html>
- Enable Shader JIT
+ Enable shader JIT
@@ -227,7 +227,7 @@
<html><head/><body><p>Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches</p></body></html>
- Enable Async Shader Compilation
+ Enable async shader compilation
@@ -237,7 +237,7 @@
<html><head/><body><p>Perform presentation on separate threads. Improves performance when using Vulkan in most applications.</p></body></html>
- Enable Async Presentation
+ Enable async presentation
@@ -303,7 +303,7 @@
<html><head/><body><p>Reduce stuttering by storing and loading generated shaders to disk.</p></body></html>
- Use Disk Shader Cache
+ Use disk shader cache
@@ -349,7 +349,7 @@
-
- Delay application render thread:
+ Delay Application Render Thread
<html><head/><body><p>Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.</p></body></html>
diff --git a/src/citra_qt/configuration/configure_layout.ui b/src/citra_qt/configuration/configure_layout.ui
index 128e146e3..1125ab2fb 100644
--- a/src/citra_qt/configuration/configure_layout.ui
+++ b/src/citra_qt/configuration/configure_layout.ui
@@ -132,14 +132,14 @@
-
- Swap Screens
+ Swap screens
-
- Rotate Screens Upright
+ Rotate screens upright
@@ -531,7 +531,7 @@
-
- <html><head/><body><p>Bottom Screen Opacity % (OpenGL Only)</p></body></html>
+ <html><head/><body><p>Bottom Screen Opacity %</p></body></html>
diff --git a/src/citra_qt/configuration/configure_storage.ui b/src/citra_qt/configuration/configure_storage.ui
index 1c8358dbc..54827022c 100644
--- a/src/citra_qt/configuration/configure_storage.ui
+++ b/src/citra_qt/configuration/configure_storage.ui
@@ -27,7 +27,7 @@
-
- Use Virtual SD
+ Use virtual SD card
@@ -42,7 +42,7 @@
-
- Use Custom Storage
+ Use custom storage location
diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui
index 244fb3c5f..080ee139a 100644
--- a/src/citra_qt/configuration/configure_system.ui
+++ b/src/citra_qt/configuration/configure_system.ui
@@ -92,7 +92,7 @@ online features (if installed)
-
- Region:
+ Region
@@ -549,7 +549,7 @@ online features (if installed)
-
- 3GX Plugin Loader:
+ 3GX Plugin Loader
diff --git a/src/citra_qt/configuration/configure_ui.ui b/src/citra_qt/configuration/configure_ui.ui
index f522a910e..f59bc171f 100644
--- a/src/citra_qt/configuration/configure_ui.ui
+++ b/src/citra_qt/configuration/configure_ui.ui
@@ -37,7 +37,7 @@
-
- Interface language:
+ Interface Language
@@ -51,7 +51,7 @@
-
- Theme:
+ Theme
@@ -78,7 +78,7 @@
-
- Icon Size:
+ Icon Size
@@ -108,7 +108,7 @@
-
- Row 1 Text:
+ Row 1 Text
@@ -148,7 +148,7 @@
-
- Row 2 Text:
+ Row 2 Text
@@ -191,14 +191,14 @@
-
- Hide Titles without Icon
+ Hide titles without icon
-
- Single Line Mode
+ Single line mode
@@ -218,7 +218,7 @@
-
- Show Advanced Frame Time Info
+ Show advanced frame time info
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 049f7579c..601bb2349 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -644,6 +644,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
shortcut_menu->addAction(tr("Add to Applications Menu"));
#endif
+#ifdef ENABLE_DEVELOPER_OPTIONS
+ context_menu.addSeparator();
+ QAction* stress_test_launch = context_menu.addAction(tr("Stress Test: App Launch"));
+#endif
+
context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Properties"));
@@ -755,6 +760,10 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
connect(remove_play_time_data, &QAction::triggered,
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
+#ifdef ENABLE_DEVELOPER_OPTIONS
+ connect(stress_test_launch, &QAction::triggered,
+ [this, path]() { emit StartingLaunchStressTest(path); });
+#endif
connect(properties, &QAction::triggered, this,
[this, path]() { emit OpenPerGameGeneralRequested(path); });
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index 92556bc04..d563f74b1 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -107,6 +107,7 @@ signals:
void AddDirectory();
void ShowList(bool show);
void PopulatingCompleted();
+ void StartingLaunchStressTest(const QString& game_path);
private slots:
void OnItemExpanded(const QModelIndex& item);
diff --git a/src/citra_qt/multiplayer/direct_connect.cpp b/src/citra_qt/multiplayer/direct_connect.cpp
index c77aac36f..f1e29b145 100644
--- a/src/citra_qt/multiplayer/direct_connect.cpp
+++ b/src/citra_qt/multiplayer/direct_connect.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -90,7 +90,8 @@ void DirectConnectWindow::Connect() {
room_member->Join(ui->nickname->text().toStdString(),
Service::CFG::GetConsoleIdHash(system),
ui->ip->text().toStdString().c_str(), port, 0,
- Network::NoPreferredMac, ui->password->text().toStdString().c_str());
+ Service::CFG::GetConsoleMacAddress(system),
+ ui->password->text().toStdString().c_str());
}
});
watcher->setFuture(f);
diff --git a/src/citra_qt/multiplayer/host_room.cpp b/src/citra_qt/multiplayer/host_room.cpp
index 5f29eed2d..53de0071c 100644
--- a/src/citra_qt/multiplayer/host_room.cpp
+++ b/src/citra_qt/multiplayer/host_room.cpp
@@ -193,8 +193,8 @@ void HostRoomWindow::Host() {
}
#endif
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
- "127.0.0.1", static_cast(port), 0, Network::NoPreferredMac, password,
- token);
+ "127.0.0.1", static_cast(port), 0,
+ Service::CFG::GetConsoleMacAddress(system), password, token);
// Store settings
UISettings::values.room_nickname = ui->username->text();
diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp
index 7a7f032c1..c18c8eefd 100644
--- a/src/citra_qt/multiplayer/lobby.cpp
+++ b/src/citra_qt/multiplayer/lobby.cpp
@@ -175,7 +175,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
#endif
if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(),
- static_cast(port), 0, Network::NoPreferredMac, password, token);
+ static_cast(port), 0, Service::CFG::GetConsoleMacAddress(system),
+ password, token);
}
});
watcher->setFuture(f);
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/common/hacks/hack_list.cpp b/src/common/hacks/hack_list.cpp
index c5702df5e..ce93ad2ef 100644
--- a/src/common/hacks/hack_list.cpp
+++ b/src/common/hacks/hack_list.cpp
@@ -67,6 +67,9 @@ HackManager hack_manager = {
0x0004013000002C02, // Normal
0x0004013000002C03, // Safe mode
0x0004013020002C03, // New 3DS safe mode
+
+ // DLP
+ 0x0004013000002802,
},
}},
diff --git a/src/common/settings.h b/src/common/settings.h
index 0bb947269..77061db16 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -585,9 +585,9 @@ struct Values {
SwitchableSetting enable_realtime_audio{false, "enable_realtime_audio"};
SwitchableSetting volume{1.f, 0.f, 1.f, "volume"};
Setting output_type{AudioCore::SinkType::Auto, "output_type"};
- Setting output_device{"auto", "output_device"};
+ Setting output_device{"Auto", "output_device"};
Setting input_type{AudioCore::InputType::Auto, "input_type"};
- Setting input_device{"auto", "input_device"};
+ Setting input_device{"Auto", "input_device"};
// Camera
std::array camera_name;
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..9b0d5a709 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;
}
@@ -3253,9 +3255,15 @@ void Module::Interface::BeginImportProgramTemporarily(Kernel::HLERequestContext&
// Create our CIAFile handle for the app to write to, and while the app writes Citra will store
// contents out to sdmc/nand
const FileSys::Path cia_path = {};
- auto file = std::make_shared(
- am->system.Kernel(), std::make_unique(am->system, FS::MediaType::NAND), cia_path);
+ std::shared_ptr file;
+ {
+ auto cia_file = std::make_unique(am->system, FS::MediaType::NAND);
+ AuthorizeCIAFileDecryption(cia_file.get(), ctx);
+
+ file =
+ std::make_shared(am->system.Kernel(), std::move(cia_file), cia_path);
+ }
am->cia_installing = true;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
@@ -3446,7 +3454,7 @@ void Module::Interface::GetProgramInfoFromCia(Kernel::HLERequestContext& ctx) {
title_info.version = tmd.GetTitleVersion();
title_info.type = tmd.GetTitleType();
- IPC::RequestBuilder rb = rp.MakeBuilder(8, 0);
+ IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
rb.Push(ResultSuccess);
rb.PushRaw(title_info);
}
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 4d04af017..070612250 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -1264,6 +1264,10 @@ std::string GetConsoleIdHash(Core::System& system) {
return fmt::format("{:02x}", fmt::join(hash.begin(), hash.end(), ""));
}
+std::array GetConsoleMacAddress(Core::System& system) {
+ return MacToArray(GetModule(system)->GetMacAddress());
+}
+
std::array MacToArray(const std::string& mac) {
std::array ret;
int last = -1;
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index 7536680c0..8b02d6f8e 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -672,6 +672,8 @@ u64 MacToU64(const std::string& mac);
std::string GenerateRandomMAC();
+std::array GetConsoleMacAddress(Core::System& system);
+
} // namespace Service::CFG
SERVICE_CONSTRUCT(Service::CFG::Module)
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 3a08ffbee..1672feee3 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -106,15 +106,19 @@ void NWM_UDS::BroadcastNodeMap() {
packet.channel = network_channel;
packet.type = Network::WifiPacket::PacketType::NodeMap;
packet.destination_address = Network::BroadcastMac;
- std::size_t num_entries = std::count_if(node_map.begin(), node_map.end(),
- [](const auto& node) { return node.second.connected; });
+ auto node_can_broad = [](auto& node) -> bool {
+ return node.second.connected && !node.second.spec;
+ };
+ std::size_t num_entries =
+ std::count_if(node_map.begin(), node_map.end(),
+ [&node_can_broad](const auto& node) { return node_can_broad(node); });
using node_t = decltype(node_map)::value_type;
packet.data.resize(sizeof(num_entries) +
(sizeof(node_t::first) + sizeof(node_t::second.node_id)) * num_entries);
std::memcpy(packet.data.data(), &num_entries, sizeof(num_entries));
std::size_t offset = sizeof(num_entries);
for (const auto& node : node_map) {
- if (node.second.connected) {
+ if (node_can_broad(node)) {
std::memcpy(packet.data.data() + offset, node.first.data(), sizeof(node.first));
std::memcpy(packet.data.data() + offset + sizeof(node.first), &node.second.node_id,
sizeof(node.second.node_id));
@@ -185,7 +189,8 @@ void NWM_UDS::HandleAssociationResponseFrame(const Network::WifiPacket& packet)
using Network::WifiPacket;
WifiPacket eapol_start;
eapol_start.channel = network_channel;
- eapol_start.data = GenerateEAPoLStartFrame(std::get(assoc_result), current_node);
+ eapol_start.data =
+ GenerateEAPoLStartFrame(std::get(assoc_result), conn_type, current_node);
// TODO(B3N30): Encrypt the packet.
eapol_start.destination_address = packet.transmitter_address;
eapol_start.type = WifiPacket::PacketType::Data;
@@ -217,24 +222,36 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
ASSERT(connection_status.max_nodes != connection_status.total_nodes);
- auto node = DeserializeNodeInfoFromFrame(packet.data);
+ auto eapol_start = DeserializeEAPolStartPacket(packet.data);
- // Get an unused network node id
- u16 node_id = GetNextAvailableNodeId();
- node.network_node_id = node_id;
+ auto node = DeserializeNodeInfo(eapol_start.node);
- connection_status.node_bitmask |= 1 << (node_id - 1);
- connection_status.changed_nodes |= 1 << (node_id - 1);
- connection_status.nodes[node_id - 1] = node.network_node_id;
- connection_status.total_nodes++;
+ if (eapol_start.conn_type == ConnectionType::Client) {
+ // Get an unused network node id
+ u16 node_id = GetNextAvailableNodeId();
+ node.network_node_id = node_id;
- node_info[node_id - 1] = node;
- network_info.total_nodes++;
+ connection_status.node_bitmask |= 1 << (node_id - 1);
+ connection_status.changed_nodes |= 1 << (node_id - 1);
+ connection_status.nodes[node_id - 1] = node.network_node_id;
+ connection_status.total_nodes++;
- node_map[packet.transmitter_address].node_id = node.network_node_id;
- node_map[packet.transmitter_address].connected = true;
+ node_info[node_id - 1] = node;
+ network_info.total_nodes++;
- BroadcastNodeMap();
+ node_map[packet.transmitter_address].node_id = node.network_node_id;
+ node_map[packet.transmitter_address].connected = true;
+ node_map[packet.transmitter_address].spec = false;
+
+ BroadcastNodeMap();
+ } else if (eapol_start.conn_type == ConnectionType::Spectator) {
+ node_map[packet.transmitter_address].node_id = NodeIDSpec;
+ node_map[packet.transmitter_address].connected = true;
+ node_map[packet.transmitter_address].spec = true;
+ } else {
+ LOG_ERROR(Service_NWM, "Client tried connecting with unknown connection type: 0x{:x}",
+ static_cast(eapol_start.conn_type));
+ }
// Send the EAPoL-Logoff packet.
using Network::WifiPacket;
@@ -282,15 +299,23 @@ void NWM_UDS::HandleEAPoLPacket(const Network::WifiPacket& packet) {
node_info[index - 1] = DeserializeNodeInfo(node);
}
+ if (conn_type == ConnectionType::Client) {
+ connection_status.status = NetworkStatus::ConnectedAsClient;
+ } else if (conn_type == ConnectionType::Spectator) {
+ connection_status.status = NetworkStatus::ConnectedAsSpectator;
+ } else {
+ LOG_ERROR(Service_NWM, "Unknown connection type: 0x{:x}", static_cast(conn_type));
+ }
+
// We're now connected, signal the application
- connection_status.status = NetworkStatus::ConnectedAsClient;
connection_status.status_change_reason = NetworkStatusChangeReason::ConnectionEstablished;
// Some games require ConnectToNetwork to block, for now it doesn't
// If blocking is implemented this lock needs to be changed,
// otherwise it might cause deadlocks
connection_status_event->Signal();
connection_event->Signal();
- } else if (connection_status.status == NetworkStatus::ConnectedAsClient) {
+ } else if (connection_status.status == NetworkStatus::ConnectedAsClient ||
+ connection_status.status == NetworkStatus::ConnectedAsSpectator) {
// TODO(B3N30): Remove that section and send/receive a proper connection_status packet
// On a 3ds this packet wouldn't be addressed to already connected clients
// We use this information because in the current implementation the host
@@ -328,9 +353,9 @@ void NWM_UDS::HandleSecureDataPacket(const Network::WifiPacket& packet) {
std::scoped_lock lock{connection_status_mutex, system.Kernel().GetHLELock()};
if (connection_status.status != NetworkStatus::ConnectedAsHost &&
- connection_status.status != NetworkStatus::ConnectedAsClient) {
- // TODO(B3N30): Handle spectators
- LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket, because connection status is {}",
+ connection_status.status != NetworkStatus::ConnectedAsClient &&
+ connection_status.status != NetworkStatus::ConnectedAsSpectator) {
+ LOG_DEBUG(Service_NWM, "Ignored SecureDataPacket because connection status is {}",
static_cast(connection_status.status));
return;
}
@@ -370,12 +395,14 @@ void NWM_UDS::HandleSecureDataPacket(const Network::WifiPacket& packet) {
// TODO(B3N30): Allow more than one bind node per channel.
auto channel_info = channel_data.find(secure_data.data_channel);
// Ignore packets from channels we're not interested in.
- if (channel_info == channel_data.end())
+ if (channel_info == channel_data.end()) {
return;
+ }
if (channel_info->second.network_node_id != BroadcastNetworkNodeId &&
- channel_info->second.network_node_id != secure_data.src_node_id)
+ channel_info->second.network_node_id != secure_data.src_node_id) {
return;
+ }
// Add the received packet to the data queue.
channel_info->second.received_packets.emplace_back(packet.data);
@@ -432,7 +459,9 @@ void NWM_UDS::HandleAuthenticationFrame(const Network::WifiPacket& packet) {
// Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
using Network::WifiPacket;
- WifiPacket auth_request;
+ AuthenticationFrame auth_request;
+ memcpy(&auth_request, packet.data.data(), sizeof(auth_request));
+ WifiPacket auth_response;
{
std::scoped_lock lock(connection_status_mutex);
if (connection_status.status != NetworkStatus::ConnectedAsHost) {
@@ -454,13 +483,13 @@ void NWM_UDS::HandleAuthenticationFrame(const Network::WifiPacket& packet) {
return;
}
// Respond with an authentication response frame with SEQ2
- auth_request.channel = network_channel;
- auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
- auth_request.destination_address = packet.transmitter_address;
- auth_request.type = WifiPacket::PacketType::Authentication;
+ auth_response.channel = network_channel;
+ auth_response.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
+ auth_response.destination_address = packet.transmitter_address;
+ auth_response.type = WifiPacket::PacketType::Authentication;
node_map[packet.transmitter_address].connected = false;
}
- SendPacket(auth_request);
+ SendPacket(auth_response);
SendAssociationResponseFrame(packet.transmitter_address);
}
@@ -495,16 +524,16 @@ void NWM_UDS::HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
return;
}
- connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
- connection_status.changed_nodes |= 1 << (node.node_id - 1);
- connection_status.total_nodes--;
- connection_status.nodes[node.node_id - 1] = 0;
-
- network_info.total_nodes--;
- // TODO(B3N30): broadcast new connection_status to clients
+ if (!node.spec) {
+ connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
+ connection_status.changed_nodes |= 1 << (node.node_id - 1);
+ connection_status.total_nodes--;
+ connection_status.nodes[node.node_id - 1] = 0;
+ network_info.total_nodes--;
+ // TODO(B3N30): broadcast new connection_status to clients
+ }
node_it->Reset();
-
connection_status_event->Signal();
}
@@ -588,14 +617,19 @@ void NWM_UDS::RecvBeaconBroadcastData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
u32 out_buffer_size = rp.Pop();
+
+ // scan input struct
u32 unk1 = rp.Pop();
u32 unk2 = rp.Pop();
MacAddress mac_address;
rp.PopRaw(mac_address);
+ // uninitialized data in scan input struct
rp.Skip(9, false);
+ // end scan input struct
+
u32 wlan_comm_id = rp.Pop();
u32 id = rp.Pop();
// From 3dbrew:
@@ -1042,6 +1076,7 @@ void NWM_UDS::DestroyNetwork(Kernel::HLERequestContext& ctx) {
}
void NWM_UDS::DisconnectNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NWM, "disconnecting from network");
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@@ -1277,6 +1312,7 @@ void NWM_UDS::ConnectToNetwork(Kernel::HLERequestContext& ctx, u16 command_id,
std::vector passphrase) {
network_info = {};
std::memcpy(&network_info, network_info_buffer.data(), network_info_buffer.size());
+ conn_type = static_cast(connection_type);
// Start the connection sequence
StartConnectionSequence(network_info.host_mac_address);
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index cf59e7bcd..045d4ba96 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -1,4 +1,4 @@
-// Copyright 2014 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,6 +18,7 @@
#include
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hle/service/nwm/uds_common.h"
#include "core/hle/service/service.h"
#include "network/network.h"
@@ -47,6 +48,8 @@ const u16 DefaultBeaconInterval = 100;
/// The maximum number of nodes that can exist in an UDS session.
constexpr u32 UDSMaxNodes = 16;
+constexpr u16 NodeIDSpec = 0;
+
struct NodeInfo {
u64_le friend_code_seed;
std::array username;
@@ -95,7 +98,7 @@ static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect
struct NetworkInfo {
std::array host_mac_address;
u8 channel;
- INSERT_PADDING_BYTES(1);
+ u8 unk1;
u8 initialized;
INSERT_PADDING_BYTES(3);
std::array oui_value;
@@ -477,7 +480,7 @@ private:
void HandleSecureDataPacket(const Network::WifiPacket& packet);
/*
- * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
+ * Start a connection sequence with a UDS server. The sequence starts by sending an 802.11
* authentication frame with SEQ1.
*/
void StartConnectionSequence(const MacAddress& server);
@@ -526,6 +529,9 @@ private:
// Node information about our own system.
NodeInfo current_node;
+ // whether you are connecting as a client or a spec
+ ConnectionType conn_type;
+
struct BindNodeData {
u32 bind_node_id; ///< Id of the bind node associated with this data.
u8 channel; ///< Channel that this bind node was bound to.
@@ -548,6 +554,7 @@ private:
// Mapping of mac addresses to their respective node_ids.
struct Node {
bool connected;
+ bool spec;
u16 node_id;
private:
diff --git a/src/core/hle/service/nwm/uds_common.h b/src/core/hle/service/nwm/uds_common.h
new file mode 100644
index 000000000..fb2e118fa
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_common.h
@@ -0,0 +1,16 @@
+// Copyright Citra Emulator Project / Azahar Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::NWM {
+
+enum class ConnectionType : u8 {
+ Client = 0x1,
+ Spectator = 0x2,
+};
+
+}; // namespace Service::NWM
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
index abf2b36f9..305aec843 100644
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -286,9 +286,11 @@ SecureDataHeader ParseSecureDataHeader(std::span data) {
return header;
}
-std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
+std::vector GenerateEAPoLStartFrame(u16 association_id, ConnectionType conn_type,
+ const NodeInfo& node_info) {
EAPoLStartPacket eapol_start{};
eapol_start.association_id = association_id;
+ eapol_start.conn_type = conn_type;
eapol_start.node.friend_code_seed = node_info.friend_code_seed;
std::copy(node_info.username.begin(), node_info.username.end(),
@@ -327,13 +329,7 @@ NodeInfo DeserializeNodeInfoFromFrame(std::span frame) {
// Skip the LLC header
std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
- NodeInfo node{};
- node.friend_code_seed = eapol_start.node.friend_code_seed;
-
- std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(),
- node.username.begin());
-
- return node;
+ return DeserializeNodeInfo(eapol_start.node);
}
NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) {
@@ -380,4 +376,11 @@ EAPoLLogoffPacket ParseEAPoLLogoffFrame(std::span frame) {
return eapol_logoff;
}
+EAPoLStartPacket DeserializeEAPolStartPacket(std::span frame) {
+ EAPoLStartPacket eapol_start;
+
+ std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start));
+ return eapol_start;
+}
+
} // namespace Service::NWM
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
index 291cca0f5..faaf53448 100644
--- a/src/core/hle/service/nwm/uds_data.h
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -1,4 +1,4 @@
-// Copyright 2017 Citra Emulator Project
+// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nwm/uds_beacon.h"
+#include "core/hle/service/nwm/uds_common.h"
#include "core/hle/service/service.h"
namespace Service::NWM {
@@ -90,9 +91,8 @@ constexpr u16 EAPoLStartMagic = 0x201;
struct EAPoLStartPacket {
u16_be magic = EAPoLStartMagic;
u16_be association_id;
- // This value is hardcoded to 1 in the NWM module.
- u16_be unknown = 1;
- INSERT_PADDING_BYTES(2);
+ enum_le conn_type;
+ INSERT_PADDING_BYTES(3);
EAPoLNodeInfo node;
};
@@ -132,7 +132,8 @@ SecureDataHeader ParseSecureDataHeader(std::span data);
* communication.
* @returns The generated frame body.
*/
-std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
+std::vector GenerateEAPoLStartFrame(u16 association_id, ConnectionType conn_type,
+ const NodeInfo& node_info);
/*
* Returns the EtherType of the specified 802.11 frame.
@@ -151,6 +152,8 @@ u16 GetEAPoLFrameType(std::span frame);
*/
NodeInfo DeserializeNodeInfoFromFrame(std::span frame);
+EAPoLStartPacket DeserializeEAPolStartPacket(std::span frame);
+
/*
* Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo.
*/
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b574b574a..148d60185 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -78,7 +78,7 @@ const std::array service_module_map{
false},
{"CECD", 0x00040130'00002602, CECD::InstallInterfaces, false},
{"CFG", 0x00040130'00001702, CFG::InstallInterfaces, false},
- {"DLP", 0x00040130'00002802, DLP::InstallInterfaces, false},
+ {"DLP", 0x00040130'00002802, DLP::InstallInterfaces, true},
{"DSP", 0x00040130'00001A02, DSP::InstallInterfaces, false},
{"FRD", 0x00040130'00003202, FRD::InstallInterfaces, true},
{"GSP", 0x00040130'00001C02, GSP::InstallInterfaces, false},
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 61aa18928..514de372c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -710,10 +710,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
}
void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
- // TODO: Allow for second layer opacity in portrait mode android
-
- if (!isPortrait &&
- (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
+ if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
state.blend.src_a_func = GL_CONSTANT_ALPHA;
@@ -724,8 +721,7 @@ void RendererOpenGL::ApplySecondLayerOpacity(bool isPortrait) {
}
void RendererOpenGL::ResetSecondLayerOpacity(bool isPortrait) {
- if (!isPortrait &&
- (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout) &&
+ if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.src_rgb_func = GL_ONE;
state.blend.dst_rgb_func = GL_ZERO;
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ee8b1910a..d4a63dd9b 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -376,7 +376,13 @@ void RendererVulkan::BuildPipelines() {
};
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
- .blendEnable = false,
+ .blendEnable = true,
+ .srcColorBlendFactor = vk::BlendFactor::eConstantAlpha,
+ .dstColorBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
+ .colorBlendOp = vk::BlendOp::eAdd,
+ .srcAlphaBlendFactor = vk::BlendFactor::eConstantAlpha,
+ .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusConstantAlpha,
+ .alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
@@ -385,7 +391,6 @@ void RendererVulkan::BuildPipelines() {
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorblend_attachment,
- .blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
};
const vk::Viewport placeholder_viewport = vk::Viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
@@ -398,6 +403,7 @@ void RendererVulkan::BuildPipelines() {
};
const std::array dynamic_states = {
+ vk::DynamicState::eBlendConstants,
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
@@ -729,6 +735,13 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl
});
}
+void RendererVulkan::ApplySecondLayerOpacity(float alpha) {
+ scheduler.Record([alpha](vk::CommandBuffer cmdbuf) {
+ const std::array blend_constants = {0.0f, 0.0f, 0.0f, alpha};
+ cmdbuf.setBlendConstants(blend_constants.data());
+ });
+}
+
void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& top_screen) {
if (!layout.top_screen_enabled) {
@@ -867,13 +880,30 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout&
draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height);
draw_info.layer = 0;
+
+ // Apply the initial default opacity value; Needed to avoid flickering
+ ApplySecondLayerOpacity(1.0f);
+
+ bool use_custom_opacity =
+ Settings::values.layout_option.GetValue() == Settings::LayoutOption::CustomLayout &&
+ Settings::values.custom_second_layer_opacity.GetValue() < 100;
+ float second_alpha = use_custom_opacity
+ ? Settings::values.custom_second_layer_opacity.GetValue() / 100.0f
+ : 1.0f;
+
if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, top_screen);
draw_info.layer = 0;
+ if (use_custom_opacity) {
+ ApplySecondLayerOpacity(second_alpha);
+ }
DrawBottomScreen(layout, bottom_screen);
} else {
DrawBottomScreen(layout, bottom_screen);
draw_info.layer = 0;
+ if (use_custom_opacity) {
+ ApplySecondLayerOpacity(second_alpha);
+ }
DrawTopScreen(layout, top_screen);
}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 8486915e4..15807a4f3 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -98,12 +98,16 @@ private:
void DrawScreens(Frame* frame, const Layout::FramebufferLayout& layout, bool flipped);
void DrawBottomScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& bottom_screen);
+
void DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle& top_screen);
void DrawSingleScreen(u32 screen_id, float x, float y, float w, float h,
Layout::DisplayOrientation orientation);
void DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, float x, float y, float w,
float h, Layout::DisplayOrientation orientation);
+
+ void ApplySecondLayerOpacity(float alpha);
+
void LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, ScreenInfo& screen_info,
bool right_eye);
void FillScreen(Common::Vec3 color, const TextureInfo& texture);
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