diff options
| author | 2020-09-23 09:52:25 -0400 | |
|---|---|---|
| committer | 2020-11-15 23:33:19 -0500 | |
| commit | 57d89e291de0eacfd368784309a0cbf89d38dcc8 (patch) | |
| tree | eedaf34c8fda2dc47c07cf5e2fe528d78aa9adc0 /src | |
| parent | configure_input_player: Implement input exclusivity and persistence (diff) | |
| download | yuzu-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.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 271 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.h | 20 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_debug_controller.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_debug_controller.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.cpp | 30 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input.h | 4 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 111 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.h | 21 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_per_game.cpp | 3 | ||||
| -rw-r--r-- | src/yuzu/configuration/input_profiles.cpp | 131 | ||||
| -rw-r--r-- | src/yuzu/configuration/input_profiles.h | 32 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 2 |
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 | ||
| 15 | namespace FS = Common::FS; | 16 | namespace FS = Common::FS; |
| 16 | 17 | ||
| 17 | Config::Config(const std::string& config_file, bool is_global) { | 18 | Config::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 | ||
| 27 | Config::~Config() { | 41 | Config::~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 | ||
| 245 | void Config::ReadPlayerValues() { | 259 | void 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() { | |||
| 436 | void Config::ReadControlValues() { | 469 | void 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 | ||
| 923 | void Config::SavePlayerValues() { | 958 | void 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() { | |||
| 1087 | void Config::SaveControlValues() { | 1128 | void 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 | |||
| 1562 | void Config::ReadControlPlayerValue(std::size_t player_index) { | ||
| 1563 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1564 | ReadPlayerValue(player_index); | ||
| 1565 | qt_config->endGroup(); | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | void Config::SaveControlPlayerValue(std::size_t player_index) { | ||
| 1569 | qt_config->beginGroup(QStringLiteral("Controls")); | ||
| 1570 | SavePlayerValue(player_index); | ||
| 1571 | qt_config->endGroup(); | ||
| 1572 | } | ||
| 1573 | |||
| 1574 | const 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 | ||
| 17 | class Config { | 17 | class Config { |
| 18 | public: | 18 | public: |
| 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 | ||
| 35 | private: | 47 | private: |
| 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 | ||
| 8 | ConfigureDebugController::ConfigureDebugController(QWidget* parent, | 8 | ConfigureDebugController::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 | ||
| 11 | class QPushButton; | 11 | class QPushButton; |
| 12 | 12 | ||
| 13 | class InputProfiles; | ||
| 14 | |||
| 13 | namespace InputCommon { | 15 | namespace InputCommon { |
| 14 | class InputSubsystem; | 16 | class InputSubsystem; |
| 15 | } | 17 | } |
| @@ -22,8 +24,8 @@ class ConfigureDebugController : public QDialog { | |||
| 22 | Q_OBJECT | 24 | Q_OBJECT |
| 23 | 25 | ||
| 24 | public: | 26 | public: |
| 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 | ||
| 27 | namespace { | 28 | namespace { |
| 28 | template <typename Dialog, typename... Args> | 29 | template <typename Dialog, typename... Args> |
| @@ -64,7 +65,8 @@ void OnDockedModeChanged(bool last_state, bool new_state) { | |||
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | ConfigureInput::ConfigureInput(QWidget* parent) | 67 | ConfigureInput::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; | |||
| 73 | void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | 75 | void 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; | |||
| 19 | class QString; | 19 | class QString; |
| 20 | class QTimer; | 20 | class QTimer; |
| 21 | 21 | ||
| 22 | class InputProfiles; | ||
| 23 | |||
| 22 | namespace InputCommon { | 24 | namespace InputCommon { |
| 23 | class InputSubsystem; | 25 | class 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 | ||
| 26 | constexpr std::size_t HANDHELD_INDEX = 8; | 28 | constexpr std::size_t HANDHELD_INDEX = 8; |
| 27 | 29 | ||
| @@ -240,10 +242,11 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) | |||
| 240 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, | 242 | ConfigureInputPlayer::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 | |||
| 1079 | void 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 | |||
| 1106 | void 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 | |||
| 1124 | void 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 | |||
| 1143 | void 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 | |||
| 1160 | void 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; | |||
| 26 | class QTimer; | 26 | class QTimer; |
| 27 | class QWidget; | 27 | class QWidget; |
| 28 | 28 | ||
| 29 | class InputProfiles; | ||
| 30 | |||
| 29 | namespace InputCommon { | 31 | namespace InputCommon { |
| 30 | class InputSubsystem; | 32 | class InputSubsystem; |
| 31 | } | 33 | } |
| @@ -45,7 +47,7 @@ class ConfigureInputPlayer : public QWidget { | |||
| 45 | public: | 47 | public: |
| 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 | ||
| 30 | ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) | 30 | ConfigurePerGame::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 | |||
| 12 | namespace FS = Common::FS; | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | |||
| 16 | bool 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 | |||
| 21 | bool 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 | |||
| 31 | std::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 | |||
| 43 | InputProfiles::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 | |||
| 60 | InputProfiles::~InputProfiles() = default; | ||
| 61 | |||
| 62 | std::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 | |||
| 78 | bool InputProfiles::IsProfileNameValid(std::string_view profile_name) { | ||
| 79 | return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos; | ||
| 80 | } | ||
| 81 | |||
| 82 | bool 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 | |||
| 93 | bool 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 | |||
| 106 | bool 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 | |||
| 120 | bool 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 | |||
| 129 | bool 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 | |||
| 11 | class Config; | ||
| 12 | |||
| 13 | class InputProfiles { | ||
| 14 | |||
| 15 | public: | ||
| 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 | |||
| 28 | private: | ||
| 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(); |