summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2020-09-23 09:52:25 -0400
committerGravatar Morph2020-11-15 23:33:19 -0500
commit57d89e291de0eacfd368784309a0cbf89d38dcc8 (patch)
treeeedaf34c8fda2dc47c07cf5e2fe528d78aa9adc0 /src
parentconfigure_input_player: Implement input exclusivity and persistence (diff)
downloadyuzu-57d89e291de0eacfd368784309a0cbf89d38dcc8.tar.gz
yuzu-57d89e291de0eacfd368784309a0cbf89d38dcc8.tar.xz
yuzu-57d89e291de0eacfd368784309a0cbf89d38dcc8.zip
input_profiles: Implement input profiles
Diffstat (limited to 'src')
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/configuration/config.cpp271
-rw-r--r--src/yuzu/configuration/config.h20
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp6
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h6
-rw-r--r--src/yuzu/configuration/configure_input.cpp30
-rw-r--r--src/yuzu/configuration/configure_input.h4
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp111
-rw-r--r--src/yuzu/configuration/configure_input_player.h21
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp3
-rw-r--r--src/yuzu/configuration/input_profiles.cpp131
-rw-r--r--src/yuzu/configuration/input_profiles.h32
-rw-r--r--src/yuzu/main.cpp2
13 files changed, 509 insertions, 130 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 8abb74d56..22fe0a2a6 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -108,6 +108,8 @@ add_executable(yuzu
108 configuration/configure_web.cpp 108 configuration/configure_web.cpp
109 configuration/configure_web.h 109 configuration/configure_web.h
110 configuration/configure_web.ui 110 configuration/configure_web.ui
111 configuration/input_profiles.cpp
112 configuration/input_profiles.h
111 debugger/console.cpp 113 debugger/console.cpp
112 debugger/console.h 114 debugger/console.h
113 debugger/profiler.cpp 115 debugger/profiler.cpp
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1ce62e4a6..5c8b02fbe 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
5#include <array> 5#include <array>
6#include <QKeySequence> 6#include <QKeySequence>
7#include <QSettings> 7#include <QSettings>
8#include "common/common_paths.h"
8#include "common/file_util.h" 9#include "common/file_util.h"
9#include "core/hle/service/acc/profile_manager.h" 10#include "core/hle/service/acc/profile_manager.h"
10#include "core/hle/service/hid/controllers/npad.h" 11#include "core/hle/service/hid/controllers/npad.h"
@@ -14,14 +15,27 @@
14 15
15namespace FS = Common::FS; 16namespace FS = Common::FS;
16 17
17Config::Config(const std::string& config_file, bool is_global) { 18Config::Config(const std::string& config_file, ConfigType config_type) : type(config_type) {
18 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 19 global = config_type == ConfigType::GlobalConfig;
19 qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file; 20
20 FS::CreateFullPath(qt_config_loc); 21 switch (config_type) {
21 qt_config = 22 case ConfigType::GlobalConfig:
22 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 23 case ConfigType::PerGameConfig:
23 global = is_global; 24 qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir),
24 Reload(); 25 config_file);
26 FS::CreateFullPath(qt_config_loc);
27 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
28 QSettings::IniFormat);
29 Reload();
30 break;
31 case ConfigType::InputProfile:
32 qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini",
33 FS::GetUserPath(FS::UserPath::ConfigDir), config_file);
34 FS::CreateFullPath(qt_config_loc);
35 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
36 QSettings::IniFormat);
37 break;
38 }
25} 39}
26 40
27Config::~Config() { 41Config::~Config() {
@@ -242,84 +256,103 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
242}}; 256}};
243// clang-format on 257// clang-format on
244 258
245void Config::ReadPlayerValues() { 259void Config::ReadPlayerValue(std::size_t player_index) {
246 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 260 const QString player_prefix = [this, player_index] {
247 auto& player = Settings::values.players[p]; 261 if (type == ConfigType::InputProfile) {
262 return QString{};
263 } else {
264 return QStringLiteral("player_%1_").arg(player_index);
265 }
266 }();
267
268 auto& player = Settings::values.players[player_index];
269
270 if (player_prefix.isEmpty()) {
271 const auto controller = static_cast<Settings::ControllerType>(
272 qt_config
273 ->value(QStringLiteral("%1type").arg(player_prefix),
274 static_cast<u8>(Settings::ControllerType::ProController))
275 .toUInt());
248 276
277 if (controller == Settings::ControllerType::LeftJoycon ||
278 controller == Settings::ControllerType::RightJoycon) {
279 player.controller_type = controller;
280 }
281 } else {
249 player.connected = 282 player.connected =
250 ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool(); 283 ReadSetting(QStringLiteral("%1connected").arg(player_prefix), false).toBool();
251 284
252 player.controller_type = static_cast<Settings::ControllerType>( 285 player.controller_type = static_cast<Settings::ControllerType>(
253 qt_config 286 qt_config
254 ->value(QStringLiteral("player_%1_type").arg(p), 287 ->value(QStringLiteral("%1type").arg(player_prefix),
255 static_cast<u8>(Settings::ControllerType::ProController)) 288 static_cast<u8>(Settings::ControllerType::ProController))
256 .toUInt()); 289 .toUInt());
257 290
258 player.body_color_left = qt_config 291 player.body_color_left = qt_config
259 ->value(QStringLiteral("player_%1_body_color_left").arg(p), 292 ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
260 Settings::JOYCON_BODY_NEON_BLUE) 293 Settings::JOYCON_BODY_NEON_BLUE)
261 .toUInt(); 294 .toUInt();
262 player.body_color_right = qt_config 295 player.body_color_right =
263 ->value(QStringLiteral("player_%1_body_color_right").arg(p), 296 qt_config
264 Settings::JOYCON_BODY_NEON_RED) 297 ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
265 .toUInt(); 298 Settings::JOYCON_BODY_NEON_RED)
266 player.button_color_left = qt_config 299 .toUInt();
267 ->value(QStringLiteral("player_%1_button_color_left").arg(p), 300 player.button_color_left =
268 Settings::JOYCON_BUTTONS_NEON_BLUE) 301 qt_config
269 .toUInt(); 302 ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
303 Settings::JOYCON_BUTTONS_NEON_BLUE)
304 .toUInt();
270 player.button_color_right = 305 player.button_color_right =
271 qt_config 306 qt_config
272 ->value(QStringLiteral("player_%1_button_color_right").arg(p), 307 ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
273 Settings::JOYCON_BUTTONS_NEON_RED) 308 Settings::JOYCON_BUTTONS_NEON_RED)
274 .toUInt(); 309 .toUInt();
310 }
275 311
276 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 312 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
277 const std::string default_param = 313 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
278 InputCommon::GenerateKeyboardParam(default_buttons[i]); 314 auto& player_buttons = player.buttons[i];
279 auto& player_buttons = player.buttons[i]; 315
280 316 player_buttons = qt_config
281 player_buttons = qt_config 317 ->value(QStringLiteral("%1").arg(player_prefix) +
282 ->value(QStringLiteral("player_%1_").arg(p) + 318 QString::fromUtf8(Settings::NativeButton::mapping[i]),
283 QString::fromUtf8(Settings::NativeButton::mapping[i]), 319 QString::fromStdString(default_param))
284 QString::fromStdString(default_param)) 320 .toString()
285 .toString() 321 .toStdString();
286 .toStdString(); 322 if (player_buttons.empty()) {
287 if (player_buttons.empty()) { 323 player_buttons = default_param;
288 player_buttons = default_param;
289 }
290 } 324 }
325 }
291 326
292 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 327 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
293 const std::string default_param = 328 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
294 InputCommon::GenerateKeyboardParam(default_motions[i]); 329 auto& player_motions = player.motions[i];
295 auto& player_motions = player.motions[i]; 330
296 331 player_motions = qt_config
297 player_motions = qt_config 332 ->value(QStringLiteral("%1").arg(player_prefix) +
298 ->value(QStringLiteral("player_%1_").arg(p) + 333 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
299 QString::fromUtf8(Settings::NativeMotion::mapping[i]), 334 QString::fromStdString(default_param))
300 QString::fromStdString(default_param)) 335 .toString()
301 .toString() 336 .toStdString();
302 .toStdString(); 337 if (player_motions.empty()) {
303 if (player_motions.empty()) { 338 player_motions = default_param;
304 player_motions = default_param;
305 }
306 } 339 }
340 }
307 341
308 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 342 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
309 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 343 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
310 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 344 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
311 default_analogs[i][3], default_stick_mod[i], 0.5f); 345 default_analogs[i][3], default_stick_mod[i], 0.5f);
312 auto& player_analogs = player.analogs[i]; 346 auto& player_analogs = player.analogs[i];
313 347
314 player_analogs = qt_config 348 player_analogs = qt_config
315 ->value(QStringLiteral("player_%1_").arg(p) + 349 ->value(QStringLiteral("%1").arg(player_prefix) +
316 QString::fromUtf8(Settings::NativeAnalog::mapping[i]), 350 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
317 QString::fromStdString(default_param)) 351 QString::fromStdString(default_param))
318 .toString() 352 .toString()
319 .toStdString(); 353 .toStdString();
320 if (player_analogs.empty()) { 354 if (player_analogs.empty()) {
321 player_analogs = default_param; 355 player_analogs = default_param;
322 }
323 } 356 }
324 } 357 }
325} 358}
@@ -436,7 +469,9 @@ void Config::ReadAudioValues() {
436void Config::ReadControlValues() { 469void Config::ReadControlValues() {
437 qt_config->beginGroup(QStringLiteral("Controls")); 470 qt_config->beginGroup(QStringLiteral("Controls"));
438 471
439 ReadPlayerValues(); 472 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
473 ReadPlayerValue(p);
474 }
440 ReadDebugValues(); 475 ReadDebugValues();
441 ReadKeyboardValues(); 476 ReadKeyboardValues();
442 ReadMouseValues(); 477 ReadMouseValues();
@@ -920,49 +955,55 @@ void Config::ReadValues() {
920 ReadSystemValues(); 955 ReadSystemValues();
921} 956}
922 957
923void Config::SavePlayerValues() { 958void Config::SavePlayerValue(std::size_t player_index) {
924 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { 959 const QString player_prefix = [this, player_index] {
925 const auto& player = Settings::values.players[p]; 960 if (type == ConfigType::InputProfile) {
961 return QString{};
962 } else {
963 return QStringLiteral("player_%1_").arg(player_index);
964 }
965 }();
966
967 const auto& player = Settings::values.players[player_index];
926 968
927 WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false); 969 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
928 WriteSetting(QStringLiteral("player_%1_type").arg(p), 970 static_cast<u8>(player.controller_type),
929 static_cast<u8>(player.controller_type), 971 static_cast<u8>(Settings::ControllerType::ProController));
930 static_cast<u8>(Settings::ControllerType::ProController));
931 972
932 WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left, 973 if (!player_prefix.isEmpty()) {
974 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
975 WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
933 Settings::JOYCON_BODY_NEON_BLUE); 976 Settings::JOYCON_BODY_NEON_BLUE);
934 WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right, 977 WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
935 Settings::JOYCON_BODY_NEON_RED); 978 player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
936 WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left, 979 WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
937 Settings::JOYCON_BUTTONS_NEON_BLUE); 980 player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
938 WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p), 981 WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
939 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); 982 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
983 }
940 984
941 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 985 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
942 const std::string default_param = 986 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
943 InputCommon::GenerateKeyboardParam(default_buttons[i]); 987 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
944 WriteSetting(QStringLiteral("player_%1_").arg(p) + 988 QString::fromStdString(Settings::NativeButton::mapping[i]),
945 QString::fromStdString(Settings::NativeButton::mapping[i]), 989 QString::fromStdString(player.buttons[i]),
946 QString::fromStdString(player.buttons[i]), 990 QString::fromStdString(default_param));
947 QString::fromStdString(default_param)); 991 }
948 } 992 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
949 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 993 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
950 const std::string default_param = 994 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
951 InputCommon::GenerateKeyboardParam(default_motions[i]); 995 QString::fromStdString(Settings::NativeMotion::mapping[i]),
952 WriteSetting(QStringLiteral("player_%1_").arg(p) + 996 QString::fromStdString(player.motions[i]),
953 QString::fromStdString(Settings::NativeMotion::mapping[i]), 997 QString::fromStdString(default_param));
954 QString::fromStdString(player.motions[i]), 998 }
955 QString::fromStdString(default_param)); 999 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
956 } 1000 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
957 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 1001 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
958 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 1002 default_analogs[i][3], default_stick_mod[i], 0.5f);
959 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 1003 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
960 default_analogs[i][3], default_stick_mod[i], 0.5f); 1004 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
961 WriteSetting(QStringLiteral("player_%1_").arg(p) + 1005 QString::fromStdString(player.analogs[i]),
962 QString::fromStdString(Settings::NativeAnalog::mapping[i]), 1006 QString::fromStdString(default_param));
963 QString::fromStdString(player.analogs[i]),
964 QString::fromStdString(default_param));
965 }
966 } 1007 }
967} 1008}
968 1009
@@ -1087,7 +1128,9 @@ void Config::SaveAudioValues() {
1087void Config::SaveControlValues() { 1128void Config::SaveControlValues() {
1088 qt_config->beginGroup(QStringLiteral("Controls")); 1129 qt_config->beginGroup(QStringLiteral("Controls"));
1089 1130
1090 SavePlayerValues(); 1131 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
1132 SavePlayerValue(p);
1133 }
1091 SaveDebugValues(); 1134 SaveDebugValues();
1092 SaveMouseValues(); 1135 SaveMouseValues();
1093 SaveTouchscreenValues(); 1136 SaveTouchscreenValues();
@@ -1515,3 +1558,19 @@ void Config::Save() {
1515 Settings::Sanitize(); 1558 Settings::Sanitize();
1516 SaveValues(); 1559 SaveValues();
1517} 1560}
1561
1562void Config::ReadControlPlayerValue(std::size_t player_index) {
1563 qt_config->beginGroup(QStringLiteral("Controls"));
1564 ReadPlayerValue(player_index);
1565 qt_config->endGroup();
1566}
1567
1568void Config::SaveControlPlayerValue(std::size_t player_index) {
1569 qt_config->beginGroup(QStringLiteral("Controls"));
1570 SavePlayerValue(player_index);
1571 qt_config->endGroup();
1572}
1573
1574const std::string& Config::GetConfigFilePath() const {
1575 return qt_config_loc;
1576}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 5d8e45d78..a1ffca48f 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -16,12 +16,24 @@ class QSettings;
16 16
17class Config { 17class Config {
18public: 18public:
19 explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true); 19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_loc = "qt-config",
26 ConfigType config_type = ConfigType::GlobalConfig);
20 ~Config(); 27 ~Config();
21 28
22 void Reload(); 29 void Reload();
23 void Save(); 30 void Save();
24 31
32 void ReadControlPlayerValue(std::size_t player_index);
33 void SaveControlPlayerValue(std::size_t player_index);
34
35 const std::string& GetConfigFilePath() const;
36
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 37 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; 38 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
27 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 39 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
@@ -34,7 +46,7 @@ public:
34 46
35private: 47private:
36 void ReadValues(); 48 void ReadValues();
37 void ReadPlayerValues(); 49 void ReadPlayerValue(std::size_t player_index);
38 void ReadDebugValues(); 50 void ReadDebugValues();
39 void ReadKeyboardValues(); 51 void ReadKeyboardValues();
40 void ReadMouseValues(); 52 void ReadMouseValues();
@@ -62,7 +74,7 @@ private:
62 void ReadWebServiceValues(); 74 void ReadWebServiceValues();
63 75
64 void SaveValues(); 76 void SaveValues();
65 void SavePlayerValues(); 77 void SavePlayerValue(std::size_t player_index);
66 void SaveDebugValues(); 78 void SaveDebugValues();
67 void SaveMouseValues(); 79 void SaveMouseValues();
68 void SaveTouchscreenValues(); 80 void SaveTouchscreenValues();
@@ -111,9 +123,9 @@ private:
111 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, 123 void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
112 const QVariant& default_value); 124 const QVariant& default_value);
113 125
126 ConfigType type;
114 std::unique_ptr<QSettings> qt_config; 127 std::unique_ptr<QSettings> qt_config;
115 std::string qt_config_loc; 128 std::string qt_config_loc;
116
117 bool global; 129 bool global;
118}; 130};
119 131
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
index 0097c9a29..6dc9c5e57 100644
--- a/src/yuzu/configuration/configure_debug_controller.cpp
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -6,9 +6,11 @@
6#include "yuzu/configuration/configure_debug_controller.h" 6#include "yuzu/configuration/configure_debug_controller.h"
7 7
8ConfigureDebugController::ConfigureDebugController(QWidget* parent, 8ConfigureDebugController::ConfigureDebugController(QWidget* parent,
9 InputCommon::InputSubsystem* input_subsystem) 9 InputCommon::InputSubsystem* input_subsystem,
10 InputProfiles* profiles)
10 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()), 11 : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
11 debug_controller(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, true)) { 12 debug_controller(
13 new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) {
12 ui->setupUi(this); 14 ui->setupUi(this);
13 15
14 ui->controllerLayout->addWidget(debug_controller); 16 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..2694b3419 100644
--- a/src/yuzu/configuration/configure_debug_controller.h
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -10,6 +10,8 @@
10 10
11class QPushButton; 11class QPushButton;
12 12
13class InputProfiles;
14
13namespace InputCommon { 15namespace InputCommon {
14class InputSubsystem; 16class InputSubsystem;
15} 17}
@@ -22,8 +24,8 @@ class ConfigureDebugController : public QDialog {
22 Q_OBJECT 24 Q_OBJECT
23 25
24public: 26public:
25 explicit ConfigureDebugController(QWidget* parent, 27 explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
26 InputCommon::InputSubsystem* input_subsystem); 28 InputProfiles* profiles);
27 ~ConfigureDebugController() override; 29 ~ConfigureDebugController() override;
28 30
29 void ApplyConfiguration(); 31 void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f2932aa0b..523ece426 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -23,6 +23,7 @@
23#include "yuzu/configuration/configure_motion_touch.h" 23#include "yuzu/configuration/configure_motion_touch.h"
24#include "yuzu/configuration/configure_mouse_advanced.h" 24#include "yuzu/configuration/configure_mouse_advanced.h"
25#include "yuzu/configuration/configure_touchscreen_advanced.h" 25#include "yuzu/configuration/configure_touchscreen_advanced.h"
26#include "yuzu/configuration/input_profiles.h"
26 27
27namespace { 28namespace {
28template <typename Dialog, typename... Args> 29template <typename Dialog, typename... Args>
@@ -64,7 +65,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
64} 65}
65 66
66ConfigureInput::ConfigureInput(QWidget* parent) 67ConfigureInput::ConfigureInput(QWidget* parent)
67 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { 68 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
69 profiles(std::make_unique<InputProfiles>()) {
68 ui->setupUi(this); 70 ui->setupUi(this);
69} 71}
70 72
@@ -73,14 +75,22 @@ ConfigureInput::~ConfigureInput() = default;
73void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, 75void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
74 std::size_t max_players) { 76 std::size_t max_players) {
75 player_controllers = { 77 player_controllers = {
76 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem), 78 new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem,
77 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem), 79 profiles.get()),
78 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem), 80 new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem,
79 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem), 81 profiles.get()),
80 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem), 82 new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem,
81 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem), 83 profiles.get()),
82 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem), 84 new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem,
83 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem), 85 profiles.get()),
86 new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem,
87 profiles.get()),
88 new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem,
89 profiles.get()),
90 new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem,
91 profiles.get()),
92 new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem,
93 profiles.get()),
84 }; 94 };
85 95
86 player_tabs = { 96 player_tabs = {
@@ -134,7 +144,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
134 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); 144 ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
135 ui->tabAdvanced->layout()->addWidget(advanced); 145 ui->tabAdvanced->layout()->addWidget(advanced);
136 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] { 146 connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
137 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem); 147 CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get());
138 }); 148 });
139 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] { 149 connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
140 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem); 150 CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 0e8b2fd4e..f135a4299 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -19,6 +19,8 @@ class QCheckBox;
19class QString; 19class QString;
20class QTimer; 20class QTimer;
21 21
22class InputProfiles;
23
22namespace InputCommon { 24namespace InputCommon {
23class InputSubsystem; 25class InputSubsystem;
24} 26}
@@ -61,6 +63,8 @@ private:
61 63
62 std::unique_ptr<Ui::ConfigureInput> ui; 64 std::unique_ptr<Ui::ConfigureInput> ui;
63 65
66 std::unique_ptr<InputProfiles> profiles;
67
64 std::array<ConfigureInputPlayer*, 8> player_controllers; 68 std::array<ConfigureInputPlayer*, 8> player_controllers;
65 std::array<QWidget*, 8> player_tabs; 69 std::array<QWidget*, 8> player_tabs;
66 std::array<QCheckBox*, 8> player_connected; 70 std::array<QCheckBox*, 8> player_connected;
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 0de0c6999..b4de2f6af 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -22,6 +22,8 @@
22#include "ui_configure_input_player.h" 22#include "ui_configure_input_player.h"
23#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
24#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
25#include "yuzu/configuration/input_profiles.h"
26#include "yuzu/util/limitable_input_dialog.h"
25 27
26constexpr std::size_t HANDHELD_INDEX = 8; 28constexpr std::size_t HANDHELD_INDEX = 8;
27 29
@@ -240,10 +242,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
240ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, 242ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
241 QWidget* bottom_row, 243 QWidget* bottom_row,
242 InputCommon::InputSubsystem* input_subsystem_, 244 InputCommon::InputSubsystem* input_subsystem_,
243 bool debug) 245 InputProfiles* profiles_, bool debug)
244 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 246 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
245 debug(debug), input_subsystem{input_subsystem_}, timeout_timer(std::make_unique<QTimer>()), 247 debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
246 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row) { 248 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
249 bottom_row(bottom_row) {
247 ui->setupUi(this); 250 ui->setupUi(this);
248 251
249 setFocusPolicy(Qt::ClickFocus); 252 setFocusPolicy(Qt::ClickFocus);
@@ -521,6 +524,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
521 } 524 }
522 }); 525 });
523 526
527 RefreshInputProfiles();
528
529 connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
530 &ConfigureInputPlayer::CreateProfile);
531 connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
532 &ConfigureInputPlayer::DeleteProfile);
533 connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
534 &ConfigureInputPlayer::LoadProfile);
535 connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
536 &ConfigureInputPlayer::SaveProfile);
537
524 LoadConfiguration(); 538 LoadConfiguration();
525 539
526 // TODO(wwylele): enable this when we actually emulate it 540 // TODO(wwylele): enable this when we actually emulate it
@@ -1061,3 +1075,94 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1061 1075
1062 SetPollingResult({}, true); 1076 SetPollingResult({}, true);
1063} 1077}
1078
1079void ConfigureInputPlayer::CreateProfile() {
1080 const auto profile_name =
1081 LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
1082
1083 if (profile_name.isEmpty()) {
1084 return;
1085 }
1086
1087 if (!profiles->IsProfileNameValid(profile_name.toStdString())) {
1088 QMessageBox::critical(this, tr("Create Input Profile"),
1089 tr("The given profile name is not valid!"));
1090 return;
1091 }
1092
1093 ApplyConfiguration();
1094
1095 if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
1096 QMessageBox::critical(this, tr("Create Input Profile"),
1097 tr("Failed to create the input profile \"%1\"").arg(profile_name));
1098 RefreshInputProfiles();
1099 return;
1100 }
1101
1102 ui->comboProfiles->addItem(profile_name);
1103 ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
1104}
1105
1106void ConfigureInputPlayer::DeleteProfile() {
1107 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
1108
1109 if (profile_name.isEmpty()) {
1110 return;
1111 }
1112
1113 if (!profiles->DeleteProfile(profile_name.toStdString())) {
1114 QMessageBox::critical(this, tr("Delete Input Profile"),
1115 tr("Failed to delete the input profile \"%1\"").arg(profile_name));
1116 RefreshInputProfiles();
1117 return;
1118 }
1119
1120 ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
1121 ui->comboProfiles->setCurrentIndex(-1);
1122}
1123
1124void ConfigureInputPlayer::LoadProfile() {
1125 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
1126
1127 if (profile_name.isEmpty()) {
1128 return;
1129 }
1130
1131 ApplyConfiguration();
1132
1133 if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
1134 QMessageBox::critical(this, tr("Load Input Profile"),
1135 tr("Failed to load the input profile \"%1\"").arg(profile_name));
1136 RefreshInputProfiles();
1137 return;
1138 }
1139
1140 LoadConfiguration();
1141}
1142
1143void ConfigureInputPlayer::SaveProfile() {
1144 const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
1145
1146 if (profile_name.isEmpty()) {
1147 return;
1148 }
1149
1150 ApplyConfiguration();
1151
1152 if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
1153 QMessageBox::critical(this, tr("Save Input Profile"),
1154 tr("Failed to save the input profile \"%1\"").arg(profile_name));
1155 RefreshInputProfiles();
1156 return;
1157 }
1158}
1159
1160void ConfigureInputPlayer::RefreshInputProfiles() {
1161 ui->comboProfiles->clear();
1162
1163 for (const auto& profile_name : profiles->GetInputProfileNames()) {
1164 ui->comboProfiles->addItem(QString::fromStdString(profile_name));
1165 }
1166
1167 ui->comboProfiles->setCurrentIndex(-1);
1168}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index a5414e624..05dee5af5 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -26,6 +26,8 @@ class QString;
26class QTimer; 26class QTimer;
27class QWidget; 27class QWidget;
28 28
29class InputProfiles;
30
29namespace InputCommon { 31namespace InputCommon {
30class InputSubsystem; 32class InputSubsystem;
31} 33}
@@ -45,7 +47,7 @@ class ConfigureInputPlayer : public QWidget {
45public: 47public:
46 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row, 48 explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
47 InputCommon::InputSubsystem* input_subsystem_, 49 InputCommon::InputSubsystem* input_subsystem_,
48 bool debug = false); 50 InputProfiles* profiles_, bool debug = false);
49 ~ConfigureInputPlayer() override; 51 ~ConfigureInputPlayer() override;
50 52
51 /// Save all button configurations to settings file. 53 /// Save all button configurations to settings file.
@@ -116,6 +118,21 @@ private:
116 /// Gets the default controller mapping for this device and auto configures the input to match. 118 /// Gets the default controller mapping for this device and auto configures the input to match.
117 void UpdateMappingWithDefaults(); 119 void UpdateMappingWithDefaults();
118 120
121 /// Creates a controller profile.
122 void CreateProfile();
123
124 /// Deletes the selected controller profile.
125 void DeleteProfile();
126
127 /// Loads the selected controller profile.
128 void LoadProfile();
129
130 /// Saves the current controller configuration into a selected controller profile.
131 void SaveProfile();
132
133 /// Refreshes the list of controller profiles.
134 void RefreshInputProfiles();
135
119 std::unique_ptr<Ui::ConfigureInputPlayer> ui; 136 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
120 137
121 std::size_t player_index; 138 std::size_t player_index;
@@ -123,6 +140,8 @@ private:
123 140
124 InputCommon::InputSubsystem* input_subsystem; 141 InputCommon::InputSubsystem* input_subsystem;
125 142
143 InputProfiles* profiles;
144
126 std::unique_ptr<QTimer> timeout_timer; 145 std::unique_ptr<QTimer> timeout_timer;
127 std::unique_ptr<QTimer> poll_timer; 146 std::unique_ptr<QTimer> poll_timer;
128 147
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 @@
29 29
30ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) 30ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { 31 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
32 game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); 32 game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id),
33 Config::ConfigType::PerGameConfig);
33 34
34 Settings::SetConfiguringGlobal(false); 35 Settings::SetConfiguringGlobal(false);
35 36
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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <fmt/format.h>
6
7#include "common/common_paths.h"
8#include "common/file_util.h"
9#include "yuzu/configuration/config.h"
10#include "yuzu/configuration/input_profiles.h"
11
12namespace FS = Common::FS;
13
14namespace {
15
16bool ProfileExistsInFilesystem(std::string_view profile_name) {
17 return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini",
18 FS::GetUserPath(FS::UserPath::ConfigDir), profile_name));
19}
20
21bool IsINI(std::string_view filename) {
22 const std::size_t index = filename.rfind('.');
23
24 if (index == std::string::npos) {
25 return false;
26 }
27
28 return filename.substr(index) == ".ini";
29}
30
31std::string GetNameWithoutExtension(const std::string& filename) {
32 const std::size_t index = filename.rfind('.');
33
34 if (index == std::string::npos) {
35 return filename;
36 }
37
38 return filename.substr(0, index);
39}
40
41} // namespace
42
43InputProfiles::InputProfiles() {
44 const std::string input_profile_loc =
45 fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir));
46
47 FS::ForeachDirectoryEntry(
48 nullptr, input_profile_loc,
49 [this](u64* entries_out, const std::string& directory, const std::string& filename) {
50 if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) {
51 map_profiles.insert_or_assign(
52 GetNameWithoutExtension(filename),
53 std::make_unique<Config>(GetNameWithoutExtension(filename),
54 Config::ConfigType::InputProfile));
55 }
56 return true;
57 });
58}
59
60InputProfiles::~InputProfiles() = default;
61
62std::vector<std::string> InputProfiles::GetInputProfileNames() {
63 std::vector<std::string> profile_names;
64 profile_names.reserve(map_profiles.size());
65
66 for (const auto& [profile_name, config] : map_profiles) {
67 if (!ProfileExistsInFilesystem(profile_name)) {
68 DeleteProfile(profile_name);
69 continue;
70 }
71
72 profile_names.push_back(profile_name);
73 }
74
75 return profile_names;
76}
77
78bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
79 return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
80}
81
82bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
83 if (ProfileExistsInMap(profile_name)) {
84 return false;
85 }
86
87 map_profiles.insert_or_assign(
88 profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
89
90 return SaveProfile(profile_name, player_index);
91}
92
93bool InputProfiles::DeleteProfile(const std::string& profile_name) {
94 if (!ProfileExistsInMap(profile_name)) {
95 return false;
96 }
97
98 if (!ProfileExistsInFilesystem(profile_name) ||
99 FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) {
100 map_profiles.erase(profile_name);
101 }
102
103 return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
104}
105
106bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
107 if (!ProfileExistsInMap(profile_name)) {
108 return false;
109 }
110
111 if (!ProfileExistsInFilesystem(profile_name)) {
112 map_profiles.erase(profile_name);
113 return false;
114 }
115
116 map_profiles[profile_name]->ReadControlPlayerValue(player_index);
117 return true;
118}
119
120bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
121 if (!ProfileExistsInMap(profile_name)) {
122 return false;
123 }
124
125 map_profiles[profile_name]->SaveControlPlayerValue(player_index);
126 return true;
127}
128
129bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
130 return map_profiles.find(profile_name) != map_profiles.end();
131}
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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <string_view>
9#include <unordered_map>
10
11class Config;
12
13class InputProfiles {
14
15public:
16 explicit InputProfiles();
17 virtual ~InputProfiles();
18
19 std::vector<std::string> GetInputProfileNames();
20
21 static bool IsProfileNameValid(std::string_view profile_name);
22
23 bool CreateProfile(const std::string& profile_name, std::size_t player_index);
24 bool DeleteProfile(const std::string& profile_name);
25 bool LoadProfile(const std::string& profile_name, std::size_t player_index);
26 bool SaveProfile(const std::string& profile_name, std::size_t player_index);
27
28private:
29 bool ProfileExistsInMap(const std::string& profile_name) const;
30
31 std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
32};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 18e68e590..4ff7fd92f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1087,7 +1087,7 @@ void GMainWindow::BootGame(const QString& filename) {
1087 const auto loader = Loader::GetLoader(v_file); 1087 const auto loader = Loader::GetLoader(v_file);
1088 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1088 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1089 // Load per game settings 1089 // Load per game settings
1090 Config per_game_config(fmt::format("{:016X}.ini", title_id), false); 1090 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
1091 } 1091 }
1092 1092
1093 Settings::LogSettings(); 1093 Settings::LogSettings();