diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 0cfbb0d57..f3d9fcfc9 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -198,6 +198,8 @@ set(COMMON_SRC_FILES FpUtils.h FrameDump.cpp FrameDump.h + InputConfig.cpp + InputConfig.h GenericMipsExecutor.h gs/GsCachedArea.cpp gs/GsCachedArea.h diff --git a/Source/InputConfig.cpp b/Source/InputConfig.cpp new file mode 100644 index 000000000..9aa5507f3 --- /dev/null +++ b/Source/InputConfig.cpp @@ -0,0 +1,46 @@ +#include "AppConfig.h" +#include "InputConfig.h" +#include "PathUtils.h" + +#define PROFILE_PATH ("inputprofiles") + +CInputConfig::CInputConfig(const Framework::CConfig::PathType& path) + : CConfig(path) +{ +} + +bool CInputConfig::IsValidProfileName(std::string name) +{ + static const std::string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"; + for(auto c : name) + { + if(valid_chars.find(c) == std::string::npos) + { + return false; + } + } + return true; +} + +std::unique_ptr CInputConfig::LoadProfile(std::string name) +{ + auto path = GetProfilePath() / name; + path.replace_extension(".xml"); + return std::make_unique(path); +} + +Framework::CConfig::PathType CInputConfig::GetProfilePath() +{ + auto profile_path = CAppConfig::GetBasePath() / PROFILE_PATH; + Framework::PathUtils::EnsurePathExists(profile_path); + return profile_path; +} + +Framework::CConfig::PathType CInputConfig::GetProfile(std::string name) +{ + auto profile_path = CAppConfig::GetBasePath() / PROFILE_PATH; + Framework::PathUtils::EnsurePathExists(profile_path); + profile_path /= name; + profile_path.replace_extension(".xml"); + return profile_path; +} diff --git a/Source/InputConfig.h b/Source/InputConfig.h new file mode 100644 index 000000000..93a6d8c95 --- /dev/null +++ b/Source/InputConfig.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Config.h" + +#define DEFAULT_PROFILE ("default") + +class CInputConfig : public Framework::CConfig +{ +public: + CInputConfig(const Framework::CConfig::PathType& path); + virtual ~CInputConfig() = default; + + static bool IsValidProfileName(std::string); + static CConfig::PathType GetProfilePath(); + static CConfig::PathType GetProfile(std::string = DEFAULT_PROFILE); + static std::unique_ptr LoadProfile(std::string = DEFAULT_PROFILE); +}; diff --git a/Source/input/InputBindingManager.cpp b/Source/input/InputBindingManager.cpp index 726e802d2..c101c73d8 100644 --- a/Source/input/InputBindingManager.cpp +++ b/Source/input/InputBindingManager.cpp @@ -98,23 +98,9 @@ static void SaveBindingTargetPreference(Framework::CConfig& config, const char* } CInputBindingManager::CInputBindingManager() - : m_config(CAppConfig::GetInstance()) { - for(unsigned int pad = 0; pad < MAX_PADS; pad++) - { - for(unsigned int button = 0; button < PS2::CControllerInfo::MAX_BUTTONS; button++) - { - auto prefBase = Framework::CConfig::MakePreferenceName(CONFIG_PREFIX, m_padPreferenceName[pad], PS2::CControllerInfo::m_buttonName[button]); - m_config.RegisterPreferenceInteger(Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDING_TYPE).c_str(), 0); - RegisterBindingTargetPreference(m_config, Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDINGTARGET1).c_str()); - if(PS2::CControllerInfo::IsAxis(static_cast(button))) - { - RegisterBindingTargetPreference(m_config, Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDINGTARGET2).c_str()); - } - CPovHatBinding::RegisterPreferences(m_config, prefBase.c_str()); - } - } - Load(); + m_config = CInputConfig::LoadProfile(); + Reload(); } bool CInputBindingManager::HasBindings() const @@ -174,15 +160,31 @@ void CInputBindingManager::OnInputEventReceived(const BINDINGTARGET& target, uin } } -void CInputBindingManager::Load() +void CInputBindingManager::Reload() { + + for(unsigned int pad = 0; pad < MAX_PADS; pad++) + { + for(unsigned int button = 0; button < PS2::CControllerInfo::MAX_BUTTONS; button++) + { + auto prefBase = Framework::CConfig::MakePreferenceName(CONFIG_PREFIX, m_padPreferenceName[pad], PS2::CControllerInfo::m_buttonName[button]); + m_config->RegisterPreferenceInteger(Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDING_TYPE).c_str(), 0); + RegisterBindingTargetPreference(*m_config, Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDINGTARGET1).c_str()); + if(PS2::CControllerInfo::IsAxis(static_cast(button))) + { + RegisterBindingTargetPreference(*m_config, Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDINGTARGET2).c_str()); + } + CPovHatBinding::RegisterPreferences(*m_config, prefBase.c_str()); + } + } + for(unsigned int pad = 0; pad < MAX_PADS; pad++) { for(unsigned int button = 0; button < PS2::CControllerInfo::MAX_BUTTONS; button++) { BINDINGTYPE bindingType = BINDING_UNBOUND; auto prefBase = Framework::CConfig::MakePreferenceName(CONFIG_PREFIX, m_padPreferenceName[pad], PS2::CControllerInfo::m_buttonName[button]); - bindingType = static_cast(m_config.GetPreferenceInteger((prefBase + "." + std::string(CONFIG_BINDING_TYPE)).c_str())); + bindingType = static_cast(m_config->GetPreferenceInteger((prefBase + "." + std::string(CONFIG_BINDING_TYPE)).c_str())); if(bindingType == BINDING_UNBOUND) continue; BindingPtr binding; switch(bindingType) @@ -199,7 +201,7 @@ void CInputBindingManager::Load() } if(binding) { - binding->Load(m_config, prefBase.c_str()); + binding->Load(*m_config, prefBase.c_str()); } m_bindings[pad][button] = binding; } @@ -207,6 +209,12 @@ void CInputBindingManager::Load() ResetBindingValues(); } +void CInputBindingManager::Load(std::string profile) +{ + m_config = CInputConfig::LoadProfile(profile); + Reload(); +} + void CInputBindingManager::Save() { for(unsigned int pad = 0; pad < MAX_PADS; pad++) @@ -216,11 +224,11 @@ void CInputBindingManager::Save() const auto& binding = m_bindings[pad][button]; if(!binding) continue; auto prefBase = Framework::CConfig::MakePreferenceName(CONFIG_PREFIX, m_padPreferenceName[pad], PS2::CControllerInfo::m_buttonName[button]); - m_config.SetPreferenceInteger(Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDING_TYPE).c_str(), binding->GetBindingType()); - binding->Save(m_config, prefBase.c_str()); + m_config->SetPreferenceInteger(Framework::CConfig::MakePreferenceName(prefBase, CONFIG_BINDING_TYPE).c_str(), binding->GetBindingType()); + binding->Save(*m_config, prefBase.c_str()); } } - m_config.Save(); + m_config->Save(); } const CInputBindingManager::CBinding* CInputBindingManager::GetBinding(uint32 pad, PS2::CControllerInfo::BUTTON button) const diff --git a/Source/input/InputBindingManager.h b/Source/input/InputBindingManager.h index 00199c97a..1b705a1dc 100644 --- a/Source/input/InputBindingManager.h +++ b/Source/input/InputBindingManager.h @@ -1,11 +1,13 @@ #pragma once #include "Config.h" +#include "InputConfig.h" #include "ControllerInfo.h" #include "InputProvider.h" #include #include #include +#include class CInputBindingManager { @@ -60,7 +62,8 @@ public: void SetPovHatBinding(uint32, PS2::CControllerInfo::BUTTON, const BINDINGTARGET&, uint32); void SetSimulatedAxisBinding(uint32, PS2::CControllerInfo::BUTTON, const BINDINGTARGET&, const BINDINGTARGET&); - void Load(); + void Reload(); + void Load(std::string); void Save(); private: @@ -149,6 +152,6 @@ private: static uint32 m_buttonDefaultValue[PS2::CControllerInfo::MAX_BUTTONS]; static const char* m_padPreferenceName[MAX_PADS]; - Framework::CConfig& m_config; + std::unique_ptr m_config; ProviderMap m_providers; }; diff --git a/Source/ui_qt/ControllerConfig/controllerconfigdialog.cpp b/Source/ui_qt/ControllerConfig/controllerconfigdialog.cpp index b7e41d44a..b40deb328 100644 --- a/Source/ui_qt/ControllerConfig/controllerconfigdialog.cpp +++ b/Source/ui_qt/ControllerConfig/controllerconfigdialog.cpp @@ -6,10 +6,18 @@ #include #include #include +#include +#include "AppConfig.h" +#include "PreferenceDefs.h" +#include "PathUtils.h" #include "bindingmodel.h" #include "ControllerInfo.h" #include "inputeventselectiondialog.h" +#include "QStringUtils.h" + +#include "filesystem_def.h" +#include ControllerConfigDialog::ControllerConfigDialog(CInputBindingManager* inputBindingManager, CInputProviderQtKey* qtKeyInputProvider, QWidget* parent) : QDialog(parent) @@ -18,7 +26,23 @@ ControllerConfigDialog::ControllerConfigDialog(CInputBindingManager* inputBindin , m_qtKeyInputProvider(qtKeyInputProvider) { ui->setupUi(this); + std::string profile = CAppConfig::GetInstance().GetPreferenceString(PREF_INPUT_PAD1_PROFILE); + PrepareBindingsView(); + + auto path = CInputConfig::GetProfilePath(); + if(fs::is_directory(path)) + { + for(auto& entry : fs::directory_iterator(path)) + { + auto profile = Framework::PathUtils::GetNativeStringFromPath(entry.path().stem()); + ui->comboBox->addItem(profile.c_str()); + } + } + + auto index = ui->comboBox->findText(profile.c_str()); + if(index >= 0) + ui->comboBox->setCurrentIndex(index); } ControllerConfigDialog::~ControllerConfigDialog() @@ -43,10 +67,12 @@ void ControllerConfigDialog::on_buttonBox_clicked(QAbstractButton* button) if(button == ui->buttonBox->button(QDialogButtonBox::Ok)) { m_inputManager->Save(); + CAppConfig::GetInstance().Save(); } else if(button == ui->buttonBox->button(QDialogButtonBox::Apply)) { m_inputManager->Save(); + CAppConfig::GetInstance().Save(); } else if(button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { @@ -60,7 +86,7 @@ void ControllerConfigDialog::on_buttonBox_clicked(QAbstractButton* button) } else if(button == ui->buttonBox->button(QDialogButtonBox::Cancel)) { - m_inputManager->Load(); + m_inputManager->Reload(); } } @@ -124,3 +150,58 @@ int ControllerConfigDialog::OpenBindConfigDialog(int index) auto res = IESD.exec(); return res; } + +void ControllerConfigDialog::on_comboBox_currentIndexChanged(int index) +{ + ui->delProfileButton->setEnabled(index > 0); + + auto profile = ui->comboBox->itemText(index).toStdString(); + m_inputManager->Load(profile.c_str()); + CAppConfig::GetInstance().SetPreferenceString(PREF_INPUT_PAD1_PROFILE, profile.c_str()); + + static_cast(ui->tableView->model())->Refresh(); +} + +void ControllerConfigDialog::on_addProfileButton_clicked() +{ + std::string profile_name; + while(profile_name.empty()) + { + bool ok_pressed = false; + QString name = QInputDialog::getText(this, tr("Enter Profile Name"), tr("Only letters, numbers and dash characters allowed.\nProfile name:"), + QLineEdit::Normal, "", &ok_pressed); + + if(!ok_pressed) + return; + + if(!name.isEmpty() && CInputConfig::IsValidProfileName(name.toStdString())) + profile_name = name.toStdString(); + } + + { + auto profile_path = CInputConfig::GetProfile(profile_name); + if(!fs::exists(profile_path)) + { + ui->comboBox->addItem(profile_name.c_str()); + } + + auto index = ui->comboBox->findText(profile_name.c_str()); + if(index >= 0) + ui->comboBox->setCurrentIndex(index); + } +} + +void ControllerConfigDialog::on_delProfileButton_clicked() +{ + auto name = ui->comboBox->currentText(); + std::string profile_name = name.toStdString(); + { + auto profile_path = CInputConfig::GetProfile(profile_name); + if(fs::exists(profile_path)) + { + fs::remove(profile_path); + int index = ui->comboBox->currentIndex(); + ui->comboBox->removeItem(index); + } + } +} \ No newline at end of file diff --git a/Source/ui_qt/ControllerConfig/controllerconfigdialog.h b/Source/ui_qt/ControllerConfig/controllerconfigdialog.h index e15d48152..b8f8cb118 100644 --- a/Source/ui_qt/ControllerConfig/controllerconfigdialog.h +++ b/Source/ui_qt/ControllerConfig/controllerconfigdialog.h @@ -27,6 +27,10 @@ private slots: void on_tableView_doubleClicked(const QModelIndex& index); void on_ConfigAllButton_clicked(); + void on_comboBox_currentIndexChanged(int index); + void on_addProfileButton_clicked(); + void on_delProfileButton_clicked(); + private: void PrepareBindingsView(); int OpenBindConfigDialog(int index); diff --git a/Source/ui_qt/PreferenceDefs.h b/Source/ui_qt/PreferenceDefs.h index 4a2429c57..846f322b1 100644 --- a/Source/ui_qt/PreferenceDefs.h +++ b/Source/ui_qt/PreferenceDefs.h @@ -3,3 +3,4 @@ #define PREFERENCE_AUDIO_ENABLEOUTPUT "audio.enableoutput" #define PREF_UI_PAUSEWHENFOCUSLOST "ui.pausewhenfocuslost" #define PREF_VIDEO_GS_HANDLER "video.gshandler" +#define PREF_INPUT_PAD1_PROFILE "input.pad1.profile" diff --git a/Source/ui_qt/Qt_ui/controllerconfigdialog.ui b/Source/ui_qt/Qt_ui/controllerconfigdialog.ui index 431c7e343..2db3e872c 100644 --- a/Source/ui_qt/Qt_ui/controllerconfigdialog.ui +++ b/Source/ui_qt/Qt_ui/controllerconfigdialog.ui @@ -26,26 +26,6 @@ Controller Manager - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults - - - false - - - - - - - Config All - - - @@ -83,6 +63,53 @@ QGroupBox::title { + + + + Config All + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + false + + + + + + + Profile(s) + + + + + + + + + + Add + + + + + + + Remove + + + + + + diff --git a/Source/ui_qt/mainwindow.cpp b/Source/ui_qt/mainwindow.cpp index 07ede498b..83bd43efd 100644 --- a/Source/ui_qt/mainwindow.cpp +++ b/Source/ui_qt/mainwindow.cpp @@ -150,7 +150,10 @@ void MainWindow::InitVirtualMachine() { m_virtualMachine->CreatePadHandler(CPH_GenericInput::GetFactoryFunction()); auto padHandler = static_cast(m_virtualMachine->GetPadHandler()); + auto profile = CAppConfig::GetInstance().GetPreferenceString(PREF_INPUT_PAD1_PROFILE); + auto& bindingManager = padHandler->GetBindingManager(); + bindingManager.Load(profile); //Create QtKeyInputProvider m_qtKeyInputProvider = std::make_shared(); @@ -642,6 +645,7 @@ void MainWindow::RegisterPreferences() CAppConfig::GetInstance().RegisterPreferenceBoolean(PREFERENCE_AUDIO_ENABLEOUTPUT, true); CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_UI_PAUSEWHENFOCUSLOST, true); CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_VIDEO_GS_HANDLER, SettingsDialog::GS_HANDLERS::OPENGL); + CAppConfig::GetInstance().RegisterPreferenceString(PREF_INPUT_PAD1_PROFILE, "default"); } void MainWindow::focusOutEvent(QFocusEvent* event)