|
|
@@ -0,0 +1,345 @@ |
|
|
// Copyright 2021 Dolphin Emulator Project |
|
|
// Licensed under GPLv2+ |
|
|
// Refer to the license.txt file included. |
|
|
|
|
|
#include "DolphinQt/Config/WiimoteControllersWidget.h" |
|
|
|
|
|
#include <QApplication> |
|
|
#include <QCheckBox> |
|
|
#include <QComboBox> |
|
|
#include <QGridLayout> |
|
|
#include <QGroupBox> |
|
|
#include <QLabel> |
|
|
#include <QPushButton> |
|
|
#include <QRadioButton> |
|
|
#include <QScreen> |
|
|
#include <QVBoxLayout> |
|
|
|
|
|
#include <map> |
|
|
#include <optional> |
|
|
|
|
|
#include "Core/ConfigManager.h" |
|
|
#include "Core/Core.h" |
|
|
#include "Core/HW/Wiimote.h" |
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h" |
|
|
#include "Core/IOS/IOS.h" |
|
|
#include "Core/IOS/USB/Bluetooth/BTReal.h" |
|
|
|
|
|
#include "DolphinQt/Config/Mapping/MappingWindow.h" |
|
|
#include "DolphinQt/QtUtils/ModalMessageBox.h" |
|
|
#include "DolphinQt/Settings.h" |
|
|
|
|
|
#include "UICommon/UICommon.h" |
|
|
|
|
|
WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(parent) |
|
|
{ |
|
|
CreateLayout(); |
|
|
LoadSettings(); |
|
|
ConnectWidgets(); |
|
|
} |
|
|
|
|
|
static int GetRadioButtonIndicatorWidth() |
|
|
{ |
|
|
const QStyle* style = QApplication::style(); |
|
|
QStyleOptionButton opt; |
|
|
|
|
|
// TODO: why does the macOS style act different? Is it because of the magic with |
|
|
// Cocoa widgets it does behind the scenes? |
|
|
if (style->objectName() == QStringLiteral("macintosh")) |
|
|
return style->subElementRect(QStyle::SE_RadioButtonIndicator, &opt).width(); |
|
|
|
|
|
return style->subElementRect(QStyle::SE_RadioButtonContents, &opt).left(); |
|
|
} |
|
|
|
|
|
static int GetLayoutHorizontalSpacing(const QGridLayout* layout) |
|
|
{ |
|
|
// TODO: shouldn't layout->horizontalSpacing() do all this? Why does it return -1? |
|
|
int hspacing = layout->horizontalSpacing(); |
|
|
if (hspacing >= 0) |
|
|
return hspacing; |
|
|
|
|
|
// According to docs, this is the fallback if horizontalSpacing() isn't set. |
|
|
auto style = layout->parentWidget()->style(); |
|
|
hspacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); |
|
|
if (hspacing >= 0) |
|
|
return hspacing; |
|
|
|
|
|
// Docs claim this is deprecated, but on macOS with Qt 5.8 this is the only one that actually |
|
|
// works. |
|
|
float pixel_ratio = QGuiApplication::primaryScreen()->devicePixelRatio(); |
|
|
#ifdef __APPLE__ |
|
|
// TODO is this still required? |
|
|
hspacing = pixel_ratio * style->pixelMetric(QStyle::PM_DefaultLayoutSpacing); |
|
|
if (hspacing >= 0) |
|
|
return hspacing; |
|
|
#endif |
|
|
|
|
|
// Ripped from qtbase/src/widgets/styles/qcommonstyle.cpp |
|
|
return pixel_ratio * 6; |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::CreateLayout() |
|
|
{ |
|
|
m_wiimote_layout = new QGridLayout(); |
|
|
m_wiimote_box = new QGroupBox(tr("Wii Remotes")); |
|
|
m_wiimote_box->setLayout(m_wiimote_layout); |
|
|
|
|
|
m_wiimote_passthrough = new QRadioButton(tr("Passthrough a Bluetooth adapter")); |
|
|
m_wiimote_sync = new QPushButton(tr("Sync")); |
|
|
m_wiimote_reset = new QPushButton(tr("Reset")); |
|
|
m_wiimote_refresh = new QPushButton(tr("Refresh")); |
|
|
m_wiimote_pt_labels[0] = new QLabel(tr("Sync real Wii Remotes and pair them")); |
|
|
m_wiimote_pt_labels[1] = new QLabel(tr("Reset all saved Wii Remote pairings")); |
|
|
m_wiimote_emu = new QRadioButton(tr("Emulate the Wii's Bluetooth adapter")); |
|
|
m_wiimote_continuous_scanning = new QCheckBox(tr("Continuous Scanning")); |
|
|
m_wiimote_real_balance_board = new QCheckBox(tr("Real Balance Board")); |
|
|
m_wiimote_speaker_data = new QCheckBox(tr("Enable Speaker Data")); |
|
|
m_wiimote_ciface = new QCheckBox(tr("Connect Wii Remotes for Emulated Controllers")); |
|
|
|
|
|
m_wiimote_layout->setVerticalSpacing(7); |
|
|
m_wiimote_layout->setColumnMinimumWidth(0, GetRadioButtonIndicatorWidth() - |
|
|
GetLayoutHorizontalSpacing(m_wiimote_layout)); |
|
|
m_wiimote_layout->setColumnStretch(2, 1); |
|
|
|
|
|
// Passthrough BT |
|
|
m_wiimote_layout->addWidget(m_wiimote_passthrough, m_wiimote_layout->rowCount(), 0, 1, -1); |
|
|
|
|
|
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_sync, sync_row, 3); |
|
|
|
|
|
int reset_row = m_wiimote_layout->rowCount(); |
|
|
m_wiimote_layout->addWidget(m_wiimote_pt_labels[1], reset_row, 1, 1, 2); |
|
|
m_wiimote_layout->addWidget(m_wiimote_reset, reset_row, 3); |
|
|
|
|
|
// Emulated BT |
|
|
m_wiimote_layout->addWidget(m_wiimote_emu, m_wiimote_layout->rowCount(), 0, 1, -1); |
|
|
|
|
|
for (size_t i = 0; i < m_wiimote_groups.size(); i++) |
|
|
{ |
|
|
auto* wm_label = m_wiimote_labels[i] = new QLabel(tr("Wii Remote %1").arg(i + 1)); |
|
|
auto* wm_box = m_wiimote_boxes[i] = new QComboBox(); |
|
|
auto* wm_button = m_wiimote_buttons[i] = new QPushButton(tr("Configure")); |
|
|
|
|
|
for (const auto& item : {tr("None"), tr("Emulated Wii Remote"), tr("Real Wii Remote")}) |
|
|
wm_box->addItem(item); |
|
|
|
|
|
int wm_row = m_wiimote_layout->rowCount(); |
|
|
m_wiimote_layout->addWidget(wm_label, wm_row, 1); |
|
|
m_wiimote_layout->addWidget(wm_box, wm_row, 2); |
|
|
m_wiimote_layout->addWidget(wm_button, wm_row, 3); |
|
|
} |
|
|
|
|
|
m_wiimote_layout->addWidget(m_wiimote_real_balance_board, m_wiimote_layout->rowCount(), 1, 1, -1); |
|
|
m_wiimote_layout->addWidget(m_wiimote_speaker_data, m_wiimote_layout->rowCount(), 1, 1, -1); |
|
|
|
|
|
m_wiimote_layout->addWidget(m_wiimote_ciface, m_wiimote_layout->rowCount(), 0, 1, -1); |
|
|
|
|
|
int continuous_scanning_row = m_wiimote_layout->rowCount(); |
|
|
m_wiimote_layout->addWidget(m_wiimote_continuous_scanning, continuous_scanning_row, 0, 1, 3); |
|
|
m_wiimote_layout->addWidget(m_wiimote_refresh, continuous_scanning_row, 3); |
|
|
|
|
|
auto* layout = new QVBoxLayout; |
|
|
layout->setMargin(0); |
|
|
layout->setAlignment(Qt::AlignTop); |
|
|
layout->addWidget(m_wiimote_box); |
|
|
setLayout(layout); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::ConnectWidgets() |
|
|
{ |
|
|
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, |
|
|
&WiimoteControllersWidget::UpdateDisabledWiimoteControls); |
|
|
|
|
|
connect(m_wiimote_passthrough, &QRadioButton::toggled, this, |
|
|
&WiimoteControllersWidget::OnWiimoteModeChanged); |
|
|
connect(m_wiimote_ciface, &QCheckBox::toggled, this, |
|
|
&WiimoteControllersWidget::OnWiimoteModeChanged); |
|
|
connect(m_wiimote_ciface, &QCheckBox::toggled, this, |
|
|
&WiimoteReal::HandleWiimotesInControllerInterfaceSettingChange); |
|
|
connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this, |
|
|
&WiimoteControllersWidget::OnWiimoteModeChanged); |
|
|
|
|
|
connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this, |
|
|
&WiimoteControllersWidget::SaveSettings); |
|
|
connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this, |
|
|
&WiimoteControllersWidget::SaveSettings); |
|
|
connect(m_wiimote_speaker_data, &QCheckBox::toggled, this, |
|
|
&WiimoteControllersWidget::SaveSettings); |
|
|
connect(m_wiimote_sync, &QPushButton::clicked, this, |
|
|
&WiimoteControllersWidget::OnBluetoothPassthroughSyncPressed); |
|
|
connect(m_wiimote_reset, &QPushButton::clicked, this, |
|
|
&WiimoteControllersWidget::OnBluetoothPassthroughResetPressed); |
|
|
connect(m_wiimote_refresh, &QPushButton::clicked, this, |
|
|
&WiimoteControllersWidget::OnWiimoteRefreshPressed); |
|
|
|
|
|
for (size_t i = 0; i < m_wiimote_groups.size(); i++) |
|
|
{ |
|
|
connect(m_wiimote_boxes[i], qOverload<int>(&QComboBox::currentIndexChanged), this, |
|
|
&WiimoteControllersWidget::SaveSettings); |
|
|
connect(m_wiimote_boxes[i], qOverload<int>(&QComboBox::currentIndexChanged), this, |
|
|
&WiimoteControllersWidget::OnWiimoteModeChanged); |
|
|
connect(m_wiimote_buttons[i], &QPushButton::clicked, this, |
|
|
&WiimoteControllersWidget::OnWiimoteConfigure); |
|
|
} |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::OnWiimoteModeChanged() |
|
|
{ |
|
|
SaveSettings(); |
|
|
|
|
|
// Make sure continuous scanning setting is applied. |
|
|
WiimoteReal::Initialize(::Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES); |
|
|
|
|
|
UpdateDisabledWiimoteControls(); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::UpdateDisabledWiimoteControls() |
|
|
{ |
|
|
const bool running = Core::GetState() != Core::State::Uninitialized; |
|
|
|
|
|
m_wiimote_emu->setEnabled(!running); |
|
|
m_wiimote_passthrough->setEnabled(!running); |
|
|
|
|
|
const bool running_gc = running && !SConfig::GetInstance().bWii; |
|
|
const bool enable_passthrough = m_wiimote_passthrough->isChecked() && !running_gc; |
|
|
const bool enable_emu_bt = !m_wiimote_passthrough->isChecked() && !running_gc; |
|
|
|
|
|
m_wiimote_sync->setEnabled(enable_passthrough); |
|
|
m_wiimote_reset->setEnabled(enable_passthrough); |
|
|
|
|
|
for (auto* pt_label : m_wiimote_pt_labels) |
|
|
pt_label->setEnabled(enable_passthrough); |
|
|
|
|
|
for (size_t i = 0; i < m_wiimote_groups.size(); i++) |
|
|
{ |
|
|
m_wiimote_labels[i]->setEnabled(enable_emu_bt); |
|
|
m_wiimote_boxes[i]->setEnabled(enable_emu_bt); |
|
|
|
|
|
const bool is_emu_wiimote = m_wiimote_boxes[i]->currentIndex() == 1; |
|
|
m_wiimote_buttons[i]->setEnabled(enable_emu_bt && is_emu_wiimote); |
|
|
} |
|
|
|
|
|
m_wiimote_real_balance_board->setEnabled(enable_emu_bt); |
|
|
m_wiimote_speaker_data->setEnabled(enable_emu_bt); |
|
|
|
|
|
const bool ciface_wiimotes = m_wiimote_ciface->isChecked(); |
|
|
|
|
|
m_wiimote_refresh->setEnabled((enable_emu_bt || ciface_wiimotes) && |
|
|
!m_wiimote_continuous_scanning->isChecked()); |
|
|
m_wiimote_continuous_scanning->setEnabled(enable_emu_bt || ciface_wiimotes); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::OnBluetoothPassthroughResetPressed() |
|
|
{ |
|
|
const auto ios = IOS::HLE::GetIOS(); |
|
|
|
|
|
if (!ios) |
|
|
{ |
|
|
ModalMessageBox::warning( |
|
|
this, tr("Warning"), |
|
|
tr("Saved Wii Remote pairings can only be reset when a Wii game is running.")); |
|
|
return; |
|
|
} |
|
|
|
|
|
auto device = ios->GetDeviceByName("/dev/usb/oh1/57e/305"); |
|
|
if (device != nullptr) |
|
|
{ |
|
|
std::static_pointer_cast<IOS::HLE::Device::BluetoothBase>(device)->TriggerSyncButtonHeldEvent(); |
|
|
} |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::OnBluetoothPassthroughSyncPressed() |
|
|
{ |
|
|
const auto ios = IOS::HLE::GetIOS(); |
|
|
|
|
|
if (!ios) |
|
|
{ |
|
|
ModalMessageBox::warning(this, tr("Warning"), |
|
|
tr("A sync can only be triggered when a Wii game is running.")); |
|
|
return; |
|
|
} |
|
|
|
|
|
auto device = ios->GetDeviceByName("/dev/usb/oh1/57e/305"); |
|
|
|
|
|
if (device != nullptr) |
|
|
{ |
|
|
std::static_pointer_cast<IOS::HLE::Device::BluetoothBase>(device) |
|
|
->TriggerSyncButtonPressedEvent(); |
|
|
} |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::OnWiimoteRefreshPressed() |
|
|
{ |
|
|
WiimoteReal::Refresh(); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::OnWiimoteConfigure() |
|
|
{ |
|
|
size_t index; |
|
|
for (index = 0; index < m_wiimote_groups.size(); index++) |
|
|
{ |
|
|
if (m_wiimote_buttons[index] == QObject::sender()) |
|
|
break; |
|
|
} |
|
|
|
|
|
MappingWindow::Type type; |
|
|
switch (m_wiimote_boxes[index]->currentIndex()) |
|
|
{ |
|
|
case 0: // None |
|
|
case 2: // Real Wii Remote |
|
|
return; |
|
|
case 1: // Emulated Wii Remote |
|
|
type = MappingWindow::Type::MAPPING_WIIMOTE_EMU; |
|
|
break; |
|
|
default: |
|
|
return; |
|
|
} |
|
|
|
|
|
MappingWindow* window = new MappingWindow(this, type, static_cast<int>(index)); |
|
|
window->setAttribute(Qt::WA_DeleteOnClose, true); |
|
|
window->setWindowModality(Qt::WindowModality::WindowModal); |
|
|
window->show(); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::LoadSettings() |
|
|
{ |
|
|
for (size_t i = 0; i < m_wiimote_groups.size(); i++) |
|
|
{ |
|
|
m_wiimote_boxes[i]->setCurrentIndex(int(WiimoteCommon::GetSource(u32(i)))); |
|
|
} |
|
|
m_wiimote_real_balance_board->setChecked(WiimoteCommon::GetSource(WIIMOTE_BALANCE_BOARD) == |
|
|
WiimoteSource::Real); |
|
|
m_wiimote_speaker_data->setChecked(SConfig::GetInstance().m_WiimoteEnableSpeaker); |
|
|
m_wiimote_ciface->setChecked(SConfig::GetInstance().connect_wiimotes_for_ciface); |
|
|
m_wiimote_continuous_scanning->setChecked(SConfig::GetInstance().m_WiimoteContinuousScanning); |
|
|
|
|
|
if (SConfig::GetInstance().m_bt_passthrough_enabled) |
|
|
m_wiimote_passthrough->setChecked(true); |
|
|
else |
|
|
m_wiimote_emu->setChecked(true); |
|
|
|
|
|
OnWiimoteModeChanged(); |
|
|
} |
|
|
|
|
|
void WiimoteControllersWidget::SaveSettings() |
|
|
{ |
|
|
SConfig::GetInstance().m_WiimoteEnableSpeaker = m_wiimote_speaker_data->isChecked(); |
|
|
SConfig::GetInstance().connect_wiimotes_for_ciface = m_wiimote_ciface->isChecked(); |
|
|
SConfig::GetInstance().m_WiimoteContinuousScanning = m_wiimote_continuous_scanning->isChecked(); |
|
|
SConfig::GetInstance().m_bt_passthrough_enabled = m_wiimote_passthrough->isChecked(); |
|
|
|
|
|
WiimoteCommon::SetSource(WIIMOTE_BALANCE_BOARD, m_wiimote_real_balance_board->isChecked() ? |
|
|
WiimoteSource::Real : |
|
|
WiimoteSource::None); |
|
|
|
|
|
for (size_t i = 0; i < m_wiimote_groups.size(); i++) |
|
|
{ |
|
|
const int index = m_wiimote_boxes[i]->currentIndex(); |
|
|
WiimoteCommon::SetSource(u32(i), WiimoteSource(index)); |
|
|
} |
|
|
|
|
|
UICommon::SaveWiimoteSources(); |
|
|
|
|
|
SConfig::GetInstance().SaveSettings(); |
|
|
} |