mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-27 01:02:48 +01:00
input_profiles: Implement input profiles
This commit is contained in:
parent
75eaab2e0f
commit
57d89e291d
13 changed files with 506 additions and 127 deletions
|
@ -108,6 +108,8 @@ add_executable(yuzu
|
|||
configuration/configure_web.cpp
|
||||
configuration/configure_web.h
|
||||
configuration/configure_web.ui
|
||||
configuration/input_profiles.cpp
|
||||
configuration/input_profiles.h
|
||||
debugger/console.cpp
|
||||
debugger/console.h
|
||||
debugger/profiler.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,27 @@
|
|||
|
||||
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;
|
||||
Config::Config(const std::string& config_file, ConfigType config_type) : type(config_type) {
|
||||
global = config_type == ConfigType::GlobalConfig;
|
||||
|
||||
switch (config_type) {
|
||||
case ConfigType::GlobalConfig:
|
||||
case ConfigType::PerGameConfig:
|
||||
qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", 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;
|
||||
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_file);
|
||||
FS::CreateFullPath(qt_config_loc);
|
||||
qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
|
||||
QSettings::IniFormat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
|
@ -242,44 +256,65 @@ 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::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[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), false).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.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),
|
||||
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("player_%1_button_color_left").arg(p),
|
||||
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]);
|
||||
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) +
|
||||
->value(QStringLiteral("%1").arg(player_prefix) +
|
||||
QString::fromUtf8(Settings::NativeButton::mapping[i]),
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
|
@ -290,12 +325,11 @@ void Config::ReadPlayerValues() {
|
|||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
|
||||
const std::string default_param =
|
||||
InputCommon::GenerateKeyboardParam(default_motions[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) +
|
||||
->value(QStringLiteral("%1").arg(player_prefix) +
|
||||
QString::fromUtf8(Settings::NativeMotion::mapping[i]),
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
|
@ -312,7 +346,7 @@ void Config::ReadPlayerValues() {
|
|||
auto& player_analogs = player.analogs[i];
|
||||
|
||||
player_analogs = qt_config
|
||||
->value(QStringLiteral("player_%1_").arg(p) +
|
||||
->value(QStringLiteral("%1").arg(player_prefix) +
|
||||
QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
|
@ -321,7 +355,6 @@ void Config::ReadPlayerValues() {
|
|||
player_analogs = default_param;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadDebugValues() {
|
||||
|
@ -436,7 +469,9 @@ void Config::ReadAudioValues() {
|
|||
void Config::ReadControlValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Controls"));
|
||||
|
||||
ReadPlayerValues();
|
||||
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
|
||||
ReadPlayerValue(p);
|
||||
}
|
||||
ReadDebugValues();
|
||||
ReadKeyboardValues();
|
||||
ReadMouseValues();
|
||||
|
@ -920,36 +955,43 @@ 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),
|
||||
const auto& player = Settings::values.players[player_index];
|
||||
|
||||
WriteSetting(QStringLiteral("%1type").arg(player_prefix),
|
||||
static_cast<u8>(player.controller_type),
|
||||
static_cast<u8>(Settings::ControllerType::ProController));
|
||||
|
||||
WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
|
||||
if (!player_prefix.isEmpty()) {
|
||||
WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
|
||||
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) +
|
||||
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::NativeMotion::NumMotions; ++i) {
|
||||
const std::string default_param =
|
||||
InputCommon::GenerateKeyboardParam(default_motions[i]);
|
||||
WriteSetting(QStringLiteral("player_%1_").arg(p) +
|
||||
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));
|
||||
|
@ -958,12 +1000,11 @@ void Config::SavePlayerValues() {
|
|||
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) +
|
||||
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
|
||||
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(player.analogs[i]),
|
||||
QString::fromStdString(default_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::SaveDebugValues() {
|
||||
|
@ -1087,7 +1128,9 @@ void Config::SaveAudioValues() {
|
|||
void Config::SaveControlValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Controls"));
|
||||
|
||||
SavePlayerValues();
|
||||
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
|
||||
SavePlayerValue(p);
|
||||
}
|
||||
SaveDebugValues();
|
||||
SaveMouseValues();
|
||||
SaveTouchscreenValues();
|
||||
|
@ -1515,3 +1558,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;
|
||||
}
|
||||
|
|
|
@ -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_loc = "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;
|
||||
|
@ -34,7 +46,7 @@ public:
|
|||
|
||||
private:
|
||||
void ReadValues();
|
||||
void ReadPlayerValues();
|
||||
void ReadPlayerValue(std::size_t player_index);
|
||||
void ReadDebugValues();
|
||||
void ReadKeyboardValues();
|
||||
void ReadMouseValues();
|
||||
|
@ -62,7 +74,7 @@ private:
|
|||
void ReadWebServiceValues();
|
||||
|
||||
void SaveValues();
|
||||
void SavePlayerValues();
|
||||
void SavePlayerValue(std::size_t player_index);
|
||||
void SaveDebugValues();
|
||||
void SaveMouseValues();
|
||||
void SaveTouchscreenValues();
|
||||
|
@ -111,9 +123,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;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
#include "yuzu/configuration/configure_debug_controller.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);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
class QPushButton;
|
||||
|
||||
class InputProfiles;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
@ -22,8 +24,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();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "yuzu/configuration/configure_motion_touch.h"
|
||||
#include "yuzu/configuration/configure_mouse_advanced.h"
|
||||
#include "yuzu/configuration/configure_touchscreen_advanced.h"
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
|
||||
namespace {
|
||||
template <typename Dialog, typename... Args>
|
||||
|
@ -64,7 +65,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 +75,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 = {
|
||||
|
@ -134,7 +144,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);
|
||||
|
|
|
@ -19,6 +19,8 @@ class QCheckBox;
|
|||
class QString;
|
||||
class QTimer;
|
||||
|
||||
class InputProfiles;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
@ -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;
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "ui_configure_input_player.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
#include "yuzu/configuration/input_profiles.h"
|
||||
#include "yuzu/util/limitable_input_dialog.h"
|
||||
|
||||
constexpr std::size_t HANDHELD_INDEX = 8;
|
||||
|
||||
|
@ -240,10 +242,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);
|
||||
|
@ -521,6 +524,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
|||
}
|
||||
});
|
||||
|
||||
RefreshInputProfiles();
|
||||
|
||||
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
|
||||
|
@ -1061,3 +1075,94 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
SetPollingResult({}, true);
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::CreateProfile() {
|
||||
const auto profile_name =
|
||||
LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
|
||||
|
||||
if (profile_name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!profiles->IsProfileNameValid(profile_name.toStdString())) {
|
||||
QMessageBox::critical(this, tr("Create Input Profile"),
|
||||
tr("The given profile name is not valid!"));
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyConfiguration();
|
||||
|
||||
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));
|
||||
RefreshInputProfiles();
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!profiles->DeleteProfile(profile_name.toStdString())) {
|
||||
QMessageBox::critical(this, tr("Delete Input Profile"),
|
||||
tr("Failed to delete the input profile \"%1\"").arg(profile_name));
|
||||
RefreshInputProfiles();
|
||||
return;
|
||||
}
|
||||
|
||||
ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
|
||||
ui->comboProfiles->setCurrentIndex(-1);
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::LoadProfile() {
|
||||
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
|
||||
|
||||
if (profile_name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
RefreshInputProfiles();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
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));
|
||||
RefreshInputProfiles();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::RefreshInputProfiles() {
|
||||
ui->comboProfiles->clear();
|
||||
|
||||
for (const auto& profile_name : profiles->GetInputProfileNames()) {
|
||||
ui->comboProfiles->addItem(QString::fromStdString(profile_name));
|
||||
}
|
||||
|
||||
ui->comboProfiles->setCurrentIndex(-1);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ class QString;
|
|||
class QTimer;
|
||||
class QWidget;
|
||||
|
||||
class InputProfiles;
|
||||
|
||||
namespace InputCommon {
|
||||
class InputSubsystem;
|
||||
}
|
||||
|
@ -45,7 +47,7 @@ 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.
|
||||
|
@ -116,6 +118,21 @@ 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();
|
||||
|
||||
/// Refreshes the list of controller profiles.
|
||||
void RefreshInputProfiles();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
|
||||
|
||||
std::size_t player_index;
|
||||
|
@ -123,6 +140,8 @@ private:
|
|||
|
||||
InputCommon::InputSubsystem* input_subsystem;
|
||||
|
||||
InputProfiles* profiles;
|
||||
|
||||
std::unique_ptr<QTimer> timeout_timer;
|
||||
std::unique_ptr<QTimer> poll_timer;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
131
src/yuzu/configuration/input_profiles.cpp
Normal file
131
src/yuzu/configuration/input_profiles.cpp
Normal file
|
@ -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();
|
||||
}
|
32
src/yuzu/configuration/input_profiles.h
Normal file
32
src/yuzu/configuration/input_profiles.h
Normal file
|
@ -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;
|
||||
};
|
|
@ -1087,7 +1087,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
|||
const auto loader = Loader::GetLoader(v_file);
|
||||
if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
|
||||
// Load per game settings
|
||||
Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
|
||||
Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
|
||||
}
|
||||
|
||||
Settings::LogSettings();
|
||||
|
|
Loading…
Reference in a new issue