narr don't look

This commit is contained in:
FearlessTobi
2023-01-20 19:20:41 +01:00
committed by GPUCode
parent dfc359affb
commit a2a1f6c14c
19 changed files with 738 additions and 420 deletions

View File

@ -15,8 +15,8 @@
#include "common/param_package.h" #include "common/param_package.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "input_common/drivers/udp_client.h"
#include "input_common/main.h" #include "input_common/main.h"
#include "input_common/udp/client.h"
#include "network/network_settings.h" #include "network/network_settings.h"
Config::Config() { Config::Config() {
@ -97,7 +97,6 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<Type, range
void Config::ReadValues() { void Config::ReadValues() {
// Controls // Controls
// TODO: add multiple input profile support
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
Settings::values.current_input_profile.buttons[i] = Settings::values.current_input_profile.buttons[i] =

View File

@ -88,6 +88,8 @@ add_executable(citra-qt
configuration/configure_cheats.cpp configuration/configure_cheats.cpp
configuration/configure_cheats.h configuration/configure_cheats.h
configuration/configure_cheats.ui configuration/configure_cheats.ui
configuration/input_profiles.cpp
configuration/input_profiles.h
debugger/console.h debugger/console.h
debugger/console.cpp debugger/console.cpp
debugger/graphics/graphics.cpp debugger/graphics/graphics.cpp

View File

@ -27,17 +27,21 @@ Config::~Config() {
} }
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_Up, Qt::Key_Down,
Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Right, Qt::Key_Q, Qt::Key_E, Qt::Key_M, Qt::Key_N,
Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B, Qt::Key_O, Qt::Key_P, Qt::Key_R, Qt::Key_T, Qt::Key_B,
};
const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
Qt::Key_7,
Qt::Key_8,
}; };
const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{ {
Qt::Key_Up, Qt::Key_W,
Qt::Key_Down, Qt::Key_S,
Qt::Key_Left, Qt::Key_A,
Qt::Key_Right,
Qt::Key_D, Qt::Key_D,
}, },
{ {
@ -45,10 +49,14 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
Qt::Key_K, Qt::Key_K,
Qt::Key_J, Qt::Key_J,
Qt::Key_L, Qt::Key_L,
Qt::Key_D,
}, },
}}; }};
const std::array<int, 2> Config::default_stick_mod = {
Qt::Key_Shift,
0,
};
// This shouldn't have anything except static initializers (no functions). So // This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE. // QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as // This must be in alphabetical order according to action name as it must have the same order as
@ -97,12 +105,21 @@ void Config::Initialize(const std::string& config_name) {
case ConfigType::PerGameConfig: case ConfigType::PerGameConfig:
qt_config_loc = fmt::format("{}/custom/{}", fs_config_loc, config_file); qt_config_loc = fmt::format("{}/custom/{}", fs_config_loc, config_file);
break; break;
case ConfigType::InputProfile:
qt_config_loc = fmt::format("{}/input/{}", fs_config_loc, config_file);
break;
} }
FileUtil::CreateFullPath(qt_config_loc); FileUtil::CreateFullPath(qt_config_loc);
qt_config = qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
Reload(); if (type != ConfigType::InputProfile) {
Reload();
}
}
bool Config::IsCustomConfig() {
return type == ConfigType::PerGameConfig;
} }
/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their /* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their
@ -317,9 +334,78 @@ void Config::ReadCameraValues() {
qt_config->endGroup(); qt_config->endGroup();
} }
void Config::ReadControlValues() { void Config::ReadPlayerValues() {
qt_config->beginGroup(QStringLiteral("Controls")); const QString player_prefix = [this] {
if (type == ConfigType::InputProfile) {
return QString{};
} else {
return QStringLiteral("player_");
}
}();
auto& player = Settings::values.players.GetValue();
if (IsCustomConfig()) {
const auto profile_name =
qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{})
.toString()
.toStdString();
if (profile_name.empty()) {
// Use the global input config
player = Settings::values.players.GetValue(true);
return;
}
player.profile_name = profile_name;
}
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
auto& player_buttons = player.buttons[i];
player_buttons = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeButton::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_buttons.empty()) {
player_buttons = default_param;
}
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& player_analogs = player.analogs[i];
player_analogs = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_analogs.empty()) {
player_analogs = default_param;
}
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
auto& player_motions = player.motions[i];
player_motions = qt_config
->value(QStringLiteral("%1").arg(player_prefix) +
QString::fromUtf8(Settings::NativeMotion::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (player_motions.empty()) {
player_motions = default_param;
}
}
}
void Config::ReadMotionTouchValues() {
int num_touch_from_button_maps = int num_touch_from_button_maps =
qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
@ -352,80 +438,26 @@ void Config::ReadControlValues() {
} }
qt_config->endArray(); qt_config->endArray();
Settings::values.current_input_profile_index = ReadBasicSetting(Settings::values.touch_device);
ReadSetting(QStringLiteral("profile"), 0).toInt(); ReadBasicSetting(Settings::values.touch_from_button_map_index);
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
const auto append_profile = [this, num_touch_from_button_maps] { ReadBasicSetting(Settings::values.udp_input_address);
Settings::InputProfile profile; ReadBasicSetting(Settings::values.udp_input_port);
profile.name = ReadBasicSetting(Settings::values.udp_pad_index);
ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString(); }
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (profile.buttons[i].empty())
profile.buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
profile.analogs[i] = ReadSetting(QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(default_param))
.toString()
.toStdString();
if (profile.analogs[i].empty())
profile.analogs[i] = default_param;
}
profile.motion_device =
ReadSetting(QStringLiteral("motion_device"),
QStringLiteral(
"engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"))
.toString()
.toStdString();
profile.touch_device =
ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
.toString()
.toStdString();
profile.use_touch_from_button =
ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
profile.touch_from_button_map_index =
ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
profile.touch_from_button_map_index =
std::clamp(profile.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
profile.udp_input_address =
ReadSetting(QStringLiteral("udp_input_address"), QString::fromUtf8("127.0.0.1"))
.toString()
.toStdString();
profile.udp_input_port =
static_cast<u16>(ReadSetting(QStringLiteral("udp_input_port"), 26760).toInt());
profile.udp_pad_index =
static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
Settings::values.input_profiles.emplace_back(std::move(profile));
};
int num_input_profiles = qt_config->beginReadArray(QStringLiteral("profiles")); void Config::ReadControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
for (int i = 0; i < num_input_profiles; ++i) { Settings::values.players.SetGlobal(!IsCustomConfig());
qt_config->setArrayIndex(i); ReadPlayerValues();
append_profile(); if (IsCustomConfig()) {
qt_config->endGroup();
return;
} }
ReadMotionTouchValues();
qt_config->endArray();
// create a input profile if no input profiles exist, with the default or old settings
if (num_input_profiles == 0) {
append_profile();
num_input_profiles = 1;
}
// ensure that the current input profile index is valid.
Settings::values.current_input_profile_index =
std::clamp(Settings::values.current_input_profile_index, 0, num_input_profiles - 1);
Settings::LoadProfile(Settings::values.current_input_profile_index);
qt_config->endGroup(); qt_config->endGroup();
} }
@ -882,60 +914,87 @@ void Config::SaveCameraValues() {
qt_config->endGroup(); qt_config->endGroup();
} }
void Config::SaveControlValues() { void Config::SavePlayerValues() {
qt_config->beginGroup(QStringLiteral("Controls")); const QString player_prefix = [this] {
if (type == ConfigType::InputProfile) {
return QString{};
} else {
return QStringLiteral("player_");
}
}();
WriteSetting(QStringLiteral("profile"), Settings::values.current_input_profile_index, 0); const auto& player = Settings::values.players.GetValue();
qt_config->beginWriteArray(QStringLiteral("profiles")); if (IsCustomConfig()) {
for (std::size_t p = 0; p < Settings::values.input_profiles.size(); ++p) { if (player.profile_name.empty()) {
qt_config->setArrayIndex(static_cast<int>(p)); // No custom profile selected
const auto& profile = Settings::values.input_profiles[p]; return;
WriteSetting(QStringLiteral("name"), QString::fromStdString(profile.name),
QStringLiteral("default"));
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(profile.buttons[i]),
QString::fromStdString(default_param));
} }
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix),
std::string default_param = InputCommon::GenerateAnalogParamFromKeys( QString::fromStdString(player.profile_name), QString{});
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
WriteSetting(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(profile.analogs[i]),
QString::fromStdString(default_param));
}
WriteSetting(
QStringLiteral("motion_device"), QString::fromStdString(profile.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"));
WriteSetting(QStringLiteral("touch_device"), QString::fromStdString(profile.touch_device),
QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("use_touch_from_button"), profile.use_touch_from_button, false);
WriteSetting(QStringLiteral("touch_from_button_map"), profile.touch_from_button_map_index,
0);
WriteSetting(QStringLiteral("udp_input_address"),
QString::fromStdString(profile.udp_input_address),
QString::fromUtf8("127.0.0.1"));
WriteSetting(QStringLiteral("udp_input_port"), profile.udp_input_port, 26760);
WriteSetting(QStringLiteral("udp_pad_index"), profile.udp_pad_index, 0);
} }
qt_config->endArray();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
QString::fromStdString(player.buttons[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(player.analogs[i]),
QString::fromStdString(default_param));
}
for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
WriteSetting(QStringLiteral("%1").arg(player_prefix) +
QString::fromStdString(Settings::NativeMotion::mapping[i]),
QString::fromStdString(player.motions[i]),
QString::fromStdString(default_param));
}
}
void Config::SaveMotionTouchValues() {
WriteBasicSetting(Settings::values.touch_device);
WriteBasicSetting(Settings::values.touch_from_button_map_index);
WriteBasicSetting(Settings::values.udp_input_address);
WriteBasicSetting(Settings::values.udp_input_port);
WriteBasicSetting(Settings::values.udp_pad_index);
qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
qt_config->setArrayIndex(static_cast<int>(p)); qt_config->setArrayIndex(static_cast<int>(p));
const auto& map = Settings::values.touch_from_button_maps[p]; WriteSetting(QStringLiteral("name"),
WriteSetting(QStringLiteral("name"), QString::fromStdString(map.name), QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
QStringLiteral("default")); QStringLiteral("default"));
qt_config->beginWriteArray(QStringLiteral("entries")); qt_config->beginWriteArray(QStringLiteral("entries"));
for (std::size_t q = 0; q < map.buttons.size(); ++q) { for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
++q) {
qt_config->setArrayIndex(static_cast<int>(q)); qt_config->setArrayIndex(static_cast<int>(q));
WriteSetting(QStringLiteral("bind"), QString::fromStdString(map.buttons[q])); WriteSetting(
QStringLiteral("bind"),
QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
} }
qt_config->endArray(); qt_config->endArray();
} }
qt_config->endArray(); qt_config->endArray();
}
void Config::SaveControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
Settings::values.players.SetGlobal(!IsCustomConfig());
SavePlayerValues();
if (IsCustomConfig()) {
qt_config->endGroup();
return;
}
SaveMotionTouchValues();
qt_config->endGroup(); qt_config->endGroup();
} }
@ -1299,3 +1358,26 @@ void Config::Reload() {
void Config::Save() { void Config::Save() {
SaveValues(); SaveValues();
} }
void Config::ReadControlPlayerValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
ReadPlayerValues();
qt_config->endGroup();
}
void Config::SaveControlPlayerValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
SavePlayerValues();
qt_config->endGroup();
}
void Config::ClearControlPlayerValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
// If key is an empty string, all keys in the current group() are removed.
qt_config->remove(QString{});
qt_config->endGroup();
}
const std::string& Config::GetConfigFilePath() const {
return qt_config_loc;
}

View File

@ -15,7 +15,11 @@ class QSettings;
class Config { class Config {
public: public:
enum class ConfigType : u32 { GlobalConfig, PerGameConfig }; enum class ConfigType : u32 {
GlobalConfig,
PerGameConfig,
InputProfile,
};
explicit Config(const std::string& config_name = "qt-config", explicit Config(const std::string& config_name = "qt-config",
ConfigType config_type = ConfigType::GlobalConfig); ConfigType config_type = ConfigType::GlobalConfig);
@ -24,17 +28,28 @@ public:
void Reload(); void Reload();
void Save(); void Save();
// Used for input profiles
void ReadControlPlayerValues();
void SaveControlPlayerValues();
void ClearControlPlayerValues();
const std::string& GetConfigFilePath() const;
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 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, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
static const std::array<UISettings::Shortcut, 28> default_hotkeys; static const std::array<UISettings::Shortcut, 28> default_hotkeys;
static const std::array<int, 2> default_stick_mod;
private: private:
void Initialize(const std::string& config_name); void Initialize(const std::string& config_name);
bool IsCustomConfig();
void ReadValues(); void ReadValues();
void ReadPlayerValues();
void ReadMotionTouchValues();
void ReadControlValues();
void ReadAudioValues(); void ReadAudioValues();
void ReadCameraValues(); void ReadCameraValues();
void ReadControlValues();
void ReadCoreValues(); void ReadCoreValues();
void ReadDataStorageValues(); void ReadDataStorageValues();
void ReadDebuggingValues(); void ReadDebuggingValues();
@ -54,9 +69,11 @@ private:
void ReadVideoDumpingValues(); void ReadVideoDumpingValues();
void SaveValues(); void SaveValues();
void SavePlayerValues();
void SaveMotionTouchValues();
void SaveControlValues();
void SaveAudioValues(); void SaveAudioValues();
void SaveCameraValues(); void SaveCameraValues();
void SaveControlValues();
void SaveCoreValues(); void SaveCoreValues();
void SaveDataStorageValues(); void SaveDataStorageValues();
void SaveDebuggingValues(); void SaveDebuggingValues();

View File

@ -61,7 +61,6 @@ void ConfigureDialog::ApplyConfiguration() {
ui->generalTab->ApplyConfiguration(); ui->generalTab->ApplyConfiguration();
ui->systemTab->ApplyConfiguration(); ui->systemTab->ApplyConfiguration();
ui->inputTab->ApplyConfiguration(); ui->inputTab->ApplyConfiguration();
ui->inputTab->ApplyProfile();
ui->hotkeysTab->ApplyConfiguration(registry); ui->hotkeysTab->ApplyConfiguration(registry);
ui->graphicsTab->ApplyConfiguration(); ui->graphicsTab->ApplyConfiguration();
ui->enhancementsTab->ApplyConfiguration(); ui->enhancementsTab->ApplyConfiguration();

View File

@ -5,19 +5,26 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <QGridLayout>
#include <QInputDialog> #include <QInputDialog>
#include <QKeyEvent> #include <QKeyEvent>
#include <QLabel>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QSlider>
#include <QTimer> #include <QTimer>
#include <QWheelEvent>
#include "citra_qt/bootmanager.h"
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_input.h" #include "citra_qt/configuration/configure_input.h"
#include "citra_qt/configuration/configure_motion_touch.h" #include "citra_qt/configuration/configure_motion_touch.h"
#include "citra_qt/configuration/input_profiles.h"
#include "common/assert.h"
#include "common/param_package.h" #include "common/param_package.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h" #include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
#include "input_common/main.h"
#include "ui_configure_input.h" #include "ui_configure_input.h"
const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
@ -271,15 +278,10 @@ ConfigureInput::~ConfigureInput() {
void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem_) { void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem_) {
input_subsystem = input_subsystem_; input_subsystem = input_subsystem_;
emulated_controller = Core::System::GetInstance().HIDCore().GetEmulatedController(); emulated_controller = Core::System::GetInstance().HIDCore().GetEmulatedController();
profiles = std::make_unique<InputProfiles>();
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
for (const auto& profile : Settings::values.input_profiles) {
ui->profile->addItem(QString::fromStdString(profile.name));
}
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index);
button_map = { button_map = {
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonDpadUp, ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight,
@ -294,14 +296,12 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem_) {
ui->buttonCirclePadDown, ui->buttonCirclePadDown,
ui->buttonCirclePadLeft, ui->buttonCirclePadLeft,
ui->buttonCirclePadRight, ui->buttonCirclePadRight,
nullptr,
}, },
{ {
ui->buttonCStickUp, ui->buttonCStickUp,
ui->buttonCStickDown, ui->buttonCStickDown,
ui->buttonCStickLeft, ui->buttonCStickLeft,
ui->buttonCStickRight, ui->buttonCStickRight,
nullptr,
}, },
}}; }};
@ -624,13 +624,12 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem_) {
} }
}); });
// UpdateInputProfiles(); UpdateInputProfiles();
connect(ui->buttonProfilesNew, &QPushButton::clicked, this, &ConfigureInput::CreateProfile);
connect(ui->buttonProfilesNew, &QPushButton::clicked, this, &ConfigureInput::NewProfile);
connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile); connect(ui->buttonProfilesDelete, &QPushButton::clicked, this, &ConfigureInput::DeleteProfile);
// connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this, connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
// &ConfigureInput::LoadProfile); &ConfigureInput::LoadProfile);
// connect(ui->buttonProfilesSave, &QPushButton::clicked, this, &ConfigureInput::SaveProfile); connect(ui->buttonProfilesSave, &QPushButton::clicked, this, &ConfigureInput::SaveProfile);
connect(ui->buttonMotionTouch, &QPushButton::clicked, [this, input_subsystem_] { connect(ui->buttonMotionTouch, &QPushButton::clicked, [this, input_subsystem_] {
QDialog* motion_touch_dialog = new ConfigureMotionTouch(this, input_subsystem_); QDialog* motion_touch_dialog = new ConfigureMotionTouch(this, input_subsystem_);
@ -648,41 +647,6 @@ void ConfigureInput::ApplyConfiguration() {
emulated_controller->EnableConfiguration(); emulated_controller->EnableConfiguration();
} }
void ConfigureInput::ApplyProfile() {
Settings::values.current_input_profile_index = ui->profile->currentIndex();
}
void ConfigureInput::EmitInputKeysChanged() {
emit InputKeysChanged(GetUsedKeyboardKeys());
}
void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) {
hotkey_list = new_key_list;
}
QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
QList<QKeySequence> list;
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
const auto& button_param = buttons_param[button];
if (button_param.Get("engine", "") == "keyboard") {
list << QKeySequence(button_param.Get("code", 0));
}
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
const auto& analog_param = analogs_param[analog_id];
if (analog_param.Get("engine", "") == "analog_from_button") {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
const Common::ParamPackage sub_button{
analog_param.Get(analog_sub_buttons[sub_button_id], "")};
list << QKeySequence(sub_button.Get("code", 0));
}
}
}
return list;
}
void ConfigureInput::LoadConfiguration() { void ConfigureInput::LoadConfiguration() {
emulated_controller->ReloadFromSettings(); emulated_controller->ReloadFromSettings();
@ -813,6 +777,7 @@ void ConfigureInput::UpdateMappingWithDefaults() {
button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
Config::default_buttons[button_id])}); Config::default_buttons[button_id])});
} }
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
Common::ParamPackage analog_param{}; Common::ParamPackage analog_param{};
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
@ -821,8 +786,8 @@ void ConfigureInput::UpdateMappingWithDefaults() {
SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
} }
/* analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
Config::default_stick_mod[analog_id]));*/ Config::default_stick_mod[analog_id]));
emulated_controller->SetStickParam(analog_id, analog_param); emulated_controller->SetStickParam(analog_id, analog_param);
} }
@ -861,6 +826,7 @@ void ConfigureInput::HandleClick(QPushButton* button, std::size_t button_id,
if (timeout_timer->isActive()) { if (timeout_timer->isActive()) {
return; return;
} }
button->setText(tr("[waiting]"));
button->setFocus(); button->setFocus();
input_setter = std::move(new_input_setter); input_setter = std::move(new_input_setter);
@ -870,7 +836,7 @@ void ConfigureInput::HandleClick(QPushButton* button, std::size_t button_id,
QWidget::grabMouse(); QWidget::grabMouse();
QWidget::grabKeyboard(); QWidget::grabKeyboard();
timeout_timer->start(2500); // Cancel after 2.5 seconds timeout_timer->start(4000); // Cancel after 4 seconds
poll_timer->start(25); // Check for new inputs every 25ms poll_timer->start(25); // Check for new inputs every 25ms
} }
@ -913,30 +879,44 @@ void ConfigureInput::SetPollingResult(const Common::ParamPackage& params, bool a
input_setter = std::nullopt; input_setter = std::nullopt;
} }
void ConfigureInput::keyPressEvent(QKeyEvent* event) { void ConfigureInput::mousePressEvent(QMouseEvent* event) {
if (!input_setter || !event) if (!input_setter || !event) {
return; return;
if (event->key() != Qt::Key_Escape && event->key() != previous_key_code) {
if (want_keyboard_keys) {
// Check if key is already bound
if (hotkey_list.contains(QKeySequence(event->key())) ||
GetUsedKeyboardKeys().contains(QKeySequence(event->key()))) {
SetPollingResult({}, true);
QMessageBox::critical(this, tr("Error!"),
tr("You're using a key that's already bound."));
return;
}
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop
// polling
return;
}
} }
SetPollingResult({}, true);
previous_key_code = 0; const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
}
void ConfigureInput::wheelEvent(QWheelEvent* event) {
const int x = event->angleDelta().x();
const int y = event->angleDelta().y();
input_subsystem->GetMouse()->MouseWheelChange(x, y);
}
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
event->ignore();
if (event->key() != Qt::Key_Escape) {
input_subsystem->GetKeyboard()->PressKey(event->key());
}
}
bool ConfigureInput::eventFilter(QObject* object, QEvent* event) {
if (object == ui->comboDevices && event->type() == QEvent::MouseButtonPress) {
UpdateInputDeviceCombobox();
}
return object->eventFilter(object, event);
}
void ConfigureInput::changeEvent(QEvent* event) {
if (event->type() == QEvent::LanguageChange) {
RetranslateUI();
}
QWidget::changeEvent(event);
} }
void ConfigureInput::RetranslateUI() { void ConfigureInput::RetranslateUI() {
@ -1028,60 +1008,128 @@ void ConfigureInput::UpdateInputDeviceCombobox() {
} }
} }
void ConfigureInput::NewProfile() { void ConfigureInput::EmitInputKeysChanged() {
const QString name = emit InputKeysChanged(GetUsedKeyboardKeys());
QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile.")); }
if (name.isEmpty()) {
void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) {
hotkey_list = new_key_list;
}
QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() {
QList<QKeySequence> list;
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
const auto& button_param = emulated_controller->GetButtonParam(button_id);
if (button_param.Get("engine", "") == "keyboard") {
list << QKeySequence(button_param.Get("code", 0));
}
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
const auto& analog_param = emulated_controller->GetStickParam(analog_id);
if (analog_param.Get("engine", "") == "analog_from_button") {
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
const Common::ParamPackage sub_button{
analog_param.Get(analog_sub_buttons[sub_button_id], "")};
list << QKeySequence(sub_button.Get("code", 0));
}
}
}
return list;
}
void ConfigureInput::CreateProfile() {
const QString profile_name =
QInputDialog::getText(this, tr("New Profile"), tr("Enter a profile name:"));
if (profile_name.isEmpty()) {
return; return;
} }
if (IsProfileNameDuplicate(name)) {
WarnProposedProfileNameIsDuplicate(); if (!InputProfiles::IsProfileNameValid(profile_name.toStdString())) {
QMessageBox::critical(this, tr("Create Input Profile"),
tr("The given profile name is not valid!"));
return; return;
} }
ApplyConfiguration(); ApplyConfiguration();
Settings::SaveProfile(ui->profile->currentIndex());
Settings::CreateProfile(name.toStdString()); if (!profiles->CreateProfile(profile_name.toStdString())) {
ui->profile->addItem(name); QMessageBox::critical(this, tr("Create Input Profile"),
ui->profile->setCurrentIndex(Settings::values.current_input_profile_index); tr("Failed to create the input profile \"%1\"").arg(profile_name));
LoadConfiguration(); UpdateInputProfiles();
ui->buttonProfilesDelete->setEnabled(ui->profile->count() > 1); return;
}
UpdateInputProfiles();
ui->comboProfiles->addItem(profile_name);
ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
} }
void ConfigureInput::DeleteProfile() { void ConfigureInput::DeleteProfile() {
const auto answer = QMessageBox::question( const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->profile->currentText()));
if (answer != QMessageBox::Yes) { if (profile_name.isEmpty()) {
return; return;
} }
const int index = ui->profile->currentIndex();
ui->profile->removeItem(index); if (!profiles->DeleteProfile(profile_name.toStdString())) {
ui->profile->setCurrentIndex(0); QMessageBox::critical(this, tr("Delete Input Profile"),
Settings::DeleteProfile(index); tr("Failed to delete the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
return;
}
UpdateInputProfiles();
ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
ui->comboProfiles->setCurrentIndex(-1);
}
void ConfigureInput::LoadProfile() {
const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
if (profile_name.isEmpty()) {
return;
}
ApplyConfiguration();
if (!profiles->LoadProfile(profile_name.toStdString())) {
QMessageBox::critical(this, tr("Load Input Profile"),
tr("Failed to load the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
return;
}
LoadConfiguration(); LoadConfiguration();
ui->buttonProfilesDelete->setEnabled(ui->profile->count() > 1);
} }
void ConfigureInput::RenameProfile() { void ConfigureInput::SaveProfile() {
const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:")); const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
if (new_name.isEmpty()) {
return; if (profile_name.isEmpty()) {
}
if (IsProfileNameDuplicate(new_name)) {
WarnProposedProfileNameIsDuplicate();
return; return;
} }
ui->profile->setItemText(ui->profile->currentIndex(), new_name); ApplyConfiguration();
Settings::RenameCurrentProfile(new_name.toStdString());
Settings::SaveProfile(ui->profile->currentIndex()); if (!profiles->SaveProfile(profile_name.toStdString())) {
QMessageBox::critical(this, tr("Save Input Profile"),
tr("Failed to save the input profile \"%1\"").arg(profile_name));
UpdateInputProfiles();
return;
}
} }
bool ConfigureInput::IsProfileNameDuplicate(const QString& name) const { void ConfigureInput::UpdateInputProfiles() {
return ui->profile->findText(name, Qt::MatchFixedString | Qt::MatchCaseSensitive) != -1; ui->comboProfiles->clear();
}
void ConfigureInput::WarnProposedProfileNameIsDuplicate() { for (const auto& profile_name : profiles->GetInputProfileNames()) {
QMessageBox::warning(this, tr("Duplicate profile name"), ui->comboProfiles->addItem(QString::fromStdString(profile_name));
tr("Profile name already exists. Please choose a different name.")); }
ui->comboProfiles->setCurrentIndex(-1);
} }

View File

@ -9,29 +9,42 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
#include <unordered_map> #include <vector>
#include <QGroupBox>
#include <QKeySequence>
#include <QSpinBox>
#include <QWidget> #include <QWidget>
#include "common/param_package.h" #include "common/param_package.h"
#include "common/settings.h" #include "common/settings.h"
#include "input_common/main.h" #include "ui_configure_input.h"
class QCheckBox;
class QKeyEvent; class QKeyEvent;
class QLabel; class QLabel;
class QPushButton; class QPushButton;
class QSlider; class QSlider;
class QSpinBox;
class QString; class QString;
class QTimer; class QTimer;
class QWidget;
namespace Core::HID { class InputProfiles;
class EmulatedController;
} // namespace Core::HID namespace InputCommon {
class InputSubsystem;
}
namespace InputCommon::Polling {
enum class InputType;
} // namespace InputCommon::Polling
namespace Ui { namespace Ui {
class ConfigureInput; class ConfigureInput;
} } // namespace Ui
namespace Core::HID {
class HIDCore;
class EmulatedController;
} // namespace Core::HID
class ConfigureInput : public QWidget { class ConfigureInput : public QWidget {
Q_OBJECT Q_OBJECT
@ -40,18 +53,32 @@ public:
explicit ConfigureInput(QWidget* parent = nullptr); explicit ConfigureInput(QWidget* parent = nullptr);
~ConfigureInput() override; ~ConfigureInput() override;
/// Initializes the ConfigureInput dialog
void Initialize(InputCommon::InputSubsystem* input_subsystem_); void Initialize(InputCommon::InputSubsystem* input_subsystem_);
/// Save all button configurations to settings file
void ApplyConfiguration();
void RetranslateUI();
/// Load configuration settings. /// Load configuration settings.
void LoadConfiguration(); void LoadConfiguration();
/// Save all button configurations to settings file.
void ApplyConfiguration();
/// Update the input devices combobox.
void UpdateInputDeviceCombobox();
/// Updates the list of controller profiles.
void UpdateInputProfiles();
/// Restore all buttons to their default values.
void RestoreDefaults();
/// Clear all input configuration.
void ClearAll();
/// Updates UI with selected language translations
void RetranslateUI();
void EmitInputKeysChanged(); void EmitInputKeysChanged();
/// Save the current input profile index
void ApplyProfile();
public slots: public slots:
void OnHotkeysChanged(QList<QKeySequence> new_key_list); void OnHotkeysChanged(QList<QKeySequence> new_key_list);
@ -59,16 +86,65 @@ signals:
void InputKeysChanged(QList<QKeySequence> new_key_list); void InputKeysChanged(QList<QKeySequence> new_key_list);
private: private:
/// Generates list of all used keys
QList<QKeySequence> GetUsedKeyboardKeys();
QString ButtonToText(const Common::ParamPackage& param); QString ButtonToText(const Common::ParamPackage& param);
QString AnalogToText(const Common::ParamPackage& param, const std::string& dir); QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
void changeEvent(QEvent* event) override;
/// Called when the button was pressed.
void HandleClick(QPushButton* button, std::size_t button_id,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::InputType type);
/// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Checks whether a given input can be accepted.
bool IsInputAcceptable(const Common::ParamPackage& params) const;
/// Handle mouse button press events.
void mousePressEvent(QMouseEvent* event) override;
/// Handle mouse wheel move events.
void wheelEvent(QWheelEvent* event) override;
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
/// Handle combobox list refresh
bool eventFilter(QObject* object, QEvent* event) override;
/// Update UI to reflect current configuration. /// Update UI to reflect current configuration.
void UpdateUI(); void UpdateUI();
/// Update the available input devices.
void UpdateInputDevices();
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
/// Creates a controller profile.
void CreateProfile();
/// Deletes the selected controller profile.
void DeleteProfile();
/// Loads the selected controller profile.
void LoadProfile();
/// Saves the current controller configuration into a selected controller profile.
void SaveProfile();
std::unique_ptr<Ui::ConfigureInput> ui; std::unique_ptr<Ui::ConfigureInput> ui;
InputCommon::InputSubsystem* input_subsystem; InputCommon::InputSubsystem* input_subsystem;
std::unique_ptr<InputProfiles> profiles;
std::unique_ptr<QTimer> timeout_timer; std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer; std::unique_ptr<QTimer> poll_timer;
@ -77,23 +153,18 @@ private:
Core::HID::EmulatedController* emulated_controller; Core::HID::EmulatedController* emulated_controller;
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
/// Each button input is represented by a QPushButton. /// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
/// A group of five QPushButtons represent one analog input. The buttons each represent up, /// A group of four QPushButtons represent one analog input. The buttons each represent up,
/// down, left, right, and modifier, respectively. /// down, left, right, respectively.
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
analog_map_buttons; analog_map_buttons;
/// Analog inputs are also represented each with a single button, used to configure with an /// Each motion input is represented by a QPushButton.
/// actual analog stick std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider; std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
@ -104,8 +175,7 @@ private:
std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox; std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox; std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
/// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once. static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
bool map_analog_stick_accepted{};
/** /**
* List of keys currently registered to hotkeys. * List of keys currently registered to hotkeys.
@ -114,48 +184,10 @@ private:
*/ */
QList<QKeySequence> hotkey_list; QList<QKeySequence> hotkey_list;
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
/// keyboard events are ignored. bool map_analog_stick_accepted{};
bool want_keyboard_keys = false;
/// List of physical devices users can map with. If a SDL backed device is selected, then you
/// can use this device to get a default mapping.
std::vector<Common::ParamPackage> input_devices; std::vector<Common::ParamPackage> input_devices;
/// Generates list of all used keys
QList<QKeySequence> GetUsedKeyboardKeys();
/// Restore all buttons to their default values.
void RestoreDefaults();
/// Clear all input configuration
void ClearAll();
/// Gets the default controller mapping for this device and auto configures the input to match.
void UpdateMappingWithDefaults();
/// Called when the button was pressed.
void HandleClick(QPushButton* button, std::size_t button_id,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::InputType type);
/// The key code of the previous state of the key being currently bound.
int previous_key_code;
/// Checks whether a given input can be accepted.
bool IsInputAcceptable(const Common::ParamPackage& params) const;
/// Finish polling and configure input using the input_setter
void SetPollingResult(const Common::ParamPackage& params, bool abort);
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
void UpdateInputDevices();
void UpdateInputDeviceCombobox();
/// input profiles
void NewProfile();
void DeleteProfile();
void RenameProfile();
bool IsProfileNameDuplicate(const QString& name) const;
void WarnProposedProfileNameIsDuplicate();
}; };

View File

@ -68,7 +68,14 @@
<number>5</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QComboBox" name="profile"/> <widget class="QComboBox" name="comboProfiles"/>
</item>
<item>
<widget class="QPushButton" name="buttonProfilesSave">
<property name="text">
<string>Save</string>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="buttonProfilesNew"> <widget class="QPushButton" name="buttonProfilesNew">
@ -77,13 +84,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="buttonProfilesRename">
<property name="text">
<string>Rename</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="buttonProfilesDelete"> <widget class="QPushButton" name="buttonProfilesDelete">
<property name="text"> <property name="text">

View File

@ -120,37 +120,23 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
ConfigureMotionTouch::~ConfigureMotionTouch() = default; ConfigureMotionTouch::~ConfigureMotionTouch() = default;
void ConfigureMotionTouch::SetConfiguration() { void ConfigureMotionTouch::SetConfiguration() {
const Common::ParamPackage motion_param(Settings::values.current_input_profile.motion_device); const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
const Common::ParamPackage touch_param(Settings::values.current_input_profile.touch_device);
const std::string motion_engine = motion_param.Get("engine", "motion_emu");
const std::string touch_engine = touch_param.Get("engine", "emu_window");
ui->motion_provider->setCurrentIndex(
ui->motion_provider->findData(QString::fromStdString(motion_engine)));
ui->touch_provider->setCurrentIndex(
ui->touch_provider->findData(QString::fromStdString(touch_engine)));
ui->touch_from_button_checkbox->setChecked(
Settings::values.current_input_profile.use_touch_from_button);
touch_from_button_maps = Settings::values.touch_from_button_maps; touch_from_button_maps = Settings::values.touch_from_button_maps;
for (const auto& touch_map : touch_from_button_maps) { for (const auto& touch_map : touch_from_button_maps) {
ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
} }
ui->touch_from_button_map->setCurrentIndex( ui->touch_from_button_map->setCurrentIndex(
Settings::values.current_input_profile.touch_from_button_map_index); Settings::values.touch_from_button_map_index.GetValue());
ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
guid = motion_param.Get("guid", "0");
port = motion_param.Get("port", 0);
min_x = touch_param.Get("min_x", 100); min_x = touch_param.Get("min_x", 100);
min_y = touch_param.Get("min_y", 50); min_y = touch_param.Get("min_y", 50);
max_x = touch_param.Get("max_x", 1800); max_x = touch_param.Get("max_x", 1800);
max_y = touch_param.Get("max_y", 850); max_y = touch_param.Get("max_y", 850);
ui->udp_server->setText( ui->udp_server->setText(QString::fromStdString(Settings::values.udp_input_address.GetValue()));
QString::fromStdString(Settings::values.current_input_profile.udp_input_address)); ui->udp_port->setText(QString::number(Settings::values.udp_input_port.GetValue()));
ui->udp_port->setText(QString::number(Settings::values.current_input_profile.udp_input_port)); ui->udp_pad_index->setCurrentIndex(Settings::values.udp_pad_index.GetValue());
ui->udp_pad_index->setCurrentIndex(Settings::values.current_input_profile.udp_pad_index);
} }
void ConfigureMotionTouch::UpdateUiDisplay() { void ConfigureMotionTouch::UpdateUiDisplay() {
@ -326,19 +312,12 @@ void ConfigureMotionTouch::ApplyConfiguration() {
touch_param.Set("max_y", max_y); touch_param.Set("max_y", max_y);
} }
Settings::values.current_input_profile.motion_device = motion_param.Serialize(); Settings::values.touch_device = touch_param.Serialize();
Settings::values.current_input_profile.touch_device = touch_param.Serialize(); Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
Settings::values.current_input_profile.use_touch_from_button =
ui->touch_from_button_checkbox->isChecked();
Settings::values.current_input_profile.touch_from_button_map_index =
ui->touch_from_button_map->currentIndex();
Settings::values.touch_from_button_maps = touch_from_button_maps; Settings::values.touch_from_button_maps = touch_from_button_maps;
Settings::values.current_input_profile.udp_input_address = ui->udp_server->text().toStdString(); Settings::values.udp_input_address = ui->udp_server->text().toStdString();
Settings::values.current_input_profile.udp_input_port = Settings::values.udp_input_port = static_cast<u16>(ui->udp_port->text().toInt());
static_cast<u16>(ui->udp_port->text().toInt()); Settings::values.udp_pad_index = static_cast<u8>(ui->udp_pad_index->currentIndex());
Settings::values.current_input_profile.udp_pad_index =
static_cast<u8>(ui->udp_pad_index->currentIndex());
Settings::SaveProfile(Settings::values.current_input_profile_index);
input_subsystem->ReloadInputDevices(); input_subsystem->ReloadInputDevices();
accept(); accept();

View File

@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/input_profiles.h"
#include "common/common_paths.h"
#include "common/file_util.h"
namespace {
bool ProfileExistsInFilesystem(std::string_view profile_name) {
return FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "input" +
DIR_SEP + fmt::format("{}.ini", profile_name));
}
bool IsINI(const std::string& filename) {
return filename.ends_with(".ini");
}
std::string GetNameWithoutExtension(const std::string& filename) {
const auto last_dot = filename.find_last_of(".");
if (last_dot == std::string::npos) {
return filename;
}
return filename.substr(0, last_dot);
}
} // namespace
InputProfiles::InputProfiles() {
const auto input_profile_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "input";
if (!FileUtil::IsDirectory(input_profile_loc)) {
return;
}
FileUtil::ForeachDirectoryEntry(
nullptr, input_profile_loc,
[this](u64* num_entries_out, const std::string& directory, const std::string& filename) {
const auto name_without_ext = GetNameWithoutExtension(filename);
if (IsINI(filename) && IsProfileNameValid(name_without_ext)) {
map_profiles.insert_or_assign(
name_without_ext,
std::make_unique<Config>(name_without_ext, 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);
}
std::stable_sort(profile_names.begin(), profile_names.end());
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) {
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);
}
bool InputProfiles::DeleteProfile(const std::string& profile_name) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
if (!ProfileExistsInFilesystem(profile_name) ||
FileUtil::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) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
if (!ProfileExistsInFilesystem(profile_name)) {
map_profiles.erase(profile_name);
return false;
}
map_profiles[profile_name]->ReadControlPlayerValues();
return true;
}
bool InputProfiles::SaveProfile(const std::string& profile_name) {
if (!ProfileExistsInMap(profile_name)) {
return false;
}
map_profiles[profile_name]->SaveControlPlayerValues();
return true;
}
bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
return map_profiles.find(profile_name) != map_profiles.end();
}

View File

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <unordered_map>
namespace Core {
class System;
}
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);
bool DeleteProfile(const std::string& profile_name);
bool LoadProfile(const std::string& profile_name);
bool SaveProfile(const std::string& profile_name);
private:
bool ProfileExistsInMap(const std::string& profile_name) const;
std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
};

View File

@ -1966,17 +1966,17 @@ void GMainWindow::OnLoadState() {
} }
void GMainWindow::OnConfigure() { void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps;
game_list->SetDirectoryWatcherEnabled(false); game_list->SetDirectoryWatcherEnabled(false);
Settings::SetConfiguringGlobal(true); Settings::SetConfiguringGlobal(true);
ConfigureDialog configureDialog(this, hotkey_registry, input_subsystem.get(), ConfigureDialog configureDialog(this, hotkey_registry, input_subsystem.get(),
!multiplayer_state->IsHostingPublicRoom()); !multiplayer_state->IsHostingPublicRoom());
connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, connect(&configureDialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged); &GMainWindow::OnLanguageChanged);
auto old_theme = UISettings::values.theme;
const int old_input_profile_index = Settings::values.current_input_profile_index;
const auto old_input_profiles = Settings::values.input_profiles;
const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
auto result = configureDialog.exec(); auto result = configureDialog.exec();
game_list->SetDirectoryWatcherEnabled(true); game_list->SetDirectoryWatcherEnabled(true);
if (result == QDialog::Accepted) { if (result == QDialog::Accepted) {
@ -2001,9 +2001,7 @@ void GMainWindow::OnConfigure() {
UpdateSecondaryWindowVisibility(); UpdateSecondaryWindowVisibility();
UpdateBootHomeMenuState(); UpdateBootHomeMenuState();
} else { } else {
Settings::values.input_profiles = old_input_profiles;
Settings::values.touch_from_button_maps = old_touch_from_button_maps; Settings::values.touch_from_button_maps = old_touch_from_button_maps;
Settings::LoadProfile(old_input_profile_index);
} }
} }

View File

@ -112,7 +112,7 @@ bool CreateDir(const std::string& filename);
bool CreateFullPath(const std::string& fullPath); bool CreateFullPath(const std::string& fullPath);
// Deletes a given filename, return true on success // Deletes a given filename, return true on success
// Doesn't supports deleting a directory // Doesn't support deleting a directory
bool Delete(const std::string& filename); bool Delete(const std::string& filename);
// Deletes a directory filename, returns true on success // Deletes a directory filename, returns true on success

View File

@ -101,7 +101,6 @@ void Apply() {
Core::DSP().SetSink(values.output_type.GetValue(), values.output_device.GetValue()); Core::DSP().SetSink(values.output_type.GetValue(), values.output_device.GetValue());
Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue()); Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue());
<<<<<<< HEAD
auto hid = Service::HID::GetModule(system); auto hid = Service::HID::GetModule(system);
if (hid) { if (hid) {
hid->ReloadInputDevices(); hid->ReloadInputDevices();
@ -120,8 +119,6 @@ void Apply() {
if (ir_rst) if (ir_rst)
ir_rst->ReloadInputDevices(); ir_rst->ReloadInputDevices();
=======
>>>>>>> 811fc4f3a (Stash "DONE")
auto cam = Service::CAM::GetModule(system); auto cam = Service::CAM::GetModule(system);
if (cam) { if (cam) {
cam->ReloadCameraDevices(); cam->ReloadCameraDevices();
@ -255,31 +252,4 @@ void RestoreGlobalState(bool is_powered_on) {
values.preload_textures.SetGlobal(true); values.preload_textures.SetGlobal(true);
} }
void LoadProfile(int index) {
Settings::values.current_input_profile = Settings::values.input_profiles[index];
Settings::values.current_input_profile_index = index;
}
void SaveProfile(int index) {
Settings::values.input_profiles[index] = Settings::values.current_input_profile;
}
void CreateProfile(std::string name) {
Settings::InputProfile profile = values.current_input_profile;
profile.name = std::move(name);
Settings::values.input_profiles.push_back(std::move(profile));
Settings::values.current_input_profile_index =
static_cast<int>(Settings::values.input_profiles.size()) - 1;
Settings::LoadProfile(Settings::values.current_input_profile_index);
}
void DeleteProfile(int index) {
Settings::values.input_profiles.erase(Settings::values.input_profiles.begin() + index);
Settings::LoadProfile(0);
}
void RenameCurrentProfile(std::string new_name) {
Settings::values.current_input_profile.name = std::move(new_name);
}
} // namespace Settings } // namespace Settings

View File

@ -113,7 +113,7 @@ constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN; constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
static const std::array<const char*, NumButtons> mapping = {{ constexpr std::array<const char*, NumButtons> mapping = {{
"button_a", "button_a",
"button_b", "button_b",
"button_x", "button_x",
@ -132,13 +132,13 @@ static const std::array<const char*, NumButtons> mapping = {{
"button_zr", "button_zr",
"button_home", "button_home",
}}; }};
} // namespace NativeButton } // namespace NativeButton
namespace NativeAnalog { namespace NativeAnalog {
enum Values { enum Values {
CirclePad, CirclePad,
CStick, CStick,
NumAnalogs, NumAnalogs,
}; };
@ -160,9 +160,24 @@ constexpr int MOTION_HID_BEGIN = MotionLeft;
constexpr int MOTION_HID_END = NumMotions; constexpr int MOTION_HID_END = NumMotions;
constexpr int NUM_MOTIONS_HID = NumMotions; constexpr int NUM_MOTIONS_HID = NumMotions;
extern const std::array<const char*, NumMotions> mapping; constexpr std::array<const char*, NumMotions> mapping = {{
"motionleft",
"motionright",
}};
} // namespace NativeMotion } // namespace NativeMotion
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
struct PlayerInput {
ButtonsRaw buttons;
AnalogsRaw analogs;
MotionsRaw motions;
std::string profile_name;
};
/** The Setting class is a simple resource manager. It defines a label and default value alongside /** The Setting class is a simple resource manager. It defines a label and default value alongside
* the actual value of the setting for simpler and less-error prone use with frontend * the actual value of the setting for simpler and less-error prone use with frontend
* configurations. Specifying a default value and label is required. A minimum and maximum range can * configurations. Specifying a default value and label is required. A minimum and maximum range can
@ -393,18 +408,36 @@ protected:
Type custom{}; ///< The custom value of the setting Type custom{}; ///< The custom value of the setting
}; };
struct InputProfile { /**
std::string name; * The InputSetting class allows for getting a reference to either the global or custom members.
std::array<std::string, NativeButton::NumButtons> buttons; * This is required as we cannot easily modify the values of user-defined types within containers
std::array<std::string, NativeAnalog::NumAnalogs> analogs; * using the SetValue() member function found in the Setting class. The primary purpose of this
std::array<std::string, NativeMotion::NumMotions> motions; * class is to store an array of 10 PlayerInput structs for both the global and custom setting and
std::string motion_device; * allows for easily accessing and modifying both settings.
std::string touch_device; */
bool use_touch_from_button; template <typename Type>
int touch_from_button_map_index; class InputSetting final {
std::string udp_input_address; public:
u16 udp_input_port; InputSetting() = default;
u8 udp_pad_index; explicit InputSetting(Type val) : Setting<Type>(val) {}
~InputSetting() = default;
void SetGlobal(bool to_global) {
use_global = to_global;
}
[[nodiscard]] bool UsingGlobal() const {
return use_global;
}
[[nodiscard]] Type& GetValue(bool need_global = false) {
if (use_global || need_global) {
return global;
}
return custom;
}
private:
bool use_global{true}; ///< The setting's global state
Type global{}; ///< The setting
Type custom{}; ///< The custom setting value
}; };
struct TouchFromButtonMap { struct TouchFromButtonMap {
@ -418,11 +451,16 @@ static constexpr s32 REGION_VALUE_AUTO_SELECT = -1;
struct Values { struct Values {
// Controls // Controls
InputProfile current_input_profile; ///< The current input profile InputSetting<PlayerInput> players;
int current_input_profile_index; ///< The current input profile index
std::vector<InputProfile> input_profiles; ///< The list of input profiles Setting<std::string> touch_device{"min_x:100,min_y:50,max_x:1800,max_y:850", "touch_device"};
Setting<int> touch_from_button_map_index{0, "touch_from_button_map"};
std::vector<TouchFromButtonMap> touch_from_button_maps; std::vector<TouchFromButtonMap> touch_from_button_maps;
Setting<std::string> udp_input_address{"127.0.0.1", "udp_input_address"};
Setting<u16> udp_input_port{26760, "udp_input_port"};
Setting<u8> udp_pad_index{0, "udp_pad_index"};
// Core // Core
Setting<bool> use_cpu_jit{true, "use_cpu_jit"}; Setting<bool> use_cpu_jit{true, "use_cpu_jit"};
SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"}; SwitchableSetting<s32, true> cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"};
@ -541,11 +579,4 @@ void LogSettings();
// Restore the global state of all applicable settings in the Values struct // Restore the global state of all applicable settings in the Values struct
void RestoreGlobalState(bool is_powered_on); void RestoreGlobalState(bool is_powered_on);
// Input profiles
void LoadProfile(int index);
void SaveProfile(int index);
void CreateProfile(std::string name);
void DeleteProfile(int index);
void RenameCurrentProfile(std::string new_name);
} // namespace Settings } // namespace Settings

View File

@ -280,7 +280,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
auto n3ds_mode = app_loader->LoadKernelN3dsMode(); auto n3ds_mode = app_loader->LoadKernelN3dsMode();
ASSERT(n3ds_mode.first); ASSERT(n3ds_mode.first);
u32 num_cores = 2; u32 num_cores = 2;
if (Settings::values.is_new_3ds) { if (Settings::values.is_new_3ds.GetValue()) {
num_cores = 4; num_cores = 4;
} }
ResultStatus init_result{ ResultStatus init_result{

View File

@ -12,7 +12,7 @@ EmulatedConsole::~EmulatedConsole() = default;
void EmulatedConsole::ReloadFromSettings() { void EmulatedConsole::ReloadFromSettings() {
// Using first motion device from player 1. No need to assign any unique config at the moment // Using first motion device from player 1. No need to assign any unique config at the moment
const auto& player = Settings::values.current_input_profile; const auto& player = Settings::values.players.GetValue();
motion_params = Common::ParamPackage(player.motions[0]); motion_params = Common::ParamPackage(player.motions[0]);
ReloadInput(); ReloadInput();
@ -42,7 +42,7 @@ void EmulatedConsole::SetTouchParams() {
} }
const auto button_index = const auto button_index =
static_cast<u64>(Settings::values.current_input_profile.touch_from_button_map_index); static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons; const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
// Map the rest of the fingers from touch from button configuration // Map the rest of the fingers from touch from button configuration

View File

@ -168,7 +168,7 @@ void EmulatedController::ReloadInput() {
} }
void EmulatedController::ReloadFromSettings() { void EmulatedController::ReloadFromSettings() {
const auto& player = Settings::values.current_input_profile; const auto& player = Settings::values.players.GetValue();
for (std::size_t i = 0; i < player.buttons.size(); i++) { for (std::size_t i = 0; i < player.buttons.size(); i++) {
button_params[i] = Common::ParamPackage(player.buttons[i]); button_params[i] = Common::ParamPackage(player.buttons[i]);
} }
@ -183,7 +183,7 @@ void EmulatedController::ReloadFromSettings() {
} }
void EmulatedController::SaveCurrentConfig() { void EmulatedController::SaveCurrentConfig() {
auto& player = Settings::values.current_input_profile; auto& player = Settings::values.players.GetValue();
for (std::size_t index = 0; index < player.buttons.size(); ++index) { for (std::size_t index = 0; index < player.buttons.size(); ++index) {
player.buttons[index] = button_params[index].Serialize(); player.buttons[index] = button_params[index].Serialize();
} }

View File

@ -151,10 +151,10 @@ UDPClient::ClientConnection::~ClientConnection() = default;
void UDPClient::ReloadSockets() { void UDPClient::ReloadSockets() {
Reset(); Reset();
const std::string& host = Settings::values.current_input_profile.udp_input_address; const std::string& host = Settings::values.udp_input_address.GetValue();
const u16 udp_input_port = Settings::values.current_input_profile.udp_input_port; const u16 udp_input_port = Settings::values.udp_input_port.GetValue();
// TODO: Hook it up again // TODO: Hook it up again!!!!!!!!!
const u8 pad_index = Settings::values.current_input_profile.udp_pad_index; const u8 pad_index = Settings::values.udp_pad_index.GetValue();
const std::size_t client_number = GetClientNumber(host, udp_input_port); const std::size_t client_number = GetClientNumber(host, udp_input_port);
if (client_number != MAX_UDP_CLIENTS) { if (client_number != MAX_UDP_CLIENTS) {