From c393db419e606b30c8f257b45ce2a1a7fd796a24 Mon Sep 17 00:00:00 2001 From: Stenzek <stenzek@gmail.com> Date: Sun, 15 Jan 2023 16:40:35 +1000 Subject: [PATCH] Qt: Add Controller LED options (where supported) --- src/duckstation-qt/CMakeLists.txt | 3 + src/duckstation-qt/colorpickerbutton.cpp | 50 +++++++++++ src/duckstation-qt/colorpickerbutton.h | 29 +++++++ .../controllerglobalsettingswidget.cpp | 51 +++++++++++- .../controllerglobalsettingswidget.h | 21 +++++ .../controllerglobalsettingswidget.ui | 26 ++++-- .../controllerledsettingsdialog.ui | 83 +++++++++++++++++++ src/duckstation-qt/duckstation-qt.vcxproj | 6 ++ .../duckstation-qt.vcxproj.filters | 7 ++ .../icons/black/svg/lightbulb-line.svg | 1 + .../icons/white/svg/lightbulb-line.svg | 1 + src/duckstation-qt/resources/resources.qrc | 2 + src/frontend-common/CMakeLists.txt | 4 +- src/frontend-common/sdl_input_source.cpp | 51 ++++++++++++ src/frontend-common/sdl_input_source.h | 6 ++ 15 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 src/duckstation-qt/colorpickerbutton.cpp create mode 100644 src/duckstation-qt/colorpickerbutton.h create mode 100644 src/duckstation-qt/controllerledsettingsdialog.ui create mode 100644 src/duckstation-qt/resources/icons/black/svg/lightbulb-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/lightbulb-line.svg diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 367f1dd93..90c18ee36 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -27,6 +27,8 @@ set(SRCS cheatmanagerdialog.ui collapsiblewidget.cpp collapsiblewidget.h + colorpickerbutton.cpp + colorpickerbutton.h consolesettingswidget.cpp consolesettingswidget.h consolesettingswidget.ui @@ -42,6 +44,7 @@ set(SRCS controllerglobalsettingswidget.cpp controllerglobalsettingswidget.h controllerglobalsettingswidget.ui + controllerledsettingsdialog.ui controllermacroeditwidget.ui controllermacrowidget.ui controllersettingsdialog.cpp diff --git a/src/duckstation-qt/colorpickerbutton.cpp b/src/duckstation-qt/colorpickerbutton.cpp new file mode 100644 index 000000000..52ed76fee --- /dev/null +++ b/src/duckstation-qt/colorpickerbutton.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com> +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#include "colorpickerbutton.h" +#include "qtutils.h" + +#include <QtWidgets/QColorDialog> + +ColorPickerButton::ColorPickerButton(QWidget* parent) : QPushButton(parent) +{ + connect(this, &QPushButton::clicked, this, &ColorPickerButton::onClicked); + updateBackgroundColor(); +} + +u32 ColorPickerButton::color() +{ + return m_color; +} + +void ColorPickerButton::setColor(u32 rgb) +{ + if (m_color == rgb) + return; + + m_color = rgb; + updateBackgroundColor(); +} + +void ColorPickerButton::updateBackgroundColor() +{ + setStyleSheet(QStringLiteral("background-color: #%1;").arg(static_cast<uint>(m_color), 8, 16, QChar('0'))); +} + +void ColorPickerButton::onClicked() +{ + const u32 red = (m_color >> 16) & 0xff; + const u32 green = (m_color >> 8) & 0xff; + const u32 blue = m_color & 0xff; + + const QColor initial(QColor::fromRgb(red, green, blue)); + const QColor selected(QColorDialog::getColor(initial, QtUtils::GetRootWidget(this), tr("Select LED Color"))); + if (initial == selected) + return; + + const u32 new_rgb = (static_cast<u32>(selected.red()) << 16) | (static_cast<u32>(selected.green()) << 8) | + static_cast<u32>(selected.blue()); + m_color = new_rgb; + updateBackgroundColor(); + emit colorChanged(new_rgb); +} diff --git a/src/duckstation-qt/colorpickerbutton.h b/src/duckstation-qt/colorpickerbutton.h new file mode 100644 index 000000000..c15908301 --- /dev/null +++ b/src/duckstation-qt/colorpickerbutton.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com> +// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) + +#pragma once +#include "common/types.h" +#include <QtWidgets/QPushButton> + +class ColorPickerButton : public QPushButton +{ + Q_OBJECT + +public: + ColorPickerButton(QWidget* parent); + +Q_SIGNALS: + void colorChanged(quint32 new_color); + +public Q_SLOTS: + quint32 color(); + void setColor(quint32 rgb); + +private Q_SLOTS: + void onClicked(); + +private: + void updateBackgroundColor(); + + u32 m_color = 0; +}; diff --git a/src/duckstation-qt/controllerglobalsettingswidget.cpp b/src/duckstation-qt/controllerglobalsettingswidget.cpp index 17d3788f5..beeed27e5 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.cpp +++ b/src/duckstation-qt/controllerglobalsettingswidget.cpp @@ -7,6 +7,8 @@ #include "qtutils.h" #include "settingwidgetbinder.h" +#include "frontend-common/sdl_input_source.h" + ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog) : QWidget(parent), m_dialog(dialog) { @@ -14,10 +16,18 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingsInterface* sif = dialog->getProfileSettingsInterface(); +#ifdef WITH_SDL2 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false); + connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, + &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled); + connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked); +#else + m_ui.enableSDLSource->setEnabled(false); + m_ui.ledSettings->setEnabled(false); +#endif + #ifdef _WIN32 SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableDInputSource, "InputSources", "DInput", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false); @@ -27,6 +37,7 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, m_ui.enableXInputSource->setEnabled(false); m_ui.enableRawInput->setEnabled(false); #endif + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode", &Settings::ParseMultitapModeName, &Settings::GetMultitapModeName, Settings::DEFAULT_MULTITAP_MODE); @@ -52,8 +63,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, m_ui.profileSettings = nullptr; } - connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, - &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled); connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); }); connect(m_ui.pointerXScale, &QSlider::valueChanged, this, @@ -90,8 +99,44 @@ void ControllerGlobalSettingsWidget::removeDeviceFromList(const QString& identif } } +void ControllerGlobalSettingsWidget::ledSettingsClicked() +{ + ControllerLEDSettingsDialog dialog(this, m_dialog); + dialog.exec(); +} + void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled() { const bool enabled = m_ui.enableSDLSource->isChecked(); m_ui.enableSDLEnhancedMode->setEnabled(enabled); + m_ui.ledSettings->setEnabled(enabled); +} + +ControllerLEDSettingsDialog::ControllerLEDSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog) + : QDialog(parent), m_dialog(dialog) +{ + m_ui.setupUi(this); + + linkButton(m_ui.SDL0LED, 0); + linkButton(m_ui.SDL1LED, 1); + linkButton(m_ui.SDL2LED, 2); + linkButton(m_ui.SDL3LED, 3); + + connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept); +} + +ControllerLEDSettingsDialog::~ControllerLEDSettingsDialog() = default; + +void ControllerLEDSettingsDialog::linkButton(ColorPickerButton* button, u32 player_id) +{ +#ifdef WITH_SDL2 + std::string key(fmt::format("Player{}LED", player_id)); + const u32 current_value = + SDLInputSource::ParseRGBForPlayerId(m_dialog->getStringValue("SDLExtra", key.c_str(), ""), player_id); + button->setColor(current_value); + + connect(button, &ColorPickerButton::colorChanged, this, [this, player_id, key = std::move(key)](u32 new_rgb) { + m_dialog->setStringValue("SDLExtra", key.c_str(), fmt::format("{:06X}", new_rgb).c_str()); + }); +#endif } diff --git a/src/duckstation-qt/controllerglobalsettingswidget.h b/src/duckstation-qt/controllerglobalsettingswidget.h index d6c0ce1d0..cb34fed51 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.h +++ b/src/duckstation-qt/controllerglobalsettingswidget.h @@ -3,12 +3,17 @@ #pragma once #include "common/types.h" + #include <QtCore/QMap> +#include <QtWidgets/QDialog> #include <QtWidgets/QWidget> #include <array> #include <vector> +#include "colorpickerbutton.h" + #include "ui_controllerglobalsettingswidget.h" +#include "ui_controllerledsettingsdialog.h" class ControllerSettingsDialog; @@ -28,7 +33,23 @@ Q_SIGNALS: private: void updateSDLOptionsEnabled(); + void ledSettingsClicked(); Ui::ControllerGlobalSettingsWidget m_ui; ControllerSettingsDialog* m_dialog; }; + +class ControllerLEDSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + ControllerLEDSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog); + ~ControllerLEDSettingsDialog(); + +private: + void linkButton(ColorPickerButton* button, u32 player_id); + + Ui::ControllerLEDSettingsDialog m_ui; + ControllerSettingsDialog* m_dialog; +}; diff --git a/src/duckstation-qt/controllerglobalsettingswidget.ui b/src/duckstation-qt/controllerglobalsettingswidget.ui index fd3ed2b38..bcb411796 100644 --- a/src/duckstation-qt/controllerglobalsettingswidget.ui +++ b/src/duckstation-qt/controllerglobalsettingswidget.ui @@ -151,11 +151,27 @@ </widget> </item> <item row="1" column="1"> - <widget class="QCheckBox" name="enableSDLEnhancedMode"> - <property name="text"> - <string>DualShock 4 / DualSense Enhanced Mode</string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QCheckBox" name="enableSDLEnhancedMode"> + <property name="text"> + <string>DualShock 4 / DualSense Enhanced Mode</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="ledSettings"> + <property name="toolTip"> + <string>Controller LED Settings</string> + </property> + <property name="icon"> + <iconset theme="lightbulb-line"> + <normaloff>.</normaloff>. + </iconset> + </property> + </widget> + </item> + </layout> </item> </layout> </widget> diff --git a/src/duckstation-qt/controllerledsettingsdialog.ui b/src/duckstation-qt/controllerledsettingsdialog.ui new file mode 100644 index 000000000..87f9aa9c2 --- /dev/null +++ b/src/duckstation-qt/controllerledsettingsdialog.ui @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ControllerLEDSettingsDialog</class> + <widget class="QDialog" name="ControllerLEDSettingsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>501</width> + <height>108</height> + </rect> + </property> + <property name="windowTitle"> + <string>Controller LED Settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>SDL-0 LED</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="ColorPickerButton" name="SDL0LED"/> + </item> + </layout> + </widget> + </item> + <item row="0" column="1"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>SDL-1 LED</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="ColorPickerButton" name="SDL1LED"/> + </item> + </layout> + </widget> + </item> + <item row="0" column="2"> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>SDL-2 LED</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="ColorPickerButton" name="SDL2LED"/> + </item> + </layout> + </widget> + </item> + <item row="0" column="3"> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>SDL-3 LED</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="ColorPickerButton" name="SDL3LED"/> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" colspan="4"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ColorPickerButton</class> + <extends>QPushButton</extends> + <header>colorpickerbutton.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index 465ab924e..f01010de3 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -12,6 +12,7 @@ <ClCompile Include="cheatmanagerdialog.cpp" /> <ClCompile Include="cheatcodeeditordialog.cpp" /> <ClCompile Include="collapsiblewidget.cpp" /> + <ClCompile Include="colorpickerbutton.cpp" /> <ClCompile Include="consolesettingswidget.cpp" /> <ClCompile Include="controllerbindingwidgets.cpp" /> <ClCompile Include="controllerglobalsettingswidget.cpp" /> @@ -79,6 +80,7 @@ <QtMoc Include="controllerbindingwidgets.h" /> <QtMoc Include="controllerglobalsettingswidget.h" /> <QtMoc Include="controllersettingsdialog.h" /> + <QtMoc Include="colorpickerbutton.h" /> <ClInclude Include="controllersettingwidgetbinder.h" /> <QtMoc Include="memoryviewwidget.h" /> <ClInclude Include="resource.h" /> @@ -232,6 +234,7 @@ <ClCompile Include="$(IntDir)moc_cheatmanagerdialog.cpp" /> <ClCompile Include="$(IntDir)moc_cheatcodeeditordialog.cpp" /> <ClCompile Include="$(IntDir)moc_collapsiblewidget.cpp" /> + <ClCompile Include="$(IntDir)moc_colorpickerbutton.cpp" /> <ClCompile Include="$(IntDir)moc_consolesettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_controllerbindingwidgets.cpp" /> <ClCompile Include="$(IntDir)moc_controllerglobalsettingswidget.cpp" /> @@ -319,6 +322,9 @@ </QtTs> </ItemGroup> <ItemGroup> + <QtUi Include="controllerledsettingsdialog.ui"> + <FileType>Document</FileType> + </QtUi> <None Include="translations\duckstation-qt_es-es.ts" /> <None Include="translations\duckstation-qt_tr.ts" /> </ItemGroup> diff --git a/src/duckstation-qt/duckstation-qt.vcxproj.filters b/src/duckstation-qt/duckstation-qt.vcxproj.filters index a484f2ef8..e98ec568c 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj.filters +++ b/src/duckstation-qt/duckstation-qt.vcxproj.filters @@ -91,6 +91,9 @@ <ClCompile Include="$(IntDir)moc_foldersettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp" /> <ClCompile Include="qttranslations.cpp" /> + <ClCompile Include="coverdownloaddialog.cpp" /> + <ClCompile Include="$(IntDir)moc_coverdownloaddialog.cpp" /> + <ClCompile Include="colorpickerbutton.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="qtutils.h" /> @@ -150,6 +153,8 @@ <QtMoc Include="gamelistrefreshthread.h" /> <QtMoc Include="gamesummarywidget.h" /> <QtMoc Include="foldersettingswidget.h" /> + <QtMoc Include="coverdownloaddialog.h" /> + <QtMoc Include="colorpickerbutton.h" /> </ItemGroup> <ItemGroup> <QtUi Include="consolesettingswidget.ui" /> @@ -189,6 +194,8 @@ <QtUi Include="controllermacrowidget.ui" /> <QtUi Include="controllermacroeditwidget.ui" /> <QtUi Include="controllerbindingwidget_mouse.ui" /> + <QtUi Include="coverdownloaddialog.ui" /> + <QtUi Include="controllerledsettingsdialog.ui" /> </ItemGroup> <ItemGroup> <Natvis Include="qt5.natvis" /> diff --git a/src/duckstation-qt/resources/icons/black/svg/lightbulb-line.svg b/src/duckstation-qt/resources/icons/black/svg/lightbulb-line.svg new file mode 100644 index 000000000..9370abc1a --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/lightbulb-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M9.973 18H11v-5h2v5h1.027c.132-1.202.745-2.194 1.74-3.277.113-.122.832-.867.917-.973a6 6 0 1 0-9.37-.002c.086.107.807.853.918.974.996 1.084 1.609 2.076 1.741 3.278zM10 20v1h4v-1h-4zm-4.246-5a8 8 0 1 1 12.49.002C17.624 15.774 16 17 16 18.5V21a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2.5C8 17 6.375 15.774 5.754 15z" fill="#000000"/></svg> \ No newline at end of file diff --git a/src/duckstation-qt/resources/icons/white/svg/lightbulb-line.svg b/src/duckstation-qt/resources/icons/white/svg/lightbulb-line.svg new file mode 100644 index 000000000..454dc0fe7 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/lightbulb-line.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M9.973 18H11v-5h2v5h1.027c.132-1.202.745-2.194 1.74-3.277.113-.122.832-.867.917-.973a6 6 0 1 0-9.37-.002c.086.107.807.853.918.974.996 1.084 1.609 2.076 1.741 3.278zM10 20v1h4v-1h-4zm-4.246-5a8 8 0 1 1 12.49.002C17.624 15.774 16 17 16 18.5V21a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-2.5C8 17 6.375 15.774 5.754 15z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/duckstation-qt/resources/resources.qrc b/src/duckstation-qt/resources/resources.qrc index 7fad1efe8..5c833fe9b 100644 --- a/src/duckstation-qt/resources/resources.qrc +++ b/src/duckstation-qt/resources/resources.qrc @@ -181,6 +181,7 @@ <file>icons/black/svg/image-fill.svg</file> <file>icons/black/svg/keyboard-line.svg</file> <file>icons/black/svg/layout-grid-line.svg</file> + <file>icons/black/svg/lightbulb-line.svg</file> <file>icons/black/svg/list-check.svg</file> <file>icons/black/svg/mouse-line.svg</file> <file>icons/black/svg/paint-brush-line.svg</file> @@ -512,6 +513,7 @@ <file>icons/white/svg/image-fill.svg</file> <file>icons/white/svg/keyboard-line.svg</file> <file>icons/white/svg/layout-grid-line.svg</file> + <file>icons/white/svg/lightbulb-line.svg</file> <file>icons/white/svg/list-check.svg</file> <file>icons/white/svg/mouse-line.svg</file> <file>icons/white/svg/paint-brush-line.svg</file> diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index 2dd5d869e..6787bf87c 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -101,8 +101,8 @@ if(SDL2_FOUND) sdl_input_source.h ) target_compile_definitions(frontend-common PUBLIC "WITH_SDL2=1") - target_include_directories(frontend-common PRIVATE ${SDL2_INCLUDE_DIRS}) - target_link_libraries(frontend-common PRIVATE ${SDL2_LIBRARIES}) + target_include_directories(frontend-common PUBLIC ${SDL2_INCLUDE_DIRS}) + target_link_libraries(frontend-common PUBLIC ${SDL2_LIBRARIES}) # Copy bundled SDL2 to output on Windows. if(WIN32) diff --git a/src/frontend-common/sdl_input_source.cpp b/src/frontend-common/sdl_input_source.cpp index 4ce09d84d..d8d7eeb0f 100644 --- a/src/frontend-common/sdl_input_source.cpp +++ b/src/frontend-common/sdl_input_source.cpp @@ -88,6 +88,18 @@ static constexpr const char* s_sdl_hat_direction_names[] = { // clang-format on }; +static constexpr const char* s_sdl_default_led_colors[] = { + "0000ff", // SDL-0 + "ff0000", // SDL-1 + "00ff00", // SDL-2 + "ffff00", // SDL-3 +}; + +static void SetControllerRGBLED(SDL_GameController* gc, u32 color) +{ + SDL_GameControllerSetLED(gc, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); +} + SDLInputSource::SDLInputSource() = default; SDLInputSource::~SDLInputSource() @@ -149,6 +161,39 @@ void SDLInputSource::LoadSettings(SettingsInterface& si) { m_controller_enhanced_mode = si.GetBoolValue("InputSources", "SDLControllerEnhancedMode", false); m_sdl_hints = si.GetKeyValueList("SDLHints"); + + for (u32 i = 0; i < MAX_LED_COLORS; i++) + { + const u32 color = GetRGBForPlayerId(si, i); + if (m_led_colors[i] == color) + continue; + + m_led_colors[i] = color; + + const auto it = GetControllerDataForPlayerId(i); + if (it == m_controllers.end() || !it->game_controller || !SDL_GameControllerHasLED(it->game_controller)) + continue; + + SetControllerRGBLED(it->game_controller, color); + } +} + +u32 SDLInputSource::GetRGBForPlayerId(SettingsInterface& si, u32 player_id) +{ + return ParseRGBForPlayerId( + si.GetStringValue("SDLExtra", fmt::format("Player{}LED", player_id).c_str(), s_sdl_default_led_colors[player_id]), + player_id); +} + +u32 SDLInputSource::ParseRGBForPlayerId(const std::string_view& str, u32 player_id) +{ + if (player_id >= MAX_LED_COLORS) + return 0; + + const u32 default_color = StringUtil::FromChars<u32>(s_sdl_default_led_colors[player_id], 16).value_or(0); + const u32 color = StringUtil::FromChars<u32>(str, 16).value_or(default_color); + + return color; } void SDLInputSource::SetHints() @@ -624,6 +669,12 @@ bool SDLInputSource::OpenDevice(int index, bool is_gamecontroller) if (!cd.haptic && !cd.use_game_controller_rumble) Log_VerbosePrintf("(SDLInputSource) Rumble is not supported on '%s'", name); + if (player_id >= 0 && static_cast<u32>(player_id) < MAX_LED_COLORS && gcontroller && + SDL_GameControllerHasLED(gcontroller)) + { + SetControllerRGBLED(gcontroller, m_led_colors[player_id]); + } + m_controllers.push_back(std::move(cd)); InputManager::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name); diff --git a/src/frontend-common/sdl_input_source.h b/src/frontend-common/sdl_input_source.h index 5daf1d341..5cd1db13f 100644 --- a/src/frontend-common/sdl_input_source.h +++ b/src/frontend-common/sdl_input_source.h @@ -14,6 +14,8 @@ class SettingsInterface; class SDLInputSource final : public InputSource { public: + static constexpr u32 MAX_LED_COLORS = 4; + SDLInputSource(); ~SDLInputSource(); @@ -38,6 +40,9 @@ public: SDL_Joystick* GetJoystickForDevice(const std::string_view& device); + static u32 GetRGBForPlayerId(SettingsInterface& si, u32 player_id); + static u32 ParseRGBForPlayerId(const std::string_view& str, u32 player_id); + private: struct ControllerData { @@ -82,5 +87,6 @@ private: bool m_sdl_subsystem_initialized = false; bool m_controller_enhanced_mode = false; + std::array<u32, MAX_LED_COLORS> m_led_colors{}; std::vector<std::pair<std::string, std::string>> m_sdl_hints; };