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 "common/log.h"
#include "common/state_wrapper.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "system.h"
Log_SetChannel(AnalogController);
@ -23,6 +24,9 @@ void AnalogController::Reset()
m_rumble_unlocked = false;
m_configuration_mode = false;
m_command_param = 0;
if (m_auto_enable_analog)
SetAnalogMode(true);
}
bool AnalogController::DoState(StateWrapper& sw)
@ -474,3 +478,18 @@ u32 AnalogController::StaticGetVibrationMotorCount()
{
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 ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
@ -70,6 +71,8 @@ public:
u32 GetVibrationMotorCount() const override;
float GetVibrationMotorStrength(u32 motor) override;
void LoadSettings(HostInterface* host_interface, const char* section) override;
private:
using MotorState = std::array<u8, NUM_MOTORS>;
@ -132,6 +135,8 @@ private:
System* m_system;
u32 m_index;
bool m_auto_enable_analog = false;
bool m_analog_mode = false;
bool m_analog_locked = false;
bool m_rumble_unlocked = false;

View file

@ -3,8 +3,8 @@
#include "common/state_wrapper.h"
#include "digital_controller.h"
#include "namco_guncon.h"
#include "playstation_mouse.h"
#include "negcon.h"
#include "playstation_mouse.h"
Controller::Controller() = default;
@ -39,6 +39,8 @@ float Controller::GetVibrationMotorStrength(u32 motor)
return 0.0f;
}
void Controller::LoadSettings(HostInterface* host_interface, const char* section) {}
std::unique_ptr<Controller> Controller::Create(System* system, ControllerType type, u32 index)
{
switch (type)
@ -198,3 +200,15 @@ std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::str
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
#include "settings.h"
#include "types.h"
#include <memory>
#include <optional>
@ -8,12 +9,14 @@
class StateWrapper;
class System;
class HostInterface;
class Controller
{
public:
using ButtonList = std::vector<std::pair<std::string, s32>>;
using AxisList = std::vector<std::pair<std::string, s32>>;
using SettingList = std::vector<SettingInfo>;
Controller();
virtual ~Controller();
@ -48,6 +51,9 @@ public:
/// Queries the state of the specified vibration motor. Values are normalized from 0..1.
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.
static std::unique_ptr<Controller> Create(System* system, ControllerType type, u32 index);
@ -65,4 +71,7 @@ public:
/// Returns the number of vibration motors.
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);
}
if (m_system && !controllers_updated)
m_system->UpdateControllerSettings();
}
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);
}
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()
{
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
/// 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.
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.
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();
/// Disables the software cursor, preventing it from being renderered.

View file

@ -4,6 +4,62 @@
#include <algorithm>
#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;
bool Settings::HasAnyPerGameMemoryCards() const

View file

@ -30,6 +30,38 @@ public:
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
{
Settings();

View file

@ -832,11 +832,24 @@ void System::UpdateControllers()
{
std::unique_ptr<Controller> controller = Controller::Create(this, type, i);
if (controller)
{
controller->LoadSettings(m_host_interface, TinyString::FromFormat("Controller%u", i + 1u));
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()
{
const Settings& settings = m_host_interface->GetSettings();

View file

@ -139,6 +139,7 @@ public:
// Access controllers for simulating input.
Controller* GetController(u32 slot) const;
void UpdateControllers();
void UpdateControllerSettings();
void UpdateMemoryCards();
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);
}
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*/)
{
retro_message msg = {};

View file

@ -24,6 +24,7 @@ public:
void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) override;
std::string GetSharedMemoryCardPath(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
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);
}
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)
{
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);
/// 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());
void putSettingValue(const QString& name, const QVariant& value);
void removeSettingValue(const QString& name);

View file

@ -456,6 +456,11 @@ void SDLHostInterface::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()
{
// Settings need to be loaded prior to creating the window for OpenGL bits.

View file

@ -34,6 +34,8 @@ public:
bool Initialize() override;
void Shutdown() override;
std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") override;
void Run();
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");
if (!rumble_value.empty())
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)
m_system->UpdateControllers();
UpdateInputMap(si);
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");
if (!rumble_value.empty())
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())
@ -1814,8 +1830,8 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress
const bool has_progress = (progress_min < progress_max);
// eat the last imgui frame, it might've been partially rendered by the caller.
//ImGui::EndFrame();
//ImGui::NewFrame();
// ImGui::EndFrame();
// ImGui::NewFrame();
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,