summaryrefslogtreecommitdiffstats
path: root/src/yuzu
diff options
context:
space:
mode:
authorFeng Chen <VonChenPlus@gmail.com>2022-09-20 05:56:43 +0200
committerGitHub <noreply@github.com>2022-09-20 05:56:43 +0200
commitc864cb57726e76e9dc4558036f3212168bec825d (patch)
treeca79c4397f40990488a7b5691e15c0fcfec507b6 /src/yuzu
parentvideo_core: Generate mipmap texture by drawing (diff)
parentMerge pull request #8849 from Morph1984/parallel-astc (diff)
downloadyuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar.gz
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar.bz2
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar.lz
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar.xz
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.tar.zst
yuzu-c864cb57726e76e9dc4558036f3212168bec825d.zip
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/config.h4
-rw-r--r--src/yuzu/configuration/configure_debug.cpp23
-rw-r--r--src/yuzu/configuration/configure_debug.h6
-rw-r--r--src/yuzu/configuration/configure_debug.ui129
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui10
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp23
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp3
-rw-r--r--src/yuzu/configuration/configure_tas.ui3
-rw-r--r--src/yuzu/configuration/configure_web.cpp21
-rw-r--r--src/yuzu/configuration/input_profiles.cpp9
-rw-r--r--src/yuzu/configuration/input_profiles.h4
-rw-r--r--src/yuzu/main.cpp57
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/mini_dump.cpp202
-rw-r--r--src/yuzu/mini_dump.h19
-rw-r--r--src/yuzu/multiplayer/chat_room.cpp25
-rw-r--r--src/yuzu/multiplayer/client_room.cpp2
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp17
-rw-r--r--src/yuzu/multiplayer/direct_connect.h7
-rw-r--r--src/yuzu/multiplayer/host_room.cpp19
-rw-r--r--src/yuzu/multiplayer/host_room.h6
-rw-r--r--src/yuzu/multiplayer/lobby.cpp20
-rw-r--r--src/yuzu/multiplayer/lobby.h9
-rw-r--r--src/yuzu/multiplayer/message.cpp10
-rw-r--r--src/yuzu/multiplayer/message.h9
-rw-r--r--src/yuzu/multiplayer/state.cpp13
-rw-r--r--src/yuzu/multiplayer/state.h9
-rw-r--r--src/yuzu/startup_checks.cpp29
-rw-r--r--src/yuzu/startup_checks.h5
35 files changed, 565 insertions, 161 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 50007338f..29d506c47 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -208,6 +208,16 @@ add_executable(yuzu
yuzu.rc
)
+if (WIN32 AND YUZU_CRASH_DUMPS)
+ target_sources(yuzu PRIVATE
+ mini_dump.cpp
+ mini_dump.h
+ )
+
+ target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY})
+ target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
+endif()
+
file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 8be311fcb..1d8072243 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -63,7 +63,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
: QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
- input_profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
+ input_profiles(std::make_unique<InputProfiles>()), system{system_} {
ui->setupUi(this);
player_widgets = {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index da6e5aa88..a4ed68422 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -15,8 +15,7 @@
namespace FS = Common::FS;
-Config::Config(Core::System& system_, const std::string& config_name, ConfigType config_type)
- : type(config_type), system{system_} {
+Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
global = config_type == ConfigType::GlobalConfig;
Initialize(config_name);
@@ -546,6 +545,7 @@ void Config::ReadDebuggingValues() {
ReadBasicSetting(Settings::values.use_debug_asserts);
ReadBasicSetting(Settings::values.use_auto_stub);
ReadBasicSetting(Settings::values.enable_all_controllers);
+ ReadBasicSetting(Settings::values.create_crash_dumps);
qt_config->endGroup();
}
@@ -684,6 +684,7 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.shader_backend);
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
+ ReadGlobalSetting(Settings::values.use_pessimistic_flushes);
ReadGlobalSetting(Settings::values.bg_red);
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
@@ -1160,6 +1161,7 @@ void Config::SaveDebuggingValues() {
WriteBasicSetting(Settings::values.use_debug_asserts);
WriteBasicSetting(Settings::values.disable_macro_jit);
WriteBasicSetting(Settings::values.enable_all_controllers);
+ WriteBasicSetting(Settings::values.create_crash_dumps);
qt_config->endGroup();
}
@@ -1300,6 +1302,7 @@ void Config::SaveRendererValues() {
Settings::values.shader_backend.UsingGlobal());
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
+ WriteGlobalSetting(Settings::values.use_pessimistic_flushes);
WriteGlobalSetting(Settings::values.bg_red);
WriteGlobalSetting(Settings::values.bg_green);
WriteGlobalSetting(Settings::values.bg_blue);
@@ -1545,7 +1548,6 @@ void Config::Reload() {
ReadValues();
// To apply default value changes
SaveValues();
- system.ApplySettings();
}
void Config::Save() {
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 486ceea94..06fa7d2d0 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -25,7 +25,7 @@ public:
InputProfile,
};
- explicit Config(Core::System& system_, const std::string& config_name = "qt-config",
+ explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig);
~Config();
@@ -194,8 +194,6 @@ private:
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
bool global;
-
- Core::System& system;
};
// These metatype declarations cannot be in common/settings.h because core is devoid of QT
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index e16d127a8..622808e94 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QDesktopServices>
+#include <QMessageBox>
#include <QUrl>
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
@@ -14,7 +15,7 @@
#include "yuzu/uisettings.h"
ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
- : QWidget(parent), ui{std::make_unique<Ui::ConfigureDebug>()}, system{system_} {
+ : QScrollArea(parent), ui{std::make_unique<Ui::ConfigureDebug>()}, system{system_} {
ui->setupUi(this);
SetConfiguration();
@@ -26,6 +27,16 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
connect(ui->toggle_gdbstub, &QCheckBox::toggled,
[&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
+
+ connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) {
+ if (crash_dump_warning_shown) {
+ return;
+ }
+ QMessageBox::warning(this, tr("Restart Required"),
+ tr("yuzu is required to restart in order to apply this setting."),
+ QMessageBox::Ok, QMessageBox::Ok);
+ crash_dump_warning_shown = true;
+ });
}
ConfigureDebug::~ConfigureDebug() = default;
@@ -71,7 +82,14 @@ void ConfigureDebug::SetConfiguration() {
ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue());
#else
ui->disable_web_applet->setEnabled(false);
- ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled"));
+ ui->disable_web_applet->setText(tr("Web applet not compiled"));
+#endif
+
+#ifdef YUZU_DBGHELP
+ ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue());
+#else
+ ui->create_crash_dumps->setEnabled(false);
+ ui->create_crash_dumps->setText(tr("MiniDump creation not compiled"));
#endif
}
@@ -84,6 +102,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
+ Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h
index 64d68ab8f..030a0b7f7 100644
--- a/src/yuzu/configuration/configure_debug.h
+++ b/src/yuzu/configuration/configure_debug.h
@@ -4,7 +4,7 @@
#pragma once
#include <memory>
-#include <QWidget>
+#include <QScrollArea>
namespace Core {
class System;
@@ -14,7 +14,7 @@ namespace Ui {
class ConfigureDebug;
}
-class ConfigureDebug : public QWidget {
+class ConfigureDebug : public QScrollArea {
Q_OBJECT
public:
@@ -32,4 +32,6 @@ private:
std::unique_ptr<Ui::ConfigureDebug> ui;
const Core::System& system;
+
+ bool crash_dump_warning_shown{false};
};
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 4c16274fc..314d47af5 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -1,62 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureDebug</class>
- <widget class="QWidget" name="ConfigureDebug">
+ <widget class="QScrollArea" name="ConfigureDebug">
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget">
<layout class="QVBoxLayout" name="verticalLayout_1">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>Debugger</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <item>
+ <widget class="QCheckBox" name="toggle_gdbstub">
+ <property name="text">
+ <string>Enable GDB Stub</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_11">
- <item>
- <widget class="QCheckBox" name="toggle_gdbstub">
- <property name="text">
- <string>Enable GDB Stub</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QLabel" name="label_11">
- <property name="text">
- <string>Port:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QSpinBox" name="gdbport_spinbox">
- <property name="minimum">
- <number>1024</number>
- </property>
- <property name="maximum">
- <number>65535</number>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QSpinBox" name="gdbport_spinbox">
+ <property name="minimum">
+ <number>1024</number>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
</item>
</layout>
- </widget>
- </item>
- </layout>
+ </item>
+ </layout>
+ </widget>
</item>
+ </layout>
+ </item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
@@ -227,6 +231,13 @@
<string>Debugging</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="reporting_services">
+ <property name="text">
+ <string>Enable Verbose Reporting Services**</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0">
<widget class="QCheckBox" name="fs_access_log">
<property name="text">
@@ -234,20 +245,20 @@
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="0" column="1">
<widget class="QCheckBox" name="dump_audio_commands">
- <property name="text">
- <string>Dump Audio Commands To Console**</string>
- </property>
<property name="toolTip">
<string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
</property>
+ <property name="text">
+ <string>Dump Audio Commands To Console**</string>
+ </property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="reporting_services">
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="create_crash_dumps">
<property name="text">
- <string>Enable Verbose Reporting Services**</string>
+ <string>Create Minidump After Crash</string>
</property>
</widget>
</item>
@@ -322,6 +333,7 @@
</item>
</layout>
</widget>
+ </widget>
<tabstops>
<tabstop>log_filter_edit</tabstop>
<tabstop>toggle_console</tabstop>
@@ -335,7 +347,6 @@
<tabstop>disable_loop_safety_checks</tabstop>
<tabstop>fs_access_log</tabstop>
<tabstop>reporting_services</tabstop>
- <tabstop>dump_audio_commands</tabstop>
<tabstop>quest_flag</tabstop>
<tabstop>enable_cpu_debugging</tabstop>
<tabstop>use_debug_asserts</tabstop>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 7c3196c83..01f074699 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,6 +28,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
+ ui->use_pessimistic_flushes->setChecked(Settings::values.use_pessimistic_flushes.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setCurrentIndex(
@@ -55,6 +56,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time, use_fast_gpu_time);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_pessimistic_flushes,
+ ui->use_pessimistic_flushes, use_pessimistic_flushes);
}
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
@@ -77,6 +80,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
+ ui->use_pessimistic_flushes->setEnabled(
+ Settings::values.use_pessimistic_flushes.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
@@ -89,6 +94,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
use_asynchronous_shaders);
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
+ ConfigurationShared::SetColoredTristate(ui->use_pessimistic_flushes,
+ Settings::values.use_pessimistic_flushes,
+ use_pessimistic_flushes);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 1ef7bd916..12e816905 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -39,6 +39,7 @@ private:
ConfigurationShared::CheckState use_vsync;
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
+ ConfigurationShared::CheckState use_pessimistic_flushes;
const Core::System& system;
};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index d6d819364..87a121471 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -100,6 +100,16 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="use_pessimistic_flushes">
+ <property name="toolTip">
+ <string>Enables pessimistic buffer flushes. This option will force unmodified buffers to be flushed, which can cost performance.</string>
+ </property>
+ <property name="text">
+ <string>Use pessimistic buffer flushes (Hack)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QWidget" name="af_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 16fba3deb..cb55472c9 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -65,7 +65,7 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system)
ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
- profiles(std::make_unique<InputProfiles>(system_)), system{system_} {
+ profiles(std::make_unique<InputProfiles>()), system{system_} {
ui->setupUi(this);
}
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 109689c88..9e5a40fe7 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -161,6 +161,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
+ const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
const auto common_button_name = input_subsystem->GetButtonName(param);
// Retrieve the names from Qt
@@ -184,7 +185,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
}
if (param.Has("axis")) {
const QString axis = QString::fromStdString(param.Get("axis", ""));
- return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
+ return QObject::tr("%1%2Axis %3").arg(toggle, invert, axis);
}
if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
@@ -362,18 +363,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
button_map[button_id]->setText(tr("[not set]"));
});
if (param.Has("code") || param.Has("button") || param.Has("hat")) {
- context_menu.addAction(tr("Toggle button"), [&] {
- const bool toggle_value = !param.Get("toggle", false);
- param.Set("toggle", toggle_value);
- button_map[button_id]->setText(ButtonToText(param));
- emulated_controller->SetButtonParam(button_id, param);
- });
context_menu.addAction(tr("Invert button"), [&] {
const bool invert_value = !param.Get("inverted", false);
param.Set("inverted", invert_value);
button_map[button_id]->setText(ButtonToText(param));
emulated_controller->SetButtonParam(button_id, param);
});
+ context_menu.addAction(tr("Toggle button"), [&] {
+ const bool toggle_value = !param.Get("toggle", false);
+ param.Set("toggle", toggle_value);
+ button_map[button_id]->setText(ButtonToText(param));
+ emulated_controller->SetButtonParam(button_id, param);
+ });
}
if (param.Has("axis")) {
context_menu.addAction(tr("Invert axis"), [&] {
@@ -398,6 +399,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
emulated_controller->SetButtonParam(button_id, param);
});
+ context_menu.addAction(tr("Toggle axis"), [&] {
+ const bool toggle_value = !param.Get("toggle", false);
+ param.Set("toggle", toggle_value);
+ button_map[button_id]->setText(ButtonToText(param));
+ emulated_controller->SetButtonParam(button_id, param);
+ });
}
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
});
@@ -1410,7 +1417,7 @@ void ConfigureInputPlayer::HandleClick(
ui->controllerFrame->BeginMappingAnalog(button_id);
}
- timeout_timer->start(2500); // Cancel after 2.5 seconds
+ timeout_timer->start(4000); // Cancel after 4 seconds
poll_timer->start(25); // Check for new inputs every 25ms
}
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index af8343b2e..c3cb8f61d 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -42,8 +42,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
- game_config =
- std::make_unique<Config>(system, config_file_name, Config::ConfigType::PerGameConfig);
+ game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
audio_tab = std::make_unique<ConfigureAudio>(system_, this);
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index cf88a5bf0..625af0c89 100644
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/src/yuzu/configuration/configure_tas.ui
@@ -16,6 +16,9 @@
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reads controller input from scripts in the same format as TAS-nx scripts.&lt;br/&gt;For a more detailed explanation, please consult the &lt;a href=&quot;https://yuzu-emu.org/help/feature/tas/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;help page&lt;/span&gt;&lt;/a&gt; on the yuzu website.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item row="1" column="0" colspan="4">
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index d668c992b..ab526e4ca 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -128,20 +128,25 @@ void ConfigureWeb::RefreshTelemetryID() {
void ConfigureWeb::OnLoginChanged() {
if (ui->edit_token->text().isEmpty()) {
user_verified = true;
-
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ // Empty = no icon
+ ui->label_token_verified->setPixmap(QPixmap());
+ ui->label_token_verified->setToolTip(QString());
} else {
user_verified = false;
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
+ // Show an info icon if it's been changed, clearer than showing failure
+ const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("info")).pixmap(16);
ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setToolTip(
+ tr("Unverified, please click Verify before saving configuration", "Tooltip"));
}
}
void ConfigureWeb::VerifyLogin() {
ui->button_verify_login->setDisabled(true);
ui->button_verify_login->setText(tr("Verifying..."));
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("sync")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verifying..."));
verify_watcher.setFuture(QtConcurrent::run(
[username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()),
token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] {
@@ -155,13 +160,13 @@ void ConfigureWeb::OnLoginVerified() {
if (verify_watcher.result()) {
user_verified = true;
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("checked")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verified", "Tooltip"));
ui->username->setText(
QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString())));
} else {
- const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16);
- ui->label_token_verified->setPixmap(pixmap);
+ ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("failed")).pixmap(16));
+ ui->label_token_verified->setToolTip(tr("Verification failed", "Tooltip"));
ui->username->setText(tr("Unspecified"));
QMessageBox::critical(this, tr("Verification failed"),
tr("Verification failed. Check that you have entered your token "
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
index 20b22e7de..807afbeb2 100644
--- a/src/yuzu/configuration/input_profiles.cpp
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -27,7 +27,7 @@ std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) {
} // namespace
-InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
+InputProfiles::InputProfiles() {
const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input";
if (!FS::IsDir(input_profile_loc)) {
@@ -43,8 +43,8 @@ InputProfiles::InputProfiles(Core::System& system_) : system{system_} {
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
map_profiles.insert_or_assign(
- name_without_ext, std::make_unique<Config>(system, name_without_ext,
- Config::ConfigType::InputProfile));
+ name_without_ext,
+ std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile));
}
return true;
@@ -80,8 +80,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p
}
map_profiles.insert_or_assign(
- profile_name,
- std::make_unique<Config>(system, profile_name, Config::ConfigType::InputProfile));
+ profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
return SaveProfile(profile_name, player_index);
}
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
index 65fc9e62c..2bf3e4250 100644
--- a/src/yuzu/configuration/input_profiles.h
+++ b/src/yuzu/configuration/input_profiles.h
@@ -15,7 +15,7 @@ class Config;
class InputProfiles {
public:
- explicit InputProfiles(Core::System& system_);
+ explicit InputProfiles();
virtual ~InputProfiles();
std::vector<std::string> GetInputProfileNames();
@@ -31,6 +31,4 @@ private:
bool ProfileExistsInMap(const std::string& profile_name) const;
std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
-
- Core::System& system;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e103df977..3c1bd19db 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -138,6 +138,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/uisettings.h"
#include "yuzu/util/clickable_label.h"
+#ifdef YUZU_DBGHELP
+#include "yuzu/mini_dump.h"
+#endif
+
using namespace Common::Literals;
#ifdef USE_DISCORD_PRESENCE
@@ -269,10 +273,9 @@ bool GMainWindow::CheckDarkMode() {
#endif // __linux__
}
-GMainWindow::GMainWindow(bool has_broken_vulkan)
+GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
- input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
- config{std::make_unique<Config>(*system)},
+ input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
#ifdef __linux__
@@ -860,7 +863,7 @@ void GMainWindow::InitializeWidgets() {
});
multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room,
- ui->action_Show_Room, system->GetRoomNetwork());
+ ui->action_Show_Room, *system);
multiplayer_state->setVisible(false);
// Create status bar
@@ -1637,7 +1640,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
- Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig);
+ Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
+ system->ApplySettings();
}
// Save configurations
@@ -2981,7 +2985,7 @@ void GMainWindow::OnConfigure() {
Settings::values.disabled_addons.clear();
- config = std::make_unique<Config>(*system);
+ config = std::make_unique<Config>();
UISettings::values.reset_to_defaults = false;
UISettings::values.game_dirs = std::move(old_game_dirs);
@@ -3042,6 +3046,7 @@ void GMainWindow::OnConfigure() {
UpdateStatusButtons();
controller_dialog->refreshConfiguration();
+ system->ApplySettings();
}
void GMainWindow::OnConfigureTas() {
@@ -3254,26 +3259,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
return;
}
- QFile nfc_file{filename};
- if (!nfc_file.open(QIODevice::ReadOnly)) {
- QMessageBox::warning(this, tr("Error opening Amiibo data file"),
- tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
- return;
- }
-
- const u64 nfc_file_size = nfc_file.size();
- std::vector<u8> buffer(nfc_file_size);
- const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
- if (nfc_file_size != read_size) {
- QMessageBox::warning(this, tr("Error reading Amiibo data file"),
- tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
- "was only able to read %2 bytes.")
- .arg(nfc_file_size)
- .arg(read_size));
- return;
- }
-
- if (!nfc->LoadAmiibo(buffer)) {
+ if (!nfc->LoadAmiibo(filename.toStdString())) {
QMessageBox::warning(this, tr("Error loading Amiibo data"),
tr("Unable to load Amiibo data."));
}
@@ -4082,7 +4068,24 @@ void GMainWindow::changeEvent(QEvent* event) {
#endif
int main(int argc, char* argv[]) {
+ std::unique_ptr<Config> config = std::make_unique<Config>();
bool has_broken_vulkan = false;
+ bool is_child = false;
+ if (CheckEnvVars(&is_child)) {
+ return 0;
+ }
+
+#ifdef YUZU_DBGHELP
+ PROCESS_INFORMATION pi;
+ if (!is_child && Settings::values.create_crash_dumps.GetValue() &&
+ MiniDump::SpawnDebuggee(argv[0], pi)) {
+ // Delete the config object so that it doesn't save when the program exits
+ config.reset(nullptr);
+ MiniDump::DebugDebuggee(pi);
+ return 0;
+ }
+#endif
+
if (StartupChecks(argv[0], &has_broken_vulkan)) {
return 0;
}
@@ -4135,7 +4138,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
- GMainWindow main_window{has_broken_vulkan};
+ GMainWindow main_window{std::move(config), has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1ae2b93d9..716aef063 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -120,7 +120,7 @@ class GMainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
- explicit GMainWindow(bool has_broken_vulkan);
+ explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan);
~GMainWindow() override;
bool DropAction(QDropEvent* event);
diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp
new file mode 100644
index 000000000..a34dc6a9c
--- /dev/null
+++ b/src/yuzu/mini_dump.cpp
@@ -0,0 +1,202 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include <filesystem>
+#include <fmt/format.h>
+#include <windows.h>
+#include "yuzu/mini_dump.h"
+#include "yuzu/startup_checks.h"
+
+// dbghelp.h must be included after windows.h
+#include <dbghelp.h>
+
+namespace MiniDump {
+
+void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
+ EXCEPTION_POINTERS* pep) {
+ char file_name[255];
+ const std::time_t the_time = std::time(nullptr);
+ std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
+
+ // Open the file
+ HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
+ fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
+ return;
+ }
+
+ // Create the minidump
+ const MINIDUMP_TYPE dump_type = MiniDumpNormal;
+
+ const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
+ dump_type, (pep != 0) ? info : 0, 0, 0);
+
+ if (write_dump_status) {
+ fmt::print(stderr, "MiniDump created: {}", file_name);
+ } else {
+ fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
+ }
+
+ // Close the file
+ CloseHandle(file_handle);
+}
+
+void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
+ EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
+
+ HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
+ if (thread_handle == nullptr) {
+ fmt::print(stderr, "OpenThread failed ({})", GetLastError());
+ return;
+ }
+
+ // Get child process context
+ CONTEXT context = {};
+ context.ContextFlags = CONTEXT_ALL;
+ if (!GetThreadContext(thread_handle, &context)) {
+ fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
+ return;
+ }
+
+ // Create exception pointers for minidump
+ EXCEPTION_POINTERS ep;
+ ep.ExceptionRecord = &record;
+ ep.ContextRecord = &context;
+
+ MINIDUMP_EXCEPTION_INFORMATION info;
+ info.ThreadId = deb_ev.dwThreadId;
+ info.ExceptionPointers = &ep;
+ info.ClientPointers = false;
+
+ CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
+
+ if (CloseHandle(thread_handle) == 0) {
+ fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
+ }
+}
+
+bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
+ std::memset(&pi, 0, sizeof(pi));
+
+ // Don't debug if we are already being debugged
+ if (IsDebuggerPresent()) {
+ return false;
+ }
+
+ if (!SpawnChild(arg0, &pi, 0)) {
+ fmt::print(stderr, "warning: continuing without crash dumps");
+ return false;
+ }
+
+ const bool can_debug = DebugActiveProcess(pi.dwProcessId);
+ if (!can_debug) {
+ fmt::print(stderr,
+ "warning: DebugActiveProcess failed ({}), continuing without crash dumps",
+ GetLastError());
+ return false;
+ }
+
+ return true;
+}
+
+static const char* ExceptionName(DWORD exception) {
+ switch (exception) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ return "EXCEPTION_ACCESS_VIOLATION";
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ return "EXCEPTION_DATATYPE_MISALIGNMENT";
+ case EXCEPTION_BREAKPOINT:
+ return "EXCEPTION_BREAKPOINT";
+ case EXCEPTION_SINGLE_STEP:
+ return "EXCEPTION_SINGLE_STEP";
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ return "EXCEPTION_FLT_DENORMAL_OPERAND";
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ return "EXCEPTION_FLT_INEXACT_RESULT";
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ return "EXCEPTION_FLT_INVALID_OPERATION";
+ case EXCEPTION_FLT_OVERFLOW:
+ return "EXCEPTION_FLT_OVERFLOW";
+ case EXCEPTION_FLT_STACK_CHECK:
+ return "EXCEPTION_FLT_STACK_CHECK";
+ case EXCEPTION_FLT_UNDERFLOW:
+ return "EXCEPTION_FLT_UNDERFLOW";
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ return "EXCEPTION_INT_DIVIDE_BY_ZERO";
+ case EXCEPTION_INT_OVERFLOW:
+ return "EXCEPTION_INT_OVERFLOW";
+ case EXCEPTION_PRIV_INSTRUCTION:
+ return "EXCEPTION_PRIV_INSTRUCTION";
+ case EXCEPTION_IN_PAGE_ERROR:
+ return "EXCEPTION_IN_PAGE_ERROR";
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ return "EXCEPTION_ILLEGAL_INSTRUCTION";
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
+ case EXCEPTION_STACK_OVERFLOW:
+ return "EXCEPTION_STACK_OVERFLOW";
+ case EXCEPTION_INVALID_DISPOSITION:
+ return "EXCEPTION_INVALID_DISPOSITION";
+ case EXCEPTION_GUARD_PAGE:
+ return "EXCEPTION_GUARD_PAGE";
+ case EXCEPTION_INVALID_HANDLE:
+ return "EXCEPTION_INVALID_HANDLE";
+ default:
+ return "unknown exception type";
+ }
+}
+
+void DebugDebuggee(PROCESS_INFORMATION& pi) {
+ DEBUG_EVENT deb_ev = {};
+
+ while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
+ const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
+ if (!wait_success) {
+ fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
+ return;
+ }
+
+ switch (deb_ev.dwDebugEventCode) {
+ case OUTPUT_DEBUG_STRING_EVENT:
+ case CREATE_PROCESS_DEBUG_EVENT:
+ case CREATE_THREAD_DEBUG_EVENT:
+ case EXIT_PROCESS_DEBUG_EVENT:
+ case EXIT_THREAD_DEBUG_EVENT:
+ case LOAD_DLL_DEBUG_EVENT:
+ case RIP_EVENT:
+ case UNLOAD_DLL_DEBUG_EVENT:
+ // Continue on all other debug events
+ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
+ break;
+ case EXCEPTION_DEBUG_EVENT:
+ EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
+
+ // We want to generate a crash dump if we are seeing the same exception again.
+ if (!deb_ev.u.Exception.dwFirstChance) {
+ fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
+ record.ExceptionCode, ExceptionName(record.ExceptionCode));
+ DumpFromDebugEvent(deb_ev, pi);
+ }
+
+ // Continue without handling the exception.
+ // Lets the debuggee use its own exception handler.
+ // - If one does not exist, we will see the exception once more where we make a minidump
+ // for. Then when it reaches here again, yuzu will probably crash.
+ // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
+ // infinite loop of exceptions.
+ ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
+ break;
+ }
+ }
+}
+
+} // namespace MiniDump
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h
new file mode 100644
index 000000000..d6b6cca84
--- /dev/null
+++ b/src/yuzu/mini_dump.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <windows.h>
+
+#include <dbghelp.h>
+
+namespace MiniDump {
+
+void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
+ EXCEPTION_POINTERS* pep);
+
+void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
+bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
+void DebugDebuggee(PROCESS_INFORMATION& pi);
+
+} // namespace MiniDump
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 1968a3c75..9e672f82e 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -16,7 +16,7 @@
#include <QUrl>
#include <QtConcurrent/QtConcurrentRun>
#include "common/logging/log.h"
-#include "core/announce_multiplayer_session.h"
+#include "network/announce_multiplayer_session.h"
#include "ui_chat_room.h"
#include "yuzu/game_list_p.h"
#include "yuzu/multiplayer/chat_room.h"
@@ -122,19 +122,22 @@ public:
static const int UsernameRole = Qt::UserRole + 2;
static const int AvatarUrlRole = Qt::UserRole + 3;
static const int GameNameRole = Qt::UserRole + 4;
+ static const int GameVersionRole = Qt::UserRole + 5;
PlayerListItem() = default;
explicit PlayerListItem(const std::string& nickname, const std::string& username,
- const std::string& avatar_url, const std::string& game_name) {
+ const std::string& avatar_url,
+ const AnnounceMultiplayerRoom::GameInfo& game_info) {
setEditable(false);
setData(QString::fromStdString(nickname), NicknameRole);
setData(QString::fromStdString(username), UsernameRole);
setData(QString::fromStdString(avatar_url), AvatarUrlRole);
- if (game_name.empty()) {
+ if (game_info.name.empty()) {
setData(QObject::tr("Not playing a game"), GameNameRole);
} else {
- setData(QString::fromStdString(game_name), GameNameRole);
+ setData(QString::fromStdString(game_info.name), GameNameRole);
}
+ setData(QString::fromStdString(game_info.version), GameVersionRole);
}
QVariant data(int role) const override {
@@ -149,7 +152,13 @@ public:
} else {
name = QStringLiteral("%1 (%2)").arg(nickname, username);
}
- return QStringLiteral("%1\n %2").arg(name, data(GameNameRole).toString());
+ const QString version = data(GameVersionRole).toString();
+ QString version_string;
+ if (!version.isEmpty()) {
+ version_string = QStringLiteral("(%1)").arg(version);
+ }
+ return QStringLiteral("%1\n %2 %3")
+ .arg(name, data(GameNameRole).toString(), version_string);
}
};
@@ -167,6 +176,10 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C
ui->chat_history->document()->setMaximumBlockCount(max_chat_lines);
+ auto font = ui->chat_history->font();
+ font.setPointSizeF(10);
+ ui->chat_history->setFont(font);
+
// register the network structs to use in slots and signals
qRegisterMetaType<Network::ChatEntry>();
qRegisterMetaType<Network::StatusMessageEntry>();
@@ -366,7 +379,7 @@ void ChatRoom::SetPlayerList(const Network::RoomMember::MemberList& member_list)
if (member.nickname.empty())
continue;
QStandardItem* name_item = new PlayerListItem(member.nickname, member.username,
- member.avatar_url, member.game_info.name);
+ member.avatar_url, member.game_info);
#ifdef ENABLE_WEB_SERVICE
if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) {
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index 86baafbf0..b34a8d004 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -10,7 +10,7 @@
#include <QTime>
#include <QtConcurrent/QtConcurrentRun>
#include "common/logging/log.h"
-#include "core/announce_multiplayer_session.h"
+#include "network/announce_multiplayer_session.h"
#include "ui_client_room.h"
#include "yuzu/game_list_p.h"
#include "yuzu/multiplayer/client_room.h"
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index 4c0ea0a6b..017063074 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -8,6 +8,8 @@
#include <QString>
#include <QtConcurrent/QtConcurrentRun>
#include "common/settings.h"
+#include "core/core.h"
+#include "core/internal_network/network_interface.h"
#include "network/network.h"
#include "ui_direct_connect.h"
#include "yuzu/main.h"
@@ -20,9 +22,10 @@
enum class ConnectionType : u8 { TraversalServer, IP };
-DirectConnectWindow::DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent)
+DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
- ui(std::make_unique<Ui::DirectConnect>()), room_network{room_network_} {
+ ui(std::make_unique<Ui::DirectConnect>()), system{system_}, room_network{
+ system.GetRoomNetwork()} {
ui->setupUi(this);
@@ -53,10 +56,20 @@ void DirectConnectWindow::RetranslateUi() {
}
void DirectConnectWindow::Connect() {
+ if (!Network::GetSelectedNetworkInterface()) {
+ NetworkMessage::ErrorManager::ShowError(
+ NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED);
+ return;
+ }
if (!ui->nickname->hasAcceptableInput()) {
NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID);
return;
}
+ if (system.IsPoweredOn()) {
+ if (!NetworkMessage::WarnGameRunning()) {
+ return;
+ }
+ }
if (const auto member = room_network.GetRoomMember().lock()) {
// Prevent the user from trying to join a room while they are already joining.
if (member->GetState() == Network::RoomMember::State::Joining) {
diff --git a/src/yuzu/multiplayer/direct_connect.h b/src/yuzu/multiplayer/direct_connect.h
index 4e1043053..e39dd1e0d 100644
--- a/src/yuzu/multiplayer/direct_connect.h
+++ b/src/yuzu/multiplayer/direct_connect.h
@@ -12,11 +12,15 @@ namespace Ui {
class DirectConnect;
}
+namespace Core {
+class System;
+}
+
class DirectConnectWindow : public QDialog {
Q_OBJECT
public:
- explicit DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent = nullptr);
+ explicit DirectConnectWindow(Core::System& system_, QWidget* parent = nullptr);
~DirectConnectWindow();
void RetranslateUi();
@@ -39,5 +43,6 @@ private:
QFutureWatcher<void>* watcher;
std::unique_ptr<Ui::DirectConnect> ui;
Validation validation;
+ Core::System& system;
Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index d70a9a3c8..0c6adfd04 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -12,7 +12,9 @@
#include <QtConcurrent/QtConcurrentRun>
#include "common/logging/log.h"
#include "common/settings.h"
-#include "core/announce_multiplayer_session.h"
+#include "core/core.h"
+#include "core/internal_network/network_interface.h"
+#include "network/announce_multiplayer_session.h"
#include "ui_host_room.h"
#include "yuzu/game_list_p.h"
#include "yuzu/main.h"
@@ -27,10 +29,11 @@
HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Core::AnnounceMultiplayerSession> session,
- Network::RoomNetwork& room_network_)
+ Core::System& system_)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::HostRoom>()),
- announce_multiplayer_session(session), room_network{room_network_} {
+ announce_multiplayer_session(session), system{system_}, room_network{
+ system.GetRoomNetwork()} {
ui->setupUi(this);
// set up validation for all of the fields
@@ -105,6 +108,11 @@ std::unique_ptr<Network::VerifyUser::Backend> HostRoomWindow::CreateVerifyBacken
}
void HostRoomWindow::Host() {
+ if (!Network::GetSelectedNetworkInterface()) {
+ NetworkMessage::ErrorManager::ShowError(
+ NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED);
+ return;
+ }
if (!ui->username->hasAcceptableInput()) {
NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID);
return;
@@ -121,6 +129,11 @@ void HostRoomWindow::Host() {
NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED);
return;
}
+ if (system.IsPoweredOn()) {
+ if (!NetworkMessage::WarnGameRunning()) {
+ return;
+ }
+ }
if (auto member = room_network.GetRoomMember().lock()) {
if (member->GetState() == Network::RoomMember::State::Joining) {
return;
diff --git a/src/yuzu/multiplayer/host_room.h b/src/yuzu/multiplayer/host_room.h
index a968042d0..034cb2eef 100644
--- a/src/yuzu/multiplayer/host_room.h
+++ b/src/yuzu/multiplayer/host_room.h
@@ -17,8 +17,9 @@ class HostRoom;
}
namespace Core {
+class System;
class AnnounceMultiplayerSession;
-}
+} // namespace Core
class ConnectionError;
class ComboBoxProxyModel;
@@ -35,7 +36,7 @@ class HostRoomWindow : public QDialog {
public:
explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Core::AnnounceMultiplayerSession> session,
- Network::RoomNetwork& room_network_);
+ Core::System& system_);
~HostRoomWindow();
/**
@@ -54,6 +55,7 @@ private:
QStandardItemModel* game_list;
ComboBoxProxyModel* proxy;
Validation validation;
+ Core::System& system;
Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 1cc518279..107d40547 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -6,6 +6,8 @@
#include <QtConcurrent/QtConcurrentRun>
#include "common/logging/log.h"
#include "common/settings.h"
+#include "core/core.h"
+#include "core/internal_network/network_interface.h"
#include "network/network.h"
#include "ui_lobby.h"
#include "yuzu/game_list_p.h"
@@ -22,11 +24,11 @@
#endif
Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
- std::shared_ptr<Core::AnnounceMultiplayerSession> session,
- Network::RoomNetwork& room_network_)
+ std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::Lobby>()),
- announce_multiplayer_session(session), room_network{room_network_} {
+ announce_multiplayer_session(session), system{system_}, room_network{
+ system.GetRoomNetwork()} {
ui->setupUi(this);
// setup the watcher for background connections
@@ -114,6 +116,18 @@ void Lobby::OnExpandRoom(const QModelIndex& index) {
}
void Lobby::OnJoinRoom(const QModelIndex& source) {
+ if (!Network::GetSelectedNetworkInterface()) {
+ NetworkMessage::ErrorManager::ShowError(
+ NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED);
+ return;
+ }
+
+ if (system.IsPoweredOn()) {
+ if (!NetworkMessage::WarnGameRunning()) {
+ return;
+ }
+ }
+
if (const auto member = room_network.GetRoomMember().lock()) {
// Prevent the user from trying to join a room while they are already joining.
if (member->GetState() == Network::RoomMember::State::Joining) {
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index 82744ca94..2696aec21 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -9,7 +9,7 @@
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include "common/announce_multiplayer_room.h"
-#include "core/announce_multiplayer_session.h"
+#include "network/announce_multiplayer_session.h"
#include "network/network.h"
#include "yuzu/multiplayer/validation.h"
@@ -20,6 +20,10 @@ class Lobby;
class LobbyModel;
class LobbyFilterProxyModel;
+namespace Core {
+class System;
+}
+
/**
* Listing of all public games pulled from services. The lobby should be simple enough for users to
* find the game they want to play, and join it.
@@ -30,7 +34,7 @@ class Lobby : public QDialog {
public:
explicit Lobby(QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Core::AnnounceMultiplayerSession> session,
- Network::RoomNetwork& room_network_);
+ Core::System& system_);
~Lobby() override;
/**
@@ -94,6 +98,7 @@ private:
std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
QFutureWatcher<void>* watcher;
Validation validation;
+ Core::System& system;
Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/multiplayer/message.cpp b/src/yuzu/multiplayer/message.cpp
index 94d7a38b8..758b5b731 100644
--- a/src/yuzu/multiplayer/message.cpp
+++ b/src/yuzu/multiplayer/message.cpp
@@ -49,6 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED(
QT_TR_NOOP("You do not have enough permission to perform this action."));
const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP(
"The user you are trying to kick/ban could not be found.\nThey may have left the room."));
+const ConnectionError ErrorManager::NO_INTERFACE_SELECTED(
+ QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and "
+ "make a selection."));
static bool WarnMessage(const std::string& title, const std::string& text) {
return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()),
@@ -60,6 +63,13 @@ void ErrorManager::ShowError(const ConnectionError& e) {
QMessageBox::critical(nullptr, tr("Error"), tr(e.GetString().c_str()));
}
+bool WarnGameRunning() {
+ return WarnMessage(
+ QT_TR_NOOP("Game already running"),
+ QT_TR_NOOP("Joining a room when the game is already running is discouraged "
+ "and can cause the room feature not to work correctly.\nProceed anyway?"));
+}
+
bool WarnCloseRoom() {
return WarnMessage(
QT_TR_NOOP("Leave Room"),
diff --git a/src/yuzu/multiplayer/message.h b/src/yuzu/multiplayer/message.h
index 812495c72..f038b9a1f 100644
--- a/src/yuzu/multiplayer/message.h
+++ b/src/yuzu/multiplayer/message.h
@@ -43,11 +43,20 @@ public:
static const ConnectionError IP_COLLISION;
static const ConnectionError PERMISSION_DENIED;
static const ConnectionError NO_SUCH_USER;
+ static const ConnectionError NO_INTERFACE_SELECTED;
/**
* Shows a standard QMessageBox with a error message
*/
static void ShowError(const ConnectionError& e);
};
+
+/**
+ * Show a standard QMessageBox with a warning message about joining a room when
+ * the game is already running
+ * return true if the user wants to close the network connection
+ */
+bool WarnGameRunning();
+
/**
* Show a standard QMessageBox with a warning message about leaving the room
* return true if the user wants to close the network connection
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp
index dba76b22b..66e098296 100644
--- a/src/yuzu/multiplayer/state.cpp
+++ b/src/yuzu/multiplayer/state.cpp
@@ -8,6 +8,7 @@
#include <QStandardItemModel>
#include "common/announce_multiplayer_room.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "yuzu/game_list.h"
#include "yuzu/multiplayer/client_room.h"
#include "yuzu/multiplayer/direct_connect.h"
@@ -19,10 +20,9 @@
#include "yuzu/util/clickable_label.h"
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_,
- QAction* leave_room_, QAction* show_room_,
- Network::RoomNetwork& room_network_)
+ QAction* leave_room_, QAction* show_room_, Core::System& system_)
: QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_),
- show_room(show_room_), room_network{room_network_} {
+ show_room(show_room_), system{system_}, room_network{system.GetRoomNetwork()} {
if (auto member = room_network.GetRoomMember().lock()) {
// register the network structs to use in slots and signals
state_callback_handle = member->BindOnStateChanged(
@@ -208,15 +208,14 @@ static void BringWidgetToFront(QWidget* widget) {
void MultiplayerState::OnViewLobby() {
if (lobby == nullptr) {
- lobby = new Lobby(this, game_list_model, announce_multiplayer_session, room_network);
+ lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system);
}
BringWidgetToFront(lobby);
}
void MultiplayerState::OnCreateRoom() {
if (host_room == nullptr) {
- host_room =
- new HostRoomWindow(this, game_list_model, announce_multiplayer_session, room_network);
+ host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system);
}
BringWidgetToFront(host_room);
}
@@ -279,7 +278,7 @@ void MultiplayerState::OnOpenNetworkRoom() {
void MultiplayerState::OnDirectConnectToRoom() {
if (direct_connect == nullptr) {
- direct_connect = new DirectConnectWindow(room_network, this);
+ direct_connect = new DirectConnectWindow(system, this);
}
BringWidgetToFront(direct_connect);
}
diff --git a/src/yuzu/multiplayer/state.h b/src/yuzu/multiplayer/state.h
index 9c60712d5..c92496413 100644
--- a/src/yuzu/multiplayer/state.h
+++ b/src/yuzu/multiplayer/state.h
@@ -4,7 +4,7 @@
#pragma once
#include <QWidget>
-#include "core/announce_multiplayer_session.h"
+#include "network/announce_multiplayer_session.h"
#include "network/network.h"
class QStandardItemModel;
@@ -14,12 +14,16 @@ class ClientRoomWindow;
class DirectConnectWindow;
class ClickableLabel;
+namespace Core {
+class System;
+}
+
class MultiplayerState : public QWidget {
Q_OBJECT;
public:
explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room,
- QAction* show_room, Network::RoomNetwork& room_network_);
+ QAction* show_room, Core::System& system_);
~MultiplayerState();
/**
@@ -86,6 +90,7 @@ private:
Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle;
bool show_notification = false;
+ Core::System& system;
Network::RoomNetwork& room_network;
};
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index 8421280bf..29b87da05 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -31,19 +31,36 @@ void CheckVulkan() {
}
}
-bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
+bool CheckEnvVars(bool* is_child) {
#ifdef _WIN32
// Check environment variable to see if we are the child
char variable_contents[8];
const DWORD startup_check_var =
GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
- if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
+ if (startup_check_var > 0 && std::strncmp(variable_contents, ENV_VAR_ENABLED_TEXT, 8) == 0) {
CheckVulkan();
return true;
}
+ // Don't perform startup checks if we are a child process
+ char is_child_s[8];
+ const DWORD is_child_len = GetEnvironmentVariableA(IS_CHILD_ENV_VAR, is_child_s, 8);
+ if (is_child_len > 0 && std::strncmp(is_child_s, ENV_VAR_ENABLED_TEXT, 8) == 0) {
+ *is_child = true;
+ return false;
+ } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) {
+ std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
+ IS_CHILD_ENV_VAR, GetLastError());
+ return true;
+ }
+#endif
+ return false;
+}
+
+bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
+#ifdef _WIN32
// Set the startup variable for child processes
- const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
+ const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT);
if (!env_var_set) {
std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
STARTUP_CHECK_ENV_VAR, GetLastError());
@@ -53,7 +70,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
PROCESS_INFORMATION process_info;
std::memset(&process_info, '\0', sizeof(process_info));
- if (!SpawnChild(arg0, &process_info)) {
+ if (!SpawnChild(arg0, &process_info, 0)) {
return false;
}
@@ -106,7 +123,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
}
#ifdef _WIN32
-bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) {
STARTUPINFOA startup_info;
std::memset(&startup_info, '\0', sizeof(startup_info));
@@ -120,7 +137,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
false, // bInheritHandles
- 0, // dwCreationFlags
+ flags, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // lpCurrentDirectory
&startup_info, // lpStartupInfo
diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h
index 096dd54a8..f2fc2d9d4 100644
--- a/src/yuzu/startup_checks.h
+++ b/src/yuzu/startup_checks.h
@@ -7,11 +7,14 @@
#include <windows.h>
#endif
+constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD";
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
+constexpr char ENV_VAR_ENABLED_TEXT[] = "ON";
void CheckVulkan();
+bool CheckEnvVars(bool* is_child);
bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
#ifdef _WIN32
-bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
+bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags);
#endif