HostInterface: Support per-controller-type settings

This commit is contained in:
Connor McLaughlin 2020-07-01 00:33:45 +10:00
parent f3b2953e40
commit 4dc9e10777
17 changed files with 247 additions and 7 deletions

View file

@ -1,6 +1,7 @@
#include "analog_controller.h" #include "analog_controller.h"
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "common/string_util.h"
#include "host_interface.h" #include "host_interface.h"
#include "system.h" #include "system.h"
Log_SetChannel(AnalogController); Log_SetChannel(AnalogController);
@ -23,6 +24,9 @@ void AnalogController::Reset()
m_rumble_unlocked = false; m_rumble_unlocked = false;
m_configuration_mode = false; m_configuration_mode = false;
m_command_param = 0; m_command_param = 0;
if (m_auto_enable_analog)
SetAnalogMode(true);
} }
bool AnalogController::DoState(StateWrapper& sw) bool AnalogController::DoState(StateWrapper& sw)
@ -474,3 +478,18 @@ u32 AnalogController::StaticGetVibrationMotorCount()
{ {
return NUM_MOTORS; return NUM_MOTORS;
} }
Controller::SettingList AnalogController::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Boolean, "AutoEnableAnalog", "Enable Analog Mode on Reset",
"Automatically enables analog mode when the console is reset/powered on.", "false"}}};
return SettingList(settings.begin(), settings.end());
}
void AnalogController::LoadSettings(HostInterface* host_interface, const char* section)
{
Controller::LoadSettings(host_interface, section);
m_auto_enable_analog = host_interface->GetBooleanSettingValue(section, "AutoEnableAnalog", false);
}

View file

@ -50,6 +50,7 @@ public:
static AxisList StaticGetAxisNames(); static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames(); static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount(); static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override; ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override; std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
@ -70,6 +71,8 @@ public:
u32 GetVibrationMotorCount() const override; u32 GetVibrationMotorCount() const override;
float GetVibrationMotorStrength(u32 motor) override; float GetVibrationMotorStrength(u32 motor) override;
void LoadSettings(HostInterface* host_interface, const char* section) override;
private: private:
using MotorState = std::array<u8, NUM_MOTORS>; using MotorState = std::array<u8, NUM_MOTORS>;
@ -132,6 +135,8 @@ private:
System* m_system; System* m_system;
u32 m_index; u32 m_index;
bool m_auto_enable_analog = false;
bool m_analog_mode = false; bool m_analog_mode = false;
bool m_analog_locked = false; bool m_analog_locked = false;
bool m_rumble_unlocked = false; bool m_rumble_unlocked = false;

View file

@ -3,8 +3,8 @@
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "digital_controller.h" #include "digital_controller.h"
#include "namco_guncon.h" #include "namco_guncon.h"
#include "playstation_mouse.h"
#include "negcon.h" #include "negcon.h"
#include "playstation_mouse.h"
Controller::Controller() = default; Controller::Controller() = default;
@ -39,6 +39,8 @@ float Controller::GetVibrationMotorStrength(u32 motor)
return 0.0f; return 0.0f;
} }
void Controller::LoadSettings(HostInterface* host_interface, const char* section) {}
std::unique_ptr<Controller> Controller::Create(System* system, ControllerType type, u32 index) std::unique_ptr<Controller> Controller::Create(System* system, ControllerType type, u32 index)
{ {
switch (type) switch (type)
@ -198,3 +200,15 @@ std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::str
return std::nullopt; return std::nullopt;
} }
} }
Controller::SettingList Controller::GetSettings(ControllerType type)
{
switch (type)
{
case ControllerType::AnalogController:
return AnalogController::StaticGetSettings();
default:
return {};
}
}

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "settings.h"
#include "types.h" #include "types.h"
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -8,12 +9,14 @@
class StateWrapper; class StateWrapper;
class System; class System;
class HostInterface;
class Controller class Controller
{ {
public: public:
using ButtonList = std::vector<std::pair<std::string, s32>>; using ButtonList = std::vector<std::pair<std::string, s32>>;
using AxisList = std::vector<std::pair<std::string, s32>>; using AxisList = std::vector<std::pair<std::string, s32>>;
using SettingList = std::vector<SettingInfo>;
Controller(); Controller();
virtual ~Controller(); virtual ~Controller();
@ -48,6 +51,9 @@ public:
/// Queries the state of the specified vibration motor. Values are normalized from 0..1. /// Queries the state of the specified vibration motor. Values are normalized from 0..1.
virtual float GetVibrationMotorStrength(u32 motor); virtual float GetVibrationMotorStrength(u32 motor);
/// Loads/refreshes any per-controller settings.
virtual void LoadSettings(HostInterface* host_interface, const char* section);
/// Creates a new controller of the specified type. /// Creates a new controller of the specified type.
static std::unique_ptr<Controller> Create(System* system, ControllerType type, u32 index); static std::unique_ptr<Controller> Create(System* system, ControllerType type, u32 index);
@ -65,4 +71,7 @@ public:
/// Returns the number of vibration motors. /// Returns the number of vibration motors.
static u32 GetVibrationMotorCount(ControllerType type); static u32 GetVibrationMotorCount(ControllerType type);
/// Returns settings for the controller.
static SettingList GetSettings(ControllerType type);
}; };

View file

@ -480,6 +480,9 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
OnControllerTypeChanged(i); OnControllerTypeChanged(i);
} }
if (m_system && !controllers_updated)
m_system->UpdateControllerSettings();
} }
if (m_display && m_settings.display_linear_filtering != old_settings.display_linear_filtering) if (m_display && m_settings.display_linear_filtering != old_settings.display_linear_filtering)
@ -566,6 +569,36 @@ std::string HostInterface::GetGameMemoryCardPath(const char* game_code, u32 slot
return GetUserDirectoryRelativePath("memcards/%s_%d.mcd", game_code, slot + 1); return GetUserDirectoryRelativePath("memcards/%s_%d.mcd", game_code, slot + 1);
} }
bool HostInterface::GetBooleanSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::string value = GetSettingValue(section, key, "");
if (value.empty())
return default_value;
std::optional<bool> bool_value = StringUtil::FromChars<bool>(value);
return bool_value.value_or(default_value);
}
bool HostInterface::GetIntegerSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
{
std::string value = GetSettingValue(section, key, "");
if (value.empty())
return default_value;
std::optional<s32> int_value = StringUtil::FromChars<s32>(value);
return int_value.value_or(default_value);
}
bool HostInterface::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::string value = GetSettingValue(section, key, "");
if (value.empty())
return default_value;
std::optional<float> float_value = StringUtil::FromChars<float>(value);
return float_value.value_or(default_value);
}
void HostInterface::ToggleSoftwareRendering() void HostInterface::ToggleSoftwareRendering()
{ {
if (!m_system || m_settings.gpu_renderer == GPURenderer::Software) if (!m_system || m_settings.gpu_renderer == GPURenderer::Software)

View file

@ -91,7 +91,8 @@ public:
/// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks /// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks
/// such as compiling shaders when starting up. /// such as compiling shaders when starting up.
virtual void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1); virtual void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1,
int progress_value = -1);
/// Retrieves information about specified game from game list. /// Retrieves information about specified game from game list.
virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title); virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title);
@ -102,7 +103,20 @@ public:
/// Returns the default path to a memory card for a specific game. /// Returns the default path to a memory card for a specific game.
virtual std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const; virtual std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const;
/// Enables the software cursor. Can be called multiple times, but must be matched by a call to DisableSoftwareCursor(). /// Returns a setting value from the configuration.
virtual std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") = 0;
/// Returns a boolean setting from the configuration.
bool GetBooleanSettingValue(const char* section, const char* key, bool default_value = false);
/// Returns an integer setting from the configuration.
bool GetIntegerSettingValue(const char* section, const char* key, s32 default_value = 0);
/// Returns a float setting from the configuration.
bool GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
/// Enables the software cursor. Can be called multiple times, but must be matched by a call to
/// DisableSoftwareCursor().
void EnableSoftwareCursor(); void EnableSoftwareCursor();
/// Disables the software cursor, preventing it from being renderered. /// Disables the software cursor, preventing it from being renderered.

View file

@ -4,6 +4,62 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
const char* SettingInfo::StringDefaultValue() const
{
return default_value ? default_value : "";
}
bool SettingInfo::BooleanDefaultValue() const
{
return default_value ? StringUtil::FromChars<bool>(default_value).value_or(false) : false;
}
s32 SettingInfo::IntegerDefaultValue() const
{
return default_value ? StringUtil::FromChars<s32>(default_value).value_or(0) : 0;
}
s32 SettingInfo::IntegerMinValue() const
{
static constexpr s32 fallback_value = std::numeric_limits<s32>::min();
return min_value ? StringUtil::FromChars<s32>(min_value).value_or(fallback_value) : fallback_value;
}
s32 SettingInfo::IntegerMaxValue() const
{
static constexpr s32 fallback_value = std::numeric_limits<s32>::max();
return max_value ? StringUtil::FromChars<s32>(max_value).value_or(fallback_value) : fallback_value;
}
s32 SettingInfo::IntegerStepValue() const
{
static constexpr s32 fallback_value = 1;
return step_value ? StringUtil::FromChars<s32>(step_value).value_or(fallback_value) : fallback_value;
}
float SettingInfo::FloatDefaultValue() const
{
return default_value ? StringUtil::FromChars<float>(default_value).value_or(0.0f) : 0.0f;
}
float SettingInfo::FloatMinValue() const
{
static constexpr float fallback_value = std::numeric_limits<float>::min();
return min_value ? StringUtil::FromChars<float>(min_value).value_or(fallback_value) : fallback_value;
}
float SettingInfo::FloatMaxValue() const
{
static constexpr float fallback_value = std::numeric_limits<float>::max();
return max_value ? StringUtil::FromChars<float>(max_value).value_or(fallback_value) : fallback_value;
}
float SettingInfo::FloatStepValue() const
{
static constexpr float fallback_value = 0.1f;
return step_value ? StringUtil::FromChars<float>(step_value).value_or(fallback_value) : fallback_value;
}
Settings::Settings() = default; Settings::Settings() = default;
bool Settings::HasAnyPerGameMemoryCards() const bool Settings::HasAnyPerGameMemoryCards() const

View file

@ -30,6 +30,38 @@ public:
virtual void DeleteValue(const char* section, const char* key) = 0; virtual void DeleteValue(const char* section, const char* key) = 0;
}; };
struct SettingInfo
{
enum class Type
{
Boolean,
Integer,
Float,
String,
Path,
};
Type type;
const char* key;
const char* visible_name;
const char* description;
const char* default_value;
const char* min_value;
const char* max_value;
const char* step_value;
const char* StringDefaultValue() const;
bool BooleanDefaultValue() const;
s32 IntegerDefaultValue() const;
s32 IntegerMinValue() const;
s32 IntegerMaxValue() const;
s32 IntegerStepValue() const;
float FloatDefaultValue() const;
float FloatMinValue() const;
float FloatMaxValue() const;
float FloatStepValue() const;
};
struct Settings struct Settings
{ {
Settings(); Settings();

View file

@ -832,11 +832,24 @@ void System::UpdateControllers()
{ {
std::unique_ptr<Controller> controller = Controller::Create(this, type, i); std::unique_ptr<Controller> controller = Controller::Create(this, type, i);
if (controller) if (controller)
{
controller->LoadSettings(m_host_interface, TinyString::FromFormat("Controller%u", i + 1u));
m_pad->SetController(i, std::move(controller)); m_pad->SetController(i, std::move(controller));
}
} }
} }
} }
void System::UpdateControllerSettings()
{
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
Controller* controller = m_pad->GetController(i);
if (controller)
controller->LoadSettings(m_host_interface, TinyString::FromFormat("Controller%u", i + 1u));
}
}
void System::UpdateMemoryCards() void System::UpdateMemoryCards()
{ {
const Settings& settings = m_host_interface->GetSettings(); const Settings& settings = m_host_interface->GetSettings();

View file

@ -139,6 +139,7 @@ public:
// Access controllers for simulating input. // Access controllers for simulating input.
Controller* GetController(u32 slot) const; Controller* GetController(u32 slot) const;
void UpdateControllers(); void UpdateControllers();
void UpdateControllerSettings();
void UpdateMemoryCards(); void UpdateMemoryCards();
bool HasMedia() const; bool HasMedia() const;

View file

@ -118,6 +118,18 @@ std::string LibretroHostInterface::GetGameMemoryCardPath(const char* game_code,
return GetUserDirectoryRelativePath("%s/%s_%d.mcd", GetSaveDirectory(), game_code, slot + 1); return GetUserDirectoryRelativePath("%s/%s_%d.mcd", GetSaveDirectory(), game_code, slot + 1);
} }
std::string LibretroHostInterface::GetSettingValue(const char* section, const char* key,
const char* default_value /*= ""*/)
{
TinyString name;
name.Format("%s.%s", section, key);
retro_variable var{name, default_value};
if (g_retro_environment_callback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
return var.value;
else
return default_value;
}
void LibretroHostInterface::AddOSDMessage(std::string message, float duration /*= 2.0f*/) void LibretroHostInterface::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
{ {
retro_message msg = {}; retro_message msg = {};

View file

@ -24,6 +24,7 @@ public:
void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) override; void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) override;
std::string GetSharedMemoryCardPath(u32 slot) const override; std::string GetSharedMemoryCardPath(u32 slot) const override;
std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const override; std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const override;
std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") override;
// Called by frontend // Called by frontend
void retro_get_system_av_info(struct retro_system_av_info* info); void retro_get_system_av_info(struct retro_system_av_info* info);

View file

@ -122,6 +122,13 @@ bool QtHostInterface::parseCommandLineParameters(int argc, char* argv[],
return CommonHostInterface::ParseCommandLineParameters(argc, argv, out_boot_params); return CommonHostInterface::ParseCommandLineParameters(argc, argv, out_boot_params);
} }
std::string QtHostInterface::GetSettingValue(const char* section, const char* key, const char* default_value)
{
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);
QVariant value = m_qsettings->value(QStringLiteral("%1/%2").arg(section).arg(key), QString(default_value));
return value.toString().toStdString();
}
QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value) QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value)
{ {
std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex); std::lock_guard<std::recursive_mutex> guard(m_qsettings_mutex);

View file

@ -50,6 +50,7 @@ public:
bool parseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params); bool parseCommandLineParameters(int argc, char* argv[], std::unique_ptr<SystemBootParameters>* out_boot_params);
/// Thread-safe QSettings access. /// Thread-safe QSettings access.
std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") override;
QVariant getSettingValue(const QString& name, const QVariant& default_value = QVariant()); QVariant getSettingValue(const QString& name, const QVariant& default_value = QVariant());
void putSettingValue(const QString& name, const QVariant& value); void putSettingValue(const QString& name, const QVariant& value);
void removeSettingValue(const QString& name); void removeSettingValue(const QString& name);

View file

@ -456,6 +456,11 @@ void SDLHostInterface::Shutdown()
CommonHostInterface::Shutdown(); CommonHostInterface::Shutdown();
} }
std::string SDLHostInterface::GetSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
return m_settings_interface->GetStringValue(section, key, default_value);
}
void SDLHostInterface::LoadSettings() void SDLHostInterface::LoadSettings()
{ {
// Settings need to be loaded prior to creating the window for OpenGL bits. // Settings need to be loaded prior to creating the window for OpenGL bits.

View file

@ -34,6 +34,8 @@ public:
bool Initialize() override; bool Initialize() override;
void Shutdown() override; void Shutdown() override;
std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") override;
void Run(); void Run();
protected: protected:

View file

@ -1483,13 +1483,21 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
const std::string rumble_value = profile.GetStringValue(section_name, "Rumble"); const std::string rumble_value = profile.GetStringValue(section_name, "Rumble");
if (!rumble_value.empty()) if (!rumble_value.empty())
si.SetStringValue(section_name, "Rumble", rumble_value.c_str()); si.SetStringValue(section_name, "Rumble", rumble_value.c_str());
}
UpdateInputMap(si); Controller::SettingList settings = Controller::GetSettings(*ctype);
for (const SettingInfo& ssi : settings)
{
const std::string value = profile.GetStringValue(section_name, ssi.key, "");
if (!value.empty())
si.SetStringValue(section_name, ssi.key, value.c_str());
}
}
if (m_system) if (m_system)
m_system->UpdateControllers(); m_system->UpdateControllers();
UpdateInputMap(si);
ReportFormattedMessage("Loaded input profile from '%s'", profile_path); ReportFormattedMessage("Loaded input profile from '%s'", profile_path);
} }
@ -1532,6 +1540,14 @@ bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInt
const std::string rumble_value = si.GetStringValue(section_name, "Rumble"); const std::string rumble_value = si.GetStringValue(section_name, "Rumble");
if (!rumble_value.empty()) if (!rumble_value.empty())
profile.SetStringValue(section_name, "Rumble", rumble_value.c_str()); profile.SetStringValue(section_name, "Rumble", rumble_value.c_str());
Controller::SettingList settings = Controller::GetSettings(ctype);
for (const SettingInfo& ssi : settings)
{
const std::string value = si.GetStringValue(section_name, ssi.key, "");
if (!value.empty())
profile.SetStringValue(section_name, ssi.key, value.c_str());
}
} }
if (!profile.Save()) if (!profile.Save())
@ -1814,8 +1830,8 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress
const bool has_progress = (progress_min < progress_max); const bool has_progress = (progress_min < progress_max);
// eat the last imgui frame, it might've been partially rendered by the caller. // eat the last imgui frame, it might've been partially rendered by the caller.
//ImGui::EndFrame(); // ImGui::EndFrame();
//ImGui::NewFrame(); // ImGui::NewFrame();
ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 50.0f : 30.0f) * scale), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(width, (has_progress ? 50.0f : 30.0f) * scale), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always, ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), ImGuiCond_Always,