wine/dlls/dinput/joystick_osx.c
2013-10-04 12:46:26 +02:00

1509 lines
50 KiB
C

/* DirectInput Joystick device for Mac OS/X
*
* Copyright 1998 Marcus Meissner
* Copyright 1998,1999 Lionel Ulmer
* Copyright 2000-2001 TransGaming Technologies Inc.
* Copyright 2009 CodeWeavers, Aric Stewart
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#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
#define E_HANDLE __carbon_E_HANDLE
#define E_ACCESSDENIED __carbon_E_ACCESSDENIED
#define E_UNEXPECTED __carbon_E_UNEXPECTED
#define E_FAIL __carbon_E_FAIL
#define E_ABORT __carbon_E_ABORT
#define E_POINTER __carbon_E_POINTER
#define E_NOINTERFACE __carbon_E_NOINTERFACE
#define E_NOTIMPL __carbon_E_NOTIMPL
#define S_FALSE __carbon_S_FALSE
#define S_OK __carbon_S_OK
#define HRESULT_FACILITY __carbon_HRESULT_FACILITY
#define IS_ERROR __carbon_IS_ERROR
#define FAILED __carbon_FAILED
#define SUCCEEDED __carbon_SUCCEEDED
#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
#undef E_HANDLE
#undef E_ACCESSDENIED
#undef E_UNEXPECTED
#undef E_FAIL
#undef E_ABORT
#undef E_POINTER
#undef E_NOINTERFACE
#undef E_NOTIMPL
#undef S_FALSE
#undef S_OK
#undef HRESULT_FACILITY
#undef IS_ERROR
#undef FAILED
#undef SUCCEEDED
#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"
#include "wine/unicode.h"
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "dinput.h"
#include "dinput_private.h"
#include "device_private.h"
#include "joystick_private.h"
#ifdef HAVE_IOHIDMANAGERCREATE
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
static IOHIDManagerRef gIOHIDManagerRef = NULL;
static CFArrayRef gCollections = NULL;
typedef struct JoystickImpl JoystickImpl;
static const IDirectInputDevice8AVtbl JoystickAvt;
static const IDirectInputDevice8WVtbl JoystickWvt;
struct JoystickImpl
{
struct JoystickGenericImpl generic;
/* osx private */
int id;
CFMutableArrayRef elementCFArrayRef;
ObjProps **propmap;
FFDeviceObjectReference ff;
struct list effects;
};
static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
{
return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
JoystickGenericImpl, base), JoystickImpl, generic);
}
static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
{
return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
JoystickGenericImpl, base), JoystickImpl, generic);
}
typedef struct _EffectImpl {
IDirectInputEffect IDirectInputEffect_iface;
LONG ref;
JoystickImpl *device;
FFEffectObjectReference effect;
GUID guid;
struct list entry;
} EffectImpl;
static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface)
{
return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface);
}
static const IDirectInputEffectVtbl EffectVtbl;
static const GUID DInput_Wine_OsX_Joystick_GUID = { /* 59CAD8F6-E617-41E2-8EB7-47B23EEEDC5A */
0x59CAD8F6, 0xE617, 0x41E2, {0x8E, 0xB7, 0x47, 0xB2, 0x3E, 0xEE, 0xDC, 0x5A}
};
static HRESULT osx_to_win32_hresult(HRESULT in)
{
/* OSX returns 16-bit COM runtime errors, which we should
* convert to win32 */
switch(in){
case 0x80000001:
return E_NOTIMPL;
case 0x80000002:
return E_OUTOFMEMORY;
case 0x80000003:
return E_INVALIDARG;
case 0x80000004:
return E_NOINTERFACE;
case 0x80000005:
return E_POINTER;
case 0x80000006:
return E_HANDLE;
case 0x80000007:
return E_ABORT;
case 0x80000008:
return E_FAIL;
case 0x80000009:
return E_ACCESSDENIED;
case 0x8000FFFF:
return E_UNEXPECTED;
}
return in;
}
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 osx_to_win32_hresult(FFCreateDevice(service, ret));
}
static CFMutableDictionaryRef creates_osx_device_match(int usage)
{
CFMutableDictionaryRef result;
result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
if ( result )
{
int number = kHIDPage_GenericDesktop;
CFNumberRef pageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
kCFNumberIntType, &number);
if ( pageCFNumberRef )
{
CFNumberRef usageCFNumberRef;
CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ),
pageCFNumberRef );
CFRelease( pageCFNumberRef );
usageCFNumberRef = CFNumberCreate( kCFAllocatorDefault,
kCFNumberIntType, &usage);
if ( usageCFNumberRef )
{
CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ),
usageCFNumberRef );
CFRelease( usageCFNumberRef );
}
else
{
ERR("CFNumberCreate() failed.\n");
return NULL;
}
}
else
{
ERR("CFNumberCreate failed.\n");
return NULL;
}
}
else
{
ERR("CFDictionaryCreateMutable failed.\n");
return NULL;
}
return result;
}
static CFIndex find_top_level(IOHIDDeviceRef tIOHIDDeviceRef, CFArrayRef topLevels)
{
CFArrayRef gElementCFArrayRef;
CFIndex numTops = 0;
if (!tIOHIDDeviceRef)
return 0;
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0);
if (gElementCFArrayRef)
{
CFIndex idx, cnt = CFArrayGetCount(gElementCFArrayRef);
for (idx=0; idx<cnt; idx++)
{
IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gElementCFArrayRef, idx);
int eleType = IOHIDElementGetType(tIOHIDElementRef);
/* Check for top-level gaming device collections */
if (eleType == kIOHIDElementTypeCollection && IOHIDElementGetParent(tIOHIDElementRef) == 0)
{
int tUsagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
int tUsage = IOHIDElementGetUsage(tIOHIDElementRef);
if (tUsagePage == kHIDPage_GenericDesktop &&
(tUsage == kHIDUsage_GD_Joystick || tUsage == kHIDUsage_GD_GamePad))
{
CFArrayAppendValue((CFMutableArrayRef)topLevels, tIOHIDElementRef);
numTops++;
}
}
}
}
return numTops;
}
static void get_element_children(IOHIDElementRef tElement, CFArrayRef childElements)
{
CFIndex idx, cnt;
CFArrayRef tElementChildrenArray = IOHIDElementGetChildren(tElement);
cnt = CFArrayGetCount(tElementChildrenArray);
if (cnt < 1)
return;
/* Either add the element to the array or grab its children */
for (idx=0; idx<cnt; idx++)
{
IOHIDElementRef tChildElementRef;
tChildElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(tElementChildrenArray, idx);
if (IOHIDElementGetType(tChildElementRef) == kIOHIDElementTypeCollection)
get_element_children(tChildElementRef, childElements);
else
CFArrayAppendValue((CFMutableArrayRef)childElements, tChildElementRef);
}
}
static int find_osx_devices(void)
{
IOReturn tIOReturn;
CFMutableDictionaryRef result;
CFSetRef devset;
CFArrayRef matching;
gIOHIDManagerRef = IOHIDManagerCreate( kCFAllocatorDefault, 0L );
tIOReturn = IOHIDManagerOpen( gIOHIDManagerRef, 0L);
if ( kIOReturnSuccess != tIOReturn )
{
ERR("Couldn't open IOHIDManager.\n");
return 0;
}
matching = CFArrayCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks );
/* build matching dictionary */
result = creates_osx_device_match(kHIDUsage_GD_Joystick);
if (!result)
{
CFRelease(matching);
return 0;
}
CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
result = creates_osx_device_match(kHIDUsage_GD_GamePad);
if (!result)
{
CFRelease(matching);
return 0;
}
CFArrayAppendValue( ( CFMutableArrayRef )matching, result );
IOHIDManagerSetDeviceMatchingMultiple( gIOHIDManagerRef, matching);
devset = IOHIDManagerCopyDevices( gIOHIDManagerRef );
if (devset)
{
CFIndex countDevices, countCollections, idx;
CFArrayRef gDevices = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFSetApplyFunction(devset, CFSetApplierFunctionCopyToCFArray, (void*)gDevices);
CFRelease( devset);
countDevices = CFArrayGetCount(gDevices);
gCollections = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!gCollections)
return 0;
countCollections = 0;
for (idx = 0; idx < countDevices; idx++)
{
CFIndex tTop;
IOHIDDeviceRef tDevice;
tDevice = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDevices, idx);
tTop = find_top_level(tDevice, gCollections);
countCollections += tTop;
}
CFRelease(gDevices);
TRACE("found %i device(s), %i collection(s)\n",(int)countDevices,(int)countCollections);
return (int)countCollections;
}
return 0;
}
static int get_osx_device_name(int id, char *name, int length)
{
CFStringRef str;
IOHIDDeviceRef tIOHIDDeviceRef;
tIOHIDDeviceRef = get_device_ref(id);
if (name)
name[0] = 0;
if (!tIOHIDDeviceRef)
return 0;
str = IOHIDDeviceGetProperty(tIOHIDDeviceRef, CFSTR( kIOHIDProductKey ));
if (str)
{
CFIndex len = CFStringGetLength(str);
if (length >= len)
{
CFStringGetCString(str,name,length,kCFStringEncodingASCII);
return len;
}
else
return (len+1);
}
return 0;
}
static void insert_sort_button(int header, IOHIDElementRef tIOHIDElementRef,
CFMutableArrayRef elementCFArrayRef, int index,
int target)
{
IOHIDElementRef targetElement;
int usage;
CFArraySetValueAtIndex(elementCFArrayRef, header+index, NULL);
targetElement = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elementCFArrayRef, header+target);
if (targetElement == NULL)
{
CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
return;
}
usage = IOHIDElementGetUsage( targetElement );
usage --; /* usage 1 based index */
insert_sort_button(header, targetElement, elementCFArrayRef, target, usage);
CFArraySetValueAtIndex(elementCFArrayRef, header+target,tIOHIDElementRef);
}
static void get_osx_device_elements(JoystickImpl *device, int axis_map[8])
{
IOHIDElementRef tIOHIDElementRef;
CFArrayRef gElementCFArrayRef;
DWORD axes = 0;
DWORD sliders = 0;
DWORD buttons = 0;
DWORD povs = 0;
device->elementCFArrayRef = NULL;
if (!gCollections)
return;
tIOHIDElementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(gCollections, device->id);
if (!tIOHIDElementRef)
return;
gElementCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
get_element_children(tIOHIDElementRef, gElementCFArrayRef);
if (gElementCFArrayRef)
{
CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
/* build our element array in the order that dinput expects */
device->elementCFArrayRef = CFArrayCreateMutable(NULL,0,NULL);
for ( idx = 0; idx < cnt; idx++ )
{
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
int eleType = IOHIDElementGetType( tIOHIDElementRef );
switch(eleType)
{
case kIOHIDElementTypeInput_Button:
{
int usagePage = IOHIDElementGetUsagePage( tIOHIDElementRef );
if (usagePage != kHIDPage_Button)
{
/* avoid strange elements found on the 360 controller */
continue;
}
if (buttons < 128)
{
CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs+buttons), tIOHIDElementRef);
buttons++;
}
break;
}
case kIOHIDElementTypeInput_Axis:
{
CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
axes++;
break;
}
case kIOHIDElementTypeInput_Misc:
{
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
switch(usage)
{
case kHIDUsage_GD_Hatswitch:
{
CFArrayInsertValueAtIndex(device->elementCFArrayRef, (axes+povs), tIOHIDElementRef);
povs++;
break;
}
case kHIDUsage_GD_Slider:
sliders ++;
if (sliders > 2)
break;
/* fallthrough, sliders are axis */
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
{
CFArrayInsertValueAtIndex(device->elementCFArrayRef, axes, tIOHIDElementRef);
axis_map[axes]=usage;
axes++;
break;
}
default:
FIXME("Unhandled usage %i\n",usage);
}
break;
}
default:
FIXME("Unhandled type %i\n",eleType);
}
}
}
device->generic.devcaps.dwAxes = axes;
device->generic.devcaps.dwButtons = buttons;
device->generic.devcaps.dwPOVs = povs;
/* Sort buttons into correct order */
for (buttons = 0; buttons < device->generic.devcaps.dwButtons; buttons++)
{
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( device->elementCFArrayRef, axes+povs+buttons);
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
usage --; /* usage is 1 indexed we need 0 indexed */
if (usage == buttons)
continue;
insert_sort_button(axes+povs, tIOHIDElementRef, device->elementCFArrayRef,buttons,usage);
}
}
static void get_osx_device_elements_props(JoystickImpl *device)
{
CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
if (gElementCFArrayRef)
{
CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
for ( idx = 0; idx < cnt; idx++ )
{
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
device->generic.props[idx].lDevMin = IOHIDElementGetLogicalMin(tIOHIDElementRef);
device->generic.props[idx].lDevMax = IOHIDElementGetLogicalMax(tIOHIDElementRef);
device->generic.props[idx].lMin = 0;
device->generic.props[idx].lMax = 0xffff;
device->generic.props[idx].lDeadZone = 0;
device->generic.props[idx].lSaturation = 0;
}
}
}
static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
{
JoystickImpl *device = impl_from_IDirectInputDevice8A(iface);
IOHIDElementRef tIOHIDTopElementRef;
IOHIDDeviceRef tIOHIDDeviceRef;
CFArrayRef gElementCFArrayRef = device->elementCFArrayRef;
TRACE("polling device %i\n",device->id);
if (!gCollections)
return;
tIOHIDTopElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gCollections, device->id);
tIOHIDDeviceRef = IOHIDElementGetDevice(tIOHIDTopElementRef);
if (!tIOHIDDeviceRef)
return;
if (gElementCFArrayRef)
{
int button_idx = 0;
int pov_idx = 0;
int slider_idx = 0;
int inst_id;
CFIndex idx, cnt = CFArrayGetCount( gElementCFArrayRef );
for ( idx = 0; idx < cnt; idx++ )
{
IOHIDValueRef valueRef;
int val, oldVal, newVal;
IOHIDElementRef tIOHIDElementRef = ( IOHIDElementRef ) CFArrayGetValueAtIndex( gElementCFArrayRef, idx );
int eleType = IOHIDElementGetType( tIOHIDElementRef );
switch(eleType)
{
case kIOHIDElementTypeInput_Button:
if(button_idx < 128)
{
IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
val = IOHIDValueGetIntegerValue(valueRef);
newVal = val ? 0x80 : 0x0;
oldVal = device->generic.js.rgbButtons[button_idx];
device->generic.js.rgbButtons[button_idx] = newVal;
if (oldVal != newVal)
{
inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
}
button_idx ++;
}
break;
case kIOHIDElementTypeInput_Misc:
{
uint32_t usage = IOHIDElementGetUsage( tIOHIDElementRef );
switch(usage)
{
case kHIDUsage_GD_Hatswitch:
{
IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
val = IOHIDValueGetIntegerValue(valueRef);
oldVal = device->generic.js.rgdwPOV[pov_idx];
if (val >= 8)
newVal = -1;
else
newVal = val * 4500;
device->generic.js.rgdwPOV[pov_idx] = newVal;
if (oldVal != newVal)
{
inst_id = DIDFT_MAKEINSTANCE(pov_idx) | DIDFT_POV;
queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
}
pov_idx ++;
break;
}
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
case kHIDUsage_GD_Slider:
{
int wine_obj = -1;
IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &valueRef);
val = IOHIDValueGetIntegerValue(valueRef);
newVal = joystick_map_axis(&device->generic.props[idx], val);
switch (usage)
{
case kHIDUsage_GD_X:
wine_obj = 0;
oldVal = device->generic.js.lX;
device->generic.js.lX = newVal;
break;
case kHIDUsage_GD_Y:
wine_obj = 1;
oldVal = device->generic.js.lY;
device->generic.js.lY = newVal;
break;
case kHIDUsage_GD_Z:
wine_obj = 2;
oldVal = device->generic.js.lZ;
device->generic.js.lZ = newVal;
break;
case kHIDUsage_GD_Rx:
wine_obj = 3;
oldVal = device->generic.js.lRx;
device->generic.js.lRx = newVal;
break;
case kHIDUsage_GD_Ry:
wine_obj = 4;
oldVal = device->generic.js.lRy;
device->generic.js.lRy = newVal;
break;
case kHIDUsage_GD_Rz:
wine_obj = 5;
oldVal = device->generic.js.lRz;
device->generic.js.lRz = newVal;
break;
case kHIDUsage_GD_Slider:
wine_obj = 6 + slider_idx;
oldVal = device->generic.js.rglSlider[slider_idx];
device->generic.js.rglSlider[slider_idx] = newVal;
slider_idx ++;
break;
}
if ((wine_obj != -1) &&
(oldVal != newVal))
{
inst_id = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
}
break;
}
default:
FIXME("unhandled usage %i\n",usage);
}
break;
}
default:
FIXME("Unhandled type %i\n",eleType);
}
}
}
}
static INT find_joystick_devices(void)
{
static INT joystick_devices_count = -1;
if (joystick_devices_count != -1) return joystick_devices_count;
joystick_devices_count = find_osx_devices();
return joystick_devices_count;
}
static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
{
if (id >= find_joystick_devices()) return E_FAIL;
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;
lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
/* we only support traditional joysticks for now */
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
sprintf(lpddi->tszInstanceName, "Joystick %d", id);
/* get the device name */
get_osx_device_name(id, lpddi->tszProductName, MAX_PATH);
lpddi->guidFFDriver = GUID_NULL;
return S_OK;
}
return S_FALSE;
}
static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
{
char name[MAX_PATH];
char friendly[32];
if (id >= find_joystick_devices()) return E_FAIL;
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;
lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
/* we only support traditional joysticks for now */
if (version >= 0x0800)
lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
sprintf(friendly, "Joystick %d", id);
MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
/* get the device name */
get_osx_device_name(id, name, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
lpddi->guidFFDriver = GUID_NULL;
return S_OK;
}
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 BOOL osx_axis_has_ff(FFCAPABILITIES *ffcaps, UInt8 axis)
{
int i;
for(i = 0; i < ffcaps->numFfAxes; ++i)
if(ffcaps->ffAxes[i] == axis)
return TRUE;
return FALSE;
}
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;
LPDIDATAFORMAT df = NULL;
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);
newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
if (newDevice == 0) {
WARN("out of memory\n");
*pdev = 0;
return DIERR_OUTOFMEMORY;
}
newDevice->id = index;
newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
newDevice->generic.guidInstance.Data3 = index;
newDevice->generic.guidProduct = DInput_Wine_OsX_Joystick_GUID;
newDevice->generic.joy_polldev = poll_osx_device_state;
/* get the device name */
get_osx_device_name(index, name, MAX_PATH);
TRACE("Name %s\n",name);
/* copy the device name */
newDevice->generic.name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
strcpy(newDevice->generic.name, name);
list_init(&newDevice->effects);
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);
TRACE("%i axes %i buttons %i povs\n",newDevice->generic.devcaps.dwAxes,newDevice->generic.devcaps.dwButtons,newDevice->generic.devcaps.dwPOVs);
if (newDevice->generic.devcaps.dwButtons > 128)
{
WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
newDevice->generic.devcaps.dwButtons = 128;
}
newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
newDevice->generic.base.ref = 1;
newDevice->generic.base.dinput = dinput;
newDevice->generic.base.guid = *rguid;
InitializeCriticalSection(&newDevice->generic.base.crit);
newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
/* Create copy of default data format */
if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++)
{
int wine_obj = -1;
BOOL has_ff = FALSE;
switch (axis_map[i])
{
case kHIDUsage_GD_X:
wine_obj = 0;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_X);
break;
case kHIDUsage_GD_Y:
wine_obj = 1;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Y);
break;
case kHIDUsage_GD_Z:
wine_obj = 2;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_Z);
break;
case kHIDUsage_GD_Rx:
wine_obj = 3;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RX);
break;
case kHIDUsage_GD_Ry:
wine_obj = 4;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RY);
break;
case kHIDUsage_GD_Rz:
wine_obj = 5;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_RZ);
break;
case kHIDUsage_GD_Slider:
wine_obj = 6 + slider_count;
has_ff = (newDevice->ff != 0) && osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(slider_count));
slider_count++;
break;
}
if (wine_obj < 0 ) continue;
memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
if(has_ff)
df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
++idx;
}
for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++)
{
memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize);
df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV;
}
for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
{
memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
df->rgodf[idx ].pguid = &GUID_Button;
df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
}
newDevice->generic.base.data_format.wine_df = df;
/* initialize default properties */
get_osx_device_elements_props(newDevice);
IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
EnterCriticalSection(&dinput->crit);
list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
LeaveCriticalSection(&dinput->crit);
newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
newDevice->generic.devcaps.dwFlags |= DIDC_ATTACHED;
if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
else
newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
newDevice->generic.devcaps.dwFFSamplePeriod = 0;
newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
newDevice->generic.devcaps.dwFirmwareRevision = 0;
newDevice->generic.devcaps.dwHardwareRevision = 0;
newDevice->generic.devcaps.dwFFDriverVersion = 0;
if (TRACE_ON(dinput)) {
_dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
_dump_DIDEVCAPS(&newDevice->generic.devcaps);
}
*pdev = newDevice;
return DI_OK;
FAILED:
hr = DIERR_OUTOFMEMORY;
if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
HeapFree(GetProcessHeap(), 0, df);
release_DataFormat(&newDevice->generic.base.data_format);
HeapFree(GetProcessHeap(),0,newDevice->generic.name);
HeapFree(GetProcessHeap(),0,newDevice);
*pdev = 0;
return hr;
}
/******************************************************************************
* get_joystick_index : Get the joystick index from a given GUID
*/
static unsigned short get_joystick_index(REFGUID guid)
{
GUID wine_joystick = DInput_Wine_OsX_Joystick_GUID;
GUID dev_guid = *guid;
wine_joystick.Data3 = 0;
dev_guid.Data3 = 0;
/* for the standard joystick GUID use index 0 */
if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
/* for the wine joystick GUIDs use the index stored in Data3 */
if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
return 0xffff;
}
static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
{
unsigned short index;
int joystick_devices_count;
TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
*pdev = NULL;
if ((joystick_devices_count = find_joystick_devices()) == 0)
return DIERR_DEVICENOTREG;
if ((index = get_joystick_index(rguid)) < 0xffff &&
joystick_devices_count && index < joystick_devices_count)
{
JoystickImpl *This;
HRESULT hr;
if (riid == NULL)
;/* nothing */
else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
IsEqualGUID(&IID_IDirectInputDevice8A, riid))
{
unicode = 0;
}
else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
IsEqualGUID(&IID_IDirectInputDevice8W, riid))
{
unicode = 1;
}
else
{
WARN("no interface\n");
return DIERR_NOINTERFACE;
}
hr = alloc_device(rguid, dinput, &This, index);
if (!This) return hr;
if (unicode)
*pdev = &This->generic.base.IDirectInputDevice8W_iface;
else
*pdev = &This->generic.base.IDirectInputDevice8A_iface;
return hr;
}
return DIERR_DEVICENOTREG;
}
static HRESULT osx_set_autocenter(JoystickImpl *This,
const DIPROPDWORD *header)
{
UInt32 v;
HRESULT hr;
if(!This->ff)
return DIERR_UNSUPPORTED;
v = header->dwData;
hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_AUTOCENTER, &v));
TRACE("returning: %08x\n", hr);
return hr;
}
static HRESULT osx_set_ffgain(JoystickImpl *This, const DIPROPDWORD *header)
{
UInt32 v;
HRESULT hr;
if(!This->ff)
return DIERR_UNSUPPORTED;
v = header->dwData;
hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(This->ff, FFPROP_FFGAIN, &v));
TRACE("returning: %08x\n", hr);
return hr;
}
static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface,
const GUID *prop, const DIPROPHEADER *header)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
switch(LOWORD(prop))
{
case (DWORD_PTR)DIPROP_AUTOCENTER:
return osx_set_autocenter(This, (const DIPROPDWORD *)header);
case (DWORD_PTR)DIPROP_FFGAIN:
return osx_set_ffgain(This, (const DIPROPDWORD *)header);
}
return JoystickWGenericImpl_SetProperty(iface, prop, header);
}
static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface,
const GUID *prop, const DIPROPHEADER *header)
{
JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
TRACE("%p %s %p\n", This, debugstr_guid(prop), header);
switch(LOWORD(prop))
{
case (DWORD_PTR)DIPROP_AUTOCENTER:
return osx_set_autocenter(This, (const DIPROPDWORD *)header);
case (DWORD_PTR)DIPROP_FFGAIN:
return osx_set_ffgain(This, (const DIPROPDWORD *)header);
}
return JoystickAGenericImpl_SetProperty(iface, prop, header);
}
static CFUUIDRef effect_win_to_mac(const GUID *effect)
{
#define DO_MAP(X) \
if(IsEqualGUID(&GUID_##X, effect)) \
return kFFEffectType_##X##_ID;
DO_MAP(ConstantForce)
DO_MAP(RampForce)
DO_MAP(Square)
DO_MAP(Sine)
DO_MAP(Triangle)
DO_MAP(SawtoothUp)
DO_MAP(SawtoothDown)
DO_MAP(Spring)
DO_MAP(Damper)
DO_MAP(Inertia)
DO_MAP(Friction)
DO_MAP(CustomForce)
#undef DO_MAP
WARN("Unknown effect GUID! %s\n", debugstr_guid(effect));
return 0;
}
static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
IUnknown *outer)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
EffectImpl *effect;
HRESULT hr;
TRACE("%p %s %p %p %p\n", iface, debugstr_guid(type), params, out, outer);
dump_DIEFFECT(params, type, 0);
if(!This->ff){
TRACE("No force feedback support\n");
*out = NULL;
return S_OK;
}
if(outer)
WARN("aggregation not implemented\n");
effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl;
effect->ref = 1;
effect->guid = *type;
effect->device = This;
/* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */
hr = osx_to_win32_hresult(FFDeviceCreateEffect(This->ff,
effect_win_to_mac(type), (FFEFFECT*)params, &effect->effect));
if(FAILED(hr)){
WARN("FFDeviceCreateEffect failed: %08x\n", hr);
HeapFree(GetProcessHeap(), 0, effect);
return hr;
}
list_add_tail(&This->effects, &effect->entry);
*out = &effect->IDirectInputEffect_iface;
TRACE("allocated effect: %p\n", effect);
return S_OK;
}
static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
const GUID *type, const DIEFFECT *params, IDirectInputEffect **out,
IUnknown *outer)
{
JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
TRACE("%p %s %p %p %p\n", iface, debugstr_guid(type), params, out, outer);
return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
type, params, out, outer);
}
static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W *iface,
DWORD flags)
{
JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
HRESULT hr;
TRACE("%p 0x%x\n", This, flags);
if(!This->ff)
return DI_NOEFFECT;
hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(This->ff, flags));
if(FAILED(hr)){
WARN("FFDeviceSendForceFeedbackCommand failed: %08x\n", hr);
return hr;
}
return S_OK;
}
static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(IDirectInputDevice8A *iface,
DWORD flags)
{
JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
TRACE("%p 0x%x\n", This, flags);
return JoystickWImpl_SendForceFeedbackCommand(&This->generic.base.IDirectInputDevice8W_iface, flags);
}
const struct dinput_device joystick_osx_device = {
"Wine OS X joystick driver",
joydev_enum_deviceA,
joydev_enum_deviceW,
joydev_create_device
};
static const IDirectInputDevice8AVtbl JoystickAvt =
{
IDirectInputDevice2AImpl_QueryInterface,
IDirectInputDevice2AImpl_AddRef,
IDirectInputDevice2AImpl_Release,
JoystickAGenericImpl_GetCapabilities,
IDirectInputDevice2AImpl_EnumObjects,
JoystickAGenericImpl_GetProperty,
JoystickAImpl_SetProperty,
IDirectInputDevice2AImpl_Acquire,
IDirectInputDevice2AImpl_Unacquire,
JoystickAGenericImpl_GetDeviceState,
IDirectInputDevice2AImpl_GetDeviceData,
IDirectInputDevice2AImpl_SetDataFormat,
IDirectInputDevice2AImpl_SetEventNotification,
IDirectInputDevice2AImpl_SetCooperativeLevel,
JoystickAGenericImpl_GetObjectInfo,
JoystickAGenericImpl_GetDeviceInfo,
IDirectInputDevice2AImpl_RunControlPanel,
IDirectInputDevice2AImpl_Initialize,
JoystickAImpl_CreateEffect,
IDirectInputDevice2AImpl_EnumEffects,
IDirectInputDevice2AImpl_GetEffectInfo,
IDirectInputDevice2AImpl_GetForceFeedbackState,
JoystickAImpl_SendForceFeedbackCommand,
IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
IDirectInputDevice2AImpl_Escape,
JoystickAGenericImpl_Poll,
IDirectInputDevice2AImpl_SendDeviceData,
IDirectInputDevice7AImpl_EnumEffectsInFile,
IDirectInputDevice7AImpl_WriteEffectToFile,
JoystickAGenericImpl_BuildActionMap,
JoystickAGenericImpl_SetActionMap,
IDirectInputDevice8AImpl_GetImageInfo
};
static const IDirectInputDevice8WVtbl JoystickWvt =
{
IDirectInputDevice2WImpl_QueryInterface,
IDirectInputDevice2WImpl_AddRef,
IDirectInputDevice2WImpl_Release,
JoystickWGenericImpl_GetCapabilities,
IDirectInputDevice2WImpl_EnumObjects,
JoystickWGenericImpl_GetProperty,
JoystickWImpl_SetProperty,
IDirectInputDevice2WImpl_Acquire,
IDirectInputDevice2WImpl_Unacquire,
JoystickWGenericImpl_GetDeviceState,
IDirectInputDevice2WImpl_GetDeviceData,
IDirectInputDevice2WImpl_SetDataFormat,
IDirectInputDevice2WImpl_SetEventNotification,
IDirectInputDevice2WImpl_SetCooperativeLevel,
JoystickWGenericImpl_GetObjectInfo,
JoystickWGenericImpl_GetDeviceInfo,
IDirectInputDevice2WImpl_RunControlPanel,
IDirectInputDevice2WImpl_Initialize,
JoystickWImpl_CreateEffect,
IDirectInputDevice2WImpl_EnumEffects,
IDirectInputDevice2WImpl_GetEffectInfo,
IDirectInputDevice2WImpl_GetForceFeedbackState,
JoystickWImpl_SendForceFeedbackCommand,
IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
IDirectInputDevice2WImpl_Escape,
JoystickWGenericImpl_Poll,
IDirectInputDevice2WImpl_SendDeviceData,
IDirectInputDevice7WImpl_EnumEffectsInFile,
IDirectInputDevice7WImpl_WriteEffectToFile,
JoystickWGenericImpl_BuildActionMap,
JoystickWGenericImpl_SetActionMap,
IDirectInputDevice8WImpl_GetImageInfo
};
static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface,
const GUID *guid, void **out)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %s %p\n", This, debugstr_guid(guid), out);
if(IsEqualIID(guid, &IID_IDirectInputEffect)){
*out = iface;
IDirectInputEffect_AddRef(iface);
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("%p, ref is now: %u\n", This, ref);
return ref;
}
static ULONG WINAPI effect_Release(IDirectInputEffect *iface)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("%p, ref is now: %u\n", This, ref);
if(!ref){
list_remove(&This->entry);
FFDeviceReleaseEffect(This->device->ff, This->effect);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst,
DWORD version, const GUID *guid)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid));
return S_OK;
}
static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p\n", This, out);
*out = This->guid;
return S_OK;
}
static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface,
DIEFFECT *effect, DWORD flags)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p 0x%x\n", This, effect, flags);
return osx_to_win32_hresult(FFEffectGetParameters(This->effect, (FFEFFECT*)effect, flags));
}
static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface,
const DIEFFECT *effect, DWORD flags)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p 0x%x\n", This, effect, flags);
dump_DIEFFECT(effect, &This->guid, flags);
return osx_to_win32_hresult(FFEffectSetParameters(This->effect, (FFEFFECT*)effect, flags));
}
static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations,
DWORD flags)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p 0x%x 0x%x\n", This, iterations, flags);
return osx_to_win32_hresult(FFEffectStart(This->effect, iterations, flags));
}
static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p\n", This);
return osx_to_win32_hresult(FFEffectStop(This->effect));
}
static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p\n", This, flags);
return osx_to_win32_hresult(FFEffectGetEffectStatus(This->effect, (UInt32*)flags));
}
static HRESULT WINAPI effect_Download(IDirectInputEffect *iface)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p\n", This);
return osx_to_win32_hresult(FFEffectDownload(This->effect));
}
static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p\n", This);
return osx_to_win32_hresult(FFEffectUnload(This->effect));
}
static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape)
{
EffectImpl *This = impl_from_IDirectInputEffect(iface);
TRACE("%p %p\n", This, escape);
return osx_to_win32_hresult(FFEffectEscape(This->effect, (FFEFFESCAPE*)escape));
}
static const IDirectInputEffectVtbl EffectVtbl = {
effect_QueryInterface,
effect_AddRef,
effect_Release,
effect_Initialize,
effect_GetEffectGuid,
effect_GetParameters,
effect_SetParameters,
effect_Start,
effect_Stop,
effect_GetEffectStatus,
effect_Download,
effect_Unload,
effect_Escape
};
#else /* HAVE_IOHIDMANAGERCREATE */
const struct dinput_device joystick_osx_device = {
"Wine OS X joystick driver",
NULL,
NULL,
NULL
};
#endif /* HAVE_IOHIDMANAGERCREATE */