[PATCH 2/3] dinput: Determine if OSX devices support force feedback

Andrew Eikum aeikum at codeweavers.com
Mon May 20 14:10:30 CDT 2013


---
 configure.ac               |   1 +
 dlls/dinput/Makefile.in    |   2 +-
 dlls/dinput/joystick_osx.c | 158 +++++++++++++++++++++++++++++++++++++--------
 3 files changed, 133 insertions(+), 28 deletions(-)

diff --git a/configure.ac b/configure.ac
index 98b51e3..05794af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -741,6 +741,7 @@ clean::
     dnl declare needed frameworks
     AC_SUBST(COREFOUNDATIONLIB,"-framework CoreFoundation")
     AC_SUBST(IOKITLIB,"-framework IOKit -framework CoreFoundation")
+    AC_SUBST(FORCEFEEDBACKLIB,"-framework ForceFeedback -framework CoreFoundation")
     AC_SUBST(APPLICATIONSERVICESLIB,"-framework ApplicationServices")
     AC_SUBST(CORESERVICESLIB,"-framework CoreServices")
     case $host_os in
diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in
index 1be48c4..e7d23d7 100644
--- a/dlls/dinput/Makefile.in
+++ b/dlls/dinput/Makefile.in
@@ -1,7 +1,7 @@
 MODULE    = dinput.dll
 IMPORTLIB = dinput
 IMPORTS   = dxguid uuid comctl32 ole32 user32 advapi32
-EXTRALIBS = @IOKITLIB@
+EXTRALIBS = @IOKITLIB@ @FORCEFEEDBACKLIB@
 
 C_SRCS = \
 	config.c \
diff --git a/dlls/dinput/joystick_osx.c b/dlls/dinput/joystick_osx.c
index 60db656..66a4acb 100644
--- a/dlls/dinput/joystick_osx.c
+++ b/dlls/dinput/joystick_osx.c
@@ -24,6 +24,11 @@
 #include "wine/port.h"
 
 #if defined(HAVE_IOKIT_HID_IOHIDLIB_H)
+#define DWORD UInt32
+#define LPDWORD UInt32*
+#define LONG SInt32
+#define LPLONG SInt32*
+#define E_PENDING __carbon_E_PENDING
 #define ULONG __carbon_ULONG
 #define E_INVALIDARG __carbon_E_INVALIDARG
 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
@@ -44,7 +49,9 @@
 #define MAKE_HRESULT __carbon_MAKE_HRESULT
 #define HRESULT __carbon_HRESULT
 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLTYPE
+#include <IOKit/IOKitLib.h>
 #include <IOKit/hid/IOHIDLib.h>
+#include <ForceFeedback/ForceFeedback.h>
 #undef ULONG
 #undef E_INVALIDARG
 #undef E_OUTOFMEMORY
@@ -65,6 +72,11 @@
 #undef MAKE_HRESULT
 #undef HRESULT
 #undef STDMETHODCALLTYPE
+#undef DWORD
+#undef LPDWORD
+#undef LONG
+#undef LPLONG
+#undef E_PENDING
 #endif /* HAVE_IOKIT_HID_IOHIDLIB_H */
 
 #include "wine/debug.h"
@@ -98,6 +110,7 @@ struct JoystickImpl
     int                    id;
     CFMutableArrayRef      elementCFArrayRef;
     ObjProps               **propmap;
+    FFDeviceObjectReference ff;
 };
 
 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
@@ -120,6 +133,60 @@ static void CFSetApplierFunctionCopyToCFArray(const void *value, void *context)
     CFArrayAppendValue( ( CFMutableArrayRef ) context, value );
 }
 
+static IOHIDDeviceRef get_device_ref(int id)
+{
+    IOHIDElementRef tIOHIDElementRef;
+    IOHIDDeviceRef tIOHIDDeviceRef;
+
+    if (!gCollections)
+        return 0;
+
+    tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, id);
+    if (!tIOHIDElementRef)
+    {
+        ERR("Invalid Element requested %i\n",id);
+        return 0;
+    }
+
+    tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDElementRef);
+    if (!tIOHIDDeviceRef)
+    {
+        ERR("Invalid Device requested %i\n",id);
+        return 0;
+    }
+
+    return tIOHIDDeviceRef;
+}
+
+static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret)
+{
+    io_service_t service;
+    CFMutableDictionaryRef matching;
+    CFTypeRef type;
+
+    matching = IOServiceMatching(kIOHIDDeviceKey);
+    if(!matching){
+        WARN("IOServiceMatching failed, force feedback disabled\n");
+        return DIERR_DEVICENOTREG;
+    }
+
+    type = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
+    if(!matching){
+        CFRelease(matching);
+        WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n");
+        return DIERR_DEVICENOTREG;
+    }
+
+    CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), type);
+
+    service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
+
+    if(!ret)
+        return FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE;
+
+    return FFCreateDevice(service, ret);
+}
+
 static CFMutableDictionaryRef creates_osx_device_match(int usage)
 {
     CFMutableDictionaryRef result;
@@ -298,30 +365,15 @@ static int find_osx_devices(void)
 static int get_osx_device_name(int id, char *name, int length)
 {
     CFStringRef str;
-    IOHIDElementRef tIOHIDElementRef;
     IOHIDDeviceRef tIOHIDDeviceRef;
 
-    if (!gCollections)
-        return 0;
-
-    tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, id);
-
-    if (!tIOHIDElementRef)
-    {
-        ERR("Invalid Element requested %i\n",id);
-        return 0;
-    }
-
-    tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDElementRef);
+    tIOHIDDeviceRef = get_device_ref(id);
 
     if (name)
         name[0] = 0;
 
     if (!tIOHIDDeviceRef)
-    {
-        ERR("Invalid Device requested %i\n",id);
         return 0;
-    }
 
     str = IOHIDDeviceGetProperty(tIOHIDDeviceRef, CFSTR( kIOHIDProductKey ));
     if (str)
@@ -655,15 +707,17 @@ static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
 {
     if (id >= find_joystick_devices()) return E_FAIL;
 
-    if (dwFlags & DIEDFL_FORCEFEEDBACK) {
-        WARN("force feedback not supported\n");
-        return S_FALSE;
-    }
-
     if ((dwDevType == 0) ||
     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
     {
+        if (dwFlags & DIEDFL_FORCEFEEDBACK) {
+            IOHIDDeviceRef device = get_device_ref(id);
+            if(!device)
+                return S_FALSE;
+            if(get_ff(device, NULL) != S_OK)
+                return S_FALSE;
+        }
         /* Return joystick */
         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
         lpddi->guidInstance.Data3 = id;
@@ -692,14 +746,16 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
 
     if (id >= find_joystick_devices()) return E_FAIL;
 
-    if (dwFlags & DIEDFL_FORCEFEEDBACK) {
-        WARN("force feedback not supported\n");
-        return S_FALSE;
-    }
-
     if ((dwDevType == 0) ||
     ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
+        if (dwFlags & DIEDFL_FORCEFEEDBACK) {
+            IOHIDDeviceRef device = get_device_ref(id);
+            if(!device)
+                return S_FALSE;
+            if(get_ff(device, NULL) != S_OK)
+                return S_FALSE;
+        }
         /* Return joystick */
         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
         lpddi->guidInstance.Data3 = id;
@@ -722,10 +778,26 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     return S_FALSE;
 }
 
+static const char *osx_ff_axis_name(UInt8 axis)
+{
+    static char ret[6];
+    switch(axis){
+    case FFJOFS_X:
+        return "FFJOFS_X";
+    case FFJOFS_Y:
+        return "FFJOFS_Y";
+    case FFJOFS_Z:
+        return "FFJOFS_Z";
+    }
+    sprintf(ret, "%u", (unsigned int)axis);
+    return ret;
+}
+
 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
                             JoystickImpl **pdev, unsigned short index)
 {
     DWORD i;
+    IOHIDDeviceRef device;
     JoystickImpl* newDevice;
     char name[MAX_PATH];
     HRESULT hr;
@@ -733,6 +805,7 @@ static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
     int idx = 0;
     int axis_map[8]; /* max axes */
     int slider_count = 0;
+    FFCAPABILITIES ffcaps;
 
     TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
 
@@ -758,6 +831,37 @@ static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
     newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
     strcpy(newDevice->generic.name, name);
 
+    device = get_device_ref(index);
+    if(get_ff(device, &newDevice->ff) == S_OK){
+        newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
+
+        hr = FFDeviceGetForceFeedbackCapabilities(newDevice->ff, &ffcaps);
+        if(SUCCEEDED(hr)){
+            TRACE("FF Capabilities:\n");
+            TRACE("\tsupportedEffects: 0x%x\n", (unsigned int)ffcaps.supportedEffects);
+            TRACE("\temulatedEffects: 0x%x\n", (unsigned int)ffcaps.emulatedEffects);
+            TRACE("\tsubType: 0x%x\n", (unsigned int)ffcaps.subType);
+            TRACE("\tnumFfAxes: %u\n", (unsigned int)ffcaps.numFfAxes);
+            TRACE("\tffAxes: [");
+            for(i = 0; i < ffcaps.numFfAxes; ++i){
+                TRACE("%s", osx_ff_axis_name(ffcaps.ffAxes[i]));
+                if(i < ffcaps.numFfAxes - 1)
+                    TRACE(", ");
+            }
+            TRACE("]\n");
+            TRACE("\tstorageCapacity: %u\n", (unsigned int)ffcaps.storageCapacity);
+            TRACE("\tplaybackCapacity: %u\n", (unsigned int)ffcaps.playbackCapacity);
+        }
+
+        hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_RESET);
+        if(FAILED(hr))
+            WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr);
+
+        hr = FFDeviceSendForceFeedbackCommand(newDevice->ff, FFSFFC_SETACTUATORSON);
+        if(FAILED(hr))
+            WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr);
+    }
+
     memset(axis_map, 0, sizeof(axis_map));
     get_osx_device_elements(newDevice, axis_map);
 
@@ -830,7 +934,7 @@ static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
     LeaveCriticalSection(&dinput->crit);
 
     newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
-    newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
+    newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
     if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
         newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
     else
-- 
1.8.2.3





More information about the wine-patches mailing list