From 4cb3baba5cf360a02dff9d6af1a839a570c40999 Mon Sep 17 00:00:00 2001 From: rlnilsen <47765059+rlnilsen@users.noreply.github.com> Date: Fri, 6 Sep 2019 17:09:30 +0200 Subject: [PATCH 1/5] Add support for motion controllers via the CemuHook controller input protocol. This is done by: 1) Implementing said protocol in a new controller input class CemuHookUDPServer. 2) Adding functionality in the WiimoteEmu class for pushing that motion input to the emulated Wiimote and MotionPlus. 3) Suitably modifying the UI for configuring an Emulated Wii Remote. --- Source/Core/Common/CommonPaths.h | 1 + Source/Core/Common/Config/Config.cpp | 12 +- Source/Core/Common/Config/Enums.h | 1 + Source/Core/Common/FileUtil.cpp | 2 + Source/Core/Common/FileUtil.h | 1 + .../Core/ConfigLoaders/BaseConfigLoader.cpp | 1 + .../Core/ConfigLoaders/IsSettingSaveable.cpp | 3 + Source/Core/Core/Core.vcxproj | 2 +- Source/Core/Core/Core.vcxproj.filters | 5 +- Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp | 87 ++++ Source/Core/Core/HW/WiimoteEmu/Dynamics.h | 6 + Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 80 +++- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 26 +- Source/Core/DolphinQt/CMakeLists.txt | 2 + .../Config/Mapping/MappingWindow.cpp | 4 +- .../Mapping/WiimoteEmuMotionControlIMU.cpp | 72 +++ .../Mapping/WiimoteEmuMotionControlIMU.h | 32 ++ Source/Core/DolphinQt/DolphinQt.vcxproj | 3 + Source/Core/InputCommon/CMakeLists.txt | 9 + .../ControllerEmu/ControlGroup/ControlGroup.h | 3 + .../ControlGroup/IMUAccelerometer.cpp | 44 ++ .../ControlGroup/IMUAccelerometer.h | 24 + .../ControllerEmu/ControlGroup/IMUCursor.cpp | 43 ++ .../ControllerEmu/ControlGroup/IMUCursor.h | 26 ++ .../ControlGroup/IMUGyroscope.cpp | 44 ++ .../ControllerEmu/ControlGroup/IMUGyroscope.h | 24 + .../CemuHookUDPServer/CemuHookUDPServer.cpp | 432 ++++++++++++++++++ .../CemuHookUDPServer/CemuHookUDPServer.h | 11 + .../CemuHookUDPServerProto.h | 270 +++++++++++ .../ControllerInterface.cpp | 12 + .../ControllerInterface/ControllerInterface.h | 1 + Source/Core/InputCommon/InputCommon.vcxproj | 11 +- .../InputCommon/InputCommon.vcxproj.filters | 32 +- 33 files changed, 1301 insertions(+), 25 deletions(-) create mode 100644 Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp create mode 100644 Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.cpp create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.cpp create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.h create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.cpp create mode 100644 Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h create mode 100644 Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp create mode 100644 Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h create mode 100644 Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index bec3fc0772..3ca7f35ab0 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -85,6 +85,7 @@ #define GFX_CONFIG "GFX.ini" #define DEBUGGER_CONFIG "Debugger.ini" #define LOGGER_CONFIG "Logger.ini" +#define CEMUHOOKUDPSERVER_CONFIG "UDPServer.ini" // Files in the directory returned by GetUserPath(D_LOGS_IDX) #define MAIN_LOG "dolphin.log" diff --git a/Source/Core/Common/Config/Config.cpp b/Source/Core/Common/Config/Config.cpp index ba7f5b9007..265da19cff 100644 --- a/Source/Core/Common/Config/Config.cpp +++ b/Source/Core/Common/Config/Config.cpp @@ -133,9 +133,15 @@ void ClearCurrentRunLayer() } static const std::map 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::CemuHookUdpServer, "CemuHookUdpServer"}}; const std::string& GetSystemName(System system) { diff --git a/Source/Core/Common/Config/Enums.h b/Source/Core/Common/Config/Enums.h index 1363c78913..ff5f543925 100644 --- a/Source/Core/Common/Config/Enums.h +++ b/Source/Core/Common/Config/Enums.h @@ -30,6 +30,7 @@ enum class System GFX, Logger, Debugger, + CemuHookUdpServer, }; constexpr std::array SEARCH_ORDER{{ diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 3c85563b56..965247cba4 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -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_CEMUHOOKUDPSERVERCONFIG_IDX] = + s_user_paths[D_CONFIG_IDX] + CEMUHOOKUDPSERVER_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; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 3cb65e093a..7a956e1200 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -70,6 +70,7 @@ enum F_MEMORYWATCHERLOCATIONS_IDX, F_MEMORYWATCHERSOCKET_IDX, F_WIISDCARD_IDX, + F_CEMUHOOKUDPSERVERCONFIG_IDX, NUM_PATH_INDICES }; diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp index 4422a096a5..813e898c20 100644 --- a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp @@ -89,6 +89,7 @@ const std::map system_to_ini = { {Config::System::GFX, F_GFXCONFIG_IDX}, {Config::System::Logger, F_LOGGERCONFIG_IDX}, {Config::System::Debugger, F_DEBUGGERCONFIG_IDX}, + {Config::System::CemuHookUdpServer, F_CEMUHOOKUDPSERVERCONFIG_IDX}, }; // INI layer configuration loader diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index 15711a65b8..7c3ac70b70 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -16,6 +16,9 @@ namespace ConfigLoaders { bool IsSettingSaveable(const Config::ConfigLocation& config_location) { + if (config_location.system == Config::System::CemuHookUdpServer) + return true; + if (config_location.system == Config::System::Logger) return true; diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index a08761f0d9..20e23993b0 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -629,4 +629,4 @@ - + \ No newline at end of file diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 3064754798..06bc1fa6b2 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -1661,8 +1661,11 @@ HW %28Flipper/Hollywood%29 + + HW %28Flipper/Hollywood%29\Wiimote\Emu + - + \ No newline at end of file diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp index bc766ce5b1..00820bf55a 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.cpp @@ -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* 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) { diff --git a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h index c7483b6e91..6d610c394b 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Dynamics.h +++ b/Source/Core/Core/HW/WiimoteEmu/Dynamics.h @@ -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* 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, diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index c1f9663c7e..8c735e5e56 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -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 diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 4bb613e526..0f627e619e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -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 m_sideways_setting; ControllerEmu::SettingValue m_upright_setting; @@ -284,5 +299,6 @@ private: RotationalState m_tilt_state; MotionState m_cursor_state; PositionalState m_shake_state; + std::optional m_imu_cursor_state; }; } // namespace WiimoteEmu diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 52b13c981d..b84119fad0 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -140,6 +140,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 diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index 7c7e99b684..cdddc07fe2 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -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; } diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp new file mode 100644 index 0000000000..f15408d64a --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp @@ -0,0 +1,72 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h" + +#include +#include +#include +#include +#include +#include + +#include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" + +#include "InputCommon/InputConfig.h" + +WiimoteEmuMotionControlIMU::WiimoteEmuMotionControlIMU(MappingWindow* window) + : MappingWidget(window) +{ + CreateMainLayout(); +} + +QGroupBox* WiimoteEmuMotionControlIMU::AddWarning(QGroupBox* groupbox) +{ + QFormLayout* layout = static_cast(groupbox->layout()); + QLabel* label; + + label = new QLabel(QLatin1String("")); + layout->addRow(label); + + label = new QLabel(tr("WARNING")); + label->setStyleSheet(QLatin1String("QLabel { color : red; }")); + layout->addRow(label); + + label = new QLabel( + tr("These controls are not intended for mapping regular buttons, triggers or axes.")); + label->setWordWrap(true); + layout->addRow(label); + + return groupbox; +} + +void WiimoteEmuMotionControlIMU::CreateMainLayout() +{ + m_main_layout = new QHBoxLayout(); + + m_main_layout->addWidget( + CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint))); + m_main_layout->addWidget(AddWarning(CreateGroupBox( + Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer)))); + m_main_layout->addWidget(AddWarning( + CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUGyroscope)))); + + setLayout(m_main_layout); +} + +void WiimoteEmuMotionControlIMU::LoadSettings() +{ + Wiimote::LoadConfig(); +} + +void WiimoteEmuMotionControlIMU::SaveSettings() +{ + Wiimote::GetConfig()->SaveConfig(); +} + +InputConfig* WiimoteEmuMotionControlIMU::GetConfig() +{ + return Wiimote::GetConfig(); +} diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h new file mode 100644 index 0000000000..0ebe152146 --- /dev/null +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h @@ -0,0 +1,32 @@ +// 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(); + static QGroupBox* AddWarning(QGroupBox* groupbox); + + // Main + QHBoxLayout* m_main_layout; +}; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 2deb34cffc..0d0aee2c7c 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -89,6 +89,7 @@ + @@ -283,6 +284,7 @@ + @@ -332,6 +334,7 @@ + diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index b47503a9ae..38f2e03d2f 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -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/CemuHookUDPServer/CemuHookUDPServer.cpp + ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h + ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h ControllerInterface/ControllerInterface.cpp ControllerInterface/ControllerInterface.h ControllerInterface/Device.cpp diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h index fff118d280..310df90e30 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/ControlGroup.h @@ -40,6 +40,9 @@ enum class GroupType Triggers, Slider, Shake, + IMUAccelerometer, + IMUGyroscope, + IMUCursor }; class ControlGroup diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.cpp new file mode 100644 index 0000000000..e994ceba5d --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.cpp @@ -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(Translate, _trans("Left"))); + controls.emplace_back(std::make_unique(Translate, _trans("Right"))); + controls.emplace_back(std::make_unique(Translate, _trans("Forward"))); + controls.emplace_back(std::make_unique(Translate, _trans("Backward"))); + controls.emplace_back(std::make_unique(Translate, _trans("Up"))); + controls.emplace_back(std::make_unique(Translate, _trans("Down"))); +} + +std::optional 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 diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h new file mode 100644 index 0000000000..7ac41ede7a --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUAccelerometer.h @@ -0,0 +1,24 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#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 GetState() const; +}; +} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.cpp new file mode 100644 index 0000000000..7133a07e59 --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.cpp @@ -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 + +#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(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 diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.h new file mode 100644 index 0000000000..578762fb85 --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUCursor.h @@ -0,0 +1,26 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#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 m_yaw_setting; +}; +} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.cpp new file mode 100644 index 0000000000..7bae049fff --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.cpp @@ -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(Translate, _trans("Pitch Up"))); + controls.emplace_back(std::make_unique(Translate, _trans("Pitch Down"))); + controls.emplace_back(std::make_unique(Translate, _trans("Roll Left"))); + controls.emplace_back(std::make_unique(Translate, _trans("Roll Right"))); + controls.emplace_back(std::make_unique(Translate, _trans("Yaw Left"))); + controls.emplace_back(std::make_unique(Translate, _trans("Yaw Right"))); +} + +std::optional 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 diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h new file mode 100644 index 0000000000..dac30143dc --- /dev/null +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h @@ -0,0 +1,24 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#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 GetState() const; +}; +} // namespace ControllerEmu diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp new file mode 100644 index 0000000000..a480320b33 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp @@ -0,0 +1,432 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" + +#include +#include + +#include +#include + +#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/CemuHookUDPServer/CemuHookUDPServerProto.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" + +namespace ciface::CemuHookUDPServer +{ +class Device : public Core::Device +{ +private: + template + 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 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 + { + 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 + { + 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 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 SERVER_ENABLED{ + {Config::System::CemuHookUdpServer, "Server", "Enabled"}, false}; +const Config::ConfigInfo SERVER_ADDRESS{ + {Config::System::CemuHookUdpServer, "Server", "IPAddress"}, DEFAULT_SERVER_ADDRESS}; +const Config::ConfigInfo SERVER_PORT{{Config::System::CemuHookUdpServer, "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 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("CemuHookUDPServer Hotplug Thread"); + NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer hotplug thread started"); + + sf::UdpSocket socket; + + while (s_hotplug_thread_running.IsSet()) + { + const auto now = std::chrono::steady_clock::now(); + if (now >= s_next_listports) + { + s_next_listports = now + SERVER_LISTPORTS_INTERVAL; + + // Request info on the four controller ports + Proto::Message 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 (socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != + sf::Socket::Status::Done) + ERROR_LOG(SERIALINTERFACE, "CemuHookUDPServer HotplugThreadFunc send failed"); + } + + // Receive controller port info + Proto::Message 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(timeout).count(); + timeout_ms = std::max(timeout_ms, 1); + std::size_t received_bytes; + sf::IpAddress sender; + u16 port; + if (ReceiveWithTimeout(socket, &msg, sizeof(msg), received_bytes, sender, port, + sf::milliseconds(timeout_ms)) == sf::Socket::Status::Done) + { + if (auto port_info = msg.CheckAndCastTo()) + { + const bool port_changed = !IsSameController(*port_info, s_port_info[port_info->pad_id]); + { + std::lock_guard lock(s_port_info_mutex); + s_port_info[port_info->pad_id] = *port_info; + } + if (port_changed) + PopulateDevices(); + } + } + } + NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer 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_hotplug_thread.join(); +} + +void Init() +{ + s_server_enabled = Config::Get(Settings::SERVER_ENABLED); + s_server_address = Config::Get(Settings::SERVER_ADDRESS); + s_server_port = Config::Get(Settings::SERVER_PORT); + + s_client_uid = Common::Random::GenerateValue(); + 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; + } + + if (s_server_enabled) + StartHotplugThread(); +} + +void PopulateDevices() +{ + NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer PopulateDevices"); + + g_controller_interface.RemoveDevice( + [](const auto* dev) { return dev->GetSource() == "UDPServer"; }); + + std::lock_guard 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(port_info.model, port_index)); + } +} + +void DeInit() +{ + StopHotplugThread(); + SaveSettings(); +} + +void SaveSettings() +{ + Config::ConfigChangeCallbackGuard config_guard; + + Config::SetBaseOrCurrent(Settings::SERVER_ENABLED, s_server_enabled); + Config::SetBaseOrCurrent(Settings::SERVER_ADDRESS, s_server_address); + Config::SetBaseOrCurrent(Settings::SERVER_PORT, s_server_port); +} + +Device::Device(Proto::DsModel model, int index) + : m_model(model), m_index(index), + m_client_uid(Common::Random::GenerateValue()), 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("Pad W", m_pad_data.button_dpad_left_analog, 255)); + AddInput(new AnalogInput("Pad S", m_pad_data.button_dpad_down_analog, 255)); + AddInput(new AnalogInput("Pad E", m_pad_data.button_dpad_right_analog, 255)); + AddInput(new AnalogInput("Pad N", m_pad_data.button_dpad_up_analog, 255)); + AddInput(new AnalogInput("Square", m_pad_data.button_square_analog, 255)); + AddInput(new AnalogInput("Cross", m_pad_data.button_cross_analog, 255)); + AddInput(new AnalogInput("Circle", m_pad_data.button_circle_analog, 255)); + AddInput(new AnalogInput("Triangle", m_pad_data.button_triangle_analog, 255)); + AddInput(new AnalogInput("L1", m_pad_data.button_l1_analog, 255)); + AddInput(new AnalogInput("R1", m_pad_data.button_r1_analog, 255)); + + AddInput(new AnalogInput("L2", m_pad_data.trigger_l2, 255)); + AddInput(new AnalogInput("R2", m_pad_data.trigger_r2, 255)); + + AddInput(new Button("L3", m_pad_data.button_states1, 0x2)); + AddInput(new Button("R3", m_pad_data.button_states1, 0x4)); + AddInput(new Button("Share", m_pad_data.button_states1, 0x1)); + AddInput(new Button("Options", m_pad_data.button_states1, 0x8)); + AddInput(new Button("PS", m_pad_data.button_ps, 0x1)); + AddInput(new Button("Touch Button", m_pad_data.button_touch, 0x1)); + + AddInput(new AnalogInput("Left X-", m_pad_data.left_stick_x, -128, -128)); + AddInput(new AnalogInput("Left X+", m_pad_data.left_stick_x, 127, -128)); + AddInput(new AnalogInput("Left Y-", m_pad_data.left_stick_y_inverted, -128, -128)); + AddInput(new AnalogInput("Left Y+", m_pad_data.left_stick_y_inverted, 127, -128)); + AddInput(new AnalogInput("Right X-", m_pad_data.right_stick_x, -128, -128)); + AddInput(new AnalogInput("Right X+", m_pad_data.right_stick_x, 127, -128)); + AddInput(new AnalogInput("Right Y-", m_pad_data.right_stick_y_inverted, -128, -128)); + AddInput(new AnalogInput("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 "UDPServer"; +} + +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 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, "CemuHookUDPServer UpdateInput send failed"); + } + + // Receive and handle controller data + Proto::Message 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()) + { + 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 Device::GetPreferredId() const +{ + return m_index; +} + +} // namespace ciface::CemuHookUDPServer diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h new file mode 100644 index 0000000000..98b2506d2e --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h @@ -0,0 +1,11 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +namespace ciface::CemuHookUDPServer +{ +void Init(); +void PopulateDevices(); +void DeInit(); +void SaveSettings(); +} // namespace ciface::CemuHookUDPServer diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h new file mode 100644 index 0000000000..60c8059292 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h @@ -0,0 +1,270 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include + +#include "Common/CommonTypes.h" + +namespace ciface::CemuHookUDPServer::Proto +{ +// CemuHook UDPServer 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(buffer), length); +} + +template +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 + std::optional 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, + "CemuHookUDPServer 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::CemuHookUDPServer::Proto diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index bb5ccb3f0e..5f20fd475b 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -30,6 +30,9 @@ #ifdef CIFACE_USE_PIPES #include "InputCommon/ControllerInterface/Pipes/Pipes.h" #endif +#ifdef CIFACE_USE_CEMUHOOKUDPSERVER +#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.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_CEMUHOOKUDPSERVER + ciface::CemuHookUDPServer::Init(); #endif RefreshDevices(); @@ -122,6 +128,9 @@ void ControllerInterface::RefreshDevices() #ifdef CIFACE_USE_PIPES ciface::Pipes::PopulateDevices(); #endif +#ifdef CIFACE_USE_CEMUHOOKUDPSERVER + ciface::CemuHookUDPServer::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_CEMUHOOKUDPSERVER + ciface::CemuHookUDPServer::DeInit(); +#endif } void ControllerInterface::AddDevice(std::shared_ptr device) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 2ad53fc79b..f1a8e0c19e 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -29,6 +29,7 @@ #if defined(USE_PIPES) #define CIFACE_USE_PIPES #endif +#define CIFACE_USE_CEMUHOOKUDPSERVER // // ControllerInterface diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index a22ad21394..e3d252cb5f 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -36,6 +36,9 @@ + + + @@ -53,6 +56,7 @@ + @@ -76,6 +80,9 @@ + + + @@ -93,6 +100,8 @@ + + @@ -122,4 +131,4 @@ - + \ No newline at end of file diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index c1f26d6141..ca29eec811 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -28,6 +28,9 @@ {07bad1aa-7e03-4f5c-ade2-a44857c5cbc3} + + {4f71c21c-85eb-4e76-ad64-40fd97bc3d6d} + @@ -120,6 +123,18 @@ ControllerEmu\ControlGroup + + ControllerEmu\ControlGroup + + + ControllerInterface\CemuHookUDPServer + + + ControllerEmu\ControlGroup + + + ControllerEmu\ControlGroup + @@ -216,8 +231,23 @@ ControllerEmu\ControlGroup + + ControllerEmu\ControlGroup + + + ControllerInterface\CemuHookUDPServer + + + ControllerEmu\ControlGroup + + + ControllerEmu\ControlGroup + + + ControllerInterface\CemuHookUDPServer + - + \ No newline at end of file From 8aec424191b295e51d3f305c20086c08dd41b480 Mon Sep 17 00:00:00 2001 From: rlnilsen <47765059+rlnilsen@users.noreply.github.com> Date: Tue, 22 Oct 2019 01:43:37 +0200 Subject: [PATCH 2/5] Controller Settings Dialog: Rename "Advanced" group box to "Common". The only setting inside, "Background Input", doesn't seem advanced to me, but it is used for both GC an Wii input. --- .../DolphinQt/Config/ControllersWindow.cpp | 23 ++++++++++--------- .../Core/DolphinQt/Config/ControllersWindow.h | 10 ++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.cpp b/Source/Core/DolphinQt/Config/ControllersWindow.cpp index 304ae7364c..0f2cfc00e3 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.cpp +++ b/Source/Core/DolphinQt/Config/ControllersWindow.cpp @@ -67,7 +67,7 @@ ControllersWindow::ControllersWindow(QWidget* parent) : QDialog(parent) CreateGamecubeLayout(); CreateWiimoteLayout(); - CreateAdvancedLayout(); + CreateCommonLayout(); CreateMainLayout(); LoadSettings(); ConnectWidgets(); @@ -199,15 +199,16 @@ 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 QHBoxLayout(); + m_common_bg_input = new QCheckBox(tr("Background Input")); - m_advanced_layout->addWidget(m_advanced_bg_input); + m_common_layout->addWidget(m_common_bg_input); - m_advanced_box->setLayout(m_advanced_layout); + m_common_box->setLayout(m_common_layout); } void ControllersWindow::CreateMainLayout() @@ -217,7 +218,7 @@ void ControllersWindow::CreateMainLayout() layout->addWidget(m_gc_box); layout->addWidget(m_wiimote_box); - layout->addWidget(m_advanced_box); + layout->addWidget(m_common_box); layout->addWidget(m_button_box); WrapInScrollArea(this, layout); @@ -232,7 +233,7 @@ 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_wiimote_continuous_scanning, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings); connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this, @@ -480,7 +481,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 +496,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 : diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.h b/Source/Core/DolphinQt/Config/ControllersWindow.h index ae03b79491..98e81bd058 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.h +++ b/Source/Core/DolphinQt/Config/ControllersWindow.h @@ -40,7 +40,7 @@ private: void CreateGamecubeLayout(); void CreateWiimoteLayout(); - void CreateAdvancedLayout(); + void CreateCommonLayout(); void CreateMainLayout(); void ConnectWidgets(); void LoadSettings(); @@ -73,8 +73,8 @@ 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; + QHBoxLayout* m_common_layout; + QCheckBox* m_common_bg_input; }; From 5ff79499a593f651978188f1711728cf67d44a59 Mon Sep 17 00:00:00 2001 From: rlnilsen <47765059+rlnilsen@users.noreply.github.com> Date: Wed, 23 Oct 2019 01:49:48 +0200 Subject: [PATCH 3/5] UDPServer: Add configuration UI. Accessed through button "Alternate Input Sources" in the "Controller Settings" dialog. --- Source/Core/DolphinQt/CMakeLists.txt | 4 + .../CemuHookUDPServerWidget.cpp | 75 +++++++++++++++++++ .../CemuHookUDPServerWidget.h | 26 +++++++ .../ControllerInterfaceWindow.cpp | 47 ++++++++++++ .../ControllerInterfaceWindow.h | 32 ++++++++ .../DolphinQt/Config/ControllersWindow.cpp | 16 +++- .../Core/DolphinQt/Config/ControllersWindow.h | 4 +- Source/Core/DolphinQt/DolphinQt.vcxproj | 8 +- .../CemuHookUDPServer/CemuHookUDPServer.cpp | 48 +++++++----- .../CemuHookUDPServer/CemuHookUDPServer.h | 10 ++- 10 files changed, 248 insertions(+), 22 deletions(-) create mode 100644 Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp create mode 100644 Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h create mode 100644 Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp create mode 100644 Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index b84119fad0..783cefbb68 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -51,6 +51,10 @@ add_executable(dolphin-emu Config/CheatCodeEditor.h Config/CheatWarningWidget.cpp Config/CheatWarningWidget.h + Config/ControllerInterface/CemuHookUDPServerWidget.cpp + Config/ControllerInterface/CemuHookUDPServerWidget.h + Config/ControllerInterface/ControllerInterfaceWindow.cpp + Config/ControllerInterface/ControllerInterfaceWindow.h Config/ControllersWindow.cpp Config/ControllersWindow.h Config/FilesystemWidget.cpp diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp new file mode 100644 index 0000000000..47ab3d71b7 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp @@ -0,0 +1,75 @@ +// Copyright 2019 Dolphin Emulator Project5~5~5~ +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" + +#include +#include +#include +#include +#include +#include + +#include "Common/Config/Config.h" +#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" + +CemuHookUDPServerWidget::CemuHookUDPServerWidget() +{ + CreateWidgets(); + ConnectWidgets(); +} + +void CemuHookUDPServerWidget::CreateWidgets() +{ + auto* main_layout = new QGridLayout; + + m_server_enabled = new QCheckBox(tr("Enable")); + m_server_enabled->setChecked(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED)); + + m_server_address = new QLineEdit( + QString::fromStdString(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS))); + + m_server_port = new QSpinBox(); + m_server_port->setMaximum(65535); + m_server_port->setValue(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_PORT)); + + auto* description = + new QLabel(tr("UDPServer protocol enables the use of input and motion data from compatible " + "sources, like PlayStation, Nintendo Switch and Steam controllers.

" + "For setup instructions, " + "" + "refer to this page.")); + description->setTextFormat(Qt::RichText); + description->setWordWrap(true); + description->setTextInteractionFlags(Qt::TextBrowserInteraction); + description->setOpenExternalLinks(true); + + main_layout->addWidget(m_server_enabled, 1, 1); + main_layout->addWidget(new QLabel(tr("Server IP Address")), 2, 1); + main_layout->addWidget(m_server_address, 2, 2); + main_layout->addWidget(new QLabel(tr("Server Port")), 3, 1); + main_layout->addWidget(m_server_port, 3, 2); + main_layout->addWidget(description, 4, 1, 1, 2); + + setLayout(main_layout); +} + +void CemuHookUDPServerWidget::ConnectWidgets() +{ + connect(m_server_enabled, &QCheckBox::toggled, this, [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED, + m_server_enabled->isChecked()); + }); + + connect(m_server_address, &QLineEdit::editingFinished, this, [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS, + m_server_address->text().toStdString()); + }); + + connect(m_server_port, static_cast(&QSpinBox::valueChanged), this, + [this] { + Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_PORT, + static_cast(m_server_port->value())); + }); +} diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h new file mode 100644 index 0000000000..24a0db18ac --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h @@ -0,0 +1,26 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +class QCheckBox; +class QLineEdit; +class QSpinBox; + +class CemuHookUDPServerWidget final : public QWidget +{ + Q_OBJECT +public: + explicit CemuHookUDPServerWidget(); + +private: + void CreateWidgets(); + void ConnectWidgets(); + + QCheckBox* m_server_enabled; + QLineEdit* m_server_address; + QSpinBox* m_server_port; +}; diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp new file mode 100644 index 0000000000..1ea5501e7a --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" + +#include +#include +#include +#include + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) +#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" +#endif + +ControllerInterfaceWindow::ControllerInterfaceWindow(QWidget* parent) : QDialog(parent) +{ + CreateMainLayout(); + + setWindowTitle(tr("Alternate Input Sources")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +void ControllerInterfaceWindow::CreateMainLayout() +{ + m_button_box = new QDialogButtonBox(QDialogButtonBox::Close); + connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); + + m_tab_widget = new QTabWidget(); +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) + m_udpserver_widget = new CemuHookUDPServerWidget(); + m_tab_widget->addTab(m_udpserver_widget, tr("UDPServer")); // TODO: use GetWrappedWidget()? +#endif + + auto* main_layout = new QVBoxLayout(); + if (m_tab_widget->count() > 0) + { + main_layout->addWidget(m_tab_widget); + } + else + { + main_layout->addWidget(new QLabel(tr("Nothing to configure")), 0, + Qt::AlignVCenter | Qt::AlignHCenter); + } + main_layout->addWidget(m_button_box); + setLayout(main_layout); +} diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h new file mode 100644 index 0000000000..47d12a9199 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h @@ -0,0 +1,32 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "InputCommon/ControllerInterface/ControllerInterface.h" + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) +class CemuHookUDPServerWidget; +#endif +class QTabWidget; +class QDialogButtonBox; + +class ControllerInterfaceWindow final : public QDialog +{ + Q_OBJECT +public: + explicit ControllerInterfaceWindow(QWidget* parent); + +private: + void CreateMainLayout(); + + QTabWidget* m_tab_widget; + QDialogButtonBox* m_button_box; + +#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) + CemuHookUDPServerWidget* m_udpserver_widget; +#endif +}; diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.cpp b/Source/Core/DolphinQt/Config/ControllersWindow.cpp index 0f2cfc00e3..1df7b1d9a7 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.cpp +++ b/Source/Core/DolphinQt/Config/ControllersWindow.cpp @@ -31,6 +31,7 @@ #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Bluetooth/BTReal.h" +#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h" #include "DolphinQt/Config/Mapping/GCPadWiiUConfigDialog.h" #include "DolphinQt/Config/Mapping/MappingWindow.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" @@ -203,10 +204,12 @@ void ControllersWindow::CreateCommonLayout() { // i18n: This is "common" as in "shared", not the opposite of "uncommon" m_common_box = new QGroupBox(tr("Common")); - m_common_layout = new QHBoxLayout(); + m_common_layout = new QVBoxLayout(); m_common_bg_input = new QCheckBox(tr("Background Input")); + m_common_configure_controller_interface = new QPushButton(tr("Alternate Input Sources")); m_common_layout->addWidget(m_common_bg_input); + m_common_layout->addWidget(m_common_configure_controller_interface); m_common_box->setLayout(m_common_layout); } @@ -219,6 +222,7 @@ void ControllersWindow::CreateMainLayout() layout->addWidget(m_gc_box); layout->addWidget(m_wiimote_box); layout->addWidget(m_common_box); + layout->addStretch(); layout->addWidget(m_button_box); WrapInScrollArea(this, layout); @@ -234,6 +238,8 @@ void ControllersWindow::ConnectWidgets() &ControllersWindow::OnWiimoteModeChanged); connect(m_common_bg_input, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings); + connect(m_common_configure_controller_interface, &QPushButton::clicked, this, + &ControllersWindow::OnControllerInterfaceConfigure); connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this, &ControllersWindow::SaveSettings); connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this, @@ -463,6 +469,14 @@ void ControllersWindow::OnWiimoteConfigure() window->show(); } +void ControllersWindow::OnControllerInterfaceConfigure() +{ + ControllerInterfaceWindow* window = new ControllerInterfaceWindow(this); + window->setAttribute(Qt::WA_DeleteOnClose, true); + window->setWindowModality(Qt::WindowModality::WindowModal); + window->show(); +} + void ControllersWindow::LoadSettings() { for (size_t i = 0; i < m_wiimote_groups.size(); i++) diff --git a/Source/Core/DolphinQt/Config/ControllersWindow.h b/Source/Core/DolphinQt/Config/ControllersWindow.h index 98e81bd058..368ef36cdb 100644 --- a/Source/Core/DolphinQt/Config/ControllersWindow.h +++ b/Source/Core/DolphinQt/Config/ControllersWindow.h @@ -37,6 +37,7 @@ private: void OnWiimoteRefreshPressed(); void OnGCPadConfigure(); void OnWiimoteConfigure(); + void OnControllerInterfaceConfigure(); void CreateGamecubeLayout(); void CreateWiimoteLayout(); @@ -75,6 +76,7 @@ private: // Common QGroupBox* m_common_box; - QHBoxLayout* m_common_layout; + QVBoxLayout* m_common_layout; QCheckBox* m_common_bg_input; + QPushButton* m_common_configure_controller_interface; }; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 0d0aee2c7c..90b334266a 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -44,7 +44,7 @@ avrt.lib;iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;Shlwapi.lib;discord-rpc.lib;%(AdditionalDependencies) - $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Debugger;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;$(ProjectDir)TAS;$(ProjectDir)FIFO;%(AdditionalIncludeDirectories) + $(ProjectDir)VideoInterface;$(ProjectDir)GameList;$(ProjectDir)Debugger;$(ProjectDir)Settings;$(ProjectDir)Config;$(ProjectDir)Config\Mapping;$(ProjectDir)Config\Graphics;$(ProjectDir)Config\ControllerInterface;$(ProjectDir)NetPlay;$(ProjectDir)QtUtils;$(ProjectDir)TAS;$(ProjectDir)FIFO;%(AdditionalIncludeDirectories) DolphinQt.manifest;%(AdditionalManifestFiles) @@ -106,6 +106,8 @@ + + @@ -187,6 +189,8 @@ + + @@ -291,6 +295,8 @@ + + diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp index a480320b33..36cd148041 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp @@ -135,6 +135,7 @@ static std::thread s_hotplug_thread; static Common::Flag s_hotplug_thread_running; static std::mutex s_port_info_mutex; static Proto::MessageType::PortInfo s_port_info[Proto::PORT_COUNT]; +static sf::UdpSocket s_socket; static bool IsSameController(const Proto::MessageType::PortInfo& a, const Proto::MessageType::PortInfo& b) @@ -162,8 +163,6 @@ static void HotplugThreadFunc() Common::SetCurrentThreadName("CemuHookUDPServer Hotplug Thread"); NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer hotplug thread started"); - sf::UdpSocket socket; - while (s_hotplug_thread_running.IsSet()) { const auto now = std::chrono::steady_clock::now(); @@ -180,7 +179,7 @@ static void HotplugThreadFunc() list_ports.pad_id[2] = 2; list_ports.pad_id[3] = 3; msg.Finish(); - if (socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != + if (s_socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != sf::Socket::Status::Done) ERROR_LOG(SERIALINTERFACE, "CemuHookUDPServer HotplugThreadFunc send failed"); } @@ -194,7 +193,7 @@ static void HotplugThreadFunc() std::size_t received_bytes; sf::IpAddress sender; u16 port; - if (ReceiveWithTimeout(socket, &msg, sizeof(msg), received_bytes, sender, port, + if (ReceiveWithTimeout(s_socket, &msg, sizeof(msg), received_bytes, sender, port, sf::milliseconds(timeout_ms)) == sf::Socket::Status::Done) { if (auto port_info = msg.CheckAndCastTo()) @@ -233,14 +232,15 @@ static void StopHotplugThread() return; } + s_socket.unbind(); // interrupt blocking socket s_hotplug_thread.join(); } -void Init() +static void Restart() { - s_server_enabled = Config::Get(Settings::SERVER_ENABLED); - s_server_address = Config::Get(Settings::SERVER_ADDRESS); - s_server_port = Config::Get(Settings::SERVER_PORT); + NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer Restart"); + + StopHotplugThread(); s_client_uid = Common::Random::GenerateValue(); s_next_listports = std::chrono::steady_clock::time_point::min(); @@ -250,10 +250,32 @@ void Init() s_port_info[port_index].pad_id = port_index; } + PopulateDevices(); // remove devices + if (s_server_enabled) StartHotplugThread(); } +static void ConfigChanged() +{ + bool server_enabled = Config::Get(Settings::SERVER_ENABLED); + std::string server_address = Config::Get(Settings::SERVER_ADDRESS); + u16 server_port = Config::Get(Settings::SERVER_PORT); + if (server_enabled != s_server_enabled || server_address != s_server_address || + server_port != s_server_port) + { + s_server_enabled = server_enabled; + s_server_address = server_address; + s_server_port = server_port; + Restart(); + } +} + +void Init() +{ + Config::AddConfigChangedCallback(ConfigChanged); +} + void PopulateDevices() { NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer PopulateDevices"); @@ -273,16 +295,6 @@ void PopulateDevices() void DeInit() { StopHotplugThread(); - SaveSettings(); -} - -void SaveSettings() -{ - Config::ConfigChangeCallbackGuard config_guard; - - Config::SetBaseOrCurrent(Settings::SERVER_ENABLED, s_server_enabled); - Config::SetBaseOrCurrent(Settings::SERVER_ADDRESS, s_server_address); - Config::SetBaseOrCurrent(Settings::SERVER_PORT, s_server_port); } Device::Device(Proto::DsModel model, int index) diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h index 98b2506d2e..f7eaa34407 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h +++ b/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h @@ -2,10 +2,18 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Config/Config.h" + namespace ciface::CemuHookUDPServer { +namespace Settings +{ +extern const Config::ConfigInfo SERVER_ENABLED; +extern const Config::ConfigInfo SERVER_ADDRESS; +extern const Config::ConfigInfo SERVER_PORT; +} // namespace Settings + void Init(); void PopulateDevices(); void DeInit(); -void SaveSettings(); } // namespace ciface::CemuHookUDPServer From 332cad21a40a2565141ca817a3cc684d8f10bfca Mon Sep 17 00:00:00 2001 From: rlnilsen <47765059+rlnilsen@users.noreply.github.com> Date: Thu, 24 Oct 2019 18:14:19 +0200 Subject: [PATCH 4/5] Wiimote Mapping UI: Motion Input tab: Rework mapping warning, add explanation of and button to open Alternate Input Sources window. --- .../Mapping/WiimoteEmuMotionControlIMU.cpp | 55 ++++++++++--------- .../Mapping/WiimoteEmuMotionControlIMU.h | 3 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp index f15408d64a..89920c703d 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.cpp @@ -8,12 +8,15 @@ #include #include #include +#include #include #include #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) @@ -22,36 +25,36 @@ WiimoteEmuMotionControlIMU::WiimoteEmuMotionControlIMU(MappingWindow* window) CreateMainLayout(); } -QGroupBox* WiimoteEmuMotionControlIMU::AddWarning(QGroupBox* groupbox) -{ - QFormLayout* layout = static_cast(groupbox->layout()); - QLabel* label; - - label = new QLabel(QLatin1String("")); - layout->addRow(label); - - label = new QLabel(tr("WARNING")); - label->setStyleSheet(QLatin1String("QLabel { color : red; }")); - layout->addRow(label); - - label = new QLabel( - tr("These controls are not intended for mapping regular buttons, triggers or axes.")); - label->setWordWrap(true); - layout->addRow(label); - - return groupbox; -} - void WiimoteEmuMotionControlIMU::CreateMainLayout() { - m_main_layout = new QHBoxLayout(); + 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(); + }); - m_main_layout->addWidget( + auto* groups_layout = new QHBoxLayout(); + groups_layout->addWidget( CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint))); - m_main_layout->addWidget(AddWarning(CreateGroupBox( - Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer)))); - m_main_layout->addWidget(AddWarning( - CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUGyroscope)))); + 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); } diff --git a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h index 0ebe152146..b1aab7d1d4 100644 --- a/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h +++ b/Source/Core/DolphinQt/Config/Mapping/WiimoteEmuMotionControlIMU.h @@ -25,8 +25,7 @@ private: void LoadSettings() override; void SaveSettings() override; void CreateMainLayout(); - static QGroupBox* AddWarning(QGroupBox* groupbox); // Main - QHBoxLayout* m_main_layout; + QVBoxLayout* m_main_layout; }; From da1f153b47a3f3f4eb9b976670bfddf68108bae9 Mon Sep 17 00:00:00 2001 From: rlnilsen <47765059+rlnilsen@users.noreply.github.com> Date: Sat, 26 Oct 2019 18:05:16 +0200 Subject: [PATCH 5/5] Rename all instances of "CemuhookUDPServer"/"UDPServer" to "DualShockUDPClient"/"DSUClient". --- Source/Core/Common/CommonPaths.h | 2 +- Source/Core/Common/Config/Config.cpp | 2 +- Source/Core/Common/Config/Enums.h | 2 +- Source/Core/Common/FileUtil.cpp | 4 +-- Source/Core/Common/FileUtil.h | 2 +- .../Core/ConfigLoaders/BaseConfigLoader.cpp | 2 +- .../Core/ConfigLoaders/IsSettingSaveable.cpp | 2 +- Source/Core/DolphinQt/CMakeLists.txt | 4 +-- .../ControllerInterfaceWindow.cpp | 10 +++--- .../ControllerInterfaceWindow.h | 8 ++--- ...idget.cpp => DualShockUDPClientWidget.cpp} | 26 +++++++-------- ...verWidget.h => DualShockUDPClientWidget.h} | 4 +-- Source/Core/DolphinQt/DolphinQt.vcxproj | 6 ++-- Source/Core/InputCommon/CMakeLists.txt | 6 ++-- .../ControllerInterface.cpp | 16 +++++----- .../ControllerInterface/ControllerInterface.h | 2 +- .../DualShockUDPClient.cpp} | 32 +++++++++---------- .../DualShockUDPClient.h} | 4 +-- .../DualShockUDPProto.h} | 8 ++--- Source/Core/InputCommon/InputCommon.vcxproj | 6 ++-- .../InputCommon/InputCommon.vcxproj.filters | 24 +++++++------- 21 files changed, 85 insertions(+), 87 deletions(-) rename Source/Core/DolphinQt/Config/ControllerInterface/{CemuHookUDPServerWidget.cpp => DualShockUDPClientWidget.cpp} (65%) rename Source/Core/DolphinQt/Config/ControllerInterface/{CemuHookUDPServerWidget.h => DualShockUDPClientWidget.h} (79%) rename Source/Core/InputCommon/ControllerInterface/{CemuHookUDPServer/CemuHookUDPServer.cpp => DualShockUDPClient/DualShockUDPClient.cpp} (92%) rename Source/Core/InputCommon/ControllerInterface/{CemuHookUDPServer/CemuHookUDPServer.h => DualShockUDPClient/DualShockUDPClient.h} (83%) rename Source/Core/InputCommon/ControllerInterface/{CemuHookUDPServer/CemuHookUDPServerProto.h => DualShockUDPClient/DualShockUDPProto.h} (95%) diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 3ca7f35ab0..46a201d823 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -85,7 +85,7 @@ #define GFX_CONFIG "GFX.ini" #define DEBUGGER_CONFIG "Debugger.ini" #define LOGGER_CONFIG "Logger.ini" -#define CEMUHOOKUDPSERVER_CONFIG "UDPServer.ini" +#define DUALSHOCKUDPCLIENT_CONFIG "DSUClient.ini" // Files in the directory returned by GetUserPath(D_LOGS_IDX) #define MAIN_LOG "dolphin.log" diff --git a/Source/Core/Common/Config/Config.cpp b/Source/Core/Common/Config/Config.cpp index 265da19cff..b65b1d6fad 100644 --- a/Source/Core/Common/Config/Config.cpp +++ b/Source/Core/Common/Config/Config.cpp @@ -141,7 +141,7 @@ static const std::map system_to_name = { {System::Logger, "Logger"}, {System::Debugger, "Debugger"}, {System::SYSCONF, "SYSCONF"}, - {System::CemuHookUdpServer, "CemuHookUdpServer"}}; + {System::DualShockUDPClient, "DualShockUDPClient"}}; const std::string& GetSystemName(System system) { diff --git a/Source/Core/Common/Config/Enums.h b/Source/Core/Common/Config/Enums.h index ff5f543925..831726ae1e 100644 --- a/Source/Core/Common/Config/Enums.h +++ b/Source/Core/Common/Config/Enums.h @@ -30,7 +30,7 @@ enum class System GFX, Logger, Debugger, - CemuHookUdpServer, + DualShockUDPClient, }; constexpr std::array SEARCH_ORDER{{ diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 965247cba4..dfc3658db1 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -794,8 +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_CEMUHOOKUDPSERVERCONFIG_IDX] = - s_user_paths[D_CONFIG_IDX] + CEMUHOOKUDPSERVER_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; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 7a956e1200..ccabf7fdfe 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -70,7 +70,7 @@ enum F_MEMORYWATCHERLOCATIONS_IDX, F_MEMORYWATCHERSOCKET_IDX, F_WIISDCARD_IDX, - F_CEMUHOOKUDPSERVERCONFIG_IDX, + F_DUALSHOCKUDPCLIENTCONFIG_IDX, NUM_PATH_INDICES }; diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp index 813e898c20..e8ac86b423 100644 --- a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp @@ -89,7 +89,7 @@ const std::map system_to_ini = { {Config::System::GFX, F_GFXCONFIG_IDX}, {Config::System::Logger, F_LOGGERCONFIG_IDX}, {Config::System::Debugger, F_DEBUGGERCONFIG_IDX}, - {Config::System::CemuHookUdpServer, F_CEMUHOOKUDPSERVERCONFIG_IDX}, + {Config::System::DualShockUDPClient, F_DUALSHOCKUDPCLIENTCONFIG_IDX}, }; // INI layer configuration loader diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index 7c3ac70b70..9483aed414 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -16,7 +16,7 @@ namespace ConfigLoaders { bool IsSettingSaveable(const Config::ConfigLocation& config_location) { - if (config_location.system == Config::System::CemuHookUdpServer) + if (config_location.system == Config::System::DualShockUDPClient) return true; if (config_location.system == Config::System::Logger) diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 783cefbb68..8c6936d10d 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -51,8 +51,8 @@ add_executable(dolphin-emu Config/CheatCodeEditor.h Config/CheatWarningWidget.cpp Config/CheatWarningWidget.h - Config/ControllerInterface/CemuHookUDPServerWidget.cpp - Config/ControllerInterface/CemuHookUDPServerWidget.h + Config/ControllerInterface/DualShockUDPClientWidget.cpp + Config/ControllerInterface/DualShockUDPClientWidget.h Config/ControllerInterface/ControllerInterfaceWindow.cpp Config/ControllerInterface/ControllerInterfaceWindow.h Config/ControllersWindow.cpp diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp index 1ea5501e7a..991e84c8f9 100644 --- a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.cpp @@ -9,8 +9,8 @@ #include #include -#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) -#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" +#if defined(CIFACE_USE_DUALSHOCKUDPCLIENT) +#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h" #endif ControllerInterfaceWindow::ControllerInterfaceWindow(QWidget* parent) : QDialog(parent) @@ -27,9 +27,9 @@ void ControllerInterfaceWindow::CreateMainLayout() connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); m_tab_widget = new QTabWidget(); -#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) - m_udpserver_widget = new CemuHookUDPServerWidget(); - m_tab_widget->addTab(m_udpserver_widget, tr("UDPServer")); // TODO: use GetWrappedWidget()? +#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(); diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h index 47d12a9199..792cc97c25 100644 --- a/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h +++ b/Source/Core/DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h @@ -8,8 +8,8 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" -#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) -class CemuHookUDPServerWidget; +#if defined(CIFACE_USE_DUALSHOCKUDPCLIENT) +class DualShockUDPClientWidget; #endif class QTabWidget; class QDialogButtonBox; @@ -26,7 +26,7 @@ private: QTabWidget* m_tab_widget; QDialogButtonBox* m_button_box; -#if defined(CIFACE_USE_CEMUHOOKUDPSERVER) - CemuHookUDPServerWidget* m_udpserver_widget; +#if defined(CIFACE_USE_DUALSHOCKUDPCLIENT) + DualShockUDPClientWidget* m_dsuclient_widget; #endif }; diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp similarity index 65% rename from Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp rename to Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp index 47ab3d71b7..965f2f6879 100644 --- a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.cpp +++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h" +#include "DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h" #include #include @@ -12,33 +12,33 @@ #include #include "Common/Config/Config.h" -#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" +#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h" -CemuHookUDPServerWidget::CemuHookUDPServerWidget() +DualShockUDPClientWidget::DualShockUDPClientWidget() { CreateWidgets(); ConnectWidgets(); } -void CemuHookUDPServerWidget::CreateWidgets() +void DualShockUDPClientWidget::CreateWidgets() { auto* main_layout = new QGridLayout; m_server_enabled = new QCheckBox(tr("Enable")); - m_server_enabled->setChecked(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED)); + m_server_enabled->setChecked(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_ENABLED)); m_server_address = new QLineEdit( - QString::fromStdString(Config::Get(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS))); + 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::CemuHookUDPServer::Settings::SERVER_PORT)); + m_server_port->setValue(Config::Get(ciface::DualShockUDPClient::Settings::SERVER_PORT)); auto* description = - new QLabel(tr("UDPServer protocol enables the use of input and motion data from compatible " + new QLabel(tr("DSU protocol enables the use of input and motion data from compatible " "sources, like PlayStation, Nintendo Switch and Steam controllers.

" "For setup instructions, " - "" + "" "refer to this page.")); description->setTextFormat(Qt::RichText); description->setWordWrap(true); @@ -55,21 +55,21 @@ void CemuHookUDPServerWidget::CreateWidgets() setLayout(main_layout); } -void CemuHookUDPServerWidget::ConnectWidgets() +void DualShockUDPClientWidget::ConnectWidgets() { connect(m_server_enabled, &QCheckBox::toggled, this, [this] { - Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ENABLED, + Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ENABLED, m_server_enabled->isChecked()); }); connect(m_server_address, &QLineEdit::editingFinished, this, [this] { - Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_ADDRESS, + Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_ADDRESS, m_server_address->text().toStdString()); }); connect(m_server_port, static_cast(&QSpinBox::valueChanged), this, [this] { - Config::SetBaseOrCurrent(ciface::CemuHookUDPServer::Settings::SERVER_PORT, + Config::SetBaseOrCurrent(ciface::DualShockUDPClient::Settings::SERVER_PORT, static_cast(m_server_port->value())); }); } diff --git a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h similarity index 79% rename from Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h rename to Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h index 24a0db18ac..22e5a88011 100644 --- a/Source/Core/DolphinQt/Config/ControllerInterface/CemuHookUDPServerWidget.h +++ b/Source/Core/DolphinQt/Config/ControllerInterface/DualShockUDPClientWidget.h @@ -10,11 +10,11 @@ class QCheckBox; class QLineEdit; class QSpinBox; -class CemuHookUDPServerWidget final : public QWidget +class DualShockUDPClientWidget final : public QWidget { Q_OBJECT public: - explicit CemuHookUDPServerWidget(); + explicit DualShockUDPClientWidget(); private: void CreateWidgets(); diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 90b334266a..13922e2894 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -106,7 +106,7 @@ - + @@ -189,7 +189,7 @@ - + @@ -295,7 +295,7 @@ - + diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 38f2e03d2f..b1420155f6 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -43,9 +43,9 @@ add_library(inputcommon ControllerEmu/ControlGroup/Triggers.h ControllerEmu/Setting/NumericSetting.cpp ControllerEmu/Setting/NumericSetting.h - ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp - ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h - ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h + ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp + ControllerInterface/DualShockUDPClient/DualShockUDPClient.h + ControllerInterface/DualShockUDPClient/DualShockUDPProto.h ControllerInterface/ControllerInterface.cpp ControllerInterface/ControllerInterface.h ControllerInterface/Device.cpp diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 5f20fd475b..35dfbde0be 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -30,8 +30,8 @@ #ifdef CIFACE_USE_PIPES #include "InputCommon/ControllerInterface/Pipes/Pipes.h" #endif -#ifdef CIFACE_USE_CEMUHOOKUDPSERVER -#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" +#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT +#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h" #endif ControllerInterface g_controller_interface; @@ -71,8 +71,8 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) #ifdef CIFACE_USE_PIPES // nothing needed #endif -#ifdef CIFACE_USE_CEMUHOOKUDPSERVER - ciface::CemuHookUDPServer::Init(); +#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT + ciface::DualShockUDPClient::Init(); #endif RefreshDevices(); @@ -128,8 +128,8 @@ void ControllerInterface::RefreshDevices() #ifdef CIFACE_USE_PIPES ciface::Pipes::PopulateDevices(); #endif -#ifdef CIFACE_USE_CEMUHOOKUDPSERVER - ciface::CemuHookUDPServer::PopulateDevices(); +#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT + ciface::DualShockUDPClient::PopulateDevices(); #endif m_is_populating_devices = false; @@ -181,8 +181,8 @@ void ControllerInterface::Shutdown() #ifdef CIFACE_USE_EVDEV ciface::evdev::Shutdown(); #endif -#ifdef CIFACE_USE_CEMUHOOKUDPSERVER - ciface::CemuHookUDPServer::DeInit(); +#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT + ciface::DualShockUDPClient::DeInit(); #endif } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index f1a8e0c19e..359bd8f0c3 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -29,7 +29,7 @@ #if defined(USE_PIPES) #define CIFACE_USE_PIPES #endif -#define CIFACE_USE_CEMUHOOKUDPSERVER +#define CIFACE_USE_DUALSHOCKUDPCLIENT // // ControllerInterface diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp similarity index 92% rename from Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp rename to Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp index 36cd148041..60086d517c 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h" +#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h" #include #include @@ -19,10 +19,10 @@ #include "Common/Thread.h" #include "Core/CoreTiming.h" #include "Core/HW/SystemTimers.h" -#include "InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h" -namespace ciface::CemuHookUDPServer +namespace ciface::DualShockUDPClient { class Device : public Core::Device { @@ -119,10 +119,10 @@ static constexpr int TOUCH_Y_AXIS_MAX = 500; namespace Settings { const Config::ConfigInfo SERVER_ENABLED{ - {Config::System::CemuHookUdpServer, "Server", "Enabled"}, false}; + {Config::System::DualShockUDPClient, "Server", "Enabled"}, false}; const Config::ConfigInfo SERVER_ADDRESS{ - {Config::System::CemuHookUdpServer, "Server", "IPAddress"}, DEFAULT_SERVER_ADDRESS}; -const Config::ConfigInfo SERVER_PORT{{Config::System::CemuHookUdpServer, "Server", "Port"}, + {Config::System::DualShockUDPClient, "Server", "IPAddress"}, DEFAULT_SERVER_ADDRESS}; +const Config::ConfigInfo SERVER_PORT{{Config::System::DualShockUDPClient, "Server", "Port"}, DEFAULT_SERVER_PORT}; } // namespace Settings @@ -160,8 +160,8 @@ static sf::Socket::Status ReceiveWithTimeout(sf::UdpSocket& socket, void* data, static void HotplugThreadFunc() { - Common::SetCurrentThreadName("CemuHookUDPServer Hotplug Thread"); - NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer hotplug thread started"); + Common::SetCurrentThreadName("DualShockUDPClient Hotplug Thread"); + NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient hotplug thread started"); while (s_hotplug_thread_running.IsSet()) { @@ -181,7 +181,7 @@ static void HotplugThreadFunc() msg.Finish(); if (s_socket.send(&list_ports, sizeof list_ports, s_server_address, s_server_port) != sf::Socket::Status::Done) - ERROR_LOG(SERIALINTERFACE, "CemuHookUDPServer HotplugThreadFunc send failed"); + ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient HotplugThreadFunc send failed"); } // Receive controller port info @@ -208,7 +208,7 @@ static void HotplugThreadFunc() } } } - NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer hotplug thread stopped"); + NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient hotplug thread stopped"); } static void StartHotplugThread() @@ -238,7 +238,7 @@ static void StopHotplugThread() static void Restart() { - NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer Restart"); + NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient Restart"); StopHotplugThread(); @@ -278,10 +278,10 @@ void Init() void PopulateDevices() { - NOTICE_LOG(SERIALINTERFACE, "CemuHookUDPServer PopulateDevices"); + NOTICE_LOG(SERIALINTERFACE, "DualShockUDPClient PopulateDevices"); g_controller_interface.RemoveDevice( - [](const auto* dev) { return dev->GetSource() == "UDPServer"; }); + [](const auto* dev) { return dev->GetSource() == "DSUClient"; }); std::lock_guard lock(s_port_info_mutex); for (int port_index = 0; port_index < Proto::PORT_COUNT; port_index++) @@ -374,7 +374,7 @@ std::string Device::GetName() const std::string Device::GetSource() const { - return "UDPServer"; + return "DSUClient"; } void Device::UpdateInput() @@ -392,7 +392,7 @@ void Device::UpdateInput() msg.Finish(); if (m_socket.send(&data_req, sizeof(data_req), s_server_address, s_server_port) != sf::Socket::Status::Done) - ERROR_LOG(SERIALINTERFACE, "CemuHookUDPServer UpdateInput send failed"); + ERROR_LOG(SERIALINTERFACE, "DualShockUDPClient UpdateInput send failed"); } // Receive and handle controller data @@ -441,4 +441,4 @@ std::optional Device::GetPreferredId() const return m_index; } -} // namespace ciface::CemuHookUDPServer +} // namespace ciface::DualShockUDPClient diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h similarity index 83% rename from Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h rename to Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h index f7eaa34407..b4bb1210c6 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServer.h +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPClient.h @@ -4,7 +4,7 @@ #include "Common/Config/Config.h" -namespace ciface::CemuHookUDPServer +namespace ciface::DualShockUDPClient { namespace Settings { @@ -16,4 +16,4 @@ extern const Config::ConfigInfo SERVER_PORT; void Init(); void PopulateDevices(); void DeInit(); -} // namespace ciface::CemuHookUDPServer +} // namespace ciface::DualShockUDPClient diff --git a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h similarity index 95% rename from Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h rename to Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h index 60c8059292..45980bcd5d 100644 --- a/Source/Core/InputCommon/ControllerInterface/CemuHookUDPServer/CemuHookUDPServerProto.h +++ b/Source/Core/InputCommon/ControllerInterface/DualShockUDPClient/DualShockUDPProto.h @@ -8,9 +8,9 @@ #include "Common/CommonTypes.h" -namespace ciface::CemuHookUDPServer::Proto +namespace ciface::DualShockUDPClient::Proto { -// CemuHook UDPServer protocol implementation using UdpServer.cs from +// CemuHook DualShockUDP protocol implementation using UdpServer.cs from // https://github.com/Ryochan7/DS4Windows as documentation. // // WARNING: Little endian host assumed @@ -245,7 +245,7 @@ struct Message if (crc32_in_header != crc32_calculated) { NOTICE_LOG(SERIALINTERFACE, - "CemuHookUDPServer Received message with bad CRC in header: got %u, expected %u", + "DualShockUDPClient Received message with bad CRC in header: got %u, expected %u", crc32_in_header, crc32_calculated); return std::nullopt; } @@ -267,4 +267,4 @@ struct Message }; #pragma pack(pop) -} // namespace ciface::CemuHookUDPServer::Proto +} // namespace ciface::DualShockUDPClient::Proto diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index e3d252cb5f..262ba2f52e 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -56,13 +56,13 @@ - + @@ -100,8 +100,6 @@ - - @@ -109,6 +107,8 @@ + + diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index ca29eec811..0c8d79326f 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -28,8 +28,8 @@ {07bad1aa-7e03-4f5c-ade2-a44857c5cbc3} - - {4f71c21c-85eb-4e76-ad64-40fd97bc3d6d} + + {ff02580e-3a62-4de4-a135-3a6c2c339a90} @@ -126,15 +126,15 @@ ControllerEmu\ControlGroup - - ControllerInterface\CemuHookUDPServer - ControllerEmu\ControlGroup ControllerEmu\ControlGroup + + ControllerInterface\DualShockUDPClient + @@ -224,9 +224,6 @@ ControllerInterface - - ControllerInterface - ControllerEmu\ControlGroup @@ -234,17 +231,18 @@ ControllerEmu\ControlGroup - - ControllerInterface\CemuHookUDPServer - ControllerEmu\ControlGroup ControllerEmu\ControlGroup - - ControllerInterface\CemuHookUDPServer + + + ControllerInterface\DualShockUDPClient + + + ControllerInterface\DualShockUDPClient