Qt: Add Controller LED options (where supported)

This commit is contained in:
Stenzek 2023-01-15 16:40:35 +10:00
parent 722771fff6
commit c393db419e
15 changed files with 331 additions and 10 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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
}

View file

@ -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;
};

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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>

After

Width:  |  Height:  |  Size: 458 B

View file

@ -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>

After

Width:  |  Height:  |  Size: 458 B

View file

@ -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>

View file

@ -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)

View file

@ -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);

View file

@ -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;
};