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/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

View file

@ -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<USBUtils::DeviceInfo> 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<USBUtils::DeviceInfo>())
// "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<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>();
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()

View file

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

View file

@ -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 <optional>
#include <QDialog>
#include <QDialogButtonBox>
@ -18,23 +20,12 @@
#include <fmt/format.h>
#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<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();
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<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()
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<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
#include <optional>
#include <vector>
#include <QDialog>
@ -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<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:
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<USBUtils::DeviceInfo> GetSelectedDevice() const;
void InitControls();
void RefreshDeviceList();
void AddUSBDeviceToWhitelist();
void OnDeviceSelection();

View file

@ -5,6 +5,7 @@
#include <array>
#include <future>
#include <optional>
#include <utility>
#include <QCheckBox>
@ -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<USBUtils::DeviceInfo> 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()