diff options
Diffstat (limited to 'src/yuzu')
50 files changed, 1001 insertions, 1210 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 4cab599b4..2eb86d6e5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -31,6 +31,8 @@ add_executable(yuzu configuration/configure_general.h configuration/configure_graphics.cpp configuration/configure_graphics.h + configuration/configure_hotkeys.cpp + configuration/configure_hotkeys.h configuration/configure_input.cpp configuration/configure_input.h configuration/configure_input_player.cpp @@ -54,8 +56,6 @@ add_executable(yuzu debugger/graphics/graphics_breakpoints.cpp debugger/graphics/graphics_breakpoints.h debugger/graphics/graphics_breakpoints_p.h - debugger/graphics/graphics_surface.cpp - debugger/graphics/graphics_surface.h debugger/console.cpp debugger/console.h debugger/profiler.cpp @@ -78,6 +78,8 @@ add_executable(yuzu ui_settings.h util/limitable_input_dialog.cpp util/limitable_input_dialog.h + util/sequence_dialog/sequence_dialog.cpp + util/sequence_dialog/sequence_dialog.h util/spinbox.cpp util/spinbox.h util/util.cpp @@ -95,6 +97,7 @@ set(UIS configuration/configure_gamelist.ui configuration/configure_general.ui configuration/configure_graphics.ui + configuration/configure_hotkeys.ui configuration/configure_input.ui configuration/configure_input_player.ui configuration/configure_input_simple.ui @@ -105,7 +108,6 @@ set(UIS configuration/configure_touchscreen_advanced.ui configuration/configure_web.ui compatdb.ui - hotkeys.ui loading_screen.ui main.ui ) diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 5c1b65a2c..743b24d76 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -4,6 +4,7 @@ #include <mutex> #include <QDialogButtonBox> +#include <QHeaderView> #include <QLabel> #include <QLineEdit> #include <QScrollArea> @@ -58,10 +59,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) scroll_area = new QScrollArea; - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); @@ -163,6 +161,6 @@ void QtProfileSelector::SelectProfile( void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; callback(uuid); } diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h index 868573324..1c2922e54 100644 --- a/src/yuzu/applets/profile_select.h +++ b/src/yuzu/applets/profile_select.h @@ -7,6 +7,7 @@ #include <vector> #include <QDialog> #include <QList> +#include <QTreeView> #include "core/frontend/applets/profile_select.h" class GMainWindow; @@ -16,7 +17,6 @@ class QLabel; class QScrollArea; class QStandardItem; class QStandardItemModel; -class QTreeView; class QVBoxLayout; class QtProfileSelectionDialog final : public QDialog { diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 8a26fdff1..f3eb29b25 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -75,13 +75,13 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); }); - buttons = new QDialogButtonBox; - buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); - buttons->addButton(parameters.submit_text.empty() - ? tr("OK") - : QString::fromStdU16String(parameters.submit_text), - QDialogButtonBox::AcceptRole); - + buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); + if (parameters.submit_text.empty()) { + buttons->addButton(QDialogButtonBox::Ok); + } else { + buttons->addButton(QString::fromStdU16String(parameters.submit_text), + QDialogButtonBox::AcceptRole); + } connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept); connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject); layout->addWidget(header_label); @@ -141,12 +141,12 @@ void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; text_output(text); } void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_check(); } diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp index 6a9138d53..ac80b2fa2 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/web_browser.cpp @@ -56,6 +56,8 @@ constexpr char NX_SHIM_INJECT_SCRIPT[] = R"( window.nx.endApplet = function() { applet_done = true; }; + + window.onkeypress = function(e) { if (e.keyCode === 13) { applet_done = true; } }; )"; QString GetNXShimInjectionScript() { @@ -102,12 +104,12 @@ void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_r void QtWebBrowser::MainWindowUnpackRomFS() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; unpack_romfs_callback(); } void QtWebBrowser::MainWindowFinishedBrowsing() { // Acquire the HLE mutex - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; finished_callback(); } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e1825e607..c29f2d2dc 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -27,16 +27,25 @@ EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} void EmuThread::run() { - if (!Settings::values.use_multi_core) { - // Single core mode must acquire OpenGL context for entire emulation session - render_window->MakeCurrent(); - } + render_window->MakeCurrent(); MicroProfileOnThreadCreate("EmuThread"); - stop_run = false; + emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + + Core::System::GetInstance().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); + + if (Settings::values.use_asynchronous_gpu_emulation) { + // Release OpenGL context for the GPU thread + render_window->DoneCurrent(); + } - // holds whether the cpu was running during the last iteration, + // 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; @@ -65,7 +74,7 @@ void EmuThread::run() { was_active = false; } else { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); } } @@ -184,7 +193,6 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); - InputCommon::StartJoystickEventHandler(); connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent), &GMainWindow::OnLoadComplete); } diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 288ce1572..9608b959f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -24,6 +24,10 @@ class GRenderWindow; class QSurface; class QOpenGLContext; +namespace VideoCore { +enum class LoadCallbackStage; +} + class EmuThread : public QThread { Q_OBJECT @@ -51,7 +55,7 @@ public: * @note This function is thread-safe */ void SetRunning(bool running) { - std::unique_lock<std::mutex> lock(running_mutex); + std::unique_lock lock{running_mutex}; this->running = running; lock.unlock(); running_cv.notify_all(); @@ -77,7 +81,7 @@ public: private: bool exec_step = false; bool running = false; - std::atomic<bool> stop_run{false}; + std::atomic_bool stop_run{false}; std::mutex running_mutex; std::condition_variable running_cv; @@ -103,6 +107,8 @@ signals: void DebugModeLeft(); void ErrorThrown(Core::System::ResultStatus, std::string); + + void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); }; class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index 5f0896f84..c8b0a5ec0 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp @@ -53,15 +53,15 @@ void CompatDB::Submit() { case CompatDBPage::Final: back(); LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); - Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", - compatibility->checkedId()); + Core::System::GetInstance().TelemetrySession().AddField( + Telemetry::FieldType::UserFeedback, "Compatibility", compatibility->checkedId()); button(NextButton)->setEnabled(false); button(NextButton)->setText(tr("Submitting")); button(QWizard::CancelButton)->setVisible(false); testcase_watcher.setFuture(QtConcurrent::run( - [this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); + [] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); break; default: LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index ddf4cf552..ca60bc0c9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <QKeySequence> #include <QSettings> #include "common/file_util.h" #include "configure_input_simple.h" @@ -9,7 +11,6 @@ #include "core/hle/service/hid/controllers/npad.h" #include "input_common/main.h" #include "yuzu/configuration/config.h" -#include "yuzu/ui_settings.h" Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. @@ -17,7 +18,6 @@ Config::Config() { FileUtil::CreateFullPath(qt_config_loc); qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); - Reload(); } @@ -205,11 +205,32 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, }; +// This shouldn't have anything except static initializers (no functions). So +// QKeySequnce(...).toString() is NOT ALLOWED HERE. +// 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. +const std::array<UISettings::Shortcut, 15> Config::default_hotkeys{ + {{"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}}, + {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}}, + {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}}, + {"Exit yuzu", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}}, + {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}}, + {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}}, + {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}}, + {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}}, + {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}}, + {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}}, + {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}}, + {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}}, + {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}}, + {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}}, + {"Change Docked Mode", "Main Window", {"F10", Qt::ApplicationShortcut}}}}; + void Config::ReadPlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { auto& player = Settings::values.players[p]; - player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); + player.connected = ReadSetting(QString("player_%1_connected").arg(p), false).toBool(); player.type = static_cast<Settings::ControllerType>( qt_config @@ -269,7 +290,7 @@ void Config::ReadPlayerValues() { } void Config::ReadDebugValues() { - Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool(); + Settings::values.debug_pad_enabled = ReadSetting("debug_pad_enabled", false).toBool(); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); Settings::values.debug_pad_buttons[i] = @@ -298,7 +319,7 @@ void Config::ReadDebugValues() { } void Config::ReadKeyboardValues() { - Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool(); + Settings::values.keyboard_enabled = ReadSetting("keyboard_enabled", false).toBool(); std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); @@ -311,7 +332,7 @@ void Config::ReadKeyboardValues() { } void Config::ReadMouseValues() { - Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool(); + Settings::values.mouse_enabled = ReadSetting("mouse_enabled", false).toBool(); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); @@ -327,16 +348,14 @@ void Config::ReadMouseValues() { } void Config::ReadTouchscreenValues() { - Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool(); + Settings::values.touchscreen.enabled = ReadSetting("touchscreen_enabled", true).toBool(); Settings::values.touchscreen.device = - qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString(); - - Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt(); - Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt(); - Settings::values.touchscreen.diameter_x = - qt_config->value("touchscreen_diameter_x", 15).toUInt(); - Settings::values.touchscreen.diameter_y = - qt_config->value("touchscreen_diameter_y", 15).toUInt(); + ReadSetting("touchscreen_device", "engine:emu_window").toString().toStdString(); + + Settings::values.touchscreen.finger = ReadSetting("touchscreen_finger", 0).toUInt(); + Settings::values.touchscreen.rotation_angle = ReadSetting("touchscreen_angle", 0).toUInt(); + Settings::values.touchscreen.diameter_x = ReadSetting("touchscreen_diameter_x", 15).toUInt(); + Settings::values.touchscreen.diameter_y = ReadSetting("touchscreen_diameter_y", 15).toUInt(); qt_config->endGroup(); } @@ -357,38 +376,41 @@ void Config::ReadValues() { ReadTouchscreenValues(); Settings::values.motion_device = - qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") + ReadSetting("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") .toString() .toStdString(); qt_config->beginGroup("Core"); - Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); - Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); + Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); + Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("Renderer"); - Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); - Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); - Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); + Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); + Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); + Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); + Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); Settings::values.use_accurate_gpu_emulation = - qt_config->value("use_accurate_gpu_emulation", false).toBool(); + ReadSetting("use_accurate_gpu_emulation", false).toBool(); + Settings::values.use_asynchronous_gpu_emulation = + ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); - Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); - Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); - Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); + Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); + Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); + Settings::values.bg_blue = ReadSetting("bg_blue", 0.0).toFloat(); qt_config->endGroup(); qt_config->beginGroup("Audio"); - Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); + Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString(); Settings::values.enable_audio_stretching = - qt_config->value("enable_audio_stretching", true).toBool(); + ReadSetting("enable_audio_stretching", true).toBool(); Settings::values.audio_device_id = - qt_config->value("output_device", "auto").toString().toStdString(); - Settings::values.volume = qt_config->value("volume", 1).toFloat(); + ReadSetting("output_device", "auto").toString().toStdString(); + Settings::values.volume = ReadSetting("volume", 1).toFloat(); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); - Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); + Settings::values.use_virtual_sd = ReadSetting("use_virtual_sd", true).toBool(); FileUtil::GetUserPath( FileUtil::UserPath::NANDDir, qt_config @@ -406,30 +428,29 @@ void Config::ReadValues() { qt_config->endGroup(); qt_config->beginGroup("Core"); - Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); - Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); + Settings::values.use_cpu_jit = ReadSetting("use_cpu_jit", true).toBool(); + Settings::values.use_multi_core = ReadSetting("use_multi_core", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("System"); - Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); - Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); + Settings::values.use_docked_mode = ReadSetting("use_docked_mode", false).toBool(); - Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, - Service::Account::MAX_USERS - 1); + Settings::values.current_user = + std::clamp<int>(ReadSetting("current_user", 0).toInt(), 0, Service::Account::MAX_USERS - 1); - Settings::values.language_index = qt_config->value("language_index", 1).toInt(); + Settings::values.language_index = ReadSetting("language_index", 1).toInt(); - const auto rng_seed_enabled = qt_config->value("rng_seed_enabled", false).toBool(); + const auto rng_seed_enabled = ReadSetting("rng_seed_enabled", false).toBool(); if (rng_seed_enabled) { - Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); + Settings::values.rng_seed = ReadSetting("rng_seed", 0).toULongLong(); } else { Settings::values.rng_seed = std::nullopt; } - const auto custom_rtc_enabled = qt_config->value("custom_rtc_enabled", false).toBool(); + const auto custom_rtc_enabled = ReadSetting("custom_rtc_enabled", false).toBool(); if (custom_rtc_enabled) { Settings::values.custom_rtc = - std::chrono::seconds(qt_config->value("custom_rtc", 0).toULongLong()); + std::chrono::seconds(ReadSetting("custom_rtc", 0).toULongLong()); } else { Settings::values.custom_rtc = std::nullopt; } @@ -437,35 +458,35 @@ void Config::ReadValues() { qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); - Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); - Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool(); + Settings::values.log_filter = ReadSetting("log_filter", "*:Info").toString().toStdString(); + Settings::values.use_dev_keys = ReadSetting("use_dev_keys", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("Debugging"); - Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); - Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); - Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); - Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool(); - Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); + Settings::values.use_gdbstub = ReadSetting("use_gdbstub", false).toBool(); + Settings::values.gdbstub_port = ReadSetting("gdbstub_port", 24689).toInt(); + Settings::values.program_args = ReadSetting("program_args", "").toString().toStdString(); + Settings::values.dump_exefs = ReadSetting("dump_exefs", false).toBool(); + Settings::values.dump_nso = ReadSetting("dump_nso", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("WebService"); - Settings::values.enable_telemetry = qt_config->value("enable_telemetry", true).toBool(); + Settings::values.enable_telemetry = ReadSetting("enable_telemetry", true).toBool(); Settings::values.web_api_url = - qt_config->value("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); - Settings::values.yuzu_username = qt_config->value("yuzu_username").toString().toStdString(); - Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); + ReadSetting("web_api_url", "https://api.yuzu-emu.org").toString().toStdString(); + Settings::values.yuzu_username = ReadSetting("yuzu_username").toString().toStdString(); + Settings::values.yuzu_token = ReadSetting("yuzu_token").toString().toStdString(); qt_config->endGroup(); const auto size = qt_config->beginReadArray("DisabledAddOns"); for (int i = 0; i < size; ++i) { qt_config->setArrayIndex(i); - const auto title_id = qt_config->value("title_id", 0).toULongLong(); + const auto title_id = ReadSetting("title_id", 0).toULongLong(); std::vector<std::string> out; const auto d_size = qt_config->beginReadArray("disabled"); for (int j = 0; j < d_size; ++j) { qt_config->setArrayIndex(j); - out.push_back(qt_config->value("d", "").toString().toStdString()); + out.push_back(ReadSetting("d", "").toString().toStdString()); } qt_config->endArray(); Settings::values.disabled_addons.insert_or_assign(title_id, out); @@ -473,72 +494,64 @@ void Config::ReadValues() { qt_config->endArray(); qt_config->beginGroup("UI"); - UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); + UISettings::values.theme = ReadSetting("theme", UISettings::themes[0].second).toString(); UISettings::values.enable_discord_presence = - qt_config->value("enable_discord_presence", true).toBool(); + ReadSetting("enable_discord_presence", true).toBool(); UISettings::values.screenshot_resolution_factor = - static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt()); - UISettings::values.select_user_on_boot = - qt_config->value("select_user_on_boot", false).toBool(); + static_cast<u16>(ReadSetting("screenshot_resolution_factor", 0).toUInt()); + UISettings::values.select_user_on_boot = ReadSetting("select_user_on_boot", false).toBool(); qt_config->beginGroup("UIGameList"); - UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); - UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool(); - UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt(); - UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt(); - UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt(); + UISettings::values.show_unknown = ReadSetting("show_unknown", true).toBool(); + UISettings::values.show_add_ons = ReadSetting("show_add_ons", true).toBool(); + UISettings::values.icon_size = ReadSetting("icon_size", 64).toUInt(); + UISettings::values.row_1_text_id = ReadSetting("row_1_text_id", 3).toUInt(); + UISettings::values.row_2_text_id = ReadSetting("row_2_text_id", 2).toUInt(); qt_config->endGroup(); qt_config->beginGroup("UILayout"); - UISettings::values.geometry = qt_config->value("geometry").toByteArray(); - UISettings::values.state = qt_config->value("state").toByteArray(); - UISettings::values.renderwindow_geometry = - qt_config->value("geometryRenderWindow").toByteArray(); - UISettings::values.gamelist_header_state = - qt_config->value("gameListHeaderState").toByteArray(); + UISettings::values.geometry = ReadSetting("geometry").toByteArray(); + UISettings::values.state = ReadSetting("state").toByteArray(); + UISettings::values.renderwindow_geometry = ReadSetting("geometryRenderWindow").toByteArray(); + UISettings::values.gamelist_header_state = ReadSetting("gameListHeaderState").toByteArray(); UISettings::values.microprofile_geometry = - qt_config->value("microProfileDialogGeometry").toByteArray(); + ReadSetting("microProfileDialogGeometry").toByteArray(); UISettings::values.microprofile_visible = - qt_config->value("microProfileDialogVisible", false).toBool(); + ReadSetting("microProfileDialogVisible", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("Paths"); - UISettings::values.roms_path = qt_config->value("romsPath").toString(); - UISettings::values.symbols_path = qt_config->value("symbolsPath").toString(); - UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString(); - UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool(); - UISettings::values.recent_files = qt_config->value("recentFiles").toStringList(); + UISettings::values.roms_path = ReadSetting("romsPath").toString(); + UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); + UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); + UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); + UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); qt_config->endGroup(); qt_config->beginGroup("Shortcuts"); - QStringList groups = qt_config->childGroups(); - for (auto group : groups) { + for (auto [name, group, shortcut] : default_hotkeys) { + auto [keyseq, context] = shortcut; qt_config->beginGroup(group); - - QStringList hotkeys = qt_config->childGroups(); - for (auto hotkey : hotkeys) { - qt_config->beginGroup(hotkey); - UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( - group + "/" + hotkey, - UISettings::ContextualShortcut(qt_config->value("KeySeq").toString(), - qt_config->value("Context").toInt()))); - qt_config->endGroup(); - } - + qt_config->beginGroup(name); + UISettings::values.shortcuts.push_back( + {name, + group, + {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); + qt_config->endGroup(); qt_config->endGroup(); } qt_config->endGroup(); - UISettings::values.single_window_mode = qt_config->value("singleWindowMode", true).toBool(); - UISettings::values.fullscreen = qt_config->value("fullscreen", false).toBool(); - UISettings::values.display_titlebar = qt_config->value("displayTitleBars", true).toBool(); - UISettings::values.show_filter_bar = qt_config->value("showFilterBar", true).toBool(); - UISettings::values.show_status_bar = qt_config->value("showStatusBar", true).toBool(); - UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool(); - UISettings::values.first_start = qt_config->value("firstStart", true).toBool(); - UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt(); - UISettings::values.show_console = qt_config->value("showConsole", false).toBool(); - UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt(); + UISettings::values.single_window_mode = ReadSetting("singleWindowMode", true).toBool(); + UISettings::values.fullscreen = ReadSetting("fullscreen", false).toBool(); + UISettings::values.display_titlebar = ReadSetting("displayTitleBars", true).toBool(); + UISettings::values.show_filter_bar = ReadSetting("showFilterBar", true).toBool(); + UISettings::values.show_status_bar = ReadSetting("showStatusBar", true).toBool(); + UISettings::values.confirm_before_closing = ReadSetting("confirmClose", true).toBool(); + UISettings::values.first_start = ReadSetting("firstStart", true).toBool(); + UISettings::values.callout_flags = ReadSetting("calloutFlags", 0).toUInt(); + UISettings::values.show_console = ReadSetting("showConsole", false).toBool(); + UISettings::values.profile_index = ReadSetting("profileIndex", 0).toUInt(); ApplyDefaultProfileIfInputInvalid(); @@ -549,62 +562,79 @@ void Config::SavePlayerValues() { for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { const auto& player = Settings::values.players[p]; - qt_config->setValue(QString("player_%1_connected").arg(p), player.connected); - qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type)); + WriteSetting(QString("player_%1_connected").arg(p), player.connected, false); + WriteSetting(QString("player_%1_type").arg(p), static_cast<u8>(player.type), + static_cast<u8>(Settings::ControllerType::DualJoycon)); - qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left); - qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right); - qt_config->setValue(QString("player_%1_button_color_left").arg(p), - player.button_color_left); - qt_config->setValue(QString("player_%1_button_color_right").arg(p), - player.button_color_right); + WriteSetting(QString("player_%1_body_color_left").arg(p), player.body_color_left, + Settings::JOYCON_BODY_NEON_BLUE); + WriteSetting(QString("player_%1_body_color_right").arg(p), player.body_color_right, + Settings::JOYCON_BODY_NEON_RED); + WriteSetting(QString("player_%1_button_color_left").arg(p), player.button_color_left, + Settings::JOYCON_BUTTONS_NEON_BLUE); + WriteSetting(QString("player_%1_button_color_right").arg(p), player.button_color_right, + Settings::JOYCON_BUTTONS_NEON_RED); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - qt_config->setValue(QString("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i])); + std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QString("player_%1_").arg(p) + + QString::fromStdString(Settings::NativeButton::mapping[i]), + QString::fromStdString(player.buttons[i]), + QString::fromStdString(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - qt_config->setValue(QString("player_%1_").arg(p) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i])); + std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_analogs[i][4], 0.5f); + WriteSetting(QString("player_%1_").arg(p) + + QString::fromStdString(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(player.analogs[i]), + QString::fromStdString(default_param)); } } } void Config::SaveDebugValues() { - qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled); + WriteSetting("debug_pad_enabled", Settings::values.debug_pad_enabled, false); for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - qt_config->setValue(QString("debug_pad_") + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_buttons[i])); + std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(QString("debug_pad_") + + QString::fromStdString(Settings::NativeButton::mapping[i]), + QString::fromStdString(Settings::values.debug_pad_buttons[i]), + QString::fromStdString(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - qt_config->setValue(QString("debug_pad_") + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_analogs[i])); + std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_analogs[i][4], 0.5f); + WriteSetting(QString("debug_pad_") + + QString::fromStdString(Settings::NativeAnalog::mapping[i]), + QString::fromStdString(Settings::values.debug_pad_analogs[i]), + QString::fromStdString(default_param)); } } void Config::SaveMouseValues() { - qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled); + WriteSetting("mouse_enabled", Settings::values.mouse_enabled, false); for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { - qt_config->setValue(QString("mouse_") + - QString::fromStdString(Settings::NativeMouseButton::mapping[i]), - QString::fromStdString(Settings::values.mouse_buttons[i])); + std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); + WriteSetting(QString("mouse_") + + QString::fromStdString(Settings::NativeMouseButton::mapping[i]), + QString::fromStdString(Settings::values.mouse_buttons[i]), + QString::fromStdString(default_param)); } } void Config::SaveTouchscreenValues() { - qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled); - qt_config->setValue("touchscreen_device", - QString::fromStdString(Settings::values.touchscreen.device)); - - qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger); - qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle); - qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x); - qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y); + WriteSetting("touchscreen_enabled", Settings::values.touchscreen.enabled, true); + WriteSetting("touchscreen_device", QString::fromStdString(Settings::values.touchscreen.device), + "engine:emu_window"); + + WriteSetting("touchscreen_finger", Settings::values.touchscreen.finger, 0); + WriteSetting("touchscreen_angle", Settings::values.touchscreen.rotation_angle, 0); + WriteSetting("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x, 15); + WriteSetting("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y, 15); } void Config::SaveValues() { @@ -615,88 +645,95 @@ void Config::SaveValues() { SaveMouseValues(); SaveTouchscreenValues(); - qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); - qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled); + WriteSetting("motion_device", QString::fromStdString(Settings::values.motion_device), + "engine:motion_emu,update_period:100,sensitivity:0.01"); + WriteSetting("keyboard_enabled", Settings::values.keyboard_enabled, false); qt_config->endGroup(); qt_config->beginGroup("Core"); - qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit); - qt_config->setValue("use_multi_core", Settings::values.use_multi_core); + WriteSetting("use_cpu_jit", Settings::values.use_cpu_jit, true); + WriteSetting("use_multi_core", Settings::values.use_multi_core, false); qt_config->endGroup(); qt_config->beginGroup("Renderer"); - qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); - qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); - qt_config->setValue("frame_limit", Settings::values.frame_limit); - qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation); + WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); + WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); + WriteSetting("frame_limit", Settings::values.frame_limit, 100); + WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); + WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); + WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, + false); // Cast to double because Qt's written float values are not human-readable - qt_config->setValue("bg_red", (double)Settings::values.bg_red); - qt_config->setValue("bg_green", (double)Settings::values.bg_green); - qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); + WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); + WriteSetting("bg_green", (double)Settings::values.bg_green, 0.0); + WriteSetting("bg_blue", (double)Settings::values.bg_blue, 0.0); qt_config->endGroup(); qt_config->beginGroup("Audio"); - qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); - qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); - qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); - qt_config->setValue("volume", Settings::values.volume); + WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto"); + WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true); + WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto"); + WriteSetting("volume", Settings::values.volume, 1.0f); qt_config->endGroup(); qt_config->beginGroup("Data Storage"); - qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); - qt_config->setValue("nand_directory", - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); - qt_config->setValue("sdmc_directory", - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); + WriteSetting("use_virtual_sd", Settings::values.use_virtual_sd, true); + WriteSetting("nand_directory", + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); + WriteSetting("sdmc_directory", + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); qt_config->endGroup(); qt_config->beginGroup("System"); - qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); - qt_config->setValue("enable_nfc", Settings::values.enable_nfc); - qt_config->setValue("current_user", Settings::values.current_user); - qt_config->setValue("language_index", Settings::values.language_index); + WriteSetting("use_docked_mode", Settings::values.use_docked_mode, false); + WriteSetting("current_user", Settings::values.current_user, 0); + WriteSetting("language_index", Settings::values.language_index, 1); - qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); - qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); + WriteSetting("rng_seed_enabled", Settings::values.rng_seed.has_value(), false); + WriteSetting("rng_seed", Settings::values.rng_seed.value_or(0), 0); - qt_config->setValue("custom_rtc_enabled", Settings::values.custom_rtc.has_value()); - qt_config->setValue("custom_rtc", - QVariant::fromValue<long long>( - Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count())); + WriteSetting("custom_rtc_enabled", Settings::values.custom_rtc.has_value(), false); + WriteSetting("custom_rtc", + QVariant::fromValue<long long>( + Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), + 0); qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); - qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); - qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys); + WriteSetting("log_filter", QString::fromStdString(Settings::values.log_filter), "*:Info"); + WriteSetting("use_dev_keys", Settings::values.use_dev_keys, false); qt_config->endGroup(); qt_config->beginGroup("Debugging"); - qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); - qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); - qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); - qt_config->setValue("dump_exefs", Settings::values.dump_exefs); - qt_config->setValue("dump_nso", Settings::values.dump_nso); + WriteSetting("use_gdbstub", Settings::values.use_gdbstub, false); + WriteSetting("gdbstub_port", Settings::values.gdbstub_port, 24689); + WriteSetting("program_args", QString::fromStdString(Settings::values.program_args), ""); + WriteSetting("dump_exefs", Settings::values.dump_exefs, false); + WriteSetting("dump_nso", Settings::values.dump_nso, false); qt_config->endGroup(); qt_config->beginGroup("WebService"); - qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); - qt_config->setValue("web_api_url", QString::fromStdString(Settings::values.web_api_url)); - qt_config->setValue("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); - qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); + WriteSetting("enable_telemetry", Settings::values.enable_telemetry, true); + WriteSetting("web_api_url", QString::fromStdString(Settings::values.web_api_url), + "https://api.yuzu-emu.org"); + WriteSetting("yuzu_username", QString::fromStdString(Settings::values.yuzu_username)); + WriteSetting("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); qt_config->endGroup(); qt_config->beginWriteArray("DisabledAddOns"); int i = 0; for (const auto& elem : Settings::values.disabled_addons) { qt_config->setArrayIndex(i); - qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); + WriteSetting("title_id", QVariant::fromValue<u64>(elem.first), 0); qt_config->beginWriteArray("disabled"); for (std::size_t j = 0; j < elem.second.size(); ++j) { qt_config->setArrayIndex(static_cast<int>(j)); - qt_config->setValue("d", QString::fromStdString(elem.second[j])); + WriteSetting("d", QString::fromStdString(elem.second[j]), ""); } qt_config->endArray(); ++i; @@ -704,60 +741,93 @@ void Config::SaveValues() { qt_config->endArray(); qt_config->beginGroup("UI"); - qt_config->setValue("theme", UISettings::values.theme); - qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); - qt_config->setValue("screenshot_resolution_factor", - UISettings::values.screenshot_resolution_factor); - qt_config->setValue("select_user_on_boot", UISettings::values.select_user_on_boot); + WriteSetting("theme", UISettings::values.theme, UISettings::themes[0].second); + WriteSetting("enable_discord_presence", UISettings::values.enable_discord_presence, true); + WriteSetting("screenshot_resolution_factor", UISettings::values.screenshot_resolution_factor, + 0); + WriteSetting("select_user_on_boot", UISettings::values.select_user_on_boot, false); qt_config->beginGroup("UIGameList"); - qt_config->setValue("show_unknown", UISettings::values.show_unknown); - qt_config->setValue("show_add_ons", UISettings::values.show_add_ons); - qt_config->setValue("icon_size", UISettings::values.icon_size); - qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id); - qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id); + WriteSetting("show_unknown", UISettings::values.show_unknown, true); + WriteSetting("show_add_ons", UISettings::values.show_add_ons, true); + WriteSetting("icon_size", UISettings::values.icon_size, 64); + WriteSetting("row_1_text_id", UISettings::values.row_1_text_id, 3); + WriteSetting("row_2_text_id", UISettings::values.row_2_text_id, 2); qt_config->endGroup(); qt_config->beginGroup("UILayout"); - qt_config->setValue("geometry", UISettings::values.geometry); - qt_config->setValue("state", UISettings::values.state); - qt_config->setValue("geometryRenderWindow", UISettings::values.renderwindow_geometry); - qt_config->setValue("gameListHeaderState", UISettings::values.gamelist_header_state); - qt_config->setValue("microProfileDialogGeometry", UISettings::values.microprofile_geometry); - qt_config->setValue("microProfileDialogVisible", UISettings::values.microprofile_visible); + WriteSetting("geometry", UISettings::values.geometry); + WriteSetting("state", UISettings::values.state); + WriteSetting("geometryRenderWindow", UISettings::values.renderwindow_geometry); + WriteSetting("gameListHeaderState", UISettings::values.gamelist_header_state); + WriteSetting("microProfileDialogGeometry", UISettings::values.microprofile_geometry); + WriteSetting("microProfileDialogVisible", UISettings::values.microprofile_visible, false); qt_config->endGroup(); qt_config->beginGroup("Paths"); - qt_config->setValue("romsPath", UISettings::values.roms_path); - qt_config->setValue("symbolsPath", UISettings::values.symbols_path); - qt_config->setValue("screenshotPath", UISettings::values.screenshot_path); - qt_config->setValue("gameListRootDir", UISettings::values.gamedir); - qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); - qt_config->setValue("recentFiles", UISettings::values.recent_files); + WriteSetting("romsPath", UISettings::values.roms_path); + WriteSetting("symbolsPath", UISettings::values.symbols_path); + WriteSetting("screenshotPath", UISettings::values.screenshot_path); + WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); + WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); + WriteSetting("recentFiles", UISettings::values.recent_files); qt_config->endGroup(); qt_config->beginGroup("Shortcuts"); - for (auto shortcut : UISettings::values.shortcuts) { - qt_config->setValue(shortcut.first + "/KeySeq", shortcut.second.first); - qt_config->setValue(shortcut.first + "/Context", shortcut.second.second); + // Lengths of UISettings::values.shortcuts & default_hotkeys are same. + // However, their ordering must also be the same. + for (std::size_t i = 0; i < default_hotkeys.size(); i++) { + auto [name, group, shortcut] = UISettings::values.shortcuts[i]; + qt_config->beginGroup(group); + qt_config->beginGroup(name); + WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); + WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); + qt_config->endGroup(); + qt_config->endGroup(); } qt_config->endGroup(); - qt_config->setValue("singleWindowMode", UISettings::values.single_window_mode); - qt_config->setValue("fullscreen", UISettings::values.fullscreen); - qt_config->setValue("displayTitleBars", UISettings::values.display_titlebar); - qt_config->setValue("showFilterBar", UISettings::values.show_filter_bar); - qt_config->setValue("showStatusBar", UISettings::values.show_status_bar); - qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing); - qt_config->setValue("firstStart", UISettings::values.first_start); - qt_config->setValue("calloutFlags", UISettings::values.callout_flags); - qt_config->setValue("showConsole", UISettings::values.show_console); - qt_config->setValue("profileIndex", UISettings::values.profile_index); + WriteSetting("singleWindowMode", UISettings::values.single_window_mode, true); + WriteSetting("fullscreen", UISettings::values.fullscreen, false); + WriteSetting("displayTitleBars", UISettings::values.display_titlebar, true); + WriteSetting("showFilterBar", UISettings::values.show_filter_bar, true); + WriteSetting("showStatusBar", UISettings::values.show_status_bar, true); + WriteSetting("confirmClose", UISettings::values.confirm_before_closing, true); + WriteSetting("firstStart", UISettings::values.first_start, true); + WriteSetting("calloutFlags", UISettings::values.callout_flags, 0); + WriteSetting("showConsole", UISettings::values.show_console, false); + WriteSetting("profileIndex", UISettings::values.profile_index, 0); qt_config->endGroup(); } +QVariant Config::ReadSetting(const QString& name) const { + return qt_config->value(name); +} + +QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { + QVariant result; + if (qt_config->value(name + "/default", false).toBool()) { + result = default_value; + } else { + result = qt_config->value(name, default_value); + } + return result; +} + +void Config::WriteSetting(const QString& name, const QVariant& value) { + qt_config->setValue(name, value); +} + +void Config::WriteSetting(const QString& name, const QVariant& value, + const QVariant& default_value) { + qt_config->setValue(name + "/default", value == default_value); + qt_config->setValue(name, value); +} + void Config::Reload() { ReadValues(); + // To apply default value changes + SaveValues(); Settings::Apply(); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index e73ad19bb..221d2364c 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -9,6 +9,7 @@ #include <string> #include <QVariant> #include "core/settings.h" +#include "yuzu/ui_settings.h" class QSettings; @@ -42,6 +43,13 @@ private: void SaveMouseValues(); void SaveTouchscreenValues(); + QVariant ReadSetting(const QString& name) const; + QVariant ReadSetting(const QString& name, const QVariant& default_value) const; + void WriteSetting(const QString& name, const QVariant& value); + void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); + + static const std::array<UISettings::Shortcut, 15> default_hotkeys; + std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; }; diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 3f03f0b77..267717bc9 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui @@ -7,9 +7,15 @@ <x>0</x> <y>0</y> <width>382</width> - <height>241</height> + <height>650</height> </rect> </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>650</height> + </size> + </property> <property name="windowTitle"> <string>yuzu Configuration</string> </property> @@ -62,6 +68,11 @@ <string>Input</string> </attribute> </widget> + <widget class="ConfigureHotkeys" name="hotkeysTab"> + <attribute name="title"> + <string>Hotkeys</string> + </attribute> + </widget> <widget class="ConfigureGraphics" name="graphicsTab"> <attribute name="title"> <string>Graphics</string> @@ -150,6 +161,12 @@ <header>configuration/configure_input_simple.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureHotkeys</class> + <extends>QWidget</extends> + <header>configuration/configure_hotkeys.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index aa7de7b54..550cf9dca 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -7,7 +7,6 @@ #include "common/file_util.h" #include "common/logging/backend.h" #include "common/logging/filter.h" -#include "common/logging/log.h" #include "core/core.h" #include "core/settings.h" #include "ui_configure_debug.h" diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index d802443d0..51bd1f121 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -8,20 +8,22 @@ #include "ui_configure.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" +#include "yuzu/configuration/configure_input_player.h" #include "yuzu/hotkeys.h" -ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) - : QDialog(parent), ui(new Ui::ConfigureDialog) { +ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) + : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) { ui->setupUi(this); - ui->generalTab->PopulateHotkeyList(registry); + ui->hotkeysTab->Populate(registry); this->setConfiguration(); this->PopulateSelectionList(); connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); - adjustSize(); - ui->selectorList->setCurrentRow(0); + + // Synchronise lists upon initialisation + ui->hotkeysTab->EmitHotkeysChanged(); } ConfigureDialog::~ConfigureDialog() = default; @@ -34,11 +36,13 @@ void ConfigureDialog::applyConfiguration() { ui->systemTab->applyConfiguration(); ui->profileManagerTab->applyConfiguration(); ui->inputTab->applyConfiguration(); + ui->hotkeysTab->applyConfiguration(registry); ui->graphicsTab->applyConfiguration(); ui->audioTab->applyConfiguration(); ui->debugTab->applyConfiguration(); ui->webTab->applyConfiguration(); Settings::Apply(); + Settings::LogSettings(); } void ConfigureDialog::PopulateSelectionList() { @@ -46,7 +50,7 @@ void ConfigureDialog::PopulateSelectionList() { {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, {tr("Graphics"), {tr("Graphics")}}, - {tr("Controls"), {tr("Input")}}}}; + {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}}; for (const auto& entry : items) { auto* const item = new QListWidgetItem(entry.first); @@ -65,6 +69,7 @@ void ConfigureDialog::UpdateVisibleTabs() { {tr("System"), ui->systemTab}, {tr("Profiles"), ui->profileManagerTab}, {tr("Input"), ui->inputTab}, + {tr("Hotkeys"), ui->hotkeysTab}, {tr("Graphics"), ui->graphicsTab}, {tr("Audio"), ui->audioTab}, {tr("Debug"), ui->debugTab}, diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 243d9fa09..2363ba584 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -17,7 +17,7 @@ class ConfigureDialog : public QDialog { Q_OBJECT public: - explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); + explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); ~ConfigureDialog() override; void applyConfiguration(); @@ -28,4 +28,5 @@ private: void PopulateSelectionList(); std::unique_ptr<Ui::ConfigureDialog> ui; + HotkeyRegistry& registry; }; diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 4116b6cd7..e48f4f5a3 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -28,25 +28,19 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::setConfiguration() { - ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); + ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); - ui->enable_nfc->setChecked(Settings::values.enable_nfc); -} - -void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { - ui->widget->Populate(registry); } void ConfigureGeneral::applyConfiguration() { - UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); + UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); - Settings::values.enable_nfc = ui->enable_nfc->isChecked(); } diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 59738af40..df41d995b 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -20,7 +20,6 @@ public: explicit ConfigureGeneral(QWidget* parent = nullptr); ~ConfigureGeneral() override; - void PopulateHotkeyList(const HotkeyRegistry& registry); void applyConfiguration(); private: diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index dff0ad5d0..1a5721fe7 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -71,26 +71,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="EmulationGroupBox"> - <property name="title"> - <string>Emulation</string> - </property> - <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> - <item> - <layout class="QVBoxLayout" name="EmulationVerticalLayout"> - <item> - <widget class="QCheckBox" name="enable_nfc"> - <property name="text"> - <string>Enable NFC</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <widget class="QGroupBox" name="theme_group_box"> <property name="title"> <string>Theme</string> @@ -118,22 +98,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="HotKeysGroupBox"> - <property name="title"> - <string>Hotkeys</string> - </property> - <layout class="QHBoxLayout" name="HotKeysHorizontalLayout"> - <item> - <layout class="QVBoxLayout" name="HotKeysVerticalLayout"> - <item> - <widget class="GHotkeysDialog" name="widget" native="true"/> - </item> - </layout> - </item> - </layout> - </widget> - </item> - <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -150,14 +114,6 @@ </item> </layout> </widget> - <customwidgets> - <customwidget> - <class>GHotkeysDialog</class> - <extends>QWidget</extends> - <header>hotkeys.h</header> - <container>1</container> - </customwidget> - </customwidgets> <resources/> <connections/> </ui> diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 8290b4384..dd1d67488 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -62,9 +62,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) const QColor new_bg_color = QColorDialog::getColor(bg_color); if (!new_bg_color.isValid()) return; - bg_color = new_bg_color; - ui->bg_button->setStyleSheet( - QString("QPushButton { background-color: %1 }").arg(bg_color.name())); + UpdateBackgroundColorButton(new_bg_color); }); } @@ -75,11 +73,12 @@ void ConfigureGraphics::setConfiguration() { static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); ui->frame_limit->setValue(Settings::values.frame_limit); + ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); - bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, - Settings::values.bg_blue); - ui->bg_button->setStyleSheet( - QString("QPushButton { background-color: %1 }").arg(bg_color.name())); + ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + 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)); } void ConfigureGraphics::applyConfiguration() { @@ -87,8 +86,21 @@ void ConfigureGraphics::applyConfiguration() { ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); Settings::values.frame_limit = ui->frame_limit->value(); + Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->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()); } + +void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { + bg_color = color; + + QPixmap pixmap(ui->bg_button->size()); + pixmap.fill(bg_color); + + const QIcon color_icon(pixmap); + ui->bg_button->setIcon(color_icon); +} diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index d6ffc6fde..f2799822d 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -23,6 +23,8 @@ public: private: void setConfiguration(); + void UpdateBackgroundColorButton(QColor color); + std::unique_ptr<Ui::ConfigureGraphics> ui; QColor bg_color; }; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index e278cdd05..c6767e0ca 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -50,6 +50,13 @@ </layout> </item> <item> + <widget class="QCheckBox" name="use_disk_shader_cache"> + <property name="text"> + <string>Use disk shader cache</string> + </property> + </widget> + </item> + <item> <widget class="QCheckBox" name="use_accurate_gpu_emulation"> <property name="text"> <string>Use accurate GPU emulation (slow)</string> @@ -57,6 +64,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> + <property name="text"> + <string>Use asynchronous GPU emulation</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label"> diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp new file mode 100644 index 000000000..bfb562535 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -0,0 +1,121 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QMessageBox> +#include <QStandardItemModel> +#include "core/settings.h" +#include "ui_configure_hotkeys.h" +#include "yuzu/configuration/configure_hotkeys.h" +#include "yuzu/hotkeys.h" +#include "yuzu/util/sequence_dialog/sequence_dialog.h" + +ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) + : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) { + ui->setupUi(this); + setFocusPolicy(Qt::ClickFocus); + + model = new QStandardItemModel(this); + model->setColumnCount(3); + model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")}); + + connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); + ui->hotkey_list->setModel(model); + + // TODO(Kloen): Make context configurable as well (hiding the column for now) + ui->hotkey_list->hideColumn(2); + + ui->hotkey_list->setColumnWidth(0, 200); + ui->hotkey_list->resizeColumnToContents(1); +} + +ConfigureHotkeys::~ConfigureHotkeys() = default; + +void ConfigureHotkeys::EmitHotkeysChanged() { + emit HotkeysChanged(GetUsedKeyList()); +} + +QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() const { + QList<QKeySequence> list; + for (int r = 0; r < model->rowCount(); r++) { + const QStandardItem* parent = model->item(r, 0); + for (int r2 = 0; r2 < parent->rowCount(); r2++) { + const QStandardItem* keyseq = parent->child(r2, 1); + list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText); + } + } + return list; +} + +void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { + for (const auto& group : registry.hotkey_groups) { + auto* parent_item = new QStandardItem(group.first); + parent_item->setEditable(false); + for (const auto& hotkey : group.second) { + auto* action = new QStandardItem(hotkey.first); + auto* keyseq = + new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); + action->setEditable(false); + keyseq->setEditable(false); + parent_item->appendRow({action, keyseq}); + } + model->appendRow(parent_item); + } + + ui->hotkey_list->expandAll(); +} + +void ConfigureHotkeys::Configure(QModelIndex index) { + if (index.parent() == QModelIndex()) + return; + + index = index.sibling(index.row(), 1); + auto* model = ui->hotkey_list->model(); + auto previous_key = model->data(index); + + auto* hotkey_dialog = new SequenceDialog; + int return_code = hotkey_dialog->exec(); + + auto key_sequence = hotkey_dialog->GetSequence(); + + if (return_code == QDialog::Rejected || key_sequence.isEmpty()) + return; + + if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { + QMessageBox::critical(this, tr("Error in inputted key"), + tr("You're using a key that's already bound.")); + } else { + model->setData(index, key_sequence.toString(QKeySequence::NativeText)); + EmitHotkeysChanged(); + } +} + +bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) { + return GetUsedKeyList().contains(key_sequence); +} + +void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { + for (int key_id = 0; key_id < model->rowCount(); key_id++) { + const QStandardItem* parent = model->item(key_id, 0); + for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { + const QStandardItem* action = parent->child(key_column_id, 0); + const QStandardItem* keyseq = parent->child(key_column_id, 1); + for (auto& [group, sub_actions] : registry.hotkey_groups) { + if (group != parent->text()) + continue; + for (auto& [action_name, hotkey] : sub_actions) { + if (action_name != action->text()) + continue; + hotkey.keyseq = QKeySequence(keyseq->text()); + } + } + } + } + + registry.SaveHotkeys(); + Settings::Apply(); +} + +void ConfigureHotkeys::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h new file mode 100644 index 000000000..cd203aad6 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.h @@ -0,0 +1,48 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <QWidget> +#include "core/settings.h" + +namespace Ui { +class ConfigureHotkeys; +} + +class HotkeyRegistry; +class QStandardItemModel; + +class ConfigureHotkeys : public QWidget { + Q_OBJECT + +public: + explicit ConfigureHotkeys(QWidget* parent = nullptr); + ~ConfigureHotkeys() override; + + void applyConfiguration(HotkeyRegistry& registry); + void retranslateUi(); + + void EmitHotkeysChanged(); + + /** + * Populates the hotkey list widget using data from the provided registry. + * Called everytime the Configure dialog is opened. + * @param registry The HotkeyRegistry whose data is used to populate the list. + */ + void Populate(const HotkeyRegistry& registry); + +signals: + void HotkeysChanged(QList<QKeySequence> new_key_list); + +private: + void Configure(QModelIndex index); + bool IsUsedKey(QKeySequence key_sequence); + QList<QKeySequence> GetUsedKeyList() const; + + std::unique_ptr<Ui::ConfigureHotkeys> ui; + + QStandardItemModel* model; +}; diff --git a/src/yuzu/configuration/configure_hotkeys.ui b/src/yuzu/configuration/configure_hotkeys.ui new file mode 100644 index 000000000..0d0b70f38 --- /dev/null +++ b/src/yuzu/configuration/configure_hotkeys.ui @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigureHotkeys</class> + <widget class="QWidget" name="ConfigureHotkeys"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>363</width> + <height>388</height> + </rect> + </property> + <property name="windowTitle"> + <string>Hotkey Settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Double-click on a binding to change it.</string> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="hotkey_list"> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui>
\ No newline at end of file diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index ba2b32c4f..c5a245ebe 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -7,6 +7,7 @@ #include <utility> #include <QColorDialog> #include <QGridLayout> +#include <QKeyEvent> #include <QMenu> #include <QMessageBox> #include <QTimer> diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 7a53f6715..ade8d4435 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -11,17 +11,21 @@ #include <string> #include <QDialog> -#include <QKeyEvent> #include "common/param_package.h" #include "core/settings.h" -#include "input_common/main.h" #include "ui_configure_input.h" +class QKeyEvent; class QPushButton; class QString; class QTimer; +namespace InputCommon::Polling { +class DevicePoller; +enum class DeviceType; +} // namespace InputCommon::Polling + namespace Ui { class ConfigureInputPlayer; } diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp index e13d2eac8..022b94609 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_general.cpp @@ -8,7 +8,6 @@ #include <QHeaderView> #include <QMenu> -#include <QMessageBox> #include <QStandardItemModel> #include <QString> #include <QTimer> diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h index a4494446c..f8a7d5326 100644 --- a/src/yuzu/configuration/configure_per_general.h +++ b/src/yuzu/configuration/configure_per_general.h @@ -7,16 +7,16 @@ #include <memory> #include <vector> -#include <QKeyEvent> +#include <QDialog> #include <QList> -#include <QWidget> #include "core/file_sys/vfs_types.h" -class QTreeView; class QGraphicsScene; class QStandardItem; class QStandardItemModel; +class QTreeView; +class QVBoxLayout; namespace Ui { class ConfigurePerGameGeneral; diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 94e27349d..10645a2b3 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -2,23 +2,19 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> +#include <array> +#include <chrono> +#include <optional> + #include <QFileDialog> #include <QGraphicsItem> -#include <QGraphicsScene> -#include <QHeaderView> #include <QMessageBox> -#include <QStandardItemModel> -#include <QTreeView> -#include <QVBoxLayout> #include "common/assert.h" #include "common/file_util.h" -#include "common/string_util.h" #include "core/core.h" #include "core/settings.h" #include "ui_configure_system.h" #include "yuzu/configuration/configure_system.h" -#include "yuzu/util/limitable_input_dialog.h" namespace { constexpr std::array<int, 12> days_in_month = {{ diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h index 41cd255fb..3d0772c87 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.h +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h @@ -6,8 +6,6 @@ #include <memory> #include <QDialog> -#include <QWidget> -#include "yuzu/configuration/config.h" namespace Ui { class ConfigureTouchscreenAdvanced; diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp deleted file mode 100644 index 209798521..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <QBoxLayout> -#include <QComboBox> -#include <QDebug> -#include <QFileDialog> -#include <QLabel> -#include <QMouseEvent> -#include <QPushButton> -#include <QScrollArea> -#include <QSpinBox> -#include "common/vector_math.h" -#include "core/core.h" -#include "core/memory.h" -#include "video_core/engines/maxwell_3d.h" -#include "video_core/gpu.h" -#include "video_core/textures/decoders.h" -#include "video_core/textures/texture.h" -#include "yuzu/debugger/graphics/graphics_surface.h" -#include "yuzu/util/spinbox.h" - -static Tegra::Texture::TextureFormat ConvertToTextureFormat( - Tegra::RenderTargetFormat render_target_format) { - switch (render_target_format) { - case Tegra::RenderTargetFormat::RGBA8_UNORM: - return Tegra::Texture::TextureFormat::A8R8G8B8; - case Tegra::RenderTargetFormat::RGB10_A2_UNORM: - return Tegra::Texture::TextureFormat::A2B10G10R10; - default: - UNIMPLEMENTED_MSG("Unimplemented RT format"); - return Tegra::Texture::TextureFormat::A8R8G8B8; - } -} - -SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) - : QLabel(parent), surface_widget(surface_widget_) {} - -SurfacePicture::~SurfacePicture() = default; - -void SurfacePicture::mousePressEvent(QMouseEvent* event) { - // Only do something while the left mouse button is held down - if (!(event->buttons() & Qt::LeftButton)) - return; - - if (pixmap() == nullptr) - return; - - if (surface_widget) - surface_widget->Pick(event->x() * pixmap()->width() / width(), - event->y() * pixmap()->height() / height()); -} - -void SurfacePicture::mouseMoveEvent(QMouseEvent* event) { - // We also want to handle the event if the user moves the mouse while holding down the LMB - mousePressEvent(event); -} - -GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, - QWidget* parent) - : BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent), - surface_source(Source::RenderTarget0) { - setObjectName("MaxwellSurface"); - - surface_source_list = new QComboBox; - surface_source_list->addItem(tr("Render Target 0")); - surface_source_list->addItem(tr("Render Target 1")); - surface_source_list->addItem(tr("Render Target 2")); - surface_source_list->addItem(tr("Render Target 3")); - surface_source_list->addItem(tr("Render Target 4")); - surface_source_list->addItem(tr("Render Target 5")); - surface_source_list->addItem(tr("Render Target 6")); - surface_source_list->addItem(tr("Render Target 7")); - surface_source_list->addItem(tr("Z Buffer")); - surface_source_list->addItem(tr("Custom")); - surface_source_list->setCurrentIndex(static_cast<int>(surface_source)); - - surface_address_control = new CSpinBox; - surface_address_control->SetBase(16); - surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); - surface_address_control->SetPrefix("0x"); - - unsigned max_dimension = 16384; // TODO: Find actual maximum - - surface_width_control = new QSpinBox; - surface_width_control->setRange(0, max_dimension); - - surface_height_control = new QSpinBox; - surface_height_control->setRange(0, max_dimension); - - surface_picker_x_control = new QSpinBox; - surface_picker_x_control->setRange(0, max_dimension - 1); - - surface_picker_y_control = new QSpinBox; - surface_picker_y_control->setRange(0, max_dimension - 1); - - surface_format_control = new QComboBox; - - // Color formats sorted by Maxwell texture format index - surface_format_control->addItem(tr("None")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("A8R8G8B8")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("Unknown")); - surface_format_control->addItem(tr("DXT1")); - surface_format_control->addItem(tr("DXT23")); - surface_format_control->addItem(tr("DXT45")); - surface_format_control->addItem(tr("DXN1")); - surface_format_control->addItem(tr("DXN2")); - - surface_info_label = new QLabel(); - surface_info_label->setWordWrap(true); - - surface_picture_label = new SurfacePicture(0, this); - surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop); - surface_picture_label->setScaledContents(false); - - auto scroll_area = new QScrollArea(); - scroll_area->setBackgroundRole(QPalette::Dark); - scroll_area->setWidgetResizable(false); - scroll_area->setWidget(surface_picture_label); - - save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); - - // Connections - connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate); - connect(surface_source_list, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &GraphicsSurfaceWidget::OnSurfaceSourceChanged); - connect(surface_address_control, &CSpinBox::ValueChanged, this, - &GraphicsSurfaceWidget::OnSurfaceAddressChanged); - connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged); - connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged); - connect(surface_format_control, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &GraphicsSurfaceWidget::OnSurfaceFormatChanged); - connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged); - connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), - this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged); - connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface); - - auto main_widget = new QWidget; - auto main_layout = new QVBoxLayout; - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("Source:"))); - sub_layout->addWidget(surface_source_list); - main_layout->addLayout(sub_layout); - } - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("GPU Address:"))); - sub_layout->addWidget(surface_address_control); - main_layout->addLayout(sub_layout); - } - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("Width:"))); - sub_layout->addWidget(surface_width_control); - main_layout->addLayout(sub_layout); - } - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("Height:"))); - sub_layout->addWidget(surface_height_control); - main_layout->addLayout(sub_layout); - } - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("Format:"))); - sub_layout->addWidget(surface_format_control); - main_layout->addLayout(sub_layout); - } - main_layout->addWidget(scroll_area); - - auto info_layout = new QHBoxLayout; - { - auto xy_layout = new QVBoxLayout; - { - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("X:"))); - sub_layout->addWidget(surface_picker_x_control); - xy_layout->addLayout(sub_layout); - } - { - auto sub_layout = new QHBoxLayout; - sub_layout->addWidget(new QLabel(tr("Y:"))); - sub_layout->addWidget(surface_picker_y_control); - xy_layout->addLayout(sub_layout); - } - } - info_layout->addLayout(xy_layout); - surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - info_layout->addWidget(surface_info_label); - } - main_layout->addLayout(info_layout); - - main_layout->addWidget(save_surface); - main_widget->setLayout(main_layout); - setWidget(main_widget); - - // Load current data - TODO: Make sure this works when emulation is not running - if (debug_context && debug_context->at_breakpoint) { - emit Update(); - widget()->setEnabled(debug_context->at_breakpoint); - } else { - widget()->setEnabled(false); - } -} - -void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) { - emit Update(); - widget()->setEnabled(true); -} - -void GraphicsSurfaceWidget::OnResumed() { - widget()->setEnabled(false); -} - -void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { - surface_source = static_cast<Source>(new_value); - emit Update(); -} - -void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { - if (surface_address != new_value) { - surface_address = static_cast<Tegra::GPUVAddr>(new_value); - - surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); - emit Update(); - } -} - -void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) { - if (surface_width != static_cast<unsigned>(new_value)) { - surface_width = static_cast<unsigned>(new_value); - - surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); - emit Update(); - } -} - -void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) { - if (surface_height != static_cast<unsigned>(new_value)) { - surface_height = static_cast<unsigned>(new_value); - - surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); - emit Update(); - } -} - -void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) { - if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) { - surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value); - - surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom)); - emit Update(); - } -} - -void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) { - if (surface_picker_x != new_value) { - surface_picker_x = new_value; - Pick(surface_picker_x, surface_picker_y); - } -} - -void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) { - if (surface_picker_y != new_value) { - surface_picker_y = new_value; - Pick(surface_picker_x, surface_picker_y); - } -} - -void GraphicsSurfaceWidget::Pick(int x, int y) { - surface_picker_x_control->setValue(x); - surface_picker_y_control->setValue(y); - - if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 || - y >= static_cast<int>(surface_height)) { - surface_info_label->setText(tr("Pixel out of bounds")); - surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - return; - } - - surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>")); - surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); -} - -void GraphicsSurfaceWidget::OnUpdate() { - auto& gpu = Core::System::GetInstance().GPU(); - - QPixmap pixmap; - - switch (surface_source) { - case Source::RenderTarget0: - case Source::RenderTarget1: - case Source::RenderTarget2: - case Source::RenderTarget3: - case Source::RenderTarget4: - case Source::RenderTarget5: - case Source::RenderTarget6: - case Source::RenderTarget7: { - // TODO: Store a reference to the registers in the debug context instead of accessing them - // directly... - - const auto& registers = gpu.Maxwell3D().regs; - const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - - static_cast<std::size_t>(Source::RenderTarget0)]; - - surface_address = rt.Address(); - surface_width = rt.width; - surface_height = rt.height; - if (rt.format != Tegra::RenderTargetFormat::NONE) { - surface_format = ConvertToTextureFormat(rt.format); - } - - break; - } - - case Source::Custom: { - // Keep user-specified values - break; - } - - default: - qDebug() << "Unknown surface source " << static_cast<int>(surface_source); - break; - } - - surface_address_control->SetValue(surface_address); - surface_width_control->setValue(surface_width); - surface_height_control->setValue(surface_height); - surface_format_control->setCurrentIndex(static_cast<int>(surface_format)); - - if (surface_address == 0) { - surface_picture_label->hide(); - surface_info_label->setText(tr("(invalid surface address)")); - surface_info_label->setAlignment(Qt::AlignCenter); - surface_picker_x_control->setEnabled(false); - surface_picker_y_control->setEnabled(false); - save_surface->setEnabled(false); - return; - } - - // TODO: Implement a good way to visualize alpha components! - - QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); - std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); - - // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. - // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. - auto unswizzled_data = Tegra::Texture::UnswizzleTexture( - *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, - surface_height, 1U); - - auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, - surface_width, surface_height); - - surface_picture_label->show(); - - for (unsigned int y = 0; y < surface_height; ++y) { - for (unsigned int x = 0; x < surface_width; ++x) { - Math::Vec4<u8> color; - color[0] = texture_data[x + y * surface_width + 0]; - color[1] = texture_data[x + y * surface_width + 1]; - color[2] = texture_data[x + y * surface_width + 2]; - color[3] = texture_data[x + y * surface_width + 3]; - decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a())); - } - } - - pixmap = QPixmap::fromImage(decoded_image); - surface_picture_label->setPixmap(pixmap); - surface_picture_label->resize(pixmap.size()); - - // Update the info with pixel data - surface_picker_x_control->setEnabled(true); - surface_picker_y_control->setEnabled(true); - Pick(surface_picker_x, surface_picker_y); - - // Enable saving the converted pixmap to file - save_surface->setEnabled(true); -} - -void GraphicsSurfaceWidget::SaveSurface() { - QString png_filter = tr("Portable Network Graphic (*.png)"); - QString bin_filter = tr("Binary data (*.bin)"); - - QString selectedFilter; - QString filename = QFileDialog::getSaveFileName( - this, tr("Save Surface"), - QString("texture-0x%1.png").arg(QString::number(surface_address, 16)), - QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter); - - if (filename.isEmpty()) { - // If the user canceled the dialog, don't save anything. - return; - } - - if (selectedFilter == png_filter) { - const QPixmap* pixmap = surface_picture_label->pixmap(); - ASSERT_MSG(pixmap != nullptr, "No pixmap set"); - - QFile file(filename); - file.open(QIODevice::WriteOnly); - if (pixmap) - pixmap->save(&file, "PNG"); - } else if (selectedFilter == bin_filter) { - auto& gpu = Core::System::GetInstance().GPU(); - std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); - - const u8* buffer = Memory::GetPointer(*address); - ASSERT_MSG(buffer != nullptr, "Memory not accessible"); - - QFile file(filename); - file.open(QIODevice::WriteOnly); - int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format); - QByteArray data(reinterpret_cast<const char*>(buffer), size); - file.write(data); - } else { - UNREACHABLE_MSG("Unhandled filter selected"); - } -} diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h deleted file mode 100644 index 323e39d94..000000000 --- a/src/yuzu/debugger/graphics/graphics_surface.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <QLabel> -#include <QPushButton> -#include "video_core/memory_manager.h" -#include "video_core/textures/texture.h" -#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h" - -class QComboBox; -class QSpinBox; -class CSpinBox; - -class GraphicsSurfaceWidget; - -class SurfacePicture : public QLabel { - Q_OBJECT - -public: - explicit SurfacePicture(QWidget* parent = nullptr, - GraphicsSurfaceWidget* surface_widget = nullptr); - ~SurfacePicture() override; - -protected slots: - void mouseMoveEvent(QMouseEvent* event) override; - void mousePressEvent(QMouseEvent* event) override; - -private: - GraphicsSurfaceWidget* surface_widget; -}; - -class GraphicsSurfaceWidget : public BreakPointObserverDock { - Q_OBJECT - - using Event = Tegra::DebugContext::Event; - - enum class Source { - RenderTarget0 = 0, - RenderTarget1 = 1, - RenderTarget2 = 2, - RenderTarget3 = 3, - RenderTarget4 = 4, - RenderTarget5 = 5, - RenderTarget6 = 6, - RenderTarget7 = 7, - ZBuffer = 8, - Custom = 9, - }; - -public: - explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context, - QWidget* parent = nullptr); - void Pick(int x, int y); - -public slots: - void OnSurfaceSourceChanged(int new_value); - void OnSurfaceAddressChanged(qint64 new_value); - void OnSurfaceWidthChanged(int new_value); - void OnSurfaceHeightChanged(int new_value); - void OnSurfaceFormatChanged(int new_value); - void OnSurfacePickerXChanged(int new_value); - void OnSurfacePickerYChanged(int new_value); - void OnUpdate(); - -signals: - void Update(); - -private: - void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; - void OnResumed() override; - - void SaveSurface(); - - QComboBox* surface_source_list; - CSpinBox* surface_address_control; - QSpinBox* surface_width_control; - QSpinBox* surface_height_control; - QComboBox* surface_format_control; - - SurfacePicture* surface_picture_label; - QSpinBox* surface_picker_x_control; - QSpinBox* surface_picker_y_control; - QLabel* surface_info_label; - QPushButton* save_surface; - - Source surface_source; - Tegra::GPUVAddr surface_address; - unsigned surface_width; - unsigned surface_height; - Tegra::Texture::TextureFormat surface_format; - int surface_picker_x = 0; - int surface_picker_y = 0; -}; diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index 8b30e0a85..86e03e46d 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp @@ -7,6 +7,7 @@ #include <QMouseEvent> #include <QPainter> #include <QString> +#include <QTimer> #include "common/common_types.h" #include "common/microprofile.h" #include "yuzu/debugger/profiler.h" diff --git a/src/yuzu/debugger/profiler.h b/src/yuzu/debugger/profiler.h index eae1e9e3c..8e69fdb06 100644 --- a/src/yuzu/debugger/profiler.h +++ b/src/yuzu/debugger/profiler.h @@ -4,10 +4,11 @@ #pragma once -#include <QAbstractItemModel> -#include <QDockWidget> -#include <QTimer> -#include "common/microprofile.h" +#include <QWidget> + +class QAction; +class QHideEvent; +class QShowEvent; class MicroProfileDialog : public QWidget { Q_OBJECT diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 0c0864742..593bb681f 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -13,7 +13,6 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/timer.h" #include "core/hle/kernel/wait_object.h" #include "core/memory.h" @@ -82,9 +81,8 @@ QString WaitTreeText::GetText() const { return text; } -WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - +WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) + : mutex_address(mutex_address) { mutex_value = Memory::Read32(mutex_address); owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); owner = handle_table.Get<Kernel::Thread>(owner_handle); @@ -155,8 +153,6 @@ std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitO switch (object.GetHandleType()) { case Kernel::HandleType::ReadableEvent: return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); - case Kernel::HandleType::Timer: - return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object)); case Kernel::HandleType::Thread: return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object)); default: @@ -238,6 +234,9 @@ QString WaitTreeThread::GetText() const { case Kernel::ThreadStatus::WaitMutex: status = tr("waiting for mutex"); break; + case Kernel::ThreadStatus::WaitCondVar: + status = tr("waiting for condition variable"); + break; case Kernel::ThreadStatus::WaitArb: status = tr("waiting for address arbiter"); break; @@ -273,6 +272,7 @@ QColor WaitTreeThread::GetColor() const { case Kernel::ThreadStatus::WaitSynchAll: case Kernel::ThreadStatus::WaitSynchAny: case Kernel::ThreadStatus::WaitMutex: + case Kernel::ThreadStatus::WaitCondVar: case Kernel::ThreadStatus::WaitArb: return QColor(Qt::GlobalColor::red); case Kernel::ThreadStatus::Dormant: @@ -319,7 +319,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); if (mutex_wait_address != 0) { - list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address)); + const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); + list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address, handle_table)); } else { list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); } @@ -348,23 +349,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const { return list; } -WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {} -WaitTreeTimer::~WaitTreeTimer() = default; - -std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const { - std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren()); - - const auto& timer = static_cast<const Kernel::Timer&>(object); - - list.push_back(std::make_unique<WaitTreeText>( - tr("reset type = %1").arg(GetResetTypeQString(timer.GetResetType())))); - list.push_back( - std::make_unique<WaitTreeText>(tr("initial delay = %1").arg(timer.GetInitialDelay()))); - list.push_back( - std::make_unique<WaitTreeText>(tr("interval delay = %1").arg(timer.GetIntervalDelay()))); - return list; -} - WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list) : thread_list(list) {} WaitTreeThreadList::~WaitTreeThreadList() = default; diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index e639ef412..62886609d 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -17,10 +17,10 @@ class EmuThread; namespace Kernel { +class HandleTable; class ReadableEvent; class WaitObject; class Thread; -class Timer; } // namespace Kernel class WaitTreeThread; @@ -73,7 +73,7 @@ public: class WaitTreeMutexInfo : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeMutexInfo(VAddr mutex_address); + explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table); ~WaitTreeMutexInfo() override; QString GetText() const override; @@ -150,15 +150,6 @@ public: std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; }; -class WaitTreeTimer : public WaitTreeWaitObject { - Q_OBJECT -public: - explicit WaitTreeTimer(const Kernel::Timer& object); - ~WaitTreeTimer() override; - - std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; -}; - class WaitTreeThreadList : public WaitTreeExpandableItem { Q_OBJECT public: diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index c0e3c5fa9..b0ca766ec 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -18,6 +18,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" #include "yuzu/compatibility_list.h" #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" @@ -193,8 +194,9 @@ void GameList::onFilterCloseClicked() { main_window->filterBarSetChecked(false); } -GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) - : QWidget{parent}, vfs(std::move(vfs)) { +GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider, + GMainWindow* parent) + : QWidget{parent}, vfs(std::move(vfs)), provider(provider) { watcher = new QFileSystemWatcher(this); connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); @@ -329,6 +331,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { QMenu context_menu; QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); + QAction* open_transferable_shader_cache = + context_menu.addAction(tr("Open Transferable Shader Cache")); context_menu.addSeparator(); QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); @@ -344,6 +348,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); connect(open_lfs_location, &QAction::triggered, [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); + connect(open_transferable_shader_cache, &QAction::triggered, + [&]() { emit OpenTransferableShaderCacheRequested(program_id); }); connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, @@ -428,7 +434,8 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { emit ShouldCancelWorker(); - GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list); + GameListWorker* worker = + new GameListWorker(vfs, provider, dir_path, deep_scan, compatibility_list); connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, @@ -460,9 +467,10 @@ void GameList::LoadInterfaceLayout() { const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; void GameList::RefreshGameDirectory() { - if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { + if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); search_field->clear(); - PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index b317eb2fc..56007eef8 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -26,8 +26,9 @@ class GameListSearchField; class GMainWindow; namespace FileSys { +class ManualContentProvider; class VfsFilesystem; -} +} // namespace FileSys enum class GameListOpenTarget { SaveData, @@ -47,7 +48,8 @@ public: COLUMN_COUNT, // Number of columns }; - explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); + explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, + FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr); ~GameList() override; void clearFilter(); @@ -66,6 +68,7 @@ signals: void GameChosen(QString game_path); void ShouldCancelWorker(); void OpenFolderRequested(u64 program_id, GameListOpenTarget target); + void OpenTransferableShaderCacheRequested(u64 program_id); void DumpRomFSRequested(u64 program_id, const std::string& game_path); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, @@ -85,6 +88,7 @@ private: void RefreshGameDirectory(); std::shared_ptr<FileSys::VfsFilesystem> vfs; + FileSys::ManualContentProvider* provider; GameListSearchField* search_field; GMainWindow* main_window = nullptr; QVBoxLayout* layout = nullptr; diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index b37710f59..8687e7c5a 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -12,12 +12,15 @@ #include "common/common_paths.h" #include "common/file_util.h" +#include "core/core.h" +#include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/mode.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/submission_package.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "yuzu/compatibility_list.h" @@ -119,20 +122,25 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri } } // Anonymous namespace -GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan, - const CompatibilityList& compatibility_list) - : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan), +GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, + FileSys::ManualContentProvider* provider, QString dir_path, + bool deep_scan, const CompatibilityList& compatibility_list) + : vfs(std::move(vfs)), provider(provider), dir_path(std::move(dir_path)), deep_scan(deep_scan), compatibility_list(compatibility_list) {} GameListWorker::~GameListWorker() = default; -void GameListWorker::AddInstalledTitlesToGameList() { - const auto cache = Service::FileSystem::GetUnionContents(); - const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Program); +void GameListWorker::AddTitlesToGameList() { + const auto& cache = dynamic_cast<FileSys::ContentProviderUnion&>( + Core::System::GetInstance().GetContentProvider()); + const auto installed_games = cache.ListEntriesFilterOrigin( + std::nullopt, FileSys::TitleType::Application, FileSys::ContentRecordType::Program); - for (const auto& game : installed_games) { - const auto file = cache.GetEntryUnparsed(game); + for (const auto& [slot, game] : installed_games) { + if (slot == FileSys::ContentProviderUnionSlot::FrontendManual) + continue; + + const auto file = cache.GetEntryUnparsed(game.title_id, game.type); std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); if (!loader) continue; @@ -150,45 +158,13 @@ void GameListWorker::AddInstalledTitlesToGameList() { emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id, compatibility_list, patch)); } - - const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application, - FileSys::ContentRecordType::Control); - - for (const auto& entry : control_data) { - auto nca = cache.GetEntry(entry); - if (nca != nullptr) { - nca_control_map.insert_or_assign(entry.title_id, std::move(nca)); - } - } } -void GameListWorker::FillControlMap(const std::string& dir_path) { - const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { - if (stop_processing) { - // Breaks the callback loop - return false; - } - - const std::string physical_name = directory + DIR_SEP + virtual_name; - const QFileInfo file_info(QString::fromStdString(physical_name)); - if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) { - auto nca = - std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); - if (nca->GetType() == FileSys::NCAContentType::Control) { - const u64 title_id = nca->GetTitleId(); - nca_control_map.insert_or_assign(title_id, std::move(nca)); - } - } - return true; - }; - - FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback); -} - -void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { - const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { +void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, + unsigned int recursion) { + const auto callback = [this, target, recursion](u64* num_entries_out, + const std::string& directory, + const std::string& virtual_name) -> bool { if (stop_processing) { // Breaks the callback loop. return false; @@ -198,7 +174,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign const bool is_dir = FileUtil::IsDirectory(physical_name); if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { - auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); + const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); + auto loader = Loader::GetLoader(file); if (!loader) { return true; } @@ -209,31 +186,42 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign return true; } - std::vector<u8> icon; - const auto res1 = loader->ReadIcon(icon); - u64 program_id = 0; const auto res2 = loader->ReadProgramId(program_id); - std::string name = " "; - const auto res3 = loader->ReadTitle(name); + if (target == ScanTarget::FillManualContentProvider) { + if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { + provider->AddEntry(FileSys::TitleType::Application, + FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), + program_id, file); + } else if (res2 == Loader::ResultStatus::Success && + (file_type == Loader::FileType::XCI || + file_type == Loader::FileType::NSP)) { + const auto nsp = file_type == Loader::FileType::NSP + ? std::make_shared<FileSys::NSP>(file) + : FileSys::XCI{file}.GetSecurePartitionNSP(); + for (const auto& title : nsp->GetNCAs()) { + for (const auto& entry : title.second) { + provider->AddEntry(entry.first.first, entry.first.second, title.first, + entry.second->GetBaseFile()); + } + } + } + } else { + std::vector<u8> icon; + const auto res1 = loader->ReadIcon(icon); - const FileSys::PatchManager patch{program_id}; + std::string name = " "; + const auto res3 = loader->ReadTitle(name); - if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success && - res2 == Loader::ResultStatus::Success) { - // Use from metadata pool. - if (nca_control_map.find(program_id) != nca_control_map.end()) { - const auto& nca = nca_control_map[program_id]; - GetMetadataFromControlNCA(patch, *nca, icon, name); - } - } + const FileSys::PatchManager patch{program_id}; - emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, - compatibility_list, patch)); + emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, + compatibility_list, patch)); + } } else if (is_dir && recursion > 0) { watch_list.append(QString::fromStdString(physical_name)); - AddFstEntriesToGameList(physical_name, recursion - 1); + ScanFileSystem(target, physical_name, recursion - 1); } return true; @@ -245,10 +233,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign void GameListWorker::run() { stop_processing = false; watch_list.append(dir_path); - FillControlMap(dir_path.toStdString()); - AddInstalledTitlesToGameList(); - AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); - nca_control_map.clear(); + provider->ClearAllEntries(); + ScanFileSystem(ScanTarget::FillManualContentProvider, dir_path.toStdString(), + deep_scan ? 256 : 0); + AddTitlesToGameList(); + ScanFileSystem(ScanTarget::PopulateGameList, dir_path.toStdString(), deep_scan ? 256 : 0); emit Finished(watch_list); } diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 0e42d0bde..7c3074af9 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -33,7 +33,8 @@ class GameListWorker : public QObject, public QRunnable { Q_OBJECT public: - GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, QString dir_path, bool deep_scan, + GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, + FileSys::ManualContentProvider* provider, QString dir_path, bool deep_scan, const CompatibilityList& compatibility_list); ~GameListWorker() override; @@ -58,12 +59,17 @@ signals: void Finished(QStringList watch_list); private: - void AddInstalledTitlesToGameList(); - void FillControlMap(const std::string& dir_path); - void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); + void AddTitlesToGameList(); + + enum class ScanTarget { + FillManualContentProvider, + PopulateGameList, + }; + + void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion = 0); std::shared_ptr<FileSys::VfsFilesystem> vfs; - std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map; + FileSys::ManualContentProvider* provider; QStringList watch_list; QString dir_path; bool deep_scan; diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index dce399774..4582e7f21 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <map> #include <QKeySequence> #include <QShortcut> #include <QTreeWidgetItem> @@ -13,47 +12,32 @@ HotkeyRegistry::HotkeyRegistry() = default; HotkeyRegistry::~HotkeyRegistry() = default; -void HotkeyRegistry::LoadHotkeys() { - // Make sure NOT to use a reference here because it would become invalid once we call - // beginGroup() - for (auto shortcut : UISettings::values.shortcuts) { - const QStringList cat = shortcut.first.split('/'); - Q_ASSERT(cat.size() >= 2); - - // RegisterHotkey assigns default keybindings, so use old values as default parameters - Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; - if (!shortcut.second.first.isEmpty()) { - hk.keyseq = QKeySequence::fromString(shortcut.second.first); - hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second); - } - if (hk.shortcut) - hk.shortcut->setKey(hk.keyseq); - } -} - void HotkeyRegistry::SaveHotkeys() { UISettings::values.shortcuts.clear(); for (const auto& group : hotkey_groups) { for (const auto& hotkey : group.second) { - UISettings::values.shortcuts.emplace_back( - UISettings::Shortcut(group.first + '/' + hotkey.first, - UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), - hotkey.second.context))); + UISettings::values.shortcuts.push_back( + {hotkey.first, group.first, + UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), + hotkey.second.context)}); } } } -void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, - const QKeySequence& default_keyseq, - Qt::ShortcutContext default_context) { - auto& hotkey_group = hotkey_groups[group]; - if (hotkey_group.find(action) != hotkey_group.end()) { - return; +void HotkeyRegistry::LoadHotkeys() { + // Make sure NOT to use a reference here because it would become invalid once we call + // beginGroup() + for (auto shortcut : UISettings::values.shortcuts) { + Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; + if (!shortcut.shortcut.first.isEmpty()) { + hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText); + hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second); + } + if (hk.shortcut) { + hk.shortcut->disconnect(); + hk.shortcut->setKey(hk.keyseq); + } } - - auto& hotkey_action = hotkey_groups[group][action]; - hotkey_action.keyseq = default_keyseq; - hotkey_action.context = default_context; } QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { @@ -65,24 +49,11 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action return hk.shortcut; } -GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { - ui.setupUi(this); +QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { + return hotkey_groups[group][action].keyseq; } -void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { - for (const auto& group : registry.hotkey_groups) { - QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); - for (const auto& hotkey : group.second) { - QStringList columns; - columns << hotkey.first << hotkey.second.keyseq.toString(); - QTreeWidgetItem* item = new QTreeWidgetItem(columns); - toplevel_item->addChild(item); - } - ui.treeWidget->addTopLevelItem(toplevel_item); - } - // TODO: Make context configurable as well (hiding the column for now) - ui.treeWidget->setColumnCount(2); - - ui.treeWidget->resizeColumnToContents(0); - ui.treeWidget->resizeColumnToContents(1); +Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, + const QString& action) { + return hotkey_groups[group][action].context; } diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index f38e6c002..4f526dc7e 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h @@ -5,7 +5,6 @@ #pragma once #include <map> -#include "ui_hotkeys.h" class QDialog; class QKeySequence; @@ -14,7 +13,7 @@ class QShortcut; class HotkeyRegistry final { public: - friend class GHotkeysDialog; + friend class ConfigureHotkeys; explicit HotkeyRegistry(); ~HotkeyRegistry(); @@ -49,22 +48,27 @@ public: QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); /** - * Register a hotkey. + * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. * - * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") - * @param action Name of the action (e.g. "Start Emulation", "Load Image") - * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the - * settings file before - * @param default_context Default context to assign if the hotkey wasn't present in the settings - * file before - * @warning Both the group and action strings will be displayed in the hotkey settings dialog + * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). + * @param action Name of the action (e.g. "Start Emulation", "Load Image"). + */ + QKeySequence GetKeySequence(const QString& group, const QString& action); + + /** + * Returns a Qt::ShortcutContext object who can be connected to other + * QAction::setShortcutContext. + * + * @param group General group this shortcut context belongs to (e.g. "Main Window", + * "Debugger"). + * @param action Name of the action (e.g. "Start Emulation", "Load Image"). */ - void RegisterHotkey(const QString& group, const QString& action, - const QKeySequence& default_keyseq = {}, - Qt::ShortcutContext default_context = Qt::WindowShortcut); + Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); private: struct Hotkey { + Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} + QKeySequence keyseq; QShortcut* shortcut = nullptr; Qt::ShortcutContext context = Qt::WindowShortcut; @@ -75,15 +79,3 @@ private: HotkeyGroupMap hotkey_groups; }; - -class GHotkeysDialog : public QWidget { - Q_OBJECT - -public: - explicit GHotkeysDialog(QWidget* parent = nullptr); - - void Populate(const HotkeyRegistry& registry); - -private: - Ui::hotkeys ui; -}; diff --git a/src/yuzu/hotkeys.ui b/src/yuzu/hotkeys.ui deleted file mode 100644 index 050fe064e..000000000 --- a/src/yuzu/hotkeys.ui +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>hotkeys</class> - <widget class="QWidget" name="hotkeys"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>363</width> - <height>388</height> - </rect> - </property> - <property name="windowTitle"> - <string>Hotkey Settings</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTreeWidget" name="treeWidget"> - <property name="selectionBehavior"> - <enum>QAbstractItemView::SelectItems</enum> - </property> - <property name="headerHidden"> - <bool>false</bool> - </property> - <column> - <property name="text"> - <string>Action</string> - </property> - </column> - <column> - <property name="text"> - <string>Hotkey</string> - </property> - </column> - <column> - <property name="text"> - <string>Context</string> - </property> - </column> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index 907aac4f1..4e2d988cd 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -43,6 +43,7 @@ QProgressBar { } QProgressBar::chunk { background-color: #0ab9e6; + width: 1px; })"; constexpr const char PROGRESSBAR_STYLE_BUILD[] = R"( @@ -53,7 +54,8 @@ QProgressBar { padding: 2px; } QProgressBar::chunk { - background-color: #ff3c28; + background-color: #ff3c28; + width: 1px; })"; constexpr const char PROGRESSBAR_STYLE_COMPLETE[] = R"( @@ -190,7 +192,12 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size } // update labels and progress bar - ui->stage->setText(stage_translations[stage].arg(value).arg(total)); + if (stage == VideoCore::LoadCallbackStage::Decompile || + stage == VideoCore::LoadCallbackStage::Build) { + ui->stage->setText(stage_translations[stage].arg(value).arg(total)); + } else { + ui->stage->setText(stage_translations[stage]); + } ui->value->setText(estimate); ui->progress_bar->setValue(static_cast<int>(value)); previous_time = now; diff --git a/src/yuzu/loading_screen.ui b/src/yuzu/loading_screen.ui index a67d273fd..820b47536 100644 --- a/src/yuzu/loading_screen.ui +++ b/src/yuzu/loading_screen.ui @@ -132,7 +132,7 @@ border-radius: 15px; font: 75 15pt "Arial";</string> </property> <property name="text"> - <string>Stage 1 of 2. Estimate Time 5m 4s</string> + <string>Estimated Time 5m 4s</string> </property> </widget> </item> @@ -146,6 +146,9 @@ font: 75 15pt "Arial";</string> <property name="text"> <string/> </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> <property name="margin"> <number>30</number> </property> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ae3b49709..bdee44b04 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -11,9 +11,11 @@ #include "applets/profile_select.h" #include "applets/software_keyboard.h" #include "applets/web_browser.h" +#include "configuration/configure_input.h" #include "configuration/configure_per_general.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" +#include "core/frontend/scope_acquire_window_context.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/hid/controllers/npad.h" @@ -35,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include <glad/glad.h> #define QT_NO_OPENGL +#include <QClipboard> +#include <QDesktopServices> #include <QDesktopWidget> #include <QDialogButtonBox> #include <QFile> #include <QFileDialog> +#include <QInputDialog> #include <QMessageBox> +#include <QProgressBar> +#include <QProgressDialog> +#include <QShortcut> +#include <QStatusBar> #include <QtConcurrent/QtConcurrent> -#include <QtGui> -#include <QtWidgets> + #include <fmt/format.h> #include "common/common_paths.h" #include "common/detached_tasks.h" @@ -53,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" -#include "common/string_util.h" #include "common/telemetry.h" #include "core/core.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" @@ -69,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/frontend/applets/software_keyboard.h" #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_ldr.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" @@ -85,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/configuration/configure_dialog.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/graphics/graphics_breakpoints.h" -#include "yuzu/debugger/graphics/graphics_surface.h" #include "yuzu/debugger/profiler.h" #include "yuzu/debugger/wait_tree.h" #include "yuzu/discord.h" @@ -166,7 +170,8 @@ static void InitializeLogging() { GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr), - vfs(std::make_shared<FileSys::RealVfsFilesystem>()) { + vfs(std::make_shared<FileSys::RealVfsFilesystem>()), + provider(std::make_unique<FileSys::ManualContentProvider>()) { InitializeLogging(); debug_context = Tegra::DebugContext::Construct(); @@ -198,13 +203,18 @@ GMainWindow::GMainWindow() .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); show(); + Core::System::GetInstance().SetContentProvider( + std::make_unique<FileSys::ContentProviderUnion>()); + Core::System::GetInstance().RegisterContentProvider( + FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); + Service::FileSystem::CreateFactories(*vfs); + // Gen keys if necessary OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); - // Necessary to load titles from nand in gamelist. - Service::FileSystem::CreateFactories(*vfs); game_list->LoadCompatibilityList(); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); // Show one-time "callout" messages to the user ShowTelemetryCallout(); @@ -338,6 +348,11 @@ void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view .arg(QString::fromStdString(std::to_string(key_code)))); }; + QMessageBox::information( + this, tr("Exit"), + tr("To exit the web application, use the game provided controls to select exit, select the " + "'Exit Web Applet' option in the menu bar, or press the 'Enter' key.")); + bool running_exit_check = false; while (!finished) { QApplication::processEvents(); @@ -409,7 +424,7 @@ void GMainWindow::InitializeWidgets() { render_window = new GRenderWindow(this, emu_thread.get()); render_window->hide(); - game_list = new GameList(vfs, this); + game_list = new GameList(vfs, provider.get(), this); ui.horizontalLayout->addWidget(game_list); loading_screen = new LoadingScreen(this); @@ -468,11 +483,6 @@ void GMainWindow::InitializeDebugWidgets() { graphicsBreakpointsWidget->hide(); debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction()); - graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this); - addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget); - graphicsSurfaceWidget->hide(); - debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction()); - waitTreeWidget = new WaitTreeWidget(this); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); @@ -504,32 +514,34 @@ void GMainWindow::InitializeRecentFileMenuActions() { } void GMainWindow::InitializeHotkeys() { - hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open); - hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); - hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); - hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5)); - hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); - hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), - Qt::ApplicationShortcut); - hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), - Qt::ApplicationShortcut); - hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"), - Qt::ApplicationShortcut); - hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), - Qt::ApplicationShortcut); - hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), - Qt::ApplicationShortcut); - hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", - QKeySequence(QKeySequence::Print)); - hotkey_registry.LoadHotkeys(); + ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File")); + ui.action_Load_File->setShortcutContext( + hotkey_registry.GetShortcutContext("Main Window", "Load File")); + + ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit yuzu")); + ui.action_Exit->setShortcutContext( + hotkey_registry.GetShortcutContext("Main Window", "Exit yuzu")); + + ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation")); + ui.action_Stop->setShortcutContext( + hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation")); + + ui.action_Show_Filter_Bar->setShortcut( + hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar")); + ui.action_Show_Filter_Bar->setShortcutContext( + hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar")); + + ui.action_Show_Status_Bar->setShortcut( + hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar")); + ui.action_Show_Status_Bar->setShortcutContext( + hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar")); + connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, &GMainWindow::OnMenuLoadFile); - connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), - &QShortcut::activated, this, &GMainWindow::OnStartGame); - connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, - this, [&] { + connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this), + &QShortcut::activated, this, [&] { if (emulation_running) { if (emu_thread->IsRunning()) { OnPauseGame(); @@ -538,8 +550,8 @@ void GMainWindow::InitializeHotkeys() { } } }); - connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this, - [this] { + connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this), + &QShortcut::activated, this, [this] { if (!Core::System::GetInstance().IsPoweredOn()) return; BootGame(QString(game_path)); @@ -560,7 +572,10 @@ void GMainWindow::InitializeHotkeys() { Settings::values.use_frame_limit = !Settings::values.use_frame_limit; UpdateStatusBar(); }); - constexpr u16 SPEED_LIMIT_STEP = 5; + // 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; connect(hotkey_registry.GetHotkey("Main Window", "Increase Speed Limit", this), &QShortcut::activated, this, [&] { if (Settings::values.frame_limit < 9999 - SPEED_LIMIT_STEP) { @@ -587,6 +602,12 @@ void GMainWindow::InitializeHotkeys() { OnCaptureScreenshot(); } }); + connect(hotkey_registry.GetHotkey("Main Window", "Change Docked Mode", this), + &QShortcut::activated, this, [&] { + Settings::values.use_docked_mode = !Settings::values.use_docked_mode; + OnDockedModeChanged(!Settings::values.use_docked_mode, + Settings::values.use_docked_mode); + }); } void GMainWindow::SetDefaultUIGeometry() { @@ -631,6 +652,8 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); + connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, + &GMainWindow::OnTransferableShaderCacheOpenFile); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, @@ -675,7 +698,6 @@ void GMainWindow::ConnectMenuEvents() { &GMainWindow::ToggleWindowMode); connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, &GMainWindow::OnDisplayTitleBars); - ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F")); connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); @@ -747,13 +769,15 @@ bool GMainWindow::LoadROM(const QString& filename) { ShutdownGame(); render_window->InitRenderTarget(); - render_window->MakeCurrent(); - if (!gladLoadGL()) { - QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), - tr("Your GPU may not support OpenGL 4.3, or you do not " - "have the latest graphics driver.")); - return false; + { + Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window}; + if (!gladLoadGL()) { + QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), + tr("Your GPU may not support OpenGL 4.3, or you do not " + "have the latest graphics driver.")); + return false; + } } QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); @@ -794,8 +818,6 @@ bool GMainWindow::LoadROM(const QString& filename) { "wiki</a>. This message will not be shown again.")); } - render_window->DoneCurrent(); - if (result != Core::System::ResultStatus::Success) { switch (result) { case Core::System::ResultStatus::ErrorGetLoader: @@ -845,7 +867,7 @@ bool GMainWindow::LoadROM(const QString& filename) { } game_path = filename; - Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); + system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "Qt"); return true; } @@ -886,6 +908,9 @@ void GMainWindow::BootGame(const QString& filename) { connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget, &WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection); + connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen, + &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); + // Update the GUI if (ui.action_Single_Window_Mode->isChecked()) { game_list->hide(); @@ -1062,6 +1087,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } +void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { + ASSERT(program_id != 0); + + const QString tranferable_shader_cache_folder_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" + + DIR_SEP + "transferable"; + + const QString transferable_shader_cache_file_path = + tranferable_shader_cache_folder_path + DIR_SEP + + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); + + if (!QFile::exists(transferable_shader_cache_file_path)) { + QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), + tr("A shader cache for this title does not exist.")); + return; + } + + // Windows supports opening a folder with selecting a specified file in explorer. On every other + // OS we just open the transferable shader cache folder without preselecting the transferable + // shader cache file for the selected game. +#if defined(Q_OS_WIN) + const QString explorer = QStringLiteral("explorer"); + QStringList param; + if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { + param << QStringLiteral("/select,"); + } + param << QDir::toNativeSeparators(transferable_shader_cache_file_path); + QProcess::startDetached(explorer, param); +#else + QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); +#endif +} + static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { std::size_t out = 0; @@ -1121,7 +1179,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto installed = Service::FileSystem::GetUnionContents(); + const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); if (!romfs_title_id) { @@ -1221,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } config->Save(); @@ -1310,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() { const auto success = [this]() { QMessageBox::information(this, tr("Successfully Installed"), tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); }; const auto failed = [this]() { @@ -1437,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() { void GMainWindow::OnMenuSelectGameListRoot() { QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (!dir_path.isEmpty()) { - UISettings::values.gamedir = dir_path; - game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); + UISettings::values.game_directory_path = dir_path; + game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); } } @@ -1460,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) : FileUtil::UserPath::NANDDir, dir_path.toStdString()); Service::FileSystem::CreateFactories(*vfs); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } @@ -1604,6 +1664,7 @@ void GMainWindow::OnConfigure() { auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.applyConfiguration(); + InitializeHotkeys(); if (UISettings::values.theme != old_theme) UpdateUITheme(); if (UISettings::values.enable_discord_presence != old_discord_presence) @@ -1611,8 +1672,8 @@ void GMainWindow::OnConfigure() { const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } config->Save(); @@ -1681,12 +1742,16 @@ void GMainWindow::OnToggleFilterBar() { void GMainWindow::OnCaptureScreenshot() { OnPauseGame(); - const QString path = - QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), - UISettings::values.screenshot_path, tr("PNG Image (*.png)")); - if (!path.isEmpty()) { - UISettings::values.screenshot_path = QFileInfo(path).path(); - render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, + tr("PNG Image (*.png)")); + png_dialog.setAcceptMode(QFileDialog::AcceptSave); + png_dialog.setDefaultSuffix("png"); + if (png_dialog.exec()) { + const QString path = png_dialog.selectedFiles().first(); + if (!path.isEmpty()) { + UISettings::values.screenshot_path = QFileInfo(path).path(); + render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + } } OnStartGame(); } @@ -1858,18 +1923,19 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { Service::FileSystem::CreateFactories(*vfs); if (behavior == ReinitializeKeyBehavior::Warning) { - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } -std::optional<u64> GMainWindow::SelectRomFSDumpTarget( - const FileSys::RegisteredCacheUnion& installed, u64 program_id) { +std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, + u64 program_id) { const auto dlc_entries = installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); - std::vector<FileSys::RegisteredCacheEntry> dlc_match; + std::vector<FileSys::ContentProviderEntry> dlc_match; dlc_match.reserve(dlc_entries.size()); std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), - [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) { + [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id && installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; }); @@ -2055,6 +2121,9 @@ int main(int argc, char* argv[]) { GMainWindow main_window; // After settings have been loaded by GMainWindow, apply the filter main_window.show(); + + Settings::LogSettings(); + int result = app.exec(); detached_tasks.WaitForAllTasks(); return result; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 080484995..ce5045819 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -23,7 +23,6 @@ class EmuThread; class GameList; class GImageInfo; class GraphicsBreakPointsWidget; -class GraphicsSurfaceWidget; class GRenderWindow; class LoadingScreen; class MicroProfileDialog; @@ -37,7 +36,8 @@ struct SoftwareKeyboardParameters; } // namespace Core::Frontend namespace FileSys { -class RegisteredCacheUnion; +class ContentProvider; +class ManualContentProvider; class VfsFilesystem; } // namespace FileSys @@ -120,7 +120,6 @@ private: void InitializeWidgets(); void InitializeDebugWidgets(); void InitializeRecentFileMenuActions(); - void InitializeHotkeys(); void SetDefaultUIGeometry(); void RestoreUIState(); @@ -176,6 +175,7 @@ private slots: /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); + void OnTransferableShaderCacheOpenFile(u64 program_id); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, @@ -195,6 +195,7 @@ private slots: void OnAbout(); void OnToggleFilterBar(); void OnDisplayTitleBars(bool); + void InitializeHotkeys(); void ToggleFullscreen(); void ShowFullscreen(); void HideFullscreen(); @@ -204,7 +205,7 @@ private slots: void OnReinitializeKeys(ReinitializeKeyBehavior behavior); private: - std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); + std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); void UpdateStatusBar(); Ui::MainWindow ui; @@ -232,12 +233,12 @@ private: // FS std::shared_ptr<FileSys::VfsFilesystem> vfs; + std::unique_ptr<FileSys::ManualContentProvider> provider; // Debugger panes ProfilerWidget* profilerWidget; MicroProfileDialog* microProfileDialog; GraphicsBreakPointsWidget* graphicsBreakpointsWidget; - GraphicsSurfaceWidget* graphicsSurfaceWidget; WaitTreeWidget* waitTreeWidget; QAction* actions_recent_files[max_recent_files_item]; diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp index a314493fc..4bdc302e0 100644 --- a/src/yuzu/ui_settings.cpp +++ b/src/yuzu/ui_settings.cpp @@ -12,5 +12,4 @@ const Themes themes{{ }}; Values values = {}; - } // namespace UISettings diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 82aaeedb0..dbd318e20 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h @@ -15,7 +15,12 @@ namespace UISettings { using ContextualShortcut = std::pair<QString, int>; -using Shortcut = std::pair<QString, ContextualShortcut>; + +struct Shortcut { + QString name; + QString group; + ContextualShortcut shortcut; +}; using Themes = std::array<std::pair<const char*, const char*>, 2>; extern const Themes themes; @@ -50,8 +55,8 @@ struct Values { QString roms_path; QString symbols_path; QString screenshot_path; - QString gamedir; - bool gamedir_deepscan; + QString game_directory_path; + bool game_directory_deepscan; QStringList recent_files; QString theme; diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp new file mode 100644 index 000000000..d3edf6ec3 --- /dev/null +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp @@ -0,0 +1,37 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QDialogButtonBox> +#include <QKeySequenceEdit> +#include <QVBoxLayout> +#include "yuzu/util/sequence_dialog/sequence_dialog.h" + +SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { + setWindowTitle(tr("Enter a hotkey")); + auto* layout = new QVBoxLayout(this); + key_sequence = new QKeySequenceEdit; + layout->addWidget(key_sequence); + auto* buttons = + new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + buttons->setCenterButtons(true); + layout->addWidget(buttons); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +SequenceDialog::~SequenceDialog() = default; + +QKeySequence SequenceDialog::GetSequence() const { + // Only the first key is returned. The other 3, if present, are ignored. + return QKeySequence(key_sequence->keySequence()[0]); +} + +bool SequenceDialog::focusNextPrevChild(bool next) { + return false; +} + +void SequenceDialog::closeEvent(QCloseEvent*) { + reject(); +} diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.h b/src/yuzu/util/sequence_dialog/sequence_dialog.h new file mode 100644 index 000000000..969c77740 --- /dev/null +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.h @@ -0,0 +1,24 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QDialog> + +class QKeySequenceEdit; + +class SequenceDialog : public QDialog { + Q_OBJECT + +public: + explicit SequenceDialog(QWidget* parent = nullptr); + ~SequenceDialog() override; + + QKeySequence GetSequence() const; + void closeEvent(QCloseEvent*) override; + +private: + QKeySequenceEdit* key_sequence; + bool focusNextPrevChild(bool next) override; +}; |