From 61c36b0cc85c4e4ff85e9cb099c7fe16eb6db6ca Mon Sep 17 00:00:00 2001 From: Andrew Strauss Date: Mon, 9 Feb 2026 21:47:30 -0500 Subject: [PATCH] Add new window to configure SDL hints and store them in the main ini file Signed-off-by: Andrew Strauss --- Source/Core/Core/Config/MainSettings.cpp | 17 + Source/Core/Core/Config/MainSettings.h | 7 + Source/Core/DolphinQt/CMakeLists.txt | 2 + .../Config/CommonControllersWidget.cpp | 13 + .../Config/CommonControllersWidget.h | 2 + .../Config/SDLHints/SDLHintsWindow.cpp | 313 ++++++++++++++++++ .../Config/SDLHints/SDLHintsWindow.h | 48 +++ Source/Core/DolphinQt/DolphinQt.vcxproj | 2 + .../ControllerInterface/SDL/SDL.cpp | 34 +- 9 files changed, 433 insertions(+), 5 deletions(-) create mode 100644 Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.cpp create mode 100644 Source/Core/DolphinQt/Config/SDLHints/SDLHintsWindow.h diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index d11d48c898..8696a60417 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -525,6 +525,23 @@ const Info MAIN_MOVIE_SHOW_OSD{{System::Main, "Movie", "ShowMovieWindow"}, const Info 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 MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS{ + {System::Main, "SDL_Hints", "SDL_JOYSTICK_ENHANCED_REPORTS"}, ""}; +const Info MAIN_SDL_HINT_JOYSTICK_WGI{{System::Main, "SDL_Hints", "SDL_JOYSTICK_WGI"}, + ""}; +const Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED{ + {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"}, ""}; +const Info MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT{ + {System::Main, "SDL_Hints", "SDL_JOYSTICK_DIRECTINPUT"}, ""}; +const Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS{ + {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"}, ""}; +const Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS{ + {System::Main, "SDL_Hints", "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS"}, ""}; + // Main.Debug const Info 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 00c11ea6dd..6a694e64cf 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -338,6 +338,13 @@ extern const Info MAIN_MOVIE_SHOW_OSD; extern const Info MAIN_INPUT_BACKGROUND_INPUT; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_ENHANCED_REPORTS; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_WGI; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_DIRECTINPUT; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS; +extern const Config::Info MAIN_SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS; + // Main.Debug extern const Info MAIN_DEBUG_JIT_OFF; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 457ed8a157..622fb35e1f 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/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/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 +#include +#include +#include +#include +#include +#include +#include +#include + +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.

If unsure, " + "leave this checked.")); + 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.

If unsure, leave this " + "checked.")); + 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.

If unsure, " + "leave this checked.")); + 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.

If unsure, leave this " + "unchecked.")); + 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 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& 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 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 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(obj); + if (table_widget) + { + if (event->type() == QEvent::Resize) + { + auto* const resize_event = static_cast(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 + +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/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 09f9163036..6f580bbc60 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -67,6 +67,7 @@ + @@ -301,6 +302,7 @@ + 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 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& 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");