FullscreenUI: Add USB to controller settings

This commit is contained in:
Connor McLaughlin 2022-12-19 22:29:14 +10:00 committed by refractionpcsx2
parent c477ea1628
commit 690d4fa4b1
1 changed files with 240 additions and 29 deletions

View File

@ -45,6 +45,7 @@
#include "PAD/Host/PAD.h"
#include "ps2/BiosTools.h"
#include "Sio.h"
#include "USB/USB.h"
#include "VMManager.h"
#include "svnrev.h"
@ -344,12 +345,18 @@ namespace FullscreenUI
static void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
const char* default_value, const char* const* options, const char* const* option_values, size_t option_count, bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
static void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
const char* default_value, SettingInfo::GetOptionsCallback options_callback, bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
static void DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
float default_value, const char* const* options, const float* option_values, size_t option_count, bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
static void DrawFolderSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key,
const std::string& runtime_var, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font,
ImFont* summary_font = g_medium_font);
static void DrawPathSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key, const char* default_value,
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font,
ImFont* summary_font = g_medium_font);
static void DrawClampingModeSetting(SettingsInterface* bsi, const char* title, const char* summary, bool vu);
static void PopulateGraphicsAdapterList();
static void PopulateGameListDirectoryCache(SettingsInterface* si);
@ -360,6 +367,7 @@ namespace FullscreenUI
const char* display_name, bool show_type = true);
static void ClearInputBindingVariables();
static void StartAutomaticBinding(u32 port);
static void DrawSettingInfoSetting(SettingsInterface* bsi, const char* section, const char* key, const SettingInfo& si);
static SettingsPage s_settings_page = SettingsPage::Interface;
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
@ -2043,6 +2051,49 @@ void FullscreenUI::DrawStringListSetting(SettingsInterface* bsi, const char* tit
}
}
void FullscreenUI::DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* key, const char* default_value, SettingInfo::GetOptionsCallback option_callback, bool enabled, float height, ImFont* font,
ImFont* summary_font)
{
const bool game_settings = IsEditingGameSettings(bsi);
const std::optional<std::string> value(
bsi->GetOptionalStringValue(section, key, game_settings ? std::nullopt : std::optional<const char*>(default_value)));
if (MenuButtonWithValue(title, summary, value.has_value() ? value->c_str() : "Use Global Setting", enabled, height, font, summary_font))
{
std::vector<std::pair<std::string, std::string>> raw_options(option_callback());
ImGuiFullscreen::ChoiceDialogOptions cd_options;
cd_options.reserve(raw_options.size() + 1);
if (game_settings)
cd_options.emplace_back("Use Global Setting", !value.has_value());
for (size_t i = 0; i < raw_options.size(); i++)
cd_options.emplace_back(raw_options[i].second, (value.has_value() && value.value() == raw_options[i].first));
OpenChoiceDialog(title, false, std::move(cd_options),
[game_settings, section, key, raw_options = std::move(raw_options)](s32 index, const std::string& title, bool checked) {
if (index >= 0)
{
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
if (game_settings)
{
if (index == 0)
bsi->DeleteValue(section, key);
else
bsi->SetStringValue(section, key, raw_options[index - 1].first.c_str());
}
else
{
bsi->SetStringValue(section, key, raw_options[index].first.c_str());
}
SetSettingsChanged(bsi);
}
CloseChoiceDialog();
});
}
}
void FullscreenUI::DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* key, float default_value, const char* const* options, const float* option_values, size_t option_count, bool enabled,
float height, ImFont* font, ImFont* summary_font)
@ -2132,6 +2183,41 @@ void FullscreenUI::DrawFolderSetting(SettingsInterface* bsi, const char* title,
}
}
void FullscreenUI::DrawPathSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key,
const char* default_value, bool enabled /* = true */, float height /* = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT */,
ImFont* font /* = g_large_font */, ImFont* summary_font /* = g_medium_font */)
{
const bool game_settings = IsEditingGameSettings(bsi);
const std::optional<std::string> value(
bsi->GetOptionalStringValue(section, key, game_settings ? std::nullopt : std::optional<const char*>(default_value)));
if (MenuButton(title, value.has_value() ? value->c_str() : "Use Global Setting"))
{
auto callback = [game_settings = IsEditingGameSettings(bsi), section = std::string(section), key = std::string(key)](
const std::string& dir) {
if (dir.empty())
return;
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
std::string relative_path(Path::MakeRelative(dir, EmuFolders::DataRoot));
bsi->SetStringValue(section.c_str(), key.c_str(), relative_path.c_str());
SetSettingsChanged(bsi);
Host::RunOnCPUThread(&Host::Internal::UpdateEmuFolders);
s_cover_image_map.clear();
CloseFileSelector();
};
std::string initial_path;
if (value.has_value())
initial_path = Path::GetDirectory(value.value());
OpenFileSelector(title, false, std::move(callback), { "*" }, std::move(initial_path));
}
}
void FullscreenUI::StartAutomaticBinding(u32 port)
{
// messy because the enumeration has to happen on the input thread
@ -2178,6 +2264,49 @@ void FullscreenUI::StartAutomaticBinding(u32 port)
});
}
void FullscreenUI::DrawSettingInfoSetting(SettingsInterface* bsi, const char* section, const char* key, const SettingInfo& si)
{
std::string title(fmt::format(ICON_FA_COG " {}", si.display_name));
switch (si.type)
{
case SettingInfo::Type::Boolean:
DrawToggleSetting(bsi, title.c_str(), si.description, section, key, si.BooleanDefaultValue(), true, false);
break;
case SettingInfo::Type::Integer:
DrawIntRangeSetting(bsi, title.c_str(), si.description, section, key, si.IntegerDefaultValue(), si.IntegerMinValue(),
si.IntegerMaxValue(), si.format, true);
break;
case SettingInfo::Type::IntegerList:
DrawIntListSetting(
bsi, title.c_str(), si.description, section, key, si.IntegerDefaultValue(), si.options, 0, si.IntegerMinValue(), true);
break;
case SettingInfo::Type::Float:
DrawFloatSpinBoxSetting(bsi, title.c_str(), si.description, section, key, si.FloatDefaultValue(), si.FloatMinValue(),
si.FloatMaxValue(), si.FloatStepValue(), si.multiplier, si.format, true);
break;
case SettingInfo::Type::StringList:
{
if (si.get_options)
DrawStringListSetting(bsi, title.c_str(), si.description, section, key, si.StringDefaultValue(), si.get_options, true);
else
DrawStringListSetting(
bsi, title.c_str(), si.description, section, key, si.StringDefaultValue(), si.options, si.options, 0, true);
}
break;
case SettingInfo::Type::Path:
DrawPathSetting(bsi, title.c_str(), section, key, si.StringDefaultValue(), true);
break;
default:
break;
}
}
void FullscreenUI::SwitchToSettings()
{
s_game_settings_entry.reset();
@ -3618,6 +3747,7 @@ void FullscreenUI::DrawControllerSettingsPage()
if (is_mtap_port && !mtap_enabled[mtap_port])
continue;
ImGui::PushID(global_slot);
MenuHeading(
(mtap_enabled[mtap_port] ? fmt::format(ICON_FA_PLUG " Controller Port {}{}", mtap_port + 1, mtap_slot_names[mtap_slot]) :
fmt::format(ICON_FA_PLUG " Controller Port {}", mtap_port + 1))
@ -3626,7 +3756,7 @@ void FullscreenUI::DrawControllerSettingsPage()
const char* section = sections[global_slot];
const std::string type(bsi->GetStringValue(section, "Type", PAD::GetDefaultPadType(global_slot)));
const PAD::ControllerInfo* ci = PAD::GetControllerInfo(type);
if (MenuButton(fmt::format(ICON_FA_GAMEPAD " Controller Type##type{}", global_slot).c_str(), ci ? ci->display_name : "Unknown"))
if (MenuButton(ICON_FA_GAMEPAD " Controller Type", ci ? ci->display_name : "Unknown"))
{
std::vector<std::pair<std::string, std::string>> raw_options(PAD::GetControllerTypeNames());
ImGuiFullscreen::ChoiceDialogOptions options;
@ -3650,7 +3780,10 @@ void FullscreenUI::DrawControllerSettingsPage()
}
if (!ci || ci->num_bindings == 0)
{
ImGui::PopID();
continue;
}
if (MenuButton(ICON_FA_MAGIC " Automatic Mapping", "Attempts to map the selected port to a chosen controller."))
StartAutomaticBinding(global_slot);
@ -3666,13 +3799,20 @@ void FullscreenUI::DrawControllerSettingsPage()
fmt::format(ICON_FA_MICROCHIP " Controller Port {} Macros", mtap_port + 1))
.c_str());
static bool macro_button_expanded[PAD::NUM_CONTROLLER_PORTS][PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {};
for (u32 macro_index = 0; macro_index < PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_index++)
{
DrawInputBindingButton(bsi, InputBindingInfo::Type::Macro, section, fmt::format("Macro{}", macro_index + 1).c_str(),
fmt::format("Macro {} Trigger", macro_index + 1).c_str());
bool& expanded = macro_button_expanded[global_slot][macro_index];
expanded ^= MenuHeadingButton(fmt::format(ICON_FA_MICROCHIP " Macro Button {}", macro_index + 1).c_str(),
macro_button_expanded[global_slot][macro_index] ? ICON_FA_CHEVRON_UP : ICON_FA_CHEVRON_DOWN);
if (!expanded)
continue;
DrawInputBindingButton(bsi, InputBindingInfo::Type::Macro, section, fmt::format("Macro{}", macro_index + 1).c_str(), "Trigger");
std::string binds_string(bsi->GetStringValue(section, fmt::format("Macro{}Binds", macro_index + 1).c_str()));
if (MenuButton(fmt::format(ICON_FA_KEYBOARD " Macro {} Buttons", macro_index + 1).c_str(),
if (MenuButton(fmt::format(ICON_FA_KEYBOARD " Buttons", macro_index + 1).c_str(),
binds_string.empty() ? "No Buttons Selected" : binds_string.c_str()))
{
std::vector<std::string_view> buttons_split(StringUtil::SplitString(binds_string, '&', true));
@ -3735,12 +3875,11 @@ void FullscreenUI::DrawControllerSettingsPage()
}
const std::string freq_key(fmt::format("Macro{}Frequency", macro_index + 1));
const std::string freq_title(fmt::format(ICON_FA_LIGHTBULB " Macro {} Frequency", macro_index + 1));
s32 frequency = bsi->GetIntValue(section, freq_key.c_str(), 0);
const std::string freq_summary((frequency == 0) ? std::string("Macro will not auto-toggle.") :
fmt::format("Macro will toggle every {} frames.", frequency));
if (MenuButton(freq_title.c_str(), freq_summary.c_str()))
ImGui::OpenPopup(freq_title.c_str());
if (MenuButton(ICON_FA_LIGHTBULB " Frequency", freq_summary.c_str()))
ImGui::OpenPopup(freq_key.c_str());
ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
@ -3753,7 +3892,7 @@ void FullscreenUI::DrawControllerSettingsPage()
LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING));
if (ImGui::BeginPopupModal(
freq_title.c_str(), nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
freq_key.c_str(), nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
{
ImGui::SetNextItemWidth(LayoutScale(450.0f));
if (ImGui::SliderInt("##value", &frequency, 0, 60, "Toggle every %d frames", ImGuiSliderFlags_NoInput))
@ -3786,29 +3925,101 @@ void FullscreenUI::DrawControllerSettingsPage()
for (u32 i = 0; i < ci->num_settings; i++)
{
const SettingInfo& si = ci->settings[i];
std::string title(fmt::format(ICON_FA_COG " {}", si.display_name));
switch (si.type)
{
case SettingInfo::Type::Boolean:
DrawToggleSetting(bsi, title.c_str(), si.description, section, si.name, si.BooleanDefaultValue(), true, false);
break;
case SettingInfo::Type::Integer:
DrawIntRangeSetting(bsi, title.c_str(), si.description, section, si.name, si.IntegerDefaultValue(),
si.IntegerMinValue(), si.IntegerMaxValue(), si.format, true);
break;
case SettingInfo::Type::IntegerList:
DrawIntListSetting(bsi, title.c_str(), si.description, section, si.name, si.IntegerDefaultValue(), si.options, 0,
si.IntegerMinValue(), true);
break;
case SettingInfo::Type::Float:
DrawFloatSpinBoxSetting(bsi, title.c_str(), si.description, section, si.name, si.FloatDefaultValue(),
si.FloatMinValue(), si.FloatMaxValue(), si.FloatStepValue(), si.multiplier, si.format, true);
break;
default:
break;
}
DrawSettingInfoSetting(bsi, section, si.name, si);
}
}
ImGui::PopID();
}
static constexpr const char* usb_sections[USB::NUM_PORTS] = {"USB1", "USB2"};
for (u32 port = 0; port < USB::NUM_PORTS; port++)
{
ImGui::PushID(port);
MenuHeading(fmt::format(ICON_FA_PLUG " USB Port {}", port + 1).c_str());
const std::string type(USB::GetConfigDevice(*bsi, port));
if (MenuButton(ICON_FA_GAMEPAD " Device Type", USB::GetDeviceName(type)))
{
std::vector<std::pair<std::string, std::string>> raw_options(USB::GetDeviceTypes());
ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(raw_options.size());
for (auto& it : raw_options)
{
options.emplace_back(std::move(it.second), type == it.first);
}
OpenChoiceDialog(fmt::format("Port {} Device", port + 1).c_str(), false, std::move(options),
[game_settings = IsEditingGameSettings(bsi), raw_options = std::move(raw_options), port](
s32 index, const std::string& title, bool checked) {
if (index < 0)
return;
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
USB::SetConfigDevice(*bsi, port, raw_options[static_cast<u32>(index)].first.c_str());
SetSettingsChanged(bsi);
CloseChoiceDialog();
});
}
if (type.empty() || type == "None")
{
ImGui::PopID();
continue;
}
const u32 subtype = USB::GetConfigSubType(*bsi, port, type);
const gsl::span<const char*> subtypes(USB::GetDeviceSubtypes(type));
if (!subtypes.empty())
{
const char* subtype_name = USB::GetDeviceSubtypeName(type, subtype);
if (MenuButton(ICON_FA_COG " Device Subtype", subtype_name))
{
ImGuiFullscreen::ChoiceDialogOptions options;
options.reserve(subtypes.size());
for (u32 i = 0; i < subtypes.size(); i++)
options.emplace_back(subtypes[i], i == subtype);
OpenChoiceDialog(fmt::format("Port {} Subtype", port + 1).c_str(), false, std::move(options),
[game_settings = IsEditingGameSettings(bsi), port, type](s32 index, const std::string& title, bool checked) {
if (index < 0)
return;
auto lock = Host::GetSettingsLock();
SettingsInterface* bsi = GetEditingSettingsInterface(game_settings);
USB::SetConfigSubType(*bsi, port, type.c_str(), static_cast<u32>(index));
SetSettingsChanged(bsi);
CloseChoiceDialog();
});
}
}
const gsl::span<const InputBindingInfo> bindings(USB::GetDeviceBindings(type, subtype));
if (!bindings.empty())
{
MenuHeading(fmt::format(ICON_FA_KEYBOARD " {} Bindings", USB::GetDeviceName(type)).c_str());
if (MenuButton(ICON_FA_FOLDER_MINUS " Clear Bindings", "Clears all bindings for this USB controller."))
{
USB::ClearPortBindings(*bsi, port);
SetSettingsChanged(bsi);
}
const std::string section(USB::GetConfigSection(port));
for (const InputBindingInfo& bi : bindings)
DrawInputBindingButton(bsi, bi.bind_type, section.c_str(), USB::GetConfigSubKey(type, bi.name).c_str(), bi.display_name);
}
const gsl::span<const SettingInfo> settings(USB::GetDeviceSettings(type, subtype));
if (!settings.empty())
{
MenuHeading(fmt::format(ICON_FA_SLIDERS_H " {} Settings", USB::GetDeviceName(type)).c_str());
const std::string section(USB::GetConfigSection(port));
for (const SettingInfo& si : settings)
DrawSettingInfoSetting(bsi, section.c_str(), USB::GetConfigSubKey(type, si.name).c_str(), si);
}
ImGui::PopID();
}
EndMenuButtons();