Compare commits

..

2 Commits

Author SHA1 Message Date
5b95f66146 Android #99 2023-10-13 00:57:52 +00:00
019d648d4e Merge PR 11649 2023-10-13 00:57:52 +00:00
27 changed files with 176 additions and 275 deletions

View File

@ -11,6 +11,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
include(DownloadExternals) include(DownloadExternals)
include(CMakeDependentOption) include(CMakeDependentOption)
include(CTest) include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options. # Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON # OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
@ -98,8 +99,47 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
DESTINATION "${vvl_lib_path}") DESTINATION "${vvl_lib_path}")
endif() endif()
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID) if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON) set(CMAKE_SKIP_INSTALL_RULES ON)
set(LLVM_VERSION "15.0.6")
# Note: even though libcxx and libcxxabi have separate releases on the project page,
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
# must fetch the source release for the entire llvm tree.
FetchContent_Declare(llvm
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
TLS_VERIFY TRUE
)
FetchContent_MakeAvailable(llvm)
# libcxx has support for most of the range library, but it's gated behind a flag:
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
# Disable standard header inclusion
set(ANDROID_STL "none")
# libcxxabi
set(LIBCXXABI_INCLUDE_TESTS OFF)
set(LIBCXXABI_ENABLE_SHARED FALSE)
set(LIBCXXABI_ENABLE_STATIC TRUE)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
link_libraries(cxxabi_static)
# libcxx
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
set(LIBCXX_CXX_ABI "libcxxabi")
set(LIBCXX_INCLUDE_TESTS OFF)
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
set(LIBCXX_INCLUDE_DOCS OFF)
set(LIBCXX_ENABLE_SHARED FALSE)
set(LIBCXX_ENABLE_STATIC TRUE)
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
link_libraries(cxx_static cxx-headers)
endif() endif()
if (YUZU_USE_BUNDLED_VCPKG) if (YUZU_USE_BUNDLED_VCPKG)
@ -289,7 +329,7 @@ find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE) find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED) find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader) find_package(inih 52 MODULE COMPONENTS INIReader)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(LLVM 17 MODULE COMPONENTS Demangle)
find_package(lz4 REQUIRED) find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE) find_package(Opus 1.3 MODULE)
@ -360,9 +400,6 @@ function(set_yuzu_qt_components)
if (ENABLE_QT_TRANSLATION) if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif() endif()
if (USE_DISCORD_PRESENCE)
list(APPEND YUZU_QT_COMPONENTS2 Network)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components) endfunction(set_yuzu_qt_components)

View File

@ -1,5 +1,6 @@
| Pull Request | Commit | Title | Author | Merged? | | Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----| |----|----|----|----|----|
| [11649](https://github.com/yuzu-emu/yuzu//pull/11649) | [`a5fb9de6f`](https://github.com/yuzu-emu/yuzu//pull/11649/files) | android: Driver manager | [t895](https://github.com/t895/) | Yes |
End of merge log. You can find the original README.md below the break. End of merge log. You can find the original README.md below the break.

View File

@ -27,7 +27,7 @@ android {
namespace = "org.yuzu.yuzu_emu" namespace = "org.yuzu.yuzu_emu"
compileSdkVersion = "android-34" compileSdkVersion = "android-34"
ndkVersion = "26.1.10909125" ndkVersion = "25.2.9519653"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
@ -203,23 +203,23 @@ ktlint {
} }
dependencies { dependencies {
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.1") implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.6.1") implementation("androidx.fragment:fragment-ktx:1.6.0")
implementation("androidx.documentfile:documentfile:1.0.1") implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference:1.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4") implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4") implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4") implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("info.debatty:java-string-similarity:2.0.0") implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
} }

View File

@ -28,6 +28,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:appCategory="game" android:appCategory="game"
android:localeConfig="@xml/locales_config" android:localeConfig="@xml/locales_config"
android:banner="@drawable/tv_banner" android:banner="@drawable/tv_banner"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/data_extraction_rules" android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31" android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">

View File

@ -26,7 +26,6 @@ import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.File
import java.io.IOException import java.io.IOException
class DriverManagerFragment : Fragment() { class DriverManagerFragment : Fragment() {
@ -155,29 +154,29 @@ class DriverManagerFragment : Fragment() {
R.string.installing_driver, R.string.installing_driver,
false false
) { ) {
val driverPath =
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip // Ignore file exceptions when a user selects an invalid zip
try { try {
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) { GpuDriverHelper.copyDriverToInternalStorage(result)
throw IOException("Driver failed validation!")
}
} catch (_: IOException) { } catch (_: IOException) {
if (driverFile.exists()) {
driverFile.delete()
}
return@newInstance getString(R.string.select_gpu_driver_error) return@newInstance getString(R.string.select_gpu_driver_error)
} }
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile) val driverData = GpuDriverHelper.customDriverData
if (driverData.name == null) {
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverInList = val driverInList =
driverViewModel.driverList.value.firstOrNull { it.second == driverData } driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) { if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed) return@newInstance getString(R.string.driver_already_installed)
} else { } else {
driverViewModel.addDriver(Pair(driverPath, driverData)) driverViewModel.addDriver(
Pair(
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
driverData
)
)
driverViewModel.setNewDriverInstalled(true) driverViewModel.setNewDriverInstalled(true)
} }
return@newInstance Any() return@newInstance Any()

View File

@ -3,8 +3,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id("com.android.application") version "8.1.2" apply false id("com.android.application") version "8.0.2" apply false
id("com.android.library") version "8.1.2" apply false id("com.android.library") version "8.0.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.21" apply false id("org.jetbrains.kotlin.android") version "1.8.21" apply false
} }

View File

@ -204,10 +204,6 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
// paused and we'll desync, so just play silence. // paused and we'll desync, so just play silence.
if (system.IsPaused() || system.IsShuttingDown()) { if (system.IsPaused() || system.IsShuttingDown()) {
if (system.IsShuttingDown()) { if (system.IsShuttingDown()) {
{
std::scoped_lock lk{release_mutex};
queued_buffers.store(0);
}
release_cv.notify_one(); release_cv.notify_one();
} }

View File

@ -15,13 +15,12 @@
#include <condition_variable> #include <condition_variable>
#include <stop_token> #include <stop_token>
#include <thread> #include <thread>
#include <utility>
namespace Common { namespace Common {
template <typename Condvar, typename Lock, typename Pred> template <typename Condvar, typename Lock, typename Pred>
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) { void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
cv.wait(lk, token, std::forward<Pred>(pred)); cv.wait(lk, token, std::move(pred));
} }
template <typename Rep, typename Period> template <typename Rep, typename Period>
@ -110,7 +109,7 @@ public:
// Insert the callback. // Insert the callback.
stop_state_callback ret = ++m_next_callback; stop_state_callback ret = ++m_next_callback;
m_callbacks.emplace(ret, std::move(f)); m_callbacks.emplace(ret, move(f));
return ret; return ret;
} }
@ -163,7 +162,7 @@ private:
friend class stop_source; friend class stop_source;
template <typename Callback> template <typename Callback>
friend class stop_callback; friend class stop_callback;
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {} stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
private: private:
shared_ptr<polyfill::stop_state> m_stop_state; shared_ptr<polyfill::stop_state> m_stop_state;
@ -199,7 +198,7 @@ public:
private: private:
friend class jthread; friend class jthread;
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state) explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
: m_stop_state(std::move(stop_state)) {} : m_stop_state(move(stop_state)) {}
private: private:
shared_ptr<polyfill::stop_state> m_stop_state; shared_ptr<polyfill::stop_state> m_stop_state;
@ -219,16 +218,16 @@ public:
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
: m_stop_state(st.m_stop_state) { : m_stop_state(st.m_stop_state) {
if (m_stop_state) { if (m_stop_state) {
m_callback = m_stop_state->insert_callback(std::move(cb)); m_callback = m_stop_state->insert_callback(move(cb));
} }
} }
template <typename C> template <typename C>
requires constructible_from<Callback, C> requires constructible_from<Callback, C>
explicit stop_callback(stop_token&& st, explicit stop_callback(stop_token&& st,
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>) C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
: m_stop_state(std::move(st.m_stop_state)) { : m_stop_state(move(st.m_stop_state)) {
if (m_stop_state) { if (m_stop_state) {
m_callback = m_stop_state->insert_callback(std::move(cb)); m_callback = m_stop_state->insert_callback(move(cb));
} }
} }
~stop_callback() { ~stop_callback() {
@ -261,7 +260,7 @@ public:
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>> typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
explicit jthread(F&& f, Args&&... args) explicit jthread(F&& f, Args&&... args)
: m_stop_state(make_shared<polyfill::stop_state>()), : m_stop_state(make_shared<polyfill::stop_state>()),
m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {} m_thread(make_thread(move(f), move(args)...)) {}
~jthread() { ~jthread() {
if (joinable()) { if (joinable()) {
@ -318,9 +317,9 @@ private:
template <typename F, typename... Args> template <typename F, typename... Args>
thread make_thread(F&& f, Args&&... args) { thread make_thread(F&& f, Args&&... args) {
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) { if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...); return thread(move(f), get_stop_token(), move(args)...);
} else { } else {
return thread(std::forward<F>(f), std::forward<Args>(args)...); return thread(move(f), move(args)...);
} }
} }

View File

@ -45,7 +45,6 @@ SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
SWITCHABLE(Language, true); SWITCHABLE(Language, true);
SWITCHABLE(MemoryLayout, true);
SWITCHABLE(NvdecEmulation, false); SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true); SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true); SWITCHABLE(RendererBackend, true);
@ -62,10 +61,6 @@ SWITCHABLE(u32, false);
SWITCHABLE(u8, false); SWITCHABLE(u8, false);
SWITCHABLE(u8, true); SWITCHABLE(u8, true);
// Used in UISettings
// TODO see if we can move this to uisettings.cpp
SWITCHABLE(ConfirmStop, true);
#undef SETTING #undef SETTING
#undef SWITCHABLE #undef SWITCHABLE
#endif #endif

View File

@ -67,7 +67,6 @@ SWITCHABLE(CpuAccuracy, true);
SWITCHABLE(FullscreenMode, true); SWITCHABLE(FullscreenMode, true);
SWITCHABLE(GpuAccuracy, true); SWITCHABLE(GpuAccuracy, true);
SWITCHABLE(Language, true); SWITCHABLE(Language, true);
SWITCHABLE(MemoryLayout, true);
SWITCHABLE(NvdecEmulation, false); SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true); SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true); SWITCHABLE(RendererBackend, true);
@ -84,10 +83,6 @@ SWITCHABLE(u32, false);
SWITCHABLE(u8, false); SWITCHABLE(u8, false);
SWITCHABLE(u8, true); SWITCHABLE(u8, true);
// Used in UISettings
// TODO see if we can move this to uisettings.h
SWITCHABLE(ConfirmStop, true);
#undef SETTING #undef SETTING
#undef SWITCHABLE #undef SWITCHABLE
#endif #endif

View File

@ -133,8 +133,6 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
ENUM(FullscreenMode, Borderless, Exclusive); ENUM(FullscreenMode, Borderless, Exclusive);
ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(NvdecEmulation, Off, Cpu, Gpu);

View File

@ -3405,11 +3405,6 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K
new_attr, KMemoryBlockDisableMergeAttribute::Locked, new_attr, KMemoryBlockDisableMergeAttribute::Locked,
KMemoryBlockDisableMergeAttribute::None); KMemoryBlockDisableMergeAttribute::None);
// If we have an output page group, open.
if (out_pg) {
out_pg->Open();
}
R_SUCCEED(); R_SUCCEED();
} }

View File

@ -338,7 +338,6 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host,
for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
PreSetController(identifier); PreSetController(identifier);
PreSetMotion(identifier, 0);
} }
} }

View File

@ -48,14 +48,8 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
SetInlineIndexBuffer(regs.inline_index_4x8.index3); SetInlineIndexBuffer(regs.inline_index_4x8.index3);
break; break;
case MAXWELL3D_REG_INDEX(vertex_array_instance_first): case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(),
regs.vertex_array_instance_first.start.Value(),
regs.vertex_array_instance_first.count.Value(), false);
break;
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(), LOG_WARNING(HW_GPU, "(STUBBED) called");
regs.vertex_array_instance_subsequent.start.Value(),
regs.vertex_array_instance_subsequent.count.Value(), true);
break; break;
} }
case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
@ -90,22 +84,6 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve
ProcessDraw(false, num_instances); ProcessDraw(false, num_instances);
} }
void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
bool subsequent) {
draw_state.topology = topology;
draw_state.vertex_buffer.first = vertex_first;
draw_state.vertex_buffer.count = vertex_count;
if (!subsequent) {
draw_state.instance_count = 1;
}
draw_state.base_instance = draw_state.instance_count - 1;
draw_state.draw_mode = DrawMode::Instance;
draw_state.instance_count++;
ProcessDraw(false, 1);
}
void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
u32 base_index, u32 base_instance, u32 num_instances) { u32 base_index, u32 base_instance, u32 num_instances) {
const auto& regs{maxwell3d->regs}; const auto& regs{maxwell3d->regs};

View File

@ -66,8 +66,6 @@ public:
void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
u32 base_instance, u32 num_instances); u32 base_instance, u32 num_instances);
void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
bool subsequent);
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
u32 base_instance, u32 num_instances); u32 base_instance, u32 num_instances);

View File

@ -137,6 +137,16 @@ bool Codec::CreateGpuAvDevice() {
break; break;
} }
if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
#if defined(__unix__)
// Some linux decoding backends are reported to crash with this config method
// TODO(ameerj): Properly support this method
if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
// skip zero-copy decoders, we don't currently support them
LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
av_hwdevice_get_type_name(type), config->methods);
continue;
}
#endif
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
av_codec_ctx->pix_fmt = config->pix_fmt; av_codec_ctx->pix_fmt = config->pix_fmt;
return true; return true;

View File

@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE)
discord_impl.cpp discord_impl.cpp
discord_impl.h discord_impl.h
) )
target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network) target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
endif() endif()

View File

@ -128,8 +128,8 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},

View File

@ -115,9 +115,17 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
for (std::size_t i = 0; i < player_tabs.size(); ++i) { for (std::size_t i = 0; i < player_tabs.size(); ++i) {
player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
player_tabs[i]->layout()->addWidget(player_controllers[i]); player_tabs[i]->layout()->addWidget(player_controllers[i]);
connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) { connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
// Ensures that the controllers are always connected in sequential order // Ensures that the controllers are always connected in sequential order
this->propagateMouseClickOnPlayers(i, checked, true); if (is_connected) {
for (std::size_t index = 0; index <= i; ++index) {
player_connected[index]->setChecked(is_connected);
}
} else {
for (std::size_t index = i; index < player_tabs.size(); ++index) {
player_connected[index]->setChecked(is_connected);
}
}
}); });
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
&ConfigureInput::UpdateAllInputDevices); &ConfigureInput::UpdateAllInputDevices);
@ -175,30 +183,6 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
LoadConfiguration(); LoadConfiguration();
} }
void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) {
// Origin has already been toggled
if (!origin) {
player_connected[player_index]->setChecked(checked);
}
if (checked) {
// Check all previous buttons when checked
if (player_index > 0) {
propagateMouseClickOnPlayers(player_index - 1, checked, false);
}
} else {
// Unchecked all following buttons when unchecked
if (player_index < player_tabs.size() - 1) {
// Reconnect current player if it was the last one checked
// (player number was reduced by more than one)
if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) {
player_connected[player_index]->setCheckState(Qt::Checked);
}
propagateMouseClickOnPlayers(player_index + 1, checked, false);
}
}
}
QList<QWidget*> ConfigureInput::GetSubTabs() const { QList<QWidget*> ConfigureInput::GetSubTabs() const {
return { return {
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,

View File

@ -56,7 +56,6 @@ private:
void UpdateDockedState(bool is_handheld); void UpdateDockedState(bool is_handheld);
void UpdateAllInputDevices(); void UpdateAllInputDevices();
void UpdateAllInputProfiles(std::size_t player_index); void UpdateAllInputProfiles(std::size_t player_index);
void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked);
/// Load configuration settings. /// Load configuration settings.
void LoadConfiguration(); void LoadConfiguration();

View File

@ -157,7 +157,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
@ -384,13 +383,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
translations->insert( translations->insert(
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(), {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
translations->insert(
{Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
{
PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"),
PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"),
PAIR(ConfirmStop, Ask_Never, "Never ask"),
}});
#undef PAIR #undef PAIR
#undef CTX_PAIR #undef CTX_PAIR

View File

@ -826,13 +826,12 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size); tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
// Before deleting rows, cancel the worker so that it is not using them
emit ShouldCancelWorker();
// Delete any rows that might already exist if we're repopulating // Delete any rows that might already exist if we're repopulating
item_model->removeRows(0, item_model->rowCount()); item_model->removeRows(0, item_model->rowCount());
search_field->clear(); search_field->clear();
emit ShouldCancelWorker();
GameListWorker* worker = GameListWorker* worker =
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system); new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);

View File

@ -293,7 +293,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
GameListDir* parent_dir) { GameListDir* parent_dir) {
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool { const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
if (stop_requested) { if (stop_processing) {
// Breaks the callback loop. // Breaks the callback loop.
return false; return false;
} }
@ -399,6 +399,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
} }
void GameListWorker::run() { void GameListWorker::run() {
stop_processing = false;
provider->ClearAllEntries(); provider->ClearAllEntries();
for (UISettings::GameDir& game_dir : game_dirs) { for (UISettings::GameDir& game_dir : game_dirs) {
@ -426,11 +427,9 @@ void GameListWorker::run() {
} }
emit Finished(watch_list); emit Finished(watch_list);
processing_completed.Set();
} }
void GameListWorker::Cancel() { void GameListWorker::Cancel() {
this->disconnect(); this->disconnect();
stop_requested.store(true); stop_processing = true;
processing_completed.Wait();
} }

View File

@ -12,7 +12,6 @@
#include <QRunnable> #include <QRunnable>
#include <QString> #include <QString>
#include "common/thread.h"
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "yuzu/play_time_manager.h" #include "yuzu/play_time_manager.h"
@ -83,9 +82,7 @@ private:
const PlayTime::PlayTimeManager& play_time_manager; const PlayTime::PlayTimeManager& play_time_manager;
QStringList watch_list; QStringList watch_list;
std::atomic_bool stop_processing;
Common::Event processing_completed;
std::atomic_bool stop_requested = false;
Core::System& system; Core::System& system;
}; };

View File

@ -67,7 +67,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#define QT_NO_OPENGL #define QT_NO_OPENGL
#include <QClipboard> #include <QClipboard>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDir>
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QInputDialog> #include <QInputDialog>
@ -77,7 +76,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QPushButton> #include <QPushButton>
#include <QScreen> #include <QScreen>
#include <QShortcut> #include <QShortcut>
#include <QStandardPaths>
#include <QStatusBar> #include <QStatusBar>
#include <QString> #include <QString>
#include <QSysInfo> #include <QSysInfo>
@ -211,7 +209,7 @@ void GMainWindow::ShowTelemetryCallout() {
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous " tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
"data is collected</a> to help improve yuzu. " "data is collected</a> to help improve yuzu. "
"<br/><br/>Would you like to share your usage data with us?"); "<br/><br/>Would you like to share your usage data with us?");
if (!question(this, tr("Telemetry"), telemetry_message)) { if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) {
Settings::values.enable_telemetry = false; Settings::values.enable_telemetry = false;
system->ApplySettings(); system->ApplySettings();
} }
@ -2420,8 +2418,9 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
} }
}(); }();
if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No, if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
QMessageBox::No)) { QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes) {
return; return;
} }
@ -2520,8 +2519,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
} }
}(); }();
if (!GMainWindow::question(this, tr("Remove File"), question, if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { QMessageBox::No) != QMessageBox::Yes) {
return; return;
} }
@ -2870,50 +2869,44 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
#endif // __linux__ #endif // __linux__
std::filesystem::path target_directory{}; std::filesystem::path target_directory{};
// Determine target directory for shortcut
#if defined(WIN32)
const char* home = std::getenv("USERPROFILE");
#else
const char* home = std::getenv("HOME");
#endif
const std::filesystem::path home_path = (home == nullptr ? "~" : home);
const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
switch (target) { if (target == GameListShortcutTarget::Desktop) {
case GameListShortcutTarget::Desktop: { target_directory = home_path / "Desktop";
const QString desktop_path = if (!Common::FS::IsDir(target_directory)) {
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QMessageBox::critical(
target_directory = desktop_path.toUtf8().toStdString(); this, tr("Create Shortcut"),
break; tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.")
} .arg(QString::fromStdString(target_directory.generic_string())),
case GameListShortcutTarget::Applications: { QMessageBox::StandardButton::Ok);
const QString applications_path = return;
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); }
if (applications_path.isEmpty()) { } else if (target == GameListShortcutTarget::Applications) {
const char* home = std::getenv("HOME"); target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) /
if (home != nullptr) { "applications";
target_directory = std::filesystem::path(home) / ".local/share/applications"; if (!Common::FS::CreateDirs(target_directory)) {
} QMessageBox::critical(
} else { this, tr("Create Shortcut"),
target_directory = applications_path.toUtf8().toStdString(); tr("Cannot create shortcut in applications menu. Path \"%1\" "
"does not exist and cannot be created.")
.arg(QString::fromStdString(target_directory.generic_string())),
QMessageBox::StandardButton::Ok);
return;
} }
break;
}
default:
return;
}
const QDir dir(QString::fromStdString(target_directory.generic_string()));
if (!dir.exists()) {
QMessageBox::critical(this, tr("Create Shortcut"),
tr("Cannot create shortcut. Path \"%1\" does not exist.")
.arg(QString::fromStdString(target_directory.generic_string())),
QMessageBox::StandardButton::Ok);
return;
} }
const std::string game_file_name = std::filesystem::path(game_path).filename().string(); const std::string game_file_name = std::filesystem::path(game_path).filename().string();
// Determine full paths for icon and shortcut // Determine full paths for icon and shortcut
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || defined(__FreeBSD__)
const char* home = std::getenv("HOME");
const std::filesystem::path home_path = (home == nullptr ? "~" : home);
const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
std::filesystem::path system_icons_path = std::filesystem::path system_icons_path =
(xdg_data_home == nullptr ? home_path / ".local/share/" (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) /
: std::filesystem::path(xdg_data_home)) /
"icons/hicolor/256x256"; "icons/hicolor/256x256";
if (!Common::FS::CreateDirs(system_icons_path)) { if (!Common::FS::CreateDirs(system_icons_path)) {
QMessageBox::critical( QMessageBox::critical(
@ -3408,13 +3401,10 @@ void GMainWindow::OnRestartGame() {
if (!system->IsPoweredOn()) { if (!system->IsPoweredOn()) {
return; return;
} }
// Make a copy since ShutdownGame edits game_path
if (ConfirmShutdownGame()) { const auto current_game = QString(current_game_path);
// Make a copy since ShutdownGame edits game_path ShutdownGame();
const auto current_game = QString(current_game_path); BootGame(current_game);
ShutdownGame();
BootGame(current_game);
}
} }
void GMainWindow::OnPauseGame() { void GMainWindow::OnPauseGame() {
@ -3436,39 +3426,18 @@ void GMainWindow::OnPauseContinueGame() {
} }
void GMainWindow::OnStopGame() { void GMainWindow::OnStopGame() {
if (ConfirmShutdownGame()) { if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
play_time_manager->Stop(); return;
// Update game list to show new play time
game_list->PopulateAsync(UISettings::values.game_dirs);
if (OnShutdownBegin()) {
OnShutdownBeginDialog();
} else {
OnEmulationStopped();
}
} }
}
bool GMainWindow::ConfirmShutdownGame() { play_time_manager->Stop();
if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) { // Update game list to show new play time
if (system->GetExitLocked()) { game_list->PopulateAsync(UISettings::values.game_dirs);
if (!ConfirmForceLockedExit()) { if (OnShutdownBegin()) {
return false; OnShutdownBeginDialog();
}
} else {
if (!ConfirmChangeGame()) {
return false;
}
}
} else { } else {
if (UISettings::values.confirm_before_stopping.GetValue() == OnEmulationStopped();
ConfirmStop::Ask_Based_On_Game &&
system->GetExitLocked()) {
if (!ConfirmForceLockedExit()) {
return false;
}
}
} }
return true;
} }
void GMainWindow::OnLoadComplete() { void GMainWindow::OnLoadComplete() {
@ -3848,11 +3817,22 @@ void GMainWindow::OnTasRecord() {
const bool is_recording = input_subsystem->GetTas()->Record(); const bool is_recording = input_subsystem->GetTas()->Record();
if (!is_recording) { if (!is_recording) {
is_tas_recording_dialog_active = true; is_tas_recording_dialog_active = true;
ControllerNavigation* controller_navigation =
bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), new ControllerNavigation(system->HIDCore(), this);
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); // Use QMessageBox instead of question so we can link controller navigation
QMessageBox* box_dialog = new QMessageBox();
input_subsystem->GetTas()->SaveRecording(answer); box_dialog->setWindowTitle(tr("TAS Recording"));
box_dialog->setText(tr("Overwrite file of player 1?"));
box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
box_dialog->setDefaultButton(QMessageBox::Yes);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
[box_dialog](Qt::Key key) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(box_dialog, event);
});
int res = box_dialog->exec();
controller_navigation->UnloadController();
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
is_tas_recording_dialog_active = false; is_tas_recording_dialog_active = false;
} }
OnTasStateChanged(); OnTasStateChanged();
@ -4093,29 +4073,6 @@ void GMainWindow::OnLoadAmiibo() {
LoadAmiibo(filename); LoadAmiibo(filename);
} }
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton) {
QMessageBox* box_dialog = new QMessageBox(parent);
box_dialog->setWindowTitle(title);
box_dialog->setText(text);
box_dialog->setStandardButtons(buttons);
box_dialog->setDefaultButton(defaultButton);
ControllerNavigation* controller_navigation =
new ControllerNavigation(system->HIDCore(), box_dialog);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
[box_dialog](Qt::Key key) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(box_dialog, event);
});
int res = box_dialog->exec();
controller_navigation->UnloadController();
return res == QMessageBox::Yes;
}
void GMainWindow::LoadAmiibo(const QString& filename) { void GMainWindow::LoadAmiibo(const QString& filename) {
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
const QString title = tr("Error loading Amiibo data"); const QString title = tr("Error loading Amiibo data");
@ -4849,7 +4806,8 @@ bool GMainWindow::ConfirmClose() {
return true; return true;
} }
const auto text = tr("Are you sure you want to close yuzu?"); const auto text = tr("Are you sure you want to close yuzu?");
return question(this, tr("yuzu"), text); const auto answer = QMessageBox::question(this, tr("yuzu"), text);
return answer != QMessageBox::No;
} }
void GMainWindow::closeEvent(QCloseEvent* event) { void GMainWindow::closeEvent(QCloseEvent* event) {
@ -4942,11 +4900,11 @@ bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr) if (emu_thread == nullptr)
return true; return true;
// Use custom question to link controller navigation const auto answer = QMessageBox::question(
return question(
this, tr("yuzu"), this, tr("yuzu"),
tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
return answer != QMessageBox::No;
} }
bool GMainWindow::ConfirmForceLockedExit() { bool GMainWindow::ConfirmForceLockedExit() {
@ -4956,7 +4914,8 @@ bool GMainWindow::ConfirmForceLockedExit() {
const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
"Would you like to bypass this and exit anyway?"); "Would you like to bypass this and exit anyway?");
return question(this, tr("yuzu"), text); const auto answer = QMessageBox::question(this, tr("yuzu"), text);
return answer != QMessageBox::No;
} }
void GMainWindow::RequestGameExit() { void GMainWindow::RequestGameExit() {

View File

@ -7,7 +7,6 @@
#include <optional> #include <optional>
#include <QMainWindow> #include <QMainWindow>
#include <QMessageBox>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
@ -16,7 +15,6 @@
#include "input_common/drivers/tas_input.h" #include "input_common/drivers/tas_input.h"
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "yuzu/hotkeys.h" #include "yuzu/hotkeys.h"
#include "yuzu/util/controller_navigation.h"
#ifdef __unix__ #ifdef __unix__
#include <QVariant> #include <QVariant>
@ -426,11 +424,6 @@ private:
bool CheckSystemArchiveDecryption(); bool CheckSystemArchiveDecryption();
bool CheckFirmwarePresence(); bool CheckFirmwarePresence();
void ConfigureFilesystemProvider(const std::string& filepath); void ConfigureFilesystemProvider(const std::string& filepath);
/**
* Open (or not) the right confirm dialog based on current setting and game exit lock
* @returns true if the player confirmed or the settings do no require it
*/
bool ConfirmShutdownGame();
QString GetTasStateDescription() const; QString GetTasStateDescription() const;
bool CreateShortcut(const std::string& shortcut_path, const std::string& title, bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
@ -438,17 +431,6 @@ private:
const std::string& command, const std::string& arguments, const std::string& command, const std::string& arguments,
const std::string& categories, const std::string& keywords); const std::string& categories, const std::string& keywords);
/**
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
* The only difference is that it returns a boolean.
*
* @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button.
*/
bool question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
std::unique_ptr<Ui::MainWindow> ui; std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<Core::System> system; std::unique_ptr<Core::System> system;

View File

@ -16,9 +16,7 @@
#include "common/settings_enums.h" #include "common/settings_enums.h"
using Settings::Category; using Settings::Category;
using Settings::ConfirmStop;
using Settings::Setting; using Settings::Setting;
using Settings::SwitchableSetting;
#ifndef CANNOT_EXPLICITLY_INSTANTIATE #ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings { namespace Settings {
@ -96,15 +94,6 @@ struct Values {
Setting<bool> confirm_before_closing{ Setting<bool> confirm_before_closing{
linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
true, true}; true, true};
SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
ConfirmStop::Ask_Always,
"confirmStop",
Category::UiGeneral,
Settings::Specialization::Default,
true,
true};
Setting<bool> first_start{linkage, true, "firstStart", Category::Ui}; Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
Setting<bool> pause_when_in_background{linkage, Setting<bool> pause_when_in_background{linkage,
false, false,