From: Ken Thomases Subject: [PATCH 2/2] winejoystick: Add WinMM joystick support for OS X. Message-Id: <725AF386-C71F-420E-843C-5777A4000BCB@codeweavers.com> Date: Mon, 16 Mar 2015 18:44:08 -0500 --- configure.ac | 2 +- dlls/winejoystick.drv/Makefile.in | 2 + dlls/winejoystick.drv/joystick_osx.c | 754 +++++++++++++++++++++++++++++++++++ 3 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 dlls/winejoystick.drv/joystick_osx.c diff --git a/configure.ac b/configure.ac index f242181..d23227a 100644 --- a/configure.ac +++ b/configure.ac @@ -1767,7 +1767,7 @@ dnl **** Disable unsupported winmm drivers **** test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no} test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no} test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no} -test "$ac_cv_header_linux_joystick_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} +test "$ac_cv_header_linux_joystick_h" = "yes" -o "$ac_cv_header_IOKit_hid_IOHIDLib_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} dnl **** Check for any sound system **** if test "x$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \ diff --git a/dlls/winejoystick.drv/Makefile.in b/dlls/winejoystick.drv/Makefile.in index f0aaae3..cb893cf 100644 --- a/dlls/winejoystick.drv/Makefile.in +++ b/dlls/winejoystick.drv/Makefile.in @@ -1,6 +1,8 @@ MODULE = winejoystick.drv IMPORTS = winmm user32 +EXTRALIBS = $(IOKIT_LIBS) C_SRCS = \ joystick_linux.c \ + joystick_osx.c \ joystick.c diff --git a/dlls/winejoystick.drv/joystick_osx.c b/dlls/winejoystick.drv/joystick_osx.c new file mode 100644 index 0000000..c8850af --- /dev/null +++ b/dlls/winejoystick.drv/joystick_osx.c @@ -0,0 +1,754 @@ +/* + * WinMM joystick driver OS X implementation + * + * Copyright 1997 Andreas Mohr + * Copyright 1998 Marcus Meissner + * Copyright 1998,1999 Lionel Ulmer + * Copyright 2000 Wolfgang Schwotzer + * Copyright 2000-2001 TransGaming Technologies Inc. + * Copyright 2002 David Hagood + * Copyright 2009 CodeWeavers, Aric Stewart + * Copyright 2015 Ken Thomases for CodeWeavers Inc. + * + * 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" + +#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 +#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 + +#include "joystick.h" + +#include "wine/debug.h" + + +WINE_DEFAULT_DEBUG_CHANNEL(joystick); + + +#define MAXJOYSTICK (JOYSTICKID2 + 30) + + +enum { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_RX, + AXIS_RY, + AXIS_RZ, + NUM_AXES +}; + +struct axis { + IOHIDElementRef element; + CFIndex min_value, max_value; +}; + +typedef struct { + BOOL in_use; + IOHIDElementRef element; + struct axis axes[NUM_AXES]; + CFMutableArrayRef buttons; + IOHIDElementRef hatswitch; +} joystick_t; + + +static joystick_t joysticks[MAXJOYSTICK]; +static CFMutableArrayRef device_main_elements = NULL; + + +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 int axis_for_usage(int usage) +{ + switch (usage) + { + case kHIDUsage_GD_X: return AXIS_X; + case kHIDUsage_GD_Y: return AXIS_Y; + case kHIDUsage_GD_Z: return AXIS_Z; + case kHIDUsage_GD_Rx: return AXIS_RX; + case kHIDUsage_GD_Ry: return AXIS_RY; + case kHIDUsage_GD_Rz: return AXIS_RZ; + } + + return -1; +} + + +/************************************************************************** + * joystick_from_id + */ +static joystick_t* joystick_from_id(DWORD_PTR device_id) +{ + int index; + + if ((device_id - (DWORD_PTR)joysticks) % sizeof(joysticks[0]) != 0) + return NULL; + index = (device_id - (DWORD_PTR)joysticks) / sizeof(joysticks[0]); + if (index < 0 || index >= MAXJOYSTICK || !((joystick_t*)device_id)->in_use) + return NULL; + + return (joystick_t*)device_id; +} + +/************************************************************************** + * create_osx_device_match + */ +static CFDictionaryRef create_osx_device_match(int usage) +{ + CFDictionaryRef result = NULL; + int number; + CFStringRef keys[] = { CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) }; + CFNumberRef values[2]; + int i; + + TRACE("usage %d\n", usage); + + number = kHIDPage_GenericDesktop; + values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &number); + values[1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + + if (values[0] && values[1]) + { + result = CFDictionaryCreate(NULL, (const void**)keys, (const void**)values, sizeof(values) / sizeof(values[0]), + &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (!result) + ERR("CFDictionaryCreate failed.\n"); + } + else + ERR("CFNumberCreate failed.\n"); + + for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) + if (values[i]) CFRelease(values[i]); + + return result; +} + +/************************************************************************** + * find_top_level + */ +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 i, count = CFArrayGetCount(elements); + for (i = 0; i < count; i++) + { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); + int type = IOHIDElementGetType(element); + + TRACE("element %s\n", debugstr_element(element)); + + /* Check for top-level gaming device collections */ + if (type == kIOHIDElementTypeCollection && IOHIDElementGetParent(element) == 0) + { + int usage_page = IOHIDElementGetUsagePage(element); + int usage = IOHIDElementGetUsage(element); + + if (usage_page == kHIDPage_GenericDesktop && + (usage == kHIDUsage_GD_Joystick || usage == kHIDUsage_GD_GamePad)) + { + CFArrayAppendValue(main_elements, element); + total++; + } + } + } + CFRelease(elements); + } + + TRACE("-> total %d\n", (int)total); + return total; +} + +/************************************************************************** + * find_osx_devices + */ +static int find_osx_devices(void) +{ + IOHIDManagerRef hid_manager; + int usages[] = { kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad }; + int i; + CFDictionaryRef matching_dicts[sizeof(usages) / sizeof(usages[0])]; + CFArrayRef matching; + CFSetRef devset; + + TRACE("()\n"); + + hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L); + if (IOHIDManagerOpen(hid_manager, 0) != kIOReturnSuccess) + { + ERR("Couldn't open IOHIDManager.\n"); + CFRelease(hid_manager); + return 0; + } + + for (i = 0; i < sizeof(matching_dicts) / sizeof(matching_dicts[0]); i++) + { + matching_dicts[i] = create_osx_device_match(usages[i]); + if (!matching_dicts[i]) + { + while (i > 0) + CFRelease(matching_dicts[--i]); + goto fail; + } + } + + matching = CFArrayCreate(NULL, (const void**)matching_dicts, sizeof(matching_dicts) / sizeof(matching_dicts[0]), + &kCFTypeArrayCallBacks); + + for (i = 0; i < sizeof(matching_dicts) / sizeof(matching_dicts[0]); i++) + CFRelease(matching_dicts[i]); + + IOHIDManagerSetDeviceMatchingMultiple(hid_manager, matching); + CFRelease(matching); + devset = IOHIDManagerCopyDevices(hid_manager); + if (devset) + { + CFIndex num_devices, num_main_elements; + const void** refs; + CFArrayRef devices; + + num_devices = CFSetGetCount(devset); + refs = HeapAlloc(GetProcessHeap(), 0, num_devices * sizeof(*refs)); + if (!refs) + { + CFRelease(devset); + goto fail; + } + + CFSetGetValues(devset, refs); + devices = CFArrayCreate(NULL, refs, num_devices, &kCFTypeArrayCallBacks); + HeapFree(GetProcessHeap(), 0, refs); + CFRelease(devset); + if (!devices) + goto fail; + + device_main_elements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if (!device_main_elements) + { + CFRelease(devices); + goto fail; + } + + num_main_elements = 0; + for (i = 0; i < num_devices; i++) + { + IOHIDDeviceRef hid_device = (IOHIDDeviceRef)CFArrayGetValueAtIndex(devices, i); + TRACE("hid_device %s\n", debugstr_device(hid_device)); + num_main_elements += find_top_level(hid_device, device_main_elements); + } + + CFRelease(devices); + + TRACE("found %i device(s), %i collection(s)\n",(int)num_devices,(int)num_main_elements); + return (int)num_main_elements; + } + +fail: + IOHIDManagerClose(hid_manager, 0); + CFRelease(hid_manager); + return 0; +} + +/************************************************************************** + * collect_joystick_elements + */ +static void collect_joystick_elements(joystick_t* joystick, IOHIDElementRef collection) +{ + CFIndex i, count; + CFArrayRef children = IOHIDElementGetChildren(collection); + + TRACE("collection %s\n", debugstr_element(collection)); + + count = CFArrayGetCount(children); + for (i = 0; i < count; i++) + { + IOHIDElementRef child; + int type; + + child = (IOHIDElementRef)CFArrayGetValueAtIndex(children, i); + TRACE("child %s\n", debugstr_element(child)); + type = IOHIDElementGetType(child); + switch (type) + { + case kIOHIDElementTypeCollection: + collect_joystick_elements(joystick, child); + break; + case kIOHIDElementTypeInput_Button: + { + int usage_page = IOHIDElementGetUsagePage(child); + + TRACE("kIOHIDElementTypeInput_Button usage_page %d\n", usage_page); + + /* avoid strange elements found on the 360 controller */ + if (usage_page == kHIDPage_Button) + CFArrayAppendValue(joystick->buttons, child); + break; + } + case kIOHIDElementTypeInput_Axis: + { + TRACE("kIOHIDElementTypeInput_Axis; ignoring\n"); + break; + } + case kIOHIDElementTypeInput_Misc: + { + uint32_t usage = IOHIDElementGetUsage( child ); + switch(usage) + { + case kHIDUsage_GD_Hatswitch: + { + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n"); + if (joystick->hatswitch) + TRACE(" ignoring additional hatswitch\n"); + else + joystick->hatswitch = (IOHIDElementRef)CFRetain(child); + 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: + { + int axis = axis_for_usage(usage); + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_ (%d) axis %d\n", usage, axis); + if (axis < 0 || joystick->axes[axis].element) + TRACE(" ignoring\n"); + else + { + joystick->axes[axis].element = (IOHIDElementRef)CFRetain(child); + joystick->axes[axis].min_value = IOHIDElementGetLogicalMin(child); + joystick->axes[axis].max_value = IOHIDElementGetLogicalMax(child); + } + break; + } + case kHIDUsage_GD_Slider: + TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Slider; ignoring\n"); + break; + default: + FIXME("kIOHIDElementTypeInput_Misc / Unhandled usage %d\n", usage); + break; + } + break; + } + default: + FIXME("Unhandled type %i\n",type); + break; + } + } +} + +/************************************************************************** + * button_usage_comparator + */ +static CFComparisonResult button_usage_comparator(const void *val1, const void *val2, void *context) +{ + IOHIDElementRef element1 = (IOHIDElementRef)val1, element2 = (IOHIDElementRef)val2; + int usage1 = IOHIDElementGetUsage(element1), usage2 = IOHIDElementGetUsage(element2); + + if (usage1 < usage2) + return kCFCompareLessThan; + if (usage1 > usage2) + return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +/************************************************************************** + * driver_open + */ +LRESULT driver_open(LPSTR str, DWORD index) +{ + if (index >= MAXJOYSTICK || joysticks[index].in_use) + return 0; + + joysticks[index].in_use = TRUE; + return (LRESULT)&joysticks[index]; +} + +/************************************************************************** + * driver_close + */ +LRESULT driver_close(DWORD_PTR device_id) +{ + joystick_t* joystick = joystick_from_id(device_id); + int i; + + if (joystick == NULL) + return 0; + + CFRelease(joystick->element); + for (i = 0; i < NUM_AXES; i++) + { + if (joystick->axes[i].element) + CFRelease(joystick->axes[i].element); + } + if (joystick->buttons) + CFRelease(joystick->buttons); + if (joystick->hatswitch) + CFRelease(joystick->hatswitch); + + memset(joystick, 0, sizeof(*joystick)); + return 1; +} + +/************************************************************************** + * open_joystick + */ +static BOOL open_joystick(joystick_t* joystick) +{ + CFIndex index; + CFRange range; + + if (joystick->element) + return TRUE; + + if (!device_main_elements) + { + find_osx_devices(); + if (!device_main_elements) + return FALSE; + } + + index = joystick - joysticks; + if (index > CFArrayGetCount(device_main_elements)) + return FALSE; + + joystick->element = (IOHIDElementRef)CFArrayGetValueAtIndex(device_main_elements, index); + joystick->buttons = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + collect_joystick_elements(joystick, joystick->element); + + /* Sort buttons into correct order */ + range.location = 0; + range.length = CFArrayGetCount(joystick->buttons); + CFArraySortValues(joystick->buttons, range, button_usage_comparator, NULL); + if (range.length > 32) + { + /* Delete any buttons beyond the first 32 */ + range.location = 32; + range.length -= 32; + CFArrayReplaceValues(joystick->buttons, range, NULL, 0); + } + + return TRUE; +} + + +/************************************************************************** + * driver_joyGetDevCaps + */ +LRESULT driver_joyGetDevCaps(DWORD_PTR device_id, JOYCAPSW* caps, DWORD size) +{ + joystick_t* joystick; + IOHIDDeviceRef device; + + if ((joystick = joystick_from_id(device_id)) == NULL) + return MMSYSERR_NODRIVER; + + if (!open_joystick(joystick)) + return JOYERR_PARMS; + + caps->szPname[0] = 0; + + device = IOHIDElementGetDevice(joystick->element); + if (device) + { + CFStringRef product_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (product_name) + { + CFRange range; + + range.location = 0; + range.length = min(MAXPNAMELEN - 1, CFStringGetLength(product_name)); + CFStringGetCharacters(product_name, range, (UniChar*)caps->szPname); + caps->szPname[range.length] = 0; + } + } + + caps->wMid = MM_MICROSOFT; + caps->wPid = MM_PC_JOYSTICK; + caps->wXmin = 0; + caps->wXmax = 0xFFFF; + caps->wYmin = 0; + caps->wYmax = 0xFFFF; + caps->wZmin = 0; + caps->wZmax = joystick->axes[AXIS_Z].element ? 0xFFFF : 0; + caps->wNumButtons = CFArrayGetCount(joystick->buttons); + if (size == sizeof(JOYCAPSW)) + { + int i; + + /* complete 95 structure */ + caps->wRmin = 0; + caps->wRmax = 0xFFFF; + caps->wUmin = 0; + caps->wUmax = 0xFFFF; + caps->wVmin = 0; + caps->wVmax = 0xFFFF; + caps->wMaxAxes = 6; /* same as MS Joystick Driver */ + caps->wNumAxes = 0; + caps->wMaxButtons = 32; /* same as MS Joystick Driver */ + caps->szRegKey[0] = 0; + caps->szOEMVxD[0] = 0; + caps->wCaps = 0; + + for (i = 0; i < NUM_AXES; i++) + { + if (joystick->axes[i].element) + { + caps->wNumAxes++; + switch (i) + { + case AXIS_Z: caps->wCaps |= JOYCAPS_HASZ; break; + case AXIS_RX: caps->wCaps |= JOYCAPS_HASU; break; + case AXIS_RY: caps->wCaps |= JOYCAPS_HASV; break; + case AXIS_RZ: caps->wCaps |= JOYCAPS_HASR; break; + } + } + } + + if (joystick->hatswitch) + caps->wCaps |= JOYCAPS_HASPOV | JOYCAPS_POV4DIR; + } + + TRACE("name %s buttons %u axes %d caps 0x%08x\n", debugstr_w(caps->szPname), caps->wNumButtons, caps->wNumAxes, caps->wCaps); + + return JOYERR_NOERROR; +} + +/************************************************************************** + * driver_joyGetPosEx + */ +LRESULT driver_joyGetPosEx(DWORD_PTR device_id, JOYINFOEX* info) +{ + static const struct { + DWORD flag; + off_t offset; + } axis_map[NUM_AXES] = { + { JOY_RETURNX, FIELD_OFFSET(JOYINFOEX, dwXpos) }, + { JOY_RETURNY, FIELD_OFFSET(JOYINFOEX, dwYpos) }, + { JOY_RETURNZ, FIELD_OFFSET(JOYINFOEX, dwZpos) }, + { JOY_RETURNU, FIELD_OFFSET(JOYINFOEX, dwUpos) }, + { JOY_RETURNV, FIELD_OFFSET(JOYINFOEX, dwVpos) }, + { JOY_RETURNR, FIELD_OFFSET(JOYINFOEX, dwRpos) }, + }; + + joystick_t* joystick; + IOHIDDeviceRef device; + CFIndex i, count; + IOHIDValueRef valueRef; + long value; + + if ((joystick = joystick_from_id(device_id)) == NULL) + return MMSYSERR_NODRIVER; + + if (!open_joystick(joystick)) + return JOYERR_PARMS; + + device = IOHIDElementGetDevice(joystick->element); + + if (info->dwFlags & JOY_RETURNBUTTONS) + { + info->dwButtons = 0; + info->dwButtonNumber = 0; + + count = CFArrayGetCount(joystick->buttons); + for (i = 0; i < count; i++) + { + IOHIDElementRef button = (IOHIDElementRef)CFArrayGetValueAtIndex(joystick->buttons, i); + IOHIDDeviceGetValue(device, button, &valueRef); + value = IOHIDValueGetIntegerValue(valueRef); + if (value) + { + info->dwButtons |= 1 << i; + if (!info->dwButtonNumber) + info->dwButtonNumber = i + 1; + } + } + } + + for (i = 0; i < NUM_AXES; i++) + { + if (info->dwFlags & axis_map[i].flag) + { + DWORD* field = (DWORD*)((char*)info + axis_map[i].offset); + if (joystick->axes[i].element) + { + IOHIDDeviceGetValue(device, joystick->axes[i].element, &valueRef); + value = IOHIDValueGetIntegerValue(valueRef) - joystick->axes[i].min_value; + *field = MulDiv(value, 0xFFFF, joystick->axes[i].max_value - joystick->axes[i].min_value); + } + else + { + *field = 0; + info->dwFlags &= ~axis_map[i].flag; + } + } + } + + if (info->dwFlags & JOY_RETURNPOV) + { + if (joystick->hatswitch) + { + IOHIDDeviceGetValue(device, joystick->hatswitch, &valueRef); + value = IOHIDValueGetIntegerValue(valueRef); + if (value >= 8) + info->dwPOV = JOY_POVCENTERED; + else + info->dwPOV = value * 4500; + } + else + { + info->dwPOV = 0; + info->dwFlags &= ~JOY_RETURNPOV; + } + } + + TRACE("x: %d, y: %d, z: %d, r: %d, u: %d, v: %d, buttons: 0x%04x, pov %d, flags: 0x%04x\n", + info->dwXpos, info->dwYpos, info->dwZpos, info->dwRpos, info->dwUpos, info->dwVpos, info->dwButtons, info->dwPOV, info->dwFlags); + + return JOYERR_NOERROR; +} + +/************************************************************************** + * driver_joyGetPos + */ +LRESULT driver_joyGetPos(DWORD_PTR device_id, JOYINFO* info) +{ + JOYINFOEX ji; + LONG ret; + + memset(&ji, 0, sizeof(ji)); + + ji.dwSize = sizeof(ji); + ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNBUTTONS; + ret = driver_joyGetPosEx(device_id, &ji); + if (ret == JOYERR_NOERROR) + { + info->wXpos = ji.dwXpos; + info->wYpos = ji.dwYpos; + info->wZpos = ji.dwZpos; + info->wButtons = ji.dwButtons; + } + + return ret; +} + +#endif /* HAVE_IOKIT_HID_IOHIDLIB_H */