diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp index adccc12ea5..4c1f230c4b 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingCommon.cpp @@ -12,7 +12,6 @@ namespace MappingCommon { - constexpr int INPUT_DETECT_TIME = 3000; constexpr int OUTPUT_DETECT_TIME = 2000; diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp index 9968bc798f..6de1f97d7e 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp @@ -150,6 +150,8 @@ Joystick::Joystick(/*const LPCDIDEVICEINSTANCE lpddi, */ const LPDIRECTINPUTDEVI Joystick::~Joystick() { + DeInitForceFeedback(); + m_device->Unacquire(); m_device->Release(); } @@ -265,5 +267,5 @@ ControlState Joystick::Hat::GetState() const return (abs((int)(m_hat / 4500 - m_direction * 2 + 8) % 8 - 4) > 2); } -} -} +} // namespace DInput +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp index 34d0ed018b..3f8df41419 100644 --- a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp @@ -3,23 +3,25 @@ // Refer to the license.txt file included. #include "InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h" + #include #include + #include "Common/Thread.h" namespace ciface { namespace ForceFeedback { -// template instantiation -template class ForceFeedbackDevice::Force; -template class ForceFeedbackDevice::Force; -template class ForceFeedbackDevice::Force; +// Template instantiation: +template class ForceFeedbackDevice::TypedForce; +template class ForceFeedbackDevice::TypedForce; +template class ForceFeedbackDevice::TypedForce; struct ForceType { GUID guid; - const std::string name; + const char* name; }; static const ForceType force_type_names[] = { @@ -36,6 +38,42 @@ static const ForceType force_type_names[] = { //{GUID_Friction, "Friction"}, }; +void ForceFeedbackDevice::DeInitForceFeedback() +{ + if (!m_run_thread.TestAndClear()) + return; + + SignalUpdateThread(); + m_update_thread.join(); +} + +void ForceFeedbackDevice::ThreadFunc() +{ + Common::SetCurrentThreadName("ForceFeedback update thread"); + + while (m_run_thread.IsSet()) + { + m_update_event.Wait(); + + for (auto output : Outputs()) + { + auto& force = *static_cast(output); + force.UpdateOutput(); + } + } + + for (auto output : Outputs()) + { + auto& force = *static_cast(output); + force.Release(); + } +} + +void ForceFeedbackDevice::SignalUpdateThread() +{ + m_update_event.Set(); +} + bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, int cAxes) { if (cAxes == 0) @@ -43,14 +81,14 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i // TODO: check for DIDC_FORCEFEEDBACK in devcaps? - // temporary + // Temporary for creating the effect: DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y}; LONG rglDirection[2] = {-200, 0}; - DIEFFECT eff; - memset(&eff, 0, sizeof(eff)); + DIEFFECT eff{}; eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + // Infinite seems to work just fine: eff.dwDuration = INFINITE; // (4 * DI_SECONDS) eff.dwSamplePeriod = 0; eff.dwGain = DI_FFNOMINALMAX; @@ -60,19 +98,19 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; - // initialize parameters - DICONSTANTFORCE diCF = {-10000}; + // Initialize parameters. + DICONSTANTFORCE diCF{}; diCF.lMagnitude = DI_FFNOMINALMAX; - DIRAMPFORCE diRF = {0}; - DIPERIODIC diPE = {0}; + DIRAMPFORCE diRF{}; + DIPERIODIC diPE{}; - // doesn't seem needed + // This doesn't seem needed: // DIENVELOPE env; // eff.lpEnvelope = &env; // ZeroMemory(&env, sizeof(env)); // env.dwSize = sizeof(env); - for (const ForceType& f : force_type_names) + for (auto& f : force_type_names) { if (f.guid == GUID_ConstantForce) { @@ -86,7 +124,7 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i } else { - // all other forces need periodic parameters + // All other forces need periodic parameters: eff.cbTypeSpecificParams = sizeof(DIPERIODIC); eff.lpvTypeSpecificParams = &diPE; } @@ -95,15 +133,15 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i if (SUCCEEDED(device->CreateEffect(f.guid, &eff, &pEffect, nullptr))) { if (f.guid == GUID_ConstantForce) - AddOutput(new ForceConstant(f.name, pEffect)); + AddOutput(new ForceConstant(this, f.name, pEffect)); else if (f.guid == GUID_RampForce) - AddOutput(new ForceRamp(f.name, pEffect)); + AddOutput(new ForceRamp(this, f.name, pEffect)); else - AddOutput(new ForcePeriodic(f.name, pEffect)); + AddOutput(new ForcePeriodic(this, f.name, pEffect)); } } - // disable autocentering + // Disable autocentering: if (Outputs().size()) { DIPROPDWORD dipdw; @@ -113,95 +151,114 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i dipdw.diph.dwHow = DIPH_DEVICE; dipdw.dwData = DIPROPAUTOCENTER_OFF; device->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph); + + m_run_thread.Set(); + m_update_thread = std::thread(&ForceFeedbackDevice::ThreadFunc, this); } return true; } template -ForceFeedbackDevice::Force

::~Force() +void ForceFeedbackDevice::TypedForce

::PlayEffect() { - m_iface->Stop(); - m_iface->Unload(); - m_iface->Release(); -} - -template -void ForceFeedbackDevice::Force

::Update() -{ - DIEFFECT eff = {}; + DIEFFECT eff{}; eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - eff.cbTypeSpecificParams = sizeof(P); - eff.lpvTypeSpecificParams = ¶ms; - - // set params and start effect - m_iface->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START); + eff.cbTypeSpecificParams = sizeof(m_params); + eff.lpvTypeSpecificParams = &m_params; + m_effect->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START); } template -void ForceFeedbackDevice::Force

::Stop() +void ForceFeedbackDevice::TypedForce

::StopEffect() { - m_iface->Stop(); + m_effect->Stop(); } template <> -void ForceFeedbackDevice::ForceConstant::SetState(const ControlState state) +bool ForceFeedbackDevice::ForceConstant::UpdateParameters(int magnitude) { - const LONG new_val = LONG(10000 * state); + const auto old_magnitude = m_params.lMagnitude; - if (params.lMagnitude == new_val) - return; + m_params.lMagnitude = magnitude; - params.lMagnitude = new_val; - if (new_val) - Update(); - else - Stop(); + return old_magnitude != m_params.lMagnitude; } template <> -void ForceFeedbackDevice::ForceRamp::SetState(const ControlState state) +bool ForceFeedbackDevice::ForceRamp::UpdateParameters(int magnitude) { - const LONG new_val = LONG(10000 * state); + const auto old_magnitude = m_params.lStart; - if (params.lStart == new_val) - return; + m_params.lStart = m_params.lEnd = magnitude; - params.lStart = params.lEnd = new_val; - if (new_val) - Update(); - else - Stop(); + return old_magnitude != m_params.lStart; } template <> -void ForceFeedbackDevice::ForcePeriodic::SetState(const ControlState state) +bool ForceFeedbackDevice::ForcePeriodic::UpdateParameters(int magnitude) { - const DWORD new_val = DWORD(10000 * state); + const auto old_magnitude = m_params.dwMagnitude; - if (params.dwMagnitude == new_val) - return; + m_params.dwMagnitude = magnitude; + // Zero is working fine for me: + // params.dwPeriod = 0;//DWORD(0.05 * DI_SECONDS); - params.dwMagnitude = new_val; - if (new_val) - Update(); - else - Stop(); + return old_magnitude != m_params.dwMagnitude; } template -ForceFeedbackDevice::Force

::Force(const std::string& name, LPDIRECTINPUTEFFECT iface) - : m_name(name), m_iface(iface) +ForceFeedbackDevice::TypedForce

::TypedForce(ForceFeedbackDevice* parent, std::string name, + LPDIRECTINPUTEFFECT effect) + : Force(parent, std::move(name), effect), m_params{} { - memset(¶ms, 0, sizeof(params)); } template -std::string ForceFeedbackDevice::Force

::GetName() const +void ForceFeedbackDevice::TypedForce

::UpdateEffect(int magnitude) +{ + if (UpdateParameters(magnitude)) + { + if (magnitude) + PlayEffect(); + else + StopEffect(); + } +} + +std::string ForceFeedbackDevice::Force::GetName() const { return m_name; } + +ForceFeedbackDevice::Force::Force(ForceFeedbackDevice* parent, const std::string name, + LPDIRECTINPUTEFFECT effect) + : m_effect(effect), m_parent(*parent), m_name(std::move(name)), m_desired_magnitude() +{ } + +void ForceFeedbackDevice::Force::SetState(ControlState state) +{ + const auto new_val = int(DI_FFNOMINALMAX * state); + + if (m_desired_magnitude.exchange(new_val) != new_val) + m_parent.SignalUpdateThread(); } + +void ForceFeedbackDevice::Force::UpdateOutput() +{ + UpdateEffect(m_desired_magnitude); +} + +void ForceFeedbackDevice::Force::Release() +{ + // This isn't in the destructor because it should happen before the device is released. + m_effect->Stop(); + m_effect->Unload(); + m_effect->Release(); +} + +} // namespace ForceFeedback +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h index 5994bf2547..ca1ad6647a 100644 --- a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h @@ -4,9 +4,12 @@ #pragma once -#include +#include #include +#include +#include "Common/Event.h" +#include "Common/Flag.h" #include "InputCommon/ControllerInterface/Device.h" #ifdef _WIN32 @@ -22,30 +25,63 @@ namespace ForceFeedback { class ForceFeedbackDevice : public Core::Device { +public: + bool InitForceFeedback(const LPDIRECTINPUTDEVICE8, int cAxes); + void DeInitForceFeedback(); + private: - template + void ThreadFunc(); + class Force : public Output { public: - Force(const std::string& name, LPDIRECTINPUTEFFECT iface); - ~Force(); + Force(ForceFeedbackDevice* parent, const std::string name, LPDIRECTINPUTEFFECT effect); + + void UpdateOutput(); + void Release(); - std::string GetName() const override; void SetState(ControlState state) override; - void Update(); - void Stop(); + std::string GetName() const override; + + protected: + const LPDIRECTINPUTEFFECT m_effect; private: - const std::string m_name; - LPDIRECTINPUTEFFECT m_iface; - P params; - }; - typedef Force ForceConstant; - typedef Force ForceRamp; - typedef Force ForcePeriodic; + virtual void UpdateEffect(int magnitude) = 0; -public: - bool InitForceFeedback(const LPDIRECTINPUTDEVICE8, int cAxes); + ForceFeedbackDevice& m_parent; + const std::string m_name; + std::atomic m_desired_magnitude; + }; + + template + class TypedForce : public Force + { + public: + TypedForce(ForceFeedbackDevice* parent, const std::string name, LPDIRECTINPUTEFFECT effect); + + private: + void UpdateEffect(int magnitude) override; + + // Returns true if parameters changed. + bool UpdateParameters(int magnitude); + + void PlayEffect(); + void StopEffect(); + + P m_params = {}; + }; + + void SignalUpdateThread(); + + typedef TypedForce ForceConstant; + typedef TypedForce ForceRamp; + typedef TypedForce ForcePeriodic; + + std::thread m_update_thread; + Common::Event m_update_event; + Common::Flag m_run_thread; }; -} -} + +} // namespace ForceFeedback +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm index ecbef721a5..bfe2f27505 100644 --- a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm @@ -99,6 +99,8 @@ Joystick::Joystick(IOHIDDeviceRef device, std::string name) Joystick::~Joystick() { + DeInitForceFeedback(); + if (m_ff_device) m_ff_device->Release(); }