Merge pull request #8352 from rlnilsen/motion-controller-support-via-cemuhook-protocol

Support for motion controllers like the DualShock 4
This commit is contained in:
Pierre Bourdon
2019-10-28 16:39:10 +01:00
committed by GitHub
39 changed files with 1548 additions and 45 deletions
+1
View File
@@ -85,6 +85,7 @@
#define GFX_CONFIG "GFX.ini"
#define DEBUGGER_CONFIG "Debugger.ini"
#define LOGGER_CONFIG "Logger.ini"
#define DUALSHOCKUDPCLIENT_CONFIG "DSUClient.ini"
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "dolphin.log"
+9 -3
View File
@@ -133,9 +133,15 @@ void ClearCurrentRunLayer()
}
static const std::map<System, std::string> system_to_name = {
{System::Main, "Dolphin"}, {System::GCPad, "GCPad"}, {System::WiiPad, "Wiimote"},
{System::GCKeyboard, "GCKeyboard"}, {System::GFX, "Graphics"}, {System::Logger, "Logger"},
{System::Debugger, "Debugger"}, {System::SYSCONF, "SYSCONF"}};
{System::Main, "Dolphin"},
{System::GCPad, "GCPad"},
{System::WiiPad, "Wiimote"},
{System::GCKeyboard, "GCKeyboard"},
{System::GFX, "Graphics"},
{System::Logger, "Logger"},
{System::Debugger, "Debugger"},
{System::SYSCONF, "SYSCONF"},
{System::DualShockUDPClient, "DualShockUDPClient"}};
const std::string& GetSystemName(System system)
{
+1
View File
@@ -30,6 +30,7 @@ enum class System
GFX,
Logger,
Debugger,
DualShockUDPClient,
};
constexpr std::array<LayerType, 7> SEARCH_ORDER{{
+2
View File
@@ -794,6 +794,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[F_GFXCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + GFX_CONFIG;
s_user_paths[F_DEBUGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
s_user_paths[F_LOGGERCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + LOGGER_CONFIG;
s_user_paths[F_DUALSHOCKUDPCLIENTCONFIG_IDX] =
s_user_paths[D_CONFIG_IDX] + DUALSHOCKUDPCLIENT_CONFIG;
s_user_paths[F_MAINLOG_IDX] = s_user_paths[D_LOGS_IDX] + MAIN_LOG;
s_user_paths[F_MEM1DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM1_DUMP;
s_user_paths[F_MEM2DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM2_DUMP;
+1
View File
@@ -70,6 +70,7 @@ enum
F_MEMORYWATCHERLOCATIONS_IDX,
F_MEMORYWATCHERSOCKET_IDX,
F_WIISDCARD_IDX,
F_DUALSHOCKUDPCLIENTCONFIG_IDX,
NUM_PATH_INDICES
};
@@ -89,6 +89,7 @@ const std::map<Config::System, int> system_to_ini = {
{Config::System::GFX, F_GFXCONFIG_IDX},
{Config::System::Logger, F_LOGGERCONFIG_IDX},
{Config::System::Debugger, F_DEBUGGERCONFIG_IDX},
{Config::System::DualShockUDPClient, F_DUALSHOCKUDPCLIENTCONFIG_IDX},
};
// INI layer configuration loader
@@ -16,6 +16,9 @@ namespace ConfigLoaders
{
bool IsSettingSaveable(const Config::ConfigLocation& config_location)
{
if (config_location.system == Config::System::DualShockUDPClient)
return true;
if (config_location.system == Config::System::Logger)
return true;
+1 -1
View File
@@ -629,4 +629,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+4 -1
View File
@@ -1661,8 +1661,11 @@
<ClInclude Include="HW\AddressSpace.h">
<Filter>HW %28Flipper/Hollywood%29</Filter>
</ClInclude>
<ClInclude Include="HW\WiimoteEmu\Constants.h">
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
</ItemGroup>
</Project>
</Project>
@@ -13,6 +13,9 @@
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/Force.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
namespace
@@ -268,6 +271,90 @@ void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& angle_ta
}
}
static Common::Vec3 NormalizeAngle(Common::Vec3 angle)
{
// TODO: There must be a more elegant way to do this
angle.x = fmod(angle.x, float(MathUtil::TAU));
angle.y = fmod(angle.y, float(MathUtil::TAU));
angle.z = fmod(angle.z, float(MathUtil::TAU));
angle.x += angle.x < 0 ? float(MathUtil::TAU) : 0;
angle.y += angle.y < 0 ? float(MathUtil::TAU) : 0;
angle.z += angle.z < 0 ? float(MathUtil::TAU) : 0;
return angle;
}
static Common::Vec3 ComplementaryFilter(const Common::Vec3& angle,
const Common::Vec3& accelerometer,
const Common::Vec3& gyroscope, float time_elapsed)
{
Common::Vec3 gyroangle = angle + gyroscope * time_elapsed;
gyroangle = NormalizeAngle(gyroangle);
// Calculate accelerometer tilt angles
Common::Vec3 accangle = gyroangle;
if ((accelerometer.x != 0 && accelerometer.y != 0) || accelerometer.z != 0)
{
float accpitch = -atan2(accelerometer.y, -accelerometer.z) + float(MathUtil::PI);
float accroll = atan2(accelerometer.x, -accelerometer.z) + float(MathUtil::PI);
accangle = {accpitch, accroll, gyroangle.z};
}
// Massage accelerometer and gyroscope angle values so that averaging them works when they are on
// opposite sides of TAU / zero (which both represent the same angle)
// TODO: There must be a more elegant way to do this
constexpr float DEG360 = float(MathUtil::TAU);
constexpr float DEG270 = DEG360 * 0.75f;
constexpr float DEG90 = DEG360 * 0.25f;
if (accangle.x < DEG90 && gyroangle.x > DEG270)
accangle.x += DEG360;
else if (gyroangle.x < DEG90 && accangle.x > DEG270)
gyroangle.x += DEG360;
if (accangle.y < DEG90 && gyroangle.y > DEG270)
accangle.y += DEG360;
else if (gyroangle.y < DEG90 && accangle.y > DEG270)
gyroangle.y += DEG360;
// Combine accelerometer and gyroscope angles
return NormalizeAngle((gyroangle * 0.98f) + (accangle * 0.02f));
}
void EmulateIMUCursor(std::optional<RotationalState>* state, ControllerEmu::IMUCursor* imu_ir_group,
ControllerEmu::IMUAccelerometer* imu_accelerometer_group,
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed)
{
// Avoid having to double dereference
auto& st = *state;
auto accel = imu_accelerometer_group->GetState();
auto ang_vel = imu_gyroscope_group->GetState();
// The IMU Cursor requires both an accelerometer and a gyroscope to function correctly.
if (!(accel.has_value() && ang_vel.has_value()))
{
st = std::nullopt;
return;
}
if (!st.has_value())
st = RotationalState{};
st->angle = ComplementaryFilter(st->angle, accel.value(), ang_vel.value(), time_elapsed);
// Reset camera yaw angle
constexpr ControlState BUTTON_THRESHOLD = 0.5;
if (imu_ir_group->controls[0]->control_ref->State() > BUTTON_THRESHOLD)
st->angle.z = 0;
// Limit camera yaw angle
float totalyaw = float(imu_ir_group->GetTotalYaw());
float yawmax = totalyaw / 2;
float yawmin = float(MathUtil::TAU) - totalyaw / 2;
if (st->angle.z > yawmax && st->angle.z <= float(MathUtil::PI))
st->angle.z = yawmax;
if (st->angle.z < yawmin && st->angle.z > float(MathUtil::PI))
st->angle.z = yawmin;
}
void ApproachPositionWithJerk(PositionalState* state, const Common::Vec3& position_target,
const Common::Vec3& max_jerk, float time_elapsed)
{
@@ -11,6 +11,9 @@
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/Force.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
namespace WiimoteEmu
@@ -53,6 +56,9 @@ void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_group, flo
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed);
void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed);
void EmulateCursor(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed);
void EmulateIMUCursor(std::optional<RotationalState>* state, ControllerEmu::IMUCursor* imu_ir_group,
ControllerEmu::IMUAccelerometer* imu_accelerometer_group,
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed);
// Convert m/s/s acceleration data to the format used by Wiimote/Nunchuk (10-bit unsigned integers).
WiimoteCommon::DataReportBuilder::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
+68 -12
View File
@@ -41,6 +41,9 @@
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/Force.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/ModifySettingsButton.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
@@ -150,6 +153,7 @@ void Wiimote::Reset()
m_tilt_state = {};
m_cursor_state = {};
m_shake_state = {};
m_imu_cursor_state = {};
}
Wiimote::Wiimote(const unsigned int index) : m_index(index)
@@ -169,6 +173,11 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index)
groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing")));
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake")));
groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer(
"IMUAccelerometer", _trans("Accelerometer")));
groups.emplace_back(m_imu_gyroscope =
new ControllerEmu::IMUGyroscope("IMUGyroscope", _trans("Gyroscope")));
groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point")));
// Extension
groups.emplace_back(m_attachments = new ControllerEmu::Attachments(_trans("Extension")));
@@ -263,6 +272,12 @@ ControllerEmu::ControlGroup* Wiimote::GetWiimoteGroup(WiimoteGroup group)
return m_options;
case WiimoteGroup::Hotkeys:
return m_hotkeys;
case WiimoteGroup::IMUAccelerometer:
return m_imu_accelerometer;
case WiimoteGroup::IMUGyroscope:
return m_imu_gyroscope;
case WiimoteGroup::IMUPoint:
return m_imu_ir;
default:
assert(false);
return nullptr;
@@ -447,7 +462,7 @@ void Wiimote::SendDataReport()
{
// Calibration values are 8-bit but we want 10-bit precision, so << 2.
DataReportBuilder::AccelData accel =
ConvertAccelData(GetAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
rpt_builder.SetAccelData(accel);
}
@@ -456,7 +471,7 @@ void Wiimote::SendDataReport()
{
// Note: Camera logic currently contains no changing state so we can just update it here.
// If that changes this should be moved to Wiimote::Update();
m_camera_logic.Update(GetTransformation());
m_camera_logic.Update(GetTotalTransformation());
// The real wiimote reads camera data from the i2c bus starting at offset 0x37:
const u8 camera_data_offset =
@@ -477,7 +492,7 @@ void Wiimote::SendDataReport()
if (m_is_motion_plus_attached)
{
// TODO: Make input preparation triggered by bus read.
m_motion_plus.PrepareInput(GetAngularVelocity());
m_motion_plus.PrepareInput(GetTotalAngularVelocity());
}
u8* ext_data = rpt_builder.GetExtDataPtr();
@@ -670,6 +685,20 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
m_dpad->SetControlExpression(3, "Right"); // Right
#endif
// Motion Source
m_imu_accelerometer->SetControlExpression(0, "Accel Left");
m_imu_accelerometer->SetControlExpression(1, "Accel Right");
m_imu_accelerometer->SetControlExpression(2, "Accel Forward");
m_imu_accelerometer->SetControlExpression(3, "Accel Backward");
m_imu_accelerometer->SetControlExpression(4, "Accel Up");
m_imu_accelerometer->SetControlExpression(5, "Accel Down");
m_imu_gyroscope->SetControlExpression(0, "Gyro Pitch Up");
m_imu_gyroscope->SetControlExpression(1, "Gyro Pitch Down");
m_imu_gyroscope->SetControlExpression(2, "Gyro Roll Left");
m_imu_gyroscope->SetControlExpression(3, "Gyro Roll Right");
m_imu_gyroscope->SetControlExpression(4, "Gyro Yaw Left");
m_imu_gyroscope->SetControlExpression(5, "Gyro Yaw Right");
// Enable Nunchuk:
constexpr ExtensionNumber DEFAULT_EXT = ExtensionNumber::NUNCHUK;
m_attachments->SetSelectedAttachment(DEFAULT_EXT);
@@ -720,14 +749,14 @@ void Wiimote::StepDynamics()
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateCursor(&m_cursor_state, m_ir, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateIMUCursor(&m_imu_cursor_state, m_imu_ir, m_imu_accelerometer, m_imu_gyroscope,
1.f / ::Wiimote::UPDATE_FREQ);
}
Common::Vec3 Wiimote::GetAcceleration()
Common::Vec3 Wiimote::GetAcceleration(Common::Vec3 extra_acceleration)
{
Common::Vec3 accel =
GetOrientation() *
GetTransformation().Transform(
m_swing_state.acceleration + Common::Vec3(0, 0, float(GRAVITY_ACCELERATION)), 0);
Common::Vec3 accel = GetOrientation() * GetTransformation().Transform(
m_swing_state.acceleration + extra_acceleration, 0);
// Our shake effects have never been affected by orientation. Should they be?
accel += m_shake_state.acceleration;
@@ -735,13 +764,13 @@ Common::Vec3 Wiimote::GetAcceleration()
return accel;
}
Common::Vec3 Wiimote::GetAngularVelocity()
Common::Vec3 Wiimote::GetAngularVelocity(Common::Vec3 extra_angular_velocity)
{
return GetOrientation() * (m_tilt_state.angular_velocity + m_swing_state.angular_velocity +
m_cursor_state.angular_velocity);
m_cursor_state.angular_velocity + extra_angular_velocity);
}
Common::Matrix44 Wiimote::GetTransformation() const
Common::Matrix44 Wiimote::GetTransformation(Common::Vec3 extra_rotation) const
{
// Includes positional and rotational effects of:
// Cursor, Swing, Tilt, Shake
@@ -749,7 +778,7 @@ Common::Matrix44 Wiimote::GetTransformation() const
// TODO: Think about and clean up matrix order + make nunchuk match.
return Common::Matrix44::Translate(-m_shake_state.position) *
Common::Matrix44::FromMatrix33(GetRotationalMatrix(
-m_tilt_state.angle - m_swing_state.angle - m_cursor_state.angle)) *
-m_tilt_state.angle - m_swing_state.angle - m_cursor_state.angle - extra_rotation)) *
Common::Matrix44::Translate(-m_swing_state.position - m_cursor_state.position);
}
@@ -759,4 +788,31 @@ Common::Matrix33 Wiimote::GetOrientation() const
Common::Matrix33::RotateX(float(MathUtil::TAU / 4 * IsUpright()));
}
Common::Vec3 Wiimote::GetTotalAcceleration()
{
auto accel = m_imu_accelerometer->GetState();
if (accel.has_value())
return GetAcceleration(accel.value());
else
return GetAcceleration();
}
Common::Vec3 Wiimote::GetTotalAngularVelocity()
{
auto ang_vel = m_imu_gyroscope->GetState();
if (ang_vel.has_value())
return GetAngularVelocity(ang_vel.value());
else
return GetAngularVelocity();
}
Common::Matrix44 Wiimote::GetTotalTransformation() const
{
auto state = m_imu_cursor_state;
if (state.has_value())
return GetTransformation(state->angle);
else
return GetTransformation();
}
} // namespace WiimoteEmu
+21 -5
View File
@@ -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 @@ private:
// 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 @@ private:
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 @@ private:
RotationalState m_tilt_state;
MotionState m_cursor_state;
PositionalState m_shake_state;
std::optional<RotationalState> m_imu_cursor_state;
};
} // namespace WiimoteEmu
+6
View File
@@ -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 @@ private:
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 @@ private:
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;
};

Some files were not shown because too many files have changed in this diff Show More