diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index f277a982b6..47df65334b 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -364,8 +364,8 @@ add_executable(dolphin-emu Settings/InterfacePane.h Settings/PathPane.cpp Settings/PathPane.h - Settings/USBDeviceAddToWhitelistDialog.cpp - Settings/USBDeviceAddToWhitelistDialog.h + Settings/USBDevicePicker.cpp + Settings/USBDevicePicker.h Settings/WiiPane.cpp Settings/WiiPane.h SkylanderPortal/SkylanderModifyDialog.cpp diff --git a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp index 8517e4328e..1d78196907 100644 --- a/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp +++ b/Source/Core/DolphinQt/Config/WiimoteControllersWidget.cpp @@ -37,6 +37,7 @@ #include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/Settings.h" +#include "DolphinQt/Settings/USBDevicePicker.h" WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent) { @@ -133,6 +134,9 @@ void WiimoteControllersWidget::OnBluetoothAdapterRefreshComplete( m_bluetooth_adapters->addItem(device_info, QVariant::fromValue(disconnected_device)); m_bluetooth_adapters->setCurrentIndex(m_bluetooth_adapters->count() - 1); } + + m_bluetooth_adapters->insertSeparator(m_bluetooth_adapters->count()); + m_bluetooth_adapters->addItem(tr("More Options...")); } static int GetRadioButtonIndicatorWidth() @@ -299,6 +303,8 @@ void WiimoteControllersWidget::ConnectWidgets() void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index) { + std::optional device_info; + bool needs_refresh = false; // "Automatic" selection if (index == 0) { @@ -308,19 +314,31 @@ void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index) Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); return; } - - const QVariant item_data = m_bluetooth_adapters->itemData(index); - - if (!item_data.isValid() || !item_data.canConvert()) + // "More Options..." selection + else if (index == m_bluetooth_adapters->count() - 1) { - ERROR_LOG_FMT(COMMON, "Invalid Bluetooth device info selected in WiimoteControllersWidget"); - return; + device_info = USBDevicePicker::Run(this, tr("Select A Bluetooth Device")); + needs_refresh = true; + } + else + { + const QVariant item_data = m_bluetooth_adapters->itemData(index); + + if (!item_data.isValid() || !item_data.canConvert()) + { + ERROR_LOG_FMT(COMMON, "Invalid Bluetooth device info selected in WiimoteControllersWidget"); + return; + } + device_info = item_data.value(); } - const auto& device_info = item_data.value(); - - Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid); - Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid); + if (device_info.has_value()) + { + Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info->pid); + Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info->vid); + } + if (needs_refresh) + StartBluetoothAdapterRefresh(); } void WiimoteControllersWidget::OnBluetoothPassthroughResetPressed() diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 0250f7260e..a7181c2797 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -219,7 +219,7 @@ - + @@ -429,7 +429,7 @@ - + diff --git a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp b/Source/Core/DolphinQt/Settings/USBDevicePicker.cpp similarity index 55% rename from Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp rename to Source/Core/DolphinQt/Settings/USBDevicePicker.cpp index 05f940505c..bf4261e43f 100644 --- a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.cpp +++ b/Source/Core/DolphinQt/Settings/USBDevicePicker.cpp @@ -1,7 +1,9 @@ // Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h" +#include "DolphinQt/Settings/USBDevicePicker.h" + +#include #include #include @@ -18,23 +20,12 @@ #include -#include "Common/StringUtil.h" - -#include "Core/Config/MainSettings.h" -#include "Core/ConfigManager.h" #include "Core/USBUtils.h" -#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/Settings/WiiPane.h" -static bool IsValidUSBIDString(const std::string& string) -{ - if (string.empty() || string.length() > 4) - return false; - return std::ranges::all_of(string, Common::IsXDigit); -} - -USBDeviceAddToWhitelistDialog::USBDeviceAddToWhitelistDialog(QWidget* parent) : QDialog(parent) +USBDevicePicker::USBDevicePicker(QWidget* parent, FilterFunctionType filter) + : QDialog(parent), m_filter(std::move(filter)) { InitControls(); setLayout(main_layout); @@ -42,19 +33,28 @@ USBDeviceAddToWhitelistDialog::USBDeviceAddToWhitelistDialog(QWidget* parent) : adjustSize(); } -void USBDeviceAddToWhitelistDialog::InitControls() +std::optional USBDevicePicker::Run(QWidget* parent, const QString& title, + FilterFunctionType filter) { - setWindowTitle(tr("Add New USB Device")); + USBDevicePicker picker(parent, std::move(filter)); + picker.setWindowTitle(title); - m_whitelist_buttonbox = new QDialogButtonBox(); - auto* add_button = new QPushButton(tr("Add")); + if (picker.exec() == QDialog::Accepted) + return picker.GetSelectedDevice(); + + return std::nullopt; +} + +void USBDevicePicker::InitControls() +{ + m_picker_buttonbox = new QDialogButtonBox(); + auto* select_button = new QPushButton(tr("Select")); auto* cancel_button = new QPushButton(tr("Cancel")); - m_whitelist_buttonbox->addButton(add_button, QDialogButtonBox::AcceptRole); - m_whitelist_buttonbox->addButton(cancel_button, QDialogButtonBox::RejectRole); - connect(add_button, &QPushButton::clicked, this, - &USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist); - connect(cancel_button, &QPushButton::clicked, this, &USBDeviceAddToWhitelistDialog::reject); - add_button->setDefault(true); + m_picker_buttonbox->addButton(select_button, QDialogButtonBox::AcceptRole); + m_picker_buttonbox->addButton(cancel_button, QDialogButtonBox::RejectRole); + connect(select_button, &QPushButton::clicked, this, &QDialog::accept); + connect(cancel_button, &QPushButton::clicked, this, &QDialog::reject); + select_button->setDefault(true); main_layout = new QVBoxLayout(); enter_device_id_label = new QLabel(tr("Enter USB device ID")); @@ -87,31 +87,33 @@ void USBDeviceAddToWhitelistDialog::InitControls() usb_inserted_devices_list = new QListWidget(); m_refresh_devices_timer = new QTimer(this); connect(usb_inserted_devices_list, &QListWidget::currentItemChanged, this, - &USBDeviceAddToWhitelistDialog::OnDeviceSelection); - connect(usb_inserted_devices_list, &QListWidget::itemDoubleClicked, add_button, + &USBDevicePicker::OnDeviceSelection); + connect(usb_inserted_devices_list, &QListWidget::itemDoubleClicked, select_button, &QPushButton::clicked); - connect(m_refresh_devices_timer, &QTimer::timeout, this, - &USBDeviceAddToWhitelistDialog::RefreshDeviceList); + connect(m_refresh_devices_timer, &QTimer::timeout, this, &USBDevicePicker::RefreshDeviceList); RefreshDeviceList(); m_refresh_devices_timer->start(1000); main_layout->addWidget(usb_inserted_devices_list); - main_layout->addWidget(m_whitelist_buttonbox); + main_layout->addWidget(m_picker_buttonbox); // i18n: VID means Vendor ID (in the context of a USB device) device_vid_textbox->setPlaceholderText(tr("Device VID")); // i18n: PID means Product ID (in the context of a USB device), not Process ID device_pid_textbox->setPlaceholderText(tr("Device PID")); + const QRegularExpression hex_regex(QStringLiteral("^[0-9A-Fa-f]*$")); + const QRegularExpressionValidator* hex_validator = + new QRegularExpressionValidator(hex_regex, this); + device_vid_textbox->setValidator(hex_validator); device_vid_textbox->setMaxLength(4); + device_pid_textbox->setValidator(hex_validator); device_pid_textbox->setMaxLength(4); } -void USBDeviceAddToWhitelistDialog::RefreshDeviceList() -{ - const auto whitelist = Config::GetUSBDeviceWhitelist(); - const auto& current_devices = USBUtils::ListDevices( - [&whitelist](const USBUtils::DeviceInfo& device) { return !whitelist.contains(device); }); +void USBDevicePicker::RefreshDeviceList() +{ + const auto& current_devices = USBUtils::ListDevices(m_filter); if (current_devices == m_shown_devices) return; @@ -129,44 +131,7 @@ void USBDeviceAddToWhitelistDialog::RefreshDeviceList() m_shown_devices = current_devices; } - -void USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist() -{ - const std::string vid_string(StripWhitespace(device_vid_textbox->text().toStdString())); - const std::string pid_string(StripWhitespace(device_pid_textbox->text().toStdString())); - if (!IsValidUSBIDString(vid_string)) - { - ModalMessageBox::critical(this, tr("USB Whitelist Error"), - // i18n: Here, VID means Vendor ID (for a USB device). - tr("The entered VID is invalid.")); - return; - } - if (!IsValidUSBIDString(pid_string)) - { - ModalMessageBox::critical(this, tr("USB Whitelist Error"), - // i18n: Here, PID means Product ID (for a USB device). - tr("The entered PID is invalid.")); - return; - } - - const u16 vid = static_cast(std::stoul(vid_string, nullptr, 16)); - const u16 pid = static_cast(std::stoul(pid_string, nullptr, 16)); - const USBUtils::DeviceInfo new_device{vid, pid}; - - auto whitelist = Config::GetUSBDeviceWhitelist(); - if (whitelist.contains(new_device)) - { - ModalMessageBox::critical(this, tr("USB Whitelist Error"), - tr("This USB device is already whitelisted.")); - return; - } - whitelist.emplace(new_device); - Config::SetUSBDeviceWhitelist(whitelist); - Config::Save(); - accept(); -} - -void USBDeviceAddToWhitelistDialog::OnDeviceSelection() +void USBDevicePicker::OnDeviceSelection() { auto* current_item = usb_inserted_devices_list->currentItem(); if (!current_item) @@ -178,3 +143,19 @@ void USBDeviceAddToWhitelistDialog::OnDeviceSelection() device_vid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.vid))); device_pid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.pid))); } + +std::optional USBDevicePicker::GetSelectedDevice() const +{ + const std::string vid_string(device_vid_textbox->text().toStdString()); + const std::string pid_string(device_pid_textbox->text().toStdString()); + + if (vid_string.empty() || pid_string.empty()) + return std::nullopt; + + const u16 vid = static_cast(std::stoul(vid_string, nullptr, 16)); + const u16 pid = static_cast(std::stoul(pid_string, nullptr, 16)); + + const USBUtils::DeviceInfo device{vid, pid}; + + return device; +} diff --git a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h b/Source/Core/DolphinQt/Settings/USBDevicePicker.h similarity index 62% rename from Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h rename to Source/Core/DolphinQt/Settings/USBDevicePicker.h index deb513698a..4ab25cdecd 100644 --- a/Source/Core/DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h +++ b/Source/Core/DolphinQt/Settings/USBDevicePicker.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -24,16 +25,21 @@ class QPushButton; class QErrorMessage; class QMessageBox; -class USBDeviceAddToWhitelistDialog final : public QDialog +class USBDevicePicker final : public QDialog { Q_OBJECT public: - explicit USBDeviceAddToWhitelistDialog(QWidget* parent); + using FilterFunctionType = std::function; + explicit USBDevicePicker(QWidget* parent, FilterFunctionType filter); + + static std::optional Run( + QWidget* parent, const QString& title, + const FilterFunctionType filter = [](const USBUtils::DeviceInfo&) { return true; }); private: static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100; QTimer* m_refresh_devices_timer; - QDialogButtonBox* m_whitelist_buttonbox; + QDialogButtonBox* m_picker_buttonbox; QVBoxLayout* main_layout; QLabel* enter_device_id_label; QHBoxLayout* entry_hbox_layout; @@ -42,9 +48,12 @@ private: QLabel* select_label; QListWidget* usb_inserted_devices_list; + FilterFunctionType m_filter; + + std::optional GetSelectedDevice() const; + void InitControls(); void RefreshDeviceList(); - void AddUSBDeviceToWhitelist(); void OnDeviceSelection(); diff --git a/Source/Core/DolphinQt/Settings/WiiPane.cpp b/Source/Core/DolphinQt/Settings/WiiPane.cpp index 5aa1caea13..28689e62af 100644 --- a/Source/Core/DolphinQt/Settings/WiiPane.cpp +++ b/Source/Core/DolphinQt/Settings/WiiPane.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -39,7 +40,7 @@ #include "DolphinQt/QtUtils/QtUtils.h" #include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/Settings.h" -#include "DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h" +#include "DolphinQt/Settings/USBDevicePicker.h" // SYSCONF uses 0 for bottom and 1 for top, but we place them in // the other order in the GUI so that Top will be above Bottom, @@ -460,10 +461,24 @@ void WiiPane::ValidateSelectionState() void WiiPane::OnUSBWhitelistAddButton() { - USBDeviceAddToWhitelistDialog usb_whitelist_dialog(this); - connect(&usb_whitelist_dialog, &USBDeviceAddToWhitelistDialog::accepted, this, - &WiiPane::PopulateUSBPassthroughListWidget); - usb_whitelist_dialog.exec(); + auto whitelist = Config::GetUSBDeviceWhitelist(); + + const std::optional usb_device = USBDevicePicker::Run( + this, tr("Add New USB Device"), + [&whitelist](const USBUtils::DeviceInfo& device) { return !whitelist.contains(device); }); + if (!usb_device) + return; + + if (whitelist.contains(*usb_device)) + { + ModalMessageBox::critical(this, tr("USB Whitelist Error"), + tr("This USB device is already whitelisted.")); + return; + } + whitelist.emplace(*usb_device); + Config::SetUSBDeviceWhitelist(whitelist); + Config::Save(); + PopulateUSBPassthroughListWidget(); } void WiiPane::OnUSBWhitelistRemoveButton()