diff options
| author | Jules Blok <jules.blok@gmail.com> | 2014-02-05 19:28:32 +0900 |
|---|---|---|
| committer | Jules Blok <jules.blok@gmail.com> | 2014-02-09 17:01:38 +0900 |
| commit | 02a95c139e5ff56ceca903614b74dba73bf88fc5 (patch) | |
| tree | 202c2cb70182a36d14772b975c3efc241a6718ad /Source/Core/InputCommon/ControllerInterface/ForceFeedback | |
| parent | 249b00c4691fb4dbd9e1c833ceccc53526babd21 (diff) | |
ControllerInterface: Move DInput ForceFeedback support to a seperate class
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface/ForceFeedback')
| -rw-r--r-- | Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp | 227 | ||||
| -rw-r--r-- | Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h | 65 |
2 files changed, 292 insertions, 0 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp new file mode 100644 index 0000000000..34b44acf99 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp @@ -0,0 +1,227 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "ForceFeedbackDevice.h" + +namespace ciface +{ +namespace ForceFeedback +{ + +// template instantiation +template class ForceFeedbackDevice::Force<DICONSTANTFORCE>; +template class ForceFeedbackDevice::Force<DIRAMPFORCE>; +template class ForceFeedbackDevice::Force<DIPERIODIC>; + +static const struct +{ + GUID guid; + const char* name; +} force_type_names[] = +{ + {GUID_ConstantForce, "Constant"}, // DICONSTANTFORCE + {GUID_RampForce, "Ramp"}, // DIRAMPFORCE + {GUID_Square, "Square"}, // DIPERIODIC ... + {GUID_Sine, "Sine"}, + {GUID_Triangle, "Triangle"}, + {GUID_SawtoothUp, "Sawtooth Up"}, + {GUID_SawtoothDown, "Sawtooth Down"}, + //{GUID_Spring, "Spring"}, // DICUSTOMFORCE ... < I think + //{GUID_Damper, "Damper"}, + //{GUID_Inertia, "Inertia"}, + //{GUID_Friction, "Friction"}, +}; + +ForceFeedbackDevice::~ForceFeedbackDevice() +{ + // release the ff effect iface's + for (EffectState& state : m_state_out) + { + state.iface->Stop(); + state.iface->Unload(); + state.iface->Release(); + } +} + +bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, int cAxes) +{ + if (cAxes == 0) + return false; + + // TODO: check for DIDC_FORCEFEEDBACK in devcaps? + + // temporary + DWORD rgdwAxes[2] = {DIJOFS_X, DIJOFS_Y}; + LONG rglDirection[2] = {-200, 0}; + + DIEFFECT eff; + ZeroMemory(&eff, sizeof(eff)); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + eff.dwDuration = INFINITE; // (4 * DI_SECONDS) + eff.dwSamplePeriod = 0; + eff.dwGain = DI_FFNOMINALMAX; + eff.dwTriggerButton = DIEB_NOTRIGGER; + eff.dwTriggerRepeatInterval = 0; + eff.cAxes = std::min((DWORD)1, (DWORD)cAxes); + eff.rgdwAxes = rgdwAxes; + eff.rglDirection = rglDirection; + + // DIPERIODIC is the largest, so we'll use that + DIPERIODIC ff; + eff.lpvTypeSpecificParams = &ff; + ZeroMemory(&ff, sizeof(ff)); + + // doesn't seem needed + //DIENVELOPE env; + //eff.lpEnvelope = &env; + //ZeroMemory(&env, sizeof(env)); + //env.dwSize = sizeof(env); + + for (unsigned int f = 0; f < sizeof(force_type_names)/sizeof(*force_type_names); ++f) + { + // ugly if ladder + if (0 == f) + { + DICONSTANTFORCE diCF = {-10000}; + diCF.lMagnitude = DI_FFNOMINALMAX; + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &diCF; + } + else if (1 == f) + { + eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + } + else + { + eff.cbTypeSpecificParams = sizeof(DIPERIODIC); + } + + LPDIRECTINPUTEFFECT pEffect; + if (SUCCEEDED(device->CreateEffect(force_type_names[f].guid, &eff, &pEffect, NULL))) + { + m_state_out.push_back(EffectState(pEffect)); + + // ugly if ladder again :/ + if (0 == f) + AddOutput(new ForceConstant(f, m_state_out.back())); + else if (1 == f) + AddOutput(new ForceRamp(f, m_state_out.back())); + else + AddOutput(new ForcePeriodic(f, m_state_out.back())); + } + } + + // disable autocentering + if (Outputs().size()) + { + DIPROPDWORD dipdw; + dipdw.diph.dwSize = sizeof( DIPROPDWORD ); + dipdw.diph.dwHeaderSize = sizeof( DIPROPHEADER ); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = DIPROPAUTOCENTER_OFF; + device->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ); + } + + return true; +} + +bool ForceFeedbackDevice::UpdateOutput() +{ + size_t ok_count = 0; + + DIEFFECT eff; + ZeroMemory(&eff, sizeof(eff)); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + + for (EffectState& state : m_state_out) + { + if (state.params) + { + if (state.size) + { + eff.cbTypeSpecificParams = state.size; + eff.lpvTypeSpecificParams = state.params; + // set params and start effect + ok_count += SUCCEEDED(state.iface->SetParameters(&eff, DIEP_TYPESPECIFICPARAMS | DIEP_START)); + } + else + { + ok_count += SUCCEEDED(state.iface->Stop()); + } + + state.params = NULL; + } + else + { + ++ok_count; + } + } + + return (m_state_out.size() == ok_count); +} + +void ForceFeedbackDevice::ForceConstant::SetState(const ControlState state) +{ + const LONG new_val = LONG(10000 * state); + + LONG &val = params.lMagnitude; + if (val != new_val) + { + val = new_val; + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +void ForceFeedbackDevice::ForceRamp::SetState(const ControlState state) +{ + const LONG new_val = LONG(10000 * state); + + if (params.lStart != new_val) + { + params.lStart = params.lEnd = new_val; + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +void ForceFeedbackDevice::ForcePeriodic::SetState(const ControlState state) +{ + const DWORD new_val = DWORD(10000 * state); + + DWORD &val = params.dwMagnitude; + if (val != new_val) + { + val = new_val; + //params.dwPeriod = 0;//DWORD(0.05 * DI_SECONDS); // zero is working fine for me + + m_state.params = ¶ms; // tells UpdateOutput the state has changed + + // tells UpdateOutput to either start or stop the force + m_state.size = new_val ? sizeof(params) : 0; + } +} + +template <typename P> +ForceFeedbackDevice::Force<P>::Force(u8 index, EffectState& state) +: m_index(index), m_state(state) +{ + ZeroMemory(¶ms, sizeof(params)); +} + +template <typename P> +std::string ForceFeedbackDevice::Force<P>::GetName() const +{ + return force_type_names[m_index].name; +} + +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h new file mode 100644 index 0000000000..a9128dbd1a --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h @@ -0,0 +1,65 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _FORCEFEEDBACKDEVICE_H_ +#define _FORCEFEEDBACKDEVICE_H_ + +#include "../Device.h" + +#define DIRECTINPUT_VERSION 0x0800 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#include <dinput.h> + +#include <list> + +namespace ciface +{ +namespace ForceFeedback +{ + + +class ForceFeedbackDevice : public Core::Device +{ +private: + struct EffectState + { + EffectState(LPDIRECTINPUTEFFECT eff) : iface(eff), params(NULL), size(0) {} + + LPDIRECTINPUTEFFECT iface; + void* params; // null when force hasn't changed + u8 size; // zero when force should stop + }; + + template <typename P> + class Force : public Output + { + public: + std::string GetName() const; + Force(u8 index, EffectState& state); + void SetState(ControlState state); + private: + const u8 m_index; + EffectState& m_state; + P params; + }; + typedef Force<DICONSTANTFORCE> ForceConstant; + typedef Force<DIRAMPFORCE> ForceRamp; + typedef Force<DIPERIODIC> ForcePeriodic; + +public: + bool InitForceFeedback(const LPDIRECTINPUTDEVICE8, int cAxes); + bool UpdateOutput(); + + virtual ~ForceFeedbackDevice(); +private: + std::list<EffectState> m_state_out; +}; + + +} +} + +#endif |
