summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinQt/Config/ControllersWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/DolphinQt/Config/ControllersWindow.cpp')
-rw-r--r--Source/Core/DolphinQt/Config/ControllersWindow.cpp540
1 files changed, 540 insertions, 0 deletions
diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.cpp b/Source/Core/DolphinQt/Config/ControllersWindow.cpp
new file mode 100644
index 0000000000..981d8bf3b8
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/ControllersWindow.cpp
@@ -0,0 +1,540 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/Config/ControllersWindow.h"
+
+#include <QApplication>
+#include <QBoxLayout>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QRadioButton>
+#include <QScreen>
+#include <QVBoxLayout>
+
+#include <map>
+#include <optional>
+
+#include "Core/ConfigManager.h"
+#include "Core/Core.h"
+#include "Core/HW/SI/SI.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 "Core/NetPlayProto.h"
+
+#include "DolphinQt/Config/Mapping/GCPadWiiUConfigDialog.h"
+#include "DolphinQt/Config/Mapping/MappingWindow.h"
+#include "DolphinQt/QtUtils/WrapInScrollArea.h"
+#include "DolphinQt/Settings.h"
+
+#include "InputCommon/GCAdapter.h"
+
+#include "UICommon/UICommon.h"
+
+static const std::map<SerialInterface::SIDevices, int> s_gc_types = {
+ {SerialInterface::SIDEVICE_NONE, 0}, {SerialInterface::SIDEVICE_GC_CONTROLLER, 1},
+ {SerialInterface::SIDEVICE_WIIU_ADAPTER, 2}, {SerialInterface::SIDEVICE_GC_STEERING, 3},
+ {SerialInterface::SIDEVICE_DANCEMAT, 4}, {SerialInterface::SIDEVICE_GC_TARUKONGA, 5},
+ {SerialInterface::SIDEVICE_GC_GBA, 6}, {SerialInterface::SIDEVICE_GC_KEYBOARD, 7}};
+
+static std::optional<int> ToGCMenuIndex(const SerialInterface::SIDevices sidevice)
+{
+ auto it = s_gc_types.find(sidevice);
+ return it != s_gc_types.end() ? it->second : std::optional<int>();
+}
+
+static std::optional<SerialInterface::SIDevices> FromGCMenuIndex(const int menudevice)
+{
+ auto it = std::find_if(s_gc_types.begin(), s_gc_types.end(),
+ [=](auto pair) { return pair.second == menudevice; });
+ return it != s_gc_types.end() ? it->first : std::optional<SerialInterface::SIDevices>();
+}
+
+ControllersWindow::ControllersWindow(QWidget* parent) : QDialog(parent)
+{
+ setWindowTitle(tr("Controller Settings"));
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ CreateGamecubeLayout();
+ CreateWiimoteLayout();
+ CreateAdvancedLayout();
+ CreateMainLayout();
+ LoadSettings();
+ ConnectWidgets();
+}
+
+void ControllersWindow::CreateGamecubeLayout()
+{
+ m_gc_box = new QGroupBox(tr("GameCube Controllers"));
+ m_gc_layout = new QGridLayout();
+ m_gc_layout->setVerticalSpacing(7);
+ m_gc_layout->setColumnStretch(1, 1);
+
+ for (size_t i = 0; i < m_gc_groups.size(); i++)
+ {
+ auto* gc_label = new QLabel(tr("Port %1").arg(i + 1));
+ auto* gc_box = m_gc_controller_boxes[i] = new QComboBox();
+ auto* gc_button = m_gc_buttons[i] = new QPushButton(tr("Configure"));
+
+ for (const auto& item :
+ {tr("None"), tr("Standard Controller"), tr("GameCube Adapter for Wii U"),
+ tr("Steering Wheel"), tr("Dance Mat"), tr("DK Bongos"), tr("GBA"), tr("Keyboard")})
+ {
+ gc_box->addItem(item);
+ }
+
+ int controller_row = m_gc_layout->rowCount();
+ m_gc_layout->addWidget(gc_label, controller_row, 0);
+ m_gc_layout->addWidget(gc_box, controller_row, 1);
+ m_gc_layout->addWidget(gc_button, controller_row, 2);
+ }
+ m_gc_box->setLayout(m_gc_layout);
+}
+
+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();
+ hspacing = pixel_ratio * style->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
+ if (hspacing >= 0)
+ return hspacing;
+
+ // Ripped from qtbase/src/widgets/styles/qcommonstyle.cpp
+ return pixel_ratio * 6;
+}
+
+void ControllersWindow::CreateWiimoteLayout()
+{
+ 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_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);
+ }
+
+ int continuous_scanning_row = m_wiimote_layout->rowCount();
+ m_wiimote_layout->addWidget(m_wiimote_continuous_scanning, continuous_scanning_row, 1, 1, 2);
+ m_wiimote_layout->addWidget(m_wiimote_refresh, continuous_scanning_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);
+}
+
+void ControllersWindow::CreateAdvancedLayout()
+{
+ m_advanced_box = new QGroupBox(tr("Advanced"));
+ m_advanced_layout = new QHBoxLayout();
+ m_advanced_bg_input = new QCheckBox(tr("Background Input"));
+
+ m_advanced_layout->addWidget(m_advanced_bg_input);
+
+ m_advanced_box->setLayout(m_advanced_layout);
+}
+
+void ControllersWindow::CreateMainLayout()
+{
+ auto* layout = new QVBoxLayout();
+ m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
+
+ layout->addWidget(m_gc_box);
+ layout->addWidget(m_wiimote_box);
+ layout->addWidget(m_advanced_box);
+ layout->addWidget(m_button_box);
+
+ WrapInScrollArea(this, layout);
+}
+
+void ControllersWindow::ConnectWidgets()
+{
+ connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
+ [=](Core::State state) { OnEmulationStateChanged(state != Core::State::Uninitialized); });
+
+ connect(m_wiimote_passthrough, &QRadioButton::toggled, this,
+ &ControllersWindow::OnWiimoteModeChanged);
+
+ connect(m_advanced_bg_input, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings);
+ connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this,
+ &ControllersWindow::SaveSettings);
+ connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this,
+ &ControllersWindow::SaveSettings);
+ connect(m_wiimote_speaker_data, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings);
+ connect(m_wiimote_sync, &QPushButton::clicked, this,
+ &ControllersWindow::OnBluetoothPassthroughSyncPressed);
+ connect(m_wiimote_reset, &QPushButton::clicked, this,
+ &ControllersWindow::OnBluetoothPassthroughResetPressed);
+ connect(m_wiimote_refresh, &QPushButton::clicked, this,
+ &ControllersWindow::OnWiimoteRefreshPressed);
+
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ {
+ connect(m_wiimote_boxes[i],
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+ &ControllersWindow::SaveSettings);
+ connect(m_wiimote_boxes[i],
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+ &ControllersWindow::OnWiimoteTypeChanged);
+ connect(m_wiimote_buttons[i], &QPushButton::clicked, this,
+ &ControllersWindow::OnWiimoteConfigure);
+
+ connect(m_gc_controller_boxes[i],
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+ &ControllersWindow::SaveSettings);
+ connect(m_gc_controller_boxes[i],
+ static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+ &ControllersWindow::OnGCTypeChanged);
+ connect(m_gc_buttons[i], &QPushButton::clicked, this, &ControllersWindow::OnGCPadConfigure);
+ }
+}
+
+void ControllersWindow::OnWiimoteModeChanged(bool passthrough)
+{
+ SaveSettings();
+
+ m_wiimote_sync->setEnabled(passthrough);
+ m_wiimote_reset->setEnabled(passthrough);
+
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ {
+ const int index = m_wiimote_boxes[i]->currentIndex();
+
+ if (i < 2)
+ m_wiimote_pt_labels[i]->setEnabled(passthrough);
+
+ m_wiimote_labels[i]->setEnabled(!passthrough);
+ m_wiimote_boxes[i]->setEnabled(!passthrough);
+ m_wiimote_buttons[i]->setEnabled(!passthrough && index != 0 && index != 2);
+ }
+
+ m_wiimote_refresh->setEnabled(!passthrough);
+ m_wiimote_real_balance_board->setEnabled(!passthrough);
+ m_wiimote_speaker_data->setEnabled(!passthrough);
+ m_wiimote_continuous_scanning->setEnabled(!passthrough);
+}
+
+void ControllersWindow::OnWiimoteTypeChanged(int type)
+{
+ const auto* box = static_cast<QComboBox*>(QObject::sender());
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ {
+ if (m_wiimote_boxes[i] == box)
+ {
+ const int index = box->currentIndex();
+ m_wiimote_buttons[i]->setEnabled(index != 0 && index != 2);
+ return;
+ }
+ }
+
+ SaveSettings();
+}
+
+void ControllersWindow::OnGCTypeChanged(int type)
+{
+ const auto* box = static_cast<QComboBox*>(QObject::sender());
+
+ for (size_t i = 0; i < m_gc_groups.size(); i++)
+ {
+ if (m_gc_controller_boxes[i] == box)
+ {
+ const int index = box->currentIndex();
+ m_gc_buttons[i]->setEnabled(index != 0 && index != 6);
+ return;
+ }
+ }
+
+ SaveSettings();
+}
+
+void ControllersWindow::OnBluetoothPassthroughResetPressed()
+{
+ const auto ios = IOS::HLE::GetIOS();
+
+ if (!ios)
+ {
+ QMessageBox error(this);
+ error.setIcon(QMessageBox::Warning);
+ error.setWindowTitle(tr("Warning"));
+ error.setText(tr("Saved Wii Remote pairings can only be reset when a Wii game is running."));
+ error.exec();
+ return;
+ }
+
+ auto device = ios->GetDeviceByName("/dev/usb/oh1/57e/305");
+ if (device != nullptr)
+ {
+ std::static_pointer_cast<IOS::HLE::Device::BluetoothBase>(device)->TriggerSyncButtonHeldEvent();
+ }
+}
+
+void ControllersWindow::OnBluetoothPassthroughSyncPressed()
+{
+ const auto ios = IOS::HLE::GetIOS();
+
+ if (!ios)
+ {
+ QMessageBox error(this);
+ error.setIcon(QMessageBox::Warning);
+ error.setWindowTitle(tr("Warning"));
+ error.setText(tr("A sync can only be triggered when a Wii game is running."));
+ error.exec();
+ return;
+ }
+
+ auto device = ios->GetDeviceByName("/dev/usb/oh1/57e/305");
+
+ if (device != nullptr)
+ {
+ std::static_pointer_cast<IOS::HLE::Device::BluetoothBase>(device)
+ ->TriggerSyncButtonPressedEvent();
+ }
+}
+
+void ControllersWindow::OnWiimoteRefreshPressed()
+{
+ WiimoteReal::Refresh();
+}
+
+void ControllersWindow::OnEmulationStateChanged(bool running)
+{
+ if (!SConfig::GetInstance().bWii || NetPlay::IsNetPlayRunning())
+ {
+ m_wiimote_sync->setEnabled(!running);
+ m_wiimote_reset->setEnabled(!running);
+
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ m_wiimote_boxes[i]->setEnabled(!running);
+ }
+
+ m_wiimote_emu->setEnabled(!running);
+ m_wiimote_passthrough->setEnabled(!running);
+
+ if (!SConfig::GetInstance().bWii)
+ {
+ m_wiimote_real_balance_board->setEnabled(!running);
+ m_wiimote_continuous_scanning->setEnabled(!running);
+ m_wiimote_speaker_data->setEnabled(!running);
+ }
+}
+
+void ControllersWindow::OnGCPadConfigure()
+{
+ size_t index;
+ for (index = 0; index < m_gc_groups.size(); index++)
+ {
+ if (m_gc_buttons[index] == QObject::sender())
+ break;
+ }
+
+ MappingWindow::Type type;
+
+ switch (m_gc_controller_boxes[index]->currentIndex())
+ {
+ case 0: // None
+ case 6: // GBA
+ return;
+ case 1: // Standard Controller
+ type = MappingWindow::Type::MAPPING_GCPAD;
+ break;
+ case 2: // GameCube Adapter for Wii U
+ GCPadWiiUConfigDialog(static_cast<int>(index), this).exec();
+ return;
+ case 3: // Steering Wheel
+ type = MappingWindow::Type::MAPPING_GC_STEERINGWHEEL;
+ break;
+ case 4: // Dance Mat
+ type = MappingWindow::Type::MAPPING_GC_DANCEMAT;
+ break;
+ case 5: // DK Bongos
+ type = MappingWindow::Type::MAPPING_GC_BONGOS;
+ break;
+ case 7: // Keyboard
+ type = MappingWindow::Type::MAPPING_GC_KEYBOARD;
+ break;
+ default:
+ return;
+ }
+
+ MappingWindow(this, type, static_cast<int>(index)).exec();
+}
+
+void ControllersWindow::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(this, type, static_cast<int>(index)).exec();
+}
+
+void ControllersWindow::UnimplementedButton()
+{
+ QMessageBox error_dialog(this);
+
+ error_dialog.setIcon(QMessageBox::Warning);
+ error_dialog.setWindowTitle(tr("Unimplemented"));
+ error_dialog.setText(tr("Not implemented yet."));
+ error_dialog.exec();
+}
+
+void ControllersWindow::LoadSettings()
+{
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ {
+ const std::optional<int> gc_index = ToGCMenuIndex(SConfig::GetInstance().m_SIDevice[i]);
+ if (gc_index)
+ m_gc_controller_boxes[i]->setCurrentIndex(*gc_index);
+ m_wiimote_boxes[i]->setCurrentIndex(g_wiimote_sources[i]);
+ }
+ m_wiimote_real_balance_board->setChecked(g_wiimote_sources[WIIMOTE_BALANCE_BOARD] ==
+ WIIMOTE_SRC_REAL);
+ m_wiimote_speaker_data->setChecked(SConfig::GetInstance().m_WiimoteEnableSpeaker);
+ m_wiimote_continuous_scanning->setChecked(SConfig::GetInstance().m_WiimoteContinuousScanning);
+
+ m_advanced_bg_input->setChecked(SConfig::GetInstance().m_BackgroundInput);
+
+ if (SConfig::GetInstance().m_bt_passthrough_enabled)
+ m_wiimote_passthrough->setChecked(true);
+ else
+ m_wiimote_emu->setChecked(true);
+
+ OnWiimoteModeChanged(SConfig::GetInstance().m_bt_passthrough_enabled);
+}
+
+void ControllersWindow::SaveSettings()
+{
+ SConfig::GetInstance().m_WiimoteEnableSpeaker = m_wiimote_speaker_data->isChecked();
+ SConfig::GetInstance().m_WiimoteContinuousScanning = m_wiimote_continuous_scanning->isChecked();
+ SConfig::GetInstance().m_bt_passthrough_enabled = m_wiimote_passthrough->isChecked();
+ SConfig::GetInstance().m_BackgroundInput = m_advanced_bg_input->isChecked();
+
+ WiimoteReal::ChangeWiimoteSource(WIIMOTE_BALANCE_BOARD,
+ m_wiimote_real_balance_board->isChecked() ? WIIMOTE_SRC_REAL :
+ WIIMOTE_SRC_NONE);
+
+ for (size_t i = 0; i < m_wiimote_groups.size(); i++)
+ {
+ const int index = m_wiimote_boxes[i]->currentIndex();
+ g_wiimote_sources[i] = index;
+ m_wiimote_buttons[i]->setEnabled(index != 0 && index != 2);
+
+ if (Core::IsRunning())
+ WiimoteReal::ChangeWiimoteSource(static_cast<u32>(i), index);
+ }
+
+ UICommon::SaveWiimoteSources();
+
+ for (size_t i = 0; i < m_gc_groups.size(); i++)
+ {
+ const int index = m_gc_controller_boxes[i]->currentIndex();
+ const std::optional<SerialInterface::SIDevices> si_device = FromGCMenuIndex(index);
+ if (si_device)
+ {
+ SConfig::GetInstance().m_SIDevice[i] = *si_device;
+
+ if (Core::IsRunning())
+ SerialInterface::ChangeDevice(*si_device, static_cast<s32>(i));
+ }
+
+ m_gc_buttons[i]->setEnabled(index != 0 && index != 6);
+ }
+
+ if (GCAdapter::UseAdapter())
+ GCAdapter::StartScanThread();
+ else
+ GCAdapter::StopScanThread();
+
+ SConfig::GetInstance().SaveSettings();
+}