diff options
37 files changed, 393 insertions, 191 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bef9d6ed..166024844 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul | |||
| 11 | include(DownloadExternals) | 11 | include(DownloadExternals) |
| 12 | include(CMakeDependentOption) | 12 | include(CMakeDependentOption) |
| 13 | include(CTest) | 13 | include(CTest) |
| 14 | include(FetchContent) | ||
| 15 | 14 | ||
| 16 | # Set bundled sdl2/qt as dependent options. | 15 | # Set bundled sdl2/qt as dependent options. |
| 17 | # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON | 16 | # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON |
| @@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL) | |||
| 99 | DESTINATION "${vvl_lib_path}") | 98 | DESTINATION "${vvl_lib_path}") |
| 100 | endif() | 99 | endif() |
| 101 | 100 | ||
| 102 | # On Android, fetch and compile libcxx before doing anything else | ||
| 103 | if (ANDROID) | 101 | if (ANDROID) |
| 104 | set(CMAKE_SKIP_INSTALL_RULES ON) | 102 | set(CMAKE_SKIP_INSTALL_RULES ON) |
| 105 | set(LLVM_VERSION "15.0.6") | ||
| 106 | |||
| 107 | # Note: even though libcxx and libcxxabi have separate releases on the project page, | ||
| 108 | # the separated releases cannot be compiled. Only in-tree builds work. Therefore we | ||
| 109 | # must fetch the source release for the entire llvm tree. | ||
| 110 | FetchContent_Declare(llvm | ||
| 111 | URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz" | ||
| 112 | URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92 | ||
| 113 | TLS_VERIFY TRUE | ||
| 114 | ) | ||
| 115 | FetchContent_MakeAvailable(llvm) | ||
| 116 | |||
| 117 | # libcxx has support for most of the range library, but it's gated behind a flag: | ||
| 118 | add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL) | ||
| 119 | |||
| 120 | # Disable standard header inclusion | ||
| 121 | set(ANDROID_STL "none") | ||
| 122 | |||
| 123 | # libcxxabi | ||
| 124 | set(LIBCXXABI_INCLUDE_TESTS OFF) | ||
| 125 | set(LIBCXXABI_ENABLE_SHARED FALSE) | ||
| 126 | set(LIBCXXABI_ENABLE_STATIC TRUE) | ||
| 127 | set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE) | ||
| 128 | add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi") | ||
| 129 | link_libraries(cxxabi_static) | ||
| 130 | |||
| 131 | # libcxx | ||
| 132 | set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE) | ||
| 133 | set(LIBCXX_CXX_ABI "libcxxabi") | ||
| 134 | set(LIBCXX_INCLUDE_TESTS OFF) | ||
| 135 | set(LIBCXX_INCLUDE_BENCHMARKS OFF) | ||
| 136 | set(LIBCXX_INCLUDE_DOCS OFF) | ||
| 137 | set(LIBCXX_ENABLE_SHARED FALSE) | ||
| 138 | set(LIBCXX_ENABLE_STATIC TRUE) | ||
| 139 | set(LIBCXX_ENABLE_ASSERTIONS FALSE) | ||
| 140 | add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx") | ||
| 141 | set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}") | ||
| 142 | link_libraries(cxx_static cxx-headers) | ||
| 143 | endif() | 103 | endif() |
| 144 | 104 | ||
| 145 | if (YUZU_USE_BUNDLED_VCPKG) | 105 | if (YUZU_USE_BUNDLED_VCPKG) |
| @@ -329,7 +289,7 @@ find_package(Boost 1.79.0 REQUIRED context) | |||
| 329 | find_package(enet 1.3 MODULE) | 289 | find_package(enet 1.3 MODULE) |
| 330 | find_package(fmt 9 REQUIRED) | 290 | find_package(fmt 9 REQUIRED) |
| 331 | find_package(inih 52 MODULE COMPONENTS INIReader) | 291 | find_package(inih 52 MODULE COMPONENTS INIReader) |
| 332 | find_package(LLVM 17 MODULE COMPONENTS Demangle) | 292 | find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) |
| 333 | find_package(lz4 REQUIRED) | 293 | find_package(lz4 REQUIRED) |
| 334 | find_package(nlohmann_json 3.8 REQUIRED) | 294 | find_package(nlohmann_json 3.8 REQUIRED) |
| 335 | find_package(Opus 1.3 MODULE) | 295 | find_package(Opus 1.3 MODULE) |
| @@ -400,6 +360,9 @@ function(set_yuzu_qt_components) | |||
| 400 | if (ENABLE_QT_TRANSLATION) | 360 | if (ENABLE_QT_TRANSLATION) |
| 401 | list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) | 361 | list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) |
| 402 | endif() | 362 | endif() |
| 363 | if (USE_DISCORD_PRESENCE) | ||
| 364 | list(APPEND YUZU_QT_COMPONENTS2 Network) | ||
| 365 | endif() | ||
| 403 | set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) | 366 | set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) |
| 404 | endfunction(set_yuzu_qt_components) | 367 | endfunction(set_yuzu_qt_components) |
| 405 | 368 | ||
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index 84a3308b7..ac43d84b7 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts | |||
| @@ -27,7 +27,7 @@ android { | |||
| 27 | namespace = "org.yuzu.yuzu_emu" | 27 | namespace = "org.yuzu.yuzu_emu" |
| 28 | 28 | ||
| 29 | compileSdkVersion = "android-34" | 29 | compileSdkVersion = "android-34" |
| 30 | ndkVersion = "25.2.9519653" | 30 | ndkVersion = "26.1.10909125" |
| 31 | 31 | ||
| 32 | buildFeatures { | 32 | buildFeatures { |
| 33 | viewBinding = true | 33 | viewBinding = true |
| @@ -203,23 +203,23 @@ ktlint { | |||
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | dependencies { | 205 | dependencies { |
| 206 | implementation("androidx.core:core-ktx:1.10.1") | 206 | implementation("androidx.core:core-ktx:1.12.0") |
| 207 | implementation("androidx.appcompat:appcompat:1.6.1") | 207 | implementation("androidx.appcompat:appcompat:1.6.1") |
| 208 | implementation("androidx.recyclerview:recyclerview:1.3.0") | 208 | implementation("androidx.recyclerview:recyclerview:1.3.1") |
| 209 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") | 209 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") |
| 210 | implementation("androidx.fragment:fragment-ktx:1.6.0") | 210 | implementation("androidx.fragment:fragment-ktx:1.6.1") |
| 211 | implementation("androidx.documentfile:documentfile:1.0.1") | 211 | implementation("androidx.documentfile:documentfile:1.0.1") |
| 212 | implementation("com.google.android.material:material:1.9.0") | 212 | implementation("com.google.android.material:material:1.9.0") |
| 213 | implementation("androidx.preference:preference:1.2.0") | 213 | implementation("androidx.preference:preference-ktx:1.2.1") |
| 214 | implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") | 214 | implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") |
| 215 | implementation("io.coil-kt:coil:2.2.2") | 215 | implementation("io.coil-kt:coil:2.2.2") |
| 216 | implementation("androidx.core:core-splashscreen:1.0.1") | 216 | implementation("androidx.core:core-splashscreen:1.0.1") |
| 217 | implementation("androidx.window:window:1.2.0-beta03") | 217 | implementation("androidx.window:window:1.2.0-beta03") |
| 218 | implementation("org.ini4j:ini4j:0.5.4") | 218 | implementation("org.ini4j:ini4j:0.5.4") |
| 219 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") | 219 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") |
| 220 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") | 220 | implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") |
| 221 | implementation("androidx.navigation:navigation-fragment-ktx:2.6.0") | 221 | implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") |
| 222 | implementation("androidx.navigation:navigation-ui-ktx:2.6.0") | 222 | implementation("androidx.navigation:navigation-ui-ktx:2.7.4") |
| 223 | implementation("info.debatty:java-string-similarity:2.0.0") | 223 | implementation("info.debatty:java-string-similarity:2.0.0") |
| 224 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") | 224 | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") |
| 225 | } | 225 | } |
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 832c08e15..a67351727 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml | |||
| @@ -28,7 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||
| 28 | android:appCategory="game" | 28 | android:appCategory="game" |
| 29 | android:localeConfig="@xml/locales_config" | 29 | android:localeConfig="@xml/locales_config" |
| 30 | android:banner="@drawable/tv_banner" | 30 | android:banner="@drawable/tv_banner" |
| 31 | android:extractNativeLibs="true" | ||
| 32 | android:fullBackupContent="@xml/data_extraction_rules" | 31 | android:fullBackupContent="@xml/data_extraction_rules" |
| 33 | android:dataExtractionRules="@xml/data_extraction_rules_api_31" | 32 | android:dataExtractionRules="@xml/data_extraction_rules_api_31" |
| 34 | android:enableOnBackInvokedCallback="true"> | 33 | android:enableOnBackInvokedCallback="true"> |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt index 10b1d3547..df21d74b2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | |||
| @@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.DriverViewModel | |||
| 26 | import org.yuzu.yuzu_emu.model.HomeViewModel | 26 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 27 | import org.yuzu.yuzu_emu.utils.FileUtil | 27 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 28 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | 28 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
| 29 | import java.io.File | ||
| 29 | import java.io.IOException | 30 | import java.io.IOException |
| 30 | 31 | ||
| 31 | class DriverManagerFragment : Fragment() { | 32 | class DriverManagerFragment : Fragment() { |
| @@ -154,29 +155,29 @@ class DriverManagerFragment : Fragment() { | |||
| 154 | R.string.installing_driver, | 155 | R.string.installing_driver, |
| 155 | false | 156 | false |
| 156 | ) { | 157 | ) { |
| 158 | val driverPath = | ||
| 159 | "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}" | ||
| 160 | val driverFile = File(driverPath) | ||
| 161 | |||
| 157 | // Ignore file exceptions when a user selects an invalid zip | 162 | // Ignore file exceptions when a user selects an invalid zip |
| 158 | try { | 163 | try { |
| 159 | GpuDriverHelper.copyDriverToInternalStorage(result) | 164 | if (!GpuDriverHelper.copyDriverToInternalStorage(result)) { |
| 165 | throw IOException("Driver failed validation!") | ||
| 166 | } | ||
| 160 | } catch (_: IOException) { | 167 | } catch (_: IOException) { |
| 168 | if (driverFile.exists()) { | ||
| 169 | driverFile.delete() | ||
| 170 | } | ||
| 161 | return@newInstance getString(R.string.select_gpu_driver_error) | 171 | return@newInstance getString(R.string.select_gpu_driver_error) |
| 162 | } | 172 | } |
| 163 | 173 | ||
| 164 | val driverData = GpuDriverHelper.customDriverData | 174 | val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) |
| 165 | if (driverData.name == null) { | ||
| 166 | return@newInstance getString(R.string.select_gpu_driver_error) | ||
| 167 | } | ||
| 168 | |||
| 169 | val driverInList = | 175 | val driverInList = |
| 170 | driverViewModel.driverList.value.firstOrNull { it.second == driverData } | 176 | driverViewModel.driverList.value.firstOrNull { it.second == driverData } |
| 171 | if (driverInList != null) { | 177 | if (driverInList != null) { |
| 172 | return@newInstance getString(R.string.driver_already_installed) | 178 | return@newInstance getString(R.string.driver_already_installed) |
| 173 | } else { | 179 | } else { |
| 174 | driverViewModel.addDriver( | 180 | driverViewModel.addDriver(Pair(driverPath, driverData)) |
| 175 | Pair( | ||
| 176 | "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}", | ||
| 177 | driverData | ||
| 178 | ) | ||
| 179 | ) | ||
| 180 | driverViewModel.setNewDriverInstalled(true) | 181 | driverViewModel.setNewDriverInstalled(true) |
| 181 | } | 182 | } |
| 182 | return@newInstance Any() | 183 | return@newInstance Any() |
diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts index 80f370c16..51e559321 100644 --- a/src/android/build.gradle.kts +++ b/src/android/build.gradle.kts | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | 3 | ||
| 4 | // Top-level build file where you can add configuration options common to all sub-projects/modules. | 4 | // Top-level build file where you can add configuration options common to all sub-projects/modules. |
| 5 | plugins { | 5 | plugins { |
| 6 | id("com.android.application") version "8.0.2" apply false | 6 | id("com.android.application") version "8.1.2" apply false |
| 7 | id("com.android.library") version "8.0.2" apply false | 7 | id("com.android.library") version "8.1.2" apply false |
| 8 | id("org.jetbrains.kotlin.android") version "1.8.21" apply false | 8 | id("org.jetbrains.kotlin.android") version "1.8.21" apply false |
| 9 | } | 9 | } |
| 10 | 10 | ||
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 6081352a2..d66d04fae 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp | |||
| @@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz | |||
| 204 | // paused and we'll desync, so just play silence. | 204 | // paused and we'll desync, so just play silence. |
| 205 | if (system.IsPaused() || system.IsShuttingDown()) { | 205 | if (system.IsPaused() || system.IsShuttingDown()) { |
| 206 | if (system.IsShuttingDown()) { | 206 | if (system.IsShuttingDown()) { |
| 207 | { | ||
| 208 | std::scoped_lock lk{release_mutex}; | ||
| 209 | queued_buffers.store(0); | ||
| 210 | } | ||
| 207 | release_cv.notify_one(); | 211 | release_cv.notify_one(); |
| 208 | } | 212 | } |
| 209 | 213 | ||
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index 41cbb9ed5..12e59a893 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h | |||
| @@ -15,12 +15,13 @@ | |||
| 15 | #include <condition_variable> | 15 | #include <condition_variable> |
| 16 | #include <stop_token> | 16 | #include <stop_token> |
| 17 | #include <thread> | 17 | #include <thread> |
| 18 | #include <utility> | ||
| 18 | 19 | ||
| 19 | namespace Common { | 20 | namespace Common { |
| 20 | 21 | ||
| 21 | template <typename Condvar, typename Lock, typename Pred> | 22 | template <typename Condvar, typename Lock, typename Pred> |
| 22 | void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) { | 23 | void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) { |
| 23 | cv.wait(lk, token, std::move(pred)); | 24 | cv.wait(lk, token, std::forward<Pred>(pred)); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | template <typename Rep, typename Period> | 27 | template <typename Rep, typename Period> |
| @@ -109,7 +110,7 @@ public: | |||
| 109 | 110 | ||
| 110 | // Insert the callback. | 111 | // Insert the callback. |
| 111 | stop_state_callback ret = ++m_next_callback; | 112 | stop_state_callback ret = ++m_next_callback; |
| 112 | m_callbacks.emplace(ret, move(f)); | 113 | m_callbacks.emplace(ret, std::move(f)); |
| 113 | return ret; | 114 | return ret; |
| 114 | } | 115 | } |
| 115 | 116 | ||
| @@ -162,7 +163,7 @@ private: | |||
| 162 | friend class stop_source; | 163 | friend class stop_source; |
| 163 | template <typename Callback> | 164 | template <typename Callback> |
| 164 | friend class stop_callback; | 165 | friend class stop_callback; |
| 165 | stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {} | 166 | stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {} |
| 166 | 167 | ||
| 167 | private: | 168 | private: |
| 168 | shared_ptr<polyfill::stop_state> m_stop_state; | 169 | shared_ptr<polyfill::stop_state> m_stop_state; |
| @@ -198,7 +199,7 @@ public: | |||
| 198 | private: | 199 | private: |
| 199 | friend class jthread; | 200 | friend class jthread; |
| 200 | explicit stop_source(shared_ptr<polyfill::stop_state> stop_state) | 201 | explicit stop_source(shared_ptr<polyfill::stop_state> stop_state) |
| 201 | : m_stop_state(move(stop_state)) {} | 202 | : m_stop_state(std::move(stop_state)) {} |
| 202 | 203 | ||
| 203 | private: | 204 | private: |
| 204 | shared_ptr<polyfill::stop_state> m_stop_state; | 205 | shared_ptr<polyfill::stop_state> m_stop_state; |
| @@ -218,16 +219,16 @@ public: | |||
| 218 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) | 219 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) |
| 219 | : m_stop_state(st.m_stop_state) { | 220 | : m_stop_state(st.m_stop_state) { |
| 220 | if (m_stop_state) { | 221 | if (m_stop_state) { |
| 221 | m_callback = m_stop_state->insert_callback(move(cb)); | 222 | m_callback = m_stop_state->insert_callback(std::move(cb)); |
| 222 | } | 223 | } |
| 223 | } | 224 | } |
| 224 | template <typename C> | 225 | template <typename C> |
| 225 | requires constructible_from<Callback, C> | 226 | requires constructible_from<Callback, C> |
| 226 | explicit stop_callback(stop_token&& st, | 227 | explicit stop_callback(stop_token&& st, |
| 227 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) | 228 | C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) |
| 228 | : m_stop_state(move(st.m_stop_state)) { | 229 | : m_stop_state(std::move(st.m_stop_state)) { |
| 229 | if (m_stop_state) { | 230 | if (m_stop_state) { |
| 230 | m_callback = m_stop_state->insert_callback(move(cb)); | 231 | m_callback = m_stop_state->insert_callback(std::move(cb)); |
| 231 | } | 232 | } |
| 232 | } | 233 | } |
| 233 | ~stop_callback() { | 234 | ~stop_callback() { |
| @@ -260,7 +261,7 @@ public: | |||
| 260 | typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>> | 261 | typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>> |
| 261 | explicit jthread(F&& f, Args&&... args) | 262 | explicit jthread(F&& f, Args&&... args) |
| 262 | : m_stop_state(make_shared<polyfill::stop_state>()), | 263 | : m_stop_state(make_shared<polyfill::stop_state>()), |
| 263 | m_thread(make_thread(move(f), move(args)...)) {} | 264 | m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {} |
| 264 | 265 | ||
| 265 | ~jthread() { | 266 | ~jthread() { |
| 266 | if (joinable()) { | 267 | if (joinable()) { |
| @@ -317,9 +318,9 @@ private: | |||
| 317 | template <typename F, typename... Args> | 318 | template <typename F, typename... Args> |
| 318 | thread make_thread(F&& f, Args&&... args) { | 319 | thread make_thread(F&& f, Args&&... args) { |
| 319 | if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) { | 320 | if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) { |
| 320 | return thread(move(f), get_stop_token(), move(args)...); | 321 | return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...); |
| 321 | } else { | 322 | } else { |
| 322 | return thread(move(f), move(args)...); | 323 | return thread(std::forward<F>(f), std::forward<Args>(args)...); |
| 323 | } | 324 | } |
| 324 | } | 325 | } |
| 325 | 326 | ||
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 3fde3cae6..98b43e49c 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true); | |||
| 45 | SWITCHABLE(FullscreenMode, true); | 45 | SWITCHABLE(FullscreenMode, true); |
| 46 | SWITCHABLE(GpuAccuracy, true); | 46 | SWITCHABLE(GpuAccuracy, true); |
| 47 | SWITCHABLE(Language, true); | 47 | SWITCHABLE(Language, true); |
| 48 | SWITCHABLE(MemoryLayout, true); | ||
| 48 | SWITCHABLE(NvdecEmulation, false); | 49 | SWITCHABLE(NvdecEmulation, false); |
| 49 | SWITCHABLE(Region, true); | 50 | SWITCHABLE(Region, true); |
| 50 | SWITCHABLE(RendererBackend, true); | 51 | SWITCHABLE(RendererBackend, true); |
| @@ -61,6 +62,10 @@ SWITCHABLE(u32, false); | |||
| 61 | SWITCHABLE(u8, false); | 62 | SWITCHABLE(u8, false); |
| 62 | SWITCHABLE(u8, true); | 63 | SWITCHABLE(u8, true); |
| 63 | 64 | ||
| 65 | // Used in UISettings | ||
| 66 | // TODO see if we can move this to uisettings.cpp | ||
| 67 | SWITCHABLE(ConfirmStop, true); | ||
| 68 | |||
| 64 | #undef SETTING | 69 | #undef SETTING |
| 65 | #undef SWITCHABLE | 70 | #undef SWITCHABLE |
| 66 | #endif | 71 | #endif |
diff --git a/src/common/settings.h b/src/common/settings.h index 98ab0ec2e..236e33bee 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true); | |||
| 67 | SWITCHABLE(FullscreenMode, true); | 67 | SWITCHABLE(FullscreenMode, true); |
| 68 | SWITCHABLE(GpuAccuracy, true); | 68 | SWITCHABLE(GpuAccuracy, true); |
| 69 | SWITCHABLE(Language, true); | 69 | SWITCHABLE(Language, true); |
| 70 | SWITCHABLE(MemoryLayout, true); | ||
| 70 | SWITCHABLE(NvdecEmulation, false); | 71 | SWITCHABLE(NvdecEmulation, false); |
| 71 | SWITCHABLE(Region, true); | 72 | SWITCHABLE(Region, true); |
| 72 | SWITCHABLE(RendererBackend, true); | 73 | SWITCHABLE(RendererBackend, true); |
| @@ -83,6 +84,10 @@ SWITCHABLE(u32, false); | |||
| 83 | SWITCHABLE(u8, false); | 84 | SWITCHABLE(u8, false); |
| 84 | SWITCHABLE(u8, true); | 85 | SWITCHABLE(u8, true); |
| 85 | 86 | ||
| 87 | // Used in UISettings | ||
| 88 | // TODO see if we can move this to uisettings.h | ||
| 89 | SWITCHABLE(ConfirmStop, true); | ||
| 90 | |||
| 86 | #undef SETTING | 91 | #undef SETTING |
| 87 | #undef SWITCHABLE | 92 | #undef SWITCHABLE |
| 88 | #endif | 93 | #endif |
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 815cafe15..11429d7a8 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h | |||
| @@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); | |||
| 133 | 133 | ||
| 134 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); | 134 | ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); |
| 135 | 135 | ||
| 136 | ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); | ||
| 137 | |||
| 136 | ENUM(FullscreenMode, Borderless, Exclusive); | 138 | ENUM(FullscreenMode, Borderless, Exclusive); |
| 137 | 139 | ||
| 138 | ENUM(NvdecEmulation, Off, Cpu, Gpu); | 140 | ENUM(NvdecEmulation, Off, Cpu, Gpu); |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index b971401e6..b7d14060c 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -49,7 +49,7 @@ public: | |||
| 49 | : ServiceFramework{system_, "IManagerForSystemService"} { | 49 | : ServiceFramework{system_, "IManagerForSystemService"} { |
| 50 | // clang-format off | 50 | // clang-format off |
| 51 | static const FunctionInfo functions[] = { | 51 | static const FunctionInfo functions[] = { |
| 52 | {0, nullptr, "CheckAvailability"}, | 52 | {0, &IManagerForSystemService::CheckAvailability, "CheckAvailability"}, |
| 53 | {1, nullptr, "GetAccountId"}, | 53 | {1, nullptr, "GetAccountId"}, |
| 54 | {2, nullptr, "EnsureIdTokenCacheAsync"}, | 54 | {2, nullptr, "EnsureIdTokenCacheAsync"}, |
| 55 | {3, nullptr, "LoadIdTokenCache"}, | 55 | {3, nullptr, "LoadIdTokenCache"}, |
| @@ -78,6 +78,13 @@ public: | |||
| 78 | 78 | ||
| 79 | RegisterHandlers(functions); | 79 | RegisterHandlers(functions); |
| 80 | } | 80 | } |
| 81 | |||
| 82 | private: | ||
| 83 | void CheckAvailability(HLERequestContext& ctx) { | ||
| 84 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 85 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 86 | rb.Push(ResultSuccess); | ||
| 87 | } | ||
| 81 | }; | 88 | }; |
| 82 | 89 | ||
| 83 | // 3.0.0+ | 90 | // 3.0.0+ |
| @@ -837,6 +844,29 @@ void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) { | |||
| 837 | rb.Push(ResultSuccess); | 844 | rb.Push(ResultSuccess); |
| 838 | } | 845 | } |
| 839 | 846 | ||
| 847 | void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) { | ||
| 848 | const auto user_id = Common::UUID::MakeRandom(); | ||
| 849 | profile_manager->CreateNewUser(user_id, "yuzu"); | ||
| 850 | |||
| 851 | LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); | ||
| 852 | |||
| 853 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 854 | rb.Push(ResultSuccess); | ||
| 855 | rb.PushRaw(user_id); | ||
| 856 | } | ||
| 857 | |||
| 858 | void Module::Interface::CompleteUserRegistration(HLERequestContext& ctx) { | ||
| 859 | IPC::RequestParser rp{ctx}; | ||
| 860 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | ||
| 861 | |||
| 862 | LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); | ||
| 863 | |||
| 864 | profile_manager->WriteUserSaveFile(); | ||
| 865 | |||
| 866 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 867 | rb.Push(ResultSuccess); | ||
| 868 | } | ||
| 869 | |||
| 840 | void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { | 870 | void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { |
| 841 | IPC::RequestParser rp{ctx}; | 871 | IPC::RequestParser rp{ctx}; |
| 842 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); | 872 | Common::UUID user_id = rp.PopRaw<Common::UUID>(); |
| @@ -880,6 +910,17 @@ void Module::Interface::StoreSaveDataThumbnailApplication(HLERequestContext& ctx | |||
| 880 | StoreSaveDataThumbnail(ctx, uuid, tid); | 910 | StoreSaveDataThumbnail(ctx, uuid, tid); |
| 881 | } | 911 | } |
| 882 | 912 | ||
| 913 | void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext& ctx) { | ||
| 914 | IPC::RequestParser rp{ctx}; | ||
| 915 | const auto uuid = rp.PopRaw<Common::UUID>(); | ||
| 916 | |||
| 917 | LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString()); | ||
| 918 | |||
| 919 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 920 | rb.Push(ResultSuccess); | ||
| 921 | rb.PushIpcInterface<IManagerForSystemService>(system, uuid); | ||
| 922 | } | ||
| 923 | |||
| 883 | void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { | 924 | void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { |
| 884 | IPC::RequestParser rp{ctx}; | 925 | IPC::RequestParser rp{ctx}; |
| 885 | const auto uuid = rp.PopRaw<Common::UUID>(); | 926 | const auto uuid = rp.PopRaw<Common::UUID>(); |
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 6b4735c2f..0395229b4 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h | |||
| @@ -33,10 +33,13 @@ public: | |||
| 33 | void TrySelectUserWithoutInteraction(HLERequestContext& ctx); | 33 | void TrySelectUserWithoutInteraction(HLERequestContext& ctx); |
| 34 | void IsUserAccountSwitchLocked(HLERequestContext& ctx); | 34 | void IsUserAccountSwitchLocked(HLERequestContext& ctx); |
| 35 | void InitializeApplicationInfoV2(HLERequestContext& ctx); | 35 | void InitializeApplicationInfoV2(HLERequestContext& ctx); |
| 36 | void BeginUserRegistration(HLERequestContext& ctx); | ||
| 37 | void CompleteUserRegistration(HLERequestContext& ctx); | ||
| 36 | void GetProfileEditor(HLERequestContext& ctx); | 38 | void GetProfileEditor(HLERequestContext& ctx); |
| 37 | void ListQualifiedUsers(HLERequestContext& ctx); | 39 | void ListQualifiedUsers(HLERequestContext& ctx); |
| 38 | void ListOpenContextStoredUsers(HLERequestContext& ctx); | 40 | void ListOpenContextStoredUsers(HLERequestContext& ctx); |
| 39 | void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); | 41 | void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); |
| 42 | void GetBaasAccountManagerForSystemService(HLERequestContext& ctx); | ||
| 40 | void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); | 43 | void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); |
| 41 | 44 | ||
| 42 | private: | 45 | private: |
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index d9882ecd3..770d13ec5 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp | |||
| @@ -23,7 +23,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager> | |||
| 23 | {99, nullptr, "DebugActivateOpenContextRetention"}, | 23 | {99, nullptr, "DebugActivateOpenContextRetention"}, |
| 24 | {100, nullptr, "GetUserRegistrationNotifier"}, | 24 | {100, nullptr, "GetUserRegistrationNotifier"}, |
| 25 | {101, nullptr, "GetUserStateChangeNotifier"}, | 25 | {101, nullptr, "GetUserStateChangeNotifier"}, |
| 26 | {102, nullptr, "GetBaasAccountManagerForSystemService"}, | 26 | {102, &ACC_SU::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"}, |
| 27 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, | 27 | {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, |
| 28 | {104, nullptr, "GetProfileUpdateNotifier"}, | 28 | {104, nullptr, "GetProfileUpdateNotifier"}, |
| 29 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, | 29 | {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, |
| @@ -40,8 +40,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager> | |||
| 40 | {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, | 40 | {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, |
| 41 | {190, nullptr, "GetUserLastOpenedApplication"}, | 41 | {190, nullptr, "GetUserLastOpenedApplication"}, |
| 42 | {191, nullptr, "ActivateOpenContextHolder"}, | 42 | {191, nullptr, "ActivateOpenContextHolder"}, |
| 43 | {200, nullptr, "BeginUserRegistration"}, | 43 | {200, &ACC_SU::BeginUserRegistration, "BeginUserRegistration"}, |
| 44 | {201, nullptr, "CompleteUserRegistration"}, | 44 | {201, &ACC_SU::CompleteUserRegistration, "CompleteUserRegistration"}, |
| 45 | {202, nullptr, "CancelUserRegistration"}, | 45 | {202, nullptr, "CancelUserRegistration"}, |
| 46 | {203, nullptr, "DeleteUser"}, | 46 | {203, nullptr, "DeleteUser"}, |
| 47 | {204, nullptr, "SetUserPosition"}, | 47 | {204, nullptr, "SetUserPosition"}, |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 993a5a57a..900e32200 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -96,9 +96,10 @@ public: | |||
| 96 | bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, | 96 | bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, |
| 97 | const UserData& data_new); | 97 | const UserData& data_new); |
| 98 | 98 | ||
| 99 | void WriteUserSaveFile(); | ||
| 100 | |||
| 99 | private: | 101 | private: |
| 100 | void ParseUserSaveFile(); | 102 | void ParseUserSaveFile(); |
| 101 | void WriteUserSaveFile(); | ||
| 102 | std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | 103 | std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); |
| 103 | bool RemoveProfileAtIndex(std::size_t index); | 104 | bool RemoveProfileAtIndex(std::size_t index); |
| 104 | 105 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ac376b55a..98765b81a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_) | |||
| 210 | {21, nullptr, "ClearAppletTransitionBuffer"}, | 210 | {21, nullptr, "ClearAppletTransitionBuffer"}, |
| 211 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, | 211 | {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, |
| 212 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, | 212 | {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, |
| 213 | {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, | 213 | {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, |
| 214 | {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, | 214 | {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, |
| 215 | {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, | 215 | {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, |
| 216 | {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, | 216 | {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, |
| 217 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, | 217 | {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, |
| @@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { | |||
| 239 | rb.Push(ResultSuccess); | 239 | rb.Push(ResultSuccess); |
| 240 | } | 240 | } |
| 241 | 241 | ||
| 242 | void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { | ||
| 243 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 244 | |||
| 245 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 246 | rb.Push(ResultSuccess); | ||
| 247 | rb.Push(1U); | ||
| 248 | rb.Push(0); | ||
| 249 | } | ||
| 250 | |||
| 251 | void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { | ||
| 252 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 253 | |||
| 254 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 255 | rb.Push(ResultSuccess); | ||
| 256 | } | ||
| 257 | |||
| 242 | void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { | 258 | void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { |
| 243 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 259 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 244 | 260 | ||
| @@ -1557,7 +1573,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) | |||
| 1557 | {100, nullptr, "CreateGameMovieTrimmer"}, | 1573 | {100, nullptr, "CreateGameMovieTrimmer"}, |
| 1558 | {101, nullptr, "ReserveResourceForMovieOperation"}, | 1574 | {101, nullptr, "ReserveResourceForMovieOperation"}, |
| 1559 | {102, nullptr, "UnreserveResourceForMovieOperation"}, | 1575 | {102, nullptr, "UnreserveResourceForMovieOperation"}, |
| 1560 | {110, nullptr, "GetMainAppletAvailableUsers"}, | 1576 | {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"}, |
| 1561 | {120, nullptr, "GetLaunchStorageInfoForDebug"}, | 1577 | {120, nullptr, "GetLaunchStorageInfoForDebug"}, |
| 1562 | {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, | 1578 | {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, |
| 1563 | {140, nullptr, "SetApplicationMemoryReservation"}, | 1579 | {140, nullptr, "SetApplicationMemoryReservation"}, |
| @@ -1652,6 +1668,25 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& | |||
| 1652 | rb.PushRaw(applet_info); | 1668 | rb.PushRaw(applet_info); |
| 1653 | } | 1669 | } |
| 1654 | 1670 | ||
| 1671 | void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { | ||
| 1672 | const Service::Account::ProfileManager manager{}; | ||
| 1673 | bool is_empty{true}; | ||
| 1674 | s32 user_count{-1}; | ||
| 1675 | |||
| 1676 | LOG_INFO(Service_AM, "called"); | ||
| 1677 | |||
| 1678 | if (manager.GetUserCount() > 0) { | ||
| 1679 | is_empty = false; | ||
| 1680 | user_count = static_cast<s32>(manager.GetUserCount()); | ||
| 1681 | ctx.WriteBuffer(manager.GetAllUsers()); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 1685 | rb.Push(ResultSuccess); | ||
| 1686 | rb.Push<u8>(is_empty); | ||
| 1687 | rb.Push(user_count); | ||
| 1688 | } | ||
| 1689 | |||
| 1655 | void ILibraryAppletSelfAccessor::PushInShowAlbum() { | 1690 | void ILibraryAppletSelfAccessor::PushInShowAlbum() { |
| 1656 | const Applets::CommonArguments arguments{ | 1691 | const Applets::CommonArguments arguments{ |
| 1657 | .arguments_version = Applets::CommonArgumentVersion::Version3, | 1692 | .arguments_version = Applets::CommonArgumentVersion::Version3, |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 4a045cfd4..64b3f3fe2 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -124,6 +124,8 @@ public: | |||
| 124 | private: | 124 | private: |
| 125 | void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); | 125 | void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); |
| 126 | void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); | 126 | void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); |
| 127 | void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); | ||
| 128 | void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); | ||
| 127 | void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); | 129 | void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); |
| 128 | void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); | 130 | void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); |
| 129 | }; | 131 | }; |
| @@ -345,6 +347,7 @@ private: | |||
| 345 | void GetLibraryAppletInfo(HLERequestContext& ctx); | 347 | void GetLibraryAppletInfo(HLERequestContext& ctx); |
| 346 | void ExitProcessAndReturn(HLERequestContext& ctx); | 348 | void ExitProcessAndReturn(HLERequestContext& ctx); |
| 347 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); | 349 | void GetCallerAppletIdentityInfo(HLERequestContext& ctx); |
| 350 | void GetMainAppletAvailableUsers(HLERequestContext& ctx); | ||
| 348 | 351 | ||
| 349 | void PushInShowAlbum(); | 352 | void PushInShowAlbum(); |
| 350 | void PushInShowCabinetData(); | 353 | void PushInShowCabinetData(); |
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 808b21069..77db60e92 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -338,6 +338,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host, | |||
| 338 | for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { | 338 | for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { |
| 339 | const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); | 339 | const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); |
| 340 | PreSetController(identifier); | 340 | PreSetController(identifier); |
| 341 | PreSetMotion(identifier, 0); | ||
| 341 | } | 342 | } |
| 342 | } | 343 | } |
| 343 | 344 | ||
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index c4f6e8d12..eed267361 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h | |||
| @@ -62,7 +62,11 @@ using BufferId = SlotId; | |||
| 62 | using VideoCore::Surface::PixelFormat; | 62 | using VideoCore::Surface::PixelFormat; |
| 63 | using namespace Common::Literals; | 63 | using namespace Common::Literals; |
| 64 | 64 | ||
| 65 | #ifdef __APPLE__ | ||
| 66 | constexpr u32 NUM_VERTEX_BUFFERS = 16; | ||
| 67 | #else | ||
| 65 | constexpr u32 NUM_VERTEX_BUFFERS = 32; | 68 | constexpr u32 NUM_VERTEX_BUFFERS = 32; |
| 69 | #endif | ||
| 66 | constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; | 70 | constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; |
| 67 | constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; | 71 | constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; |
| 68 | constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; | 72 | constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; |
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index f34090791..d77ff455b 100644 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp | |||
| @@ -48,8 +48,14 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) { | |||
| 48 | SetInlineIndexBuffer(regs.inline_index_4x8.index3); | 48 | SetInlineIndexBuffer(regs.inline_index_4x8.index3); |
| 49 | break; | 49 | break; |
| 50 | case MAXWELL3D_REG_INDEX(vertex_array_instance_first): | 50 | case MAXWELL3D_REG_INDEX(vertex_array_instance_first): |
| 51 | DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(), | ||
| 52 | regs.vertex_array_instance_first.start.Value(), | ||
| 53 | regs.vertex_array_instance_first.count.Value(), false); | ||
| 54 | break; | ||
| 51 | case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { | 55 | case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { |
| 52 | LOG_WARNING(HW_GPU, "(STUBBED) called"); | 56 | DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(), |
| 57 | regs.vertex_array_instance_subsequent.start.Value(), | ||
| 58 | regs.vertex_array_instance_subsequent.count.Value(), true); | ||
| 53 | break; | 59 | break; |
| 54 | } | 60 | } |
| 55 | case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { | 61 | case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { |
| @@ -84,6 +90,22 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve | |||
| 84 | ProcessDraw(false, num_instances); | 90 | ProcessDraw(false, num_instances); |
| 85 | } | 91 | } |
| 86 | 92 | ||
| 93 | void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, | ||
| 94 | bool subsequent) { | ||
| 95 | draw_state.topology = topology; | ||
| 96 | draw_state.vertex_buffer.first = vertex_first; | ||
| 97 | draw_state.vertex_buffer.count = vertex_count; | ||
| 98 | |||
| 99 | if (!subsequent) { | ||
| 100 | draw_state.instance_count = 1; | ||
| 101 | } | ||
| 102 | |||
| 103 | draw_state.base_instance = draw_state.instance_count - 1; | ||
| 104 | draw_state.draw_mode = DrawMode::Instance; | ||
| 105 | draw_state.instance_count++; | ||
| 106 | ProcessDraw(false, 1); | ||
| 107 | } | ||
| 108 | |||
| 87 | void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, | 109 | void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, |
| 88 | u32 base_index, u32 base_instance, u32 num_instances) { | 110 | u32 base_index, u32 base_instance, u32 num_instances) { |
| 89 | const auto& regs{maxwell3d->regs}; | 111 | const auto& regs{maxwell3d->regs}; |
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h index 18d959143..cfc8127fc 100644 --- a/src/video_core/engines/draw_manager.h +++ b/src/video_core/engines/draw_manager.h | |||
| @@ -66,6 +66,8 @@ public: | |||
| 66 | 66 | ||
| 67 | void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, | 67 | void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, |
| 68 | u32 base_instance, u32 num_instances); | 68 | u32 base_instance, u32 num_instances); |
| 69 | void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, | ||
| 70 | bool subsequent); | ||
| 69 | 71 | ||
| 70 | void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, | 72 | void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, |
| 71 | u32 base_instance, u32 num_instances); | 73 | u32 base_instance, u32 num_instances); |
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index 8d7da50fc..dbcf508e5 100644 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp | |||
| @@ -137,16 +137,6 @@ bool Codec::CreateGpuAvDevice() { | |||
| 137 | break; | 137 | break; |
| 138 | } | 138 | } |
| 139 | if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { | 139 | if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { |
| 140 | #if defined(__unix__) | ||
| 141 | // Some linux decoding backends are reported to crash with this config method | ||
| 142 | // TODO(ameerj): Properly support this method | ||
| 143 | if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) { | ||
| 144 | // skip zero-copy decoders, we don't currently support them | ||
| 145 | LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", | ||
| 146 | av_hwdevice_get_type_name(type), config->methods); | ||
| 147 | continue; | ||
| 148 | } | ||
| 149 | #endif | ||
| 150 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); | 140 | LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); |
| 151 | av_codec_ctx->pix_fmt = config->pix_fmt; | 141 | av_codec_ctx->pix_fmt = config->pix_fmt; |
| 152 | return true; | 142 | return true; |
diff --git a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag index d33131d7c..b81a54056 100644 --- a/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag +++ b/src/video_core/host_shaders/convert_d24s8_to_abgr8.frag | |||
| @@ -3,16 +3,16 @@ | |||
| 3 | 3 | ||
| 4 | #version 450 | 4 | #version 450 |
| 5 | 5 | ||
| 6 | precision mediump int; | ||
| 7 | precision highp float; | ||
| 8 | |||
| 6 | layout(binding = 0) uniform sampler2D depth_tex; | 9 | layout(binding = 0) uniform sampler2D depth_tex; |
| 7 | layout(binding = 1) uniform isampler2D stencil_tex; | 10 | layout(binding = 1) uniform usampler2D stencil_tex; |
| 8 | 11 | ||
| 9 | layout(location = 0) out vec4 color; | 12 | layout(location = 0) out vec4 color; |
| 10 | 13 | ||
| 11 | void main() { | 14 | void main() { |
| 12 | ivec2 coord = ivec2(gl_FragCoord.xy); | 15 | ivec2 coord = ivec2(gl_FragCoord.xy); |
| 13 | uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f)); | ||
| 14 | uint stencil = uint(textureLod(stencil_tex, coord, 0).r); | ||
| 15 | |||
| 16 | highp uint depth_val = | 16 | highp uint depth_val = |
| 17 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); | 17 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); |
| 18 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; | 18 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; |
diff --git a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag index 31db7d426..6a457981d 100644 --- a/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag +++ b/src/video_core/host_shaders/convert_s8d24_to_abgr8.frag | |||
| @@ -3,16 +3,16 @@ | |||
| 3 | 3 | ||
| 4 | #version 450 | 4 | #version 450 |
| 5 | 5 | ||
| 6 | precision mediump int; | ||
| 7 | precision highp float; | ||
| 8 | |||
| 6 | layout(binding = 0) uniform sampler2D depth_tex; | 9 | layout(binding = 0) uniform sampler2D depth_tex; |
| 7 | layout(binding = 1) uniform isampler2D stencil_tex; | 10 | layout(binding = 1) uniform usampler2D stencil_tex; |
| 8 | 11 | ||
| 9 | layout(location = 0) out vec4 color; | 12 | layout(location = 0) out vec4 color; |
| 10 | 13 | ||
| 11 | void main() { | 14 | void main() { |
| 12 | ivec2 coord = ivec2(gl_FragCoord.xy); | 15 | ivec2 coord = ivec2(gl_FragCoord.xy); |
| 13 | uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f)); | ||
| 14 | uint stencil = uint(textureLod(stencil_tex, coord, 0).r); | ||
| 15 | |||
| 16 | highp uint depth_val = | 16 | highp uint depth_val = |
| 17 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); | 17 | uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0)); |
| 18 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; | 18 | lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r; |
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 2edaafa7e..66c03bf17 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp | |||
| @@ -1436,6 +1436,7 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) { | |||
| 1436 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, | 1436 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, |
| 1437 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | 1437 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, |
| 1438 | }; | 1438 | }; |
| 1439 | impl->scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 1439 | if (is_prebarrier) { | 1440 | if (is_prebarrier) { |
| 1440 | impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { | 1441 | impl->scheduler.Record([](vk::CommandBuffer cmdbuf) { |
| 1441 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | 1442 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 83f2b6045..61d03daae 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -975,6 +975,19 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs | |||
| 975 | if (!state_tracker.TouchScissors()) { | 975 | if (!state_tracker.TouchScissors()) { |
| 976 | return; | 976 | return; |
| 977 | } | 977 | } |
| 978 | if (!regs.viewport_scale_offset_enabled) { | ||
| 979 | const auto x = static_cast<float>(regs.surface_clip.x); | ||
| 980 | const auto y = static_cast<float>(regs.surface_clip.y); | ||
| 981 | const auto width = static_cast<float>(regs.surface_clip.width); | ||
| 982 | const auto height = static_cast<float>(regs.surface_clip.height); | ||
| 983 | VkRect2D scissor; | ||
| 984 | scissor.offset.x = static_cast<u32>(x); | ||
| 985 | scissor.offset.y = static_cast<u32>(y); | ||
| 986 | scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f); | ||
| 987 | scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f); | ||
| 988 | scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); }); | ||
| 989 | return; | ||
| 990 | } | ||
| 978 | u32 up_scale = 1; | 991 | u32 up_scale = 1; |
| 979 | u32 down_shift = 0; | 992 | u32 down_shift = 0; |
| 980 | if (texture_cache.IsRescaling()) { | 993 | if (texture_cache.IsRescaling()) { |
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index ae9f1de64..7746a88d3 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp | |||
| @@ -19,7 +19,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat | |||
| 19 | VkSampleCountFlagBits samples) { | 19 | VkSampleCountFlagBits samples) { |
| 20 | using MaxwellToVK::SurfaceFormat; | 20 | using MaxwellToVK::SurfaceFormat; |
| 21 | return { | 21 | return { |
| 22 | .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, | 22 | .flags = {}, |
| 23 | .format = SurfaceFormat(device, FormatType::Optimal, true, format).format, | 23 | .format = SurfaceFormat(device, FormatType::Optimal, true, format).format, |
| 24 | .samples = samples, | 24 | .samples = samples, |
| 25 | .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | 25 | .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, |
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp index 6279d8e9e..2b7e0df72 100644 --- a/src/video_core/texture_cache/formatter.cpp +++ b/src/video_core/texture_cache/formatter.cpp | |||
| @@ -10,19 +10,23 @@ | |||
| 10 | #include "video_core/texture_cache/image_info.h" | 10 | #include "video_core/texture_cache/image_info.h" |
| 11 | #include "video_core/texture_cache/image_view_base.h" | 11 | #include "video_core/texture_cache/image_view_base.h" |
| 12 | #include "video_core/texture_cache/render_targets.h" | 12 | #include "video_core/texture_cache/render_targets.h" |
| 13 | #include "video_core/texture_cache/samples_helper.h" | ||
| 13 | 14 | ||
| 14 | namespace VideoCommon { | 15 | namespace VideoCommon { |
| 15 | 16 | ||
| 16 | std::string Name(const ImageBase& image) { | 17 | std::string Name(const ImageBase& image) { |
| 17 | const GPUVAddr gpu_addr = image.gpu_addr; | 18 | const GPUVAddr gpu_addr = image.gpu_addr; |
| 18 | const ImageInfo& info = image.info; | 19 | const ImageInfo& info = image.info; |
| 19 | const u32 width = info.size.width; | 20 | u32 width = info.size.width; |
| 20 | const u32 height = info.size.height; | 21 | u32 height = info.size.height; |
| 21 | const u32 depth = info.size.depth; | 22 | const u32 depth = info.size.depth; |
| 22 | const u32 num_layers = image.info.resources.layers; | 23 | const u32 num_layers = image.info.resources.layers; |
| 23 | const u32 num_levels = image.info.resources.levels; | 24 | const u32 num_levels = image.info.resources.levels; |
| 24 | std::string resource; | 25 | std::string resource; |
| 25 | if (image.info.num_samples > 1) { | 26 | if (image.info.num_samples > 1) { |
| 27 | const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(image.info.num_samples); | ||
| 28 | width >>= samples_x; | ||
| 29 | height >>= samples_y; | ||
| 26 | resource += fmt::format(":{}xMSAA", image.info.num_samples); | 30 | resource += fmt::format(":{}xMSAA", image.info.num_samples); |
| 27 | } | 31 | } |
| 28 | if (num_layers > 1) { | 32 | if (num_layers > 1) { |
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h index 203ac1b11..2ee2f8312 100644 --- a/src/video_core/texture_cache/samples_helper.h +++ b/src/video_core/texture_cache/samples_helper.h | |||
| @@ -24,7 +24,7 @@ namespace VideoCommon { | |||
| 24 | return {2, 2}; | 24 | return {2, 2}; |
| 25 | } | 25 | } |
| 26 | ASSERT_MSG(false, "Invalid number of samples={}", num_samples); | 26 | ASSERT_MSG(false, "Invalid number of samples={}", num_samples); |
| 27 | return {1, 1}; | 27 | return {0, 0}; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | [[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) { | 30 | [[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) { |
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 8151cabf0..15596c925 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp | |||
| @@ -167,6 +167,13 @@ template <u32 GOB_EXTENT> | |||
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | [[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { | 169 | [[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { |
| 170 | if (level == 0 && info.num_levels == 1) { | ||
| 171 | return Extent3D{ | ||
| 172 | .width = info.block.width, | ||
| 173 | .height = info.block.height, | ||
| 174 | .depth = info.block.depth, | ||
| 175 | }; | ||
| 176 | } | ||
| 170 | const Extent3D blocks = NumLevelBlocks(info, level); | 177 | const Extent3D blocks = NumLevelBlocks(info, level); |
| 171 | return Extent3D{ | 178 | return Extent3D{ |
| 172 | .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width), | 179 | .width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width), |
| @@ -1293,9 +1300,9 @@ u32 MapSizeBytes(const ImageBase& image) { | |||
| 1293 | 1300 | ||
| 1294 | static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) == | 1301 | static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) == |
| 1295 | 0x7f8000); | 1302 | 0x7f8000); |
| 1296 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x4000); | 1303 | static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x40000); |
| 1297 | 1304 | ||
| 1298 | static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x4000); | 1305 | static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x40000); |
| 1299 | 1306 | ||
| 1300 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == | 1307 | static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == |
| 1301 | 0x2afc00); | 1308 | 0x2afc00); |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 9ebece907..34208ed74 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE) | |||
| 384 | discord_impl.cpp | 384 | discord_impl.cpp |
| 385 | discord_impl.h | 385 | discord_impl.h |
| 386 | ) | 386 | ) |
| 387 | target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib) | 387 | target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network) |
| 388 | target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) | 388 | target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) |
| 389 | endif() | 389 | endif() |
| 390 | 390 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1de093447..d5157c502 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -128,8 +128,8 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ | |||
| 128 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, | 128 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, |
| 129 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, | 129 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, |
| 130 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, | 130 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, |
| 131 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}}, | 131 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, |
| 132 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}}, | 132 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, |
| 133 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | 133 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, |
| 134 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | 134 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, |
| 135 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, | 135 | {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index e8f9ebfd8..5a48e388b 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -115,17 +115,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 115 | for (std::size_t i = 0; i < player_tabs.size(); ++i) { | 115 | for (std::size_t i = 0; i < player_tabs.size(); ++i) { |
| 116 | player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); | 116 | player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); |
| 117 | player_tabs[i]->layout()->addWidget(player_controllers[i]); | 117 | player_tabs[i]->layout()->addWidget(player_controllers[i]); |
| 118 | connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { | 118 | connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) { |
| 119 | // Ensures that the controllers are always connected in sequential order | 119 | // Ensures that the controllers are always connected in sequential order |
| 120 | if (is_connected) { | 120 | this->propagateMouseClickOnPlayers(i, checked, true); |
| 121 | for (std::size_t index = 0; index <= i; ++index) { | ||
| 122 | player_connected[index]->setChecked(is_connected); | ||
| 123 | } | ||
| 124 | } else { | ||
| 125 | for (std::size_t index = i; index < player_tabs.size(); ++index) { | ||
| 126 | player_connected[index]->setChecked(is_connected); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | }); | 121 | }); |
| 130 | connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, | 122 | connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, |
| 131 | &ConfigureInput::UpdateAllInputDevices); | 123 | &ConfigureInput::UpdateAllInputDevices); |
| @@ -183,6 +175,30 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | |||
| 183 | LoadConfiguration(); | 175 | LoadConfiguration(); |
| 184 | } | 176 | } |
| 185 | 177 | ||
| 178 | void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) { | ||
| 179 | // Origin has already been toggled | ||
| 180 | if (!origin) { | ||
| 181 | player_connected[player_index]->setChecked(checked); | ||
| 182 | } | ||
| 183 | |||
| 184 | if (checked) { | ||
| 185 | // Check all previous buttons when checked | ||
| 186 | if (player_index > 0) { | ||
| 187 | propagateMouseClickOnPlayers(player_index - 1, checked, false); | ||
| 188 | } | ||
| 189 | } else { | ||
| 190 | // Unchecked all following buttons when unchecked | ||
| 191 | if (player_index < player_tabs.size() - 1) { | ||
| 192 | // Reconnect current player if it was the last one checked | ||
| 193 | // (player number was reduced by more than one) | ||
| 194 | if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) { | ||
| 195 | player_connected[player_index]->setCheckState(Qt::Checked); | ||
| 196 | } | ||
| 197 | propagateMouseClickOnPlayers(player_index + 1, checked, false); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 186 | QList<QWidget*> ConfigureInput::GetSubTabs() const { | 202 | QList<QWidget*> ConfigureInput::GetSubTabs() const { |
| 187 | return { | 203 | return { |
| 188 | ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, | 204 | ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, |
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index c89189c36..abb7f7089 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h | |||
| @@ -56,6 +56,7 @@ private: | |||
| 56 | void UpdateDockedState(bool is_handheld); | 56 | void UpdateDockedState(bool is_handheld); |
| 57 | void UpdateAllInputDevices(); | 57 | void UpdateAllInputDevices(); |
| 58 | void UpdateAllInputProfiles(std::size_t player_index); | 58 | void UpdateAllInputProfiles(std::size_t player_index); |
| 59 | void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked); | ||
| 59 | 60 | ||
| 60 | /// Load configuration settings. | 61 | /// Load configuration settings. |
| 61 | void LoadConfiguration(); | 62 | void LoadConfiguration(); |
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a4e8af1b4..3fe448f27 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp | |||
| @@ -157,6 +157,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { | |||
| 157 | INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); | 157 | INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); |
| 158 | INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); | 158 | INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); |
| 159 | INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); | 159 | INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); |
| 160 | INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); | ||
| 160 | INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); | 161 | INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); |
| 161 | INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); | 162 | INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); |
| 162 | 163 | ||
| @@ -383,6 +384,13 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) { | |||
| 383 | translations->insert( | 384 | translations->insert( |
| 384 | {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), | 385 | {Settings::EnumMetadata<Settings::ConsoleMode>::Index(), |
| 385 | {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); | 386 | {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); |
| 387 | translations->insert( | ||
| 388 | {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), | ||
| 389 | { | ||
| 390 | PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), | ||
| 391 | PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), | ||
| 392 | PAIR(ConfirmStop, Ask_Never, "Never ask"), | ||
| 393 | }}); | ||
| 386 | 394 | ||
| 387 | #undef PAIR | 395 | #undef PAIR |
| 388 | #undef CTX_PAIR | 396 | #undef CTX_PAIR |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 5427758c1..1431cf2fe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -67,6 +67,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 67 | #define QT_NO_OPENGL | 67 | #define QT_NO_OPENGL |
| 68 | #include <QClipboard> | 68 | #include <QClipboard> |
| 69 | #include <QDesktopServices> | 69 | #include <QDesktopServices> |
| 70 | #include <QDir> | ||
| 70 | #include <QFile> | 71 | #include <QFile> |
| 71 | #include <QFileDialog> | 72 | #include <QFileDialog> |
| 72 | #include <QInputDialog> | 73 | #include <QInputDialog> |
| @@ -76,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 76 | #include <QPushButton> | 77 | #include <QPushButton> |
| 77 | #include <QScreen> | 78 | #include <QScreen> |
| 78 | #include <QShortcut> | 79 | #include <QShortcut> |
| 80 | #include <QStandardPaths> | ||
| 79 | #include <QStatusBar> | 81 | #include <QStatusBar> |
| 80 | #include <QString> | 82 | #include <QString> |
| 81 | #include <QSysInfo> | 83 | #include <QSysInfo> |
| @@ -209,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() { | |||
| 209 | tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " | 211 | tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " |
| 210 | "data is collected</a> to help improve yuzu. " | 212 | "data is collected</a> to help improve yuzu. " |
| 211 | "<br/><br/>Would you like to share your usage data with us?"); | 213 | "<br/><br/>Would you like to share your usage data with us?"); |
| 212 | if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { | 214 | if (!question(this, tr("Telemetry"), telemetry_message)) { |
| 213 | Settings::values.enable_telemetry = false; | 215 | Settings::values.enable_telemetry = false; |
| 214 | system->ApplySettings(); | 216 | system->ApplySettings(); |
| 215 | } | 217 | } |
| @@ -2418,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT | |||
| 2418 | } | 2420 | } |
| 2419 | }(); | 2421 | }(); |
| 2420 | 2422 | ||
| 2421 | if (QMessageBox::question(this, tr("Remove Entry"), entry_question, | 2423 | if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No, |
| 2422 | QMessageBox::Yes | QMessageBox::No, | 2424 | QMessageBox::No)) { |
| 2423 | QMessageBox::No) != QMessageBox::Yes) { | ||
| 2424 | return; | 2425 | return; |
| 2425 | } | 2426 | } |
| 2426 | 2427 | ||
| @@ -2519,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ | |||
| 2519 | } | 2520 | } |
| 2520 | }(); | 2521 | }(); |
| 2521 | 2522 | ||
| 2522 | if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, | 2523 | if (!GMainWindow::question(this, tr("Remove File"), question, |
| 2523 | QMessageBox::No) != QMessageBox::Yes) { | 2524 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { |
| 2524 | return; | 2525 | return; |
| 2525 | } | 2526 | } |
| 2526 | 2527 | ||
| @@ -2869,44 +2870,50 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2869 | #endif // __linux__ | 2870 | #endif // __linux__ |
| 2870 | 2871 | ||
| 2871 | std::filesystem::path target_directory{}; | 2872 | std::filesystem::path target_directory{}; |
| 2872 | // Determine target directory for shortcut | ||
| 2873 | #if defined(WIN32) | ||
| 2874 | const char* home = std::getenv("USERPROFILE"); | ||
| 2875 | #else | ||
| 2876 | const char* home = std::getenv("HOME"); | ||
| 2877 | #endif | ||
| 2878 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | ||
| 2879 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | ||
| 2880 | 2873 | ||
| 2881 | if (target == GameListShortcutTarget::Desktop) { | 2874 | switch (target) { |
| 2882 | target_directory = home_path / "Desktop"; | 2875 | case GameListShortcutTarget::Desktop: { |
| 2883 | if (!Common::FS::IsDir(target_directory)) { | 2876 | const QString desktop_path = |
| 2884 | QMessageBox::critical( | 2877 | QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); |
| 2885 | this, tr("Create Shortcut"), | 2878 | target_directory = desktop_path.toUtf8().toStdString(); |
| 2886 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") | 2879 | break; |
| 2887 | .arg(QString::fromStdString(target_directory.generic_string())), | 2880 | } |
| 2888 | QMessageBox::StandardButton::Ok); | 2881 | case GameListShortcutTarget::Applications: { |
| 2889 | return; | 2882 | const QString applications_path = |
| 2890 | } | 2883 | QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); |
| 2891 | } else if (target == GameListShortcutTarget::Applications) { | 2884 | if (applications_path.isEmpty()) { |
| 2892 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / | 2885 | const char* home = std::getenv("HOME"); |
| 2893 | "applications"; | 2886 | if (home != nullptr) { |
| 2894 | if (!Common::FS::CreateDirs(target_directory)) { | 2887 | target_directory = std::filesystem::path(home) / ".local/share/applications"; |
| 2895 | QMessageBox::critical( | 2888 | } |
| 2896 | this, tr("Create Shortcut"), | 2889 | } else { |
| 2897 | tr("Cannot create shortcut in applications menu. Path \"%1\" " | 2890 | target_directory = applications_path.toUtf8().toStdString(); |
| 2898 | "does not exist and cannot be created.") | ||
| 2899 | .arg(QString::fromStdString(target_directory.generic_string())), | ||
| 2900 | QMessageBox::StandardButton::Ok); | ||
| 2901 | return; | ||
| 2902 | } | 2891 | } |
| 2892 | break; | ||
| 2893 | } | ||
| 2894 | default: | ||
| 2895 | return; | ||
| 2896 | } | ||
| 2897 | |||
| 2898 | const QDir dir(QString::fromStdString(target_directory.generic_string())); | ||
| 2899 | if (!dir.exists()) { | ||
| 2900 | QMessageBox::critical(this, tr("Create Shortcut"), | ||
| 2901 | tr("Cannot create shortcut. Path \"%1\" does not exist.") | ||
| 2902 | .arg(QString::fromStdString(target_directory.generic_string())), | ||
| 2903 | QMessageBox::StandardButton::Ok); | ||
| 2904 | return; | ||
| 2903 | } | 2905 | } |
| 2904 | 2906 | ||
| 2905 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | 2907 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); |
| 2906 | // Determine full paths for icon and shortcut | 2908 | // Determine full paths for icon and shortcut |
| 2907 | #if defined(__linux__) || defined(__FreeBSD__) | 2909 | #if defined(__linux__) || defined(__FreeBSD__) |
| 2910 | const char* home = std::getenv("HOME"); | ||
| 2911 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | ||
| 2912 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | ||
| 2913 | |||
| 2908 | std::filesystem::path system_icons_path = | 2914 | std::filesystem::path system_icons_path = |
| 2909 | (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / | 2915 | (xdg_data_home == nullptr ? home_path / ".local/share/" |
| 2916 | : std::filesystem::path(xdg_data_home)) / | ||
| 2910 | "icons/hicolor/256x256"; | 2917 | "icons/hicolor/256x256"; |
| 2911 | if (!Common::FS::CreateDirs(system_icons_path)) { | 2918 | if (!Common::FS::CreateDirs(system_icons_path)) { |
| 2912 | QMessageBox::critical( | 2919 | QMessageBox::critical( |
| @@ -3401,10 +3408,13 @@ void GMainWindow::OnRestartGame() { | |||
| 3401 | if (!system->IsPoweredOn()) { | 3408 | if (!system->IsPoweredOn()) { |
| 3402 | return; | 3409 | return; |
| 3403 | } | 3410 | } |
| 3404 | // Make a copy since ShutdownGame edits game_path | 3411 | |
| 3405 | const auto current_game = QString(current_game_path); | 3412 | if (ConfirmShutdownGame()) { |
| 3406 | ShutdownGame(); | 3413 | // Make a copy since ShutdownGame edits game_path |
| 3407 | BootGame(current_game); | 3414 | const auto current_game = QString(current_game_path); |
| 3415 | ShutdownGame(); | ||
| 3416 | BootGame(current_game); | ||
| 3417 | } | ||
| 3408 | } | 3418 | } |
| 3409 | 3419 | ||
| 3410 | void GMainWindow::OnPauseGame() { | 3420 | void GMainWindow::OnPauseGame() { |
| @@ -3426,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() { | |||
| 3426 | } | 3436 | } |
| 3427 | 3437 | ||
| 3428 | void GMainWindow::OnStopGame() { | 3438 | void GMainWindow::OnStopGame() { |
| 3429 | if (system->GetExitLocked() && !ConfirmForceLockedExit()) { | 3439 | if (ConfirmShutdownGame()) { |
| 3430 | return; | 3440 | play_time_manager->Stop(); |
| 3441 | // Update game list to show new play time | ||
| 3442 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 3443 | if (OnShutdownBegin()) { | ||
| 3444 | OnShutdownBeginDialog(); | ||
| 3445 | } else { | ||
| 3446 | OnEmulationStopped(); | ||
| 3447 | } | ||
| 3431 | } | 3448 | } |
| 3449 | } | ||
| 3432 | 3450 | ||
| 3433 | play_time_manager->Stop(); | 3451 | bool GMainWindow::ConfirmShutdownGame() { |
| 3434 | // Update game list to show new play time | 3452 | if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) { |
| 3435 | game_list->PopulateAsync(UISettings::values.game_dirs); | 3453 | if (system->GetExitLocked()) { |
| 3436 | if (OnShutdownBegin()) { | 3454 | if (!ConfirmForceLockedExit()) { |
| 3437 | OnShutdownBeginDialog(); | 3455 | return false; |
| 3456 | } | ||
| 3457 | } else { | ||
| 3458 | if (!ConfirmChangeGame()) { | ||
| 3459 | return false; | ||
| 3460 | } | ||
| 3461 | } | ||
| 3438 | } else { | 3462 | } else { |
| 3439 | OnEmulationStopped(); | 3463 | if (UISettings::values.confirm_before_stopping.GetValue() == |
| 3464 | ConfirmStop::Ask_Based_On_Game && | ||
| 3465 | system->GetExitLocked()) { | ||
| 3466 | if (!ConfirmForceLockedExit()) { | ||
| 3467 | return false; | ||
| 3468 | } | ||
| 3469 | } | ||
| 3440 | } | 3470 | } |
| 3471 | return true; | ||
| 3441 | } | 3472 | } |
| 3442 | 3473 | ||
| 3443 | void GMainWindow::OnLoadComplete() { | 3474 | void GMainWindow::OnLoadComplete() { |
| @@ -3817,22 +3848,11 @@ void GMainWindow::OnTasRecord() { | |||
| 3817 | const bool is_recording = input_subsystem->GetTas()->Record(); | 3848 | const bool is_recording = input_subsystem->GetTas()->Record(); |
| 3818 | if (!is_recording) { | 3849 | if (!is_recording) { |
| 3819 | is_tas_recording_dialog_active = true; | 3850 | is_tas_recording_dialog_active = true; |
| 3820 | ControllerNavigation* controller_navigation = | 3851 | |
| 3821 | new ControllerNavigation(system->HIDCore(), this); | 3852 | bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), |
| 3822 | // Use QMessageBox instead of question so we can link controller navigation | 3853 | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |
| 3823 | QMessageBox* box_dialog = new QMessageBox(); | 3854 | |
| 3824 | box_dialog->setWindowTitle(tr("TAS Recording")); | 3855 | input_subsystem->GetTas()->SaveRecording(answer); |
| 3825 | box_dialog->setText(tr("Overwrite file of player 1?")); | ||
| 3826 | box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); | ||
| 3827 | box_dialog->setDefaultButton(QMessageBox::Yes); | ||
| 3828 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 3829 | [box_dialog](Qt::Key key) { | ||
| 3830 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 3831 | QCoreApplication::postEvent(box_dialog, event); | ||
| 3832 | }); | ||
| 3833 | int res = box_dialog->exec(); | ||
| 3834 | controller_navigation->UnloadController(); | ||
| 3835 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | ||
| 3836 | is_tas_recording_dialog_active = false; | 3856 | is_tas_recording_dialog_active = false; |
| 3837 | } | 3857 | } |
| 3838 | OnTasStateChanged(); | 3858 | OnTasStateChanged(); |
| @@ -4073,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 4073 | LoadAmiibo(filename); | 4093 | LoadAmiibo(filename); |
| 4074 | } | 4094 | } |
| 4075 | 4095 | ||
| 4096 | bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, | ||
| 4097 | QMessageBox::StandardButtons buttons, | ||
| 4098 | QMessageBox::StandardButton defaultButton) { | ||
| 4099 | |||
| 4100 | QMessageBox* box_dialog = new QMessageBox(parent); | ||
| 4101 | box_dialog->setWindowTitle(title); | ||
| 4102 | box_dialog->setText(text); | ||
| 4103 | box_dialog->setStandardButtons(buttons); | ||
| 4104 | box_dialog->setDefaultButton(defaultButton); | ||
| 4105 | |||
| 4106 | ControllerNavigation* controller_navigation = | ||
| 4107 | new ControllerNavigation(system->HIDCore(), box_dialog); | ||
| 4108 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 4109 | [box_dialog](Qt::Key key) { | ||
| 4110 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 4111 | QCoreApplication::postEvent(box_dialog, event); | ||
| 4112 | }); | ||
| 4113 | int res = box_dialog->exec(); | ||
| 4114 | |||
| 4115 | controller_navigation->UnloadController(); | ||
| 4116 | return res == QMessageBox::Yes; | ||
| 4117 | } | ||
| 4118 | |||
| 4076 | void GMainWindow::LoadAmiibo(const QString& filename) { | 4119 | void GMainWindow::LoadAmiibo(const QString& filename) { |
| 4077 | auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); | 4120 | auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); |
| 4078 | const QString title = tr("Error loading Amiibo data"); | 4121 | const QString title = tr("Error loading Amiibo data"); |
| @@ -4806,8 +4849,7 @@ bool GMainWindow::ConfirmClose() { | |||
| 4806 | return true; | 4849 | return true; |
| 4807 | } | 4850 | } |
| 4808 | const auto text = tr("Are you sure you want to close yuzu?"); | 4851 | const auto text = tr("Are you sure you want to close yuzu?"); |
| 4809 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); | 4852 | return question(this, tr("yuzu"), text); |
| 4810 | return answer != QMessageBox::No; | ||
| 4811 | } | 4853 | } |
| 4812 | 4854 | ||
| 4813 | void GMainWindow::closeEvent(QCloseEvent* event) { | 4855 | void GMainWindow::closeEvent(QCloseEvent* event) { |
| @@ -4900,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() { | |||
| 4900 | if (emu_thread == nullptr) | 4942 | if (emu_thread == nullptr) |
| 4901 | return true; | 4943 | return true; |
| 4902 | 4944 | ||
| 4903 | const auto answer = QMessageBox::question( | 4945 | // Use custom question to link controller navigation |
| 4946 | return question( | ||
| 4904 | this, tr("yuzu"), | 4947 | this, tr("yuzu"), |
| 4905 | tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), | 4948 | tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), |
| 4906 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 4949 | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |
| 4907 | return answer != QMessageBox::No; | ||
| 4908 | } | 4950 | } |
| 4909 | 4951 | ||
| 4910 | bool GMainWindow::ConfirmForceLockedExit() { | 4952 | bool GMainWindow::ConfirmForceLockedExit() { |
| @@ -4914,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() { | |||
| 4914 | const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" | 4956 | const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" |
| 4915 | "Would you like to bypass this and exit anyway?"); | 4957 | "Would you like to bypass this and exit anyway?"); |
| 4916 | 4958 | ||
| 4917 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); | 4959 | return question(this, tr("yuzu"), text); |
| 4918 | return answer != QMessageBox::No; | ||
| 4919 | } | 4960 | } |
| 4920 | 4961 | ||
| 4921 | void GMainWindow::RequestGameExit() { | 4962 | void GMainWindow::RequestGameExit() { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 2346eb3bd..270a40c5f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <optional> | 7 | #include <optional> |
| 8 | 8 | ||
| 9 | #include <QMainWindow> | 9 | #include <QMainWindow> |
| 10 | #include <QMessageBox> | ||
| 10 | #include <QTimer> | 11 | #include <QTimer> |
| 11 | #include <QTranslator> | 12 | #include <QTranslator> |
| 12 | 13 | ||
| @@ -15,6 +16,7 @@ | |||
| 15 | #include "input_common/drivers/tas_input.h" | 16 | #include "input_common/drivers/tas_input.h" |
| 16 | #include "yuzu/compatibility_list.h" | 17 | #include "yuzu/compatibility_list.h" |
| 17 | #include "yuzu/hotkeys.h" | 18 | #include "yuzu/hotkeys.h" |
| 19 | #include "yuzu/util/controller_navigation.h" | ||
| 18 | 20 | ||
| 19 | #ifdef __unix__ | 21 | #ifdef __unix__ |
| 20 | #include <QVariant> | 22 | #include <QVariant> |
| @@ -424,6 +426,11 @@ private: | |||
| 424 | bool CheckSystemArchiveDecryption(); | 426 | bool CheckSystemArchiveDecryption(); |
| 425 | bool CheckFirmwarePresence(); | 427 | bool CheckFirmwarePresence(); |
| 426 | void ConfigureFilesystemProvider(const std::string& filepath); | 428 | void ConfigureFilesystemProvider(const std::string& filepath); |
| 429 | /** | ||
| 430 | * Open (or not) the right confirm dialog based on current setting and game exit lock | ||
| 431 | * @returns true if the player confirmed or the settings do no require it | ||
| 432 | */ | ||
| 433 | bool ConfirmShutdownGame(); | ||
| 427 | 434 | ||
| 428 | QString GetTasStateDescription() const; | 435 | QString GetTasStateDescription() const; |
| 429 | bool CreateShortcut(const std::string& shortcut_path, const std::string& title, | 436 | bool CreateShortcut(const std::string& shortcut_path, const std::string& title, |
| @@ -431,6 +438,17 @@ private: | |||
| 431 | const std::string& command, const std::string& arguments, | 438 | const std::string& command, const std::string& arguments, |
| 432 | const std::string& categories, const std::string& keywords); | 439 | const std::string& categories, const std::string& keywords); |
| 433 | 440 | ||
| 441 | /** | ||
| 442 | * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog | ||
| 443 | * The only difference is that it returns a boolean. | ||
| 444 | * | ||
| 445 | * @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button. | ||
| 446 | */ | ||
| 447 | bool question(QWidget* parent, const QString& title, const QString& text, | ||
| 448 | QMessageBox::StandardButtons buttons = | ||
| 449 | QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), | ||
| 450 | QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); | ||
| 451 | |||
| 434 | std::unique_ptr<Ui::MainWindow> ui; | 452 | std::unique_ptr<Ui::MainWindow> ui; |
| 435 | 453 | ||
| 436 | std::unique_ptr<Core::System> system; | 454 | std::unique_ptr<Core::System> system; |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 975008159..b62ff620c 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -16,7 +16,9 @@ | |||
| 16 | #include "common/settings_enums.h" | 16 | #include "common/settings_enums.h" |
| 17 | 17 | ||
| 18 | using Settings::Category; | 18 | using Settings::Category; |
| 19 | using Settings::ConfirmStop; | ||
| 19 | using Settings::Setting; | 20 | using Settings::Setting; |
| 21 | using Settings::SwitchableSetting; | ||
| 20 | 22 | ||
| 21 | #ifndef CANNOT_EXPLICITLY_INSTANTIATE | 23 | #ifndef CANNOT_EXPLICITLY_INSTANTIATE |
| 22 | namespace Settings { | 24 | namespace Settings { |
| @@ -94,6 +96,15 @@ struct Values { | |||
| 94 | Setting<bool> confirm_before_closing{ | 96 | Setting<bool> confirm_before_closing{ |
| 95 | linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, | 97 | linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, |
| 96 | true, true}; | 98 | true, true}; |
| 99 | |||
| 100 | SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage, | ||
| 101 | ConfirmStop::Ask_Always, | ||
| 102 | "confirmStop", | ||
| 103 | Category::UiGeneral, | ||
| 104 | Settings::Specialization::Default, | ||
| 105 | true, | ||
| 106 | true}; | ||
| 107 | |||
| 97 | Setting<bool> first_start{linkage, true, "firstStart", Category::Ui}; | 108 | Setting<bool> first_start{linkage, true, "firstStart", Category::Ui}; |
| 98 | Setting<bool> pause_when_in_background{linkage, | 109 | Setting<bool> pause_when_in_background{linkage, |
| 99 | false, | 110 | false, |