From 690d4fa4b1d2bd0b07e13ee40375353d42f514c5 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 19 Dec 2022 22:29:14 +1000 Subject: [PATCH] FullscreenUI: Add USB to controller settings --- pcsx2/Frontend/FullscreenUI.cpp | 269 ++++++++++++++++++++++++++++---- 1 file changed, 240 insertions(+), 29 deletions(-) diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 30097b21f7..ca11c90103 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -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 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 value( + bsi->GetOptionalStringValue(section, key, game_settings ? std::nullopt : std::optional(default_value))); + + if (MenuButtonWithValue(title, summary, value.has_value() ? value->c_str() : "Use Global Setting", enabled, height, font, summary_font)) + { + std::vector> 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 value( + bsi->GetOptionalStringValue(section, key, game_settings ? std::nullopt : std::optional(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> 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 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> 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(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 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(index)); + SetSettingsChanged(bsi); + CloseChoiceDialog(); + }); + } + } + + const gsl::span 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 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();