diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b5d555ffa..c96f87ceb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,6 +242,7 @@ if(APPLE) find_library(IOK_LIBRARY IOKit) find_library(QUICKTIME_LIBRARY QuickTime) find_library(WEBKIT_LIBRARY WebKit) + find_library(FORCEFEEDBACK ForceFeedback) endif() if(WIN32) diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 1e01553dcd..a47862da18 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -141,6 +141,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ${CORESERV_LIBRARY} ${IOB_LIBRARY} ${IOK_LIBRARY} + ${FORCEFEEDBACK} ) if(wxWidgets_FOUND) list(APPEND LIBS diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 4c9858fc25..cf112c9594 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -12,13 +12,15 @@ if(WIN32) ControllerInterface/DInput/DInputJoystick.cpp ControllerInterface/DInput/DInputKeyboardMouse.cpp ControllerInterface/SDL/SDL.cpp - ControllerInterface/XInput/XInput.cpp) + ControllerInterface/XInput/XInput.cpp + ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(SRCS ${SRCS} ControllerInterface/OSX/OSX.mm ControllerInterface/OSX/OSXKeyboard.mm ControllerInterface/OSX/OSXJoystick.mm - ControllerInterface/SDL/SDL.cpp) + ControllerInterface/SDL/SDL.cpp + ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp) elseif(X11_FOUND) set(SRCS ${SRCS} ControllerInterface/SDL/SDL.cpp diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp index a8d1ba53c8..89500b2b2f 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp @@ -14,30 +14,6 @@ namespace ciface namespace DInput { -// template instantiation -template class Joystick::Force; -template class Joystick::Force; -template class Joystick::Force; - -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"}, -}; - #define DATA_BUFFER_SIZE 32 //----------------------------------------------------------------------------- @@ -267,87 +243,11 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI } } - // TODO: check for DIDC_FORCEFEEDBACK in devcaps? - - // get supported ff effects + // force feedback std::list objects; - m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS); - // got some ff axes or something - if ( objects.size() ) + if (SUCCEEDED(m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS))) { - // 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)objects.size()); - eff.rgdwAxes = rgdwAxes; - eff.rglDirection = rglDirection; - - // DIPERIODIC is the largest, so we'll use that - DIPERIODIC f; - eff.lpvTypeSpecificParams = &f; - ZeroMemory(&f, sizeof(f)); - - // 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(m_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; - m_device->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ); + InitForceFeedback(m_device, objects.size()); } ClearInputState(); @@ -355,14 +255,6 @@ Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVI Joystick::~Joystick() { - // release the ff effect iface's - for (EffectState& state : m_state_out) - { - state.iface->Stop(); - state.iface->Unload(); - state.iface->Release(); - } - m_device->Unacquire(); m_device->Release(); } @@ -434,42 +326,6 @@ bool Joystick::UpdateInput() return SUCCEEDED(hr); } -bool Joystick::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); -} - // get name std::string Joystick::Button::GetName() const @@ -507,12 +363,6 @@ std::string Joystick::Hat::GetName() const return tmpstr; } -template -std::string Joystick::Force

::GetName() const -{ - return force_type_names[m_index].name; -} - // get / set state ControlState Joystick::Axis::GetState() const @@ -535,58 +385,5 @@ ControlState Joystick::Hat::GetState() const return (abs((int)(m_hat / 4500 - m_direction * 2 + 8) % 8 - 4) > 2); } -void Joystick::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 Joystick::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 Joystick::ForcePeriodic::SetState(const ControlState state) -{ - const LONG new_val = LONG(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 -Joystick::Force

::Force(u8 index, EffectState& state) - : m_index(index), m_state(state) -{ - ZeroMemory(¶ms, sizeof(params)); -} - } } diff --git a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h index 6b6be58193..25fc745c8e 100644 --- a/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h +++ b/Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h @@ -2,14 +2,7 @@ #define _CIFACE_DINPUT_JOYSTICK_H_ #include "../Device.h" - -#define DIRECTINPUT_VERSION 0x0800 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#include - -#include +#include "../ForceFeedback/ForceFeedbackDevice.h" namespace ciface { @@ -18,18 +11,9 @@ namespace DInput void InitJoystick(IDirectInput8* const idi8, std::vector& devices, HWND hwnd); -class Joystick : public Core::Device +class Joystick : public ForceFeedback::ForceFeedbackDevice { 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 - }; - class Button : public Input { public: @@ -64,25 +48,8 @@ private: const u8 m_index, m_direction; }; - template - class Force : public Output - { - public: - std::string GetName() const; - Force(u8 index, EffectState& state); - void SetState(ControlState state); - private: - EffectState& m_state; - P params; - const u8 m_index; - }; - typedef Force ForceConstant; - typedef Force ForceRamp; - typedef Force ForcePeriodic; - public: bool UpdateInput(); - bool UpdateOutput(); void ClearInputState(); @@ -98,7 +65,6 @@ private: const unsigned int m_index; DIJOYSTATE m_state_in; - std::list m_state_out; bool m_buffered; }; diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp new file mode 100644 index 0000000000..04f2bb505f --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp @@ -0,0 +1,232 @@ +// 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; +template class ForceFeedbackDevice::Force; +template class ForceFeedbackDevice::Force; + +typedef struct +{ + GUID guid; + const char* name; +} ForceType; + +static const ForceType 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; + memset(&eff, 0, 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; + + // initialize parameters + DICONSTANTFORCE diCF = { -10000 }; + diCF.lMagnitude = DI_FFNOMINALMAX; + DIRAMPFORCE diRF = { 0 }; + DIPERIODIC diPE = { 0 }; + + // doesn't seem needed + //DIENVELOPE env; + //eff.lpEnvelope = &env; + //ZeroMemory(&env, sizeof(env)); + //env.dwSize = sizeof(env); + + for (const ForceType& f : force_type_names) + { + if (f.guid == GUID_ConstantForce) + { + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &diCF; + } + else if (f.guid == GUID_RampForce) + { + eff.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + eff.lpvTypeSpecificParams = &diRF; + } + else + { + // all other forces need periodic parameters + eff.cbTypeSpecificParams = sizeof(DIPERIODIC); + eff.lpvTypeSpecificParams = &diPE; + } + + LPDIRECTINPUTEFFECT pEffect; + if (SUCCEEDED(device->CreateEffect(f.guid, &eff, &pEffect, NULL))) + { + m_state_out.push_back(EffectState(pEffect)); + + if (f.guid == GUID_ConstantForce) + AddOutput(new ForceConstant(f.name, m_state_out.back())); + else if (f.guid == GUID_RampForce) + AddOutput(new ForceRamp(f.name, m_state_out.back())); + else + AddOutput(new ForcePeriodic(f.name, 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; + memset(&eff, 0, 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); +} + +template<> +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; + } +} + +template<> +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; + } +} + +template<> +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 +ForceFeedbackDevice::Force

::Force(const char* name, EffectState& state) +: m_name(name), m_state(state) +{ + memset(¶ms, 0, sizeof(params)); +} + +template +std::string ForceFeedbackDevice::Force

::GetName() const +{ + return m_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..3035273fa8 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h @@ -0,0 +1,69 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _FORCEFEEDBACKDEVICE_H_ +#define _FORCEFEEDBACKDEVICE_H_ + +#include "../Device.h" + +#ifdef _WIN32 +#define DIRECTINPUT_VERSION 0x0800 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#elif __APPLE__ +#include "OSX/DirectInputAdapter.h" +#endif + +#include + +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 + class Force : public Output + { + public: + std::string GetName() const; + Force(const char* name, EffectState& state); + void SetState(ControlState state); + private: + const char* m_name; + EffectState& m_state; + P params; + }; + typedef Force ForceConstant; + typedef Force ForceRamp; + typedef Force ForcePeriodic; + +public: + bool InitForceFeedback(const LPDIRECTINPUTDEVICE8, int cAxes); + bool UpdateOutput(); + + virtual ~ForceFeedbackDevice(); +private: + std::list m_state_out; +}; + + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputAdapter.h b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputAdapter.h new file mode 100644 index 0000000000..367af395ef --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputAdapter.h @@ -0,0 +1,224 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +/* + * The OS X Force Feedback API is very similar to the DirectInput API, + * but it is no longer object-oriented and all prefixes have been changed. + * + * Our implementation uses the Windows API names so we need to adapt + * for these differences on OS X. + */ + +#ifndef _DIRECTINPUTADAPTER_H_ +#define _DIRECTINPUTADAPTER_H_ + +typedef LONG* LPLONG; // Missing type for ForceFeedback.h +#include +#include +#include "DirectInputConstants.h" // Not stricty necessary + +namespace ciface +{ +namespace ForceFeedback +{ + + +// Prototypes +class IUnknownImpl; +class FFEffectAdapter; +class FFDeviceAdapter; + +// Structs +typedef FFCAPABILITIES DICAPABILITIES; +typedef FFCONDITION DICONDITION; +typedef FFCONSTANTFORCE DICONSTANTFORCE; +typedef FFCUSTOMFORCE DICUSTOMFORCE; +typedef FFEFFECT DIEFFECT; +typedef FFEFFESCAPE DIEFFESCAPE; +typedef FFENVELOPE DIENVELOPE; +typedef FFPERIODIC DIPERIODIC; +typedef FFRAMPFORCE DIRAMPFORCE; + +// Other types +typedef CFUUIDRef GUID; +typedef FFDeviceAdapter* FFDeviceAdapterReference; +typedef FFEffectAdapter* FFEffectAdapterReference; +typedef FFDeviceAdapterReference LPDIRECTINPUTDEVICE8; +typedef FFEffectAdapterReference LPDIRECTINPUTEFFECT; + +// Property structures +#define DIPH_DEVICE 0 + +typedef struct DIPROPHEADER { + DWORD dwSize; + DWORD dwHeaderSize; + DWORD dwObj; + DWORD dwHow; +} DIPROPHEADER, *LPDIPROPHEADER; + +typedef struct DIPROPDWORD { + DIPROPHEADER diph; + DWORD dwData; +} DIPROPDWORD, *LPDIPROPDWORD; + +class IUnknownImpl : public IUnknown +{ +private: + std::atomic m_cRef; + +public: + IUnknownImpl() : m_cRef(1) {} + virtual ~IUnknownImpl() {} + + HRESULT QueryInterface(REFIID iid, LPVOID *ppv) + { + *ppv = NULL; + + if (CFEqual(&iid, IUnknownUUID)) + *ppv = this; + if (NULL == *ppv) + return E_NOINTERFACE; + + ((IUnknown*)*ppv)->AddRef(); + + return S_OK; + } + + ULONG AddRef() + { + return ++m_cRef; + } + + ULONG Release() + { + if (--m_cRef == 0) + delete this; + + return m_cRef; + } +}; + +class FFEffectAdapter : public IUnknownImpl +{ +private: + // Only used for destruction + FFDeviceObjectReference m_device; + +public: + FFEffectObjectReference m_effect; + + FFEffectAdapter(FFDeviceObjectReference device, FFEffectObjectReference effect) : m_device(device), m_effect(effect) {} + ~FFEffectAdapter() { FFDeviceReleaseEffect(m_device, m_effect); } + + HRESULT Download() + { + return FFEffectDownload(m_effect); + } + + HRESULT Escape(FFEFFESCAPE *pFFEffectEscape) + { + return FFEffectEscape(m_effect, pFFEffectEscape); + } + + HRESULT GetEffectStatus(FFEffectStatusFlag *pFlags) + { + return FFEffectGetEffectStatus(m_effect, pFlags); + } + + HRESULT GetParameters(FFEFFECT *pFFEffect, FFEffectParameterFlag flags) + { + return FFEffectGetParameters(m_effect, pFFEffect, flags); + } + + HRESULT SetParameters(FFEFFECT *pFFEffect, FFEffectParameterFlag flags) + { + return FFEffectSetParameters(m_effect, pFFEffect, flags); + } + + HRESULT Start(UInt32 iterations, FFEffectStartFlag flags) + { + return FFEffectStart(m_effect, iterations, flags); + } + + HRESULT Stop() + { + return FFEffectStop(m_effect); + } + + HRESULT Unload() + { + return FFEffectUnload(m_effect); + } +}; + +class FFDeviceAdapter : public IUnknownImpl +{ +public: + FFDeviceObjectReference m_device; + + FFDeviceAdapter(FFDeviceObjectReference device) : m_device(device) {} + ~FFDeviceAdapter() { FFReleaseDevice(m_device); } + + static HRESULT Create(io_service_t hidDevice, FFDeviceAdapterReference *pDeviceReference) + { + FFDeviceObjectReference ref; + + HRESULT hr = FFCreateDevice(hidDevice, &ref); + if(SUCCEEDED(hr)) + *pDeviceReference = new FFDeviceAdapter(ref); + + return hr; + } + + HRESULT CreateEffect(CFUUIDRef uuidRef, FFEFFECT *pEffectDefinition, FFEffectAdapterReference *pEffectReference, IUnknown *punkOuter) + { + FFEffectObjectReference ref; + + HRESULT hr = FFDeviceCreateEffect(m_device, uuidRef, pEffectDefinition, &ref); + if(SUCCEEDED(hr)) + *pEffectReference = new FFEffectAdapter(m_device, ref); + + return hr; + } + + HRESULT Escape(FFEFFESCAPE *pFFEffectEscape) + { + return FFDeviceEscape(m_device, pFFEffectEscape); + } + + HRESULT GetForceFeedbackState(FFState *pFFState) + { + return FFDeviceGetForceFeedbackState(m_device, pFFState); + } + + HRESULT SendForceFeedbackCommand(FFCommandFlag flags) + { + return FFDeviceSendForceFeedbackCommand(m_device, flags); + } + + HRESULT SetCooperativeLevel(void *taskIdentifier, FFCooperativeLevelFlag flags) + { + return FFDeviceSetCooperativeLevel(m_device, taskIdentifier, flags); + } + + HRESULT SetProperty(FFProperty property, const LPDIPROPHEADER pdiph) + { + // There are only two properties supported + if (property != DIPROP_FFGAIN && property != DIPROP_AUTOCENTER) + return DIERR_UNSUPPORTED; + + // And they are both device properties + if (pdiph->dwHow != DIPH_DEVICE) + return DIERR_INVALIDPARAM; + + UInt32 value = ((const LPDIPROPDWORD)pdiph)->dwData; + return FFDeviceSetForceFeedbackProperty(m_device, property, &value); + } +}; + + +} +} + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputConstants.h b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputConstants.h new file mode 100644 index 0000000000..3fdee28971 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputConstants.h @@ -0,0 +1,150 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _DIRECTINPUTCONSTANTS_H_ +#define _DIRECTINPUTCONSTANTS_H_ + +/* + * Define all constants from ForceFeedbackConstants.h with DirectInput prefixes. + * + * No effort was made to confirm if all definitions are actually supported by + * DirectInput, so some of these definitions may actually only exist on Mac OS X. + */ + +// UUIDs +#define GUID_ConstantForce kFFEffectType_ConstantForce_ID +#define GUID_CustomForce kFFEffectType_CustomForce_ID +#define GUID_Damper kFFEffectType_Damper_ID +#define GUID_Friction kFFEffectType_Friction_ID +#define GUID_Inertia kFFEffectType_Inertia_ID +#define GUID_RampForce kFFEffectType_RampForce_ID +#define GUID_SawtoothDown kFFEffectType_SawtoothDown_ID +#define GUID_SawtoothUp kFFEffectType_SawtoothUp_ID +#define GUID_Sine kFFEffectType_Sine_ID +#define GUID_Spring kFFEffectType_Spring_ID +#define GUID_Square kFFEffectType_Square_ID +#define GUID_Triangle kFFEffectType_Triangle_ID + +// Miscellaneous +#define DI_DEGREES FF_DEGREES +#define DI_DOWNLOADSKIPPED FF_DOWNLOADSKIPPED +#define DI_EFFECTRESTARTED FF_EFFECTRESTARTED +#define DI_FALSE FF_FALSE +#define DI_FFNOMINALMAX FF_FFNOMINALMAX +#define DI_INFINITE FF_INFINITE +#define DI_OK FF_OK +#define DI_SECONDS FF_SECONDS +#define DI_TRUNCATED FF_TRUNCATED +#define DI_TRUNCATEDANDRESTARTED FF_TRUNCATEDANDRESTARTED +#define DIEFF_OBJECTOFFSETS FFEFF_OBJECTOFFSETS +#define DIERR_DEVICEFULL FFERR_DEVICEFULL +#define DIERR_DEVICENOTREG FFERR_DEVICENOTREG +#define DIERR_DEVICEPAUSED FFERR_DEVICEPAUSED +#define DIERR_DEVICERELEASED FFERR_DEVICERELEASED +#define DIERR_EFFECTPLAYING FFERR_EFFECTPLAYING +#define DIERR_EFFECTTYPEMISMATCH FFERR_EFFECTTYPEMISMATCH +#define DIERR_EFFECTTYPENOTSUPPORTED FFERR_EFFECTTYPENOTSUPPORTED +#define DIERR_GENERIC FFERR_GENERIC +#define DIERR_HASEFFECTS FFERR_HASEFFECTS +#define DIERR_INCOMPLETEEFFECT FFERR_INCOMPLETEEFFECT +#define DIERR_INTERNAL FFERR_INTERNAL +#define DIERR_INVALIDDOWNLOADID FFERR_INVALIDDOWNLOADID +#define DIERR_INVALIDPARAM FFERR_INVALIDPARAM +#define DIERR_MOREDATA FFERR_MOREDATA +#define DIERR_NOINTERFACE FFERR_NOINTERFACE +#define DIERR_NOTDOWNLOADED FFERR_NOTDOWNLOADED +#define DIERR_NOTINITIALIZED FFERR_NOTINITIALIZED +#define DIERR_OUTOFMEMORY FFERR_OUTOFMEMORY +#define DIERR_UNPLUGGED FFERR_UNPLUGGED +#define DIERR_UNSUPPORTED FFERR_UNSUPPORTED +#define DIERR_UNSUPPORTEDAXIS FFERR_UNSUPPORTEDAXIS +#define DIJOFS_X FFJOFS_X +#define DIJOFS_Y FFJOFS_Y +#define DIJOFS_Z FFJOFS_Z + +// FFCapabilitiesEffectSubType +#define DICAP_ST_KINESTHETIC FFCAP_ST_KINESTHETIC +#define DICAP_ST_VIBRATION FFCAP_ST_VIBRATION + +// FFCapabilitiesEffectType +#define DICAP_ET_CONSTANTFORCE FFCAP_ET_CONSTANTFORCE +#define DICAP_ET_RAMPFORCE FFCAP_ET_RAMPFORCE +#define DICAP_ET_SQUARE FFCAP_ET_SQUARE +#define DICAP_ET_SINE FFCAP_ET_SINE +#define DICAP_ET_TRIANGLE FFCAP_ET_TRIANGLE +#define DICAP_ET_SAWTOOTHUP FFCAP_ET_SAWTOOTHUP +#define DICAP_ET_SAWTOOTHDOWN FFCAP_ET_SAWTOOTHDOWN +#define DICAP_ET_SPRING FFCAP_ET_SPRING +#define DICAP_ET_DAMPER FFCAP_ET_DAMPER +#define DICAP_ET_INERTIA FFCAP_ET_INERTIA +#define DICAP_ET_FRICTION FFCAP_ET_FRICTION +#define DICAP_ET_CUSTOMFORCE FFCAP_ET_CUSTOMFORCE + +// FFCommandFlag +#define DISFFC_RESET FFSFFC_RESET +#define DISFFC_STOPALL FFSFFC_STOPALL +#define DISFFC_PAUSE FFSFFC_PAUSE +#define DISFFC_CONTINUE FFSFFC_CONTINUE +#define DISFFC_SETACTUATORSON FFSFFC_SETACTUATORSON +#define DISFFC_SETACTUATORSOFF FFSFFC_SETACTUATORSOFF + +// FFCooperativeLevelFlag +#define DISCL_EXCLUSIVE FFSCL_EXCLUSIVE +#define DISCL_NONEXCLUSIVE FFSCL_NONEXCLUSIVE +#define DISCL_FOREGROUND FFSCL_FOREGROUND +#define DISCL_BACKGROUND FFSCL_BACKGROUND + +// FFCoordinateSystemFlag +#define DIEFF_CARTESIAN FFEFF_CARTESIAN +#define DIEFF_POLAR FFEFF_POLAR +#define DIEFF_SPHERICAL FFEFF_SPHERICAL + +// FFEffectParameterFlag +#define DIEP_DURATION FFEP_DURATION +#define DIEP_SAMPLEPERIOD FFEP_SAMPLEPERIOD +#define DIEP_GAIN FFEP_GAIN +#define DIEP_TRIGGERBUTTON FFEP_TRIGGERBUTTON +#define DIEP_TRIGGERREPEATINTERVAL FFEP_TRIGGERREPEATINTERVAL +#define DIEP_AXES FFEP_AXES +#define DIEP_DIRECTION FFEP_DIRECTION +#define DIEP_ENVELOPE FFEP_ENVELOPE +#define DIEP_TYPESPECIFICPARAMS FFEP_TYPESPECIFICPARAMS +#define DIEP_STARTDELAY FFEP_STARTDELAY +#define DIEP_ALLPARAMS FFEP_ALLPARAMS +#define DIEP_START FFEP_START +#define DIEP_NORESTART FFEP_NORESTART +#define DIEP_NODOWNLOAD FFEP_NODOWNLOAD +#define DIEB_NOTRIGGER FFEB_NOTRIGGER + +// FFEffectStartFlag +#define DIES_SOLO FFES_SOLO +#define DIES_NODOWNLOAD FFES_NODOWNLOAD + +// FFEffectStatusFlag +#define DIEGES_NOTPLAYING FFEGES_NOTPLAYING +#define DIEGES_PLAYING FFEGES_PLAYING +#define DIEGES_EMULATED FFEGES_EMULATED + +// FFProperty +#define DIPROP_FFGAIN FFPROP_FFGAIN +#define DIPROP_AUTOCENTER FFPROP_AUTOCENTER +// not defined in ForceFeedbackConstants.h +#define DIPROPAUTOCENTER_OFF 0 +#define DIPROPAUTOCENTER_ON 1 + +// FFState +#define DIGFFS_EMPTY FFGFFS_EMPTY +#define DIGFFS_STOPPED FFGFFS_STOPPED +#define DIGFFS_PAUSED FFGFFS_PAUSED +#define DIGFFS_ACTUATORSON FFGFFS_ACTUATORSON +#define DIGFFS_ACTUATORSOFF FFGFFS_ACTUATORSOFF +#define DIGFFS_POWERON FFGFFS_POWERON +#define DIGFFS_POWEROFF FFGFFS_POWEROFF +#define DIGFFS_SAFETYSWITCHON FFGFFS_SAFETYSWITCHON +#define DIGFFS_SAFETYSWITCHOFF FFGFFS_SAFETYSWITCHOFF +#define DIGFFS_USERFFSWITCHON FFGFFS_USERFFSWITCHON +#define DIGFFS_USERFFSWITCHOFF FFGFFS_USERFFSWITCHOFF +#define DIGFFS_DEVICELOST FFGFFS_DEVICELOST + +#endif diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h index ebe908522b..f53fc14cb5 100644 --- a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h @@ -1,13 +1,14 @@ #include #include "../Device.h" +#include "../ForceFeedback/ForceFeedbackDevice.h" namespace ciface { namespace OSX { -class Joystick : public Core::Device +class Joystick : public ForceFeedback::ForceFeedbackDevice { private: class Button : public Input @@ -62,9 +63,9 @@ private: public: bool UpdateInput(); - bool UpdateOutput(); Joystick(IOHIDDeviceRef device, std::string name, int index); + ~Joystick(); std::string GetName() const; std::string GetSource() const; @@ -74,6 +75,8 @@ private: const IOHIDDeviceRef m_device; const std::string m_device_name; const int m_index; + + ForceFeedback::FFDeviceAdapterReference m_ff_device; }; } diff --git a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm index ebd713d142..2a26e35447 100644 --- a/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm +++ b/Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm @@ -15,6 +15,7 @@ Joystick::Joystick(IOHIDDeviceRef device, std::string name, int index) : m_device(device) , m_device_name(name) , m_index(index) + , m_ff_device(nullptr) { // Buttons NSDictionary *buttonDict = @@ -71,6 +72,20 @@ Joystick::Joystick(IOHIDDeviceRef device, std::string name, int index) } CFRelease(axes); } + + // Force Feedback + FFCAPABILITIES ff_caps; + if (SUCCEEDED(ForceFeedback::FFDeviceAdapter::Create(IOHIDDeviceGetService(m_device), &m_ff_device)) && + SUCCEEDED(FFDeviceGetForceFeedbackCapabilities(m_ff_device->m_device, &ff_caps))) + { + InitForceFeedback(m_ff_device, ff_caps.numFfAxes); + } +} + +Joystick::~Joystick() +{ + if (m_ff_device) + m_ff_device->Release(); } bool Joystick::UpdateInput() @@ -78,11 +93,6 @@ bool Joystick::UpdateInput() return true; } -bool Joystick::UpdateOutput() -{ - return true; -} - std::string Joystick::GetName() const { return m_device_name; diff --git a/Source/Core/InputCommon/InputCommon.vcxproj b/Source/Core/InputCommon/InputCommon.vcxproj index 43eede3076..968bd235d7 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj +++ b/Source/Core/InputCommon/InputCommon.vcxproj @@ -1,4 +1,4 @@ - + @@ -50,6 +50,7 @@ + @@ -67,6 +68,7 @@ + @@ -86,4 +88,4 @@ - + \ No newline at end of file diff --git a/Source/Core/InputCommon/InputCommon.vcxproj.filters b/Source/Core/InputCommon/InputCommon.vcxproj.filters index 7e344e6976..3e06237cdc 100644 --- a/Source/Core/InputCommon/InputCommon.vcxproj.filters +++ b/Source/Core/InputCommon/InputCommon.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -13,6 +13,9 @@ {07bad1aa-7e03-4f5c-ade2-a44857c5cbc3} + + {e10ce316-283c-4be0-848d-578dec2b6404} + @@ -44,6 +47,9 @@ ControllerInterface + + ControllerInterface\ForceFeedback + @@ -76,8 +82,11 @@ ControllerInterface + + ControllerInterface\ForceFeedback + - + \ No newline at end of file