diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/android/app/build.gradle.kts | 16 | ||||
| -rw-r--r-- | src/android/app/src/main/AndroidManifest.xml | 1 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | 25 | ||||
| -rw-r--r-- | src/android/build.gradle.kts | 4 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_stream.cpp | 4 | ||||
| -rw-r--r-- | src/common/polyfill_thread.h | 21 | ||||
| -rw-r--r-- | src/common/settings.cpp | 5 | ||||
| -rw-r--r-- | src/common/settings.h | 5 | ||||
| -rw-r--r-- | src/common/settings_enums.h | 2 | ||||
| -rw-r--r-- | src/video_core/engines/draw_manager.cpp | 24 | ||||
| -rw-r--r-- | src/video_core/engines/draw_manager.h | 2 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 4 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.cpp | 36 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.h | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/shared_translation.cpp | 8 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 115 | ||||
| -rw-r--r-- | src/yuzu/main.h | 18 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 11 |
19 files changed, 216 insertions, 88 deletions
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/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/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 1a6b63856..1431cf2fe 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -211,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() { | |||
| 211 | tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " | 211 | tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " |
| 212 | "data is collected</a> to help improve yuzu. " | 212 | "data is collected</a> to help improve yuzu. " |
| 213 | "<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?"); |
| 214 | if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { | 214 | if (!question(this, tr("Telemetry"), telemetry_message)) { |
| 215 | Settings::values.enable_telemetry = false; | 215 | Settings::values.enable_telemetry = false; |
| 216 | system->ApplySettings(); | 216 | system->ApplySettings(); |
| 217 | } | 217 | } |
| @@ -2420,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT | |||
| 2420 | } | 2420 | } |
| 2421 | }(); | 2421 | }(); |
| 2422 | 2422 | ||
| 2423 | if (QMessageBox::question(this, tr("Remove Entry"), entry_question, | 2423 | if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No, |
| 2424 | QMessageBox::Yes | QMessageBox::No, | 2424 | QMessageBox::No)) { |
| 2425 | QMessageBox::No) != QMessageBox::Yes) { | ||
| 2426 | return; | 2425 | return; |
| 2427 | } | 2426 | } |
| 2428 | 2427 | ||
| @@ -2521,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ | |||
| 2521 | } | 2520 | } |
| 2522 | }(); | 2521 | }(); |
| 2523 | 2522 | ||
| 2524 | if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, | 2523 | if (!GMainWindow::question(this, tr("Remove File"), question, |
| 2525 | QMessageBox::No) != QMessageBox::Yes) { | 2524 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { |
| 2526 | return; | 2525 | return; |
| 2527 | } | 2526 | } |
| 2528 | 2527 | ||
| @@ -3409,10 +3408,13 @@ void GMainWindow::OnRestartGame() { | |||
| 3409 | if (!system->IsPoweredOn()) { | 3408 | if (!system->IsPoweredOn()) { |
| 3410 | return; | 3409 | return; |
| 3411 | } | 3410 | } |
| 3412 | // Make a copy since ShutdownGame edits game_path | 3411 | |
| 3413 | const auto current_game = QString(current_game_path); | 3412 | if (ConfirmShutdownGame()) { |
| 3414 | ShutdownGame(); | 3413 | // Make a copy since ShutdownGame edits game_path |
| 3415 | BootGame(current_game); | 3414 | const auto current_game = QString(current_game_path); |
| 3415 | ShutdownGame(); | ||
| 3416 | BootGame(current_game); | ||
| 3417 | } | ||
| 3416 | } | 3418 | } |
| 3417 | 3419 | ||
| 3418 | void GMainWindow::OnPauseGame() { | 3420 | void GMainWindow::OnPauseGame() { |
| @@ -3434,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() { | |||
| 3434 | } | 3436 | } |
| 3435 | 3437 | ||
| 3436 | void GMainWindow::OnStopGame() { | 3438 | void GMainWindow::OnStopGame() { |
| 3437 | if (system->GetExitLocked() && !ConfirmForceLockedExit()) { | 3439 | if (ConfirmShutdownGame()) { |
| 3438 | 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 | } | ||
| 3439 | } | 3448 | } |
| 3449 | } | ||
| 3440 | 3450 | ||
| 3441 | play_time_manager->Stop(); | 3451 | bool GMainWindow::ConfirmShutdownGame() { |
| 3442 | // Update game list to show new play time | 3452 | if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) { |
| 3443 | game_list->PopulateAsync(UISettings::values.game_dirs); | 3453 | if (system->GetExitLocked()) { |
| 3444 | if (OnShutdownBegin()) { | 3454 | if (!ConfirmForceLockedExit()) { |
| 3445 | OnShutdownBeginDialog(); | 3455 | return false; |
| 3456 | } | ||
| 3457 | } else { | ||
| 3458 | if (!ConfirmChangeGame()) { | ||
| 3459 | return false; | ||
| 3460 | } | ||
| 3461 | } | ||
| 3446 | } else { | 3462 | } else { |
| 3447 | 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 | } | ||
| 3448 | } | 3470 | } |
| 3471 | return true; | ||
| 3449 | } | 3472 | } |
| 3450 | 3473 | ||
| 3451 | void GMainWindow::OnLoadComplete() { | 3474 | void GMainWindow::OnLoadComplete() { |
| @@ -3825,22 +3848,11 @@ void GMainWindow::OnTasRecord() { | |||
| 3825 | const bool is_recording = input_subsystem->GetTas()->Record(); | 3848 | const bool is_recording = input_subsystem->GetTas()->Record(); |
| 3826 | if (!is_recording) { | 3849 | if (!is_recording) { |
| 3827 | is_tas_recording_dialog_active = true; | 3850 | is_tas_recording_dialog_active = true; |
| 3828 | ControllerNavigation* controller_navigation = | 3851 | |
| 3829 | new ControllerNavigation(system->HIDCore(), this); | 3852 | bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), |
| 3830 | // Use QMessageBox instead of question so we can link controller navigation | 3853 | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |
| 3831 | QMessageBox* box_dialog = new QMessageBox(); | 3854 | |
| 3832 | box_dialog->setWindowTitle(tr("TAS Recording")); | 3855 | input_subsystem->GetTas()->SaveRecording(answer); |
| 3833 | box_dialog->setText(tr("Overwrite file of player 1?")); | ||
| 3834 | box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); | ||
| 3835 | box_dialog->setDefaultButton(QMessageBox::Yes); | ||
| 3836 | connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, | ||
| 3837 | [box_dialog](Qt::Key key) { | ||
| 3838 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); | ||
| 3839 | QCoreApplication::postEvent(box_dialog, event); | ||
| 3840 | }); | ||
| 3841 | int res = box_dialog->exec(); | ||
| 3842 | controller_navigation->UnloadController(); | ||
| 3843 | input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); | ||
| 3844 | is_tas_recording_dialog_active = false; | 3856 | is_tas_recording_dialog_active = false; |
| 3845 | } | 3857 | } |
| 3846 | OnTasStateChanged(); | 3858 | OnTasStateChanged(); |
| @@ -4081,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 4081 | LoadAmiibo(filename); | 4093 | LoadAmiibo(filename); |
| 4082 | } | 4094 | } |
| 4083 | 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 | |||
| 4084 | void GMainWindow::LoadAmiibo(const QString& filename) { | 4119 | void GMainWindow::LoadAmiibo(const QString& filename) { |
| 4085 | auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); | 4120 | auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); |
| 4086 | const QString title = tr("Error loading Amiibo data"); | 4121 | const QString title = tr("Error loading Amiibo data"); |
| @@ -4814,8 +4849,7 @@ bool GMainWindow::ConfirmClose() { | |||
| 4814 | return true; | 4849 | return true; |
| 4815 | } | 4850 | } |
| 4816 | 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?"); |
| 4817 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); | 4852 | return question(this, tr("yuzu"), text); |
| 4818 | return answer != QMessageBox::No; | ||
| 4819 | } | 4853 | } |
| 4820 | 4854 | ||
| 4821 | void GMainWindow::closeEvent(QCloseEvent* event) { | 4855 | void GMainWindow::closeEvent(QCloseEvent* event) { |
| @@ -4908,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() { | |||
| 4908 | if (emu_thread == nullptr) | 4942 | if (emu_thread == nullptr) |
| 4909 | return true; | 4943 | return true; |
| 4910 | 4944 | ||
| 4911 | const auto answer = QMessageBox::question( | 4945 | // Use custom question to link controller navigation |
| 4946 | return question( | ||
| 4912 | this, tr("yuzu"), | 4947 | this, tr("yuzu"), |
| 4913 | 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."), |
| 4914 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No); | 4949 | QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); |
| 4915 | return answer != QMessageBox::No; | ||
| 4916 | } | 4950 | } |
| 4917 | 4951 | ||
| 4918 | bool GMainWindow::ConfirmForceLockedExit() { | 4952 | bool GMainWindow::ConfirmForceLockedExit() { |
| @@ -4922,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() { | |||
| 4922 | 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" |
| 4923 | "Would you like to bypass this and exit anyway?"); | 4957 | "Would you like to bypass this and exit anyway?"); |
| 4924 | 4958 | ||
| 4925 | const auto answer = QMessageBox::question(this, tr("yuzu"), text); | 4959 | return question(this, tr("yuzu"), text); |
| 4926 | return answer != QMessageBox::No; | ||
| 4927 | } | 4960 | } |
| 4928 | 4961 | ||
| 4929 | 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, |