summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface
diff options
context:
space:
mode:
authorPierre Bourdon <delroth@gmail.com>2014-02-09 16:10:07 +0100
committerPierre Bourdon <delroth@gmail.com>2014-02-09 16:10:07 +0100
commit9da6900595318620690a5cdb5094a05e17cf094e (patch)
tree1523960f4b36fd4fd819528a57462ab422a411ed /Source/Core/InputCommon/ControllerInterface
parent56603b297a72720367e213bbe7a39fe8c91ba90a (diff)
parent2063eddfa33167e7b29985c88362de95722830d2 (diff)
Merge pull request #12 from Armada651/osx_rumble
OS X Rumble Support
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.cpp209
-rw-r--r--Source/Core/InputCommon/ControllerInterface/DInput/DInputJoystick.h38
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp232
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ForceFeedback/ForceFeedbackDevice.h69
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputAdapter.h224
-rw-r--r--Source/Core/InputCommon/ControllerInterface/ForceFeedback/OSX/DirectInputConstants.h150
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.h7
-rw-r--r--Source/Core/InputCommon/ControllerInterface/OSX/OSXJoystick.mm16
8 files changed, 698 insertions, 247 deletions
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<DICONSTANTFORCE>;
-template class Joystick::Force<DIRAMPFORCE>;
-template class Joystick::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"},
-};
-
#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<DIDEVICEOBJECTINSTANCE> objects;
- m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS);
- // got some ff axes or something
- if ( objects.size() )
- {
- // 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())
+ if (SUCCEEDED(m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS)))
{
- 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 <typename P>
-std::string Joystick::Force<P>::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 = &params; // 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 = &params; // 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 = &params; // 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>
-Joystick::Force<P>::Force(u8 index, EffectState& state)
- : m_index(index), m_state(state)
-{
- ZeroMemory(&params, 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 <Windows.h>
-#include <dinput.h>
-
-#include <list>
+#include "../ForceFeedback/ForceFeedbackDevice.h"
namespace ciface
{
@@ -18,18 +11,9 @@ namespace DInput
void InitJoystick(IDirectInput8* const idi8, std::vector<Core::Device*>& 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 <typename P>
- 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<DICONSTANTFORCE> ForceConstant;
- typedef Force<DIRAMPFORCE> ForceRamp;
- typedef Force<DIPERIODIC> ForcePeriodic;
-
public:
bool UpdateInput();
- bool UpdateOutput();
void ClearInputState();
@@ -98,7 +65,6 @@ private:
const unsigned int m_index;
DIJOYSTATE m_state_in;
- std::list<EffectState> 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<DICONSTANTFORCE>;
+template class ForceFeedbackDevice::Force<DIRAMPFORCE>;
+template class ForceFeedbackDevice::Force<DIPERIODIC>;
+
+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 = &params; // 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 = &params; // 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 = &params; // 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(const char* name, EffectState& state)
+: m_name(name), m_state(state)
+{
+ memset(&params, 0, sizeof(params));
+}
+
+template <typename P>
+std::string ForceFeedbackDevice::Force<P>::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 <Windows.h>
+#include <dinput.h>
+#elif __APPLE__
+#include "OSX/DirectInputAdapter.h"
+#endif
+
+#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(const char* name, EffectState& state);
+ void SetState(ControlState state);
+ private:
+ const char* m_name;
+ 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
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 <CoreFoundation/CoreFoundation.h>
+#include <ForceFeedback/ForceFeedback.h>
+#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<ULONG> 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 <IOKit/hid/IOHIDLib.h>
#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,14 +72,23 @@ 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);
+ }
}
-bool Joystick::UpdateInput()
+Joystick::~Joystick()
{
- return true;
+ if (m_ff_device)
+ m_ff_device->Release();
}
-bool Joystick::UpdateOutput()
+bool Joystick::UpdateInput()
{
return true;
}