diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index b84119fad0..783cefbb68 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -51,6 +51,10 @@ add_executable(dolphin-emu Config/CheatCodeEditor.h Config/CheatWarningWidget.cpp Config/CheatWarningWidget.h + Config/ControllerInterface/CemuHookUDPServerWidget.cpp + Config/ControllerInterface/CemuHookUDPServerWidget.h + Config/ControllerInterface/ControllerInterfaceWindow.cpp + Config/ControllerInterface/ControllerInterfaceWindow.h Config/ControllersWindow.cpp Config/ControllersWindow.h Config/FilesystemWidget.cpp diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp new file mode 100644 index 0000000000..47ab3d71b7 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp @@ -0,0 +1,75 @@ +// Copyright 2019 Dolphin Emulator Project5~5~5~ +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" + +#include +#include +#include +#include +#include +#include + +#include "Common/Config/Config.h" +#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" + +CemuHookUDPServerWidget::CemuHookUDPServerWidget() +{ + CreateWidgets(); + ConnectWidgets(); +} + +void CemuHookUDPServerWidget::CreateWidgets() +{ + auto* main_layout = new QGridLayout; + + m_server_enabled = new QCheckBox(tr("Enable")); + m_server_enabled->setChecked(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED)); + + m_server_address = new QLineEdit( + QString::fromStdString(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS))); + + m_server_port = new QSpinBox(); + m_server_port->setMaximum(65535); + m_server_port->setValue(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_PORT)); + + auto* description = + new QLabel(tr("UDPServer protocol enables the use of input and motion data from compatible " + "sources, like PlayStation, Nintendo Switch and Steam controllers.

" + "For setup instructions, " + "" + "refer to this page.")); + description->setTextFormat(Qt::RichText); + description->setWordWrap(true); + description->setTextInteractionFlags(Qt::TextBrowserInteraction); + description->setOpenExternalLinks(true); + + main_layout->addWidget(m_server_enabled, 1, 1); + main_layout->addWidget(new QLabel(tr("Server IP Address")), 2, 1); + main_layout->addWidget(m_server_address, 2, 2); + main_layout->addWidget(new QLabel(tr("Server Port")), 3, 1); + main_layout->addWidget(m_server_port, 3, 2); + main_layout->addWidget(description, 4, 1, 1, 2); + + setLayout(main_layout); +} + +void CemuHookUDPServerWidget::ConnectWidgets() +{ + connect(m_server_enabled, &QCheckBox::toggled, this, [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED, + m_server_enabled->isChecked()); + }); + + connect(m_server_address, &QLineEdit::editingFinished, this, [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS, + m_server_address->text().toStdString()); + }); + + connect(m_server_port, static_cast(&QSpinBox::valueChanged), this, + [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_PORT, + static_cast(m_server_port->value())); + }); +} diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h new file mode 100644 index 0000000000..24a0db18ac --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h @@ -0,0 +1,26 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +class QCheckBox; +class QLineEdit; +class QSpinBox; + +class CemuHookUDPServerWidget final : public QWidget +{ + Q_OBJECT +public: + explicit CemuHookUDPServerWidget(); + +private: + void CreateWidgets(); + void ConnectWidgets(); + + QCheckBox* m_server_enabled; + QLineEdit* m_server_address; + QSpinBox* m_server_port; +}; diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp new file mode 100644 index 0000000000..1ea5501e7a --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" + +#include +#include +#include +#include + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) +#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" +#endif + +ControllerInterfaceWindow::ControllerInterfaceWindow(QWidget* parent) : QDialog(parent) +{ + CreateMainLayout(); + + setWindowTitle(tr("Alternate Input Sources")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +void ControllerInterfaceWindow::CreateMainLayout() +{ + m_button_box = new QDialogButtonBox(QDialogButtonBox::Close); + connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + m_tab_widget = new QTabWidget(); +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) + m_udpserver_widget = new CemuHookUDPServerWidget(); + m_tab_widget->addTab(m_udpserver_widget, tr("UDPServer")); // TODO: use GetWrappedWidget()? +#endif + + auto* main_layout = new QVBoxLayout(); + if (m_tab_widget->count() > 0) + { + main_layout->addWidget(m_tab_widget); + } + else + { + main_layout->addWidget(new QLabel(tr("Nothing to configure")), 0, + Qt::AlignVCenter | Qt::AlignHCenter); + } + main_layout->addWidget(m_button_box); + setLayout(main_layout); +} diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h new file mode 100644 index 0000000000..47d12a9199 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h @@ -0,0 +1,32 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "InputCommon/ControllerInterface/ControllerInterface.h" + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) +class CemuHookUDPServerWidget; +#endif +class QTabWidget; +class QDialogButtonBox; + +class ControllerInterfaceWindow final : public QDialog +{ + Q_OBJECT +public: + explicit ControllerInterfaceWindow(QWidget* parent); + +private: + void CreateMainLayout(); + + QTabWidget* m_tab_widget; + QDialogButtonBox* m_button_box; + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) + CemuHookUDPServerWidget* m_udpserver_widget; +#endif +}; diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.cpp b/Source/Core/DolphinQt/Config/ControllersWindow.cpp index 0f2cfc00e3..1df7b1d9a7 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.cpp +++ b/Source/Core/DolphinQt/Config/ControllersWindow.cpp @@ -31,6 +31,7 @@ #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Bluetooth/BTReal.h" +#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" #include "DolphinQt/Config/Mapping/GCPadWiiUConfigDialog.h" #include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" @@ -203,10 +204,12 @@ void ControllersWindow::CreateCommonLayout() { // i18n: This is "common" as in "shared", not the opposite of "uncommon" m_common_box = new QGroupBox(tr("Common")); - m_common_layout = new QHBoxLayout(); + m_common_layout = new QVBoxLayout(); m_common_bg_input = new QCheckBox(tr("Background Input")); + m_common_configure_controller_interface = new QPushButton(tr("Alternate Input Sources")); m_common_layout->addWidget(m_common_bg_input); + m_common_layout->addWidget(m_common_configure_controller_interface); m_common_box->setLayout(m_common_layout); } @@ -219,6 +222,7 @@ void ControllersWindow::CreateMainLayout() layout->addWidget(m_gc_box); layout->addWidget(m_wiimote_box); layout->addWidget(m_common_box); + layout->addStretch(); layout->addWidget(m_button_box); WrapInScrollArea(this, layout); @@ -234,6 +238,8 @@ void ControllersWindow::ConnectWidgets() &ControllersWindow::OnWiimoteModeChanged); connect(m_common_bg_input, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings); + connect(m_common_configure_controller_interface, &QPushButton::clicked, this, + &ControllersWindow::OnControllerInterfaceConfigure); connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings); connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this, @@ -463,6 +469,14 @@ void ControllersWindow::OnWiimoteConfigure() window->show(); } +void ControllersWindow::OnControllerInterfaceConfigure() +{ + ControllerInterfaceWindow* window = new ControllerInterfaceWindow(this); + window->setAttribute(Qt::WA_DeleteOnClose, true); + window->setWindowModality(Qt::WindowModality::WindowModal); + window->show(); +} + void ControllersWindow::LoadSettings() { for (size_t i = 0; i < m_wiimote_groups.size(); i++) diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.h b/Source/Core/DolphinQt/Config/ControllersWindow.h index 98e81bd058..368ef36cdb 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.h +++ b/Source/Core/DolphinQt/Config/ControllersWindow.h @@ -37,6 +37,7 @@ private: void OnWiimoteRefreshPressed(); void OnGCPadConfigure(); void OnWiimoteConfigure(); + void OnControllerInterfaceConfigure(); void CreateGamecubeLayout(); void CreateWiimoteLayout(); @@ -75,6 +76,7 @@ private: // Common QGroupBox* m_common_box; - QHBoxLayout* m_common_layout; + QVBoxLayout* m_common_layout; QCheckBox* m_common_bg_input; + QPushButton* m_common_configure_controller_interface; }; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 0d0aee2c7c..90b334266a 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -44,7 +44,7 @@ avrt.lib;iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;Shlwapi.lib;discord-rpc.lib;%(AdditionalDependencies) - $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Debugger;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;$(ProjectDir)TAS;$(ProjectDir)FIFO;%(AdditionalIncludeDirectories) + $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Debugger;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)Config\ControllerInterface;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;$(ProjectDir)TAS;$(ProjectDir)FIFO;%(AdditionalIncludeDirectories) DolphinQt.manifest;%(AdditionalManifestFiles) @@ -106,6 +106,8 @@ + + @@ -187,6 +189,8 @@ + + @@ -291,6 +295,8 @@ + + diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp index a480320b33..36cd148041 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp @@ -135,6 +135,7 @@ static std::thread s_hotplug_thread; static Common::Flag s_hotplug_thread_running; static std::mutex s_port_info_mutex; static Proto::MessageType::PortInfo s_port_info[Proto::PORT_COUNT]; +static sf::UdpSocket s_socket; static bool IsSameController(const Proto::MessageType::PortInfo& a, const Proto::MessageType::PortInfo& b) @@ -162,8 +163,6 @@ static void HotplugThreadFunc() Common::SetCurrentThreadName("CemuHookUDPServer Hotplug Thread"); NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer hotplug thread started"); - sf::UdpSocket socket; - while (s_hotplug_thread_running.IsSet()) { const auto now = std::chrono::steady_clock::now(); @@ -180,7 +179,7 @@ static void HotplugThreadFunc() list_ports.pad_id[2] = 2; list_ports.pad_id[3] = 3; msg.Finish(); - if (socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != + if (s_socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != sf::Socket::Status::Done) ERROR_LOG(SERIALINTERFACE, "CemuHookUDPServer HotplugThreadFunc send failed"); } @@ -194,7 +193,7 @@ static void HotplugThreadFunc() std::size_t received_bytes; sf::IpAddress sender; u16 port; - if (ReceiveWithTimeout(socket, &msg, sizeof(msg), received_bytes, sender, port, + if (ReceiveWithTimeout(s_socket, &msg, sizeof(msg), received_bytes, sender, port, sf::milliseconds(timeout_ms)) == sf::Socket::Status::Done) { if (auto port_info = msg.CheckAndCastTo()) @@ -233,14 +232,15 @@ static void StopHotplugThread() return; } + s_socket.unbind(); // interrupt blocking socket s_hotplug_thread.join(); } -void Init() +static void Restart() { - s_server_enabled = Config::Get(Settings::SERVER_ENABLED); - s_server_address = Config::Get(Settings::SERVER_ADDRESS); - s_server_port = Config::Get(Settings::SERVER_PORT); + NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer Restart"); + + StopHotplugThread(); s_client_uid = Common::Random::GenerateValue(); s_next_listports = std::chrono::steady_clock::time_point::min(); @@ -250,10 +250,32 @@ void Init() s_port_info[port_index].pad_id = port_index; } + PopulateDevices(); // remove devices + if (s_server_enabled) StartHotplugThread(); } +static void ConfigChanged() +{ + bool server_enabled = Config::Get(Settings::SERVER_ENABLED); + std::string server_address = Config::Get(Settings::SERVER_ADDRESS); + u16 server_port = Config::Get(Settings::SERVER_PORT); + if (server_enabled != s_server_enabled || server_address != s_server_address || + server_port != s_server_port) + { + s_server_enabled = server_enabled; + s_server_address = server_address; + s_server_port = server_port; + Restart(); + } +} + +void Init() +{ + Config::AddConfigChangedCallback(ConfigChanged); +} + void PopulateDevices() { NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer PopulateDevices"); @@ -273,16 +295,6 @@ void PopulateDevices() void DeInit() { StopHotplugThread(); - SaveSettings(); -} - -void SaveSettings() -{ - Config::ConfigChangeCallbackGuard config_guard; - - Config::SetBaseOrCurrent(Settings::SERVER_ENABLED, s_server_enabled); - Config::SetBaseOrCurrent(Settings::SERVER_ADDRESS, s_server_address); - Config::SetBaseOrCurrent(Settings::SERVER_PORT, s_server_port); } Device::Device(Proto::DsModel model, int index) diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h index 98b2506d2e..f7eaa34407 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h @@ -2,10 +2,18 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Config/Config.h" + namespace ciface::CemuHookUDPServer { +namespace Settings +{ +extern const Config::ConfigInfo SERVER_ENABLED; +extern const Config::ConfigInfo SERVER_ADDRESS; +extern const Config::ConfigInfo SERVER_PORT; +} // namespace Settings + void Init(); void PopulateDevices(); void DeInit(); -void SaveSettings(); } // namespace ciface::CemuHookUDPServer