WiimoteControllersWidget: Fix UI hang on opening

Fix the UI hanging for several seconds when opening the Controllers
window.

Move the scan for Bluetooth adapters onto a separate thread so the Host
thread doesn't have to wait for it.

Only automatically scan for adapters once, when opening the Controllers
window for the first time. Add a Refresh button to let the user refresh
the adapter list afterward.
This commit is contained in:
Dentomologist
2025-05-15 02:37:01 -07:00
parent fc833f845c
commit ffd46cd10c
3 changed files with 76 additions and 12 deletions

View File

@ -27,7 +27,6 @@ void ControllersWindow::showEvent(QShowEvent* event)
{ {
QDialog::showEvent(event); QDialog::showEvent(event);
m_wiimote_controllers->UpdateBluetoothAvailableStatus(); m_wiimote_controllers->UpdateBluetoothAvailableStatus();
m_wiimote_controllers->RefreshBluetoothAdapters();
} }
void ControllersWindow::CreateMainLayout() void ControllersWindow::CreateMainLayout()

View File

@ -19,6 +19,7 @@
#include <optional> #include <optional>
#include "Common/Config/Config.h" #include "Common/Config/Config.h"
#include "Common/WorkQueueThread.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
#include "Core/Config/WiimoteSettings.h" #include "Core/Config/WiimoteSettings.h"
@ -35,6 +36,7 @@
#include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h" #include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h" #include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
@ -51,6 +53,14 @@ WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(pa
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this](Core::State state) { LoadSettings(state); }); [this](Core::State state) { LoadSettings(state); });
LoadSettings(Core::GetState(Core::System::GetInstance())); LoadSettings(Core::GetState(Core::System::GetInstance()));
m_bluetooth_adapter_refresh_thread.Reset("Bluetooth Adapter Refresh Thread");
StartBluetoothAdapterRefresh();
}
WiimoteControllersWidget::~WiimoteControllersWidget()
{
m_bluetooth_adapter_refresh_thread.WaitForCompletion();
} }
void WiimoteControllersWidget::UpdateBluetoothAvailableStatus() void WiimoteControllersWidget::UpdateBluetoothAvailableStatus()
@ -58,17 +68,46 @@ void WiimoteControllersWidget::UpdateBluetoothAvailableStatus()
m_bluetooth_unavailable->setHidden(WiimoteReal::IsScannerReady()); m_bluetooth_unavailable->setHidden(WiimoteReal::IsScannerReady());
} }
void WiimoteControllersWidget::RefreshBluetoothAdapters() void WiimoteControllersWidget::StartBluetoothAdapterRefresh()
{ {
m_bluetooth_adapters->clear(); if (m_bluetooth_adapter_scan_in_progress)
return;
m_bluetooth_adapters->clear();
m_bluetooth_adapters->setDisabled(true);
m_bluetooth_adapters->addItem(tr("Scanning for adapters..."));
m_bluetooth_adapter_scan_in_progress = true;
const auto scan_func = [this]() {
INFO_LOG_FMT(COMMON, "Refreshing Bluetooth adapter list...");
auto device_list = IOS::HLE::BluetoothRealDevice::ListDevices();
INFO_LOG_FMT(COMMON, "{} Bluetooth adapters available.", device_list.size());
const auto refresh_complete_func = [this, devices = std::move(device_list)]() {
OnBluetoothAdapterRefreshComplete(devices);
};
QueueOnObject(this, refresh_complete_func);
};
m_bluetooth_adapter_refresh_thread.Push(scan_func);
}
void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete(
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices)
{
const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); const int configured_vid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID); const int configured_pid = Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID);
bool found_configured_device = configured_vid == -1 || configured_pid == -1; bool found_configured_device = configured_vid == -1 || configured_pid == -1;
m_bluetooth_adapters->clear();
m_bluetooth_adapter_scan_in_progress = false;
const auto state = Core::GetState(Core::System::GetInstance());
UpdateBluetoothAdapterWidgetsEnabled(state);
m_bluetooth_adapters->addItem(tr("Automatic")); m_bluetooth_adapters->addItem(tr("Automatic"));
for (auto& device : IOS::HLE::BluetoothRealDevice::ListDevices()) for (auto& device : devices)
{ {
std::string name = device.name.empty() ? tr("Unknown Device").toStdString() : device.name; std::string name = device.name.empty() ? tr("Unknown Device").toStdString() : device.name;
QString device_info = QString device_info =
@ -149,8 +188,9 @@ void WiimoteControllersWidget::CreateLayout()
m_wiimote_box->setLayout(m_wiimote_layout); m_wiimote_box->setLayout(m_wiimote_layout);
m_wiimote_passthrough = new QRadioButton(tr("Passthrough a Bluetooth adapter")); m_wiimote_passthrough = new QRadioButton(tr("Passthrough a Bluetooth adapter"));
m_bluetooth_adapters_label = new QLabel(tr("Bluetooth adapter")); m_bluetooth_adapters_label = new QLabel(tr("Adapter"));
m_bluetooth_adapters = new QComboBox(); m_bluetooth_adapters = new QComboBox();
m_bluetooth_adapters_refresh = new NonDefaultQPushButton(tr("Refresh"));
m_wiimote_sync = new NonDefaultQPushButton(tr("Sync")); m_wiimote_sync = new NonDefaultQPushButton(tr("Sync"));
m_wiimote_reset = new NonDefaultQPushButton(tr("Reset")); m_wiimote_reset = new NonDefaultQPushButton(tr("Reset"));
m_wiimote_refresh = new NonDefaultQPushButton(tr("Refresh")); m_wiimote_refresh = new NonDefaultQPushButton(tr("Refresh"));
@ -172,7 +212,8 @@ void WiimoteControllersWidget::CreateLayout()
int adapter_row = m_wiimote_layout->rowCount(); int adapter_row = m_wiimote_layout->rowCount();
m_wiimote_layout->addWidget(m_bluetooth_adapters_label, adapter_row, 1, 1, 1); m_wiimote_layout->addWidget(m_bluetooth_adapters_label, adapter_row, 1, 1, 1);
m_wiimote_layout->addWidget(m_bluetooth_adapters, adapter_row, 2, 1, 2); m_wiimote_layout->addWidget(m_bluetooth_adapters, adapter_row, 2, 1, 1);
m_wiimote_layout->addWidget(m_bluetooth_adapters_refresh, adapter_row, 3, 1, 1);
int sync_row = m_wiimote_layout->rowCount(); int sync_row = m_wiimote_layout->rowCount();
m_wiimote_layout->addWidget(m_wiimote_pt_labels[0], sync_row, 1, 1, 2); m_wiimote_layout->addWidget(m_wiimote_pt_labels[0], sync_row, 1, 1, 2);
@ -242,6 +283,8 @@ void WiimoteControllersWidget::ConnectWidgets()
&WiimoteControllersWidget::SaveSettings); &WiimoteControllersWidget::SaveSettings);
connect(m_bluetooth_adapters, &QComboBox::activated, this, connect(m_bluetooth_adapters, &QComboBox::activated, this,
&WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged); &WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged);
connect(m_bluetooth_adapters_refresh, &QPushButton::clicked, this,
&WiimoteControllersWidget::StartBluetoothAdapterRefresh);
connect(m_wiimote_sync, &QPushButton::clicked, this, connect(m_wiimote_sync, &QPushButton::clicked, this,
&WiimoteControllersWidget::OnBluetoothPassthroughSyncPressed); &WiimoteControllersWidget::OnBluetoothPassthroughSyncPressed);
connect(m_wiimote_reset, &QPushButton::clicked, this, connect(m_wiimote_reset, &QPushButton::clicked, this,
@ -339,6 +382,19 @@ void WiimoteControllersWidget::OnWiimoteConfigure(size_t index)
window->show(); window->show();
} }
void WiimoteControllersWidget::UpdateBluetoothAdapterWidgetsEnabled(const Core::State state)
{
const bool running = state != Core::State::Uninitialized;
const bool running_wii = running && Core::System::GetInstance().IsWii();
const bool enable_adapter_refresh = m_wiimote_passthrough->isChecked() && !running_wii;
const bool enable_adapter_selection =
enable_adapter_refresh && !m_bluetooth_adapter_scan_in_progress;
m_bluetooth_adapters_label->setEnabled(enable_adapter_selection);
m_bluetooth_adapters->setEnabled(enable_adapter_selection);
m_bluetooth_adapters_refresh->setEnabled(enable_adapter_refresh);
}
void WiimoteControllersWidget::LoadSettings(Core::State state) void WiimoteControllersWidget::LoadSettings(Core::State state)
{ {
for (size_t i = 0; i < m_wiimote_groups.size(); i++) for (size_t i = 0; i < m_wiimote_groups.size(); i++)
@ -369,15 +425,13 @@ void WiimoteControllersWidget::LoadSettings(Core::State state)
m_wiimote_passthrough->setEnabled(!running); m_wiimote_passthrough->setEnabled(!running);
const bool running_gc = running && !Core::System::GetInstance().IsWii(); const bool running_gc = running && !Core::System::GetInstance().IsWii();
const bool running_wii = running && Core::System::GetInstance().IsWii();
const bool enable_passthrough = m_wiimote_passthrough->isChecked() && !running_gc; const bool enable_passthrough = m_wiimote_passthrough->isChecked() && !running_gc;
const bool enable_adapter_selection = m_wiimote_passthrough->isChecked() && !running_wii;
const bool enable_emu_bt = !m_wiimote_passthrough->isChecked() && !running_gc; const bool enable_emu_bt = !m_wiimote_passthrough->isChecked() && !running_gc;
const bool is_netplay = NetPlay::IsNetPlayRunning(); const bool is_netplay = NetPlay::IsNetPlayRunning();
const bool running_netplay = running && is_netplay; const bool running_netplay = running && is_netplay;
m_bluetooth_adapters_label->setEnabled(enable_adapter_selection); UpdateBluetoothAdapterWidgetsEnabled(state);
m_bluetooth_adapters->setEnabled(enable_adapter_selection);
m_wiimote_sync->setEnabled(enable_passthrough); m_wiimote_sync->setEnabled(enable_passthrough);
m_wiimote_reset->setEnabled(enable_passthrough); m_wiimote_reset->setEnabled(enable_passthrough);

View File

@ -3,9 +3,12 @@
#pragma once #pragma once
#include <array>
#include <QWidget> #include <QWidget>
#include <array> #include "Common/WorkQueueThread.h"
#include "Core/IOS/USB/Bluetooth/BTReal.h"
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
@ -26,17 +29,21 @@ class WiimoteControllersWidget final : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit WiimoteControllersWidget(QWidget* parent); explicit WiimoteControllersWidget(QWidget* parent);
~WiimoteControllersWidget();
void UpdateBluetoothAvailableStatus(); void UpdateBluetoothAvailableStatus();
void RefreshBluetoothAdapters();
private: private:
void SaveSettings(); void SaveSettings();
void OnBluetoothPassthroughDeviceChanged(int index); void OnBluetoothPassthroughDeviceChanged(int index);
void OnBluetoothPassthroughSyncPressed(); void OnBluetoothPassthroughSyncPressed();
void OnBluetoothPassthroughResetPressed(); void OnBluetoothPassthroughResetPressed();
void OnBluetoothAdapterRefreshComplete(
const std::vector<IOS::HLE::BluetoothRealDevice::BluetoothDeviceInfo>& devices);
void OnWiimoteRefreshPressed(); void OnWiimoteRefreshPressed();
void OnWiimoteConfigure(size_t index); void OnWiimoteConfigure(size_t index);
void StartBluetoothAdapterRefresh();
void UpdateBluetoothAdapterWidgetsEnabled(Core::State state);
void CreateLayout(); void CreateLayout();
void ConnectWidgets(); void ConnectWidgets();
@ -50,10 +57,14 @@ private:
std::array<QHBoxLayout*, 4> m_wiimote_groups; std::array<QHBoxLayout*, 4> m_wiimote_groups;
std::array<QLabel*, 2> m_wiimote_pt_labels; std::array<QLabel*, 2> m_wiimote_pt_labels;
Common::AsyncWorkThreadSP m_bluetooth_adapter_refresh_thread;
bool m_bluetooth_adapter_scan_in_progress = false;
QRadioButton* m_wiimote_emu; QRadioButton* m_wiimote_emu;
QRadioButton* m_wiimote_passthrough; QRadioButton* m_wiimote_passthrough;
QLabel* m_bluetooth_adapters_label; QLabel* m_bluetooth_adapters_label;
QComboBox* m_bluetooth_adapters; QComboBox* m_bluetooth_adapters;
QPushButton* m_bluetooth_adapters_refresh;
QPushButton* m_wiimote_sync; QPushButton* m_wiimote_sync;
QPushButton* m_wiimote_reset; QPushButton* m_wiimote_reset;
QCheckBox* m_wiimote_continuous_scanning; QCheckBox* m_wiimote_continuous_scanning;