mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
HostInterface: Support per-controller-type settings
This commit is contained in:
parent
f3b2953e40
commit
4dc9e10777
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -139,6 +139,7 @@ public:
|
|||
// Access controllers for simulating input.
|
||||
Controller* GetController(u32 slot) const;
|
||||
void UpdateControllers();
|
||||
void UpdateControllerSettings();
|
||||
void UpdateMemoryCards();
|
||||
|
||||
bool HasMedia() const;
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue