This commit is contained in:
Joshua Vandaële 2025-08-10 23:25:08 +02:00 committed by GitHub
commit af590db18f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 118 additions and 95 deletions

View file

@ -364,8 +364,8 @@ add_executable(dolphin-emu
Settings/InterfacePane.h Settings/InterfacePane.h
Settings/PathPane.cpp Settings/PathPane.cpp
Settings/PathPane.h Settings/PathPane.h
Settings/USBDeviceAddToWhitelistDialog.cpp Settings/USBDevicePicker.cpp
Settings/USBDeviceAddToWhitelistDialog.h Settings/USBDevicePicker.h
Settings/WiiPane.cpp Settings/WiiPane.cpp
Settings/WiiPane.h Settings/WiiPane.h
SkylanderPortal/SkylanderModifyDialog.cpp SkylanderPortal/SkylanderModifyDialog.cpp

View file

@ -37,6 +37,7 @@
#include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
#include "DolphinQt/Settings/USBDevicePicker.h"
WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent) 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->addItem(device_info, QVariant::fromValue(disconnected_device));
m_bluetooth_adapters->setCurrentIndex(m_bluetooth_adapters->count() - 1); 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() static int GetRadioButtonIndicatorWidth()
@ -299,6 +303,8 @@ void WiimoteControllersWidget::ConnectWidgets()
void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index) void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index)
{ {
std::optional<USBUtils::DeviceInfo> device_info;
bool needs_refresh = false;
// "Automatic" selection // "Automatic" selection
if (index == 0) if (index == 0)
{ {
@ -308,19 +314,31 @@ void WiimoteControllersWidget::OnBluetoothPassthroughDeviceChanged(int index)
Config::MAIN_BLUETOOTH_PASSTHROUGH_VID); Config::MAIN_BLUETOOTH_PASSTHROUGH_VID);
return; return;
} }
// "More Options..." selection
const QVariant item_data = m_bluetooth_adapters->itemData(index); else if (index == m_bluetooth_adapters->count() - 1)
if (!item_data.isValid() || !item_data.canConvert<USBUtils::DeviceInfo>())
{ {
ERROR_LOG_FMT(COMMON, "Invalid Bluetooth device info selected in WiimoteControllersWidget"); device_info = USBDevicePicker::Run(this, tr("Select A Bluetooth Device"));
return; needs_refresh = true;
}
else
{
const QVariant item_data = m_bluetooth_adapters->itemData(index);
if (!item_data.isValid() || !item_data.canConvert<USBUtils::DeviceInfo>())
{
ERROR_LOG_FMT(COMMON, "Invalid Bluetooth device info selected in WiimoteControllersWidget");
return;
}
device_info = item_data.value<USBUtils::DeviceInfo>();
} }
const auto& device_info = item_data.value<USBUtils::DeviceInfo>(); if (device_info.has_value())
{
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info.pid); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_PID, device_info->pid);
Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info.vid); Config::SetBaseOrCurrent(Config::MAIN_BLUETOOTH_PASSTHROUGH_VID, device_info->vid);
}
if (needs_refresh)
StartBluetoothAdapterRefresh();
} }
void WiimoteControllersWidget::OnBluetoothPassthroughResetPressed() void WiimoteControllersWidget::OnBluetoothPassthroughResetPressed()

View file

@ -219,7 +219,7 @@
<ClCompile Include="Settings\GeneralPane.cpp" /> <ClCompile Include="Settings\GeneralPane.cpp" />
<ClCompile Include="Settings\InterfacePane.cpp" /> <ClCompile Include="Settings\InterfacePane.cpp" />
<ClCompile Include="Settings\PathPane.cpp" /> <ClCompile Include="Settings\PathPane.cpp" />
<ClCompile Include="Settings\USBDeviceAddToWhitelistDialog.cpp" /> <ClCompile Include="Settings\USBDevicePicker.cpp" />
<ClCompile Include="Settings\WiiPane.cpp" /> <ClCompile Include="Settings\WiiPane.cpp" />
<ClCompile Include="SkylanderPortal\SkylanderModifyDialog.cpp" /> <ClCompile Include="SkylanderPortal\SkylanderModifyDialog.cpp" />
<ClCompile Include="SkylanderPortal\SkylanderPortalWindow.cpp" /> <ClCompile Include="SkylanderPortal\SkylanderPortalWindow.cpp" />
@ -429,7 +429,7 @@
<QtMoc Include="Settings\GeneralPane.h" /> <QtMoc Include="Settings\GeneralPane.h" />
<QtMoc Include="Settings\InterfacePane.h" /> <QtMoc Include="Settings\InterfacePane.h" />
<QtMoc Include="Settings\PathPane.h" /> <QtMoc Include="Settings\PathPane.h" />
<QtMoc Include="Settings\USBDeviceAddToWhitelistDialog.h" /> <QtMoc Include="Settings\USBDevicePicker.h" />
<QtMoc Include="Settings\WiiPane.h" /> <QtMoc Include="Settings\WiiPane.h" />
<QtMoc Include="SkylanderPortal\SkylanderPortalWindow.h" /> <QtMoc Include="SkylanderPortal\SkylanderPortalWindow.h" />
<QtMoc Include="TAS\GCTASInputWindow.h" /> <QtMoc Include="TAS\GCTASInputWindow.h" />

View file

@ -1,7 +1,9 @@
// Copyright 2017 Dolphin Emulator Project // Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Settings/USBDeviceAddToWhitelistDialog.h" #include "DolphinQt/Settings/USBDevicePicker.h"
#include <optional>
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@ -18,23 +20,12 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "Common/StringUtil.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/USBUtils.h" #include "Core/USBUtils.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/Settings/WiiPane.h" #include "DolphinQt/Settings/WiiPane.h"
static bool IsValidUSBIDString(const std::string& string) USBDevicePicker::USBDevicePicker(QWidget* parent, FilterFunctionType filter)
{ : QDialog(parent), m_filter(std::move(filter))
if (string.empty() || string.length() > 4)
return false;
return std::ranges::all_of(string, Common::IsXDigit);
}
USBDeviceAddToWhitelistDialog::USBDeviceAddToWhitelistDialog(QWidget* parent) : QDialog(parent)
{ {
InitControls(); InitControls();
setLayout(main_layout); setLayout(main_layout);
@ -42,19 +33,28 @@ USBDeviceAddToWhitelistDialog::USBDeviceAddToWhitelistDialog(QWidget* parent) :
adjustSize(); adjustSize();
} }
void USBDeviceAddToWhitelistDialog::InitControls() std::optional<USBUtils::DeviceInfo> 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(); if (picker.exec() == QDialog::Accepted)
auto* add_button = new QPushButton(tr("Add")); 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")); auto* cancel_button = new QPushButton(tr("Cancel"));
m_whitelist_buttonbox->addButton(add_button, QDialogButtonBox::AcceptRole); m_picker_buttonbox->addButton(select_button, QDialogButtonBox::AcceptRole);
m_whitelist_buttonbox->addButton(cancel_button, QDialogButtonBox::RejectRole); m_picker_buttonbox->addButton(cancel_button, QDialogButtonBox::RejectRole);
connect(add_button, &QPushButton::clicked, this, connect(select_button, &QPushButton::clicked, this, &QDialog::accept);
&USBDeviceAddToWhitelistDialog::AddUSBDeviceToWhitelist); connect(cancel_button, &QPushButton::clicked, this, &QDialog::reject);
connect(cancel_button, &QPushButton::clicked, this, &USBDeviceAddToWhitelistDialog::reject); select_button->setDefault(true);
add_button->setDefault(true);
main_layout = new QVBoxLayout(); main_layout = new QVBoxLayout();
enter_device_id_label = new QLabel(tr("Enter USB device ID")); enter_device_id_label = new QLabel(tr("Enter USB device ID"));
@ -87,31 +87,33 @@ void USBDeviceAddToWhitelistDialog::InitControls()
usb_inserted_devices_list = new QListWidget(); usb_inserted_devices_list = new QListWidget();
m_refresh_devices_timer = new QTimer(this); m_refresh_devices_timer = new QTimer(this);
connect(usb_inserted_devices_list, &QListWidget::currentItemChanged, this, connect(usb_inserted_devices_list, &QListWidget::currentItemChanged, this,
&USBDeviceAddToWhitelistDialog::OnDeviceSelection); &USBDevicePicker::OnDeviceSelection);
connect(usb_inserted_devices_list, &QListWidget::itemDoubleClicked, add_button, connect(usb_inserted_devices_list, &QListWidget::itemDoubleClicked, select_button,
&QPushButton::clicked); &QPushButton::clicked);
connect(m_refresh_devices_timer, &QTimer::timeout, this, connect(m_refresh_devices_timer, &QTimer::timeout, this, &USBDevicePicker::RefreshDeviceList);
&USBDeviceAddToWhitelistDialog::RefreshDeviceList);
RefreshDeviceList(); RefreshDeviceList();
m_refresh_devices_timer->start(1000); m_refresh_devices_timer->start(1000);
main_layout->addWidget(usb_inserted_devices_list); 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) // i18n: VID means Vendor ID (in the context of a USB device)
device_vid_textbox->setPlaceholderText(tr("Device VID")); device_vid_textbox->setPlaceholderText(tr("Device VID"));
// i18n: PID means Product ID (in the context of a USB device), not Process ID // i18n: PID means Product ID (in the context of a USB device), not Process ID
device_pid_textbox->setPlaceholderText(tr("Device PID")); 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_vid_textbox->setMaxLength(4);
device_pid_textbox->setValidator(hex_validator);
device_pid_textbox->setMaxLength(4); device_pid_textbox->setMaxLength(4);
} }
void USBDeviceAddToWhitelistDialog::RefreshDeviceList()
{
const auto whitelist = Config::GetUSBDeviceWhitelist();
const auto& current_devices = USBUtils::ListDevices( void USBDevicePicker::RefreshDeviceList()
[&whitelist](const USBUtils::DeviceInfo& device) { return !whitelist.contains(device); }); {
const auto& current_devices = USBUtils::ListDevices(m_filter);
if (current_devices == m_shown_devices) if (current_devices == m_shown_devices)
return; return;
@ -129,44 +131,7 @@ void USBDeviceAddToWhitelistDialog::RefreshDeviceList()
m_shown_devices = current_devices; m_shown_devices = current_devices;
} }
void USBDevicePicker::OnDeviceSelection()
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<u16>(std::stoul(vid_string, nullptr, 16));
const u16 pid = static_cast<u16>(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()
{ {
auto* current_item = usb_inserted_devices_list->currentItem(); auto* current_item = usb_inserted_devices_list->currentItem();
if (!current_item) if (!current_item)
@ -178,3 +143,19 @@ void USBDeviceAddToWhitelistDialog::OnDeviceSelection()
device_vid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.vid))); device_vid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.vid)));
device_pid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.pid))); device_pid_textbox->setText(QString::fromStdString(fmt::format("{:04x}", device.pid)));
} }
std::optional<USBUtils::DeviceInfo> 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<u16>(std::stoul(vid_string, nullptr, 16));
const u16 pid = static_cast<u16>(std::stoul(pid_string, nullptr, 16));
const USBUtils::DeviceInfo device{vid, pid};
return device;
}

View file

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <optional>
#include <vector> #include <vector>
#include <QDialog> #include <QDialog>
@ -24,16 +25,21 @@ class QPushButton;
class QErrorMessage; class QErrorMessage;
class QMessageBox; class QMessageBox;
class USBDeviceAddToWhitelistDialog final : public QDialog class USBDevicePicker final : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit USBDeviceAddToWhitelistDialog(QWidget* parent); using FilterFunctionType = std::function<bool(const USBUtils::DeviceInfo&)>;
explicit USBDevicePicker(QWidget* parent, FilterFunctionType filter);
static std::optional<USBUtils::DeviceInfo> Run(
QWidget* parent, const QString& title,
const FilterFunctionType filter = [](const USBUtils::DeviceInfo&) { return true; });
private: private:
static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100; static constexpr int DEVICE_REFRESH_INTERVAL_MS = 100;
QTimer* m_refresh_devices_timer; QTimer* m_refresh_devices_timer;
QDialogButtonBox* m_whitelist_buttonbox; QDialogButtonBox* m_picker_buttonbox;
QVBoxLayout* main_layout; QVBoxLayout* main_layout;
QLabel* enter_device_id_label; QLabel* enter_device_id_label;
QHBoxLayout* entry_hbox_layout; QHBoxLayout* entry_hbox_layout;
@ -42,9 +48,12 @@ private:
QLabel* select_label; QLabel* select_label;
QListWidget* usb_inserted_devices_list; QListWidget* usb_inserted_devices_list;
FilterFunctionType m_filter;
std::optional<USBUtils::DeviceInfo> GetSelectedDevice() const;
void InitControls(); void InitControls();
void RefreshDeviceList(); void RefreshDeviceList();
void AddUSBDeviceToWhitelist();
void OnDeviceSelection(); void OnDeviceSelection();

View file

@ -5,6 +5,7 @@
#include <array> #include <array>
#include <future> #include <future>
#include <optional>
#include <utility> #include <utility>
#include <QCheckBox> #include <QCheckBox>
@ -39,7 +40,7 @@
#include "DolphinQt/QtUtils/QtUtils.h" #include "DolphinQt/QtUtils/QtUtils.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.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 // 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, // the other order in the GUI so that Top will be above Bottom,
@ -460,10 +461,24 @@ void WiiPane::ValidateSelectionState()
void WiiPane::OnUSBWhitelistAddButton() void WiiPane::OnUSBWhitelistAddButton()
{ {
USBDeviceAddToWhitelistDialog usb_whitelist_dialog(this); auto whitelist = Config::GetUSBDeviceWhitelist();
connect(&usb_whitelist_dialog, &USBDeviceAddToWhitelistDialog::accepted, this,
&WiiPane::PopulateUSBPassthroughListWidget); const std::optional<USBUtils::DeviceInfo> usb_device = USBDevicePicker::Run(
usb_whitelist_dialog.exec(); 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() void WiiPane::OnUSBWhitelistRemoveButton()