ForceFeedback: Add OSX rumble support

This commit is contained in:
Jules Blok 2014-01-29 08:11:51 +09:00
parent 02a95c139e
commit c6d650c058
9 changed files with 409 additions and 12 deletions

View File

@ -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)

View File

@ -141,6 +141,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
${CORESERV_LIBRARY}
${IOB_LIBRARY}
${IOK_LIBRARY}
${FORCEFEEDBACK}
)
if(wxWidgets_FOUND)
list(APPEND LIBS

View File

@ -19,7 +19,8 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
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

View File

@ -56,7 +56,7 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i
LONG rglDirection[2] = {-200, 0};
DIEFFECT eff;
ZeroMemory(&eff, sizeof(eff));
memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE; // (4 * DI_SECONDS)
@ -71,7 +71,7 @@ bool ForceFeedbackDevice::InitForceFeedback(const LPDIRECTINPUTDEVICE8 device, i
// DIPERIODIC is the largest, so we'll use that
DIPERIODIC ff;
eff.lpvTypeSpecificParams = &ff;
ZeroMemory(&ff, sizeof(ff));
memset(&ff, 0, sizeof(ff));
// doesn't seem needed
//DIENVELOPE env;
@ -133,7 +133,7 @@ bool ForceFeedbackDevice::UpdateOutput()
size_t ok_count = 0;
DIEFFECT eff;
ZeroMemory(&eff, sizeof(eff));
memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
@ -164,6 +164,7 @@ bool ForceFeedbackDevice::UpdateOutput()
return (m_state_out.size() == ok_count);
}
template<>
void ForceFeedbackDevice::ForceConstant::SetState(const ControlState state)
{
const LONG new_val = LONG(10000 * state);
@ -179,6 +180,7 @@ void ForceFeedbackDevice::ForceConstant::SetState(const ControlState state)
}
}
template<>
void ForceFeedbackDevice::ForceRamp::SetState(const ControlState state)
{
const LONG new_val = LONG(10000 * state);
@ -193,6 +195,7 @@ void ForceFeedbackDevice::ForceRamp::SetState(const ControlState state)
}
}
template<>
void ForceFeedbackDevice::ForcePeriodic::SetState(const ControlState state)
{
const DWORD new_val = DWORD(10000 * state);
@ -214,7 +217,7 @@ template <typename P>
ForceFeedbackDevice::Force<P>::Force(u8 index, EffectState& state)
: m_index(index), m_state(state)
{
ZeroMemory(&params, sizeof(params));
memset(&params, 0, sizeof(params));
}
template <typename P>

View File

@ -7,11 +7,15 @@
#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>

View File

@ -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

View File

@ -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

View File

@ -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;
};
}

View File

@ -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;