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:
commit
1f3d1a9b7f
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@ enum class System
|
|||
GFX,
|
||||
Logger,
|
||||
Debugger,
|
||||
DualShockUDPClient,
|
||||
};
|
||||
|
||||
constexpr std::array<LayerType, 7> SEARCH_ORDER{{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1661,6 +1661,9 @@
|
|||
<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" />
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
// Copyright 2019 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
#include <SFML/Network/SocketSelector.hpp>
|
||||
#include <SFML/Network/UdpSocket.hpp>
|
||||
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/Matrix.h"
|
||||
#include "Common/Random.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
||||
#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h"
|
||||
|
||||
namespace ciface::DualShockUDPClient
|
||||
{
|
||||
class Device : public Core::Device
|
||||
{
|
||||
private:
|
||||
template <class T>
|
||||
class Button : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
Button(std::string name, const T& buttons, unsigned mask)
|
||||
: m_name(std::move(name)), m_buttons(buttons), m_mask(mask)
|
||||
{
|
||||
}
|
||||
std::string GetName() const override { return m_name; }
|
||||
ControlState GetState() const override { return (m_buttons & m_mask) != 0; }
|
||||
|
||||
private:
|
||||
const std::string m_name;
|
||||
const T& m_buttons;
|
||||
unsigned m_mask;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class AnalogInput : public Core::Device::Input
|
||||
{
|
||||
public:
|
||||
AnalogInput(std::string name, const T& input, ControlState range, ControlState offset = 0)
|
||||
: m_name(std::move(name)), m_input(input), m_range(range), m_offset(offset)
|
||||
{
|
||||
}
|
||||
std::string GetName() const override { return m_name; }
|
||||
ControlState GetState() const override { return (ControlState(m_input) + m_offset) / m_range; }
|
||||
|
||||
private:
|
||||
const std::string m_name;
|
||||
const T& m_input;
|
||||
const ControlState m_range;
|
||||
const ControlState m_offset;
|
||||
};
|
||||
|
||||
class TouchInput : public AnalogInput<int>
|
||||
{
|
||||
public:
|
||||
TouchInput(std::string name, const int& input, ControlState range)
|
||||
: AnalogInput(std::move(name), input, range)
|
||||
{
|
||||
}
|
||||
bool IsDetectable() override { return false; }
|
||||
};
|
||||
|
||||
class AccelerometerInput : public AnalogInput<double>
|
||||
{
|
||||
public:
|
||||
AccelerometerInput(std::string name, const double& input, ControlState range)
|
||||
: AnalogInput(std::move(name), input, range)
|
||||
{
|
||||
}
|
||||
bool IsDetectable() override { return false; }
|
||||
};
|
||||
|
||||
using GyroInput = AccelerometerInput;
|
||||
|
||||
public:
|
||||
void UpdateInput() override;
|
||||
|
||||
Device(Proto::DsModel model, int index);
|
||||
|
||||
std::string GetName() const final override;
|
||||
std::string GetSource() const final override;
|
||||
std::optional<int> GetPreferredId() const final override;
|
||||
|
||||
private:
|
||||
const Proto::DsModel m_model;
|
||||
const int m_index;
|
||||
u32 m_client_uid;
|
||||
sf::UdpSocket m_socket;
|
||||
Common::DVec3 m_accel;
|
||||
Common::DVec3 m_gyro;
|
||||
std::chrono::steady_clock::time_point m_next_reregister;
|
||||
Proto::MessageType::PadDataResponse m_pad_data;
|
||||
Proto::Touch m_prev_touch;
|
||||
bool m_prev_touch_valid;
|
||||
int m_touch_x;
|
||||
int m_touch_y;
|
||||
};
|
||||
|
||||
static constexpr double GRAVITY_ACCELERATION = 9.80665;
|
||||
static constexpr char DEFAULT_SERVER_ADDRESS[] = "127.0.0.1";
|
||||
static constexpr u16 DEFAULT_SERVER_PORT = 26760;
|
||||
static constexpr auto SERVER_REREGISTER_INTERVAL = std::chrono::seconds{1};
|
||||
static constexpr auto SERVER_LISTPORTS_INTERVAL = std::chrono::seconds{1};
|
||||
static constexpr int TOUCH_X_AXIS_MAX = 1000;
|
||||
static constexpr int TOUCH_Y_AXIS_MAX = 500;
|
||||
|
||||
namespace Settings
|
||||
{
|
||||
const Config::ConfigInfo<bool> SERVER_ENABLED{
|
||||
{Config::System::DualShockUDPClient, "Server", "Enabled"}, false};
|
||||
const Config::ConfigInfo<std::string> SERVER_ADDRESS{
|
||||
{Config::System::DualShockUDPClient, "Server", "IPAddress"}, DEFAULT_SERVER_ADDRESS};
|
||||
const Config::ConfigInfo<int> SERVER_PORT{{Config::System::DualShockUDPClient, "Server", "Port"},
|
||||
DEFAULT_SERVER_PORT};
|
||||
} // namespace Settings
|
||||
|
||||
static bool s_server_enabled;
|
||||
static std::string s_server_address;
|
||||
static u16 s_server_port;
|
||||
static u32 s_client_uid;
|
||||
static std::chrono::steady_clock::time_point s_next_listports;
|
||||
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)
|
||||
{
|
||||
// compare everything but battery_status
|
||||
return a.pad_id == b.pad_id && a.pad_state == b.pad_state && a.model == b.model &&
|
||||
a.connection_type == b.connection_type &&
|
||||
memcmp(a.pad_mac_address, b.pad_mac_address, sizeof a.pad_mac_address) == 0;
|
||||
}
|
||||
|
||||
static sf::Socket::Status ReceiveWithTimeout(sf::UdpSocket& socket, void* data, std::size_t size,
|
||||
std::size_t& received, sf::IpAddress& remoteAddress,
|
||||
unsigned short& remotePort, sf::Time timeout)
|
||||
{
|
||||
sf::SocketSelector selector;
|
||||
selector.add(socket);
|
||||
if (selector.wait(timeout))
|
||||
return socket.receive(data, size, received, remoteAddress, remotePort);
|
||||
else
|
||||
return sf::Socket::NotReady;
|
||||
}
|
||||
|
||||
static void HotplugThreadFunc()
|
||||
{
|
||||
Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread");
|
||||
NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient hotplug thread started");
|
||||
|
||||
while (s_hotplug_thread_running.IsSet())
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now >= s_next_listports)
|
||||
{
|
||||
s_next_listports = now + SERVER_LISTPORTS_INTERVAL;
|
||||
|
||||
// Request info on the four controller ports
|
||||
Proto::Message<Proto::MessageType::ListPorts> msg(s_client_uid);
|
||||
auto& list_ports = msg.m_message;
|
||||
list_ports.pad_request_count = 4;
|
||||
list_ports.pad_id[0] = 0;
|
||||
list_ports.pad_id[1] = 1;
|
||||
list_ports.pad_id[2] = 2;
|
||||
list_ports.pad_id[3] = 3;
|
||||
msg.Finish();
|
||||
if (s_socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) !=
|
||||
sf::Socket::Status::Done)
|
||||
ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient HotplugThreadFunc send failed");
|
||||
}
|
||||
|
||||
// Receive controller port info
|
||||
Proto::Message<Proto::MessageType::FromServer> msg;
|
||||
const auto timeout = s_next_listports - std::chrono::steady_clock::now();
|
||||
// ReceiveWithTimeout treats a timeout of zero as infinite timeout, which we don't want
|
||||
auto timeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count();
|
||||
timeout_ms = std::max<decltype(timeout_ms)>(timeout_ms, 1);
|
||||
std::size_t received_bytes;
|
||||
sf::IpAddress sender;
|
||||
u16 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<Proto::MessageType::PortInfo>())
|
||||
{
|
||||
const bool port_changed = !IsSameController(*port_info, s_port_info[port_info->pad_id]);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_port_info_mutex);
|
||||
s_port_info[port_info->pad_id] = *port_info;
|
||||
}
|
||||
if (port_changed)
|
||||
PopulateDevices();
|
||||
}
|
||||
}
|
||||
}
|
||||
NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient hotplug thread stopped");
|
||||
}
|
||||
|
||||
static void StartHotplugThread()
|
||||
{
|
||||
// Mark the thread as running.
|
||||
if (!s_hotplug_thread_running.TestAndSet())
|
||||
{
|
||||
// It was already running.
|
||||
return;
|
||||
}
|
||||
|
||||
s_hotplug_thread = std::thread(HotplugThreadFunc);
|
||||
}
|
||||
|
||||
static void StopHotplugThread()
|
||||
{
|
||||
// Tell the hotplug thread to stop.
|
||||
if (!s_hotplug_thread_running.TestAndClear())
|
||||
{
|
||||
// It wasn't running, we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
s_socket.unbind(); // interrupt blocking socket
|
||||
s_hotplug_thread.join();
|
||||
}
|
||||
|
||||
static void Restart()
|
||||
{
|
||||
NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient Restart");
|
||||
|
||||
StopHotplugThread();
|
||||
|
||||
s_client_uid = Common::Random::GenerateValue<u32>();
|
||||
s_next_listports = std::chrono::steady_clock::time_point::min();
|
||||
for (int port_index = 0; port_index < Proto::PORT_COUNT; port_index++)
|
||||
{
|
||||
s_port_info[port_index] = {};
|
||||
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, "DualShockUDPClient PopulateDevices");
|
||||
|
||||
g_controller_interface.RemoveDevice(
|
||||
[](const auto* dev) { return dev->GetSource() == "DSUClient"; });
|
||||
|
||||
std::lock_guard<std::mutex> lock(s_port_info_mutex);
|
||||
for (int port_index = 0; port_index < Proto::PORT_COUNT; port_index++)
|
||||
{
|
||||
Proto::MessageType::PortInfo port_info = s_port_info[port_index];
|
||||
if (port_info.pad_state == Proto::DsState::Connected)
|
||||
g_controller_interface.AddDevice(std::make_shared<Device>(port_info.model, port_index));
|
||||
}
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
StopHotplugThread();
|
||||
}
|
||||
|
||||
Device::Device(Proto::DsModel model, int index)
|
||||
: m_model(model), m_index(index),
|
||||
m_client_uid(Common::Random::GenerateValue<u32>()), m_accel{}, m_gyro{},
|
||||
m_next_reregister(std::chrono::steady_clock::time_point::min()), m_pad_data{}, m_prev_touch{},
|
||||
m_prev_touch_valid(false), m_touch_x(0), m_touch_y(0)
|
||||
{
|
||||
m_socket.setBlocking(false);
|
||||
|
||||
AddInput(new AnalogInput<u8>("Pad W", m_pad_data.button_dpad_left_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Pad S", m_pad_data.button_dpad_down_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Pad E", m_pad_data.button_dpad_right_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Pad N", m_pad_data.button_dpad_up_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Square", m_pad_data.button_square_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Cross", m_pad_data.button_cross_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Circle", m_pad_data.button_circle_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("Triangle", m_pad_data.button_triangle_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("L1", m_pad_data.button_l1_analog, 255));
|
||||
AddInput(new AnalogInput<u8>("R1", m_pad_data.button_r1_analog, 255));
|
||||
|
||||
AddInput(new AnalogInput<u8>("L2", m_pad_data.trigger_l2, 255));
|
||||
AddInput(new AnalogInput<u8>("R2", m_pad_data.trigger_r2, 255));
|
||||
|
||||
AddInput(new Button<u8>("L3", m_pad_data.button_states1, 0x2));
|
||||
AddInput(new Button<u8>("R3", m_pad_data.button_states1, 0x4));
|
||||
AddInput(new Button<u8>("Share", m_pad_data.button_states1, 0x1));
|
||||
AddInput(new Button<u8>("Options", m_pad_data.button_states1, 0x8));
|
||||
AddInput(new Button<u8>("PS", m_pad_data.button_ps, 0x1));
|
||||
AddInput(new Button<u8>("Touch Button", m_pad_data.button_touch, 0x1));
|
||||
|
||||
AddInput(new AnalogInput<u8>("Left X-", m_pad_data.left_stick_x, -128, -128));
|
||||
AddInput(new AnalogInput<u8>("Left X+", m_pad_data.left_stick_x, 127, -128));
|
||||
AddInput(new AnalogInput<u8>("Left Y-", m_pad_data.left_stick_y_inverted, -128, -128));
|
||||
AddInput(new AnalogInput<u8>("Left Y+", m_pad_data.left_stick_y_inverted, 127, -128));
|
||||
AddInput(new AnalogInput<u8>("Right X-", m_pad_data.right_stick_x, -128, -128));
|
||||
AddInput(new AnalogInput<u8>("Right X+", m_pad_data.right_stick_x, 127, -128));
|
||||
AddInput(new AnalogInput<u8>("Right Y-", m_pad_data.right_stick_y_inverted, -128, -128));
|
||||
AddInput(new AnalogInput<u8>("Right Y+", m_pad_data.right_stick_y_inverted, 127, -128));
|
||||
|
||||
AddInput(new TouchInput("Touch X-", m_touch_x, -TOUCH_X_AXIS_MAX));
|
||||
AddInput(new TouchInput("Touch X+", m_touch_x, TOUCH_X_AXIS_MAX));
|
||||
AddInput(new TouchInput("Touch Y-", m_touch_y, -TOUCH_Y_AXIS_MAX));
|
||||
AddInput(new TouchInput("Touch Y+", m_touch_y, TOUCH_Y_AXIS_MAX));
|
||||
|
||||
AddInput(new AccelerometerInput("Accel Left", m_accel.x, 1));
|
||||
AddInput(new AccelerometerInput("Accel Right", m_accel.x, -1));
|
||||
AddInput(new AccelerometerInput("Accel Backward", m_accel.y, 1));
|
||||
AddInput(new AccelerometerInput("Accel Forward", m_accel.y, -1));
|
||||
AddInput(new AccelerometerInput("Accel Up", m_accel.z, 1));
|
||||
AddInput(new AccelerometerInput("Accel Down", m_accel.z, -1));
|
||||
|
||||
AddInput(new GyroInput("Gyro Pitch Up", m_gyro.x, -1));
|
||||
AddInput(new GyroInput("Gyro Pitch Down", m_gyro.x, 1));
|
||||
AddInput(new GyroInput("Gyro Roll Right", m_gyro.y, -1));
|
||||
AddInput(new GyroInput("Gyro Roll Left", m_gyro.y, 1));
|
||||
AddInput(new GyroInput("Gyro Yaw Right", m_gyro.z, -1));
|
||||
AddInput(new GyroInput("Gyro Yaw Left", m_gyro.z, 1));
|
||||
}
|
||||
|
||||
std::string Device::GetName() const
|
||||
{
|
||||
switch (m_model)
|
||||
{
|
||||
case Proto::DsModel::None:
|
||||
return "None";
|
||||
case Proto::DsModel::DS3:
|
||||
return "DualShock 3";
|
||||
case Proto::DsModel::DS4:
|
||||
return "DualShock 4";
|
||||
case Proto::DsModel::Generic:
|
||||
return "Generic Gamepad";
|
||||
default:
|
||||
return "Device";
|
||||
}
|
||||
}
|
||||
|
||||
std::string Device::GetSource() const
|
||||
{
|
||||
return "DSUClient";
|
||||
}
|
||||
|
||||
void Device::UpdateInput()
|
||||
{
|
||||
// Regularly tell the UDP server to feed us controller data
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now >= m_next_reregister)
|
||||
{
|
||||
m_next_reregister = now + SERVER_REREGISTER_INTERVAL;
|
||||
|
||||
Proto::Message<Proto::MessageType::PadDataRequest> msg(m_client_uid);
|
||||
auto& data_req = msg.m_message;
|
||||
data_req.register_flags = Proto::RegisterFlags::PadID;
|
||||
data_req.pad_id_to_register = m_index;
|
||||
msg.Finish();
|
||||
if (m_socket.send(&data_req, sizeof(data_req), s_server_address, s_server_port) !=
|
||||
sf::Socket::Status::Done)
|
||||
ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient UpdateInput send failed");
|
||||
}
|
||||
|
||||
// Receive and handle controller data
|
||||
Proto::Message<Proto::MessageType::FromServer> msg;
|
||||
std::size_t received_bytes;
|
||||
sf::IpAddress sender;
|
||||
u16 port;
|
||||
while (m_socket.receive(&msg, sizeof msg, received_bytes, sender, port) ==
|
||||
sf::Socket::Status::Done)
|
||||
{
|
||||
if (auto pad_data = msg.CheckAndCastTo<Proto::MessageType::PadDataResponse>())
|
||||
{
|
||||
m_pad_data = *pad_data;
|
||||
|
||||
m_accel.x = m_pad_data.accelerometer_x_g;
|
||||
m_accel.z = -m_pad_data.accelerometer_y_g;
|
||||
m_accel.y = -m_pad_data.accelerometer_z_inverted_g;
|
||||
m_gyro.x = -m_pad_data.gyro_pitch_deg_s;
|
||||
m_gyro.z = -m_pad_data.gyro_yaw_deg_s;
|
||||
m_gyro.y = -m_pad_data.gyro_roll_deg_s;
|
||||
|
||||
// Convert Gs to meters per second squared
|
||||
m_accel = m_accel * GRAVITY_ACCELERATION;
|
||||
|
||||
// Convert degrees per second to radians per second
|
||||
m_gyro = m_gyro * (MathUtil::TAU / 360);
|
||||
|
||||
// Update touch pad relative coordinates
|
||||
if (m_pad_data.touch1.id != m_prev_touch.id)
|
||||
m_prev_touch_valid = false;
|
||||
if (m_prev_touch_valid)
|
||||
{
|
||||
m_touch_x += m_pad_data.touch1.x - m_prev_touch.x;
|
||||
m_touch_y += m_pad_data.touch1.y - m_prev_touch.y;
|
||||
m_touch_x = std::clamp(m_touch_x, -TOUCH_X_AXIS_MAX, TOUCH_X_AXIS_MAX);
|
||||
m_touch_y = std::clamp(m_touch_y, -TOUCH_Y_AXIS_MAX, TOUCH_Y_AXIS_MAX);
|
||||
}
|
||||
m_prev_touch = m_pad_data.touch1;
|
||||
m_prev_touch_valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> Device::GetPreferredId() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
} // namespace ciface::DualShockUDPClient
|
|
@ -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" />
|
||||
|
|
|
@ -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,13 +224,26 @@
|
|||
<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" />
|
||||
|
|
Loading…
Reference in New Issue