FrontendCommon: Add input profile support

This commit is contained in:
Connor McLaughlin 2020-04-15 01:44:04 +10:00
parent 193763b4aa
commit f75ea052a6
3 changed files with 162 additions and 0 deletions

View File

@ -694,6 +694,7 @@ void HostInterface::InitializeUserDirectory()
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump/audio").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("inputprofiles").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("screenshots").c_str(), false);
result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("memcards").c_str(), false);

View File

@ -13,6 +13,7 @@
#include "sdl_audio_stream.h"
#include "sdl_controller_interface.h"
#endif
#include "ini_settings_interface.h"
#include <cstdio>
#include <cstring>
Log_SetChannel(CommonHostInterface);
@ -794,3 +795,153 @@ void CommonHostInterface::RegisterSaveStateHotkeys()
}
}
}
std::vector<std::pair<std::string, std::string>> CommonHostInterface::GetInputProfileList() const
{
FileSystem::FindResultsArray results;
FileSystem::FindFiles(GetUserDirectoryRelativePath("inputprofiles").c_str(), "*.ini",
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
std::vector<std::pair<std::string, std::string>> profile_names;
profile_names.reserve(results.size());
for (auto& it : results)
{
if (it.FileName.size() < 4)
continue;
std::string profile_name = it.FileName.substr(0, it.FileName.length() - 4);
std::string full_filename = GetUserDirectoryRelativePath("inputprofiles/%s", it.FileName.c_str());
profile_names.emplace_back(std::move(profile_name), std::move(full_filename));
}
return profile_names;
}
void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si)
{
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
{
const ControllerType ctype = m_settings.controller_types[controller_index - 1];
if (ctype == ControllerType::None)
continue;
const auto section_name = TinyString::FromFormat("Controller%u", controller_index);
si.DeleteValue(section_name, "Type");
for (const auto& button : Controller::GetButtonNames(ctype))
si.DeleteValue(section_name, button.first.c_str());
for (const auto& axis : Controller::GetAxisNames(ctype))
si.DeleteValue(section_name, axis.first.c_str());
if (Controller::GetVibrationMotorCount(ctype) > 0)
si.DeleteValue(section_name, "Rumble");
}
}
void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsInterface& si)
{
// clear bindings for all controllers
ClearAllControllerBindings(si);
INISettingsInterface profile(profile_path);
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
{
const auto section_name = TinyString::FromFormat("Controller%u", controller_index);
const std::string ctype_str = profile.GetStringValue(section_name, "Type");
if (ctype_str.empty())
continue;
std::optional<ControllerType> ctype = Settings::ParseControllerTypeName(ctype_str.c_str());
if (!ctype)
{
Log_ErrorPrintf("Invalid controller type in profile: '%s'", ctype_str.c_str());
return;
}
m_settings.controller_types[controller_index - 1] = *ctype;
HostInterface::OnControllerTypeChanged(controller_index - 1);
si.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(*ctype));
for (const auto& button : Controller::GetButtonNames(*ctype))
{
const auto key_name = TinyString::FromFormat("Button%s", button.first.c_str());
si.DeleteValue(section_name, key_name);
const std::vector<std::string> bindings = profile.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
si.AddToStringList(section_name, key_name, binding.c_str());
}
for (const auto& axis : Controller::GetAxisNames(*ctype))
{
const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str());
si.DeleteValue(section_name, axis.first.c_str());
const std::vector<std::string> bindings = profile.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
si.AddToStringList(section_name, key_name, binding.c_str());
}
si.DeleteValue(section_name, "Rumble");
const std::string rumble_value = profile.GetStringValue(section_name, "Rumble");
if (!rumble_value.empty())
si.SetStringValue(section_name, "Rumble", rumble_value.c_str());
}
UpdateInputMap(si);
if (m_system)
m_system->UpdateControllers();
ReportFormattedMessage("Loaded input profile from '%s'", profile_path);
}
bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInterface& si)
{
if (FileSystem::FileExists(profile_path))
{
if (!FileSystem::DeleteFile(profile_path))
{
Log_ErrorPrintf("Failed to delete existing input profile '%s' when saving", profile_path);
return false;
}
}
INISettingsInterface profile(profile_path);
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
{
const ControllerType ctype = m_settings.controller_types[controller_index - 1];
if (ctype == ControllerType::None)
continue;
const auto section_name = TinyString::FromFormat("Controller%u", controller_index);
profile.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(ctype));
for (const auto& button : Controller::GetButtonNames(ctype))
{
const auto key_name = TinyString::FromFormat("Button%s", button.first.c_str());
const std::vector<std::string> bindings = si.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
profile.AddToStringList(section_name, key_name, binding.c_str());
}
for (const auto& axis : Controller::GetAxisNames(ctype))
{
const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str());
const std::vector<std::string> bindings = si.GetStringList(section_name, key_name);
for (const std::string& binding : bindings)
profile.AddToStringList(section_name, key_name, binding.c_str());
}
const std::string rumble_value = si.GetStringValue(section_name, "Rumble");
if (!rumble_value.empty())
profile.SetStringValue(section_name, "Rumble", rumble_value.c_str());
}
profile.Save();
return true;
}

View File

@ -86,6 +86,15 @@ protected:
/// Reloads the input map from config. Callable from controller interface.
virtual void UpdateInputMap() = 0;
/// Returns a list of all input profiles. first - name, second - path
std::vector<std::pair<std::string, std::string>> GetInputProfileList() const;
/// Applies the specified input profile.
void ApplyInputProfile(const char* profile_path, SettingsInterface& si);
/// Saves the current input configuration to the specified profile name.
bool SaveInputProfile(const char* profile_path, SettingsInterface& si);
void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler);
bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
void UpdateInputMap(SettingsInterface& si);
@ -102,6 +111,7 @@ private:
void RegisterSaveStateHotkeys();
void UpdateControllerInputMap(SettingsInterface& si);
void UpdateHotkeyInputMap(SettingsInterface& si);
void ClearAllControllerBindings(SettingsInterface& si);
HotkeyInfoList m_hotkeys;