@@ -28,6 +28,9 @@ class ControlGroup;
class Cursor;
class Extension;
class Force;
class IMUAccelerometer;
class IMUGyroscope;
class IMUCursor;
class ModifySettingsButton;
class Output;
class Tilt;
@@ -45,9 +48,11 @@ enum class WiimoteGroup
Swing,
Rumble,
Attachments,

Options,
Hotkeys
Hotkeys,
IMUAccelerometer,
IMUGyroscope,
IMUPoint,
};

enum class NunchukGroup;
@@ -140,22 +145,29 @@ class Wiimote : public ControllerEmu::EmulatedController
// This is the region exposed over bluetooth:
static constexpr int EEPROM_FREE_SIZE = 0x1700;

static constexpr double BUTTON_THRESHOLD = 0.5;

void UpdateButtonsStatus();

// Returns simulated accelerometer data in m/s^2.
Common::Vec3 GetAcceleration();
Common::Vec3 GetAcceleration(
Common::Vec3 extra_acceleration = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)));

// Returns simulated gyroscope data in radians/s.
Common::Vec3 GetAngularVelocity();
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity = {});

// Returns the transformation of the world around the wiimote.
// Used for simulating camera data and for rotating acceleration data.
// Does not include orientation transformations.
Common::Matrix44 GetTransformation() const;
Common::Matrix44 GetTransformation(Common::Vec3 extra_rotation = {}) const;

// Returns the world rotation from the effects of sideways/upright settings.
Common::Matrix33 GetOrientation() const;

Common::Vec3 GetTotalAcceleration();
Common::Vec3 GetTotalAngularVelocity();
Common::Matrix44 GetTotalTransformation() const;

void HIDOutputReport(const void* data, u32 size);

void HandleReportRumble(const WiimoteCommon::OutputReportRumble&);
@@ -246,6 +258,9 @@ class Wiimote : public ControllerEmu::EmulatedController
ControllerEmu::Attachments* m_attachments;
ControllerEmu::ControlGroup* m_options;
ControllerEmu::ModifySettingsButton* m_hotkeys;
ControllerEmu::IMUAccelerometer* m_imu_accelerometer;
ControllerEmu::IMUGyroscope* m_imu_gyroscope;
ControllerEmu::IMUCursor* m_imu_ir;

ControllerEmu::SettingValue<bool> m_sideways_setting;
ControllerEmu::SettingValue<bool> m_upright_setting;
@@ -284,5 +299,6 @@ class Wiimote : public ControllerEmu::EmulatedController
RotationalState m_tilt_state;
MotionState m_cursor_state;
PositionalState m_shake_state;
std::optional<RotationalState> m_imu_cursor_state;
};
} // namespace WiimoteEmu
@@ -51,6 +51,10 @@ add_executable(dolphin-emu
Config/CheatCodeEditor.h
Config/CheatWarningWidget.cpp
Config/CheatWarningWidget.h
Config/ControllerInterface/DualShockUDPClientWidget.cpp
Config/ControllerInterface/DualShockUDPClientWidget.h
Config/ControllerInterface/ControllerInterfaceWindow.cpp
Config/ControllerInterface/ControllerInterfaceWindow.h
Config/ControllersWindow.cpp
Config/ControllersWindow.h
Config/FilesystemWidget.cpp
@@ -140,6 +144,8 @@ add_executable(dolphin-emu
Config/Mapping/WiimoteEmuGeneral.h
Config/Mapping/WiimoteEmuMotionControl.cpp
Config/Mapping/WiimoteEmuMotionControl.h
Config/Mapping/WiimoteEmuMotionControlIMU.cpp
Config/Mapping/WiimoteEmuMotionControlIMU.h
Config/NewPatchDialog.cpp
Config/NewPatchDialog.h
Config/PatchesWidget.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 <QDialogButtonBox>
#include <QLabel>
#include <QTabWidget>
#include <QVBoxLayout>

#if defined(CIFACE_USE_DUALSHOCKUDPCLIENT)
#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.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_DUALSHOCKUDPCLIENT)
m_dsuclient_widget = new DualShockUDPClientWidget();
m_tab_widget->addTab(m_dsuclient_widget, tr("DSU Client")); // 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);
}
@@ -0,0 +1,32 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <QDialog>

#include "InputCommon/ControllerInterface/ControllerInterface.h"

#if defined(CIFACE_USE_DUALSHOCKUDPCLIENT)
class DualShockUDPClientWidget;
#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_DUALSHOCKUDPCLIENT)
DualShockUDPClientWidget* m_dsuclient_widget;
#endif
};
@@ -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/DualShockUDPClientWidget.h"

#include <QCheckBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>

#include "Common/Config/Config.h"
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"

DualShockUDPClientWidget::DualShockUDPClientWidget()
{
CreateWidgets();
ConnectWidgets();
}

void DualShockUDPClientWidget::CreateWidgets()
{
auto* main_layout = new QGridLayout;

m_server_enabled = new QCheckBox(tr("Enable"));
m_server_enabled->setChecked(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_ENABLED));

m_server_address = new QLineEdit(
QString::fromStdString(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_ADDRESS)));

m_server_port = new QSpinBox();
m_server_port->setMaximum(65535);
m_server_port->setValue(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_PORT));

auto* description =
new QLabel(tr("DSU protocol enables the use of input and motion data from compatible "
"sources, like PlayStation, Nintendo Switch and Steam controllers.<br><br>"
"For setup instructions, "
"<a href=\"https://wiki.dolphin-emu.org/index.php?title=DSU_Client\">"
"refer to this page</a>."));
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 DualShockUDPClientWidget::ConnectWidgets()
{
connect(m_server_enabled, &QCheckBox::toggled, this, [this] {
Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ENABLED,
m_server_enabled->isChecked());
});

connect(m_server_address, &QLineEdit::editingFinished, this, [this] {
Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ADDRESS,
m_server_address->text().toStdString());
});

connect(m_server_port, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
[this] {
Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_PORT,
static_cast<u16>(m_server_port->value()));
});
}
@@ -0,0 +1,26 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <QWidget>

class QCheckBox;
class QLineEdit;
class QSpinBox;

class DualShockUDPClientWidget final : public QWidget
{
Q_OBJECT
public:
explicit DualShockUDPClientWidget();

private:
void CreateWidgets();
void ConnectWidgets();

QCheckBox* m_server_enabled;
QLineEdit* m_server_address;
QSpinBox* m_server_port;
};
@@ -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"
@@ -67,7 +68,7 @@ ControllersWindow::ControllersWindow(QWidget* parent) : QDialog(parent)

CreateGamecubeLayout();
CreateWiimoteLayout();
CreateAdvancedLayout();
CreateCommonLayout();
CreateMainLayout();
LoadSettings();
ConnectWidgets();
@@ -199,15 +200,18 @@ void ControllersWindow::CreateWiimoteLayout()
m_wiimote_layout->addWidget(m_wiimote_speaker_data, m_wiimote_layout->rowCount(), 1, 1, -1);
}

void ControllersWindow::CreateAdvancedLayout()
void ControllersWindow::CreateCommonLayout()
{
m_advanced_box = new QGroupBox(tr("Advanced"));
m_advanced_layout = new QHBoxLayout();
m_advanced_bg_input = new QCheckBox(tr("Background Input"));
// i18n: This is "common" as in "shared", not the opposite of "uncommon"
m_common_box = new QGroupBox(tr("Common"));
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_advanced_layout->addWidget(m_advanced_bg_input);
m_common_layout->addWidget(m_common_bg_input);
m_common_layout->addWidget(m_common_configure_controller_interface);

m_advanced_box->setLayout(m_advanced_layout);
m_common_box->setLayout(m_common_layout);
}

void ControllersWindow::CreateMainLayout()
@@ -217,7 +221,8 @@ void ControllersWindow::CreateMainLayout()

layout->addWidget(m_gc_box);
layout->addWidget(m_wiimote_box);
layout->addWidget(m_advanced_box);
layout->addWidget(m_common_box);
layout->addStretch();
layout->addWidget(m_button_box);

WrapInScrollArea(this, layout);
@@ -232,7 +237,9 @@ void ControllersWindow::ConnectWidgets()
connect(m_wiimote_passthrough, &QRadioButton::toggled, this,
&ControllersWindow::OnWiimoteModeChanged);

connect(m_advanced_bg_input, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings);
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,
@@ -462,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++)
@@ -480,7 +495,7 @@ void ControllersWindow::LoadSettings()
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);
m_common_bg_input->setChecked(SConfig::GetInstance().m_BackgroundInput);

if (SConfig::GetInstance().m_bt_passthrough_enabled)
m_wiimote_passthrough->setChecked(true);
@@ -495,7 +510,7 @@ 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();
SConfig::GetInstance().m_BackgroundInput = m_common_bg_input->isChecked();

WiimoteReal::ChangeWiimoteSource(WIIMOTE_BALANCE_BOARD,
m_wiimote_real_balance_board->isChecked() ? WIIMOTE_SRC_REAL :
@@ -37,10 +37,11 @@ class ControllersWindow final : public QDialog
void OnWiimoteRefreshPressed();
void OnGCPadConfigure();
void OnWiimoteConfigure();
void OnControllerInterfaceConfigure();

void CreateGamecubeLayout();
void CreateWiimoteLayout();
void CreateAdvancedLayout();
void CreateCommonLayout();
void CreateMainLayout();
void ConnectWidgets();
void LoadSettings();
@@ -73,8 +74,9 @@ class ControllersWindow final : public QDialog
QCheckBox* m_wiimote_speaker_data;
QPushButton* m_wiimote_refresh;

// Advanced
QGroupBox* m_advanced_box;
QHBoxLayout* m_advanced_layout;
QCheckBox* m_advanced_bg_input;
// Common
QGroupBox* m_common_box;
QVBoxLayout* m_common_layout;
QCheckBox* m_common_bg_input;
QPushButton* m_common_configure_controller_interface;
};
@@ -35,6 +35,7 @@
#include "DolphinQt/Config/Mapping/WiimoteEmuExtension.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuGeneral.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControl.h"
#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include "DolphinQt/Settings.h"
@@ -348,7 +349,8 @@ void MappingWindow::SetMappingType(MappingWindow::Type type)
widget = new WiimoteEmuGeneral(this, extension);
setWindowTitle(tr("Wii Remote %1").arg(GetPort() + 1));
AddWidget(tr("General and Options"), widget);
AddWidget(tr("Motion Controls"), new WiimoteEmuMotionControl(this));
AddWidget(tr("Motion Simulation"), new WiimoteEmuMotionControl(this));
AddWidget(tr("Motion Input"), new WiimoteEmuMotionControlIMU(this));
AddWidget(tr("Extension"), extension);
break;
}
@@ -0,0 +1,75 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h"

#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QString>
#include <QVBoxLayout>

#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"

#include "InputCommon/InputConfig.h"

WiimoteEmuMotionControlIMU::WiimoteEmuMotionControlIMU(MappingWindow* window)
: MappingWidget(window)
{
CreateMainLayout();
}

void WiimoteEmuMotionControlIMU::CreateMainLayout()
{
auto* warning_layout = new QHBoxLayout();
auto* warning_label =
new QLabel(tr("WARNING: The controls under Accelerometer and Gyroscope are designed to "
"interface directly with motion sensor hardware. They are not intended for "
"mapping traditional buttons, triggers or axes. You might need to configure "
"alternate input sources before using these controls."));
warning_label->setWordWrap(true);
auto* warning_input_sources_button = new QPushButton(tr("Alternate Input Sources"));
warning_layout->addWidget(warning_label, 1);
warning_layout->addWidget(warning_input_sources_button, 0, Qt::AlignRight);
connect(warning_input_sources_button, &QPushButton::clicked, this, [this] {
ControllerInterfaceWindow* window = new ControllerInterfaceWindow(this);
window->setAttribute(Qt::WA_DeleteOnClose, true);
window->setWindowModality(Qt::WindowModality::WindowModal);
window->show();
});

auto* groups_layout = new QHBoxLayout();
groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint)));
groups_layout->addWidget(CreateGroupBox(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer)));
groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUGyroscope)));

m_main_layout = new QVBoxLayout();
m_main_layout->addLayout(warning_layout);
m_main_layout->addLayout(groups_layout);

setLayout(m_main_layout);
}

void WiimoteEmuMotionControlIMU::LoadSettings()
{
Wiimote::LoadConfig();
}

void WiimoteEmuMotionControlIMU::SaveSettings()
{
Wiimote::GetConfig()->SaveConfig();
}

InputConfig* WiimoteEmuMotionControlIMU::GetConfig()
{
return Wiimote::GetConfig();
}
@@ -0,0 +1,31 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include "DolphinQt/Config/Mapping/MappingWidget.h"

class QCheckBox;
class QFormLayout;
class QGroupBox;
class QHBoxLayout;
class QLabel;
class QVBoxLayout;

class WiimoteEmuMotionControlIMU final : public MappingWidget
{
Q_OBJECT
public:
explicit WiimoteEmuMotionControlIMU(MappingWindow* window);

InputConfig* GetConfig() override;

private:
void LoadSettings() override;
void SaveSettings() override;
void CreateMainLayout();

// Main
QVBoxLayout* m_main_layout;
};
@@ -44,7 +44,7 @@
<AdditionalDependencies>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)</AdditionalDependencies>
</Link>
<ClCompile>
<AdditionalIncludeDirectories>$(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)</AdditionalIncludeDirectories>
<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)</AdditionalIncludeDirectories>
</ClCompile>
<Manifest>
<AdditionalManifestFiles>DolphinQt.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
@@ -89,6 +89,7 @@
<QtMoc Include="Config\Mapping\WiimoteEmuExtension.h" />
<QtMoc Include="Config\Mapping\WiimoteEmuGeneral.h" />
<QtMoc Include="Config\Mapping\WiimoteEmuMotionControl.h" />
<QtMoc Include="Config\Mapping\WiimoteEmuMotionControlIMU.h" />
<QtMoc Include="Config\LogConfigWidget.h" />
<QtMoc Include="Config\LogWidget.h" />
<QtMoc Include="Config\NewPatchDialog.h" />
@@ -105,6 +106,8 @@
<QtMoc Include="Config\Graphics\HacksWidget.h" />
<QtMoc Include="Config\Graphics\PostProcessingConfigWindow.h" />
<QtMoc Include="Config\Graphics\SoftwareRendererWidget.h" />
<QtMoc Include="Config\ControllerInterface\DualShockUDPClientWidget.h" />
<QtMoc Include="Config\ControllerInterface\ControllerInterfaceWindow.h" />
<QtMoc Include="Config\InfoWidget.h" />
<QtMoc Include="Config\PatchesWidget.h" />
<QtMoc Include="Config\PropertiesDialog.h" />
@@ -186,6 +189,8 @@
<ClCompile Include="$(QtMocOutPrefix)ChunkedProgressDialog.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeViewWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CodeWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)DualShockUDPClientWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ControllerInterfaceWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ControllersWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)DiscordHandler.cpp" />
<ClCompile Include="$(QtMocOutPrefix)DiscordJoinRequestDialog.cpp" />
@@ -283,12 +288,15 @@
<ClCompile Include="$(QtMocOutPrefix)WiimoteEmuExtension.cpp" />
<ClCompile Include="$(QtMocOutPrefix)WiimoteEmuGeneral.cpp" />
<ClCompile Include="$(QtMocOutPrefix)WiimoteEmuMotionControl.cpp" />
<ClCompile Include="$(QtMocOutPrefix)WiimoteEmuMotionControlIMU.cpp" />
<ClCompile Include="$(QtMocOutPrefix)WindowActivationEventFilter.cpp" />
<ClCompile Include="AboutDialog.cpp" />
<ClCompile Include="CheatsManager.cpp" />
<ClCompile Include="Config\CheatCodeEditor.cpp" />
<ClCompile Include="Config\ARCodeWidget.cpp" />
<ClCompile Include="Config\CheatWarningWidget.cpp" />
<ClCompile Include="Config\ControllerInterface\DualShockUDPClientWidget.cpp" />
<ClCompile Include="Config\ControllerInterface\ControllerInterfaceWindow.cpp" />
<ClCompile Include="Config\ControllersWindow.cpp" />
<ClCompile Include="Config\FilesystemWidget.cpp" />
<ClCompile Include="Config\GameConfigEdit.cpp" />
@@ -332,6 +340,7 @@
<ClCompile Include="Config\Mapping\WiimoteEmuExtension.cpp" />
<ClCompile Include="Config\Mapping\WiimoteEmuGeneral.cpp" />
<ClCompile Include="Config\Mapping\WiimoteEmuMotionControl.cpp" />
<ClCompile Include="Config\Mapping\WiimoteEmuMotionControlIMU.cpp" />
<ClCompile Include="Config\LogConfigWidget.cpp" />
<ClCompile Include="Config\LogWidget.cpp" />
<ClCompile Include="Config\NewPatchDialog.cpp" />
@@ -25,6 +25,12 @@ add_library(inputcommon
ControllerEmu/ControlGroup/Cursor.h
ControllerEmu/ControlGroup/Force.cpp
ControllerEmu/ControlGroup/Force.h
ControllerEmu/ControlGroup/IMUAccelerometer.cpp
ControllerEmu/ControlGroup/IMUAccelerometer.h
ControllerEmu/ControlGroup/IMUCursor.cpp
ControllerEmu/ControlGroup/IMUCursor.h
ControllerEmu/ControlGroup/IMUGyroscope.cpp
ControllerEmu/ControlGroup/IMUGyroscope.h
ControllerEmu/ControlGroup/MixedTriggers.cpp
ControllerEmu/ControlGroup/MixedTriggers.h
ControllerEmu/ControlGroup/ModifySettingsButton.cpp
@@ -37,6 +43,9 @@ add_library(inputcommon
ControllerEmu/ControlGroup/Triggers.h
ControllerEmu/Setting/NumericSetting.cpp
ControllerEmu/Setting/NumericSetting.h
ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp
ControllerInterface/DualShockUDPClient/DualShockUDPClient.h
ControllerInterface/DualShockUDPClient/DualShockUDPProto.h
ControllerInterface/ControllerInterface.cpp
ControllerInterface/ControllerInterface.h
ControllerInterface/Device.cpp
@@ -40,6 +40,9 @@ enum class GroupType
Triggers,
Slider,
Shake,
IMUAccelerometer,
IMUGyroscope,
IMUCursor
};

class ControlGroup
@@ -0,0 +1,44 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h"

#include "Common/Common.h"
#include "Common/MathUtil.h"

#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"

namespace ControllerEmu
{
IMUAccelerometer::IMUAccelerometer(std::string name, std::string ui_name)
: ControlGroup(std::move(name), std::move(ui_name), GroupType::IMUAccelerometer)
{
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Left")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Right")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Forward")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Backward")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Up")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Down")));
}

std::optional<IMUAccelerometer::StateData> IMUAccelerometer::GetState() const
{
StateData state;
state.x = (controls[0]->control_ref->State() - controls[1]->control_ref->State());
state.y = (controls[3]->control_ref->State() - controls[2]->control_ref->State());
state.z = (controls[4]->control_ref->State() - controls[5]->control_ref->State());

if (controls[0]->control_ref->BoundCount() != 0)
return state;
else
return std::nullopt;
}

} // namespace ControllerEmu
@@ -0,0 +1,24 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <string>

#include "Common/Matrix.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/Device.h"

namespace ControllerEmu
{
class IMUAccelerometer : public ControlGroup
{
public:
using StateData = Common::Vec3;

IMUAccelerometer(std::string name, std::string ui_name);

std::optional<StateData> GetState() const;
};
} // namespace ControllerEmu
@@ -0,0 +1,43 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"

#include <string>

#include "Common/Common.h"
#include "Common/MathUtil.h"

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"

namespace ControllerEmu
{
IMUCursor::IMUCursor(std::string name, std::string ui_name)
: ControlGroup(std::move(name), std::move(ui_name), GroupType::IMUCursor)
{
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Recenter")));

// Default values are optimized for "Super Mario Galaxy 2".
// This seems to be acceptable for a good number of games.

AddSetting(&m_yaw_setting,
// i18n: Refers to an amount of rotational movement about the "yaw" axis.
{_trans("Total Yaw"),
// i18n: The symbol/abbreviation for degrees (unit of angular measure).
_trans("°"),
// i18n: Refers to emulated wii remote movements.
_trans("Total rotation about the yaw axis.")},
15, 0, 360);
}

ControlState IMUCursor::GetTotalYaw() const
{
return m_yaw_setting.GetValue() * MathUtil::TAU / 360;
}

} // namespace ControllerEmu
@@ -0,0 +1,26 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <chrono>
#include <string>

#include "InputCommon/ControllerEmu/StickGate.h"
#include "InputCommon/ControllerInterface/Device.h"

namespace ControllerEmu
{
class IMUCursor : public ControlGroup
{
public:
IMUCursor(std::string name, std::string ui_name);

// Yaw movement in radians.
ControlState GetTotalYaw() const;

private:
SettingValue<double> m_yaw_setting;
};
} // namespace ControllerEmu
@@ -0,0 +1,44 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"

#include "Common/Common.h"
#include "Common/MathUtil.h"

#include "Core/HW/WiimoteEmu/WiimoteEmu.h"

#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"

namespace ControllerEmu
{
IMUGyroscope::IMUGyroscope(std::string name, std::string ui_name)
: ControlGroup(std::move(name), std::move(ui_name), GroupType::IMUGyroscope)
{
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Pitch Up")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Pitch Down")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Roll Left")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Roll Right")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Yaw Left")));
controls.emplace_back(std::make_unique<Input>(Translate, _trans("Yaw Right")));
}

std::optional<IMUGyroscope::StateData> IMUGyroscope::GetState() const
{
StateData state;
state.x = (controls[1]->control_ref->State() - controls[0]->control_ref->State());
state.y = (controls[2]->control_ref->State() - controls[3]->control_ref->State());
state.z = (controls[4]->control_ref->State() - controls[5]->control_ref->State());

if (controls[0]->control_ref->BoundCount() != 0)
return state;
else
return std::nullopt;
}

} // namespace ControllerEmu
@@ -0,0 +1,24 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <string>

#include "Common/Matrix.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/Device.h"

namespace ControllerEmu
{
class IMUGyroscope : public ControlGroup
{
public:
using StateData = Common::Vec3;

IMUGyroscope(std::string name, std::string ui_name);

std::optional<StateData> GetState() const;
};
} // namespace ControllerEmu
@@ -30,6 +30,9 @@
#ifdef CIFACE_USE_PIPES
#include "InputCommon/ControllerInterface/Pipes/Pipes.h"
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
#endif

ControllerInterface g_controller_interface;

@@ -67,6 +70,9 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
#endif
#ifdef CIFACE_USE_PIPES
// nothing needed
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::Init();
#endif

RefreshDevices();
@@ -122,6 +128,9 @@ void ControllerInterface::RefreshDevices()
#ifdef CIFACE_USE_PIPES
ciface::Pipes::PopulateDevices();
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::PopulateDevices();
#endif

m_is_populating_devices = false;
InvokeDevicesChangedCallbacks();
@@ -172,6 +181,9 @@ void ControllerInterface::Shutdown()
#ifdef CIFACE_USE_EVDEV
ciface::evdev::Shutdown();
#endif
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
ciface::DualShockUDPClient::DeInit();
#endif
}

void ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device)
@@ -29,6 +29,7 @@
#if defined(USE_PIPES)
#define CIFACE_USE_PIPES
#endif
#define CIFACE_USE_DUALSHOCKUDPCLIENT

//
// ControllerInterface

Large diffs are not rendered by default.

@@ -0,0 +1,19 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Common/Config/Config.h"

namespace ciface::DualShockUDPClient
{
namespace Settings
{
extern const Config::ConfigInfo<bool> SERVER_ENABLED;
extern const Config::ConfigInfo<std::string> SERVER_ADDRESS;
extern const Config::ConfigInfo<int> SERVER_PORT;
} // namespace Settings

void Init();
void PopulateDevices();
void DeInit();
} // namespace ciface::DualShockUDPClient
@@ -0,0 +1,270 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <optional>

#include <zlib.h>

#include "Common/CommonTypes.h"

namespace ciface::DualShockUDPClient::Proto
{
// CemuHook DualShockUDP protocol implementation using UdpServer.cs from
// https://github.com/Ryochan7/DS4Windows as documentation.
//
// WARNING: Little endian host assumed

static constexpr u16 CEMUHOOK_PROTOCOL_VERSION = 1001;

static constexpr int PORT_COUNT = 4;

static constexpr char CLIENT[] = "DSUC";
static constexpr char SERVER[] = "DSUS";

#pragma pack(push, 1)

enum class DsState : u8
{
Disconnected = 0x00,
Reserved = 0x01,
Connected = 0x02
};

enum class DsConnection : u8
{
None = 0x00,
Usb = 0x01,
Bluetooth = 0x02
};

enum class DsModel : u8
{
None = 0,
DS3 = 1,
DS4 = 2,
Generic = 3
};

enum class DsBattery : u8
{
None = 0x00,
Dying = 0x01,
Low = 0x02,
Medium = 0x03,
High = 0x04,
Full = 0x05,
Charging = 0xEE,
Charged = 0xEF
};

enum RegisterFlags : u8
{
AllPads = 0x00,
PadID = 0x01,
PadMACAdddress = 0x02
};

struct MessageHeader
{
u8 source[4];
u16 protocol_version;
u16 message_length; // actually message size minus header size
u32 crc32;
u32 source_uid;
};

struct Touch
{
u8 active;
u8 id;
s16 x;
s16 y;
};

namespace MessageType
{
struct VersionRequest
{
static constexpr auto FROM = CLIENT;
static constexpr auto TYPE = 0x100000U;
MessageHeader header;
u32 message_type;
};

struct VersionResponse
{
static constexpr auto FROM = SERVER;
static constexpr auto TYPE = 0x100000U;
MessageHeader header;
u32 message_type;
u16 max_protocol_version;
u8 padding[2];
};

struct ListPorts
{
static constexpr auto FROM = CLIENT;
static constexpr auto TYPE = 0x100001U;
MessageHeader header;
u32 message_type;
u32 pad_request_count;
u8 pad_id[4];
};

struct PortInfo
{
static constexpr auto FROM = SERVER;
static constexpr auto TYPE = 0x100001U;
MessageHeader header;
u32 message_type;
u8 pad_id;
DsState pad_state;
DsModel model;
DsConnection connection_type;
u8 pad_mac_address[6];
DsBattery battery_status;
u8 padding;
};

struct PadDataRequest
{
static constexpr auto FROM = CLIENT;
static constexpr auto TYPE = 0x100002U;
MessageHeader header;
u32 message_type;
RegisterFlags register_flags;
u8 pad_id_to_register;
u8 mac_address_to_register[6];
};

struct PadDataResponse
{
static constexpr auto FROM = SERVER;
static constexpr auto TYPE = 0x100002U;
MessageHeader header;
u32 message_type;
u8 pad_id;
DsState pad_state;
DsModel model;
DsConnection connection_type;
u8 pad_mac_address[6];
DsBattery battery_status;
u8 active;
u32 hid_packet_counter;
u8 button_states1;
u8 button_states2;
u8 button_ps;
u8 button_touch;
u8 left_stick_x;
u8 left_stick_y_inverted;
u8 right_stick_x;
u8 right_stick_y_inverted;
u8 button_dpad_left_analog;
u8 button_dpad_down_analog;
u8 button_dpad_right_analog;
u8 button_dpad_up_analog;
u8 button_square_analog;
u8 button_cross_analog;
u8 button_circle_analog;
u8 button_triangle_analog;
u8 button_r1_analog;
u8 button_l1_analog;
u8 trigger_r2;
u8 trigger_l2;
Touch touch1;
Touch touch2;
u64 timestamp_us;
float accelerometer_x_g;
float accelerometer_y_g;
float accelerometer_z_inverted_g;
float gyro_pitch_deg_s;
float gyro_yaw_deg_s;
float gyro_roll_deg_s;
};

struct FromServer
{
union
{
struct
{
MessageHeader header;
u32 message_type;
};
MessageType::VersionResponse version_response;
MessageType::PortInfo port_info;
MessageType::PadDataResponse pad_data_response;
};
};

struct FromClient
{
union
{
struct
{
MessageHeader header;
u32 message_type;
};
MessageType::VersionRequest version_request;
MessageType::ListPorts list_ports;
MessageType::PadDataRequest pad_data_request;
};
};
} // namespace MessageType

static inline u32 CRC32(const void* buffer, unsigned length)
{
return crc32(crc32(0L, Z_NULL, 0), static_cast<const Bytef*>(buffer), length);
}

template <typename MsgType>
struct Message
{
Message() : m_message{} {}

explicit Message(u32 source_uid) : m_message{}
{
memcpy((char*)m_message.header.source, MsgType::FROM, sizeof(m_message.header.source));
m_message.header.protocol_version = CEMUHOOK_PROTOCOL_VERSION;
m_message.header.message_length = sizeof(*this) - sizeof(m_message.header);
m_message.header.source_uid = source_uid;
m_message.message_type = MsgType::TYPE;
}

void Finish() { m_message.header.crc32 = CRC32(&m_message, sizeof(m_message)); }

template <class ToMsgType>
std::optional<ToMsgType> CheckAndCastTo()
{
u32 crc32_in_header = m_message.header.crc32;
// zero out the crc32 in the packet once we got it since that's whats needed for calculation
m_message.header.crc32 = 0;
u32 crc32_calculated = CRC32(&m_message, sizeof(ToMsgType));
if (crc32_in_header != crc32_calculated)
{
NOTICE_LOG(SERIALINTERFACE,
"DualShockUDPClient Received message with bad CRC in header: got %u, expected %u",
crc32_in_header, crc32_calculated);
return std::nullopt;
}
if (m_message.header.protocol_version > CEMUHOOK_PROTOCOL_VERSION)
return std::nullopt;
if (memcmp(m_message.header.source, ToMsgType::FROM, sizeof(m_message.header.source)))
return std::nullopt;
if (m_message.message_type != ToMsgType::TYPE)
return std::nullopt;
if (m_message.header.message_length + sizeof(m_message.header) > sizeof(ToMsgType))
return std::nullopt;

ToMsgType tomsg;
memcpy(&tomsg, &m_message, sizeof(tomsg));
return tomsg;
}

MsgType m_message;
};

#pragma pack(pop)
} // namespace ciface::DualShockUDPClient::Proto
@@ -36,6 +36,9 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<ClCompile Include="ControllerEmu\ControlGroup\IMUAccelerometer.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\IMUCursor.cpp" />
<ClCompile Include="ControllerEmu\ControlGroup\IMUGyroscope.cpp" />
<ClCompile Include="ControllerEmu\ControllerEmu.cpp" />
<ClCompile Include="ControllerEmu\StickGate.cpp" />
<ClCompile Include="ControllerEmu\Control\Control.cpp" />
@@ -59,6 +62,7 @@
<ClCompile Include="ControllerInterface\DInput\DInputJoystick.cpp" />
<ClCompile Include="ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
<ClCompile Include="ControllerInterface\DInput\XInputFilter.cpp" />
<ClCompile Include="ControllerInterface\DualShockUDPClient\DualShockUDPClient.cpp" />
<ClCompile Include="ControlReference\ControlReference.cpp" />
<ClCompile Include="ControlReference\ExpressionParser.cpp" />
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
@@ -76,6 +80,9 @@
<ClCompile Include="InputProfile.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ControllerEmu\ControlGroup\IMUAccelerometer.h" />
<ClInclude Include="ControllerEmu\ControlGroup\IMUCursor.h" />
<ClInclude Include="ControllerEmu\ControlGroup\IMUGyroscope.h" />
<ClInclude Include="ControllerEmu\ControllerEmu.h" />
<ClInclude Include="ControllerEmu\StickGate.h" />
<ClInclude Include="ControllerEmu\Control\Control.h" />
@@ -100,6 +107,8 @@
<ClInclude Include="ControllerInterface\DInput\DInputJoystick.h" />
<ClInclude Include="ControllerInterface\DInput\DInputKeyboardMouse.h" />
<ClInclude Include="ControllerInterface\DInput\XInputFilter.h" />
<ClInclude Include="ControllerInterface\DualShockUDPClient\DualShockUDPClient.h" />
<ClInclude Include="ControllerInterface\DualShockUDPClient\DualShockUDPProto.h" />
<ClInclude Include="ControlReference\ControlReference.h" />
<ClInclude Include="ControlReference\FunctionExpression.h" />
<ClInclude Include="ControlReference\ExpressionParser.h" />
@@ -122,4 +131,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
@@ -28,6 +28,9 @@
<Filter Include="ControllerInterface\XInput">
<UniqueIdentifier>{07bad1aa-7e03-4f5c-ade2-a44857c5cbc3}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\DualShockUDPClient">
<UniqueIdentifier>{ff02580e-3a62-4de4-a135-3a6c2c339a90}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="GCAdapter.cpp" />
@@ -120,6 +123,18 @@
<ClCompile Include="ControllerEmu\ControlGroup\Attachments.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\IMUCursor.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\IMUAccelerometer.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerEmu\ControlGroup\IMUGyroscope.cpp">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClCompile>
<ClCompile Include="ControllerInterface\DualShockUDPClient\DualShockUDPClient.cpp">
<Filter>ControllerInterface\DualShockUDPClient</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GCAdapter.h" />
@@ -209,15 +224,28 @@
<ClInclude Include="ControlReference\ControlReference.h">
<Filter>ControllerInterface</Filter>
</ClInclude>
<ClCompile Include="ControlReference\FunctionExpression.h">
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClInclude Include="InputProfile.h" />
<ClInclude Include="ControllerEmu\ControlGroup\Attachments.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\IMUCursor.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\IMUAccelerometer.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControllerEmu\ControlGroup\IMUGyroscope.h">
<Filter>ControllerEmu\ControlGroup</Filter>
</ClInclude>
<ClInclude Include="ControlReference\FunctionExpression.h" />
<ClInclude Include="ControllerInterface\DualShockUDPClient\DualShockUDPClient.h">
<Filter>ControllerInterface\DualShockUDPClient</Filter>
</ClInclude>
<ClInclude Include="ControllerInterface\DualShockUDPClient\DualShockUDPProto.h">
<Filter>ControllerInterface\DualShockUDPClient</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>