summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Source/Core/Core/CheatSearch.cpp9
-rw-r--r--Source/Core/Core/CheatSearch.h4
-rw-r--r--Source/Core/Core/Config/MainSettings.cpp17
-rw-r--r--Source/Core/Core/Config/MainSettings.h7
-rw-r--r--Source/Core/Core/PowerPC/BreakPoints.cpp14
-rw-r--r--Source/Core/Core/PowerPC/BreakPoints.h10
-rw-r--r--Source/Core/Core/PowerPC/PowerPC.cpp3
-rw-r--r--Source/Core/DolphinQt/CMakeLists.txt2
-rw-r--r--Source/Core/DolphinQt/CheatSearchWidget.cpp45
-rw-r--r--Source/Core/DolphinQt/CheatSearchWidget.h1
-rw-r--r--Source/Core/DolphinQt/Config/CommonControllersWidget.cpp13
-rw-r--r--Source/Core/DolphinQt/Config/CommonControllersWidget.h2
-rw-r--r--Source/Core/DolphinQt/Config/LogWidget.cpp3
-rw-r--r--Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.cpp313
-rw-r--r--Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.h48
-rw-r--r--Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp55
-rw-r--r--Source/Core/DolphinQt/Debugger/BreakpointWidget.h2
-rw-r--r--Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp3
-rw-r--r--Source/Core/DolphinQt/DolphinQt.vcxproj2
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp34
20 files changed, 570 insertions, 17 deletions
diff --git a/Source/Core/Core/CheatSearch.cpp b/Source/Core/Core/CheatSearch.cpp
index 2cf509e880..063a09a582 100644
--- a/Source/Core/Core/CheatSearch.cpp
+++ b/Source/Core/Core/CheatSearch.cpp
@@ -284,6 +284,15 @@ void Cheats::CheatSearchSession<T>::ResetResults()
}
template <typename T>
+void Cheats::CheatSearchSession<T>::RemoveResult(size_t index)
+{
+ if (index < m_search_results.size())
+ {
+ m_search_results.erase(m_search_results.begin() + index);
+ }
+}
+
+template <typename T>
static std::function<bool(const T& new_value)>
MakeCompareFunctionForSpecificValue(Cheats::CompareType op, const T& old_value)
{
diff --git a/Source/Core/Core/CheatSearch.h b/Source/Core/Core/CheatSearch.h
index 7df135d51a..7d1c089835 100644
--- a/Source/Core/Core/CheatSearch.h
+++ b/Source/Core/Core/CheatSearch.h
@@ -168,6 +168,9 @@ public:
virtual bool WriteValue(const Core::CPUThreadGuard& guard, std::span<u32> addresses) const = 0;
+ // User can delete a search result.
+ virtual void RemoveResult(size_t index) = 0;
+
// Create a complete copy of this search session.
virtual std::unique_ptr<CheatSearchSessionBase> Clone() const = 0;
@@ -195,6 +198,7 @@ public:
bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) override;
void ResetResults() override;
+ void RemoveResult(size_t index) override;
SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) override;
size_t GetMemoryRangeCount() const override;
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp
index 6d5f260f07..441c5dd031 100644
--- a/Source/Core/Core/Config/MainSettings.cpp
+++ b/Source/Core/Core/Config/MainSettings.cpp
@@ -529,6 +529,23 @@ const Info<bool> MAIN_MOVIE_SHOW_OSD{{System::Main, "Movie", "ShowMovieWindow"},
const Info<bool> MAIN_INPUT_BACKGROUND_INPUT{{System::Main, "Input", "BackgroundInput"}, false};
+// Main.SDL_Hints
+
+// Defaults for these values are written in SDL.cpp so they appear in the config file, and thus show
+// up in the SDL Hints config window (default values defined here would not be written).
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS{
+ {System::Main, "SDL_Hints", "SDL_JOYSTICK_ENHANCED_REPORTS"}, ""};
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI{{System::Main, "SDL_Hints", "SDL_JOYSTICK_WGI"},
+ ""};
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED{
+ {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"}, ""};
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT{
+ {System::Main, "SDL_Hints", "SDL_JOYSTICK_DIRECTINPUT"}, ""};
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS{
+ {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"}, ""};
+const Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS{
+ {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS"}, ""};
+
// Main.Debug
const Info<bool> MAIN_DEBUG_JIT_OFF{{System::Main, "Debug", "JitOff"}, false};
diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h
index 38c8fdc365..1946045e48 100644
--- a/Source/Core/Core/Config/MainSettings.h
+++ b/Source/Core/Core/Config/MainSettings.h
@@ -338,6 +338,13 @@ extern const Info<bool> MAIN_MOVIE_SHOW_OSD;
extern const Info<bool> MAIN_INPUT_BACKGROUND_INPUT;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_WGI;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS;
+extern const Config::Info<std::string> MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS;
+
// Main.Debug
extern const Info<bool> MAIN_DEBUG_JIT_OFF;
diff --git a/Source/Core/Core/PowerPC/BreakPoints.cpp b/Source/Core/Core/PowerPC/BreakPoints.cpp
index 3cf564663e..e5dca73989 100644
--- a/Source/Core/Core/PowerPC/BreakPoints.cpp
+++ b/Source/Core/Core/PowerPC/BreakPoints.cpp
@@ -34,6 +34,9 @@ bool BreakPoints::IsAddressBreakPoint(u32 address) const
bool BreakPoints::IsBreakPointEnable(u32 address) const
{
+ if (!m_breaking_enabled)
+ return false;
+
const TBreakPoint* bp = GetBreakpoint(address);
return bp != nullptr && bp->is_enabled;
}
@@ -184,6 +187,11 @@ bool BreakPoints::ToggleEnable(u32 address)
return true;
}
+void BreakPoints::EnableBreaking(bool enable)
+{
+ m_breaking_enabled = enable;
+}
+
bool BreakPoints::Remove(u32 address)
{
const auto iter = std::ranges::find(m_breakpoints, address, &TBreakPoint::address);
@@ -318,6 +326,12 @@ bool MemChecks::ToggleEnable(u32 address)
return true;
}
+void MemChecks::EnableBreaking(bool enabled)
+{
+ m_breaking_enabled = enabled;
+ Update();
+}
+
DelayedMemCheckUpdate MemChecks::Remove(u32 address)
{
const auto iter = std::ranges::find(m_mem_checks, address, &TMemCheck::start_address);
diff --git a/Source/Core/Core/PowerPC/BreakPoints.h b/Source/Core/Core/PowerPC/BreakPoints.h
index de22192f7a..da5e755dd3 100644
--- a/Source/Core/Core/PowerPC/BreakPoints.h
+++ b/Source/Core/Core/PowerPC/BreakPoints.h
@@ -87,6 +87,9 @@ public:
bool ToggleBreakPoint(u32 address);
bool ToggleEnable(u32 address);
+ void EnableBreaking(bool enable);
+ bool IsBreakingEnabled() const { return m_breaking_enabled; }
+
// Remove Breakpoint. Returns whether it was removed.
bool Remove(u32 address);
void Clear();
@@ -96,6 +99,7 @@ private:
TBreakPoints m_breakpoints;
std::optional<TBreakPoint> m_temp_breakpoint;
Core::System& m_system;
+ bool m_breaking_enabled = true;
};
class DelayedMemCheckUpdate;
@@ -126,9 +130,12 @@ public:
bool OverlapsMemcheck(u32 address, u32 length) const;
DelayedMemCheckUpdate Remove(u32 address);
+ void EnableBreaking(bool enable);
+ bool IsBreakingEnabled() const { return m_breaking_enabled; }
+
void Update();
void Clear();
- bool HasAny() const { return !m_mem_checks.empty(); }
+ bool HasAny() const { return !m_mem_checks.empty() && m_breaking_enabled; }
BitSet32 GetGPRsUsedInConditions() { return m_gprs_used_in_conditions; }
BitSet32 GetFPRsUsedInConditions() { return m_fprs_used_in_conditions; }
@@ -142,6 +149,7 @@ private:
BitSet32 m_gprs_used_in_conditions;
BitSet32 m_fprs_used_in_conditions;
bool m_mem_breakpoints_set = false;
+ bool m_breaking_enabled = true;
};
class DelayedMemCheckUpdate final
diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp
index e5ce8ffcdb..7939b6d4bc 100644
--- a/Source/Core/Core/PowerPC/PowerPC.cpp
+++ b/Source/Core/Core/PowerPC/PowerPC.cpp
@@ -639,7 +639,8 @@ bool PowerPCManager::CheckBreakPoints()
{
const TBreakPoint* bp = m_breakpoints.GetBreakpoint(m_ppc_state.pc);
- if (!bp || !bp->is_enabled || !EvaluateCondition(m_system, bp->condition))
+ if (!m_breakpoints.IsBreakingEnabled() || !bp || !bp->is_enabled ||
+ !EvaluateCondition(m_system, bp->condition))
return false;
if (bp->log_on_hit)
diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index c61edc244b..e7e70c508b 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -69,6 +69,8 @@ add_executable(dolphin-emu
Config/ConfigControls/ConfigUserPath.h
Config/ControllerInterface/ControllerInterfaceWindow.cpp
Config/ControllerInterface/ControllerInterfaceWindow.h
+ Config/SDLHints/SDLHintsWindow.cpp
+ Config/SDLHints/SDLHintsWindow.h
Config/ControllerInterface/DualShockUDPClientAddServerDialog.cpp
Config/ControllerInterface/DualShockUDPClientAddServerDialog.h
Config/ControllerInterface/DualShockUDPClientWidget.cpp
diff --git a/Source/Core/DolphinQt/CheatSearchWidget.cpp b/Source/Core/DolphinQt/CheatSearchWidget.cpp
index 629d8dbafb..35468d5fcc 100644
--- a/Source/Core/DolphinQt/CheatSearchWidget.cpp
+++ b/Source/Core/DolphinQt/CheatSearchWidget.cpp
@@ -3,7 +3,9 @@
#include "DolphinQt/CheatSearchWidget.h"
+#include <algorithm>
#include <optional>
+#include <ranges>
#include <string>
#include <unordered_map>
#include <utility>
@@ -224,6 +226,7 @@ void CheatSearchWidget::CreateWidgets()
m_address_table = new QTableWidget();
m_address_table->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_address_table->setSelectionBehavior(QAbstractItemView::SelectRows);
m_info_label_1 = new QLabel(tr("Waiting for first scan..."));
m_info_label_2 = new QLabel();
@@ -483,6 +486,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
if (m_address_table->selectedItems().isEmpty())
return;
+ std::vector<const QTableWidgetItem*> selected_items = GetSelectedAddressTableItems();
+
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
@@ -491,8 +496,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
emit ShowMemory(address);
});
- menu->addAction(tr("Add to watch"), this, [this] {
- for (auto* const item : m_address_table->selectedItems())
+ menu->addAction(tr("Add to watch"), this, [this, selected_items] {
+ for (auto* const item : selected_items)
{
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
const QString name = QStringLiteral("mem_%1").arg(address, 8, 16, QLatin1Char('0'));
@@ -501,6 +506,15 @@ void CheatSearchWidget::OnAddressTableContextMenu()
});
menu->addAction(tr("Generate Action Replay Code(s)"), this, &CheatSearchWidget::GenerateARCodes);
menu->addAction(tr("Write value"), this, &CheatSearchWidget::WriteValue);
+ menu->addAction(tr("Delete Address"), this, [this, selected_items] {
+ // Process in reverse so removal won't change the index of items about to be processed.
+ for (auto* const item : selected_items | std::views::reverse)
+ {
+ const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
+ m_last_value_session->RemoveResult(index);
+ }
+ RecreateGUITable();
+ });
menu->exec(QCursor::pos());
}
@@ -533,7 +547,7 @@ void CheatSearchWidget::GenerateARCodes()
bool had_multiple_errors = false;
std::optional<Cheats::GenerateActionReplayCodeErrorCode> error_code;
- for (auto* const item : m_address_table->selectedItems())
+ for (auto* const item : GetSelectedAddressTableItems())
{
const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
const auto result = Cheats::GenerateActionReplayCode(*m_last_value_session, index);
@@ -600,9 +614,9 @@ void CheatSearchWidget::WriteValue()
return;
}
- auto items = m_address_table->selectedItems();
+ auto items = GetSelectedAddressTableItems();
std::vector<u32> addresses(items.size());
- std::transform(items.begin(), items.end(), addresses.begin(), [](QTableWidgetItem* item) {
+ std::transform(items.begin(), items.end(), addresses.begin(), [](const QTableWidgetItem* item) {
return item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
});
Core::CPUThreadGuard guard{m_system};
@@ -610,6 +624,7 @@ void CheatSearchWidget::WriteValue()
{
m_info_label_1->setText(tr("There was an error writing (some) values."));
}
+ UpdateTableAllCurrentValues(UpdateSource::User);
}
size_t CheatSearchWidget::GetTableRowCount() const
@@ -638,6 +653,26 @@ void CheatSearchWidget::RefreshGUICurrentValues(const size_t begin_index, const
}
}
+const std::vector<const QTableWidgetItem*> CheatSearchWidget::GetSelectedAddressTableItems() const
+{
+ // Don't process each selectedItems(), as it can produce duplicate commands for one address when
+ // multiple items in the same row are selected. Instead, uses rows and gets one item from each
+ // row. All row items have identical data.
+ auto selected_rows = m_address_table->selectionModel()->selectedRows();
+
+ // Ascending address order.
+ std::sort(selected_rows.begin(), selected_rows.end(),
+ [](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
+
+ std::vector<const QTableWidgetItem*> selected_items;
+ for (const auto& index : selected_rows)
+ {
+ const int row = index.row();
+ selected_items.push_back(m_address_table->item(row, 0));
+ }
+ return selected_items;
+}
+
void CheatSearchWidget::RecreateGUITable()
{
const QSignalBlocker blocker(m_address_table);
diff --git a/Source/Core/DolphinQt/CheatSearchWidget.h b/Source/Core/DolphinQt/CheatSearchWidget.h
index 18aa5a14b0..5dec20c3ed 100644
--- a/Source/Core/DolphinQt/CheatSearchWidget.h
+++ b/Source/Core/DolphinQt/CheatSearchWidget.h
@@ -75,6 +75,7 @@ private:
int GetVisibleRowsBeginIndex() const;
int GetVisibleRowsEndIndex() const;
size_t GetTableRowCount() const;
+ const std::vector<const QTableWidgetItem*> GetSelectedAddressTableItems() const;
Core::System& m_system;
diff --git a/Source/Core/DolphinQt/Config/CommonControllersWidget.cpp b/Source/Core/DolphinQt/Config/CommonControllersWidget.cpp
index 7a5e1256af..6d905eeb8c 100644
--- a/Source/Core/DolphinQt/Config/CommonControllersWidget.cpp
+++ b/Source/Core/DolphinQt/Config/CommonControllersWidget.cpp
@@ -11,6 +11,7 @@
#include "Core/Config/MainSettings.h"
#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"
+#include "DolphinQt/Config/SDLHints/SDLHintsWindow.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
@@ -33,9 +34,11 @@ void CommonControllersWidget::CreateLayout()
m_common_bg_input = new QCheckBox(tr("Background Input"));
m_common_configure_controller_interface =
new NonDefaultQPushButton(tr("Alternate Input Sources"));
+ m_common_configure_sdl_hints = new NonDefaultQPushButton(tr("SDL Controller Settings"));
m_common_layout->addWidget(m_common_bg_input);
m_common_layout->addWidget(m_common_configure_controller_interface);
+ m_common_layout->addWidget(m_common_configure_sdl_hints);
m_common_box->setLayout(m_common_layout);
@@ -51,6 +54,8 @@ void CommonControllersWidget::ConnectWidgets()
connect(m_common_bg_input, &QCheckBox::toggled, this, &CommonControllersWidget::SaveSettings);
connect(m_common_configure_controller_interface, &QPushButton::clicked, this,
&CommonControllersWidget::OnControllerInterfaceConfigure);
+ connect(m_common_configure_sdl_hints, &QPushButton::clicked, this,
+ &CommonControllersWidget::OnSDLHintConfigure);
}
void CommonControllersWidget::OnControllerInterfaceConfigure()
@@ -61,6 +66,14 @@ void CommonControllersWidget::OnControllerInterfaceConfigure()
window->show();
}
+void CommonControllersWidget::OnSDLHintConfigure()
+{
+ SDLHintsWindow* window = new SDLHintsWindow(this);
+ window->setAttribute(Qt::WA_DeleteOnClose, true);
+ window->setWindowModality(Qt::WindowModality::WindowModal);
+ window->show();
+}
+
void CommonControllersWidget::LoadSettings()
{
SignalBlocking(m_common_bg_input)->setChecked(Config::Get(Config::MAIN_INPUT_BACKGROUND_INPUT));
diff --git a/Source/Core/DolphinQt/Config/CommonControllersWidget.h b/Source/Core/DolphinQt/Config/CommonControllersWidget.h
index 42d23f74a4..ee0d82513c 100644
--- a/Source/Core/DolphinQt/Config/CommonControllersWidget.h
+++ b/Source/Core/DolphinQt/Config/CommonControllersWidget.h
@@ -18,6 +18,7 @@ public:
private:
void OnControllerInterfaceConfigure();
+ void OnSDLHintConfigure();
void CreateLayout();
void ConnectWidgets();
@@ -29,4 +30,5 @@ private:
QVBoxLayout* m_common_layout;
QCheckBox* m_common_bg_input;
QPushButton* m_common_configure_controller_interface;
+ QPushButton* m_common_configure_sdl_hints;
};
diff --git a/Source/Core/DolphinQt/Config/LogWidget.cpp b/Source/Core/DolphinQt/Config/LogWidget.cpp
index d076b3a9ac..1b59411c08 100644
--- a/Source/Core/DolphinQt/Config/LogWidget.cpp
+++ b/Source/Core/DolphinQt/Config/LogWidget.cpp
@@ -182,8 +182,7 @@ void LogWidget::LoadSettings()
Qt::ScrollBarAlwaysOn);
// Log - Font Selection
- // Currently "Debugger Font" is not supported as there is no Qt Debugger, defaulting to Monospace
- m_log_font->setCurrentIndex(std::min(settings.value(QStringLiteral("logging/font")).toInt(), 1));
+ m_log_font->setCurrentIndex(settings.value(QStringLiteral("logging/font")).toInt());
UpdateFont();
}
diff --git a/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.cpp b/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.cpp
new file mode 100644
index 0000000000..baa3a13be8
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.cpp
@@ -0,0 +1,313 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "DolphinQt/Config/SDLHints/SDLHintsWindow.h"
+
+#include "Common/Config/Config.h"
+
+#include "Core/Config/MainSettings.h"
+
+#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
+#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
+#include "DolphinQt/QtUtils/QtUtils.h"
+#include "DolphinQt/QtUtils/SignalBlocking.h"
+
+#include <QDialogButtonBox>
+#include <QFrame>
+#include <QHeaderView>
+#include <QLabel>
+#include <QPushButton>
+#include <QResizeEvent>
+#include <QTabWidget>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+SDLHintsWindow::SDLHintsWindow(QWidget* parent) : QDialog(parent)
+{
+ CreateMainLayout();
+
+ setWindowTitle(tr("SDL Controller Settings"));
+}
+
+QSize SDLHintsWindow::sizeHint() const
+{
+ return {450, 0};
+}
+
+void SDLHintsWindow::CreateMainLayout()
+{
+ setMinimumWidth(300);
+ setMinimumHeight(270);
+
+ m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
+ connect(m_button_box, &QDialogButtonBox::rejected, this, &SDLHintsWindow::OnClose);
+
+ // Create hints table
+ m_hints_table = new QTableWidget(0, 2);
+ QHeaderView* const hints_table_header = m_hints_table->horizontalHeader();
+ m_hints_table->setHorizontalHeaderLabels({tr("Name"), tr("Value")});
+ m_hints_table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_hints_table->setSelectionMode(QTableWidget::SingleSelection);
+ hints_table_header->setSectionResizeMode(0, QHeaderView::Interactive);
+ hints_table_header->setSectionResizeMode(1, QHeaderView::Fixed);
+ hints_table_header->setMinimumSectionSize(60);
+ m_hints_table->verticalHeader()->setVisible(false);
+ hints_table_header->installEventFilter(this);
+ QObject::connect(hints_table_header, &QHeaderView::sectionResized, this,
+ &SDLHintsWindow::SectionResized);
+
+ PopulateTable();
+
+ // Create table buttons
+ auto* const add_row_btn = new NonDefaultQPushButton(tr("Add"));
+ connect(add_row_btn, &QPushButton::pressed, this, &SDLHintsWindow::AddRow);
+
+ m_rem_row_btn = new NonDefaultQPushButton(tr("Remove"));
+ m_rem_row_btn->setEnabled(false);
+ connect(m_rem_row_btn, &QPushButton::pressed, this, &SDLHintsWindow::RemoveRow);
+ connect(m_hints_table, &QTableWidget::itemSelectionChanged, this,
+ &SDLHintsWindow::SelectionChanged);
+
+ auto* const btns_layout = new QDialogButtonBox;
+ btns_layout->setContentsMargins(0, 0, 5, 5);
+ btns_layout->addButton(add_row_btn, QDialogButtonBox::ActionRole);
+ btns_layout->addButton(m_rem_row_btn, QDialogButtonBox::ActionRole);
+
+ // Create advanced tab
+ auto* advanced_layout = new QVBoxLayout();
+ advanced_layout->addWidget(m_hints_table);
+ advanced_layout->addWidget(btns_layout);
+
+ auto* advanced_frame = new QFrame();
+ advanced_frame->setLayout(advanced_layout);
+
+ // Create default tab
+ m_directinput_detection = new ToolTipCheckBox(tr("Enable DirectInput Detection"));
+ m_directinput_detection->SetDescription(
+ tr("Controls whether SDL should use DirectInput for detecting controllers. Enabling this "
+ "fixes hotplug detection issues with DualSense controllers but causes Dolphin to hang up "
+ "on shutdown when using certain 8BitDo controllers.<br><br><dolphin_emphasis>If unsure, "
+ "leave this checked.</dolphin_emphasis>"));
+ connect(m_directinput_detection, &ToolTipCheckBox::toggled, [](bool checked) {
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT, checked ? "1" : "0");
+ });
+
+ m_combine_joy_cons = new ToolTipCheckBox(tr("Use Joy-Con Pairs as a Single Controller"));
+ m_combine_joy_cons->SetDescription(
+ tr("Controls whether SDL should treat a pair of Joy-Con as a single controller or as two "
+ "separate controllers.<br><br><dolphin_emphasis>If unsure, leave this "
+ "checked.</dolphin_emphasis>"));
+ connect(m_combine_joy_cons, &ToolTipCheckBox::toggled, [](bool checked) {
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, checked ? "1" : "0");
+ });
+
+ m_horizontal_joy_cons = new ToolTipCheckBox(tr("Sideways Joy-Con"));
+ m_horizontal_joy_cons->SetDescription(
+ tr("Defines the default orientation for individual Joy-Con. This setting has no effect when "
+ "Use Joy-Con Pairs as a Single Controller is "
+ "enabled.<br><br><dolphin_emphasis>If unsure, "
+ "leave this checked.</dolphin_emphasis>"));
+ connect(m_horizontal_joy_cons, &ToolTipCheckBox::toggled, [](bool checked) {
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, checked ? "0" : "1");
+ });
+
+ m_dualsense_player_led = new ToolTipCheckBox(tr("Enable DualSense Player LEDs"));
+ m_dualsense_player_led->SetDescription(
+ tr("Controls whether the player LEDs should be lit to indicate which player is associated "
+ "with a DualSense controller.<br><br><dolphin_emphasis>If unsure, leave this "
+ "unchecked.</dolphin_emphasis>"));
+ connect(m_dualsense_player_led, &ToolTipCheckBox::toggled, [](bool checked) {
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, checked ? "1" : "0");
+ });
+
+ auto* const default_layout = new QVBoxLayout();
+ default_layout->setContentsMargins(10, 10, 10, 10);
+ default_layout->addWidget(m_directinput_detection);
+ default_layout->addWidget(m_combine_joy_cons);
+ default_layout->addWidget(m_horizontal_joy_cons);
+ default_layout->addWidget(m_dualsense_player_led);
+ default_layout->addStretch(1);
+
+ auto* const default_frame = new QFrame();
+ default_frame->setLayout(default_layout);
+
+ PopulateChecklist();
+
+ // Create the tab widget
+ m_tab_widget = new QTabWidget();
+ m_tab_widget->addTab(default_frame, tr("Main"));
+ m_tab_widget->addTab(advanced_frame, tr("Advanced"));
+
+ m_current_tab_index = 0;
+ m_tab_widget->setCurrentIndex(m_current_tab_index);
+
+ connect(m_tab_widget, &QTabWidget::currentChanged, this, &SDLHintsWindow::TabChanged);
+
+ auto* const warning_text =
+ new QLabel(tr("Dolphin must be restarted for these changes to take effect."));
+ warning_text->setWordWrap(true);
+
+ // Create main layout
+ auto* const main_layout = new QVBoxLayout();
+ main_layout->addWidget(
+ QtUtils::CreateIconWarning(this, QStyle::SP_MessageBoxWarning, warning_text), 0);
+ main_layout->addWidget(m_tab_widget, 1);
+ main_layout->addWidget(m_button_box, 0, Qt::AlignBottom | Qt::AlignRight);
+ setLayout(main_layout);
+}
+
+void SDLHintsWindow::PopulateTable()
+{
+ m_hints_table->setRowCount(0);
+
+ // Loop through all the values in the SDL_Hints settings section and load them into the table
+ std::shared_ptr<Config::Layer> layer = Config::GetLayer(Config::LayerType::Base);
+ const Config::Section& section = layer->GetSection(Config::System::Main, "SDL_Hints");
+ for (auto& row_data : section)
+ {
+ const Config::Location& location = row_data.first;
+ const std::optional<std::string>& value = row_data.second;
+
+ if (value)
+ {
+ m_hints_table->insertRow(m_hints_table->rowCount());
+ m_hints_table->setItem(m_hints_table->rowCount() - 1, 0,
+ new QTableWidgetItem(QString::fromStdString(location.key)));
+ m_hints_table->setItem(m_hints_table->rowCount() - 1, 1,
+ new QTableWidgetItem(QString::fromStdString(*value)));
+ }
+ }
+}
+
+void SDLHintsWindow::SaveTable()
+{
+ // Clear all the old values from the SDL_Hints section
+ std::shared_ptr<Config::Layer> layer = Config::GetLayer(Config::LayerType::Base);
+ Config::Section section = layer->GetSection(Config::System::Main, "SDL_Hints");
+
+ for (auto& row_data : section)
+ row_data.second.reset();
+
+ // Add each item still in the table to the config file
+ for (int row = 0; row < m_hints_table->rowCount(); ++row)
+ {
+ QTableWidgetItem* hint_name_item = m_hints_table->item(row, 0);
+ QTableWidgetItem* hint_value_item = m_hints_table->item(row, 1);
+
+ if (hint_name_item != nullptr && hint_value_item != nullptr)
+ {
+ const QString& hint_name = hint_name_item->text().trimmed();
+ const QString& hint_value = hint_value_item->text().trimmed();
+
+ if (!hint_name.isEmpty() && !hint_value.isEmpty())
+ {
+ const Config::Info<std::string> setting{
+ {Config::System::Main, "SDL_Hints", hint_name.toStdString()}, ""};
+ Config::SetBase(setting, hint_value.toStdString());
+ }
+ }
+ }
+}
+
+void SDLHintsWindow::PopulateChecklist()
+{
+ // Populate the checklist and default to the SDL default for an invalid value
+
+ // Default to checked if incorrectly set
+ SignalBlocking(m_directinput_detection)
+ ->setChecked(Config::GetBase(Config::MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT) != "0");
+
+ // Default to checked if incorrectly set
+ SignalBlocking(m_combine_joy_cons)
+ ->setChecked(Config::GetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS) != "0");
+
+ // Default to checked if incorrectly set
+ SignalBlocking(m_horizontal_joy_cons)
+ ->setChecked(Config::GetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS) != "1");
+
+ // Default to checked if incorrectly set
+ SignalBlocking(m_dualsense_player_led)
+ ->setChecked(Config::GetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED) != "0");
+}
+
+void SDLHintsWindow::AddRow()
+{
+ m_hints_table->insertRow(m_hints_table->rowCount());
+ m_hints_table->scrollToBottom();
+}
+
+void SDLHintsWindow::RemoveRow()
+{
+ QModelIndex index = m_hints_table->selectionModel()->currentIndex();
+ m_hints_table->removeRow(index.row());
+}
+
+void SDLHintsWindow::SelectionChanged()
+{
+ m_rem_row_btn->setEnabled(m_hints_table->selectionModel()->hasSelection());
+}
+
+void SDLHintsWindow::OnClose()
+{
+ TabChanged(-1); // Pass -1 to indicate exit
+
+ reject();
+}
+
+void SDLHintsWindow::TabChanged(int new_index)
+{
+ // Check which tab we're coming from, cur_tab_idx has not been updated yet
+ switch (m_current_tab_index)
+ {
+ case 1: // Coming from the advanced tab
+ SaveTable();
+ break;
+
+ default:
+ break;
+ }
+
+ // Check which tab we're going to
+ switch (new_index)
+ {
+ case 0: // Going to the main tab
+ PopulateChecklist();
+ break;
+
+ case 1: // Going to the advanced tab
+ PopulateTable();
+ break;
+
+ default:
+ break;
+ }
+
+ m_current_tab_index = new_index;
+}
+
+void SDLHintsWindow::SectionResized(int logical_index, int old_size, int new_size)
+{
+ if (logical_index == 0 && old_size != new_size)
+ {
+ QHeaderView* const header = m_hints_table->horizontalHeader();
+ header->setMaximumSectionSize(header->size().width() - header->minimumSectionSize());
+ header->resizeSection(1, header->size().width() - new_size);
+ }
+}
+
+bool SDLHintsWindow::eventFilter(QObject* obj, QEvent* event)
+{
+ auto* const table_widget = qobject_cast<QHeaderView*>(obj);
+ if (table_widget)
+ {
+ if (event->type() == QEvent::Resize)
+ {
+ auto* const resize_event = static_cast<QResizeEvent*>(event);
+ QHeaderView* header = m_hints_table->horizontalHeader();
+ header->setMaximumSectionSize(resize_event->size().width() - header->minimumSectionSize());
+ header->resizeSection(0, resize_event->size().width() - header->sectionSize(1));
+ }
+ }
+
+ return QDialog::eventFilter(obj, event);
+}
diff --git a/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.h b/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.h
new file mode 100644
index 0000000000..33103f9314
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.h
@@ -0,0 +1,48 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QDialog>
+
+class QTabWidget;
+class QDialogButtonBox;
+class QTableWidget;
+class QPushButton;
+class ToolTipCheckBox;
+
+class SDLHintsWindow final : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit SDLHintsWindow(QWidget* parent);
+
+ QSize sizeHint() const override;
+
+private:
+ void CreateMainLayout();
+
+ void PopulateTable();
+ void SaveTable();
+ void PopulateChecklist();
+ void AddRow();
+ void RemoveRow();
+ void SelectionChanged();
+ void OnClose();
+ void TabChanged(int new_index);
+ void SectionResized(int logical_index, int old_size, int new_size);
+
+ bool eventFilter(QObject* obj, QEvent* event) override;
+
+ QTabWidget* m_tab_widget;
+ QDialogButtonBox* m_button_box;
+ QTableWidget* m_hints_table;
+ QPushButton* m_rem_row_btn;
+
+ ToolTipCheckBox* m_directinput_detection;
+ ToolTipCheckBox* m_combine_joy_cons;
+ ToolTipCheckBox* m_horizontal_joy_cons;
+ ToolTipCheckBox* m_dualsense_player_led;
+
+ int m_current_tab_index;
+};
diff --git a/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp b/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp
index 09b045688c..fbb5c38d9b 100644
--- a/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp
+++ b/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp
@@ -173,6 +173,7 @@ void BreakpointWidget::CreateWidgets()
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(0);
+ m_enabled = m_toolbar->addAction(tr("Disable"), this, &BreakpointWidget::OnToggleBreaking);
m_new = m_toolbar->addAction(tr("New"), this, &BreakpointWidget::OnNewBreakpoint);
m_clear = m_toolbar->addAction(tr("Clear"), this, &BreakpointWidget::OnClear);
@@ -190,6 +191,10 @@ void BreakpointWidget::CreateWidgets()
void BreakpointWidget::UpdateIcons()
{
+ if (m_system.GetPowerPC().GetBreakPoints().IsBreakingEnabled())
+ m_enabled->setIcon(Resources::GetThemeIcon("pause"));
+ else
+ m_enabled->setIcon(Resources::GetThemeIcon("play"));
m_new->setIcon(Resources::GetThemeIcon("debugger_add_breakpoint"));
m_clear->setIcon(Resources::GetThemeIcon("debugger_clear"));
m_load->setIcon(Resources::GetThemeIcon("debugger_load"));
@@ -289,6 +294,24 @@ void BreakpointWidget::Update()
QPixmap enabled_icon =
Resources::GetThemeIcon("debugger_breakpoint").pixmap(QSize(downscale, downscale));
+ auto& power_pc = m_system.GetPowerPC();
+ auto& breakpoints = power_pc.GetBreakPoints();
+
+ if (!breakpoints.IsBreakingEnabled())
+ {
+ // Use QPainter to draw a transparent hole in the center
+ QImage image = enabled_icon.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QPainter painter(&image);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(Qt::transparent);
+ // Center and radius
+ painter.drawEllipse(QPoint(downscale / 2, downscale / 2), downscale / 4, downscale / 4);
+ painter.end();
+ enabled_icon = QPixmap::fromImage(image);
+ }
+
const auto create_item = [](const QString& string = {}) {
QTableWidgetItem* item = new QTableWidgetItem(string);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
@@ -306,8 +329,6 @@ void BreakpointWidget::Update()
Settings::Instance().IsThemeDark() ? QColor(75, 75, 75) : QColor(225, 225, 225);
disabled_item.setBackground(disabled_color);
- auto& power_pc = m_system.GetPowerPC();
- auto& breakpoints = power_pc.GetBreakPoints();
auto& memchecks = power_pc.GetMemChecks();
auto& ppc_symbol_db = power_pc.GetSymbolDB();
@@ -466,6 +487,36 @@ void BreakpointWidget::OnEditBreakpoint(u32 address, bool is_instruction_bp)
emit Host::GetInstance()->PPCBreakpointsChanged();
}
+void BreakpointWidget::OnToggleBreaking()
+{
+ auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
+ auto& memchecks = m_system.GetPowerPC().GetMemChecks();
+ // Memcheck's HasAny() will report no memchecks while breaking is disabled, so only check when
+ // breaking is true.
+ bool has_memory_bp;
+
+ // Currently toggles all code and memory breakpoints. Could be split if needed.
+ if (breakpoints.IsBreakingEnabled())
+ {
+ has_memory_bp = memchecks.HasAny();
+ breakpoints.EnableBreaking(false);
+ memchecks.EnableBreaking(false);
+ m_enabled->setText(tr("Enable"));
+ m_enabled->setIcon(Resources::GetThemeIcon("play"));
+ }
+ else
+ {
+ breakpoints.EnableBreaking(true);
+ memchecks.EnableBreaking(true);
+ has_memory_bp = memchecks.HasAny();
+ m_enabled->setText(tr("Disable"));
+ m_enabled->setIcon(Resources::GetThemeIcon("pause"));
+ }
+
+ if (has_memory_bp || !breakpoints.GetBreakPoints().empty())
+ emit Host::GetInstance()->PPCBreakpointsChanged();
+}
+
void BreakpointWidget::OnLoad()
{
Common::IniFile ini;
diff --git a/Source/Core/DolphinQt/Debugger/BreakpointWidget.h b/Source/Core/DolphinQt/Debugger/BreakpointWidget.h
index f48570f449..64dfcf1283 100644
--- a/Source/Core/DolphinQt/Debugger/BreakpointWidget.h
+++ b/Source/Core/DolphinQt/Debugger/BreakpointWidget.h
@@ -58,6 +58,7 @@ private:
void OnClear();
void OnClicked(QTableWidgetItem* item);
+ void OnToggleBreaking();
void OnNewBreakpoint();
void OnEditBreakpoint(u32 address, bool is_instruction_bp);
void OnLoad();
@@ -70,6 +71,7 @@ private:
QToolBar* m_toolbar;
QTableWidget* m_table;
+ QAction* m_enabled;
QAction* m_new;
QAction* m_clear;
QAction* m_load;
diff --git a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
index ce02b6baa5..c7a74ee29d 100644
--- a/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
+++ b/Source/Core/DolphinQt/Debugger/CodeViewWidget.cpp
@@ -319,6 +319,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
guard ? std::make_optional(power_pc.GetPPCState().pc) : std::nullopt;
const bool dark_theme = Settings::Instance().IsThemeDark();
+ const bool breaking_enabled = power_pc.GetBreakPoints().IsBreakingEnabled();
m_branches.clear();
@@ -400,7 +401,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
if (bp != nullptr)
{
auto icon = Resources::GetThemeIcon("debugger_breakpoint").pixmap(QSize(rowh - 2, rowh - 2));
- if (!bp->is_enabled)
+ if (!breaking_enabled || !bp->is_enabled)
{
QPixmap disabled_icon(icon.size());
disabled_icon.fill(Qt::transparent);
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 9df5caf413..a9459c971a 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -67,6 +67,7 @@
<ClCompile Include="Config\ConfigControls\ConfigText.cpp" />
<ClCompile Include="Config\ConfigControls\ConfigUserPath.cpp" />
<ClCompile Include="Config\ControllerInterface\ControllerInterfaceWindow.cpp" />
+ <ClCompile Include="Config\SDLHints\SDLHintsWindow.cpp" />
<ClCompile Include="Config\ControllerInterface\DualShockUDPClientAddServerDialog.cpp" />
<ClCompile Include="Config\ControllerInterface\DualShockUDPClientWidget.cpp" />
<ClCompile Include="Config\ControllerInterface\ServerStringValidator.cpp" />
@@ -302,6 +303,7 @@
<QtMoc Include="Config\ConfigControls\ConfigText.h" />
<QtMoc Include="Config\ConfigControls\ConfigUserPath.h" />
<QtMoc Include="Config\ControllerInterface\ControllerInterfaceWindow.h" />
+ <QtMoc Include="Config\SDLHints\SDLHintsWindow.h" />
<QtMoc Include="Config\ControllerInterface\DualShockUDPClientAddServerDialog.h" />
<QtMoc Include="Config\ControllerInterface\DualShockUDPClientWidget.h" />
<QtMoc Include="Config\ControllerInterface\ServerStringValidator.h" />
diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
index b5057f09d7..f6912f67ce 100644
--- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
@@ -135,19 +135,31 @@ InputBackend::InputBackend(ControllerInterface* controller_interface)
{
EnableSDLLogging();
- SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
// We have our own WGI backend. Enabling SDL's WGI handling creates even more redundant devices.
- SDL_SetHint(SDL_HINT_JOYSTICK_WGI, "0");
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_WGI) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_WGI, "0");
// Disable DualSense Player LEDs; We already colorize the Primary LED
- SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "0");
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "0");
// Disabling DirectInput support apparently solves hangs on shutdown for users with
- // "8BitDo Ultimate 2" controllers.
+ // "8BitDo Ultimate 2" controllers, however, it also breaks hotplug support for Dual Sense
+ // and DS4 Controllers, so we leave it enabled for now.
// It also works around a possibly related random hang on a IDirectInputDevice8_Acquire
// call within SDL.
- SDL_SetHint(SDL_HINT_JOYSTICK_DIRECTINPUT, "0");
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT, "1");
+
+ // Pre-populate the default Joy-Con hints so they can be easily changed
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "1");
+
+ if (Config::Get(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS) == "")
+ Config::SetBase(Config::MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "0");
// Disable SDL's GC Adapter handling when we want to handle it ourselves.
bool is_gc_adapter_configured = false;
@@ -164,6 +176,18 @@ InputBackend::InputBackend(ControllerInterface* controller_interface)
// and ControllerInterface isn't prepared for SDL to spontaneously re-initialize itself.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, is_gc_adapter_configured ? "0" : "1");
+ // Load all the hints from the config file
+ std::shared_ptr<Config::Layer> layer = Config::GetLayer(Config::LayerType::Base);
+ const Config::Section& section = layer->GetSection(Config::System::Main, "SDL_Hints");
+ for (auto& row_data : section)
+ {
+ const Config::Location& location = row_data.first;
+ const std::optional<std::string>& value = row_data.second;
+
+ if (value)
+ SDL_SetHint(location.key.c_str(), value->c_str());
+ }
+
m_hotplug_thread = std::thread([this] {
Common::SetCurrentThreadName("SDL Hotplug Thread");