From: Aric Stewart Subject: [1/2]winemac.drv: Add Joystick driver to the mac driver Message-Id: <5527E451.9080000@codeweavers.com> Date: Fri, 10 Apr 2015 09:55:13 -0500 --- dlls/winemac.drv/Makefile.in | 5 +- dlls/winemac.drv/joystick.c | 1203 +++++++++++++++++++++++++++++++++++++ dlls/winemac.drv/winemac.drv.spec | 19 + 3 files changed, 1225 insertions(+), 2 deletions(-) create mode 100644 dlls/winemac.drv/joystick.c diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 2d9c488..77ccda1 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -1,13 +1,14 @@ MODULE = winemac.drv IMPORTS = uuid user32 gdi32 advapi32 -DELAYIMPORTS = ole32 shell32 imm32 -EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit +DELAYIMPORTS = ole32 shell32 imm32 dxguid +EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit $(FORCEFEEDBACK_LIBS) C_SRCS = \ clipboard.c \ display.c \ dragdrop.c \ event.c \ + joystick.c \ gdi.c \ image.c \ ime.c \ diff --git a/dlls/winemac.drv/joystick.c b/dlls/winemac.drv/joystick.c new file mode 100644 index 0000000..95566ec --- /dev/null +++ b/dlls/winemac.drv/joystick.c @@ -0,0 +1,1203 @@ +/* + * The joystick interfaces for OS/X + * + * Copyright 1998 Marcus Meissner + * Copyright 1998,1999 Lionel Ulmer + * Copyright 2000-2001 TransGaming Technologies Inc. + * Copyright 2009,2015 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" +#include "wine/list.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 +#include +#include +#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" + +#ifdef HAVE_IOHIDMANAGERCREATE + +WINE_DEFAULT_DEBUG_CHANNEL(joystick); + +typedef struct JoystickImpl JoystickImpl; + +struct JoystickImpl { + CFArrayRef elements; + FFDeviceObjectReference ff; + struct list effects; +}; + +typedef struct _EffectImpl { + IDirectInputEffect IDirectInputEffect_iface; + LONG ref; + + JoystickImpl *device; + FFEffectObjectReference effect; + GUID guid; + + struct list entry; +} EffectImpl; + +static CFMutableArrayRef device_main_elements = NULL; +static CFMutableArrayRef devices = NULL; + +static EffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface) +{ + return CONTAINING_RECORD(iface, EffectImpl, IDirectInputEffect_iface); +} + +static const IDirectInputEffectVtbl EffectVtbl; + +VOID CDECL GamePadStopAllForceFeedbackEffects(int id, int release); + +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 const char* debugstr_cf(CFTypeRef t) +{ + CFStringRef s; + const char* ret; + + if (!t) return "(null)"; + + if (CFGetTypeID(t) == CFStringGetTypeID()) + s = t; + else + s = CFCopyDescription(t); + ret = CFStringGetCStringPtr(s, kCFStringEncodingUTF8); + if (ret) ret = debugstr_a(ret); + if (!ret) + { + const UniChar* u = CFStringGetCharactersPtr(s); + if (u) + ret = debugstr_wn((const WCHAR*)u, CFStringGetLength(s)); + } + if (!ret) + { + UniChar buf[200]; + int len = min(CFStringGetLength(s), sizeof(buf)/sizeof(buf[0])); + CFStringGetCharacters(s, CFRangeMake(0, len), buf); + ret = debugstr_wn(buf, len); + } + if (s != t) CFRelease(s); + return ret; +} + +static const char* debugstr_device(IOHIDDeviceRef device) +{ + return wine_dbg_sprintf("", device, + debugstr_cf(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)))); +} + +static const char* debugstr_element(IOHIDElementRef element) +{ + return wine_dbg_sprintf("", element, + IOHIDElementGetType(element), IOHIDElementGetUsagePage(element), + IOHIDElementGetUsage(element), IOHIDElementGetDevice(element)); +} + +static IOHIDDeviceRef get_device_ref(int id) +{ + IOHIDElementRef device_main_element; + IOHIDDeviceRef hid_device; + + TRACE("id %d\n", id); + + if (!device_main_elements || id >= CFArrayGetCount(device_main_elements)) + return 0; + + device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id); + if (!device_main_element) + { + ERR("Invalid Element requested %i\n",id); + return 0; + } + + hid_device = IOHIDElementGetDevice(device_main_element); + if (!hid_device) + { + ERR("Invalid Device requested %i\n",id); + return 0; + } + + TRACE("-> %s\n", debugstr_device(hid_device)); + return hid_device; +} + +static HRESULT get_ff(IOHIDDeviceRef device, FFDeviceObjectReference *ret) +{ + io_service_t service; + CFMutableDictionaryRef matching; + CFTypeRef location_id; + HRESULT hr; + + TRACE("device %s\n", debugstr_device(device)); + + matching = IOServiceMatching(kIOHIDDeviceKey); + if(!matching){ + WARN("IOServiceMatching failed, force feedback disabled\n"); + return DIERR_DEVICENOTREG; + } + + location_id = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey)); + if(!location_id){ + CFRelease(matching); + WARN("IOHIDDeviceGetProperty failed, force feedback disabled\n"); + return DIERR_DEVICENOTREG; + } + + CFDictionaryAddValue(matching, CFSTR(kIOHIDLocationIDKey), location_id); + + service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); + + if (ret) + hr = osx_to_win32_hresult(FFCreateDevice(service, ret)); + else + hr = FFIsForceFeedback(service) == FF_OK ? S_OK : S_FALSE; + + IOObjectRelease(service); + TRACE("-> hr 0x%08x *ret %p\n", hr, ret ? *ret : NULL); + return hr; +} + +static CFMutableDictionaryRef create_osx_device_match(int usage) +{ + CFMutableDictionaryRef result; + + TRACE("usage %d\n", usage); + + result = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + + if ( result ) + { + int number = kHIDPage_GenericDesktop; + CFNumberRef page = CFNumberCreate( kCFAllocatorDefault, + kCFNumberIntType, &number); + + if (page) + { + CFNumberRef cf_usage; + + CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsagePageKey ), page ); + CFRelease( page ); + + cf_usage = CFNumberCreate( kCFAllocatorDefault, + kCFNumberIntType, &usage); + if (cf_usage) + { + CFDictionarySetValue( result, CFSTR( kIOHIDDeviceUsageKey ), cf_usage ); + CFRelease( cf_usage ); + } + else + { + ERR("CFNumberCreate() failed.\n"); + CFRelease(result); + return NULL; + } + } + else + { + ERR("CFNumberCreate failed.\n"); + CFRelease(result); + return NULL; + } + } + else + { + ERR("CFDictionaryCreateMutable failed.\n"); + return NULL; + } + + return result; +} + +static CFIndex find_top_level(IOHIDDeviceRef hid_device, CFMutableArrayRef main_elements) +{ + CFArrayRef elements; + CFIndex total = 0; + + TRACE("hid_device %s\n", debugstr_device(hid_device)); + + if (!hid_device) + return 0; + + elements = IOHIDDeviceCopyMatchingElements(hid_device, NULL, 0); + + if (elements) + { + CFIndex idx, cnt = CFArrayGetCount(elements); + for (idx=0; idx total %d\n", (int)total); + return total; +} + +static void get_element_children(IOHIDElementRef element, CFMutableArrayRef all_children) +{ + CFIndex idx, cnt; + CFArrayRef element_children = IOHIDElementGetChildren(element); + + TRACE("element %s\n", debugstr_element(element)); + + cnt = CFArrayGetCount(element_children); + + /* Either add the element to the array or grab its children */ + for (idx=0; idx usage2) + return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +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; +} + + +INT CDECL GamePadCount(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; +} + +VOID CDECL GamePadName(int id, char *name, int length) +{ + CFStringRef str; + IOHIDDeviceRef hid_device; + + hid_device = get_device_ref(id); + + TRACE("id %d hid_device %s\n", id, debugstr_device(hid_device)); + + if (name) + name[0] = 0; + + if (!hid_device) + return; + + str = IOHIDDeviceGetProperty(hid_device, CFSTR( kIOHIDProductKey )); + if (str) + { + CFIndex len = CFStringGetLength(str); + if (length >= len) + { + CFStringGetCString(str,name,length,kCFStringEncodingASCII); + } + } +} + +static int map_axis(int usage) +{ + switch (usage) { + case kHIDUsage_GD_X: + return 0; + case kHIDUsage_GD_Y: + return 1; + case kHIDUsage_GD_Z: + return 2; + case kHIDUsage_GD_Rx: + return 3; + case kHIDUsage_GD_Ry: + return 4; + case kHIDUsage_GD_Rz: + return 5; + case kHIDUsage_GD_Slider: + return 6; + } + return -1; +} + +VOID CDECL GamePadElementCount(int id, DWORD *out_axis, DWORD *out_buttons, DWORD *out_povs, int axis_map[8]) +{ + IOHIDElementRef device_main_element; + CFMutableArrayRef elements; + DWORD sliders = 0; + JoystickImpl *device = NULL; + + TRACE("id %d\n", id); + + if (!device_main_elements || id >= CFArrayGetCount(device_main_elements)) + return; + + device_main_element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, id); + TRACE("device_main_element %s\n", debugstr_element(device_main_element)); + if (!device_main_element) + return; + + device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + device->elements = NULL; + + elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + get_element_children(device_main_element, elements); + + if (elements) + { + CFIndex idx, cnt = CFArrayGetCount( elements ); + CFMutableArrayRef axes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef povs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + for ( idx = 0; idx < cnt; idx++ ) + { + IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex( elements, idx ); + int type = IOHIDElementGetType( element ); + + TRACE("element %s\n", debugstr_element(element)); + + switch(type) + { + case kIOHIDElementTypeInput_Button: + { + int usage_page = IOHIDElementGetUsagePage( element ); + TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page); + if (usage_page != kHIDPage_Button) + { + /* avoid strange elements found on the 360 controller */ + continue; + } + + if (CFArrayGetCount(buttons) < 128) + CFArrayAppendValue(buttons, element); + break; + } + case kIOHIDElementTypeInput_Axis: + { + TRACE("kIOHIDElementTypeInput_Axis\n"); + CFArrayAppendValue(axes, element); + break; + } + case kIOHIDElementTypeInput_Misc: + { + uint32_t usage = IOHIDElementGetUsage( element ); + switch(usage) + { + case kHIDUsage_GD_Hatswitch: + { + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n"); + CFArrayAppendValue(povs, element); + 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: + { + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_* (%d)\n", usage); + axis_map[CFArrayGetCount(axes)]=map_axis(usage); + CFArrayAppendValue(axes, element); + break; + } + default: + FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %i\n", usage); + } + break; + } + default: + FIXME("Unhandled type %i\n",type); + } + } + + /* Sort buttons into correct order */ + CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), button_usage_comparator, NULL); + + *out_axis = CFArrayGetCount(axes); + *out_buttons = CFArrayGetCount(buttons); + *out_povs = CFArrayGetCount(povs); + + /* build our element array in the order that dinput expects */ + CFArrayAppendArray(axes, povs, CFRangeMake(0, *out_povs)); + CFArrayAppendArray(axes, buttons, CFRangeMake(0, *out_buttons)); + device->elements = axes; + + CFRelease(povs); + CFRelease(buttons); + CFRelease(elements); + } + else + { + *out_axis = 0; + *out_buttons = 0; + *out_povs = 0; + } +} + +VOID CDECL GamePadElementProps(int id, int index, int *min, int* max) +{ + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex(device->elements, index); + + TRACE("element %s\n", debugstr_element(element)); + + *min = IOHIDElementGetLogicalMin(element); + *max = IOHIDElementGetLogicalMax(element); +} + +VOID CDECL GamePadPollValues(int id, int* values) +{ + IOHIDElementRef device_main_element; + IOHIDDeviceRef hid_device; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + + TRACE("device %i\n", id); + + if (!device_main_elements || id >= CFArrayGetCount(device_main_elements)) + return; + + device_main_element = (IOHIDElementRef) CFArrayGetValueAtIndex(device_main_elements, id); + hid_device = IOHIDElementGetDevice(device_main_element); + TRACE("main element %s hid_device %s\n", debugstr_element(device_main_element), debugstr_device(hid_device)); + if (!hid_device) + return; + + if (device->elements) + { + int button_idx = 0; + int pov_idx = 0; + int data_idx = 0; + CFIndex idx, cnt = CFArrayGetCount(device->elements); + + for ( idx = 0; idx < cnt; idx++ ) + { + IOHIDValueRef valueRef; + int val; + IOHIDElementRef element = ( IOHIDElementRef ) CFArrayGetValueAtIndex(device->elements, idx ); + int type = IOHIDElementGetType( element ); + + TRACE("element %s\n", debugstr_element(element)); + + switch(type) + { + case kIOHIDElementTypeInput_Button: + TRACE("kIOHIDElementTypeInput_Button\n"); + if(button_idx < 128) + { + IOHIDDeviceGetValue(hid_device, element, &valueRef); + val = IOHIDValueGetIntegerValue(valueRef); + values[data_idx++] = val ? 0x80 : 0x0; + button_idx ++; + } + break; + case kIOHIDElementTypeInput_Misc: + { + uint32_t usage = IOHIDElementGetUsage( element ); + switch(usage) + { + case kHIDUsage_GD_Hatswitch: + { + int x, y; + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n"); + IOHIDDeviceGetValue(hid_device, element, &valueRef); + val = IOHIDValueGetIntegerValue(valueRef); + /* handled as a pair of axis */ + switch (val) + { + case 0: + case 4: + x = 0; + break; + case 1: + case 2: + case 3: + x = 0xffff; + break; + case 5: + case 6: + case 7: + x = -1 * 0xffff; + break; + default: + x = 0; + } + switch (val) + { + case 2: + case 6: + y = 0; + break; + case 3: + case 4: + case 5: + y = 0xffff; + break; + case 7: + case 0: + case 1: + y = -1 * 0xffff; + break; + default: + y = 0; + } + values[data_idx++] = x; + values[data_idx++] = y; + 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: + { + IOHIDDeviceGetValue(hid_device, element, &valueRef); + val = IOHIDValueGetIntegerValue(valueRef); + values[data_idx++] = val; + break; + } + default: + FIXME("kIOHIDElementTypeInput_Misc / unhandled usage %i\n", usage); + } + break; + } + default: + FIXME("Unhandled type %i\n",type); + } + } + } +} + +BOOL CDECL GamePadAlloc(int id) +{ + JoystickImpl *device; + + if (!devices) + devices = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + device = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*device)); + + if (!device) + return FALSE; + + list_init(&device->effects); + + CFArrayInsertValueAtIndex(devices, id, device); + + return TRUE; +} + +VOID CDECL GamePadDealloc(int id) +{ + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + + GamePadStopAllForceFeedbackEffects(id, TRUE); + if (device->elements) CFRelease(device->elements); + if (device->ff) FFReleaseDevice(device->ff); + HeapFree(GetProcessHeap(), 0, device); +} + +BOOL CDECL GamePadHasForceFeedback(int id, DWORD *effects) +{ + FFCAPABILITIES ffcaps; + JoystickImpl *device; + int i; + + /* This call can come during enumeration so before alloc */ + if (!devices) + GamePadAlloc(id); + device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (!device) + { + GamePadAlloc(id); + device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + } + + if(device->ff || get_ff(get_device_ref(id), &device->ff) == S_OK){ + HRESULT hr = FFDeviceGetForceFeedbackCapabilities(device->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(device->ff, FFSFFC_RESET); + if(FAILED(hr)) + WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_RESET) failed: %08x\n", hr); + + hr = FFDeviceSendForceFeedbackCommand(device->ff, FFSFFC_SETACTUATORSON); + if(FAILED(hr)) + WARN("FFDeviceSendForceFeedbackCommand(FFSFFC_SETACTUATORSON) failed: %08x\n", hr); + + if (effects) *effects = ffcaps.supportedEffects; + return TRUE; + } + return FALSE; +} + +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; +} + +BOOL CDECL GamePadElementHasForceFeedback(int id, int element) +{ + FFCAPABILITIES ffcaps; + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + + if (element > 8) /* The maximum axis */ + return FALSE; + + if (device->ff) { + hr = FFDeviceGetForceFeedbackCapabilities(device->ff, &ffcaps); + if(SUCCEEDED(hr)){ + switch (element) { + case 0: return osx_axis_has_ff(&ffcaps, FFJOFS_X); + case 1: return osx_axis_has_ff(&ffcaps, FFJOFS_Y); + case 2: return osx_axis_has_ff(&ffcaps, FFJOFS_Z); + case 3: return osx_axis_has_ff(&ffcaps, FFJOFS_RX); + case 4: return osx_axis_has_ff(&ffcaps, FFJOFS_RY); + case 5: return osx_axis_has_ff(&ffcaps, FFJOFS_RZ); + default: return osx_axis_has_ff(&ffcaps, FFJOFS_SLIDER(element - 6)); + } + } + } + return FALSE; +} + +VOID CDECL GamePadGetForceFeedbackState(int id, DWORD *state) +{ + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + FFDeviceGetForceFeedbackState(device->ff, (FFState*)state); + } +} + +HRESULT CDECL GamePadSetAutocenter(int id, DWORD data) +{ + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(device->ff, + FFPROP_AUTOCENTER, &data)); + return hr; + } + return DIERR_UNSUPPORTED; +} + +HRESULT CDECL GamePadGetAutocenter(int id, DWORD *data) +{ + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + hr = FFDeviceGetForceFeedbackProperty(device->ff, FFPROP_AUTOCENTER, data, sizeof(DWORD)); + return (hr == FF_OK) ? DI_OK : DIERR_UNSUPPORTED; + } + return DIERR_UNSUPPORTED; +} + +HRESULT CDECL GamePadSetFFGain(int id, DWORD data) +{ + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + hr = osx_to_win32_hresult(FFDeviceSetForceFeedbackProperty(device->ff, + FFPROP_FFGAIN, &data)); + return hr; + } + return DIERR_UNSUPPORTED; +} + +HRESULT CDECL GamePadGetFFGain(int id, DWORD *data) +{ + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + hr = FFDeviceGetForceFeedbackProperty(device->ff, FFPROP_FFGAIN, data, sizeof(DWORD)); + return (hr == FF_OK) ? DI_OK : DIERR_UNSUPPORTED; + } + return DIERR_UNSUPPORTED; +} + +HRESULT CDECL GamePadSendForceFeedbackCommand(int id, DWORD flags) +{ + HRESULT hr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + hr = osx_to_win32_hresult(FFDeviceSendForceFeedbackCommand(device->ff, flags)); + if (flags == DISFFC_RESET) + { + EffectImpl *itr; + LIST_FOR_EACH_ENTRY(itr, &device->effects, EffectImpl, entry) + IDirectInputEffect_Release(&itr->IDirectInputEffect_iface); + } + return hr; + } + return DI_NOEFFECT; +} + +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; +} + + +DWORD CDECL GamePadCreateDinputEffect(int id, const GUID *type, const DIEFFECT *params, IDirectInputEffect **out, IUnknown *outer) +{ + HRESULT hr; + EffectImpl *effect; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + if (device->ff) { + if(outer) + WARN("aggregation not implemented\n"); + + effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*effect)); + effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl; + effect->ref = 1; + effect->guid = *type; + effect->device = device; + + /* Mac's FFEFFECT and Win's DIEFFECT are binary identical. */ + hr = osx_to_win32_hresult(FFDeviceCreateEffect(device->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(&device->effects, &effect->entry); + *out = &effect->IDirectInputEffect_iface; + + TRACE("allocated effect: %p\n", effect); + + return S_OK; + } + else { + TRACE("No effects support\n"); + *out = NULL; + return DI_NOEFFECT; + } +} + +VOID CDECL GamePadStopAllForceFeedbackEffects(int id, int release) +{ + EffectImpl *itr; + JoystickImpl *device = (JoystickImpl*)CFArrayGetValueAtIndex(devices, id); + + LIST_FOR_EACH_ENTRY(itr, &device->effects, EffectImpl, entry) + { + IDirectInputEffect_Stop(&itr->IDirectInputEffect_iface); + IDirectInputEffect_Unload(&itr->IDirectInputEffect_iface); + if (release) + IDirectInputEffect_Release(&itr->IDirectInputEffect_iface); + } +} + +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); + 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) +{ + HRESULT hr; + EffectImpl *This = impl_from_IDirectInputEffect(iface); + TRACE("%p\n", This); + hr = FFEffectDownload(This->effect); + if (hr == FF_OK || hr == S_FALSE) + return DI_OK; + else + return osx_to_win32_hresult(hr); +} + +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 +}; + +#endif /* HAVE_IOHIDMANAGERCREATE */ diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 58a894b..91b6798 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -71,3 +71,22 @@ @ stdcall ImeToAsciiEx(long long ptr ptr long long) @ stdcall ImeUnregisterWord(wstr long wstr) @ stdcall NotifyIME(long long long long) + +# GamePad driver functions +@ stdcall GamePadCount() +@ stdcall GamePadName(long ptr long) +@ stdcall GamePadElementCount(long ptr ptr ptr ptr) +@ stdcall GamePadElementProps(long long ptr ptr) +@ stdcall GamePadPollValues(long ptr) +@ stdcall GamePadAlloc(long) +@ stdcall GamePadDealloc(long) +@ stdcall GamePadHasForceFeedback(long ptr) +@ stdcall GamePadElementHasForceFeedback(long long) +@ stdcall GamePadGetForceFeedbackState(long ptr) +@ stdcall GamePadSetAutocenter(long long) +@ stdcall GamePadGetAutocenter(long ptr) +@ stdcall GamePadSetFFGain(long long) +@ stdcall GamePadGetFFGain(long ptr) +@ stdcall GamePadSendForceFeedbackCommand(long long) +@ stdcall GamePadCreateDinputEffect(long ptr ptr ptr ptr); +@ stdcall GamePadStopAllForceFeedbackEffects(long long);