summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt17
-rw-r--r--src/yuzu/bootmanager.cpp82
-rw-r--r--src/yuzu/bootmanager.h8
-rw-r--r--src/yuzu/configuration/config.cpp362
-rw-r--r--src/yuzu/configuration/config.h29
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp76
-rw-r--r--src/yuzu/configuration/configuration_shared.h36
-rw-r--r--src/yuzu/configuration/configure_audio.cpp82
-rw-r--r--src/yuzu/configuration/configure_audio.h2
-rw-r--r--src/yuzu/configuration/configure_audio.ui50
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui13
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp78
-rw-r--r--src/yuzu/configuration/configure_general.h2
-rw-r--r--src/yuzu/configuration/configure_general.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp201
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui70
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp107
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp63
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp140
-rw-r--r--src/yuzu/configuration/configure_per_game.h51
-rw-r--r--src/yuzu/configuration/configure_per_game.ui350
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp (renamed from src/yuzu/configuration/configure_per_general.cpp)95
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h (renamed from src/yuzu/configuration/configure_per_general.h)16
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.ui38
-rw-r--r--src/yuzu/configuration/configure_per_general.ui276
-rw-r--r--src/yuzu/configuration/configure_service.cpp6
-rw-r--r--src/yuzu/configuration/configure_system.cpp190
-rw-r--r--src/yuzu/configuration/configure_system.h2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp54
-rw-r--r--src/yuzu/main.cpp210
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/main.ui18
-rw-r--r--src/yuzu/yuzu.rc2
37 files changed, 1959 insertions, 792 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 8b9404718..6b25a7fa0 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -24,6 +24,8 @@ add_executable(yuzu
compatibility_list.h
configuration/config.cpp
configuration/config.h
+ configuration/configuration_shared.cpp
+ configuration/configuration_shared.h
configuration/configure.ui
configuration/configure_audio.cpp
configuration/configure_audio.h
@@ -60,9 +62,12 @@ add_executable(yuzu
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_mouse_advanced.ui
- configuration/configure_per_general.cpp
- configuration/configure_per_general.h
- configuration/configure_per_general.ui
+ configuration/configure_per_game.cpp
+ configuration/configure_per_game.h
+ configuration/configure_per_game.ui
+ configuration/configure_per_game_addons.cpp
+ configuration/configure_per_game_addons.h
+ configuration/configure_per_game_addons.ui
configuration/configure_profile_manager.cpp
configuration/configure_profile_manager.h
configuration/configure_profile_manager.ui
@@ -147,7 +152,7 @@ endif()
create_target_directory_groups(yuzu)
target_link_libraries(yuzu PRIVATE common core input_common video_core)
-target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
+target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (ENABLE_VULKAN AND NOT WIN32)
@@ -208,6 +213,10 @@ if (MSVC)
copy_yuzu_unicorn_deps(yuzu)
endif()
+if (NOT APPLE)
+ target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
+endif()
+
if (ENABLE_VULKAN)
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1adf8932b..5738787ac 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,13 +8,16 @@
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMessageBox>
-#include <QOffscreenSurface>
-#include <QOpenGLContext>
#include <QPainter>
#include <QScreen>
#include <QStringList>
#include <QWindow>
+#ifdef HAS_OPENGL
+#include <QOffscreenSurface>
+#include <QOpenGLContext>
+#endif
+
#if !defined(WIN32) && HAS_VULKAN
#include <qpa/qplatformnativeinterface.h>
#endif
@@ -41,49 +44,65 @@ EmuThread::EmuThread() = default;
EmuThread::~EmuThread() = default;
void EmuThread::run() {
- MicroProfileOnThreadCreate("EmuThread");
+ std::string name = "yuzu:EmuControlThread";
+ MicroProfileOnThreadCreate(name.c_str());
+ Common::SetCurrentThreadName(name.c_str());
+
+ auto& system = Core::System::GetInstance();
+
+ system.RegisterHostThread();
+
+ auto& gpu = system.GPU();
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
- Core::System::GetInstance().GPU().Start();
+ gpu.Start();
+
+ gpu.ObtainContext();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
- Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
+ system.Renderer().Rasterizer().LoadDiskResources(
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
+ gpu.ReleaseContext();
+
// Holds whether the cpu was running during the last iteration,
// so that the DebugModeLeft signal can be emitted before the
// next execution step
bool was_active = false;
while (!stop_run) {
if (running) {
- if (!was_active)
+ if (was_active) {
emit DebugModeLeft();
+ }
- Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
+ running_guard = true;
+ Core::System::ResultStatus result = system.Run();
if (result != Core::System::ResultStatus::Success) {
+ running_guard = false;
this->SetRunning(false);
- emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
+ emit ErrorThrown(result, system.GetStatusDetails());
}
+ running_wait.Wait();
+ result = system.Pause();
+ if (result != Core::System::ResultStatus::Success) {
+ running_guard = false;
+ this->SetRunning(false);
+ emit ErrorThrown(result, system.GetStatusDetails());
+ }
+ running_guard = false;
- was_active = running || exec_step;
- if (!was_active && !stop_run)
+ if (!stop_run) {
+ was_active = true;
emit DebugModeEntered();
+ }
} else if (exec_step) {
- if (!was_active)
- emit DebugModeLeft();
-
- exec_step = false;
- Core::System::GetInstance().SingleStep();
- emit DebugModeEntered();
- yieldCurrentThread();
-
- was_active = false;
+ UNIMPLEMENTED();
} else {
std::unique_lock lock{running_mutex};
running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
@@ -91,13 +110,14 @@ void EmuThread::run() {
}
// Shutdown the core emulation
- Core::System::GetInstance().Shutdown();
+ system.Shutdown();
#if MICROPROFILE_ENABLED
MicroProfileOnThreadExit();
#endif
}
+#ifdef HAS_OPENGL
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
/// Create the original context that should be shared from
@@ -106,6 +126,9 @@ public:
format.setVersion(4, 3);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
+ if (Settings::values.renderer_debug) {
+ format.setOption(QSurfaceFormat::FormatOption::DebugContext);
+ }
// TODO: expose a setting for buffer value (ie default/single/double/triple)
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
format.setSwapInterval(0);
@@ -122,7 +145,7 @@ public:
// disable vsync for any shared contexts
auto format = share_context->format();
- format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
+ format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
@@ -180,6 +203,7 @@ private:
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
};
+#endif
class DummyContext : public Core::Frontend::GraphicsContext {};
@@ -352,7 +376,7 @@ QByteArray GRenderWindow::saveGeometry() {
}
qreal GRenderWindow::windowPixelRatio() const {
- return devicePixelRatio();
+ return devicePixelRatioF();
}
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
@@ -470,13 +494,15 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
}
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
- if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
+#ifdef HAS_OPENGL
+ if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
child_widget->windowHandle());
}
+#endif
return std::make_unique<DummyContext>();
}
@@ -485,7 +511,7 @@ bool GRenderWindow::InitRenderTarget() {
first_frame = false;
- switch (Settings::values.renderer_backend) {
+ switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
if (!InitializeOpenGL()) {
return false;
@@ -512,7 +538,7 @@ bool GRenderWindow::InitRenderTarget() {
OnFramebufferSizeChanged();
BackupGeometry();
- if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
+ if (Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::OpenGL) {
if (!LoadOpenGL()) {
return false;
}
@@ -557,6 +583,7 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
}
bool GRenderWindow::InitializeOpenGL() {
+#ifdef HAS_OPENGL
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
auto child = new OpenGLRenderWidget(this);
@@ -568,6 +595,11 @@ bool GRenderWindow::InitializeOpenGL() {
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
return true;
+#else
+ QMessageBox::warning(this, tr("OpenGL not available!"),
+ tr("yuzu has not been compiled with OpenGL support."));
+ return false;
+#endif
}
bool GRenderWindow::InitializeVulkan() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 3626604ca..6c59b4d5c 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -59,6 +59,12 @@ public:
this->running = running;
lock.unlock();
running_cv.notify_all();
+ if (!running) {
+ running_wait.Set();
+ /// Wait until effectively paused
+ while (running_guard)
+ ;
+ }
}
/**
@@ -84,6 +90,8 @@ private:
std::atomic_bool stop_run{false};
std::mutex running_mutex;
std::condition_variable running_cv;
+ Common::Event running_wait{};
+ std::atomic_bool running_guard{false};
signals:
/**
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b08b87426..1b2b1b2bb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -13,17 +13,20 @@
#include "input_common/udp/client.h"
#include "yuzu/configuration/config.h"
-Config::Config() {
+Config::Config(const std::string& config_file, bool is_global) {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
+ qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;
FileUtil::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
+ global = is_global;
Reload();
}
Config::~Config() {
- Save();
+ if (global) {
+ Save();
+ }
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
@@ -211,8 +214,8 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
-const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{
- {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
+const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
+ {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
{QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
{QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +223,9 @@ const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{{
{QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
{QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
{QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
- {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
- {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
+ {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
+ {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
+ {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
{QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
@@ -401,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
- Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
- .toString()
- .toStdString();
- Settings::values.enable_audio_stretching =
- ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
- Settings::values.audio_device_id =
- ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
- .toString()
- .toStdString();
- Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
+ if (global) {
+ Settings::values.sink_id =
+ ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
+ .toString()
+ .toStdString();
+ Settings::values.audio_device_id =
+ ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
+ .toString()
+ .toStdString();
+ }
+ ReadSettingGlobal(Settings::values.enable_audio_stretching,
+ QStringLiteral("enable_audio_stretching"), true);
+ ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);
qt_config->endGroup();
}
@@ -439,6 +446,8 @@ void Config::ReadControlValues() {
.toInt());
Settings::values.udp_pad_index =
static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
+ Settings::values.use_docked_mode =
+ ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
qt_config->endGroup();
}
@@ -446,7 +455,7 @@ void Config::ReadControlValues() {
void Config::ReadCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
- Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
+ ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
qt_config->endGroup();
}
@@ -533,6 +542,8 @@ void Config::ReadDebuggingValues() {
Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
Settings::values.disable_cpu_opt =
ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
+ Settings::values.disable_macro_jit =
+ ReadSetting(QStringLiteral("disable_macro_jit"), false).toBool();
qt_config->endGroup();
}
@@ -625,34 +636,28 @@ void Config::ReadPathValues() {
void Config::ReadRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
- Settings::values.renderer_backend =
- static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
- Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
- Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
- Settings::values.resolution_factor =
- ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
- Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt();
- Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt();
- Settings::values.use_frame_limit =
- ReadSetting(QStringLiteral("use_frame_limit"), true).toBool();
- Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
- Settings::values.use_disk_shader_cache =
- ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
- const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt();
- Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level);
- Settings::values.use_asynchronous_gpu_emulation =
- ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
- Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
- Settings::values.use_assembly_shaders =
- ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool();
- Settings::values.use_fast_gpu_time =
- ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool();
- Settings::values.force_30fps_mode =
- ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
-
- Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
- Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
- Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
+ ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0);
+ ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false);
+ ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0);
+ ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0);
+ ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0);
+ ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true);
+ ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100);
+ ReadSettingGlobal(Settings::values.use_disk_shader_cache,
+ QStringLiteral("use_disk_shader_cache"), true);
+ ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
+ ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
+ QStringLiteral("use_asynchronous_gpu_emulation"), false);
+ ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
+ ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
+ false);
+ ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
+ true);
+ ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
+
+ ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
+ ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
+ ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
qt_config->endGroup();
}
@@ -664,11 +669,13 @@ void Config::ReadShortcutValues() {
const auto& [keyseq, context] = shortcut;
qt_config->beginGroup(group);
qt_config->beginGroup(name);
+ // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
+ // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
+ // a file dialog in windowed mode
UISettings::values.shortcuts.push_back(
{name,
group,
- {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
- ReadSetting(QStringLiteral("Context"), context).toInt()}});
+ {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
qt_config->endGroup();
qt_config->endGroup();
}
@@ -679,35 +686,45 @@ void Config::ReadShortcutValues() {
void Config::ReadSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
- Settings::values.use_docked_mode =
- ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
-
- Settings::values.current_user = std::clamp<int>(
- ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1);
+ ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0);
+ Settings::values.current_user =
+ std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1);
- Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt();
+ ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1);
- Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt();
+ ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1);
- Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt();
+ ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0);
- const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool();
- if (rng_seed_enabled) {
- Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong();
- } else {
- Settings::values.rng_seed = std::nullopt;
+ bool rng_seed_enabled;
+ ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false);
+ bool rng_seed_global =
+ global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool();
+ Settings::values.rng_seed.SetGlobal(rng_seed_global);
+ if (global || !rng_seed_global) {
+ if (rng_seed_enabled) {
+ Settings::values.rng_seed.SetValue(
+ ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong());
+ } else {
+ Settings::values.rng_seed.SetValue(std::nullopt);
+ }
}
- const auto custom_rtc_enabled =
- ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
- if (custom_rtc_enabled) {
- Settings::values.custom_rtc =
- std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
- } else {
- Settings::values.custom_rtc = std::nullopt;
+ bool custom_rtc_enabled;
+ ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false);
+ bool custom_rtc_global =
+ global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool();
+ Settings::values.custom_rtc.SetGlobal(custom_rtc_global);
+ if (global || !custom_rtc_global) {
+ if (custom_rtc_enabled) {
+ Settings::values.custom_rtc.SetValue(
+ std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()));
+ } else {
+ Settings::values.custom_rtc.SetValue(std::nullopt);
+ }
}
- Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt();
+ ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);
qt_config->endGroup();
}
@@ -720,8 +737,6 @@ void Config::ReadUIValues() {
.toString();
UISettings::values.enable_discord_presence =
ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
- UISettings::values.screenshot_resolution_factor =
- static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt());
UISettings::values.select_user_on_boot =
ReadSetting(QStringLiteral("select_user_on_boot"), false).toBool();
@@ -803,18 +818,20 @@ void Config::ReadWebServiceValues() {
}
void Config::ReadValues() {
- ReadControlValues();
+ if (global) {
+ ReadControlValues();
+ ReadDataStorageValues();
+ ReadDebuggingValues();
+ ReadDisabledAddOnValues();
+ ReadServiceValues();
+ ReadUIValues();
+ ReadWebServiceValues();
+ ReadMiscellaneousValues();
+ }
ReadCoreValues();
ReadRendererValues();
ReadAudioValues();
- ReadDataStorageValues();
ReadSystemValues();
- ReadMiscellaneousValues();
- ReadDebuggingValues();
- ReadWebServiceValues();
- ReadServiceValues();
- ReadDisabledAddOnValues();
- ReadUIValues();
}
void Config::SavePlayerValues() {
@@ -901,30 +918,35 @@ void Config::SaveTouchscreenValues() {
}
void Config::SaveValues() {
- SaveControlValues();
+ if (global) {
+ SaveControlValues();
+ SaveDataStorageValues();
+ SaveDebuggingValues();
+ SaveDisabledAddOnValues();
+ SaveServiceValues();
+ SaveUIValues();
+ SaveWebServiceValues();
+ SaveMiscellaneousValues();
+ }
SaveCoreValues();
SaveRendererValues();
SaveAudioValues();
- SaveDataStorageValues();
SaveSystemValues();
- SaveMiscellaneousValues();
- SaveDebuggingValues();
- SaveWebServiceValues();
- SaveServiceValues();
- SaveDisabledAddOnValues();
- SaveUIValues();
}
void Config::SaveAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
- WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
- QStringLiteral("auto"));
- WriteSetting(QStringLiteral("enable_audio_stretching"),
- Settings::values.enable_audio_stretching, true);
- WriteSetting(QStringLiteral("output_device"),
- QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
- WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
+ if (global) {
+ WriteSetting(QStringLiteral("output_engine"),
+ QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto"));
+ WriteSetting(QStringLiteral("output_device"),
+ QString::fromStdString(Settings::values.audio_device_id),
+ QStringLiteral("auto"));
+ }
+ WriteSettingGlobal(QStringLiteral("enable_audio_stretching"),
+ Settings::values.enable_audio_stretching, true);
+ WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);
qt_config->endGroup();
}
@@ -947,6 +969,7 @@ void Config::SaveControlValues() {
WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
InputCommon::CemuhookUDP::DEFAULT_PORT);
WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
+ WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
qt_config->endGroup();
}
@@ -954,7 +977,7 @@ void Config::SaveControlValues() {
void Config::SaveCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
- WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
+ WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
qt_config->endGroup();
}
@@ -1011,6 +1034,7 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
+ WriteSetting(QStringLiteral("disable_macro_jit"), Settings::values.disable_macro_jit, false);
qt_config->endGroup();
}
@@ -1076,31 +1100,34 @@ void Config::SavePathValues() {
void Config::SaveRendererValues() {
qt_config->beginGroup(QStringLiteral("Renderer"));
- WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
+ WriteSettingGlobal(QStringLiteral("backend"),
+ static_cast<int>(Settings::values.renderer_backend.GetValue(global)),
+ Settings::values.renderer_backend.UsingGlobal(), 0);
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
- WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
- WriteSetting(QStringLiteral("resolution_factor"),
- static_cast<double>(Settings::values.resolution_factor), 1.0);
- WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
- WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
- WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
- WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
- WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
- true);
- WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy),
- 0);
- WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
- Settings::values.use_asynchronous_gpu_emulation, false);
- WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
- WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders,
- false);
- WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true);
- WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
+ WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
+ WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0);
+ WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0);
+ WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
+ WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
+ WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"),
+ Settings::values.use_disk_shader_cache, true);
+ WriteSettingGlobal(QStringLiteral("gpu_accuracy"),
+ static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
+ Settings::values.gpu_accuracy.UsingGlobal(), 0);
+ WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
+ Settings::values.use_asynchronous_gpu_emulation, false);
+ WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
+ WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
+ Settings::values.use_assembly_shaders, false);
+ WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
+ true);
+ WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
+ false);
// Cast to double because Qt's written float values are not human-readable
- WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0);
- WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0);
- WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0);
+ WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
+ WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
+ WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);
qt_config->endGroup();
}
@@ -1128,23 +1155,28 @@ void Config::SaveShortcutValues() {
void Config::SaveSystemValues() {
qt_config->beginGroup(QStringLiteral("System"));
- WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0);
- WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1);
- WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1);
- WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
-
- WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false);
- WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0);
-
- WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
- false);
- WriteSetting(QStringLiteral("custom_rtc"),
- QVariant::fromValue<long long>(
- Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
- 0);
-
- WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
+ WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1);
+ WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1);
+ WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0);
+
+ WriteSettingGlobal(QStringLiteral("rng_seed_enabled"),
+ Settings::values.rng_seed.GetValue(global).has_value(),
+ Settings::values.rng_seed.UsingGlobal(), false);
+ WriteSettingGlobal(QStringLiteral("rng_seed"),
+ Settings::values.rng_seed.GetValue(global).value_or(0),
+ Settings::values.rng_seed.UsingGlobal(), 0);
+
+ WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"),
+ Settings::values.custom_rtc.GetValue(global).has_value(),
+ Settings::values.custom_rtc.UsingGlobal(), false);
+ WriteSettingGlobal(
+ QStringLiteral("custom_rtc"),
+ QVariant::fromValue<long long>(
+ Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()),
+ Settings::values.custom_rtc.UsingGlobal(), 0);
+
+ WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);
qt_config->endGroup();
}
@@ -1156,8 +1188,6 @@ void Config::SaveUIValues() {
QString::fromUtf8(UISettings::themes[0].second));
WriteSetting(QStringLiteral("enable_discord_presence"),
UISettings::values.enable_discord_presence, true);
- WriteSetting(QStringLiteral("screenshot_resolution_factor"),
- UISettings::values.screenshot_resolution_factor, 0);
WriteSetting(QStringLiteral("select_user_on_boot"), UISettings::values.select_user_on_boot,
false);
@@ -1238,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
return result;
}
+template <typename Type>
+void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) {
+ const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+ setting.SetGlobal(use_global);
+ if (global || !use_global) {
+ setting.SetValue(ReadSetting(name).value<Type>());
+ }
+}
+
+template <typename Type>
+void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
+ const QVariant& default_value) {
+ const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+ setting.SetGlobal(use_global);
+ if (global || !use_global) {
+ setting.SetValue(ReadSetting(name, default_value).value<Type>());
+ }
+}
+
+template <typename Type>
+void Config::ReadSettingGlobal(Type& setting, const QString& name,
+ const QVariant& default_value) const {
+ const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
+ if (global || !use_global) {
+ setting = ReadSetting(name, default_value).value<Type>();
+ }
+}
+
void Config::WriteSetting(const QString& name, const QVariant& value) {
qt_config->setValue(name, value);
}
@@ -1248,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,
qt_config->setValue(name, value);
}
+template <typename Type>
+void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) {
+ if (!global) {
+ qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
+ }
+ if (global || !setting.UsingGlobal()) {
+ qt_config->setValue(name, setting.GetValue(global));
+ }
+}
+
+template <typename Type>
+void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
+ const QVariant& default_value) {
+ if (!global) {
+ qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal());
+ }
+ if (global || !setting.UsingGlobal()) {
+ qt_config->setValue(name + QStringLiteral("/default"),
+ setting.GetValue(global) == default_value.value<Type>());
+ qt_config->setValue(name, setting.GetValue(global));
+ }
+}
+
+void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
+ const QVariant& default_value) {
+ if (!global) {
+ qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
+ }
+ if (global || !use_global) {
+ qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
+ qt_config->setValue(name, value);
+ }
+}
+
void Config::Reload() {
ReadValues();
// To apply default value changes
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 5cd2a5feb..681f0bca5 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -7,6 +7,7 @@
#include <array>
#include <memory>
#include <string>
+#include <QMetaType>
#include <QVariant>
#include "core/settings.h"
#include "yuzu/uisettings.h"
@@ -15,7 +16,7 @@ class QSettings;
class Config {
public:
- Config();
+ explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
~Config();
void Reload();
@@ -27,7 +28,7 @@ public:
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
- static const std::array<UISettings::Shortcut, 15> default_hotkeys;
+ static const std::array<UISettings::Shortcut, 16> default_hotkeys;
private:
void ReadValues();
@@ -82,9 +83,33 @@ private:
QVariant ReadSetting(const QString& name) const;
QVariant ReadSetting(const QString& name, const QVariant& default_value) const;
+ // Templated ReadSettingGlobal functions will also look for the use_global setting and set
+ // both the value and the global state properly
+ template <typename Type>
+ void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name);
+ template <typename Type>
+ void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name,
+ const QVariant& default_value);
+ template <typename Type>
+ void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const;
+ // Templated WriteSettingGlobal functions will also write the global state if needed and will
+ // skip writing the actual setting if it defers to the global value
void WriteSetting(const QString& name, const QVariant& value);
void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value);
+ template <typename Type>
+ void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting);
+ template <typename Type>
+ void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting,
+ const QVariant& default_value);
+ void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
+ const QVariant& default_value);
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
+
+ bool global;
};
+
+// These metatype declarations cannot be in core/settings.h because core is devoid of QT
+Q_DECLARE_METATYPE(Settings::RendererBackend);
+Q_DECLARE_METATYPE(Settings::GPUAccuracy);
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
new file mode 100644
index 000000000..bb47c3933
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -0,0 +1,76 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QCheckBox>
+#include <QComboBox>
+#include "core/settings.h"
+#include "yuzu/configuration/configuration_shared.h"
+#include "yuzu/configuration/configure_per_game.h"
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting,
+ const QCheckBox* checkbox) {
+ if (checkbox->checkState() == Qt::PartiallyChecked) {
+ setting->SetGlobal(true);
+ } else {
+ setting->SetGlobal(false);
+ setting->SetValue(checkbox->checkState() == Qt::Checked);
+ }
+}
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting,
+ const QComboBox* combobox) {
+ if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ setting->SetGlobal(true);
+ } else {
+ setting->SetGlobal(false);
+ setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET);
+ }
+}
+
+void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
+ const QComboBox* combobox) {
+ if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ setting->SetGlobal(true);
+ } else {
+ setting->SetGlobal(false);
+ setting->SetValue(static_cast<Settings::RendererBackend>(
+ combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET));
+ }
+}
+
+void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
+ const Settings::Setting<bool>* setting) {
+ if (setting->UsingGlobal()) {
+ checkbox->setCheckState(Qt::PartiallyChecked);
+ } else {
+ checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+void ConfigurationShared::SetPerGameSetting(QComboBox* combobox,
+ const Settings::Setting<int>* setting) {
+ combobox->setCurrentIndex(setting->UsingGlobal()
+ ? ConfigurationShared::USE_GLOBAL_INDEX
+ : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::SetPerGameSetting(
+ QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) {
+ combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
+ : static_cast<int>(setting->GetValue()) +
+ ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::SetPerGameSetting(
+ QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) {
+ combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
+ : static_cast<int>(setting->GetValue()) +
+ ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) {
+ const QString use_global_text = ConfigurePerGame::tr("Use global configuration");
+ combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
+ combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
+}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
new file mode 100644
index 000000000..b11b1b950
--- /dev/null
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -0,0 +1,36 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QString>
+#include "core/settings.h"
+
+namespace ConfigurationShared {
+
+constexpr int USE_GLOBAL_INDEX = 0;
+constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1;
+constexpr int USE_GLOBAL_OFFSET = 2;
+
+// Global-aware apply and set functions
+
+void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox);
+void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox);
+void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting,
+ const QComboBox* combobox);
+void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting,
+ const QComboBox* combobox);
+
+void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting);
+void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting);
+void SetPerGameSetting(QComboBox* combobox,
+ const Settings::Setting<Settings::RendererBackend>* setting);
+void SetPerGameSetting(QComboBox* combobox,
+ const Settings::Setting<Settings::GPUAccuracy>* setting);
+
+void InsertGlobalItem(QComboBox* combobox);
+
+} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index f370c690f..cc021beec 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -11,6 +11,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_audio.h"
+#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_audio.h"
ConfigureAudio::ConfigureAudio(QWidget* parent)
@@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioDevices);
+ ui->volume_label->setVisible(Settings::configuring_global);
+ ui->volume_combo_box->setVisible(!Settings::configuring_global);
+
+ SetupPerGameUI();
+
SetConfiguration();
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
@@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {
SetAudioDeviceFromDeviceID();
- ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
- ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
+ ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum());
+
+ if (Settings::configuring_global) {
+ ui->toggle_audio_stretching->setChecked(
+ Settings::values.enable_audio_stretching.GetValue());
+ } else {
+ ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching,
+ &Settings::values.enable_audio_stretching);
+ if (Settings::values.volume.UsingGlobal()) {
+ ui->volume_combo_box->setCurrentIndex(0);
+ ui->volume_slider->setEnabled(false);
+ } else {
+ ui->volume_combo_box->setCurrentIndex(1);
+ ui->volume_slider->setEnabled(true);
+ }
+ }
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
@@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
}
void ConfigureAudio::ApplyConfiguration() {
- Settings::values.sink_id =
- ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
- .toStdString();
- Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
- Settings::values.audio_device_id =
- ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
- .toStdString();
- Settings::values.volume =
- static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
+ if (Settings::configuring_global) {
+ Settings::values.sink_id =
+ ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
+ .toStdString();
+ Settings::values.audio_device_id =
+ ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
+ .toStdString();
+
+ // Guard if during game and set to game-specific value
+ if (Settings::values.enable_audio_stretching.UsingGlobal()) {
+ Settings::values.enable_audio_stretching.SetValue(
+ ui->toggle_audio_stretching->isChecked());
+ }
+ if (Settings::values.volume.UsingGlobal()) {
+ Settings::values.volume.SetValue(
+ static_cast<float>(ui->volume_slider->sliderPosition()) /
+ ui->volume_slider->maximum());
+ }
+ } else {
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching,
+ ui->toggle_audio_stretching);
+ if (ui->volume_combo_box->currentIndex() == 0) {
+ Settings::values.volume.SetGlobal(true);
+ } else {
+ Settings::values.volume.SetGlobal(false);
+ Settings::values.volume.SetValue(
+ static_cast<float>(ui->volume_slider->sliderPosition()) /
+ ui->volume_slider->maximum());
+ }
+ }
}
void ConfigureAudio::changeEvent(QEvent* event) {
@@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {
ui->retranslateUi(this);
SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
}
+
+void ConfigureAudio::SetupPerGameUI() {
+ if (Settings::configuring_global) {
+ ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
+ ui->toggle_audio_stretching->setEnabled(
+ Settings::values.enable_audio_stretching.UsingGlobal());
+
+ return;
+ }
+
+ ui->toggle_audio_stretching->setTristate(true);
+ connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
+ this, [this](int index) { ui->volume_slider->setEnabled(index == 1); });
+
+ ui->output_sink_combo_box->setVisible(false);
+ ui->output_sink_label->setVisible(false);
+ ui->audio_device_combo_box->setVisible(false);
+ ui->audio_device_label->setVisible(false);
+}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index ea83bd72d..d84f4a682 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -34,5 +34,7 @@ private:
void SetAudioDeviceFromDeviceID();
void SetVolumeIndicatorText(int percentage);
+ void SetupPerGameUI();
+
std::unique_ptr<Ui::ConfigureAudio> ui;
};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index a098b9acc..862ccb988 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>188</width>
- <height>246</height>
+ <width>367</width>
+ <height>368</height>
</rect>
</property>
<layout class="QVBoxLayout">
@@ -18,9 +18,9 @@
</property>
<layout class="QVBoxLayout">
<item>
- <layout class="QHBoxLayout">
+ <layout class="QHBoxLayout" name="_3">
<item>
- <widget class="QLabel" name="label_1">
+ <widget class="QLabel" name="output_sink_label">
<property name="text">
<string>Output Engine:</string>
</property>
@@ -31,20 +31,20 @@
</item>
</layout>
</item>
- <item>
- <widget class="QCheckBox" name="toggle_audio_stretching">
- <property name="toolTip">
- <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
- </property>
- <property name="text">
- <string>Enable audio stretching</string>
- </property>
- </widget>
- </item>
<item>
- <layout class="QHBoxLayout">
+ <widget class="QCheckBox" name="toggle_audio_stretching">
+ <property name="toolTip">
+ <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
+ </property>
+ <property name="text">
+ <string>Enable audio stretching</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="_2">
<item>
- <widget class="QLabel" name="label_2">
+ <widget class="QLabel" name="audio_device_label">
<property name="text">
<string>Audio Device:</string>
</property>
@@ -61,7 +61,21 @@
<number>0</number>
</property>
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QComboBox" name="volume_combo_box">
+ <item>
+ <property name="text">
+ <string>Use global volume</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Set volume:</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="volume_label">
<property name="text">
<string>Volume:</string>
</property>
@@ -74,7 +88,7 @@
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
+ <width>30</width>
<height>20</height>
</size>
</property>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index c2026763e..2c77441fd 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -39,6 +39,8 @@ void ConfigureDebug::SetConfiguration() {
ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
+ ui->disable_macro_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
+ ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit);
}
void ConfigureDebug::ApplyConfiguration() {
@@ -51,6 +53,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
+ Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
Debugger::ToggleConsole();
Log::Filter filter;
filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index e0d4c4a44..46f0208c6 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -148,6 +148,19 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="disable_macro_jit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis">
+ <string>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</string>
+ </property>
+ <property name="text">
+ <string>Disable Macro JIT</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index df4473b46..5918e9972 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -14,6 +14,8 @@
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
+ Settings::configuring_global = true;
+
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb95423e0..1fb62d1cf 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -7,40 +7,79 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_general.h"
+#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_general.h"
#include "yuzu/uisettings.h"
ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
-
ui->setupUi(this);
+ SetupPerGameUI();
+
SetConfiguration();
- connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
+ connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() {
+ ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked);
+ });
}
ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::SetConfiguration() {
+ const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
+
+ ui->use_multi_core->setEnabled(runtime_lock);
+ ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
+
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
- ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
- ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
- ui->frame_limit->setValue(Settings::values.frame_limit);
+ ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
+ ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
+
+ if (!Settings::configuring_global) {
+ if (Settings::values.use_multi_core.UsingGlobal()) {
+ ui->use_multi_core->setCheckState(Qt::PartiallyChecked);
+ }
+ if (Settings::values.use_frame_limit.UsingGlobal()) {
+ ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked);
+ }
+ }
+
+ ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked &&
+ ui->toggle_frame_limit->isEnabled());
}
void ConfigureGeneral::ApplyConfiguration() {
- UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
- UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
- UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
- UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
+ if (Settings::configuring_global) {
+ UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
+ UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
+ UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
+ UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
+
+ // Guard if during game and set to game-specific value
+ if (Settings::values.use_frame_limit.UsingGlobal()) {
+ Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
+ Qt::Checked);
+ Settings::values.frame_limit.SetValue(ui->frame_limit->value());
+ Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked());
+ }
+ } else {
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core,
+ ui->use_multi_core);
- Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
- Settings::values.frame_limit = ui->frame_limit->value();
+ bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked;
+ Settings::values.use_frame_limit.SetGlobal(global_frame_limit);
+ Settings::values.frame_limit.SetGlobal(global_frame_limit);
+ if (!global_frame_limit) {
+ Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() ==
+ Qt::Checked);
+ Settings::values.frame_limit.SetValue(ui->frame_limit->value());
+ }
+ }
}
void ConfigureGeneral::changeEvent(QEvent* event) {
@@ -54,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {
void ConfigureGeneral::RetranslateUI() {
ui->retranslateUi(this);
}
+
+void ConfigureGeneral::SetupPerGameUI() {
+ if (Settings::configuring_global) {
+ ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
+ ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
+
+ return;
+ }
+
+ ui->toggle_check_exit->setVisible(false);
+ ui->toggle_user_on_boot->setVisible(false);
+ ui->toggle_background_pause->setVisible(false);
+ ui->toggle_hide_mouse->setVisible(false);
+
+ ui->toggle_frame_limit->setTristate(true);
+ ui->use_multi_core->setTristate(true);
+}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index ef05ce065..9c785c22e 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -28,5 +28,7 @@ private:
void SetConfiguration();
+ void SetupPerGameUI();
+
std::unique_ptr<Ui::ConfigureGeneral> ui;
};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index fc3b7e65a..2711116a2 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -52,6 +52,13 @@
</layout>
</item>
<item>
+ <widget class="QCheckBox" name="use_multi_core">
+ <property name="text">
+ <string>Multicore CPU Emulation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QCheckBox" name="toggle_check_exit">
<property name="text">
<string>Confirm exit while emulation is running</string>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index ea667caef..cb4706bd6 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -13,65 +13,27 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics.h"
+#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#ifdef HAS_VULKAN
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#endif
-namespace {
-enum class Resolution : int {
- Auto,
- Scale1x,
- Scale2x,
- Scale3x,
- Scale4x,
-};
-
-float ToResolutionFactor(Resolution option) {
- switch (option) {
- case Resolution::Auto:
- return 0.f;
- case Resolution::Scale1x:
- return 1.f;
- case Resolution::Scale2x:
- return 2.f;
- case Resolution::Scale3x:
- return 3.f;
- case Resolution::Scale4x:
- return 4.f;
- }
- return 0.f;
-}
-
-Resolution FromResolutionFactor(float factor) {
- if (factor == 0.f) {
- return Resolution::Auto;
- } else if (factor == 1.f) {
- return Resolution::Scale1x;
- } else if (factor == 2.f) {
- return Resolution::Scale2x;
- } else if (factor == 3.f) {
- return Resolution::Scale3x;
- } else if (factor == 4.f) {
- return Resolution::Scale4x;
- }
- return Resolution::Auto;
-}
-} // Anonymous namespace
-
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
- vulkan_device = Settings::values.vulkan_device;
+ vulkan_device = Settings::values.vulkan_device.GetValue();
RetrieveVulkanDevices();
ui->setupUi(this);
+ SetupPerGameUI();
+
SetConfiguration();
- connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+ connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this] { UpdateDeviceComboBox(); });
- connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
+ connect(ui->device, qOverload<int>(&QComboBox::activated), this,
[this](int device) { UpdateDeviceSelection(device); });
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@@ -81,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
}
UpdateBackgroundColorButton(new_bg_color);
});
+
+ ui->bg_label->setVisible(Settings::configuring_global);
+ ui->bg_combobox->setVisible(!Settings::configuring_global);
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -98,31 +63,95 @@ void ConfigureGraphics::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
ui->api->setEnabled(runtime_lock);
- ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
- ui->resolution_factor_combobox->setCurrentIndex(
- static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
- ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio);
- ui->use_disk_shader_cache->setEnabled(runtime_lock);
- ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
- ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
- UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
- Settings::values.bg_blue));
+ ui->use_disk_shader_cache->setEnabled(runtime_lock);
+
+ if (Settings::configuring_global) {
+ ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
+ ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
+ ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
+ ui->use_asynchronous_gpu_emulation->setChecked(
+ Settings::values.use_asynchronous_gpu_emulation.GetValue());
+ } else {
+ ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache,
+ &Settings::values.use_disk_shader_cache);
+ ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation,
+ &Settings::values.use_asynchronous_gpu_emulation);
+
+ ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
+ ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox,
+ &Settings::values.aspect_ratio);
+
+ ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
+ ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
+ }
+
+ UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(),
+ Settings::values.bg_green.GetValue(),
+ Settings::values.bg_blue.GetValue()));
UpdateDeviceComboBox();
}
void ConfigureGraphics::ApplyConfiguration() {
- Settings::values.renderer_backend = GetCurrentGraphicsBackend();
- Settings::values.vulkan_device = vulkan_device;
- Settings::values.resolution_factor =
- ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
- Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex();
- Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
- Settings::values.use_asynchronous_gpu_emulation =
- ui->use_asynchronous_gpu_emulation->isChecked();
- Settings::values.bg_red = static_cast<float>(bg_color.redF());
- Settings::values.bg_green = static_cast<float>(bg_color.greenF());
- Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
+ if (Settings::configuring_global) {
+ // Guard if during game and set to game-specific value
+ if (Settings::values.renderer_backend.UsingGlobal()) {
+ Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
+ }
+ if (Settings::values.vulkan_device.UsingGlobal()) {
+ Settings::values.vulkan_device.SetValue(vulkan_device);
+ }
+ if (Settings::values.aspect_ratio.UsingGlobal()) {
+ Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex());
+ }
+ if (Settings::values.use_disk_shader_cache.UsingGlobal()) {
+ Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked());
+ }
+ if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) {
+ Settings::values.use_asynchronous_gpu_emulation.SetValue(
+ ui->use_asynchronous_gpu_emulation->isChecked());
+ }
+ if (Settings::values.bg_red.UsingGlobal()) {
+ Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
+ Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
+ Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
+ }
+ } else {
+ if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ Settings::values.renderer_backend.SetGlobal(true);
+ Settings::values.vulkan_device.SetGlobal(true);
+ } else {
+ Settings::values.renderer_backend.SetGlobal(false);
+ Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
+ if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
+ Settings::values.vulkan_device.SetGlobal(false);
+ Settings::values.vulkan_device.SetValue(vulkan_device);
+ } else {
+ Settings::values.vulkan_device.SetGlobal(true);
+ }
+ }
+
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
+ ui->aspect_ratio_combobox);
+
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache,
+ ui->use_disk_shader_cache);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
+ ui->use_asynchronous_gpu_emulation);
+
+ if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ Settings::values.bg_red.SetGlobal(true);
+ Settings::values.bg_green.SetGlobal(true);
+ Settings::values.bg_blue.SetGlobal(true);
+ } else {
+ Settings::values.bg_red.SetGlobal(false);
+ Settings::values.bg_green.SetGlobal(false);
+ Settings::values.bg_blue.SetGlobal(false);
+ Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
+ Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
+ Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF()));
+ }
+ }
}
void ConfigureGraphics::changeEvent(QEvent* event) {
@@ -151,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
ui->device->clear();
bool enabled = false;
+
+ if (!Settings::configuring_global &&
+ ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ vulkan_device = Settings::values.vulkan_device.GetValue();
+ }
switch (GetCurrentGraphicsBackend()) {
case Settings::RendererBackend::OpenGL:
ui->device->addItem(tr("OpenGL Graphics Device"));
enabled = false;
break;
case Settings::RendererBackend::Vulkan:
- for (const auto device : vulkan_devices) {
+ for (const auto& device : vulkan_devices) {
ui->device->addItem(device);
}
ui->device->setCurrentIndex(vulkan_device);
enabled = !vulkan_devices.empty();
break;
}
+ // If in per-game config and use global is selected, don't enable.
+ enabled &= !(!Settings::configuring_global &&
+ ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
}
@@ -177,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
- return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
+ if (Settings::configuring_global) {
+ return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
+ }
+
+ if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ Settings::values.renderer_backend.SetGlobal(true);
+ return Settings::values.renderer_backend.GetValue();
+ }
+ Settings::values.renderer_backend.SetGlobal(false);
+ return static_cast<Settings::RendererBackend>(ui->api->currentIndex() -
+ ConfigurationShared::USE_GLOBAL_OFFSET);
+}
+
+void ConfigureGraphics::SetupPerGameUI() {
+ if (Settings::configuring_global) {
+ ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
+ ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
+ ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
+ ui->use_asynchronous_gpu_emulation->setEnabled(
+ Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
+ ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
+ ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
+
+ return;
+ }
+
+ connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
+ [this](int index) { ui->bg_button->setEnabled(index == 1); });
+
+ ui->use_disk_shader_cache->setTristate(true);
+ ui->use_asynchronous_gpu_emulation->setTristate(true);
+ ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox);
+ ConfigurationShared::InsertGlobalItem(ui->api);
}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 7e0596d9c..24f01c739 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -35,6 +35,8 @@ private:
void RetrieveVulkanDevices();
+ void SetupPerGameUI();
+
Settings::RendererBackend GetCurrentGraphicsBackend() const;
std::unique_ptr<Ui::ConfigureGraphics> ui;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index c816d6108..62418fc14 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -85,39 +85,34 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
- <widget class="QLabel" name="label">
+ <widget class="QLabel" name="ar_label">
<property name="text">
- <string>Internal Resolution:</string>
+ <string>Aspect Ratio:</string>
</property>
</widget>
</item>
<item>
- <widget class="QComboBox" name="resolution_factor_combobox">
- <item>
- <property name="text">
- <string>Auto (Window Size)</string>
- </property>
- </item>
+ <widget class="QComboBox" name="aspect_ratio_combobox">
<item>
<property name="text">
- <string>Native (1280x720)</string>
+ <string>Default (16:9)</string>
</property>
</item>
<item>
<property name="text">
- <string>2x Native (2560x1440)</string>
+ <string>Force 4:3</string>
</property>
</item>
<item>
<property name="text">
- <string>3x Native (3840x2160)</string>
+ <string>Force 21:9</string>
</property>
</item>
<item>
<property name="text">
- <string>4x Native (5120x2880)</string>
+ <string>Stretch to Window</string>
</property>
</item>
</widget>
@@ -125,42 +120,30 @@
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
- <widget class="QLabel" name="ar_label">
- <property name="text">
- <string>Aspect Ratio:</string>
+ <widget class="QComboBox" name="bg_combobox">
+ <property name="currentText">
+ <string>Use global background color</string>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="maxVisibleItems">
+ <number>10</number>
</property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="aspect_ratio_combobox">
- <item>
- <property name="text">
- <string>Default (16:9)</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Force 4:3</string>
- </property>
- </item>
<item>
<property name="text">
- <string>Force 21:9</string>
+ <string>Use global background color</string>
</property>
</item>
<item>
<property name="text">
- <string>Stretch to Window</string>
+ <string>Set background color:</string>
</property>
</item>
</widget>
</item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="bg_label">
<property name="text">
@@ -169,6 +152,19 @@
</widget>
</item>
<item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
<widget class="QPushButton" name="bg_button">
<property name="maximumSize">
<size>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 37aadf7f8..7c0fa7ec5 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -5,6 +5,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_graphics_advanced.h"
+#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics_advanced.h"
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
@@ -12,8 +13,7 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)
ui->setupUi(this);
- // TODO: Remove this after assembly shaders are fully integrated
- ui->use_assembly_shaders->setVisible(false);
+ SetupPerGameUI();
SetConfiguration();
}
@@ -22,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
- ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));
ui->use_vsync->setEnabled(runtime_lock);
- ui->use_vsync->setChecked(Settings::values.use_vsync);
ui->use_assembly_shaders->setEnabled(runtime_lock);
- ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders);
- ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);
ui->force_30fps_mode->setEnabled(runtime_lock);
- ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
- ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy);
+
+ if (Settings::configuring_global) {
+ ui->gpu_accuracy->setCurrentIndex(
+ static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
+ ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
+ ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
+ ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
+ ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
+ ui->anisotropic_filtering_combobox->setCurrentIndex(
+ Settings::values.max_anisotropy.GetValue());
+ } else {
+ ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy);
+ ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync);
+ ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders,
+ &Settings::values.use_assembly_shaders);
+ ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time,
+ &Settings::values.use_fast_gpu_time);
+ ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode,
+ &Settings::values.force_30fps_mode);
+ ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox,
+ &Settings::values.max_anisotropy);
+ }
}
void ConfigureGraphicsAdvanced::ApplyConfiguration() {
- auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex());
- Settings::values.gpu_accuracy = gpu_accuracy;
- Settings::values.use_vsync = ui->use_vsync->isChecked();
- Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked();
- Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked();
- Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
- Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex();
+ // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
+ const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
+ ui->gpu_accuracy->currentIndex() -
+ ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
+
+ if (Settings::configuring_global) {
+ // Must guard in case of a during-game configuration when set to be game-specific.
+ if (Settings::values.gpu_accuracy.UsingGlobal()) {
+ Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
+ }
+ if (Settings::values.use_vsync.UsingGlobal()) {
+ Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked());
+ }
+ if (Settings::values.use_assembly_shaders.UsingGlobal()) {
+ Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked());
+ }
+ if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
+ Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
+ }
+ if (Settings::values.force_30fps_mode.UsingGlobal()) {
+ Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
+ }
+ if (Settings::values.max_anisotropy.UsingGlobal()) {
+ Settings::values.max_anisotropy.SetValue(
+ ui->anisotropic_filtering_combobox->currentIndex());
+ }
+ } else {
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
+ ui->anisotropic_filtering_combobox);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders,
+ ui->use_assembly_shaders);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
+ ui->use_fast_gpu_time);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
+ ui->force_30fps_mode);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
+ ui->anisotropic_filtering_combobox);
+
+ if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
+ Settings::values.gpu_accuracy.SetGlobal(true);
+ } else {
+ Settings::values.gpu_accuracy.SetGlobal(false);
+ Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
+ }
+ }
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -55,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
void ConfigureGraphicsAdvanced::RetranslateUI() {
ui->retranslateUi(this);
}
+
+void ConfigureGraphicsAdvanced::SetupPerGameUI() {
+ // Disable if not global (only happens during game)
+ if (Settings::configuring_global) {
+ ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
+ ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
+ ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
+ ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
+ ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
+ ui->anisotropic_filtering_combobox->setEnabled(
+ Settings::values.max_anisotropy.UsingGlobal());
+
+ return;
+ }
+
+ ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy);
+ ui->use_vsync->setTristate(true);
+ ui->use_assembly_shaders->setTristate(true);
+ ui->use_fast_gpu_time->setTristate(true);
+ ui->force_30fps_mode->setTristate(true);
+ ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox);
+}
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index bbc9d4355..c043588ff 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -26,5 +26,7 @@ private:
void SetConfiguration();
+ void SetupPerGameUI();
+
std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;
};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index e4eb5594b..00433926d 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return GetKeyName(param.Get("code", 0));
}
+ if (param.Get("engine", "") == "gcpad") {
+ if (param.Has("axis")) {
+ const QString axis_str = QString::fromStdString(param.Get("axis", ""));
+ const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+ return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str);
+ }
+ if (param.Has("button")) {
+ const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
+ return QObject::tr("GC Button %1").arg(button_str);
+ }
+ return GetKeyName(param.Get("code", 0));
+ }
+
if (param.Get("engine", "") == "sdl") {
if (param.Has("hat")) {
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
return {};
}
+ if (param.Get("engine", "") == "gcpad") {
+ if (dir == "modifier") {
+ return QObject::tr("[unused]");
+ }
+
+ if (dir == "left" || dir == "right") {
+ const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
+
+ return QObject::tr("GC Axis %1").arg(axis_x_str);
+ }
+
+ if (dir == "up" || dir == "down") {
+ const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
+
+ return QObject::tr("GC Axis %1").arg(axis_y_str);
+ }
+
+ return {};
+ }
return QObject::tr("[unknown]");
}
@@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
- if (analogs_param[analog_id].Get("engine", "") == "sdl") {
+ if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
+ analogs_param[analog_id].Get("engine", "") == "gcpad") {
analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
tr("Deadzone: %1%").arg(slider_value));
analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
@@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
connect(poll_timer.get(), &QTimer::timeout, [this] {
Common::ParamPackage params;
+ if (InputCommon::GetGCButtons()->IsPolling()) {
+ params = InputCommon::GetGCButtons()->GetNextInput();
+ if (params.Has("engine")) {
+ SetPollingResult(params, false);
+ return;
+ }
+ }
+ if (InputCommon::GetGCAnalogs()->IsPolling()) {
+ params = InputCommon::GetGCAnalogs()->GetNextInput();
+ if (params.Has("engine")) {
+ SetPollingResult(params, false);
+ return;
+ }
+ }
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
if (params.Has("engine")) {
@@ -480,7 +528,9 @@ void ConfigureInputPlayer::RestoreDefaults() {
SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
}
+
UpdateButtonLabels();
+ ApplyConfiguration();
}
void ConfigureInputPlayer::ClearAll() {
@@ -505,6 +555,7 @@ void ConfigureInputPlayer::ClearAll() {
}
UpdateButtonLabels();
+ ApplyConfiguration();
}
void ConfigureInputPlayer::UpdateButtonLabels() {
@@ -531,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
analog_map_deadzone_and_modifier_slider_label[analog_id];
if (param.Has("engine")) {
- if (param.Get("engine", "") == "sdl") {
+ if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
if (!param.Has("deadzone")) {
param.Set("deadzone", 0.1f);
}
@@ -580,6 +631,11 @@ void ConfigureInputPlayer::HandleClick(
grabKeyboard();
grabMouse();
+ if (type == InputCommon::Polling::DeviceType::Button) {
+ InputCommon::GetGCButtons()->BeginConfiguration();
+ } else {
+ InputCommon::GetGCAnalogs()->BeginConfiguration();
+ }
timeout_timer->start(5000); // Cancel after 5 seconds
poll_timer->start(200); // Check for new inputs every 200ms
}
@@ -593,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
poller->Stop();
}
+ InputCommon::GetGCButtons()->EndConfiguration();
+ InputCommon::GetGCAnalogs()->EndConfiguration();
+
if (!abort) {
(*input_setter)(params);
}
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
new file mode 100644
index 000000000..1e49f0787
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -0,0 +1,140 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <QCheckBox>
+#include <QHeaderView>
+#include <QMenu>
+#include <QStandardItemModel>
+#include <QString>
+#include <QTimer>
+#include <QTreeView>
+
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/xts_archive.h"
+#include "core/loader/loader.h"
+#include "ui_configure_per_game.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_per_game.h"
+#include "yuzu/uisettings.h"
+#include "yuzu/util/util.h"
+
+ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
+ game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
+
+ Settings::configuring_global = false;
+
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+ setWindowTitle(tr("Properties"));
+
+ ui->addonsTab->SetTitleId(title_id);
+
+ scene = new QGraphicsScene;
+ ui->icon_view->setScene(scene);
+
+ LoadConfiguration();
+}
+
+ConfigurePerGame::~ConfigurePerGame() = default;
+
+void ConfigurePerGame::ApplyConfiguration() {
+ ui->addonsTab->ApplyConfiguration();
+ ui->generalTab->ApplyConfiguration();
+ ui->systemTab->ApplyConfiguration();
+ ui->graphicsTab->ApplyConfiguration();
+ ui->graphicsAdvancedTab->ApplyConfiguration();
+ ui->audioTab->ApplyConfiguration();
+
+ Settings::Apply();
+ Settings::LogSettings();
+
+ game_config->Save();
+}
+
+void ConfigurePerGame::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigurePerGame::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) {
+ this->file = std::move(file);
+ LoadConfiguration();
+}
+
+void ConfigurePerGame::LoadConfiguration() {
+ if (file == nullptr) {
+ return;
+ }
+
+ ui->addonsTab->LoadFromFile(file);
+
+ ui->display_title_id->setText(
+ QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
+
+ FileSys::PatchManager pm{title_id};
+ const auto control = pm.GetControlMetadata();
+ const auto loader = Loader::GetLoader(file);
+
+ if (control.first != nullptr) {
+ ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
+ ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
+ ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
+ } else {
+ std::string title;
+ if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
+ ui->display_name->setText(QString::fromStdString(title));
+
+ FileSys::NACP nacp;
+ if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
+ ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
+
+ ui->display_version->setText(QStringLiteral("1.0.0"));
+ }
+
+ if (control.second != nullptr) {
+ scene->clear();
+
+ QPixmap map;
+ const auto bytes = control.second->ReadAllBytes();
+ map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
+
+ scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ } else {
+ std::vector<u8> bytes;
+ if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
+ scene->clear();
+
+ QPixmap map;
+ map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
+
+ scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ }
+ }
+
+ ui->display_filename->setText(QString::fromStdString(file->GetName()));
+
+ ui->display_format->setText(
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
+
+ const auto valueText = ReadableByteSize(file->GetSize());
+ ui->display_size->setText(valueText);
+}
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
new file mode 100644
index 000000000..5f9a08cef
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -0,0 +1,51 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <QDialog>
+#include <QList>
+
+#include "core/file_sys/vfs_types.h"
+#include "yuzu/configuration/config.h"
+
+class QGraphicsScene;
+class QStandardItem;
+class QStandardItemModel;
+class QTreeView;
+class QVBoxLayout;
+
+namespace Ui {
+class ConfigurePerGame;
+}
+
+class ConfigurePerGame : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigurePerGame(QWidget* parent, u64 title_id);
+ ~ConfigurePerGame() override;
+
+ /// Save all button configurations to settings file
+ void ApplyConfiguration();
+
+ void LoadFromFile(FileSys::VirtualFile file);
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ void LoadConfiguration();
+
+ std::unique_ptr<Ui::ConfigurePerGame> ui;
+ FileSys::VirtualFile file;
+ u64 title_id;
+
+ QGraphicsScene* scene;
+
+ std::unique_ptr<Config> game_config;
+};
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
new file mode 100644
index 000000000..d2057c4ab
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -0,0 +1,350 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigurePerGame</class>
+ <widget class="QDialog" name="ConfigurePerGame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Info</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGraphicsView" name="icon_view">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>256</width>
+ <height>256</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>256</width>
+ <height>256</height>
+ </size>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="interactive">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="6" column="1">
+ <widget class="QLineEdit" name="display_size">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="display_version">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Title ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="display_title_id">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLineEdit" name="display_filename">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="display_format">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Filename</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="display_name">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="display_developer">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Format</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Version</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Developer</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="VerticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2"/>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="usesScrollButtons">
+ <bool>true</bool>
+ </property>
+ <property name="documentMode">
+ <bool>false</bool>
+ </property>
+ <property name="tabsClosable">
+ <bool>false</bool>
+ </property>
+ <widget class="ConfigurePerGameAddons" name="addonsTab">
+ <attribute name="title">
+ <string>Add-Ons</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureGeneral" name="generalTab">
+ <attribute name="title">
+ <string>General</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureSystem" name="systemTab">
+ <attribute name="title">
+ <string>System</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureGraphics" name="graphicsTab">
+ <attribute name="title">
+ <string>Graphics</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
+ <attribute name="title">
+ <string>Adv. Graphics</string>
+ </attribute>
+ </widget>
+ <widget class="ConfigureAudio" name="audioTab">
+ <attribute name="title">
+ <string>Audio</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ConfigureGeneral</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_general.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureSystem</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_system.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureAudio</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_audio.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureGraphics</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_graphics.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigureGraphicsAdvanced</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_graphics_advanced.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>ConfigurePerGameAddons</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_per_game_addons.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigurePerGame</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigurePerGame</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index d7f259f12..478d5d3a1 100644
--- a/src/yuzu/configuration/configure_per_general.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -15,23 +15,20 @@
#include "common/common_paths.h"
#include "common/file_util.h"
-#include "core/file_sys/control_metadata.h"
+#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/xts_archive.h"
#include "core/loader/loader.h"
-#include "ui_configure_per_general.h"
+#include "ui_configure_per_game_addons.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input.h"
-#include "yuzu/configuration/configure_per_general.h"
+#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/uisettings.h"
#include "yuzu/util/util.h"
-ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) {
-
+ConfigurePerGameAddons::ConfigurePerGameAddons(QWidget* parent)
+ : QWidget(parent), ui(new Ui::ConfigurePerGameAddons) {
ui->setupUi(this);
- setFocusPolicy(Qt::ClickFocus);
- setWindowTitle(tr("Properties"));
layout = new QVBoxLayout;
tree_view = new QTreeView;
@@ -52,7 +49,7 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));
// We must register all custom types with the Qt Automoc system so that we are able to use it
- // with signals/slots. In this case, QList falls under the umbrells of custom types.
+ // with signals/slots. In this case, QList falls under the umbrella of custom types.
qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
layout->setContentsMargins(0, 0, 0, 0);
@@ -61,18 +58,15 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
ui->scrollArea->setLayout(layout);
- scene = new QGraphicsScene;
- ui->icon_view->setScene(scene);
+ ui->scrollArea->setEnabled(!Core::System::GetInstance().IsPoweredOn());
connect(item_model, &QStandardItemModel::itemChanged,
[] { UISettings::values.is_game_list_reload_pending.exchange(true); });
-
- LoadConfiguration();
}
-ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default;
+ConfigurePerGameAddons::~ConfigurePerGameAddons() = default;
-void ConfigurePerGameGeneral::ApplyConfiguration() {
+void ConfigurePerGameAddons::ApplyConfiguration() {
std::vector<std::string> disabled_addons;
for (const auto& item : list_items) {
@@ -92,72 +86,35 @@ void ConfigurePerGameGeneral::ApplyConfiguration() {
Settings::values.disabled_addons[title_id] = disabled_addons;
}
-void ConfigurePerGameGeneral::changeEvent(QEvent* event) {
+void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) {
+ this->file = std::move(file);
+ LoadConfiguration();
+}
+
+void ConfigurePerGameAddons::SetTitleId(u64 id) {
+ this->title_id = id;
+}
+
+void ConfigurePerGameAddons::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
- QDialog::changeEvent(event);
+ QWidget::changeEvent(event);
}
-void ConfigurePerGameGeneral::RetranslateUI() {
+void ConfigurePerGameAddons::RetranslateUI() {
ui->retranslateUi(this);
}
-void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) {
- this->file = std::move(file);
- LoadConfiguration();
-}
-
-void ConfigurePerGameGeneral::LoadConfiguration() {
+void ConfigurePerGameAddons::LoadConfiguration() {
if (file == nullptr) {
return;
}
- ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id)));
-
FileSys::PatchManager pm{title_id};
- const auto control = pm.GetControlMetadata();
const auto loader = Loader::GetLoader(file);
- if (control.first != nullptr) {
- ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
- ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
- ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
- } else {
- std::string title;
- if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
- ui->display_name->setText(QString::fromStdString(title));
-
- FileSys::NACP nacp;
- if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success)
- ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName()));
-
- ui->display_version->setText(QStringLiteral("1.0.0"));
- }
-
- if (control.second != nullptr) {
- scene->clear();
-
- QPixmap map;
- const auto bytes = control.second->ReadAllBytes();
- map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
-
- scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
- Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
- } else {
- std::vector<u8> bytes;
- if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
- scene->clear();
-
- QPixmap map;
- map.loadFromData(bytes.data(), static_cast<u32>(bytes.size()));
-
- scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
- Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
- }
- }
-
FileSys::VirtualFile update_raw;
loader->ReadUpdateRaw(update_raw);
@@ -182,12 +139,4 @@ void ConfigurePerGameGeneral::LoadConfiguration() {
}
tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
-
- ui->display_filename->setText(QString::fromStdString(file->GetName()));
-
- ui->display_format->setText(
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
-
- const auto valueText = ReadableByteSize(file->GetSize());
- ui->display_size->setText(valueText);
}
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_game_addons.h
index a3b2cdeff..a00ec3539 100644
--- a/src/yuzu/configuration/configure_per_general.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Citra Emulator Project
+// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@@ -7,7 +7,6 @@
#include <memory>
#include <vector>
-#include <QDialog>
#include <QList>
#include "core/file_sys/vfs_types.h"
@@ -19,35 +18,36 @@ class QTreeView;
class QVBoxLayout;
namespace Ui {
-class ConfigurePerGameGeneral;
+class ConfigurePerGameAddons;
}
-class ConfigurePerGameGeneral : public QDialog {
+class ConfigurePerGameAddons : public QWidget {
Q_OBJECT
public:
- explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id);
- ~ConfigurePerGameGeneral() override;
+ explicit ConfigurePerGameAddons(QWidget* parent = nullptr);
+ ~ConfigurePerGameAddons() override;
/// Save all button configurations to settings file
void ApplyConfiguration();
void LoadFromFile(FileSys::VirtualFile file);
+ void SetTitleId(u64 id);
+
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void LoadConfiguration();
- std::unique_ptr<Ui::ConfigurePerGameGeneral> ui;
+ std::unique_ptr<Ui::ConfigurePerGameAddons> ui;
FileSys::VirtualFile file;
u64 title_id;
QVBoxLayout* layout;
QTreeView* tree_view;
QStandardItemModel* item_model;
- QGraphicsScene* scene;
std::vector<QList<QStandardItem*>> list_items;
};
diff --git a/src/yuzu/configuration/configure_per_game_addons.ui b/src/yuzu/configuration/configure_per_game_addons.ui
new file mode 100644
index 000000000..aefdebfcd
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_game_addons.ui
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigurePerGameAddons</class>
+ <widget class="QWidget" name="ConfigurePerGameAddons">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>380</width>
+ <height>280</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui
deleted file mode 100644
index 8fdd96fa4..000000000
--- a/src/yuzu/configuration/configure_per_general.ui
+++ /dev/null
@@ -1,276 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ConfigurePerGameGeneral</class>
- <widget class="QDialog" name="ConfigurePerGameGeneral">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>400</width>
- <height>520</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>ConfigurePerGameGeneral</string>
- </property>
- <layout class="QHBoxLayout" name="HorizontalLayout">
- <item>
- <layout class="QVBoxLayout" name="VerticalLayout">
- <item>
- <widget class="QGroupBox" name="GeneralGroupBox">
- <property name="title">
- <string>Info</string>
- </property>
- <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
- <item>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="6" column="1" colspan="2">
- <widget class="QLineEdit" name="display_filename">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="display_name">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Developer</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1" colspan="2">
- <widget class="QLineEdit" name="display_size">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Name</string>
- </property>
- </widget>
- </item>
- <item row="6" column="0">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>Filename</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_3">
- <property name="text">
- <string>Version</string>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Format</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLineEdit" name="display_version">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLineEdit" name="display_format">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>Size</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLineEdit" name="display_developer">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Title ID</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QLineEdit" name="display_title_id">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="0" column="2" rowspan="5">
- <widget class="QGraphicsView" name="icon_view">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>128</width>
- <height>128</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>128</width>
- <height>128</height>
- </size>
- </property>
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="sizeAdjustPolicy">
- <enum>QAbstractScrollArea::AdjustToContents</enum>
- </property>
- <property name="interactive">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="PerformanceGroupBox">
- <property name="title">
- <string>Add-Ons</string>
- </property>
- <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
- <item>
- <widget class="QScrollArea" name="scrollArea">
- <property name="widgetResizable">
- <bool>true</bool>
- </property>
- <widget class="QWidget" name="scrollAreaWidgetContents">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>350</width>
- <height>169</height>
- </rect>
- </property>
- </widget>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigurePerGameGeneral</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>269</x>
- <y>567</y>
- </hint>
- <hint type="destinationlabel">
- <x>269</x>
- <y>294</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ConfigurePerGameGeneral</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>269</x>
- <y>567</y>
- </hint>
- <hint type="destinationlabel">
- <x>269</x>
- <y>294</y>
- </hint>
- </hints>
- </connection>
- </connections>
-</ui>
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp
index 06566e981..0de7a4f0b 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_service.cpp
@@ -68,6 +68,7 @@ void ConfigureService::SetConfiguration() {
}
std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
+#ifdef YUZU_ENABLE_BOXCAT
std::optional<std::string> global;
std::map<std::string, Service::BCAT::EventStatus> map;
const auto res = Service::BCAT::Boxcat::GetStatus(global, map);
@@ -105,7 +106,10 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
.arg(QString::fromStdString(key))
.arg(FormatEventStatusString(value));
}
- return {QStringLiteral("Current Boxcat Events"), std::move(out)};
+ return {tr("Current Boxcat Events"), std::move(out)};
+#else
+ return {tr("Current Boxcat Events"), tr("There are currently no events on boxcat.")};
+#endif
}
void ConfigureService::OnBCATImplChanged() {
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 10315e7a6..68e02738b 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -14,6 +14,7 @@
#include "core/core.h"
#include "core/settings.h"
#include "ui_configure_system.h"
+#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_system.h"
ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) {
@@ -21,20 +22,25 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
&ConfigureSystem::RefreshConsoleID);
- connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
- ui->rng_seed_edit->setEnabled(checked);
- if (!checked) {
+ connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
+ ui->rng_seed_edit->setEnabled(state == Qt::Checked);
+ if (state != Qt::Checked) {
ui->rng_seed_edit->setText(QStringLiteral("00000000"));
}
});
- connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
- ui->custom_rtc_edit->setEnabled(checked);
- if (!checked) {
+ connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) {
+ ui->custom_rtc_edit->setEnabled(state == Qt::Checked);
+ if (state != Qt::Checked) {
ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());
}
});
+ ui->label_console_id->setVisible(Settings::configuring_global);
+ ui->button_regenerate_console_id->setVisible(Settings::configuring_global);
+
+ SetupPerGameUI();
+
SetConfiguration();
}
@@ -54,26 +60,58 @@ void ConfigureSystem::RetranslateUI() {
void ConfigureSystem::SetConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();
+ const auto rng_seed =
+ QStringLiteral("%1")
+ .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
+ .toUpper();
+ const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or(
+ std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
- ui->combo_language->setCurrentIndex(Settings::values.language_index);
- ui->combo_region->setCurrentIndex(Settings::values.region_index);
- ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index);
- ui->combo_sound->setCurrentIndex(Settings::values.sound_index);
-
- ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
- ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
-
- const auto rng_seed = QStringLiteral("%1")
- .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'})
- .toUpper();
- ui->rng_seed_edit->setText(rng_seed);
-
- ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
- ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());
+ if (Settings::configuring_global) {
+ ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
+ ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
+ ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
+ ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
+
+ ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
+ ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
+ Settings::values.rng_seed.UsingGlobal());
+ ui->rng_seed_edit->setText(rng_seed);
+
+ ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value());
+ ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() &&
+ Settings::values.rng_seed.UsingGlobal());
+ ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
+ } else {
+ ConfigurationShared::SetPerGameSetting(ui->combo_language,
+ &Settings::values.language_index);
+ ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index);
+ ConfigurationShared::SetPerGameSetting(ui->combo_time_zone,
+ &Settings::values.time_zone_index);
+ ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
+
+ if (Settings::values.rng_seed.UsingGlobal()) {
+ ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked);
+ } else {
+ ui->rng_seed_checkbox->setCheckState(
+ Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
+ ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value());
+ if (Settings::values.rng_seed.GetValue().has_value()) {
+ ui->rng_seed_edit->setText(rng_seed);
+ }
+ }
- const auto rtc_time = Settings::values.custom_rtc.value_or(
- std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
- ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
+ if (Settings::values.custom_rtc.UsingGlobal()) {
+ ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked);
+ } else {
+ ui->custom_rtc_checkbox->setCheckState(
+ Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked);
+ ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value());
+ if (Settings::values.custom_rtc.GetValue().has_value()) {
+ ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
+ }
+ }
+ }
}
void ConfigureSystem::ReadSystemSettings() {}
@@ -83,22 +121,78 @@ void ConfigureSystem::ApplyConfiguration() {
return;
}
- Settings::values.language_index = ui->combo_language->currentIndex();
- Settings::values.region_index = ui->combo_region->currentIndex();
- Settings::values.time_zone_index = ui->combo_time_zone->currentIndex();
- Settings::values.sound_index = ui->combo_sound->currentIndex();
+ if (Settings::configuring_global) {
+ // Guard if during game and set to game-specific value
+ if (Settings::values.language_index.UsingGlobal()) {
+ Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
+ }
+ if (Settings::values.region_index.UsingGlobal()) {
+ Settings::values.region_index.SetValue(ui->combo_region->currentIndex());
+ }
+ if (Settings::values.time_zone_index.UsingGlobal()) {
+ Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex());
+ }
+ if (Settings::values.sound_index.UsingGlobal()) {
+ Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex());
+ }
+
+ if (Settings::values.rng_seed.UsingGlobal()) {
+ if (ui->rng_seed_checkbox->isChecked()) {
+ Settings::values.rng_seed.SetValue(
+ ui->rng_seed_edit->text().toULongLong(nullptr, 16));
+ } else {
+ Settings::values.rng_seed.SetValue(std::nullopt);
+ }
+ }
- if (ui->rng_seed_checkbox->isChecked()) {
- Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
+ if (Settings::values.custom_rtc.UsingGlobal()) {
+ if (ui->custom_rtc_checkbox->isChecked()) {
+ Settings::values.custom_rtc.SetValue(
+ std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
+ } else {
+ Settings::values.custom_rtc.SetValue(std::nullopt);
+ }
+ }
} else {
- Settings::values.rng_seed = std::nullopt;
- }
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index,
+ ui->combo_language);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
+ ui->combo_time_zone);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound);
+
+ switch (ui->rng_seed_checkbox->checkState()) {
+ case Qt::Checked:
+ Settings::values.rng_seed.SetGlobal(false);
+ Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16));
+ break;
+ case Qt::Unchecked:
+ Settings::values.rng_seed.SetGlobal(false);
+ Settings::values.rng_seed.SetValue(std::nullopt);
+ break;
+ case Qt::PartiallyChecked:
+ Settings::values.rng_seed.SetGlobal(false);
+ Settings::values.rng_seed.SetValue(std::nullopt);
+ Settings::values.rng_seed.SetGlobal(true);
+ break;
+ }
- if (ui->custom_rtc_checkbox->isChecked()) {
- Settings::values.custom_rtc =
- std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch());
- } else {
- Settings::values.custom_rtc = std::nullopt;
+ switch (ui->custom_rtc_checkbox->checkState()) {
+ case Qt::Checked:
+ Settings::values.custom_rtc.SetGlobal(false);
+ Settings::values.custom_rtc.SetValue(
+ std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
+ break;
+ case Qt::Unchecked:
+ Settings::values.custom_rtc.SetGlobal(false);
+ Settings::values.custom_rtc.SetValue(std::nullopt);
+ break;
+ case Qt::PartiallyChecked:
+ Settings::values.custom_rtc.SetGlobal(false);
+ Settings::values.custom_rtc.SetValue(std::nullopt);
+ Settings::values.custom_rtc.SetGlobal(true);
+ break;
+ }
}
Settings::Apply();
@@ -120,3 +214,25 @@ void ConfigureSystem::RefreshConsoleID() {
ui->label_console_id->setText(
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
}
+
+void ConfigureSystem::SetupPerGameUI() {
+ if (Settings::configuring_global) {
+ ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
+ ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
+ ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
+ ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
+ ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal());
+ ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal());
+ ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal());
+ ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal());
+
+ return;
+ }
+
+ ConfigurationShared::InsertGlobalItem(ui->combo_language);
+ ConfigurationShared::InsertGlobalItem(ui->combo_region);
+ ConfigurationShared::InsertGlobalItem(ui->combo_time_zone);
+ ConfigurationShared::InsertGlobalItem(ui->combo_sound);
+ ui->rng_seed_checkbox->setTristate(true);
+ ui->custom_rtc_checkbox->setTristate(true);
+}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 26d42d5c5..f317ef8b5 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -32,6 +32,8 @@ private:
void RefreshConsoleID();
+ void SetupPerGameUI();
+
std::unique_ptr<Ui::ConfigureSystem> ui;
bool enabled = false;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index c1ea25fb8..9bb0a0109 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -2,10 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <fmt/format.h>
+
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/util/util.h"
#include "common/assert.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/mutex.h"
@@ -59,8 +62,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
std::size_t row = 0;
auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) {
for (std::size_t i = 0; i < threads.size(); ++i) {
- item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
- item_list.back()->row = row;
+ if (!threads[i]->IsHLEThread()) {
+ item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
+ item_list.back()->row = row;
+ }
++row;
}
};
@@ -114,20 +119,21 @@ QString WaitTreeCallstack::GetText() const {
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list;
- constexpr std::size_t BaseRegister = 29;
- auto& memory = Core::System::GetInstance().Memory();
- u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister];
+ if (thread.IsHLEThread()) {
+ return list;
+ }
- while (base_pointer != 0) {
- const u64 lr = memory.Read64(base_pointer + sizeof(u64));
- if (lr == 0) {
- break;
- }
+ if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) {
+ return list;
+ }
- list.push_back(std::make_unique<WaitTreeText>(
- tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'})));
+ auto backtrace = Core::ARM_Interface::GetBacktraceFromContext(Core::System::GetInstance(),
+ thread.GetContext64());
- base_pointer = memory.Read64(base_pointer);
+ for (auto& entry : backtrace) {
+ std::string s = fmt::format("{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
+ entry.original_address, entry.offset, entry.name);
+ list.push_back(std::make_unique<WaitTreeText>(QString::fromStdString(s)));
}
return list;
@@ -206,7 +212,15 @@ QString WaitTreeThread::GetText() const {
status = tr("running");
break;
case Kernel::ThreadStatus::Ready:
- status = tr("ready");
+ if (!thread.IsPaused()) {
+ if (thread.WasRunning()) {
+ status = tr("running");
+ } else {
+ status = tr("ready");
+ }
+ } else {
+ status = tr("paused");
+ }
break;
case Kernel::ThreadStatus::Paused:
status = tr("paused");
@@ -254,7 +268,15 @@ QColor WaitTreeThread::GetColor() const {
case Kernel::ThreadStatus::Running:
return QColor(Qt::GlobalColor::darkGreen);
case Kernel::ThreadStatus::Ready:
- return QColor(Qt::GlobalColor::darkBlue);
+ if (!thread.IsPaused()) {
+ if (thread.WasRunning()) {
+ return QColor(Qt::GlobalColor::darkGreen);
+ } else {
+ return QColor(Qt::GlobalColor::darkBlue);
+ }
+ } else {
+ return QColor(Qt::GlobalColor::lightGray);
+ }
case Kernel::ThreadStatus::Paused:
return QColor(Qt::GlobalColor::lightGray);
case Kernel::ThreadStatus::WaitHLEEvent:
@@ -319,7 +341,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) {
list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(),
- thread.IsSleepingOnWait()));
+ thread.IsWaitingSync()));
}
list.push_back(std::make_unique<WaitTreeCallstack>(thread));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 270cccc77..4d501a8f9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -16,7 +16,7 @@
#include "applets/software_keyboard.h"
#include "applets/web_browser.h"
#include "configuration/configure_input.h"
-#include "configuration/configure_per_general.h"
+#include "configuration/configure_per_game.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/general_frontend.h"
@@ -56,6 +56,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QShortcut>
#include <QStatusBar>
#include <QSysInfo>
+#include <QUrl>
#include <QtConcurrent/QtConcurrent>
#include <fmt/format.h>
@@ -217,7 +218,20 @@ GMainWindow::GMainWindow()
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch,
Common::g_scm_desc);
#ifdef ARCHITECTURE_x86_64
- LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
+ const auto& caps = Common::GetCPUCaps();
+ std::string cpu_string = caps.cpu_string;
+ if (caps.avx || caps.avx2 || caps.avx512) {
+ cpu_string += " | AVX";
+ if (caps.avx512) {
+ cpu_string += "512";
+ } else if (caps.avx2) {
+ cpu_string += '2';
+ }
+ if (caps.fma || caps.fma4) {
+ cpu_string += " | FMA";
+ }
+ }
+ LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
#endif
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GB",
@@ -520,14 +534,36 @@ void GMainWindow::InitializeWidgets() {
if (emulation_running) {
return;
}
- Settings::values.use_asynchronous_gpu_emulation =
- !Settings::values.use_asynchronous_gpu_emulation;
- async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
+ bool is_async = !Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
+ Settings::values.use_multi_core.GetValue();
+ Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
+ async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
Settings::Apply();
});
async_status_button->setText(tr("ASYNC"));
async_status_button->setCheckable(true);
- async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
+ async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
+
+ // Setup Multicore button
+ multicore_status_button = new QPushButton();
+ multicore_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
+ multicore_status_button->setFocusPolicy(Qt::NoFocus);
+ connect(multicore_status_button, &QPushButton::clicked, [&] {
+ if (emulation_running) {
+ return;
+ }
+ Settings::values.use_multi_core.SetValue(!Settings::values.use_multi_core.GetValue());
+ bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
+ Settings::values.use_multi_core.GetValue();
+ Settings::values.use_asynchronous_gpu_emulation.SetValue(is_async);
+ async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
+ multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
+ Settings::Apply();
+ });
+ multicore_status_button->setText(tr("MULTICORE"));
+ multicore_status_button->setCheckable(true);
+ multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
+ statusBar()->insertPermanentWidget(0, multicore_status_button);
statusBar()->insertPermanentWidget(0, async_status_button);
// Setup Renderer API button
@@ -545,16 +581,16 @@ void GMainWindow::InitializeWidgets() {
renderer_status_button->setCheckable(false);
renderer_status_button->setDisabled(true);
#else
- renderer_status_button->setChecked(Settings::values.renderer_backend ==
+ renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
connect(renderer_status_button, &QPushButton::clicked, [=] {
if (emulation_running) {
return;
}
if (renderer_status_button->isChecked()) {
- Settings::values.renderer_backend = Settings::RendererBackend::Vulkan;
+ Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
} else {
- Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
+ Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
}
Settings::Apply();
@@ -653,6 +689,11 @@ void GMainWindow::InitializeHotkeys() {
ui.action_Capture_Screenshot->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, capture_screenshot));
+ ui.action_Fullscreen->setShortcut(
+ hotkey_registry.GetHotkey(main_window, fullscreen, this)->key());
+ ui.action_Fullscreen->setShortcutContext(
+ hotkey_registry.GetShortcutContext(main_window, fullscreen));
+
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Load File"), this),
&QShortcut::activated, this, &GMainWindow::OnMenuLoadFile);
connect(
@@ -686,24 +727,24 @@ void GMainWindow::InitializeHotkeys() {
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this),
&QShortcut::activated, this, [&] {
- Settings::values.use_frame_limit = !Settings::values.use_frame_limit;
+ Settings::values.use_frame_limit.SetValue(
+ !Settings::values.use_frame_limit.GetValue());
UpdateStatusBar();
});
- // TODO: Remove this comment/static whenever the next major release of
- // MSVC occurs and we make it a requirement (see:
- // https://developercommunity.visualstudio.com/content/problem/93922/constexprs-are-trying-to-be-captured-in-lambda-fun.html)
- static constexpr u16 SPEED_LIMIT_STEP = 5;
+ constexpr u16 SPEED_LIMIT_STEP = 5;
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this),
&QShortcut::activated, this, [&] {
- if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) {
- Settings::values.frame_limit += SPEED_LIMIT_STEP;
+ if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) {
+ Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP +
+ Settings::values.frame_limit.GetValue());
UpdateStatusBar();
}
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this),
&QShortcut::activated, this, [&] {
- if (Settings::values.frame_limit > SPEED_LIMIT_STEP) {
- Settings::values.frame_limit -= SPEED_LIMIT_STEP;
+ if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) {
+ Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() -
+ SPEED_LIMIT_STEP);
UpdateStatusBar();
}
});
@@ -715,7 +756,7 @@ void GMainWindow::InitializeHotkeys() {
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
&QShortcut::activated, this, [&] {
- if (emu_thread->IsRunning()) {
+ if (emu_thread != nullptr && emu_thread->IsRunning()) {
OnCaptureScreenshot();
}
});
@@ -726,6 +767,9 @@ void GMainWindow::InitializeHotkeys() {
Settings::values.use_docked_mode);
dock_status_button->setChecked(Settings::values.use_docked_mode);
});
+ connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
+ &QShortcut::activated, this,
+ [] { Settings::values.audio_muted = !Settings::values.audio_muted; });
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -826,6 +870,10 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Stop, &QAction::triggered, this, &GMainWindow::OnStopGame);
connect(ui.action_Report_Compatibility, &QAction::triggered, this,
&GMainWindow::OnMenuReportCompatibility);
+ connect(ui.action_Open_Mods_Page, &QAction::triggered, this, &GMainWindow::OnOpenModsPage);
+ connect(ui.action_Open_Quickstart_Guide, &QAction::triggered, this,
+ &GMainWindow::OnOpenQuickstartGuide);
+ connect(ui.action_Open_FAQ, &QAction::triggered, this, &GMainWindow::OnOpenFAQ);
connect(ui.action_Restart, &QAction::triggered, this, [this] { BootGame(QString(game_path)); });
connect(ui.action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure);
@@ -839,10 +887,6 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Reset_Window_Size, &QAction::triggered, this, &GMainWindow::ResetWindowSize);
// Fullscreen
- ui.action_Fullscreen->setShortcut(
- hotkey_registry
- .GetHotkey(QStringLiteral("Main Window"), QStringLiteral("Fullscreen"), this)
- ->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Movie
@@ -910,6 +954,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
nullptr, // E-Commerce
});
+ system.RegisterHostThread();
+
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
const auto drd_callout =
@@ -996,6 +1042,17 @@ void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
+ u64 title_id{0};
+
+ const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
+ const auto loader = Loader::GetLoader(v_file);
+ if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
+ // Load per game settings
+ Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
+ }
+
+ Settings::LogSettings();
+
if (UISettings::values.select_user_on_boot) {
SelectAndSetCurrentUser();
}
@@ -1020,12 +1077,14 @@ void GMainWindow::BootGame(const QString& filename) {
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
// Update the GUI
+ UpdateStatusButtons();
if (ui.action_Single_Window_Mode->isChecked()) {
game_list->hide();
game_list_placeholder->hide();
}
status_bar_update_timer.start(2000);
async_status_button->setDisabled(true);
+ multicore_status_button->setDisabled(true);
renderer_status_button->setDisabled(true);
if (UISettings::values.hide_mouse) {
@@ -1034,20 +1093,20 @@ void GMainWindow::BootGame(const QString& filename) {
ui.centralwidget->setMouseTracking(true);
}
- const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
-
std::string title_name;
+ std::string title_version;
const auto res = Core::System::GetInstance().GetGameName(title_name);
- if (res != Loader::ResultStatus::Success) {
- const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
- if (metadata.first != nullptr)
- title_name = metadata.first->GetApplicationName();
- if (title_name.empty())
- title_name = FileUtil::GetFilename(filename.toStdString());
+ const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
+ if (metadata.first != nullptr) {
+ title_version = metadata.first->GetVersionString();
+ title_name = metadata.first->GetApplicationName();
+ }
+ if (res != Loader::ResultStatus::Success || title_name.empty()) {
+ title_name = FileUtil::GetFilename(filename.toStdString());
}
- LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name);
- UpdateWindowTitle(QString::fromStdString(title_name));
+ LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
+ UpdateWindowTitle(title_name, title_version);
loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
loading_screen->show();
@@ -1113,6 +1172,7 @@ void GMainWindow::ShutdownGame() {
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
async_status_button->setEnabled(true);
+ multicore_status_button->setEnabled(true);
#ifdef HAS_VULKAN
renderer_status_button->setEnabled(true);
#endif
@@ -1474,7 +1534,7 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
return;
}
- ConfigurePerGameGeneral dialog(this, title_id);
+ ConfigurePerGame dialog(this, title_id);
dialog.LoadFromFile(v_file);
auto result = dialog.exec();
if (result == QDialog::Accepted) {
@@ -1485,7 +1545,14 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
game_list->PopulateAsync(UISettings::values.game_dirs);
}
- config->Save();
+ // Do not cause the global config to write local settings into the config file
+ Settings::RestoreGlobalState();
+
+ if (!Core::System::GetInstance().IsPoweredOn()) {
+ config->Save();
+ }
+ } else {
+ Settings::RestoreGlobalState();
}
}
@@ -1772,6 +1839,9 @@ void GMainWindow::OnStopGame() {
}
ShutdownGame();
+
+ Settings::RestoreGlobalState();
+ UpdateStatusButtons();
}
void GMainWindow::OnLoadComplete() {
@@ -1797,6 +1867,26 @@ void GMainWindow::OnMenuReportCompatibility() {
}
}
+void GMainWindow::OpenURL(const QUrl& url) {
+ const bool open = QDesktopServices::openUrl(url);
+ if (!open) {
+ QMessageBox::warning(this, tr("Error opening URL"),
+ tr("Unable to open the URL \"%1\".").arg(url.toString()));
+ }
+}
+
+void GMainWindow::OnOpenModsPage() {
+ OpenURL(QUrl(QStringLiteral("https://github.com/yuzu-emu/yuzu/wiki/Switch-Mods")));
+}
+
+void GMainWindow::OnOpenQuickstartGuide() {
+ OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/help/quickstart/")));
+}
+
+void GMainWindow::OnOpenFAQ() {
+ OpenURL(QUrl(QStringLiteral("https://yuzu-emu.org/wiki/faq/")));
+}
+
void GMainWindow::ToggleFullscreen() {
if (!emulation_running) {
return;
@@ -1859,7 +1949,7 @@ void GMainWindow::ToggleWindowMode() {
void GMainWindow::ResetWindowSize() {
const auto aspect_ratio = Layout::EmulationAspectRatio(
- static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio),
+ static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio.GetValue()),
static_cast<float>(Layout::ScreenUndocked::Height) / Layout::ScreenUndocked::Width);
if (!ui.action_Single_Window_Mode->isChecked()) {
render_window->resize(Layout::ScreenUndocked::Height / aspect_ratio,
@@ -1907,12 +1997,7 @@ void GMainWindow::OnConfigure() {
ui.centralwidget->setMouseTracking(false);
}
- dock_status_button->setChecked(Settings::values.use_docked_mode);
- async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
-#ifdef HAS_VULKAN
- renderer_status_button->setChecked(Settings::values.renderer_backend ==
- Settings::RendererBackend::Vulkan);
-#endif
+ UpdateStatusButtons();
}
void GMainWindow::OnLoadAmiibo() {
@@ -1995,7 +2080,8 @@ void GMainWindow::OnCaptureScreenshot() {
OnStartGame();
}
-void GMainWindow::UpdateWindowTitle(const QString& title_name) {
+void GMainWindow::UpdateWindowTitle(const std::string& title_name,
+ const std::string& title_version) {
const auto full_name = std::string(Common::g_build_fullname);
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
@@ -2004,7 +2090,7 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
const auto date =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd")).toStdString();
- if (title_name.isEmpty()) {
+ if (title_name.empty()) {
const auto fmt = std::string(Common::g_title_bar_format_idle);
setWindowTitle(QString::fromStdString(fmt::format(fmt.empty() ? "yuzu {0}| {1}-{2}" : fmt,
full_name, branch_name, description,
@@ -2012,8 +2098,8 @@ void GMainWindow::UpdateWindowTitle(const QString& title_name) {
} else {
const auto fmt = std::string(Common::g_title_bar_format_running);
setWindowTitle(QString::fromStdString(
- fmt::format(fmt.empty() ? "yuzu {0}| {3} | {1}-{2}" : fmt, full_name, branch_name,
- description, title_name.toStdString(), date, build_id)));
+ fmt::format(fmt.empty() ? "yuzu {0}| {3} | {6} | {1}-{2}" : fmt, full_name, branch_name,
+ description, title_name, date, build_id, title_version)));
}
}
@@ -2025,21 +2111,34 @@ void GMainWindow::UpdateStatusBar() {
auto results = Core::System::GetInstance().GetAndResetPerfStats();
- if (Settings::values.use_frame_limit) {
+ if (Settings::values.use_frame_limit.GetValue()) {
emu_speed_label->setText(tr("Speed: %1% / %2%")
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
- .arg(Settings::values.frame_limit));
+ .arg(Settings::values.frame_limit.GetValue()));
} else {
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
}
game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0));
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
- emu_speed_label->setVisible(true);
+ emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
game_fps_label->setVisible(true);
emu_frametime_label->setVisible(true);
}
+void GMainWindow::UpdateStatusButtons() {
+ dock_status_button->setChecked(Settings::values.use_docked_mode);
+ multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
+ Settings::values.use_asynchronous_gpu_emulation.SetValue(
+ Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
+ Settings::values.use_multi_core.GetValue());
+ async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation.GetValue());
+#ifdef HAS_VULKAN
+ renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
+ Settings::RendererBackend::Vulkan);
+#endif
+}
+
void GMainWindow::HideMouseCursor() {
if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
mouse_hide_timer.stop();
@@ -2123,6 +2222,9 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
if (answer == QMessageBox::Yes) {
if (emu_thread) {
ShutdownGame();
+
+ Settings::RestoreGlobalState();
+ UpdateStatusButtons();
}
} else {
// Only show the message if the game is still running.
@@ -2154,7 +2256,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
"title.keys_autogenerated");
}
- Core::Crypto::KeyManager keys{};
+ Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
if (keys.BaseDeriveNecessary()) {
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
@@ -2285,9 +2387,13 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
hotkey_registry.SaveHotkeys();
// Shutdown session if the emu thread is active...
- if (emu_thread != nullptr)
+ if (emu_thread != nullptr) {
ShutdownGame();
+ Settings::RestoreGlobalState();
+ UpdateStatusButtons();
+ }
+
render_window->close();
QWidget::closeEvent(event);
@@ -2467,8 +2573,6 @@ int main(int argc, char* argv[]) {
QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
&GMainWindow::OnAppFocusStateChanged);
- Settings::LogSettings();
-
int result = app.exec();
detached_tasks.WaitForAllTasks();
return result;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 4f4c8ddbe..8e3d39c38 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -181,6 +181,9 @@ private slots:
void OnPauseGame();
void OnStopGame();
void OnMenuReportCompatibility();
+ void OnOpenModsPage();
+ void OnOpenQuickstartGuide();
+ void OnOpenFAQ();
/// Called whenever a user selects a game in the game list widget.
void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
@@ -215,10 +218,13 @@ private slots:
private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
- void UpdateWindowTitle(const QString& title_name = {});
+ void UpdateWindowTitle(const std::string& title_name = {},
+ const std::string& title_version = {});
void UpdateStatusBar();
+ void UpdateStatusButtons();
void HideMouseCursor();
void ShowMouseCursor();
+ void OpenURL(const QUrl& url);
Ui::MainWindow ui;
@@ -234,6 +240,7 @@ private:
QLabel* game_fps_label = nullptr;
QLabel* emu_frametime_label = nullptr;
QPushButton* async_status_button = nullptr;
+ QPushButton* multicore_status_button = nullptr;
QPushButton* renderer_status_button = nullptr;
QPushButton* dock_status_button = nullptr;
QTimer status_bar_update_timer;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 97c90f50b..bee6e107e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -113,6 +113,9 @@
<string>&amp;Help</string>
</property>
<addaction name="action_Report_Compatibility"/>
+ <addaction name="action_Open_Mods_Page"/>
+ <addaction name="action_Open_Quickstart_Guide"/>
+ <addaction name="action_Open_FAQ"/>
<addaction name="separator"/>
<addaction name="action_About"/>
</widget>
@@ -256,6 +259,21 @@
<bool>false</bool>
</property>
</action>
+ <action name="action_Open_Mods_Page">
+ <property name="text">
+ <string>Open Mods Page</string>
+ </property>
+ </action>
+ <action name="action_Open_Quickstart_Guide">
+ <property name="text">
+ <string>Open Quickstart Guide</string>
+ </property>
+ </action>
+ <action name="action_Open_FAQ">
+ <property name="text">
+ <string>FAQ</string>
+ </property>
+ </action>
<action name="action_Open_yuzu_Folder">
<property name="text">
<string>Open yuzu Folder</string>
diff --git a/src/yuzu/yuzu.rc b/src/yuzu/yuzu.rc
index 1b253653f..4a3645a71 100644
--- a/src/yuzu/yuzu.rc
+++ b/src/yuzu/yuzu.rc
@@ -16,4 +16,4 @@ IDI_ICON1 ICON "../../dist/yuzu.ico"
// RT_MANIFEST
//
-1 RT_MANIFEST "../../dist/yuzu.manifest"
+0 RT_MANIFEST "../../dist/yuzu.manifest"