summaryrefslogtreecommitdiff
path: root/src/yuzu/configuration
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu/configuration')
-rw-r--r--src/yuzu/configuration/config.cpp332
-rw-r--r--src/yuzu/configuration/config.h22
-rw-r--r--src/yuzu/configuration/configure.ui20
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp7
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h9
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui20
-rw-r--r--src/yuzu/configuration/configure_input.cpp76
-rw-r--r--src/yuzu/configuration/configure_input.h14
-rw-r--r--src/yuzu/configuration/configure_input.ui46
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp14
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui192
-rw-r--r--src/yuzu/configuration/configure_input_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_dialog.h38
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp566
-rw-r--r--src/yuzu/configuration/configure_input_player.h49
-rw-r--r--src/yuzu/configuration/configure_input_player.ui241
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.cpp37
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.h40
-rw-r--r--src/yuzu/configuration/configure_input_profile_dialog.ui (renamed from src/yuzu/configuration/configure_input_dialog.ui)24
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui10
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui46
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp3
-rw-r--r--src/yuzu/configuration/configure_per_game.ui20
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.ui10
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp146
-rw-r--r--src/yuzu/configuration/configure_vibration.h43
-rw-r--r--src/yuzu/configuration/configure_vibration.ui546
-rw-r--r--src/yuzu/configuration/input_profiles.cpp131
-rw-r--r--src/yuzu/configuration/input_profiles.h32
31 files changed, 2009 insertions, 786 deletions
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1ce62e4a6..6fa842cd5 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
#include <array>
#include <QKeySequence>
#include <QSettings>
+#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
@@ -14,14 +15,10 @@
namespace FS = Common::FS;
-Config::Config(const std::string& config_file, bool is_global) {
- // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
- FS::CreateFullPath(qt_config_loc);
- qt_config =
- std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
- global = is_global;
- Reload();
+Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
+ global = config_type == ConfigType::GlobalConfig;
+
+ Initialize(config_name);
}
Config::~Config() {
@@ -242,84 +239,152 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
}};
// clang-format on
-void Config::ReadPlayerValues() {
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
- auto& player = Settings::values.players[p];
+void Config::Initialize(const std::string& config_name) {
+ switch (type) {
+ case ConfigType::GlobalConfig:
+ qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir),
+ config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ Reload();
+ break;
+ case ConfigType::PerGameConfig:
+ qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ Reload();
+ break;
+ case ConfigType::InputProfile:
+ qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ break;
+ }
+}
+
+void Config::ReadPlayerValue(std::size_t player_index) {
+ const QString player_prefix = [this, player_index] {
+ if (type == ConfigType::InputProfile) {
+ return QString{};
+ } else {
+ return QStringLiteral("player_%1_").arg(player_index);
+ }
+ }();
+
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ if (player_prefix.isEmpty()) {
+ const auto controller = static_cast<Settings::ControllerType>(
+ qt_config
+ ->value(QStringLiteral("%1type").arg(player_prefix),
+ static_cast<u8>(Settings::ControllerType::ProController))
+ .toUInt());
+ if (controller == Settings::ControllerType::LeftJoycon ||
+ controller == Settings::ControllerType::RightJoycon) {
+ player.controller_type = controller;
+ }
+ } else {
player.connected =
- ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
+ ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
+ .toBool();
player.controller_type = static_cast<Settings::ControllerType>(
qt_config
- ->value(QStringLiteral("player_%1_type").arg(p),
+ ->value(QStringLiteral("%1type").arg(player_prefix),
static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
+ player.vibration_enabled =
+ qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
+ .toBool();
+
+ player.vibration_strength =
+ qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
+ .toInt();
+
player.body_color_left = qt_config
- ->value(QStringLiteral("player_%1_body_color_left").arg(p),
+ ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
Settings::JOYCON_BODY_NEON_BLUE)
.toUInt();
- player.body_color_right = qt_config
- ->value(QStringLiteral("player_%1_body_color_right").arg(p),
- Settings::JOYCON_BODY_NEON_RED)
- .toUInt();
- player.button_color_left = qt_config
- ->value(QStringLiteral("player_%1_button_color_left").arg(p),
- Settings::JOYCON_BUTTONS_NEON_BLUE)
- .toUInt();
+ player.body_color_right =
+ qt_config
+ ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
+ Settings::JOYCON_BODY_NEON_RED)
+ .toUInt();
+ player.button_color_left =
+ qt_config
+ ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
+ Settings::JOYCON_BUTTONS_NEON_BLUE)
+ .toUInt();
player.button_color_right =
qt_config
- ->value(QStringLiteral("player_%1_button_color_right").arg(p),
+ ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
Settings::JOYCON_BUTTONS_NEON_RED)
.toUInt();
+ }
- for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_buttons[i]);
- auto& player_buttons = player.buttons[i];
-
- player_buttons = qt_config
- ->value(QStringLiteral("player_%1_").arg(p) +
- QString::fromUtf8(Settings::NativeButton::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_buttons.empty()) {
- player_buttons = default_param;
- }
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ auto& player_buttons = player.buttons[i];
+
+ player_buttons = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_buttons.empty()) {
+ player_buttons = default_param;
}
+ }
- for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_motions[i]);
- auto& player_motions = player.motions[i];
-
- player_motions = qt_config
- ->value(QStringLiteral("player_%1_").arg(p) +
- QString::fromUtf8(Settings::NativeMotion::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_motions.empty()) {
- player_motions = default_param;
- }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
+ auto& player_analogs = player.analogs[i];
+
+ player_analogs = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_analogs.empty()) {
+ player_analogs = default_param;
}
+ }
- for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
- default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_stick_mod[i], 0.5f);
- auto& player_analogs = player.analogs[i];
-
- player_analogs = qt_config
- ->value(QStringLiteral("player_%1_").arg(p) +
- QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_analogs.empty()) {
- player_analogs = default_param;
- }
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ auto& player_vibrations = player.vibrations[i];
+
+ player_vibrations =
+ qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeVibration::mapping[i]),
+ QString{})
+ .toString()
+ .toStdString();
+ }
+
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ auto& player_motions = player.motions[i];
+
+ player_motions = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_motions.empty()) {
+ player_motions = default_param;
}
}
}
@@ -436,18 +501,21 @@ void Config::ReadAudioValues() {
void Config::ReadControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
- ReadPlayerValues();
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ ReadPlayerValue(p);
+ }
ReadDebugValues();
ReadKeyboardValues();
ReadMouseValues();
ReadTouchscreenValues();
ReadMotionTouchValues();
- Settings::values.vibration_enabled =
- ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
- Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
- Settings::values.use_docked_mode =
- ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
+ ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false);
+ ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
+ true);
+ ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
+ QStringLiteral("enable_accurate_vibrations"), false);
+ ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true);
qt_config->endGroup();
}
@@ -920,49 +988,64 @@ void Config::ReadValues() {
ReadSystemValues();
}
-void Config::SavePlayerValues() {
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
- const auto& player = Settings::values.players[p];
+void Config::SavePlayerValue(std::size_t player_index) {
+ const QString player_prefix = [this, player_index] {
+ if (type == ConfigType::InputProfile) {
+ return QString{};
+ } else {
+ return QStringLiteral("player_%1_").arg(player_index);
+ }
+ }();
- WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
- WriteSetting(QStringLiteral("player_%1_type").arg(p),
- static_cast<u8>(player.controller_type),
- static_cast<u8>(Settings::ControllerType::ProController));
+ const auto& player = Settings::values.players.GetValue()[player_index];
- WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
+ WriteSetting(QStringLiteral("%1type").arg(player_prefix),
+ static_cast<u8>(player.controller_type),
+ static_cast<u8>(Settings::ControllerType::ProController));
+
+ if (!player_prefix.isEmpty()) {
+ WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
+ WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
+ player.vibration_enabled, true);
+ WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
+ player.vibration_strength, 100);
+ WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
- WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right,
- Settings::JOYCON_BODY_NEON_RED);
- WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left,
- Settings::JOYCON_BUTTONS_NEON_BLUE);
- WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p),
+ WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
+ player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
+ WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
+ player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
+ WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
+ }
- for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(QStringLiteral("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::NativeMotion::NumMotions; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_motions[i]);
- WriteSetting(QStringLiteral("player_%1_").arg(p) +
- QString::fromStdString(Settings::NativeMotion::mapping[i]),
- QString::fromStdString(player.motions[i]),
- QString::fromStdString(default_param));
- }
- for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
- default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_stick_mod[i], 0.5f);
- WriteSetting(QStringLiteral("player_%1_").arg(p) +
- QString::fromStdString(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(player.analogs[i]),
- QString::fromStdString(default_param));
- }
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(player.buttons[i]),
+ QString::fromStdString(default_param));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(player.analogs[i]),
+ QString::fromStdString(default_param));
+ }
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeVibration::mapping[i]),
+ QString::fromStdString(player.vibrations[i]), QString{});
+ }
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(player.motions[i]),
+ QString::fromStdString(default_param));
}
}
@@ -1087,14 +1170,20 @@ void Config::SaveAudioValues() {
void Config::SaveControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
- SavePlayerValues();
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ SavePlayerValue(p);
+ }
SaveDebugValues();
SaveMouseValues();
SaveTouchscreenValues();
SaveMotionTouchValues();
- WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
- WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
+ WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
+ WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
+ true);
+ WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
+ Settings::values.enable_accurate_vibrations, false);
+ WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
@@ -1102,7 +1191,6 @@ void Config::SaveControlValues() {
QString::fromStdString(Settings::values.touch_device),
QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
- WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
qt_config->endGroup();
}
@@ -1515,3 +1603,19 @@ void Config::Save() {
Settings::Sanitize();
SaveValues();
}
+
+void Config::ReadControlPlayerValue(std::size_t player_index) {
+ qt_config->beginGroup(QStringLiteral("Controls"));
+ ReadPlayerValue(player_index);
+ qt_config->endGroup();
+}
+
+void Config::SaveControlPlayerValue(std::size_t player_index) {
+ qt_config->beginGroup(QStringLiteral("Controls"));
+ SavePlayerValue(player_index);
+ qt_config->endGroup();
+}
+
+const std::string& Config::GetConfigFilePath() const {
+ return qt_config_loc;
+}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 5d8e45d78..8a600e19d 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -16,12 +16,24 @@ class QSettings;
class Config {
public:
- explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
+ enum class ConfigType {
+ GlobalConfig,
+ PerGameConfig,
+ InputProfile,
+ };
+
+ explicit Config(const std::string& config_name = "qt-config",
+ ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Reload();
void Save();
+ void ReadControlPlayerValue(std::size_t player_index);
+ void SaveControlPlayerValue(std::size_t player_index);
+
+ const std::string& GetConfigFilePath() const;
+
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
@@ -33,8 +45,10 @@ public:
static const std::array<UISettings::Shortcut, 16> default_hotkeys;
private:
+ void Initialize(const std::string& config_name);
+
void ReadValues();
- void ReadPlayerValues();
+ void ReadPlayerValue(std::size_t player_index);
void ReadDebugValues();
void ReadKeyboardValues();
void ReadMouseValues();
@@ -62,7 +76,7 @@ private:
void ReadWebServiceValues();
void SaveValues();
- void SavePlayerValues();
+ void SavePlayerValue(std::size_t player_index);
void SaveDebugValues();
void SaveMouseValues();
void SaveTouchscreenValues();
@@ -111,9 +125,9 @@ private:
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
const QVariant& default_value);
+ ConfigType type;
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
-
bool global;
};
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fcf42cdcb..f92c3aff3 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -275,32 +275,12 @@
<signal>accepted()</signal>
<receiver>ConfigureDialog</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>220</x>
- <y>380</y>
- </hint>
- <hint type="destinationlabel">
- <x>220</x>
- <y>200</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureDialog</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>220</x>
- <y>380</y>
- </hint>
- <hint type="destinationlabel">
- <x>220</x>
- <y>200</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 0097c9a29..a878ef9c6 100644
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -4,11 +4,14 @@
#include "ui_configure_debug_controller.h"
#include "yuzu/configuration/configure_debug_controller.h"
+#include "yuzu/configuration/configure_input_player.h"
ConfigureDebugController::ConfigureDebugController(QWidget* parent,
- InputCommon::InputSubsystem* input_subsystem)
+ InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
- debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) {
+ debug_controller(
+ new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) {
ui->setupUi(this);
ui->controllerLayout->addWidget(debug_controller);
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
index 34dcf705f..b4f53fad5 100644
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -6,10 +6,13 @@
#include <memory>
#include <QDialog>
-#include "yuzu/configuration/configure_input_player.h"
class QPushButton;
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
namespace InputCommon {
class InputSubsystem;
}
@@ -22,8 +25,8 @@ class ConfigureDebugController : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDebugController(QWidget* parent,
- InputCommon::InputSubsystem* input_subsystem);
+ explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles);
~ConfigureDebugController() override;
void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
index a95ed50ff..7b7e6582c 100644
--- a/src/yuzu/configuration/configure_debug_controller.ui
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -66,32 +66,12 @@
<signal>accepted()</signal>
<receiver>ConfigureDebugController</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureDebugController</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 2725fcb2b..d9009091b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -23,6 +23,8 @@
#include "yuzu/configuration/configure_motion_touch.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
#include "yuzu/configuration/configure_touchscreen_advanced.h"
+#include "yuzu/configuration/configure_vibration.h"
+#include "yuzu/configuration/input_profiles.h"
namespace {
template <typename Dialog, typename... Args>
@@ -64,7 +66,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
}
ConfigureInput::ConfigureInput(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
+ profiles(std::make_unique<InputProfiles>()) {
ui->setupUi(this);
}
@@ -73,14 +76,22 @@ ConfigureInput::~ConfigureInput() = default;
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
std::size_t max_players) {
player_controllers = {
- new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem),
- new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem),
+ new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
};
player_tabs = {
@@ -113,8 +124,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
}
}
});
- connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices,
- [this] { UpdateAllInputDevices(); });
+ connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
+ &ConfigureInput::UpdateAllInputDevices);
+ connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this,
+ &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection);
connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
player_controllers[i]->ConnectPlayer(state == Qt::Checked);
});
@@ -134,7 +147,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
ui->tabAdvanced->layout()->addWidget(advanced);
connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
- CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem);
+ CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get());
});
connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
@@ -146,6 +159,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
+ connect(ui->vibrationButton, &QPushButton::clicked,
+ [this] { CallConfigureDialog<ConfigureVibration>(*this); });
+
connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
});
@@ -171,12 +187,12 @@ void ConfigureInput::ApplyConfiguration() {
advanced->ApplyConfiguration();
- const bool pre_docked_mode = Settings::values.use_docked_mode;
- Settings::values.use_docked_mode = ui->radioDocked->isChecked();
- OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
+ const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
+ Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
+ OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
- Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
- Settings::values.motion_enabled = ui->motionGroup->isChecked();
+ Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
+ Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}
void ConfigureInput::changeEvent(QEvent* event) {
@@ -193,16 +209,16 @@ void ConfigureInput::RetranslateUI() {
void ConfigureInput::LoadConfiguration() {
LoadPlayerControllerIndices();
- UpdateDockedState(Settings::values.players[8].connected);
+ UpdateDockedState(Settings::values.players.GetValue()[8].connected);
- ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
- ui->motionGroup->setChecked(Settings::values.motion_enabled);
+ ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
+ ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}
void ConfigureInput::LoadPlayerControllerIndices() {
for (std::size_t i = 0; i < player_connected.size(); ++i) {
- const auto connected = Settings::values.players[i].connected ||
- (i == 0 && Settings::values.players[8].connected);
+ const auto connected = Settings::values.players.GetValue()[i].connected ||
+ (i == 0 && Settings::values.players.GetValue()[8].connected);
player_connected[i]->setChecked(connected);
}
}
@@ -231,8 +247,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
ui->radioDocked->setEnabled(!is_handheld);
ui->radioUndocked->setEnabled(!is_handheld);
- ui->radioDocked->setChecked(Settings::values.use_docked_mode);
- ui->radioUndocked->setChecked(!Settings::values.use_docked_mode);
+ ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
+ ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
// Also force into undocked mode if the controller type is handheld.
if (is_handheld) {
@@ -242,6 +258,16 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
void ConfigureInput::UpdateAllInputDevices() {
for (const auto& player : player_controllers) {
- player->UpdateInputDevices();
+ player->UpdateInputDeviceCombobox();
+ }
+}
+
+void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) {
+ for (std::size_t i = 0; i < player_controllers.size(); ++i) {
+ if (i == player_index) {
+ continue;
+ }
+
+ player_controllers[i]->UpdateInputProfiles();
}
}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 0e8b2fd4e..f4eb0d78b 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -8,17 +8,18 @@
#include <memory>
#include <QKeyEvent>
+#include <QList>
#include <QWidget>
-#include "yuzu/configuration/configure_input_advanced.h"
-#include "yuzu/configuration/configure_input_player.h"
-
-#include "ui_configure_input.h"
-
class QCheckBox;
class QString;
class QTimer;
+class ConfigureInputAdvanced;
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
namespace InputCommon {
class InputSubsystem;
}
@@ -51,6 +52,7 @@ private:
void UpdateDockedState(bool is_handheld);
void UpdateAllInputDevices();
+ void UpdateAllInputProfiles(std::size_t player_index);
/// Load configuration settings.
void LoadConfiguration();
@@ -61,6 +63,8 @@ private:
std::unique_ptr<Ui::ConfigureInput> ui;
+ std::unique_ptr<InputProfiles> profiles;
+
std::array<ConfigureInputPlayer*, 8> player_controllers;
std::array<QWidget*, 8> player_tabs;
std::array<QCheckBox*, 8> player_connected;
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 136955224..2707025e7 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>700</width>
+ <width>680</width>
<height>540</height>
</rect>
</property>
@@ -142,7 +142,7 @@
<number>6</number>
</property>
<property name="leftMargin">
- <number>3</number>
+ <number>8</number>
</property>
<property name="topMargin">
<number>6</number>
@@ -195,30 +195,24 @@
<number>3</number>
</property>
<item>
- <widget class="QSpinBox" name="vibrationSpin">
+ <widget class="QPushButton" name="vibrationButton">
<property name="minimumSize">
<size>
- <width>65</width>
- <height>21</height>
+ <width>68</width>
+ <height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>65</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
- <property name="suffix">
- <string>%</string>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>200</number>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
</property>
- <property name="value">
- <number>100</number>
+ <property name="text">
+ <string>Configure</string>
</property>
</widget>
</item>
@@ -250,18 +244,18 @@
<widget class="QPushButton" name="motionButton">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Configure</string>
@@ -272,7 +266,7 @@
</widget>
</item>
<item alignment="Qt::AlignVCenter">
- <widget class="QWidget" name="widget" native="true">
+ <widget class="QWidget" name="connectedControllers" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>5</number>
@@ -468,13 +462,13 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -494,7 +488,7 @@
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Defaults</string>
@@ -511,13 +505,13 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -537,7 +531,7 @@
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Clear</string>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 81f9dc16c..abaf03630 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -68,8 +68,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
connect(color_buttons[button_idx], &QPushButton::clicked, this,
[this, player_idx, button_idx] {
- OnControllerButtonClick(static_cast<int>(player_idx),
- static_cast<int>(button_idx));
+ OnControllerButtonClick(player_idx, button_idx);
});
}
}
@@ -94,20 +93,21 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
-void ConfigureInputAdvanced::OnControllerButtonClick(int player_idx, int button_idx) {
+void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx,
+ std::size_t button_idx) {
const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
if (!new_bg_color.isValid()) {
return;
}
controllers_colors[player_idx][button_idx] = new_bg_color;
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
- QStringLiteral("background-color: %1; min-width: 55px;")
+ QStringLiteral("background-color: %1; min-width: 60px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
void ConfigureInputAdvanced::ApplyConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
- auto& player = Settings::values.players[player_idx];
+ auto& player = Settings::values.players.GetValue()[player_idx];
std::array<u32, 4> colors{};
std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
colors.begin(), [](QColor color) { return color.rgb(); });
@@ -126,7 +126,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
void ConfigureInputAdvanced::LoadConfiguration() {
for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
- auto& player = Settings::values.players[player_idx];
+ auto& player = Settings::values.players.GetValue()[player_idx];
std::array<u32, 4> colors = {
player.body_color_left,
player.button_color_left,
@@ -139,7 +139,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
- QStringLiteral("background-color: %1; min-width: 55px;")
+ QStringLiteral("background-color: %1; min-width: 60px;")
.arg(controllers_colors[player_idx][button_idx].name()));
}
}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 50bb87768..3083d55c1 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -35,7 +35,7 @@ private:
void RetranslateUI();
void UpdateUIEnabled();
- void OnControllerButtonClick(int player_idx, int button_idx);
+ void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx);
void LoadConfiguration();
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 5958435fc..a880a7c68 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -192,18 +192,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -247,18 +247,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -323,18 +323,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -378,18 +378,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -478,18 +478,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -533,18 +533,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -609,18 +609,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -664,18 +664,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -782,18 +782,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -837,18 +837,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -913,18 +913,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -968,18 +968,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1068,18 +1068,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1123,18 +1123,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1199,18 +1199,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1254,18 +1254,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1393,18 +1393,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1448,18 +1448,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1524,18 +1524,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1579,18 +1579,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1679,18 +1679,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1734,18 +1734,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1810,18 +1810,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1865,18 +1865,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -1983,18 +1983,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2038,18 +2038,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2114,18 +2114,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2169,18 +2169,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2269,18 +2269,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2324,18 +2324,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2400,18 +2400,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
@@ -2455,18 +2455,18 @@
</property>
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string/>
diff --git a/src/yuzu/configuration/configure_input_dialog.cpp b/src/yuzu/configuration/configure_input_dialog.cpp
deleted file mode 100644
index 1866003c2..000000000
--- a/src/yuzu/configuration/configure_input_dialog.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "ui_configure_input_dialog.h"
-#include "yuzu/configuration/configure_input_dialog.h"
-
-ConfigureInputDialog::ConfigureInputDialog(QWidget* parent, std::size_t max_players,
- InputCommon::InputSubsystem* input_subsystem)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputDialog>()),
- input_widget(new ConfigureInput(this)) {
- ui->setupUi(this);
-
- input_widget->Initialize(input_subsystem, max_players);
-
- ui->inputLayout->addWidget(input_widget);
-
- RetranslateUI();
-}
-
-ConfigureInputDialog::~ConfigureInputDialog() = default;
-
-void ConfigureInputDialog::ApplyConfiguration() {
- input_widget->ApplyConfiguration();
-}
-
-void ConfigureInputDialog::changeEvent(QEvent* event) {
- if (event->type() == QEvent::LanguageChange) {
- RetranslateUI();
- }
-
- QDialog::changeEvent(event);
-}
-
-void ConfigureInputDialog::RetranslateUI() {
- ui->retranslateUi(this);
-}
diff --git a/src/yuzu/configuration/configure_input_dialog.h b/src/yuzu/configuration/configure_input_dialog.h
deleted file mode 100644
index d1bd865f9..000000000
--- a/src/yuzu/configuration/configure_input_dialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <QDialog>
-#include "yuzu/configuration/configure_input.h"
-
-class QPushButton;
-
-namespace InputCommon {
-class InputSubsystem;
-}
-
-namespace Ui {
-class ConfigureInputDialog;
-}
-
-class ConfigureInputDialog : public QDialog {
- Q_OBJECT
-
-public:
- explicit ConfigureInputDialog(QWidget* parent, std::size_t max_players,
- InputCommon::InputSubsystem* input_subsystem);
- ~ConfigureInputDialog() override;
-
- void ApplyConfiguration();
-
-private:
- void changeEvent(QEvent* event) override;
- void RetranslateUI();
-
- std::unique_ptr<Ui::ConfigureInputDialog> ui;
-
- ConfigureInput* input_widget;
-};
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index f58ca29d7..72640f5e7 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -4,6 +4,7 @@
#include <algorithm>
#include <memory>
+#include <thread>
#include <utility>
#include <QGridLayout>
#include <QInputDialog>
@@ -22,8 +23,9 @@
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
-
-constexpr std::size_t HANDHELD_INDEX = 8;
+#include "yuzu/configuration/configure_vibration.h"
+#include "yuzu/configuration/input_profiles.h"
+#include "yuzu/util/limitable_input_dialog.h"
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
ConfigureInputPlayer::analog_sub_buttons{{
@@ -35,6 +37,8 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
namespace {
+constexpr std::size_t HANDHELD_INDEX = 8;
+
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
bool connected) {
Core::System& system{Core::System::GetInstance()};
@@ -240,10 +244,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
QWidget* bottom_row,
InputCommon::InputSubsystem* input_subsystem_,
- bool debug)
+ InputProfiles* profiles_, bool debug)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
- debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()),
- poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) {
+ debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
+ timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
+ bottom_row(bottom_row) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -366,6 +371,18 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
connect(analog_button, &QPushButton::clicked, [=, this] {
+ if (!map_analog_stick_accepted) {
+ map_analog_stick_accepted =
+ QMessageBox::information(
+ this, tr("Map Analog Stick"),
+ tr("After pressing OK, first move your joystick horizontally, and then "
+ "vertically.\nTo invert the axes, first move your joystick "
+ "vertically, and then horizontally."),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
+ if (!map_analog_stick_accepted) {
+ return;
+ }
+ }
HandleClick(
analog_map_buttons[analog_id][sub_button_id],
[=, this](const Common::ParamPackage& params) {
@@ -455,11 +472,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
});
}
+ if (debug || player_index == 9) {
+ ui->groupConnectedController->setCheckable(false);
+ }
+
// The Debug Controller can only choose the Pro Controller.
if (debug) {
ui->buttonScreenshot->setEnabled(false);
ui->buttonHome->setEnabled(false);
- ui->groupConnectedController->setCheckable(false);
QStringList debug_controller_types = {
tr("Pro Controller"),
};
@@ -477,11 +497,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
UpdateMotionButtons();
});
- connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
+ connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
&ConfigureInputPlayer::UpdateMappingWithDefaults);
+ ui->comboDevices->setCurrentIndex(-1);
+
ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
- UpdateInputDevices();
connect(ui->buttonRefreshDevices, &QPushButton::clicked,
[this] { emit RefreshInputDevices(); });
@@ -492,14 +513,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
Common::ParamPackage params;
if (input_subsystem->GetGCButtons()->IsPolling()) {
params = input_subsystem->GetGCButtons()->GetNextInput();
- if (params.Has("engine")) {
+ if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
if (input_subsystem->GetGCAnalogs()->IsPolling()) {
params = input_subsystem->GetGCAnalogs()->GetNextInput();
- if (params.Has("engine")) {
+ if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
@@ -513,13 +534,24 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
- if (params.Has("engine")) {
+ if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
});
+ UpdateInputProfiles();
+
+ connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::CreateProfile);
+ connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::DeleteProfile);
+ connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
+ &ConfigureInputPlayer::LoadProfile);
+ connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::SaveProfile);
+
LoadConfiguration();
// TODO(wwylele): enable this when we actually emulate it
@@ -529,7 +561,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
void ConfigureInputPlayer::ApplyConfiguration() {
- auto& player = Settings::values.players[player_index];
+ auto& player = Settings::values.players.GetValue()[player_index];
auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
@@ -543,33 +575,58 @@ void ConfigureInputPlayer::ApplyConfiguration() {
}
auto& motions = player.motions;
+
std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
- player.controller_type =
- static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
- player.connected = ui->groupConnectedController->isChecked();
+ const auto controller_type =
+ GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+ const auto player_connected = ui->groupConnectedController->isChecked() &&
+ controller_type != Settings::ControllerType::Handheld;
- // Player 2-8
- if (player_index != 0) {
- UpdateController(player.controller_type, player_index, player.connected);
+ if (player.controller_type == controller_type && player.connected == player_connected) {
+ // Set vibration devices in the event that the input device has changed.
+ ConfigureVibration::SetVibrationDevices(player_index);
return;
}
- // Player 1 and Handheld
- auto& handheld = Settings::values.players[HANDHELD_INDEX];
- // If Handheld is selected, copy all the settings from Player 1 to Handheld.
- if (player.controller_type == Settings::ControllerType::Handheld) {
- handheld = player;
- handheld.connected = ui->groupConnectedController->isChecked();
- player.connected = false; // Disconnect Player 1
- } else {
- player.connected = ui->groupConnectedController->isChecked();
- handheld.connected = false; // Disconnect Handheld
+ // Disconnect the controller first.
+ UpdateController(controller_type, player_index, false);
+
+ player.controller_type = controller_type;
+ player.connected = player_connected;
+
+ ConfigureVibration::SetVibrationDevices(player_index);
+
+ // Handheld
+ if (player_index == 0) {
+ auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+ if (controller_type == Settings::ControllerType::Handheld) {
+ handheld = player;
+ }
+ handheld.connected = ui->groupConnectedController->isChecked() &&
+ controller_type == Settings::ControllerType::Handheld;
+ UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
+ }
+
+ if (!player.connected) {
+ return;
}
- UpdateController(player.controller_type, player_index, player.connected);
- UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
+ // This emulates a delay between disconnecting and reconnecting controllers as some games
+ // do not respond to a change in controller type if it was instantaneous.
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(20ms);
+
+ UpdateController(controller_type, player_index, player_connected);
+}
+
+void ConfigureInputPlayer::showEvent(QShowEvent* event) {
+ if (bottom_row == nullptr) {
+ return;
+ }
+ QWidget::showEvent(event);
+ ui->main->addWidget(bottom_row);
}
void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -586,7 +643,7 @@ void ConfigureInputPlayer::RetranslateUI() {
}
void ConfigureInputPlayer::LoadConfiguration() {
- auto& player = Settings::values.players[player_index];
+ auto& player = Settings::values.players.GetValue()[player_index];
if (debug) {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -604,6 +661,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
}
UpdateUI();
+ UpdateInputDeviceCombobox();
if (debug) {
return;
@@ -612,44 +670,75 @@ void ConfigureInputPlayer::LoadConfiguration() {
ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
ui->groupConnectedController->setChecked(
player.connected ||
- (player_index == 0 && Settings::values.players[HANDHELD_INDEX].connected));
+ (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
}
-void ConfigureInputPlayer::UpdateInputDevices() {
- input_devices = input_subsystem->GetInputDevices();
- ui->comboDevices->clear();
- for (auto device : input_devices) {
- ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
- }
+void ConfigureInputPlayer::ConnectPlayer(bool connected) {
+ ui->groupConnectedController->setChecked(connected);
}
-void ConfigureInputPlayer::RestoreDefaults() {
- // Reset Buttons
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
+ // Skip input device persistence if "Input Devices" is set to "Any".
+ if (ui->comboDevices->currentIndex() == 0) {
+ UpdateInputDevices();
+ return;
}
- // Reset Analogs and Modifier Buttons
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
- }
+ // Find the first button that isn't empty.
+ const auto button_param =
+ std::find_if(buttons_param.begin(), buttons_param.end(),
+ [](const Common::ParamPackage param) { return param.Has("engine"); });
+ const bool buttons_empty = button_param == buttons_param.end();
- analogs_param[analog_id].Set(
- "modifier", InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id]));
+ const auto current_engine = button_param->Get("engine", "");
+ const auto current_guid = button_param->Get("guid", "");
+ const auto current_port = button_param->Get("port", "");
+
+ const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
+
+ UpdateInputDevices();
+
+ if (buttons_empty) {
+ return;
}
- for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
- motions_param[motion_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
+ const bool all_one_device =
+ std::all_of(buttons_param.begin(), buttons_param.end(),
+ [current_engine, current_guid, current_port,
+ is_keyboard_mouse](const Common::ParamPackage param) {
+ if (is_keyboard_mouse) {
+ return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
+ param.Get("engine", "") == "mouse";
+ }
+ return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
+ param.Get("guid", "") == current_guid &&
+ param.Get("port", "") == current_port);
+ });
+
+ if (all_one_device) {
+ if (is_keyboard_mouse) {
+ ui->comboDevices->setCurrentIndex(1);
+ return;
+ }
+ const auto devices_it = std::find_if(
+ input_devices.begin(), input_devices.end(),
+ [current_engine, current_guid, current_port](const Common::ParamPackage param) {
+ return param.Get("class", "") == current_engine &&
+ param.Get("guid", "") == current_guid &&
+ param.Get("port", "") == current_port;
+ });
+ const int device_index =
+ devices_it != input_devices.end()
+ ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
+ : 0;
+ ui->comboDevices->setCurrentIndex(device_index);
+ } else {
+ ui->comboDevices->setCurrentIndex(0);
}
+}
- UpdateUI();
- UpdateInputDevices();
- ui->comboControllerType->setCurrentIndex(0);
+void ConfigureInputPlayer::RestoreDefaults() {
+ UpdateMappingWithDefaults();
}
void ConfigureInputPlayer::ClearAll() {
@@ -752,10 +841,167 @@ void ConfigureInputPlayer::UpdateUI() {
}
}
+void ConfigureInputPlayer::UpdateInputDevices() {
+ input_devices = input_subsystem->GetInputDevices();
+ ui->comboDevices->clear();
+ for (auto device : input_devices) {
+ ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
+ }
+}
+
+void ConfigureInputPlayer::UpdateControllerIcon() {
+ // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
+ // "nonstandard" to use an image through the icon support)
+ const QString stylesheet = [this] {
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ return QStringLiteral("image: url(:/controller/pro_controller%0)");
+ case Settings::ControllerType::DualJoyconDetached:
+ return QStringLiteral("image: url(:/controller/dual_joycon%0)");
+ case Settings::ControllerType::LeftJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
+ case Settings::ControllerType::RightJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
+ case Settings::ControllerType::Handheld:
+ return QStringLiteral("image: url(:/controller/handheld%0)");
+ default:
+ return QString{};
+ }
+ }();
+
+ const QString theme = [] {
+ if (QIcon::themeName().contains(QStringLiteral("dark"))) {
+ return QStringLiteral("_dark");
+ } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
+ return QStringLiteral("_midnight");
+ } else {
+ return QString{};
+ }
+ }();
+
+ ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
+}
+
+void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
+ auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+ if (debug) {
+ layout = Settings::ControllerType::ProController;
+ }
+
+ // List of all the widgets that will be hidden by any of the following layouts that need
+ // "unhidden" after the controller type changes
+ const std::array<QWidget*, 9> layout_show = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+
+ for (auto* widget : layout_show) {
+ widget->show();
+ }
+
+ std::vector<QWidget*> layout_hidden;
+ switch (layout) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::DualJoyconDetached:
+ case Settings::ControllerType::Handheld:
+ layout_hidden = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ };
+ break;
+ case Settings::ControllerType::LeftJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+ break;
+ case Settings::ControllerType::RightJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ };
+ break;
+ }
+
+ for (auto* widget : layout_hidden) {
+ widget->hide();
+ }
+}
+
+void ConfigureInputPlayer::UpdateMotionButtons() {
+ if (debug) {
+ // Motion isn't used with the debug controller, hide both groupboxes.
+ ui->buttonMotionLeftGroup->hide();
+ ui->buttonMotionRightGroup->hide();
+ return;
+ }
+
+ // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::LeftJoycon:
+ case Settings::ControllerType::Handheld:
+ // Show "Motion 1" and hide "Motion 2".
+ ui->buttonMotionLeftGroup->show();
+ ui->buttonMotionRightGroup->hide();
+ break;
+ case Settings::ControllerType::RightJoycon:
+ // Show "Motion 2" and hide "Motion 1".
+ ui->buttonMotionLeftGroup->hide();
+ ui->buttonMotionRightGroup->show();
+ break;
+ case Settings::ControllerType::DualJoyconDetached:
+ default:
+ // Show both "Motion 1/2".
+ ui->buttonMotionLeftGroup->show();
+ ui->buttonMotionRightGroup->show();
+ break;
+ }
+}
+
void ConfigureInputPlayer::UpdateMappingWithDefaults() {
- if (ui->comboDevices->currentIndex() < 2) {
+ if (ui->comboDevices->currentIndex() == 0) {
return;
}
+
+ if (ui->comboDevices->currentIndex() == 1) {
+ // Reset keyboard bindings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
+ Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
+ Config::default_analogs[analog_id][sub_button_id])};
+ SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ }
+
+ analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam(
+ Config::default_stick_mod[analog_id]));
+ }
+
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+ motions_param[motion_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
+ }
+
+ UpdateUI();
+ return;
+ }
+
+ // Reset controller bindings
const auto& device = input_devices[ui->comboDevices->currentIndex()];
auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
@@ -828,9 +1074,27 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
}
UpdateUI();
+ UpdateInputDeviceCombobox();
+
input_setter = std::nullopt;
}
+bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
+ if (ui->comboDevices->currentIndex() == 0) {
+ return true;
+ }
+
+ // Keyboard/Mouse
+ if (ui->comboDevices->currentIndex() == 1) {
+ return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
+ }
+
+ const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
+ return params.Get("engine", "") == current_input_device.Get("class", "") &&
+ params.Get("guid", "") == current_input_device.Get("guid", "") &&
+ params.Get("port", "") == current_input_device.Get("port", "");
+}
+
void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
if (!input_setter || !event) {
return;
@@ -865,135 +1129,101 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
SetPollingResult({}, true);
}
-void ConfigureInputPlayer::UpdateControllerIcon() {
- // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
- // "nonstandard" to use an image through the icon support)
- const QString stylesheet = [this] {
- switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
- case Settings::ControllerType::ProController:
- return QStringLiteral("image: url(:/controller/pro_controller%0)");
- case Settings::ControllerType::DualJoyconDetached:
- return QStringLiteral("image: url(:/controller/dual_joycon%0)");
- case Settings::ControllerType::LeftJoycon:
- return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
- case Settings::ControllerType::RightJoycon:
- return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
- case Settings::ControllerType::Handheld:
- return QStringLiteral("image: url(:/controller/handheld%0)");
- default:
- return QString{};
- }
- }();
-
- const QString theme = [this] {
- if (QIcon::themeName().contains(QStringLiteral("dark"))) {
- return QStringLiteral("_dark");
- } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
- return QStringLiteral("_midnight");
- } else {
- return QString{};
- }
- }();
+void ConfigureInputPlayer::CreateProfile() {
+ const auto profile_name =
+ LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
- ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
-}
+ if (profile_name.isEmpty()) {
+ return;
+ }
-void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
- auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
- if (debug) {
- layout = Settings::ControllerType::ProController;
+ if (!profiles->IsProfileNameValid(profile_name.toStdString())) {
+ QMessageBox::critical(this, tr("Create Input Profile"),
+ tr("The given profile name is not valid!"));
+ return;
}
- // List of all the widgets that will be hidden by any of the following layouts that need
- // "unhidden" after the controller type changes
- const std::array<QWidget*, 9> layout_show = {
- ui->buttonShoulderButtonsSLSR,
- ui->horizontalSpacerShoulderButtonsWidget,
- ui->horizontalSpacerShoulderButtonsWidget2,
- ui->buttonShoulderButtonsLeft,
- ui->buttonMiscButtonsMinusScreenshot,
- ui->bottomLeft,
- ui->buttonShoulderButtonsRight,
- ui->buttonMiscButtonsPlusHome,
- ui->bottomRight,
- };
+ ApplyConfiguration();
- for (auto* widget : layout_show) {
- widget->show();
+ if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Create Input Profile"),
+ tr("Failed to create the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
}
- std::vector<QWidget*> layout_hidden;
- switch (layout) {
- case Settings::ControllerType::ProController:
- case Settings::ControllerType::DualJoyconDetached:
- case Settings::ControllerType::Handheld:
- layout_hidden = {
- ui->buttonShoulderButtonsSLSR,
- ui->horizontalSpacerShoulderButtonsWidget2,
- };
- break;
- case Settings::ControllerType::LeftJoycon:
- layout_hidden = {
- ui->horizontalSpacerShoulderButtonsWidget2,
- ui->buttonShoulderButtonsRight,
- ui->buttonMiscButtonsPlusHome,
- ui->bottomRight,
- };
- break;
- case Settings::ControllerType::RightJoycon:
- layout_hidden = {
- ui->horizontalSpacerShoulderButtonsWidget,
- ui->buttonShoulderButtonsLeft,
- ui->buttonMiscButtonsMinusScreenshot,
- ui->bottomLeft,
- };
- break;
+ emit RefreshInputProfiles(player_index);
+
+ ui->comboProfiles->addItem(profile_name);
+ ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
+}
+
+void ConfigureInputPlayer::DeleteProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
+ return;
}
- for (auto* widget : layout_hidden) {
- widget->hide();
+ if (!profiles->DeleteProfile(profile_name.toStdString())) {
+ QMessageBox::critical(this, tr("Delete Input Profile"),
+ tr("Failed to delete the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
}
+
+ emit RefreshInputProfiles(player_index);
+
+ ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
+ ui->comboProfiles->setCurrentIndex(-1);
}
-void ConfigureInputPlayer::UpdateMotionButtons() {
- if (debug) {
- // Motion isn't used with the debug controller, hide both groupboxes.
- ui->buttonMotionLeftGroup->hide();
- ui->buttonMotionRightGroup->hide();
+void ConfigureInputPlayer::LoadProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
return;
}
- // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
- switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
- case Settings::ControllerType::ProController:
- case Settings::ControllerType::LeftJoycon:
- case Settings::ControllerType::Handheld:
- // Show "Motion 1" and hide "Motion 2".
- ui->buttonMotionLeftGroup->show();
- ui->buttonMotionRightGroup->hide();
- break;
- case Settings::ControllerType::RightJoycon:
- // Show "Motion 2" and hide "Motion 1".
- ui->buttonMotionLeftGroup->hide();
- ui->buttonMotionRightGroup->show();
- break;
- case Settings::ControllerType::DualJoyconDetached:
- default:
- // Show both "Motion 1/2".
- ui->buttonMotionLeftGroup->show();
- ui->buttonMotionRightGroup->show();
- break;
+ ApplyConfiguration();
+
+ if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Load Input Profile"),
+ tr("Failed to load the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
}
+
+ LoadConfiguration();
}
-void ConfigureInputPlayer::showEvent(QShowEvent* event) {
- if (bottom_row == nullptr) {
+void ConfigureInputPlayer::SaveProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
+ return;
+ }
+
+ ApplyConfiguration();
+
+ if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Save Input Profile"),
+ tr("Failed to save the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
return;
}
- QWidget::showEvent(event);
- ui->main->addWidget(bottom_row);
}
-void ConfigureInputPlayer::ConnectPlayer(bool connected) {
- ui->groupConnectedController->setChecked(connected);
+void ConfigureInputPlayer::UpdateInputProfiles() {
+ ui->comboProfiles->clear();
+
+ for (const auto& profile_name : profiles->GetInputProfileNames()) {
+ ui->comboProfiles->addItem(QString::fromStdString(profile_name));
+ }
+
+ ui->comboProfiles->setCurrentIndex(-1);
}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index c19aefffa..23cf6f958 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -26,6 +26,8 @@ class QString;
class QTimer;
class QWidget;
+class InputProfiles;
+
namespace InputCommon {
class InputSubsystem;
}
@@ -45,14 +47,20 @@ class ConfigureInputPlayer : public QWidget {
public:
explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
InputCommon::InputSubsystem* input_subsystem_,
- bool debug = false);
+ InputProfiles* profiles_, bool debug = false);
~ConfigureInputPlayer() override;
/// Save all button configurations to settings file.
void ApplyConfiguration();
+ /// Set the connection state checkbox (used to sync state).
+ void ConnectPlayer(bool connected);
+
/// Update the input devices combobox.
- void UpdateInputDevices();
+ void UpdateInputDeviceCombobox();
+
+ /// Updates the list of controller profiles.
+ void UpdateInputProfiles();
/// Restore all buttons to their default values.
void RestoreDefaults();
@@ -60,9 +68,6 @@ public:
/// Clear all input configuration.
void ClearAll();
- /// Set the connection state checkbox (used to sync state).
- void ConnectPlayer(bool connected);
-
signals:
/// Emitted when this controller is connected by the user.
void Connected(bool connected);
@@ -70,6 +75,12 @@ signals:
void HandheldStateChanged(bool is_handheld);
/// Emitted when the input devices combobox is being refreshed.
void RefreshInputDevices();
+ /**
+ * Emitted when the input profiles combobox is being refreshed.
+ * The player_index represents the current player's index, and the profile combobox
+ * will not be updated for this index as they are already updated by other mechanisms.
+ */
+ void RefreshInputProfiles(std::size_t player_index);
protected:
void showEvent(QShowEvent* event) override;
@@ -89,6 +100,9 @@ private:
/// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
+ /// Checks whether a given input can be accepted.
+ bool IsInputAcceptable(const Common::ParamPackage& params) const;
+
/// Handle mouse button press events.
void mousePressEvent(QMouseEvent* event) override;
@@ -98,8 +112,8 @@ private:
/// Update UI to reflect current configuration.
void UpdateUI();
- /// Update the controller selection combobox
- void UpdateControllerCombobox();
+ /// Update the available input devices.
+ void UpdateInputDevices();
/// Update the current controller icon.
void UpdateControllerIcon();
@@ -113,6 +127,18 @@ private:
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
+ /// Creates a controller profile.
+ void CreateProfile();
+
+ /// Deletes the selected controller profile.
+ void DeleteProfile();
+
+ /// Loads the selected controller profile.
+ void LoadProfile();
+
+ /// Saves the current controller configuration into a selected controller profile.
+ void SaveProfile();
+
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
std::size_t player_index;
@@ -120,6 +146,8 @@ private:
InputCommon::InputSubsystem* input_subsystem;
+ InputProfiles* profiles;
+
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
@@ -159,12 +187,15 @@ private:
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+ /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
+ bool map_analog_stick_accepted{};
+
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
- bool want_keyboard_mouse = false;
+ bool want_keyboard_mouse{};
/// List of physical devices users can map with. If a SDL backed device is selected, then you
- /// can usue this device to get a default mapping.
+ /// can use this device to get a default mapping.
std::vector<Common::ParamPackage> input_devices;
/// Bottom row is where console wide settings are held, and its "owned" by the parent
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index e03461d9d..1e78b4c10 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -83,6 +83,12 @@
</property>
<item>
<widget class="QComboBox" name="comboControllerType">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
+ </property>
<item>
<property name="text">
<string>Pro Controller</string>
@@ -136,6 +142,12 @@
</property>
<item>
<widget class="QComboBox" name="comboDevices">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
+ </property>
<item>
<property name="text">
<string>Any</string>
@@ -152,14 +164,14 @@
<widget class="QPushButton" name="buttonRefreshDevices">
<property name="minimumSize">
<size>
- <width>24</width>
- <height>22</height>
+ <width>21</width>
+ <height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>24</width>
- <height>22</height>
+ <width>21</width>
+ <height>21</height>
</size>
</property>
<property name="styleSheet">
@@ -198,18 +210,25 @@
<number>5</number>
</property>
<item>
- <widget class="QComboBox" name="comboProfiles"/>
+ <widget class="QComboBox" name="comboProfiles">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
+ </property>
+ </widget>
</item>
<item>
<widget class="QPushButton" name="buttonProfilesSave">
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Save</string>
@@ -220,12 +239,12 @@
<widget class="QPushButton" name="buttonProfilesNew">
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>New</string>
@@ -236,12 +255,12 @@
<widget class="QPushButton" name="buttonProfilesDelete">
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Delete</string>
@@ -393,18 +412,18 @@
<widget class="QPushButton" name="buttonLStickUp">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -463,18 +482,18 @@
<widget class="QPushButton" name="buttonLStickLeft">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -512,18 +531,18 @@
<widget class="QPushButton" name="buttonLStickRight">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -594,18 +613,18 @@
<widget class="QPushButton" name="buttonLStickDown">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -664,18 +683,18 @@
<widget class="QPushButton" name="buttonLStick">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Pressed</string>
@@ -713,18 +732,18 @@
<widget class="QPushButton" name="buttonLStickMod">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Modifier</string>
@@ -759,13 +778,13 @@
<widget class="QSpinBox" name="spinboxLStickRange">
<property name="minimumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -966,18 +985,18 @@
<widget class="QPushButton" name="buttonDpadUp">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -1036,18 +1055,18 @@
<widget class="QPushButton" name="buttonDpadLeft">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -1085,18 +1104,18 @@
<widget class="QPushButton" name="buttonDpadRight">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -1167,18 +1186,18 @@
<widget class="QPushButton" name="buttonDpadDown">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -1292,18 +1311,18 @@
<widget class="QPushButton" name="buttonL">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>L</string>
@@ -1341,18 +1360,18 @@
<widget class="QPushButton" name="buttonZL">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>ZL</string>
@@ -1445,18 +1464,18 @@
<widget class="QPushButton" name="buttonMinus">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Minus</string>
@@ -1494,18 +1513,18 @@
<widget class="QPushButton" name="buttonScreenshot">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Capture</string>
@@ -1564,18 +1583,18 @@
<widget class="QPushButton" name="buttonPlus">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Plus</string>
@@ -1613,18 +1632,18 @@
<widget class="QPushButton" name="buttonHome">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Home</string>
@@ -1717,18 +1736,18 @@
<widget class="QPushButton" name="buttonR">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>R</string>
@@ -1766,18 +1785,18 @@
<widget class="QPushButton" name="buttonZR">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>ZR</string>
@@ -1870,18 +1889,18 @@
<widget class="QPushButton" name="buttonSL">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>SL</string>
@@ -1919,18 +1938,18 @@
<widget class="QPushButton" name="buttonSR">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>SR</string>
@@ -2027,18 +2046,18 @@
<widget class="QPushButton" name="buttonMotionLeft">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -2076,18 +2095,18 @@
<widget class="QPushButton" name="buttonMotionRight">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -2225,18 +2244,18 @@
<widget class="QPushButton" name="buttonX">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>X</string>
@@ -2295,18 +2314,18 @@
<widget class="QPushButton" name="buttonY">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Y</string>
@@ -2344,18 +2363,18 @@
<widget class="QPushButton" name="buttonA">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>A</string>
@@ -2426,18 +2445,18 @@
<widget class="QPushButton" name="buttonB">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>B</string>
@@ -2580,18 +2599,18 @@
<widget class="QPushButton" name="buttonRStickUp">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Up</string>
@@ -2650,18 +2669,18 @@
<widget class="QPushButton" name="buttonRStickLeft">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Left</string>
@@ -2699,18 +2718,18 @@
<widget class="QPushButton" name="buttonRStickRight">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Right</string>
@@ -2781,18 +2800,18 @@
<widget class="QPushButton" name="buttonRStickDown">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Down</string>
@@ -2851,18 +2870,18 @@
<widget class="QPushButton" name="buttonRStick">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Pressed</string>
@@ -2900,18 +2919,18 @@
<widget class="QPushButton" name="buttonRStickMod">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
- <string notr="true">min-width: 55px;</string>
+ <string notr="true">min-width: 68px;</string>
</property>
<property name="text">
<string>Modifier</string>
@@ -2946,13 +2965,13 @@
<widget class="QSpinBox" name="spinboxRStickRange">
<property name="minimumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>21</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>55</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.cpp b/src/yuzu/configuration/configure_input_profile_dialog.cpp
new file mode 100644
index 000000000..1f5cfa75b
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_profile_dialog.cpp
@@ -0,0 +1,37 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "ui_configure_input_profile_dialog.h"
+#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_input_profile_dialog.h"
+
+ConfigureInputProfileDialog::ConfigureInputProfileDialog(
+ QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
+ profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) {
+ ui->setupUi(this);
+
+ ui->controllerLayout->addWidget(profile_widget);
+
+ connect(ui->clear_all_button, &QPushButton::clicked, this,
+ [this] { profile_widget->ClearAll(); });
+ connect(ui->restore_defaults_button, &QPushButton::clicked, this,
+ [this] { profile_widget->RestoreDefaults(); });
+
+ RetranslateUI();
+}
+
+ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default;
+
+void ConfigureInputProfileDialog::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureInputProfileDialog::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.h b/src/yuzu/configuration/configure_input_profile_dialog.h
new file mode 100644
index 000000000..e6386bdbb
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_profile_dialog.h
@@ -0,0 +1,40 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+
+class QPushButton;
+
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace Ui {
+class ConfigureInputProfileDialog;
+}
+
+class ConfigureInputProfileDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputProfileDialog(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles);
+ ~ConfigureInputProfileDialog() override;
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureInputProfileDialog> ui;
+
+ ConfigureInputPlayer* profile_widget;
+};
diff --git a/src/yuzu/configuration/configure_input_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui
index b92ddb200..726cf6905 100644
--- a/src/yuzu/configuration/configure_input_dialog.ui
+++ b/src/yuzu/configuration/configure_input_profile_dialog.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>ConfigureInputDialog</class>
- <widget class="QDialog" name="ConfigureInputDialog">
+ <class>ConfigureInputProfileDialog</class>
+ <widget class="QDialog" name="ConfigureInputProfileDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>Configure Input</string>
+ <string>Create Input Profile</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@@ -30,11 +30,25 @@
<number>9</number>
</property>
<item>
- <layout class="QHBoxLayout" name="inputLayout"/>
+ <layout class="QHBoxLayout" name="controllerLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
+ <widget class="QPushButton" name="clear_all_button">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
@@ -50,7 +64,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
- <receiver>ConfigureInputDialog</receiver>
+ <receiver>ConfigureInputProfileDialog</receiver>
<slot>accept()</slot>
</connection>
</connections>
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index 602cf8cd8..5b78c5a4b 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -312,16 +312,6 @@
<signal>accepted()</signal>
<receiver>ConfigureMotionTouch</receiver>
<slot>ApplyConfiguration()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>220</x>
- <y>380</y>
- </hint>
- <hint type="destinationlabel">
- <x>220</x>
- <y>200</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 74552fdbd..5b99e1c37 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -15,7 +15,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
- min-width: 55px;
+ min-width: 60px;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -42,13 +42,13 @@
<widget class="QPushButton" name="forward_button">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>16777215</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -82,7 +82,7 @@
<widget class="QPushButton" name="back_button">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
@@ -110,7 +110,7 @@
<widget class="QPushButton" name="left_button">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
@@ -138,13 +138,13 @@
<widget class="QPushButton" name="middle_button">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>16777215</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -204,13 +204,13 @@
<widget class="QPushButton" name="right_button">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>16777215</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -256,13 +256,13 @@
<widget class="QPushButton" name="buttonClearAll">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>16777215</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -275,13 +275,13 @@
<widget class="QPushButton" name="buttonRestoreDefaults">
<property name="minimumSize">
<size>
- <width>57</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>16777215</width>
+ <width>68</width>
<height>16777215</height>
</size>
</property>
@@ -324,32 +324,12 @@
<signal>accepted()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>124</x>
- <y>266</y>
- </hint>
- <hint type="destinationlabel">
- <x>124</x>
- <y>143</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>124</x>
- <y>266</y>
- </hint>
- <hint type="destinationlabel">
- <x>124</x>
- <y>143</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 002db3f93..81464dd37 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -29,7 +29,8 @@
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
- game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
+ game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id),
+ Config::ConfigType::PerGameConfig);
Settings::SetConfiguringGlobal(false);
diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui
index d2057c4ab..25975b3b9 100644
--- a/src/yuzu/configuration/configure_per_game.ui
+++ b/src/yuzu/configuration/configure_per_game.ui
@@ -319,32 +319,12 @@
<signal>accepted()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui
index f581e27e0..757219d54 100644
--- a/src/yuzu/configuration/configure_touch_from_button.ui
+++ b/src/yuzu/configuration/configure_touch_from_button.ui
@@ -216,16 +216,6 @@ Drag points to change position, or double-click table cells to edit values.</str
<signal>rejected()</signal>
<receiver>ConfigureTouchFromButton</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>249</x>
- <y>428</y>
- </hint>
- <hint type="destinationlabel">
- <x>249</x>
- <y>224</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui
index 1171c2dd1..30ceccddb 100644
--- a/src/yuzu/configuration/configure_touchscreen_advanced.ui
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui
@@ -168,32 +168,12 @@
<signal>accepted()</signal>
<receiver>ConfigureTouchscreenAdvanced</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureTouchscreenAdvanced</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
- </connection>
+ </connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
new file mode 100644
index 000000000..7dcb2c5b9
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -0,0 +1,146 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <fmt/format.h>
+
+#include "common/param_package.h"
+#include "core/settings.h"
+#include "ui_configure_vibration.h"
+#include "yuzu/configuration/configure_vibration.h"
+
+ConfigureVibration::ConfigureVibration(QWidget* parent)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) {
+ ui->setupUi(this);
+
+ vibration_groupboxes = {
+ ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3,
+ ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6,
+ ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8,
+ };
+
+ vibration_spinboxes = {
+ ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3,
+ ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6,
+ ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8,
+ };
+
+ const auto& players = Settings::values.players.GetValue();
+
+ for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
+ vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
+ vibration_spinboxes[i]->setValue(players[i].vibration_strength);
+ }
+
+ ui->checkBoxAccurateVibration->setChecked(
+ Settings::values.enable_accurate_vibrations.GetValue());
+
+ if (!Settings::IsConfiguringGlobal()) {
+ ui->checkBoxAccurateVibration->setDisabled(true);
+ }
+
+ RetranslateUI();
+}
+
+ConfigureVibration::~ConfigureVibration() = default;
+
+void ConfigureVibration::ApplyConfiguration() {
+ auto& players = Settings::values.players.GetValue();
+
+ for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
+ players[i].vibration_enabled = vibration_groupboxes[i]->isChecked();
+ players[i].vibration_strength = vibration_spinboxes[i]->value();
+ }
+
+ Settings::values.enable_accurate_vibrations.SetValue(
+ ui->checkBoxAccurateVibration->isChecked());
+}
+
+void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
+ using namespace Settings::NativeButton;
+ static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
+ {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
+ {A, B, X, Y, R, ZR}, // Right Buttons
+ }};
+
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
+ std::unordered_map<std::string, int> params_count;
+
+ for (const auto button_index : buttons[device_idx]) {
+ const auto& player_button = player.buttons[button_index];
+
+ if (params_count.find(player_button) != params_count.end()) {
+ ++params_count[player_button];
+ continue;
+ }
+
+ params_count.insert_or_assign(player_button, 1);
+ }
+
+ const auto it = std::max_element(
+ params_count.begin(), params_count.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
+
+ auto& vibration_param_str = player.vibrations[device_idx];
+ vibration_param_str.clear();
+
+ if (it->first.empty()) {
+ continue;
+ }
+
+ const auto param = Common::ParamPackage(it->first);
+
+ const auto engine = param.Get("engine", "");
+ const auto guid = param.Get("guid", "");
+ const auto port = param.Get("port", "");
+
+ if (engine.empty() || engine == "keyboard" || engine == "mouse") {
+ continue;
+ }
+
+ vibration_param_str += fmt::format("engine:{}", engine);
+
+ if (!port.empty()) {
+ vibration_param_str += fmt::format(",port:{}", port);
+ }
+ if (!guid.empty()) {
+ vibration_param_str += fmt::format(",guid:{}", guid);
+ }
+ }
+
+ if (player.vibrations[0] != player.vibrations[1]) {
+ return;
+ }
+
+ if (!player.vibrations[0].empty() &&
+ player.controller_type != Settings::ControllerType::RightJoycon) {
+ player.vibrations[1].clear();
+ } else if (!player.vibrations[1].empty() &&
+ player.controller_type == Settings::ControllerType::RightJoycon) {
+ player.vibrations[0].clear();
+ }
+}
+
+void ConfigureVibration::SetAllVibrationDevices() {
+ // Set vibration devices for all player indices including handheld
+ for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
+ SetVibrationDevices(player_idx);
+ }
+}
+
+void ConfigureVibration::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureVibration::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
new file mode 100644
index 000000000..07411a86f
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -0,0 +1,43 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <QDialog>
+
+class QGroupBox;
+class QSpinBox;
+
+namespace Ui {
+class ConfigureVibration;
+}
+
+class ConfigureVibration : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureVibration(QWidget* parent);
+ ~ConfigureVibration() override;
+
+ void ApplyConfiguration();
+
+ static void SetVibrationDevices(std::size_t player_index);
+ static void SetAllVibrationDevices();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureVibration> ui;
+
+ static constexpr std::size_t NUM_PLAYERS = 8;
+
+ // Groupboxes encapsulating the vibration strength spinbox.
+ std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
+
+ // Spinboxes representing the vibration strength percentage.
+ std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
+};
diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui
new file mode 100644
index 000000000..efdf317a9
--- /dev/null
+++ b/src/yuzu/configuration/configure_vibration.ui
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureVibration</class>
+ <widget class="QDialog" name="ConfigureVibration">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>242</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Vibration</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="vibrationStrengthGroup">
+ <property name="title">
+ <string>Vibration</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player14Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer1">
+ <property name="title">
+ <string>Player 1</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer1">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer2">
+ <property name="title">
+ <string>Player 2</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer2">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer3">
+ <property name="title">
+ <string>Player 3</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer3">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer4">
+ <property name="title">
+ <string>Player 4</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer4">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player58Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer7">
+ <property name="title">
+ <string>Player 5</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer7">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer8">
+ <property name="title">
+ <string>Player 6</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer8">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer5">
+ <property name="title">
+ <string>Player 7</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer5">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer6">
+ <property name="title">
+ <string>Player 8</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer6">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationSettingsGroup">
+ <property name="title">
+ <string>Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="checkBoxAccurateVibration">
+ <property name="text">
+ <string>Enable Accurate Vibration</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacerVibration">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>167</width>
+ <height>55</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBoxVibration">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBoxVibration</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureVibration</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonBoxVibration</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureVibration</receiver>
+ <slot>reject()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp
new file mode 100644
index 000000000..e87aededb
--- /dev/null
+++ b/src/yuzu/configuration/input_profiles.cpp
@@ -0,0 +1,131 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fmt/format.h>
+
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/input_profiles.h"
+
+namespace FS = Common::FS;
+
+namespace {
+
+bool ProfileExistsInFilesystem(std::string_view profile_name) {
+ return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), profile_name));
+}
+
+bool IsINI(std::string_view filename) {
+ const std::size_t index = filename.rfind('.');
+
+ if (index == std::string::npos) {
+ return false;
+ }
+
+ return filename.substr(index) == ".ini";
+}
+
+std::string GetNameWithoutExtension(const std::string& filename) {
+ const std::size_t index = filename.rfind('.');
+
+ if (index == std::string::npos) {
+ return filename;
+ }
+
+ return filename.substr(0, index);
+}
+
+} // namespace
+
+InputProfiles::InputProfiles() {
+ const std::string input_profile_loc =
+ fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir));
+
+ FS::ForeachDirectoryEntry(
+ nullptr, input_profile_loc,
+ [this](u64* entries_out, const std::string& directory, const std::string& filename) {
+ if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) {
+ map_profiles.insert_or_assign(
+ GetNameWithoutExtension(filename),
+ std::make_unique<Config>(GetNameWithoutExtension(filename),
+ Config::ConfigType::InputProfile));
+ }
+ return true;
+ });
+}
+
+InputProfiles::~InputProfiles() = default;
+
+std::vector<std::string> InputProfiles::GetInputProfileNames() {
+ std::vector<std::string> profile_names;
+ profile_names.reserve(map_profiles.size());
+
+ for (const auto& [profile_name, config] : map_profiles) {
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ DeleteProfile(profile_name);
+ continue;
+ }
+
+ profile_names.push_back(profile_name);
+ }
+
+ return profile_names;
+}
+
+bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
+ return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
+}
+
+bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
+ if (ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles.insert_or_assign(
+ profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
+
+ return SaveProfile(profile_name, player_index);
+}
+
+bool InputProfiles::DeleteProfile(const std::string& profile_name) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name) ||
+ FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) {
+ map_profiles.erase(profile_name);
+ }
+
+ return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
+}
+
+bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ map_profiles.erase(profile_name);
+ return false;
+ }
+
+ map_profiles[profile_name]->ReadControlPlayerValue(player_index);
+ return true;
+}
+
+bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles[profile_name]->SaveControlPlayerValue(player_index);
+ return true;
+}
+
+bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
+ return map_profiles.find(profile_name) != map_profiles.end();
+}
diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h
new file mode 100644
index 000000000..cb41fd9be
--- /dev/null
+++ b/src/yuzu/configuration/input_profiles.h
@@ -0,0 +1,32 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+class Config;
+
+class InputProfiles {
+
+public:
+ explicit InputProfiles();
+ virtual ~InputProfiles();
+
+ std::vector<std::string> GetInputProfileNames();
+
+ static bool IsProfileNameValid(std::string_view profile_name);
+
+ bool CreateProfile(const std::string& profile_name, std::size_t player_index);
+ bool DeleteProfile(const std::string& profile_name);
+ bool LoadProfile(const std::string& profile_name, std::size_t player_index);
+ bool SaveProfile(const std::string& profile_name, std::size_t player_index);
+
+private:
+ bool ProfileExistsInMap(const std::string& profile_name) const;
+
+ std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
+};